php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #78410 Cannot "manually" unserialize class that is final and extends an internal one
Submitted: 2019-08-13 17:19 UTC Modified: 2019-08-13 18:01 UTC
From: nicolas dot grekas+php at gmail dot com Assigned: nikic (profile)
Status: Closed Package: *General Issues
PHP Version: 7.4.0beta2 OS:
Private report: No CVE-ID: None
 [2019-08-13 17:19 UTC] nicolas dot grekas+php at gmail dot com
Description:
------------
I'm trying to make the VarExporter component of Symfony work on PHP 7.4 and I'm in a dead-end. What the component provides is an implementation of serialize/unserialize using PHP as the serialization format. This cannot be implemented anymore in PHP 7.4 with the new __serialize/__unserialize methods.

The case that breaks is when a userland class is made final *and* extends an internal one.
E.g. final class Foo extends extends \ArrayIterator {...}

Because of these two conditions, ReflectionClass::newInstanceWithoutConstructor is forbidden. In PHP <=7.3, it is possible to work around the limitation by calling serialize() with a proper payload: either an empty "O:"-one when the class doesn't implement Serializable, or a "C:"-one when it does.

To implement the semantics of __unserialize, I cannot use "C:"-format.
And PHP 7.4 complains if I unserialize('O:8:"Foo":0:{}'). That's my dead-end: I cannot create such an instance to call __unserialize in userland.

The simplest way I can think of to unlock progress is to allow unserialize('O:8:"Foo":0:{}') to work. Doing so *would not call __unserialize* but would return an empty object, as it does for classes that don't implement the method.

Thank you for your help, this is a critical blocker for us.


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2019-08-13 17:27 UTC] nikic@php.net
I don't really understand what the intent here is ... why is it not possible to provide a correct O-format string to create the object? It's not going to work with an empty payload, but then again I don't think it worked with an empty C-payload before either ... right?

> The simplest way I can think of to unlock progress is to allow unserialize('O:8:"Foo":0:{}') to work. Doing so *would not call __unserialize* but would return an empty object, as it does for classes that don't implement the method.

In that case we should remove the Reflection restriction instead, because that's what this is effectively doing, just in a more roundabout way. But that restriction exists for a reason.
 [2019-08-13 17:30 UTC] nikic@php.net
-Status: Open +Status: Assigned -Assigned To: +Assigned To: nikic
 [2019-08-13 17:30 UTC] nikic@php.net
> In that case we should remove the Reflection restriction instead, because that's what this is effectively doing, just in a more roundabout way. But that restriction exists for a reason.

Having just looked at the implementation: The Reflection restriction is actually stricter than it needs to be. It prohibits newInstanceWithoutConstructor for final classes that *inherit* from internal classes as well, which should not the case. We should only be restricting final, internal classes.

I don't really understand your original issue, but this is something we can certainly relax...
 [2019-08-13 18:01 UTC] nicolas dot grekas+php at gmail dot com
> The Reflection restriction is actually stricter than it needs to be.

Relaxing that would definitely solve my use case too (and would remove one weird engine behavior too :)!
 [2019-08-13 18:25 UTC] nikic@php.net
Automatic comment on behalf of nikita.ppv@gmail.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=d891b5f458a253befaa56d325ae90518536fc2f0
Log: Fixed bug #78410
 [2019-08-13 18:25 UTC] nikic@php.net
-Status: Assigned +Status: Closed
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 10:01:29 2024 UTC