Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Add fallback hydration for properties#411

Open
madmajestro wants to merge 1 commit into
igbinary:masterigbinary/igbinary:masterfrom
madmajestro:fix-fallback-hydration-of-propertiesmadmajestro/igbinary:fix-fallback-hydration-of-propertiesCopy head branch name to clipboard
Open

Add fallback hydration for properties#411
madmajestro wants to merge 1 commit into
igbinary:masterigbinary/igbinary:masterfrom
madmajestro:fix-fallback-hydration-of-propertiesmadmajestro/igbinary:fix-fallback-hydration-of-propertiesCopy head branch name to clipboard

Conversation

@madmajestro

@madmajestro madmajestro commented Feb 23, 2026

Copy link
Copy Markdown

If the visibility of a property has changed between serialization and unserialization, or if a private or protected property needs to be restored, but the serialized data was generated by a __serialize method while no __unserialize method exists in the class, the property value cannot be found during hydration in the object's hash table. The reason for this is that in these cases the property name in the serialized representation does not match the prefixed/mangled property name of the current class definition. To resolve this, an attempt is made to determine the correctly prefixed property name by performing a lookup with the unprefixed property name in the properties_info hash table of the class.The implementation is borrowed from the current version of php-src/ext/standard/var_unserializer.re to align the behavior to the serializer of PHP >= 7.2. See 7cb5bdf64a95bd70623d33d6ea122c13b01113bd / php/php-src#2494 for the initial implementation in PHP 7.2.

Closes #410
Closes #387
Closes #156

@stof

stof commented Feb 24, 2026

Copy link
Copy Markdown

I would suggest adding another test where both __serialize and __unserialize are implemented, to make sure the new logic does not break that case.

Comment thread src/php7/igbinary.c Outdated
int mangle_property_names = 0;

#if PHP_VERSION_ID >= 80000
if (ce->__serialize) mangle_property_names = 1;

@tricky tricky Feb 24, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have not yet reviewed actual substance yet, but for style consistency; please use an explicit comparison and braces:

if (ce->__serialize != NULL) {
...
}

Also please use tabs for indentation.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm now using an explicit comparison and curly braces, but I've already used tabs in the C code and spaces in the PHPT tests, just like the other __serialize*.phpt tests do. Should I still use tabs in the tests?

@tricky

tricky commented Feb 24, 2026

Copy link
Copy Markdown
Member

I would suggest adding another test where both __serialize and __unserialize are implemented, to make sure the new logic does not break that case.

Indeed this is very much required, though there are quite a few tests already. Tests can always be improved.

@madmajestro madmajestro force-pushed the fix-fallback-hydration-of-properties branch from 157ec93 to 3973a6a Compare February 24, 2026 22:22
@madmajestro

Copy link
Copy Markdown
Author

I've added a test to check the behavior of __unserialize. Additionally, the values ​​in the __serialize and __unserialize methods are now multiplied by 10. This allows to check if the __serialize / __unserialize functions were actually used.

@madmajestro madmajestro force-pushed the fix-fallback-hydration-of-properties branch from 3973a6a to 7e01f54 Compare February 25, 2026 15:02
@madmajestro

Copy link
Copy Markdown
Author

I have just pushed an optimized version. This version uses the mangled representation of the property name already present in zend_property_info. This saves the memory allocation and string copying done in zend_mangle_property_name().

@madmajestro madmajestro force-pushed the fix-fallback-hydration-of-properties branch from 7e01f54 to 31f2cf3 Compare February 26, 2026 16:08
@madmajestro

Copy link
Copy Markdown
Author

@tricky
Friendly Ping

@madmajestro

Copy link
Copy Markdown
Author

Hm, i just saw that #387 and maybe #156 could also be resolved with this PR, if i implement the fallback logic in a different way. I will push a new version to resolve that as well...

@madmajestro madmajestro force-pushed the fix-fallback-hydration-of-properties branch from 31f2cf3 to b15eb2a Compare March 3, 2026 09:21
@madmajestro madmajestro changed the title Fix fallback hydration of private and protected properties Add fallback hydration for properties Mar 3, 2026
@madmajestro

Copy link
Copy Markdown
Author

I have now implemented the fallback hydration, as it exists in PHP's internal serializer since PHP 7.2. This should resolve several issues. In addition, the behavior of igbinary hopefully should now be identical to PHP's serializer, allowing it to be used again as a drop-in replacement.

@madmajestro madmajestro requested a review from tricky March 3, 2026 09:43
@madmajestro madmajestro force-pushed the fix-fallback-hydration-of-properties branch from b15eb2a to 1d0cb26 Compare March 4, 2026 09:11
@madmajestro

Copy link
Copy Markdown
Author

@tricky
Friendly Ping

