From effd23a6b68cb42f56d927e5078bfe901ad93f3e Mon Sep 17 00:00:00 2001 From: Robert Staddon Date: Wed, 16 Sep 2026 21:01:50 -0500 Subject: [PATCH] fix: stop file-based execution after snippet deactivation Flush the snippet object cache before writing the on-disk index, force the snippet inactive, and replace indexes on rebuild so a deactivated snippet cannot stay marked active. Fixes #561 Co-authored-by: Cursor --- CHANGELOG.md | 2 ++ src/php/Flat_Files/Snippet_Files.php | 34 ++++++++++++++++++++++++++++ src/php/snippet-ops.php | 9 ++++++-- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e94b17f42..fc02e2740 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,8 @@ * Fixed featured Community Cloud snippets failing to load with some cloud API responses. (PRO) * Fixed bulk actions in Community Cloud running against an empty selection, so selected snippets were never downloaded. (PRO) +* Fixed file-based execution keeping a snippet active after it was deactivated, when a stale object-cache copy was + written into the on-disk index. ## [3.10.2] (2026-09-01) diff --git a/src/php/Flat_Files/Snippet_Files.php b/src/php/Flat_Files/Snippet_Files.php index 1ae9a03c5..282116508 100644 --- a/src/php/Flat_Files/Snippet_Files.php +++ b/src/php/Flat_Files/Snippet_Files.php @@ -245,6 +245,9 @@ public function deactivate_snippet( int $snippet_id, bool $network ): void { return; } + // The object-cache copy can still say active; the database row does not. + $snippet->active = false; + $table = self::get_hashed_table_name( code_snippets()->db->get_table_name( $network ) ); $base_dir = self::get_base_dir( $table, $handler->get_dir_name() ); @@ -645,12 +648,14 @@ private function create_snippet_flat_files(): void { $db = code_snippets()->db; $scopes = Snippet::get_all_scopes(); + $indexes = []; $data = $db->fetch_active_snippets( $scopes ); foreach ( $data as $snippet ) { $snippet_obj = get_snippet( $snippet['id'], $db->ms_table === $snippet['table'] ); $this->handle_snippet( $snippet_obj, $snippet['table'] ); + $this->remember_index_entry( $indexes, $snippet_obj, $snippet['table'] ); } if ( is_multisite() ) { @@ -664,6 +669,7 @@ private function create_snippet_flat_files(): void { $table_name = $snippet['table']; $snippet_obj = get_snippet( $snippet['id'], false ); $this->handle_snippet( $snippet_obj, $table_name ); + $this->remember_index_entry( $indexes, $snippet_obj, $table_name ); } restore_current_blog(); @@ -671,6 +677,34 @@ private function create_snippet_flat_files(): void { $db->set_table_vars(); } + + foreach ( $indexes as $base_dir => $snippets ) { + $this->config_repo->save( $base_dir, $snippets ); + } + } + + /** + * Record a snippet for a full index rewrite. + * + * handle_snippet() merges into the existing index, which can leave a + * deactivated snippet marked active. Replacing the index with only the + * currently active set drops those stale entries. + * + * @param array>> $indexes Index lists keyed by directory. + * @param Snippet $snippet Snippet just written. + * @param string $table Database table name. + * + * @return void + */ + private function remember_index_entry( array &$indexes, Snippet $snippet, string $table ): void { + $handler = $this->handler_registry->get_handler( $snippet->type ); + + if ( ! $handler ) { + return; + } + + $base_dir = self::get_base_dir( self::get_hashed_table_name( $table ), $handler->get_dir_name() ); + $indexes[ $base_dir ][ $snippet->id ] = $snippet->get_fields(); } /** diff --git a/src/php/snippet-ops.php b/src/php/snippet-ops.php index 379050d71..38ae13f68 100644 --- a/src/php/snippet-ops.php +++ b/src/php/snippet-ops.php @@ -562,15 +562,20 @@ function deactivate_snippet( int $id, ?bool $network = null ): ?Snippet { return null; } + // Flush before get_snippet() so file-based execution does not write a stale + // cached "active" object into wp-content/code-snippets/*/index.php. + clean_snippets_cache( $table ); + + $snippet = get_snippet( $id, $network ); + $snippet->active = false; + // Update the recently active list. - $snippet = get_snippet( $id ); $recently_active = get_self_option( $network, 'recently_active_snippets', [] ); $recently_active[ $id ] = time(); update_self_option( $network, 'recently_active_snippets', $recently_active ); update_shared_network_snippets( [ $snippet ] ); do_action( 'code_snippets/deactivate_snippet', $id, $network ); - clean_snippets_cache( $table ); return $snippet; }