From a557bdc0c93f2cacc0907e22c6ede4e0bb557ed6 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 22 Nov 2021 11:04:59 +0100 Subject: [PATCH] [VarExporter] fix exporting declared but unset properties when __sleep() is implemented --- .../VarExporter/Internal/Exporter.php | 31 +++++++++---------- .../VarExporter/Tests/VarExporterTest.php | 7 +++++ 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/Symfony/Component/VarExporter/Internal/Exporter.php b/src/Symfony/Component/VarExporter/Internal/Exporter.php index 0d7310c3fb094..4c49d87da61bf 100644 --- a/src/Symfony/Component/VarExporter/Internal/Exporter.php +++ b/src/Symfony/Component/VarExporter/Internal/Exporter.php @@ -90,13 +90,12 @@ public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount $properties = []; $sleep = null; - $arrayValue = (array) $value; $proto = Registry::$prototypes[$class]; if (($value instanceof \ArrayIterator || $value instanceof \ArrayObject) && null !== $proto) { // ArrayIterator and ArrayObject need special care because their "flags" // option changes the behavior of the (array) casting operator. - $properties = self::getArrayObjectProperties($value, $arrayValue, $proto); + [$arrayValue, $properties] = self::getArrayObjectProperties($value, $proto); // populates Registry::$prototypes[$class] with a new instance Registry::getClassReflector($class, Registry::$instantiableWithoutConstructor[$class], Registry::$cloneable[$class]); @@ -108,25 +107,23 @@ public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount $properties[] = $value[$v]; } $properties = ['SplObjectStorage' => ["\0" => $properties]]; + $arrayValue = (array) $value; } elseif ($value instanceof \Serializable || $value instanceof \__PHP_Incomplete_Class) { ++$objectsCount; $objectsPool[$value] = [$id = \count($objectsPool), serialize($value), [], 0]; $value = new Reference($id); goto handle_value; - } - - if (method_exists($class, '__sleep')) { - if (!\is_array($sleep = $value->__sleep())) { - trigger_error('serialize(): __sleep should return an array only containing the names of instance-variables to serialize', \E_USER_NOTICE); - $value = null; - goto handle_value; - } - foreach ($sleep as $name) { - if (property_exists($value, $name) && !$reflector->hasProperty($name)) { - $arrayValue[$name] = $value->$name; + } else { + if (method_exists($class, '__sleep')) { + if (!\is_array($sleep = $value->__sleep())) { + trigger_error('serialize(): __sleep should return an array only containing the names of instance-variables to serialize', \E_USER_NOTICE); + $value = null; + goto handle_value; } + $sleep = array_flip($sleep); } - $sleep = array_flip($sleep); + + $arrayValue = (array) $value; } $proto = (array) $proto; @@ -370,13 +367,13 @@ private static function exportHydrator(Hydrator $value, string $indent, string $ * @param \ArrayIterator|\ArrayObject $value * @param \ArrayIterator|\ArrayObject $proto */ - private static function getArrayObjectProperties($value, array &$arrayValue, $proto): array + private static function getArrayObjectProperties($value, $proto): array { $reflector = $value instanceof \ArrayIterator ? 'ArrayIterator' : 'ArrayObject'; $reflector = Registry::$reflectors[$reflector] ?? Registry::getClassReflector($reflector); $properties = [ - $arrayValue, + $arrayValue = (array) $value, $reflector->getMethod('getFlags')->invoke($value), $value instanceof \ArrayObject ? $reflector->getMethod('getIteratorClass')->invoke($value) : 'ArrayIterator', ]; @@ -402,6 +399,6 @@ private static function getArrayObjectProperties($value, array &$arrayValue, $pr $properties = [$reflector->class => ["\0" => $properties]]; } - return $properties; + return [$arrayValue, $properties]; } } diff --git a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php index 73b9f163e99b2..fffccd4b53db1 100644 --- a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php +++ b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php @@ -332,6 +332,13 @@ public function setFlags($flags): void class GoodNight { + public $good; + + public function __construct() + { + unset($this->good); + } + public function __sleep(): array { $this->good = 'night';