From 07e90d71d87629f95b9aa4127bcdaaf007a3e8bd Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 28 Aug 2018 07:54:23 +0200 Subject: [PATCH] [VarExporter] optimize dumped code in time and space --- .../VarExporter/Internal/Exporter.php | 172 +++++++++++------- .../{Configurator.php => Hydrator.php} | 53 +++--- .../VarExporter/Internal/Reference.php | 5 +- .../VarExporter/Internal/Registry.php | 31 ++-- .../Tests/Fixtures/array-iterator.php | 13 +- .../Tests/Fixtures/array-object-custom.php | 13 +- .../Tests/Fixtures/array-object.php | 21 +-- .../VarExporter/Tests/Fixtures/clone.php | 18 +- .../VarExporter/Tests/Fixtures/datetime.php | 15 +- .../VarExporter/Tests/Fixtures/error.php | 30 +++ .../Tests/Fixtures/external-references.php | 7 + .../Fixtures/hard-references-recursive.php | 11 +- .../Tests/Fixtures/hard-references.php | 20 +- .../Tests/Fixtures/incomplete-class.php | 6 +- .../VarExporter/Tests/Fixtures/private.php | 24 +-- .../Tests/Fixtures/serializable.php | 8 +- .../Tests/Fixtures/spl-object-storage.php | 18 +- .../VarExporter/Tests/Fixtures/wakeup.php | 24 +-- .../VarExporter/Tests/VarExporterTest.php | 25 ++- .../Component/VarExporter/VarExporter.php | 13 +- 20 files changed, 308 insertions(+), 219 deletions(-) rename src/Symfony/Component/VarExporter/Internal/{Configurator.php => Hydrator.php} (66%) create mode 100644 src/Symfony/Component/VarExporter/Tests/Fixtures/error.php create mode 100644 src/Symfony/Component/VarExporter/Tests/Fixtures/external-references.php diff --git a/src/Symfony/Component/VarExporter/Internal/Exporter.php b/src/Symfony/Component/VarExporter/Internal/Exporter.php index 5ba406d800e1..69d1259e1613 100644 --- a/src/Symfony/Component/VarExporter/Internal/Exporter.php +++ b/src/Symfony/Component/VarExporter/Internal/Exporter.php @@ -48,10 +48,11 @@ public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount $refs[$k] = $value = $values[$k]; if ($value instanceof Reference && 0 > $value->id) { $valuesAreStatic = false; + ++$value->count; continue; } $refsPool[] = array(&$refs[$k], $value, &$value); - $refs[$k] = $values[$k] = new Reference(-\count($refsPool)); + $refs[$k] = $values[$k] = new Reference(-\count($refsPool), $value); } if (\is_array($value)) { @@ -80,12 +81,13 @@ public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount Registry::getClassReflector($class); serialize(Registry::$prototypes[$class]); } + $reflector = Registry::$reflectors[$class]; $proto = Registry::$prototypes[$class]; if ($value instanceof \ArrayIterator || $value instanceof \ArrayObject) { // ArrayIterator and ArrayObject need special care because their "flags" // option changes the behavior of the (array) casting operator. - $proto = Registry::$cloneable[$class] ? clone Registry::$prototypes[$class] : Registry::$reflectors[$class]->newInstanceWithoutConstructor(); + $proto = Registry::$cloneable[$class] ? clone Registry::$prototypes[$class] : $reflector->newInstanceWithoutConstructor(); $properties = self::getArrayObjectProperties($value, $arrayValue, $proto); } elseif ($value instanceof \SplObjectStorage) { // By implementing Serializable, SplObjectStorage breaks internal references, @@ -116,20 +118,29 @@ public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount foreach ($arrayValue as $name => $v) { $n = (string) $name; if ('' === $n || "\0" !== $n[0]) { - $c = $class; + $c = '*'; + $properties[$c][$n] = $v; + unset($sleep[$n]); } elseif ('*' === $n[1]) { - $c = $class; $n = substr($n, 3); + $c = $reflector->getProperty($n)->class; + if ('Error' === $c) { + $c = 'TypeError'; + } elseif ('Exception' === $c) { + $c = 'ErrorException'; + } + $properties[$c][$n] = $v; + unset($sleep[$n]); } else { $i = strpos($n, "\0", 2); $c = substr($n, 1, $i - 1); $n = substr($n, 1 + $i); - } - if (null === $sleep) { - $properties[$c][$n] = $v; - } elseif (isset($sleep[$n]) && $c === $class) { - $properties[$c][$n] = $v; - unset($sleep[$n]); + if (null === $sleep) { + $properties[$c][$n] = $v; + } elseif (isset($sleep[$n]) && $c === $class) { + $properties[$c][$n] = $v; + unset($sleep[$n]); + } } if (\array_key_exists($name, $proto) && $proto[$name] === $v) { unset($properties[$c][$n]); @@ -173,11 +184,14 @@ public static function export($value, $indent = '') if ($value instanceof Reference) { if (0 <= $value->id) { - return '\\'.Registry::class.'::$objects['.$value->id.']'; + return '$o['.$value->id.']'; + } + if (!$value->count) { + return self::export($value->value, $indent); } $value = -$value->id; - return '&\\'.Registry::class.'::$references['.$value.']'; + return '&$r['.$value.']'; } $subIndent = $indent.' '; @@ -213,9 +227,11 @@ public static function export($value, $indent = '') $code = ''; foreach ($value as $k => $v) { $code .= $subIndent; - if ($k !== ++$j) { + if (!\is_int($k) || 1 !== $k - $j) { $code .= self::export($k, $subIndent).' => '; - $j = INF; + } + if (\is_int($k)) { + $j = $k; } $code .= self::export($v, $subIndent).",\n"; } @@ -224,82 +240,110 @@ public static function export($value, $indent = '') } if ($value instanceof Values) { - $code = ''; + $code = $subIndent."\$r = [],\n"; foreach ($value->values as $k => $v) { - $code .= $subIndent.'\\'.Registry::class.'::$references['.$k.'] = '.self::export($v, $subIndent).",\n"; + $code .= $subIndent.'$r['.$k.'] = '.self::export($v, $subIndent).",\n"; } return "[\n".$code.$indent.']'; } if ($value instanceof Registry) { - $code = ''; - $reflectors = array(); - $serializables = array(); + return self::exportRegistry($value, $indent, $subIndent); + } - foreach ($value as $k => $class) { - if (':' === ($class[1] ?? null)) { - $serializables[$k] = $class; - continue; - } - $c = '\\'.$class.'::class'; - $reflectors[$class] = '\\'.Registry::class.'::$reflectors['.$c.'] ?? \\'.Registry::class.'::getClassReflector('.$c.', ' - .self::export(Registry::$instantiableWithoutConstructor[$class]).', ' - .self::export(Registry::$cloneable[$class]) - .')'; + if ($value instanceof Hydrator) { + return self::exportHydrator($value, $indent, $subIndent); + } + + throw new \UnexpectedValueException(sprintf('Cannot export value of type "%s".', \is_object($value) ? \get_class($value) : \gettype($value))); + } + + private static function exportRegistry(Registry $value, string $indent, string $subIndent): string + { + $code = ''; + $reflectors = array(); + $serializables = array(); + $seen = array(); + $prototypesAccess = 0; + $factoriesAccess = 0; + $r = '\\'.Registry::class; + $j = -1; + + foreach ($value as $k => $class) { + if (':' === ($class[1] ?? null)) { + $serializables[$k] = $class; + continue; + } + $code .= $subIndent.(1 !== $k - $j ? $k.' => ' : ''); + $j = $k; + $eol = ",\n"; + $c = '[\\'.$class.'::class]'; + if ($seen[$class] ?? false) { if (Registry::$cloneable[$class]) { - $code .= $subIndent.'clone \\'.Registry::class.'::$prototypes['.$c."],\n"; - } elseif (Registry::$instantiableWithoutConstructor[$class]) { - $code .= $subIndent.'\\'.Registry::class.'::$reflectors['.$c."]->newInstanceWithoutConstructor(),\n"; + ++$prototypesAccess; + $code .= 'clone $p'.$c; } else { - $code .= $subIndent.'\\'.Registry::class.'::$reflectors['.$c."]->newInstance(),\n"; + ++$factoriesAccess; + $code .= '$f'.$c.'()'; } - } - - if ($reflectors) { - $code = "[\n".$subIndent.implode(",\n".$subIndent, $reflectors).",\n".$indent."], [\n".$code.$indent.'], '; - $code .= !$serializables ? "[\n".$indent.']' : self::export($serializables, $indent); } else { - $code = '[], []'; - $code .= ', '.self::export($serializables, $indent); + $seen[$class] = true; + if (Registry::$cloneable[$class]) { + $code .= 'clone ('.($prototypesAccess++ ? '$p' : '($p =& '.$r.'::$prototypes)').$c.' ?? '.$r.'::p'; + } else { + $code .= '('.($factoriesAccess++ ? '$f' : '($f =& '.$r.'::$factories)').$c.' ?? '.$r.'::f'; + $eol = '()'.$eol; + } + $code .= '('.substr($c, 1, -1).', '.self::export(Registry::$instantiableWithoutConstructor[$class]).'))'; } + $code .= $eol; + } - return '\\'.Registry::class.'::push('.$code.')'; + if (1 === $prototypesAccess) { + $code = str_replace('($p =& '.$r.'::$prototypes)', $r.'::$prototypes', $code); + } + if (1 === $factoriesAccess) { + $code = str_replace('($f =& '.$r.'::$factories)', $r.'::$factories', $code); + } + if ('' !== $code) { + $code = "\n".$code.$indent; } - if ($value instanceof Configurator) { - $code = ''; - foreach ($value->properties as $class => $properties) { - $code .= $subIndent.' \\'.$class.'::class => '.self::export($properties, $subIndent.' ').",\n"; - } + if ($serializables) { + $code = $r.'::unserialize(['.$code.'], '.self::export($serializables, $indent).')'; + } else { + $code = '['.$code.']'; + } - $code = array( - self::export($value->registry, $subIndent), - self::export($value->values, $subIndent), - '' !== $code ? "[\n".$code.$subIndent.']' : '[]', - self::export($value->value, $subIndent), - self::export($value->wakeups, $subIndent), - ); + return '$o = '.$code; + } - return '\\'.\get_class($value)."::pop(\n".$subIndent.implode(",\n".$subIndent, $code)."\n".$indent.')'; + private static function exportHydrator(Hydrator $value, string $indent, string $subIndent): string + { + $code = ''; + foreach ($value->properties as $class => $properties) { + $c = '*' !== $class ? '\\'.$class.'::class' : "'*'"; + $code .= $subIndent.' '.$c.' => '.self::export($properties, $subIndent.' ').",\n"; } - throw new \UnexpectedValueException(sprintf('Cannot export value of type "%s".', \is_object($value) ? \get_class($value) : \gettype($value))); + $code = array( + self::export($value->registry, $subIndent), + self::export($value->values, $subIndent), + '' !== $code ? "[\n".$code.$subIndent.']' : '[]', + self::export($value->value, $subIndent), + self::export($value->wakeups, $subIndent), + ); + + return '\\'.\get_class($value)."::hydrate(\n".$subIndent.implode(",\n".$subIndent, $code)."\n".$indent.')'; } /** - * Extracts the state of an ArrayIterator or ArrayObject instance. - * - * For performance this method is public and has no type-hints. - * * @param \ArrayIterator|\ArrayObject $value - * @param array &$arrayValue - * @param object $proto - * - * @return array + * @param \ArrayIterator|\ArrayObject $proto */ - public static function getArrayObjectProperties($value, &$arrayValue, $proto) + private static function getArrayObjectProperties($value, array &$arrayValue, $proto): array { $reflector = $value instanceof \ArrayIterator ? 'ArrayIterator' : 'ArrayObject'; $reflector = Registry::$reflectors[$reflector] ?? Registry::getClassReflector($reflector); diff --git a/src/Symfony/Component/VarExporter/Internal/Configurator.php b/src/Symfony/Component/VarExporter/Internal/Hydrator.php similarity index 66% rename from src/Symfony/Component/VarExporter/Internal/Configurator.php rename to src/Symfony/Component/VarExporter/Internal/Hydrator.php index f1925ad59d26..6dd6075d2545 100644 --- a/src/Symfony/Component/VarExporter/Internal/Configurator.php +++ b/src/Symfony/Component/VarExporter/Internal/Hydrator.php @@ -16,9 +16,9 @@ * * @internal */ -class Configurator +class Hydrator { - public static $configurators = array(); + public static $hydrators = array(); public $registry; public $values; @@ -35,12 +35,10 @@ public function __construct(?Registry $registry, ?Values $values, array $propert $this->wakeups = $wakeups; } - public static function pop($objects, $values, $properties, $value, $wakeups) + public static function hydrate($objects, $values, $properties, $value, $wakeups) { - list(Registry::$objects, Registry::$references) = \array_pop(Registry::$stack); - foreach ($properties as $class => $vars) { - (self::$configurators[$class] ?? self::getConfigurator($class))($vars, $objects); + (self::$hydrators[$class] ?? self::getHydrator($class))($vars, $objects); } foreach ($wakeups as $i) { $objects[$i]->__wakeup(); @@ -49,18 +47,22 @@ public static function pop($objects, $values, $properties, $value, $wakeups) return $value; } - public static function getConfigurator($class) + public static function getHydrator($class) { - $classReflector = Registry::$reflectors[$class] ?? Registry::getClassReflector($class); - - if (!$classReflector->isInternal()) { - return self::$configurators[$class] = \Closure::bind(function ($properties, $objects) { + if ('*' === $class) { + return self::$hydrators[$class] = static function ($properties, $objects) { foreach ($properties as $name => $values) { foreach ($values as $i => $v) { $objects[$i]->$name = $v; } } - }, null, $class); + }; + } + + $classReflector = Registry::$reflectors[$class] ?? Registry::getClassReflector($class); + + if (!$classReflector->isInternal()) { + return self::$hydrators[$class] = (self::$hydrators['*'] ?? self::getHydrator('*'))->bindTo(null, $class); } switch ($class) { @@ -68,7 +70,7 @@ public static function getConfigurator($class) case 'ArrayObject': $constructor = $classReflector->getConstructor(); - return self::$configurators[$class] = static function ($properties, $objects) use ($constructor) { + return self::$hydrators[$class] = static function ($properties, $objects) use ($constructor) { foreach ($properties as $name => $values) { if ("\0" !== $name) { foreach ($values as $i => $v) { @@ -81,8 +83,16 @@ public static function getConfigurator($class) } }; + case 'ErrorException': + return self::$hydrators[$class] = (self::$hydrators['*'] ?? self::getHydrator('*'))->bindTo(null, new class() extends \ErrorException { + }); + + case 'TypeError': + return self::$hydrators[$class] = (self::$hydrators['*'] ?? self::getHydrator('*'))->bindTo(null, new class() extends \Error { + }); + case 'SplObjectStorage': - return self::$configurators[$class] = static function ($properties, $objects) { + return self::$hydrators[$class] = static function ($properties, $objects) { foreach ($properties as $name => $values) { if ("\0" === $name) { foreach ($values as $i => $v) { @@ -100,23 +110,18 @@ public static function getConfigurator($class) } $propertyReflectors = array(); - foreach ($classReflector->getProperties(\ReflectionProperty::IS_PROTECTED | \ReflectionProperty::IS_PRIVATE) as $propertyReflector) { + foreach ($classReflector->getProperties() as $propertyReflector) { if (!$propertyReflector->isStatic()) { $propertyReflector->setAccessible(true); $propertyReflectors[$propertyReflector->name] = $propertyReflector; } } - return self::$configurators[$class] = static function ($properties, $objects) use ($propertyReflectors) { + return self::$hydrators[$class] = static function ($properties, $objects) use ($propertyReflectors) { foreach ($properties as $name => $values) { - if (isset($propertyReflectors[$name])) { - foreach ($values as $i => $v) { - $propertyReflectors[$name]->setValue($objects[$i], $v); - } - } else { - foreach ($values as $i => $v) { - $objects[$i]->$name = $v; - } + $p = $propertyReflectors[$name]; + foreach ($values as $i => $v) { + $p->setValue($objects[$i], $v); } } }; diff --git a/src/Symfony/Component/VarExporter/Internal/Reference.php b/src/Symfony/Component/VarExporter/Internal/Reference.php index 4f39c1ff3040..e371c07b8834 100644 --- a/src/Symfony/Component/VarExporter/Internal/Reference.php +++ b/src/Symfony/Component/VarExporter/Internal/Reference.php @@ -19,9 +19,12 @@ class Reference { public $id; + public $value; + public $count = 0; - public function __construct(int $id) + public function __construct(int $id, $value = null) { $this->id = $id; + $this->value = $value; } } diff --git a/src/Symfony/Component/VarExporter/Internal/Registry.php b/src/Symfony/Component/VarExporter/Internal/Registry.php index 46c57b5c3fde..f09df4368181 100644 --- a/src/Symfony/Component/VarExporter/Internal/Registry.php +++ b/src/Symfony/Component/VarExporter/Internal/Registry.php @@ -18,11 +18,9 @@ */ class Registry { - public static $stack = array(); - public static $objects = array(); - public static $references = array(); public static $reflectors = array(); public static $prototypes = array(); + public static $factories = array(); public static $cloneable = array(); public static $instantiableWithoutConstructor = array(); @@ -33,28 +31,33 @@ public function __construct(array $classes) } } - public static function push($reflectors, $objects, $serializables) + public static function unserialize($objects, $serializables) { - self::$stack[] = array(self::$objects, self::$references); - self::$references = array(); - - if (!$serializables) { - return self::$objects = $objects; - } $unserializeCallback = ini_set('unserialize_callback_func', __CLASS__.'::getClassReflector'); try { foreach ($serializables as $k => $v) { $objects[$k] = unserialize($v); } - } catch (\Throwable $e) { - list(self::$objects, self::$references) = array_pop(self::$stack); - throw $e; } finally { ini_set('unserialize_callback_func', $unserializeCallback); } - return self::$objects = $objects; + return $objects; + } + + public static function p($class, $instantiableWithoutConstructor) + { + self::getClassReflector($class, $instantiableWithoutConstructor, true); + + return self::$prototypes[$class]; + } + + public static function f($class, $instantiableWithoutConstructor) + { + $reflector = self::$reflectors[$class] ?? self::getClassReflector($class, $instantiableWithoutConstructor, false); + + return self::$factories[$class] = \Closure::fromCallable(array($reflector, $instantiableWithoutConstructor ? 'newInstanceWithoutConstructor' : 'newInstance')); } public static function getClassReflector($class, $instantiableWithoutConstructor = null, $cloneable = null) diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/array-iterator.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/array-iterator.php index 8cdd56034903..c53b14c86790 100644 --- a/src/Symfony/Component/VarExporter/Tests/Fixtures/array-iterator.php +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/array-iterator.php @@ -1,12 +1,9 @@ [ @@ -20,6 +17,6 @@ ], ], ], - \Symfony\Component\VarExporter\Internal\Registry::$objects[0], + $o[0], [] ); diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/array-object-custom.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/array-object-custom.php index 0a67a15268bb..6e0f71a5aa5d 100644 --- a/src/Symfony/Component/VarExporter/Tests/Fixtures/array-object-custom.php +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/array-object-custom.php @@ -1,12 +1,9 @@ [ @@ -20,6 +17,6 @@ ], ], ], - \Symfony\Component\VarExporter\Internal\Registry::$objects[0], + $o[0], [] ); diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/array-object.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/array-object.php index 97c51427eec8..33f8388e1bc4 100644 --- a/src/Symfony/Component/VarExporter/Tests/Fixtures/array-object.php +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/array-object.php @@ -1,13 +1,10 @@ [ @@ -15,16 +12,18 @@ [ [ 1, - \Symfony\Component\VarExporter\Internal\Registry::$objects[0], + $o[0], ], 0, ], ], + ], + '*' => [ 'foo' => [ - \Symfony\Component\VarExporter\Internal\Registry::$objects[1], + $o[1], ], ], ], - \Symfony\Component\VarExporter\Internal\Registry::$objects[0], + $o[0], [] ); diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/clone.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/clone.php index 49d70a7268e3..da77dab65e2d 100644 --- a/src/Symfony/Component/VarExporter/Tests/Fixtures/clone.php +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/clone.php @@ -1,19 +1,15 @@ newInstanceWithoutConstructor(), - \Symfony\Component\VarExporter\Internal\Registry::$reflectors[\Symfony\Component\VarExporter\Tests\MyNotCloneable::class]->newInstanceWithoutConstructor(), - ], [ - ]), +return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( + $o = [ + (($f =& \Symfony\Component\VarExporter\Internal\Registry::$factories)[\Symfony\Component\VarExporter\Tests\MyCloneable::class] ?? \Symfony\Component\VarExporter\Internal\Registry::f(\Symfony\Component\VarExporter\Tests\MyCloneable::class, true))(), + ($f[\Symfony\Component\VarExporter\Tests\MyNotCloneable::class] ?? \Symfony\Component\VarExporter\Internal\Registry::f(\Symfony\Component\VarExporter\Tests\MyNotCloneable::class, true))(), + ], null, [], [ - \Symfony\Component\VarExporter\Internal\Registry::$objects[0], - \Symfony\Component\VarExporter\Internal\Registry::$objects[1], + $o[0], + $o[1], ], [] ); diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/datetime.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/datetime.php index 10ef69412cec..8287e154dd48 100644 --- a/src/Symfony/Component/VarExporter/Tests/Fixtures/datetime.php +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/datetime.php @@ -1,15 +1,12 @@ [ + '*' => [ 'date' => [ '1970-01-01 00:00:00.000000', ], @@ -21,7 +18,7 @@ ], ], ], - \Symfony\Component\VarExporter\Internal\Registry::$objects[0], + $o[0], [ 1 => 0, ] diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/error.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/error.php new file mode 100644 index 000000000000..e36b547bd851 --- /dev/null +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/error.php @@ -0,0 +1,30 @@ + [ + 'file' => [ + \dirname(__DIR__).\DIRECTORY_SEPARATOR.'VarExporterTest.php', + ], + 'line' => [ + 234, + ], + ], + \Error::class => [ + 'trace' => [ + [ + 'file' => \dirname(__DIR__).\DIRECTORY_SEPARATOR.'VarExporterTest.php', + 'line' => 123, + ], + ], + ], + ], + $o[0], + [ + 1 => 0, + ] +); diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/external-references.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/external-references.php new file mode 100644 index 000000000000..0405b59a8f25 --- /dev/null +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/external-references.php @@ -0,0 +1,7 @@ + [ 'prot' => [ 123, + 123, ], 'priv' => [ 234, 234, ], ], - \Symfony\Component\VarExporter\Tests\MyPrivateChildValue::class => [ - 'prot' => [ - 1 => 123, - ], - ], ], [ - \Symfony\Component\VarExporter\Internal\Registry::$objects[0], - \Symfony\Component\VarExporter\Internal\Registry::$objects[1], + $o[0], + $o[1], ], [] ); diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/serializable.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/serializable.php index da735d6e2e0c..fcf32278b068 100644 --- a/src/Symfony/Component/VarExporter/Tests/Fixtures/serializable.php +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/serializable.php @@ -1,14 +1,14 @@ [ "\0" => [ [ - \Symfony\Component\VarExporter\Internal\Registry::$objects[1], + $o[1], 345, ], ], ], ], - \Symfony\Component\VarExporter\Internal\Registry::$objects[0], + $o[0], [] ); diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/wakeup.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/wakeup.php index 236203ae3d54..73be0345b0b5 100644 --- a/src/Symfony/Component/VarExporter/Tests/Fixtures/wakeup.php +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/wakeup.php @@ -1,28 +1,28 @@ [ + '*' => [ 'sub' => [ - \Symfony\Component\VarExporter\Internal\Registry::$objects[1], + $o[1], 123, ], + 'bis' => [ + 1 => 123, + ], 'baz' => [ 1 => 123, ], ], ], - \Symfony\Component\VarExporter\Internal\Registry::$objects[0], + $o[0], [ 1 => 1, - 2 => 0, + 0, ] ); diff --git a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php index b49717053353..2ca437d347b6 100644 --- a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php +++ b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php @@ -12,9 +12,9 @@ namespace Symfony\Component\VarExporter\Tests; use PHPUnit\Framework\TestCase; +use Symfony\Component\VarDumper\Test\VarDumperTestTrait; use Symfony\Component\VarExporter\Internal\Registry; use Symfony\Component\VarExporter\VarExporter; -use Symfony\Component\VarDumper\Test\VarDumperTestTrait; class VarExporterTest extends TestCase { @@ -28,7 +28,7 @@ public function testPhpIncompleteClassesAreForbidden() { $unserializeCallback = ini_set('unserialize_callback_func', 'var_dump'); try { - Registry::push(array(), array(), array('O:20:"SomeNotExistingClass":0:{}')); + Registry::unserialize(array(), array('O:20:"SomeNotExistingClass":0:{}')); } finally { $this->assertSame('var_dump', ini_set('unserialize_callback_func', $unserializeCallback)); } @@ -80,10 +80,11 @@ public function testMarshall(string $testName, $value, bool $staticValueExpected $this->assertSame($serializedValue, serialize($value)); $dump = "assertStringEqualsFile($fixtureFile, $dump); - if ('incomplete-class' === $testName) { + if ('incomplete-class' === $testName || 'external-references' === $testName) { return; } $marshalledValue = include $fixtureFile; @@ -146,6 +147,24 @@ public function provideMarshall() $value[0] = &$value; yield array('hard-references-recursive', $value); + + static $value = array(123); + + yield array('external-references', array(&$value), true); + + unset($value); + + $value = new \Error(); + + $r = new \ReflectionProperty('Error', 'trace'); + $r->setAccessible(true); + $r->setValue($value, array('file' => __FILE__, 'line' => 123)); + + $r = new \ReflectionProperty('Error', 'line'); + $r->setAccessible(true); + $r->setValue($value, 234); + + yield array('error', $value); } } diff --git a/src/Symfony/Component/VarExporter/VarExporter.php b/src/Symfony/Component/VarExporter/VarExporter.php index 2bed1532f324..ad89359a7b06 100644 --- a/src/Symfony/Component/VarExporter/VarExporter.php +++ b/src/Symfony/Component/VarExporter/VarExporter.php @@ -11,9 +11,8 @@ namespace Symfony\Component\VarExporter; -use Symfony\Component\VarExporter\Internal\Configurator; use Symfony\Component\VarExporter\Internal\Exporter; -use Symfony\Component\VarExporter\Internal\Reference; +use Symfony\Component\VarExporter\Internal\Hydrator; use Symfony\Component\VarExporter\Internal\Registry; use Symfony\Component\VarExporter\Internal\Values; @@ -56,8 +55,10 @@ public static function export($value, bool &$isStaticValue = null): string } finally { $references = array(); foreach ($refsPool as $i => $v) { + if ($v[0]->count) { + $references[1 + $i] = $v[2]; + } $v[0] = $v[1]; - $references[1 + $i] = $v[2]; } } @@ -85,7 +86,11 @@ public static function export($value, bool &$isStaticValue = null): string } } - $value = new Configurator(new Registry($classes), $references ? new Values($references) : null, $properties, $value, $wakeups); + if ($classes || $references) { + $value = new Hydrator(new Registry($classes), $references ? new Values($references) : null, $properties, $value, $wakeups); + } else { + $isStaticValue = true; + } return Exporter::export($value); }