@madmajestro madmajestro force-pushed the fix-fallback-hydration-of-properties branch from 1d0cb26 to a78b797 Compare March 14, 2026 00:36
@madmajestro

Copy link
Copy Markdown
Author

I've re-analyzed the behavior of the PHP serializer and Igbinary once more and noticed a few edge cases where PHP and Igbinary behaved differently. I've fixed these issues and added / revised tests so that the behavior of PHP and Igbinary is now tested and compared during the tests. I hope this simplifies the review process and helps to identify differences between PHP and Igbinary in the future.

@madmajestro

Copy link
Copy Markdown
Author

Friendly ping

@madmajestro

Copy link
Copy Markdown
Author

Friendly Ping

Comment thread src/php7/igbinary.c Outdated
if ((info->flags & ZEND_ACC_VIRTUAL)) {
php_error_docref(NULL, E_WARNING,
"Cannot unserialize value for virtual property %s::$%s",
ZSTR_VAL(info->ce->name), ZSTR_VAL(key_str));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When this branch fires for a private or protected virtual property, key_str is still the mangled zend_string (e.g. \0Class\0prop for private, \0*\0prop for protected). ZSTR_VAL(key_str) then contains embedded NULs, and %s in php_error_docref truncates at the first NUL — the user sees Cannot unserialize value for virtual property Test::$ followed by garbage/nothing instead of the property name.

The included __serialize_025.phpt only exercises a public virtual property, so this case is not covered by tests. Suggest using info->name (the unmangled name from properties_info) or the local unmangled_prop:

"Cannot unserialize value for virtual property %s::$%s",
ZSTR_VAL(info->ce->name), ZSTR_VAL(info->name));

@andypost andypost Apr 29, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test to add

--TEST--
Virtual-property warning is truncated when key was originally protected/private
--SKIPIF--
<?php if (PHP_VERSION_ID < 80400) { echo "skip virtual properties need PHP 8.4+"; } ?>
--FILE--
<?php
// Bytes generated once (PHP 8.5) from:
//     class Test { protected int $prop = 1; }
//     bin2hex(igbinary_serialize(new Test()));
// 17 04 "Test" | 14 01 (array,1) | 11 07 \0*\0prop | 06 01 (int 1)
$data = hex2bin('0000000217045465737414011107002a0070726f700601');

// Same class name, but `prop` is now a public *virtual* property.
class Test {
    public int $prop { 
        get => 1;
    }
}

igbinary_unserialize($data);
?>
--EXPECTF--
Warning: igbinary_unserialize(): Cannot unserialize value for virtual property Test::$prop in %s on line %d

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for suggesting a fix and a test!

@andypost

Copy link
Copy Markdown

Stylistic nit, not blocking — a few lines below the new fallback block, the existing code reassigns info via zend_get_typed_property_info_for_slot(...) (around src/php7/igbinary.c:2881):

info = zend_get_typed_property_info_for_slot(Z_OBJ_P(z_deref), prototype_value);

When the fallback above populated info from ce->properties_info, that value is unconditionally overwritten here. Functionally correct (the type-source registration below needs the typed-property variant), but it is easy to miss that the fallback info is only used to recover the property name and never flows past this point. A one-line comment like /* override fallback info; below we need the typed-property variant */ next to that assignment would save the next reader a minute of head-scratching.

@madmajestro

Copy link
Copy Markdown
Author

Stylistic nit, not blocking — a few lines below the new fallback block, the existing code reassigns info via zend_get_typed_property_info_for_slot(...) (around src/php7/igbinary.c:2881):

I am now using two different variables. That makes it clearer.

@madmajestro madmajestro force-pushed the fix-fallback-hydration-of-properties branch from fbbe73c to 5dc295d Compare May 4, 2026 18:31
If the visibility of a property has changed between serialization and
unserialization, or if a private or protected property needs to be
restored, but the serialized data was generated by a __serialize method
while no __unserialize method exists in the class, the property value
cannot be found during hydration in the object's hash table. The reason
for this is that in these cases the property name in the serialized
representation does not match the prefixed/mangled property name of the
current class definition. To resolve this, an attempt is made to
determine the correctly prefixed property name by performing a lookup
with the unprefixed property name in the properties_info hash table
of the class. The implementation is borrowed from the current version of
php-src/ext/standard/var_unserializer.re to align the behavior to the
serializer of PHP >= 7.2. See 7cb5bdf64a95bd70623d33d6ea122c13b01113bd /
php/php-src#2494 for the initial implementation in PHP 7.2.
@madmajestro madmajestro force-pushed the fix-fallback-hydration-of-properties branch from 5dc295d to aa92730 Compare May 4, 2026 18:46
@dshafik

dshafik commented Jun 4, 2026

Copy link
Copy Markdown

This issue is blocking our upgrade to PHP 8.5, is there anything blocking this being merged and a final 3.2.17 release being created?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

5 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.