Keep the last of two keys resolving to the same property - #46
Merged
Merged
Conversation
nicolas-grekas
force-pushed
the
dynamic-parent-private
branch
from
September 29, 2026 10:28
b896d4f to
3f0c3b1
Compare
A dynamic property named like a private property of a parent class is scoped to that parent, as unserialize() writes it to that private property. With the private property set too, both were added under the same name with zend_hash_add_new(): the first value was exported instead of the last one, and debug builds of PHP aborted. The same happened with the bare and mangled names of a property returned by __serialize() without __unserialize(). Update the entry instead, so that the last key wins, as with unserialize().
nicolas-grekas
force-pushed
the
dynamic-parent-private
branch
from
September 29, 2026 10:30
3f0c3b1 to
1ceacea
Compare
nicolas-grekas
added a commit
that referenced
this pull request
Sep 29, 2026
… (nicolas-grekas) This PR was merged into the main branch. Discussion ---------- Align payloads and warnings with the polyfill and serialize() Builds on #46, and has a sibling PR in symfony/polyfill#702. `deepclone_to_array()` listed the properties and markers of objects interleaved with the ones of the objects they reference, while the polyfill lists them object by object: the same value gave different payloads, eg Doctrine's class metadata or the container builder of the Symfony Demo. Both now produce the same payloads, so that caches and exported code don't depend on which one wrote them. The names returned by `__sleep()` are resolved in their order, like `serialize()` does, so that `__unserialize()` gets them in that order too, with the warnings of `serialize()` at its levels: names returned twice and unset untyped properties were silent. This also cuts the instructions needed to export Doctrine's class metadata by 29%, while graphs of plain objects cost up to 2% more. Default values returned by `__serialize()` without `__unserialize()` are kept, as `unserialize()` writes each key in turn, integer keys aren't dropped anymore, undeclared protected ones become dynamic properties, and closed resources are reported as `Unknown resource` instead of `(null) resource`. Commits ------- b54b752 Align payloads and warnings with the polyfill and serialize()
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.
Builds on #45, for its scope fix and to not conflict in the changelog.
unserialize()writes a dynamic property named like a private property of a parent class to that private property, anddeepclone_to_array()scopes it to that parent accordingly. But when the private property is set too, both were added under the same name withzend_hash_add_new(): debug builds of PHP abort there, and release builds exported the private value whereunserialize(serialize())ends up with the dynamic one:The same happened with the bare and mangled names of a property returned by
__serialize()without__unserialize(). The last key now wins, like withunserialize()and the polyfill.