Skip to content

Commit e08201b

Browse files
[DeepClone] Support closures declared in constant expressions
PHP 8.5 allows anonymous closures in constant expressions: attribute arguments, class constants, property and parameter defaults, property hooks. They are compile-time checked to be static and capture-free, so they are fully described by their declaration site. deepclone_to_array() now encodes them under the new mask marker 1 as [class, site, attrIndex|null, closureIndex, startLine], where site is "" (the class), "NAME" (constant or enum case), "$name" (property), "name()" (method), "name()#N" (parameter) or "$name::get()" / "$name::set()#N" (property hooks). deepclone_from_array() re-evaluates the addressed constant expression through reflection, picks the Nth closure of a depth-first walk (arrays in order, objects through their array-cast properties) and verifies the declaration line still matches, so payloads that outlived a code change fail loudly ("stale payload") instead of resolving to a moved closure. Unlike the extension, which matches literals by op_array identity, the polyfill identifies them by closure name, file, line span and signature over a per-class index of every const-expr site. The closure name encodes nesting and method context, and a token-level count of closure literals per source line guards the remaining aliasing case, so a closure created at runtime never silently resolves to a const-expr literal: declaration sites the polyfill cannot tell apart are refused. When that count requires reading a file that is not there (deployments shipping only precompiled opcodes), the refusal says so and points to the extension, which needs no sources. The same literal reached through several surfaces (an attribute argument referencing a class constant, a trait alias, a promoted default applied by a constructor) resolves to the first site that exposes it; promoted properties are addressed through their constructor parameter. The payload carries no code, only names and indices. allowed_classes gates both directions: "Closure" must be allowed before any constant expression is evaluated on the to_array side, and the payload-named class must be allow-listed before it is autoloaded on the from_array side. Closures created at runtime keep throwing NotInstantiableException.
1 parent e85dd72 commit e08201b

3 files changed

Lines changed: 1046 additions & 17 deletions

File tree

0 commit comments

Comments
 (0)