diff --git a/src/Symfony/Component/VarExporter/Instantiator.php b/src/Symfony/Component/VarExporter/Instantiator.php index 7eefc3c2d2821..06abbc75a6954 100644 --- a/src/Symfony/Component/VarExporter/Instantiator.php +++ b/src/Symfony/Component/VarExporter/Instantiator.php @@ -67,7 +67,7 @@ public static function instantiate(string $class, array $properties = [], array $wrappedInstance = [$reflector->newInstanceWithoutConstructor()]; } elseif (null === Registry::$prototypes[$class]) { throw new NotInstantiableTypeException($class); - } elseif ($reflector->implementsInterface('Serializable')) { + } elseif ($reflector->implementsInterface('Serializable') && (\PHP_VERSION_ID < 70400 || !method_exists($class, '__unserialize'))) { $wrappedInstance = [unserialize('C:'.\strlen($class).':"'.$class.'":0:{}')]; } else { $wrappedInstance = [unserialize('O:'.\strlen($class).':"'.$class.'":0:{}')]; diff --git a/src/Symfony/Component/VarExporter/Internal/Exporter.php b/src/Symfony/Component/VarExporter/Internal/Exporter.php index fae9084ca040a..74a324691ea06 100644 --- a/src/Symfony/Component/VarExporter/Internal/Exporter.php +++ b/src/Symfony/Component/VarExporter/Internal/Exporter.php @@ -74,10 +74,23 @@ public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount } $class = \get_class($value); + $reflector = Registry::$reflectors[$class] ?? Registry::getClassReflector($class); + + if ($reflector->hasMethod('__serialize')) { + if (!$reflector->getMethod('__serialize')->isPublic()) { + throw new \Error(sprintf('Call to %s method %s::__serialize()', $reflector->getMethod('__serialize')->isProtected() ? 'protected' : 'private', $class)); + } + + if (!\is_array($properties = $value->__serialize())) { + throw new \Typerror($class.'::__serialize() must return an array'); + } + + goto prepare_value; + } + $properties = []; $sleep = null; $arrayValue = (array) $value; - $reflector = Registry::$reflectors[$class] ?? Registry::getClassReflector($class); $proto = Registry::$prototypes[$class]; if (($value instanceof \ArrayIterator || $value instanceof \ArrayObject) && null !== $proto) { @@ -154,10 +167,11 @@ public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount } } + prepare_value: $objectsPool[$value] = [$id = \count($objectsPool)]; $properties = self::prepare($properties, $objectsPool, $refsPool, $objectsCount, $valueIsStatic); ++$objectsCount; - $objectsPool[$value] = [$id, $class, $properties, \method_exists($class, '__wakeup') ? $objectsCount : 0]; + $objectsPool[$value] = [$id, $class, $properties, \method_exists($class, '__unserialize') ? -$objectsCount : (\method_exists($class, '__wakeup') ? $objectsCount : 0)]; $value = new Reference($id); diff --git a/src/Symfony/Component/VarExporter/Internal/Hydrator.php b/src/Symfony/Component/VarExporter/Internal/Hydrator.php index 07721df428771..5f64adf96fb53 100644 --- a/src/Symfony/Component/VarExporter/Internal/Hydrator.php +++ b/src/Symfony/Component/VarExporter/Internal/Hydrator.php @@ -42,8 +42,12 @@ public static function hydrate($objects, $values, $properties, $value, $wakeups) foreach ($properties as $class => $vars) { (self::$hydrators[$class] ?? self::getHydrator($class))($vars, $objects); } - foreach ($wakeups as $i) { - $objects[$i]->__wakeup(); + foreach ($wakeups as $k => $v) { + if (\is_array($v)) { + $objects[-$k]->__unserialize($v); + } else { + $objects[$v]->__wakeup(); + } } return $value; diff --git a/src/Symfony/Component/VarExporter/Internal/Registry.php b/src/Symfony/Component/VarExporter/Internal/Registry.php index 31ec4a0d790e1..b5069dd16aa84 100644 --- a/src/Symfony/Component/VarExporter/Internal/Registry.php +++ b/src/Symfony/Component/VarExporter/Internal/Registry.php @@ -86,14 +86,14 @@ public static function getClassReflector($class, $instantiableWithoutConstructor $proto = $reflector->newInstanceWithoutConstructor(); $instantiableWithoutConstructor = true; } catch (\ReflectionException $e) { - $proto = $reflector->implementsInterface('Serializable') ? 'C:' : 'O:'; + $proto = $reflector->implementsInterface('Serializable') && (\PHP_VERSION_ID < 70400 || !\method_exists($class, '__unserialize')) ? 'C:' : 'O:'; if ('C:' === $proto && !$reflector->getMethod('unserialize')->isInternal()) { $proto = null; } elseif (false === $proto = @unserialize($proto.\strlen($class).':"'.$class.'":0:{}')) { throw new NotInstantiableTypeException($class); } } - if (null !== $proto && !$proto instanceof \Throwable && !$proto instanceof \Serializable && !\method_exists($class, '__sleep')) { + if (null !== $proto && !$proto instanceof \Throwable && !$proto instanceof \Serializable && !\method_exists($class, '__sleep') && (\PHP_VERSION_ID < 70400 || !\method_exists($class, '__serialize'))) { try { serialize($proto); } catch (\Exception $e) { @@ -103,7 +103,7 @@ public static function getClassReflector($class, $instantiableWithoutConstructor } if (null === $cloneable) { - if (($proto instanceof \Reflector || $proto instanceof \ReflectionGenerator || $proto instanceof \ReflectionType || $proto instanceof \IteratorIterator || $proto instanceof \RecursiveIteratorIterator) && (!$proto instanceof \Serializable && !\method_exists($proto, '__wakeup'))) { + if (($proto instanceof \Reflector || $proto instanceof \ReflectionGenerator || $proto instanceof \ReflectionType || $proto instanceof \IteratorIterator || $proto instanceof \RecursiveIteratorIterator) && (!$proto instanceof \Serializable && !\method_exists($proto, '__wakeup') && (\PHP_VERSION_ID < 70400 || !\method_exists($class, '__unserialize')))) { throw new NotInstantiableTypeException($class); } diff --git a/src/Symfony/Component/VarExporter/README.md b/src/Symfony/Component/VarExporter/README.md index c3a072127e4de..180554ed1a036 100644 --- a/src/Symfony/Component/VarExporter/README.md +++ b/src/Symfony/Component/VarExporter/README.md @@ -3,7 +3,8 @@ VarExporter Component The VarExporter component allows exporting any serializable PHP data structure to plain PHP code. While doing so, it preserves all the semantics associated with -the serialization mechanism of PHP (`__wakeup`, `__sleep`, `Serializable`). +the serialization mechanism of PHP (`__wakeup`, `__sleep`, `Serializable`, +`__serialize`, `__unserialize`). It also provides an instantiator that allows creating and populating objects without calling their constructor nor any other methods. diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/php74-serializable.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/php74-serializable.php new file mode 100644 index 0000000000000..06cfac10f55d3 --- /dev/null +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/php74-serializable.php @@ -0,0 +1,16 @@ +assertSame($staticValueExpected, $isStaticValue); - if ('var-on-sleep' !== $testName) { + if ('var-on-sleep' !== $testName && 'php74-serializable' !== $testName) { $this->assertDumpEquals($dumpedValue, $value); } @@ -199,6 +199,8 @@ public function provideExport() yield ['foo-serializable', new FooSerializable('bar')]; yield ['private-constructor', PrivateConstructor::create('bar')]; + + yield ['php74-serializable', new Php74Serializable()]; } } @@ -387,3 +389,36 @@ public function unserialize($str) list($this->foo) = unserialize($str); } } + +class Php74Serializable implements \Serializable +{ + public function __serialize() + { + return [$this->foo = new \stdClass()]; + } + + public function __unserialize(array $data) + { + list($this->foo) = $data; + } + + public function __sleep() + { + throw new \BadMethodCallException(); + } + + public function __wakeup() + { + throw new \BadMethodCallException(); + } + + public function serialize() + { + throw new \BadMethodCallException(); + } + + public function unserialize($ser) + { + throw new \BadMethodCallException(); + } +} diff --git a/src/Symfony/Component/VarExporter/VarExporter.php b/src/Symfony/Component/VarExporter/VarExporter.php index 45ef7446b12c9..da9a8d43736fa 100644 --- a/src/Symfony/Component/VarExporter/VarExporter.php +++ b/src/Symfony/Component/VarExporter/VarExporter.php @@ -69,14 +69,30 @@ public static function export($value, bool &$isStaticValue = null): string $classes = []; $values = []; - $wakeups = []; + $states = []; foreach ($objectsPool as $i => $v) { list(, $classes[], $values[], $wakeup) = $objectsPool[$v]; - if ($wakeup) { - $wakeups[$wakeup] = $i; + if (0 < $wakeup) { + $states[$wakeup] = $i; + } elseif (0 > $wakeup) { + $states[-$wakeup] = [$i, array_pop($values)]; + $values[] = []; } } - ksort($wakeups); + ksort($states); + + $wakeups = [null]; + foreach ($states as $k => $v) { + if (\is_array($v)) { + $wakeups[-$v[0]] = $v[1]; + } else { + $wakeups[] = $v; + } + } + + if (null === $wakeups[0]) { + unset($wakeups[0]); + } $properties = []; foreach ($values as $i => $vars) { pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy