Skip to content

[DeepClone] Suggest the extension when refusing a named callable, and decode its global-function references#635

Merged
nicolas-grekas merged 1 commit into
1.xfrom
deepclone-suggest-ext-named-closures
Jun 11, 2026
Merged

[DeepClone] Suggest the extension when refusing a named callable, and decode its global-function references#635
nicolas-grekas merged 1 commit into
1.xfrom
deepclone-suggest-ext-named-closures

Conversation

@nicolas-grekas

@nicolas-grekas nicolas-grekas commented Jun 11, 2026

Copy link
Copy Markdown
Member
Q A
Branch? 1.x
Bug fix? no
New feature? no
Deprecations? no
Issues -
License MIT

Two related improvements to how the polyfill cooperates with the extension on first-class callables declared in constant expressions.

Refusal message. deepclone_to_array() refuses to serialize a closure over a named callable unless allow_named_closures is set. The message now warns that the option should only be enabled for trusted input, and points at the extension, which resolves more of these without the opt-in — a first-class callable declared in a constant expression, including cross-class references (Validators::check(...)) and global functions (strlen(...)), gets its declaring class recovered from reflection and is serialized as a declaration-site reference. Userland has no equivalent reflection hook, and this path only runs when the extension is absent, so the suggestion always applies.

deepclone_to_array(): serializing a closure over the named callable "strlen" requires enabling
the "allow_named_closures" option; do it only if you trust the input; alternatively, install the
"deepclone" extension, which can reference callables declared in constant expressions

Decoding the extension's global-function references. The extension can encode a reference to a global internal function declared in an attribute; such a function has no start line, so the reference is stored with line 0. The polyfill cannot produce these (no reflection hook), but must be able to decode them. ReflectionFunction::getStartLine() returns false for an internal function, so a line 0 reference was wrongly treated as stale; it is now normalized. With this, an extension-produced reference to a global internal function in an attribute round-trips through the polyfill.

The leading part of the refusal message is unchanged, so the tests (which match a substring) and any callers matching on it are unaffected.

Companion extension implementation: symfony/php-ext-deepclone#27

… decode its global-function references

Two related improvements to how the polyfill cooperates with the extension on
first-class callables declared in constant expressions.

Refusal message. deepclone_to_array() refuses to serialize a closure over a
named callable unless allow_named_closures is set. The message now warns that
the option should only be enabled for trusted input, and points at the
extension: for a first-class callable declared in a constant expression
(including cross-class references such as Validators::check(...) and global
functions such as strlen(...)) the extension recovers the declaring class from
reflection and serializes the closure as a declaration-site reference, needing
no opt-in. Userland has no equivalent reflection hook. This path only runs when
the extension is absent, so the suggestion always applies.

Decoding the extension's global-function references. The extension can encode a
reference to a global internal function declared in an attribute; such a
function has no start line, so the reference is stored with line 0. The polyfill
cannot produce these, but must decode them: ReflectionFunction reports no start
line for an internal function, so a line-0 reference is normalized rather than
treated as stale.
@nicolas-grekas nicolas-grekas force-pushed the deepclone-suggest-ext-named-closures branch from f3fb2b7 to bcc4f2b Compare June 11, 2026 15:53
@nicolas-grekas nicolas-grekas changed the title [DeepClone] Suggest the extension when refusing a first-class callable without the opt-in [DeepClone] Suggest the extension when refusing a named callable, and decode its global-function references Jun 11, 2026
@nicolas-grekas nicolas-grekas merged commit 00f9819 into 1.x Jun 11, 2026
17 of 40 checks passed
@nicolas-grekas nicolas-grekas deleted the deepclone-suggest-ext-named-closures branch June 11, 2026 15:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant