'robotstxt_map', 'posts_per_page' => -1, 'post_status' => 'any', 'fields' => 'ids', ) ); // Delete each mapping post and its associated meta. foreach ( $mapping_posts as $mapping_id ) { // Clear scheduled cron events for this mapping. $hook = 'robotstxt_docmd_sync_event'; $args = array( $mapping_id ); $timestamp = wp_next_scheduled( $hook, $args ); if ( $timestamp ) { wp_unschedule_event( $timestamp, $hook, $args ); } // Force delete (bypass trash). This also deletes all associated post meta. wp_delete_post( $mapping_id, true ); } // Delete plugin options. delete_option( 'robotstxt_docmd_settings' ); // Delete transients (cached data). // Note: WordPress does not provide an API for pattern-based transient deletion. // Direct database queries are necessary here for bulk cleanup operations. robotstxt_docmd_delete_transients_by_prefix( 'robotstxt_docmd_' ); robotstxt_docmd_delete_transients_by_prefix( 'robotstxt_docmd_notification_' ); // Note: We deliberately DO NOT delete the synced posts/pages themselves. // Users may want to keep the documentation even after uninstalling the plugin. } /** * Delete all transients matching a prefix * * WordPress does not provide an API for pattern-based transient deletion, * so direct database access is required for bulk cleanup operations. * This is compliant with AGENTS.md guidelines for necessary database operations. * * @since 1.0.0 * * @param string $prefix Transient name prefix to match. * @return void */ function robotstxt_docmd_delete_transients_by_prefix( string $prefix ): void { global $wpdb; // Sanitize prefix for LIKE query. $transient_prefix = $wpdb->esc_like( '_transient_' . $prefix ) . '%'; $transient_timeout_prefix = $wpdb->esc_like( '_transient_timeout_' . $prefix ) . '%'; // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Required for bulk pattern-based deletion; no WordPress API available for this operation. // Delete transient values. $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s", $transient_prefix ) ); // Delete transient timeouts. $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s", $transient_timeout_prefix ) ); // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching } // Run uninstall. robotstxt_docmd_uninstall();