fix: catch duplicate declarations within a batch of snippets - #492
Merged
imantsk merged 1 commit intoAug 31, 2026
Merged
Conversation
imantsk
changed the base branch from
core
to
fix/bulk-activate-validates-php-only/core
August 31, 2026 17:21
imantsk
force-pushed
the
fix/bulk-activate-validates-php-only/core
branch
from
August 31, 2026 17:37
43a73be to
f7e6831
Compare
imantsk
force-pushed
the
fix/batch-duplicate-identifiers/core
branch
from
August 31, 2026 17:37
c8cc985 to
11a86a0
Compare
Two snippets declaring the same function could both be activated in one action, and the site fataled on the very next request. Each snippet was validated on its own, against whatever PHP had declared at that moment. That set does not include the other snippets in the same batch, so neither saw the other and both passed. The redeclaration then happened at runtime, inside eval(), where it is a fatal error rather than a catchable one: the front end and the admin both returned 500, leaving no way to reach the snippets screen and switch one off. Carry the identifiers claimed by accepted snippets through the batch, so the second snippet to claim a name is held back rather than activated alongside the first. Guarded declarations are unaffected, since a function_exists() check is already recorded as an exception and cannot fatal. Anonymous functions claim no name. Scripts and stylesheets are not considered at all, as they share no namespace with PHP. Relates to #230.
imantsk
force-pushed
the
fix/bulk-activate-validates-php-only/core
branch
from
August 31, 2026 17:44
f7e6831 to
27ce583
Compare
imantsk
force-pushed
the
fix/batch-duplicate-identifiers/core
branch
from
August 31, 2026 17:44
11a86a0 to
538e4dd
Compare
imantsk
merged commit Aug 31, 2026
391f619
into
fix/bulk-activate-validates-php-only/core
13 of 14 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Addresses part of #230, and it is worse than that issue describes: this does not merely allow duplicate names, it takes the site down and locks you out of the admin.
The bug
Select two snippets that declare the same function, choose Activate, press Apply. Both activate. The next request fatals.
Each snippet is validated on its own, against whatever PHP has declared at that moment. That set does not include the other snippets in the same batch, so neither sees the other and both pass.
Why this is not a warning-level problem
The redeclaration happens at runtime inside
eval(), where it is a fatal error, not a catchable one.execute_snippet()wraps execution intry/catch ( Throwable )and the catch never fires — I checked.Measured on WP 7.1:
So you cannot get to the snippets screen to switch one off. And
?snippets-safe-mode=1, the documented escape hatch, is itself broken until #484 lands — the two compound.The fix
Carry the identifiers claimed by accepted snippets through the batch. The second snippet to claim a name is held back rather than activated alongside the first.
Deliberately conservative:
function_exists()check is already recorded as an exception and cannot fatal.add_filter( …, function () {} )is unaffected.Testing
6 unit tests. Two fail without the fix, four are guards:
Verified end to end as above. Full suite 170 tests,
phpcsclean.What this does not solve
The protection remains point in time. A snippet that is safe today still fatals if a plugin installed next month declares the same name, and nothing re-checks. On a modest install there are already over 5,600 declared function names, most of them from plugins, so that is a live risk rather than a theoretical one.
Options discussed, none free:
function_exists()guards, which the validator already honours. Converts a fatal into a snippet that quietly does not redeclare.add_action( 'init', 'my_func' )and$label = 'my_func';cannot be told apart. I tested this and it silently corrupts working snippets, which is worse than the loud failure it replaces.Worth deciding on #230 rather than here.