minor #28295 [VarExporter] optimize dumped code in time and space (nicolas-grekas)

This PR was merged into the 4.2-dev branch.

Discussion
----------

[VarExporter] optimize dumped code in time and space

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

Let's squeeze some more µs when running exported code.
On a simple case run 100k times with a few objects, I go from 1.8s to 1.5s.
The generated exports are also a bit smaller if it matters.

This works by:
- using local variables instead of manually dealing with a stack
- creating more optimized object hydrators for internal classes

This PR also fixes handling of hard references that are bound to external variables.

Commits
-------

07e90d71d8 [VarExporter] optimize dumped code in time and space
This commit is contained in:
Fabien Potencier 2018-09-04 08:34:59 +02:00
commit cabbf51af4
20 changed files with 317 additions and 228 deletions

View File

@ -48,10 +48,11 @@ class Exporter
$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 @@ class Exporter
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 @@ class Exporter
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 @@ class Exporter
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 @@ class Exporter
$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 @@ class Exporter
}
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();
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 (Registry::$cloneable[$class]) {
$code .= $subIndent.'clone \\'.Registry::class.'::$prototypes['.$c."],\n";
} elseif (Registry::$instantiableWithoutConstructor[$class]) {
$code .= $subIndent.'\\'.Registry::class.'::$reflectors['.$c."]->newInstanceWithoutConstructor(),\n";
} else {
$code .= $subIndent.'\\'.Registry::class.'::$reflectors['.$c."]->newInstance(),\n";
}
}
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);
}
return '\\'.Registry::class.'::push('.$code.')';
return self::exportRegistry($value, $indent, $subIndent);
}
if ($value instanceof Configurator) {
$code = '';
foreach ($value->properties as $class => $properties) {
$code .= $subIndent.' \\'.$class.'::class => '.self::export($properties, $subIndent.' ').",\n";
}
$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)."::pop(\n".$subIndent.implode(",\n".$subIndent, $code)."\n".$indent.')';
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]) {
++$prototypesAccess;
$code .= 'clone $p'.$c;
} else {
++$factoriesAccess;
$code .= '$f'.$c.'()';
}
} else {
$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;
}
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 ($serializables) {
$code = $r.'::unserialize(['.$code.'], '.self::export($serializables, $indent).')';
} else {
$code = '['.$code.']';
}
return '$o = '.$code;
}
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";
}
$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);

View File

@ -16,9 +16,9 @@ namespace Symfony\Component\VarExporter\Internal;
*
* @internal
*/
class Configurator
class Hydrator
{
public static $configurators = array();
public static $hydrators = array();
public $registry;
public $values;
@ -35,12 +35,10 @@ class Configurator
$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 @@ class Configurator
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 @@ class Configurator
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 @@ class Configurator
}
};
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 @@ class Configurator
}
$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);
}
}
};

View File

@ -19,9 +19,12 @@ namespace Symfony\Component\VarExporter\Internal;
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;
}
}

View File

@ -18,11 +18,9 @@ namespace Symfony\Component\VarExporter\Internal;
*/
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 @@ class Registry
}
}
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)

View File

@ -1,12 +1,9 @@
<?php
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
\Symfony\Component\VarExporter\Internal\Registry::push([
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\ArrayIterator::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\ArrayIterator::class, true, true),
], [
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\ArrayIterator::class],
], [
]),
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
$o = [
clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes[\ArrayIterator::class] ?? \Symfony\Component\VarExporter\Internal\Registry::p(\ArrayIterator::class, true)),
],
null,
[
\ArrayIterator::class => [
@ -20,6 +17,6 @@ return \Symfony\Component\VarExporter\Internal\Configurator::pop(
],
],
],
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
$o[0],
[]
);

View File

@ -1,12 +1,9 @@
<?php
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
\Symfony\Component\VarExporter\Internal\Registry::push([
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\Symfony\Component\VarExporter\Tests\MyArrayObject::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\Symfony\Component\VarExporter\Tests\MyArrayObject::class, true, true),
], [
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\Symfony\Component\VarExporter\Tests\MyArrayObject::class],
], [
]),
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
$o = [
clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes[\Symfony\Component\VarExporter\Tests\MyArrayObject::class] ?? \Symfony\Component\VarExporter\Internal\Registry::p(\Symfony\Component\VarExporter\Tests\MyArrayObject::class, true)),
],
null,
[
\ArrayObject::class => [
@ -20,6 +17,6 @@ return \Symfony\Component\VarExporter\Internal\Configurator::pop(
],
],
],
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
$o[0],
[]
);

View File

@ -1,13 +1,10 @@
<?php
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
\Symfony\Component\VarExporter\Internal\Registry::push([
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\ArrayObject::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\ArrayObject::class, true, true),
], [
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\ArrayObject::class],
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\ArrayObject::class],
], [
]),
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
$o = [
clone (($p =& \Symfony\Component\VarExporter\Internal\Registry::$prototypes)[\ArrayObject::class] ?? \Symfony\Component\VarExporter\Internal\Registry::p(\ArrayObject::class, true)),
clone $p[\ArrayObject::class],
],
null,
[
\ArrayObject::class => [
@ -15,16 +12,18 @@ return \Symfony\Component\VarExporter\Internal\Configurator::pop(
[
[
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],
[]
);

View File

@ -1,19 +1,15 @@
<?php
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
\Symfony\Component\VarExporter\Internal\Registry::push([
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\Symfony\Component\VarExporter\Tests\MyCloneable::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\Symfony\Component\VarExporter\Tests\MyCloneable::class, true, false),
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\Symfony\Component\VarExporter\Tests\MyNotCloneable::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\Symfony\Component\VarExporter\Tests\MyNotCloneable::class, true, false),
], [
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\Symfony\Component\VarExporter\Tests\MyCloneable::class]->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],
],
[]
);

View File

@ -1,15 +1,12 @@
<?php
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
\Symfony\Component\VarExporter\Internal\Registry::push([
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\DateTime::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\DateTime::class, true, true),
], [
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\DateTime::class],
], [
]),
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
$o = [
clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes[\DateTime::class] ?? \Symfony\Component\VarExporter\Internal\Registry::p(\DateTime::class, true)),
],
null,
[
\DateTime::class => [
'*' => [
'date' => [
'1970-01-01 00:00:00.000000',
],
@ -21,7 +18,7 @@ return \Symfony\Component\VarExporter\Internal\Configurator::pop(
],
],
],
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
$o[0],
[
1 => 0,
]

View File

@ -0,0 +1,30 @@
<?php
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
$o = [
(\Symfony\Component\VarExporter\Internal\Registry::$factories[\Error::class] ?? \Symfony\Component\VarExporter\Internal\Registry::f(\Error::class, true))(),
],
null,
[
\TypeError::class => [
'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,
]
);

View File

@ -0,0 +1,7 @@
<?php
return [
[
123,
],
];

View File

@ -1,15 +1,16 @@
<?php
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
\Symfony\Component\VarExporter\Internal\Registry::push([], [], []),
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
$o = [],
[
\Symfony\Component\VarExporter\Internal\Registry::$references[1] = [
&\Symfony\Component\VarExporter\Internal\Registry::$references[1],
$r = [],
$r[1] = [
&$r[1],
],
],
[],
[
&\Symfony\Component\VarExporter\Internal\Registry::$references[1],
&$r[1],
],
[]
);

View File

@ -1,20 +1,18 @@
<?php
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
\Symfony\Component\VarExporter\Internal\Registry::push([
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\stdClass::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\stdClass::class, true, true),
], [
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\stdClass::class],
], [
]),
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
$o = [
clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes[\stdClass::class] ?? \Symfony\Component\VarExporter\Internal\Registry::p(\stdClass::class, true)),
],
[
\Symfony\Component\VarExporter\Internal\Registry::$references[1] = \Symfony\Component\VarExporter\Internal\Registry::$objects[0],
$r = [],
$r[1] = $o[0],
],
[],
[
&\Symfony\Component\VarExporter\Internal\Registry::$references[1],
&\Symfony\Component\VarExporter\Internal\Registry::$references[1],
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
&$r[1],
&$r[1],
$o[0],
],
[]
);

View File

@ -1,11 +1,11 @@
<?php
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
\Symfony\Component\VarExporter\Internal\Registry::push([], [], [
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
$o = \Symfony\Component\VarExporter\Internal\Registry::unserialize([], [
'O:20:"SomeNotExistingClass":0:{}',
]),
null,
[],
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
$o[0],
[]
);

View File

@ -1,34 +1,26 @@
<?php
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
\Symfony\Component\VarExporter\Internal\Registry::push([
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\Symfony\Component\VarExporter\Tests\MyPrivateValue::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\Symfony\Component\VarExporter\Tests\MyPrivateValue::class, true, true),
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\Symfony\Component\VarExporter\Tests\MyPrivateChildValue::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\Symfony\Component\VarExporter\Tests\MyPrivateChildValue::class, true, true),
], [
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\Symfony\Component\VarExporter\Tests\MyPrivateValue::class],
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\Symfony\Component\VarExporter\Tests\MyPrivateChildValue::class],
], [
]),
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
$o = [
clone (($p =& \Symfony\Component\VarExporter\Internal\Registry::$prototypes)[\Symfony\Component\VarExporter\Tests\MyPrivateValue::class] ?? \Symfony\Component\VarExporter\Internal\Registry::p(\Symfony\Component\VarExporter\Tests\MyPrivateValue::class, true)),
clone ($p[\Symfony\Component\VarExporter\Tests\MyPrivateChildValue::class] ?? \Symfony\Component\VarExporter\Internal\Registry::p(\Symfony\Component\VarExporter\Tests\MyPrivateChildValue::class, true)),
],
null,
[
\Symfony\Component\VarExporter\Tests\MyPrivateValue::class => [
'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],
],
[]
);

View File

@ -1,14 +1,14 @@
<?php
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
\Symfony\Component\VarExporter\Internal\Registry::push([], [], [
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
$o = \Symfony\Component\VarExporter\Internal\Registry::unserialize([], [
'C:50:"Symfony\\Component\\VarExporter\\Tests\\MySerializable":3:{123}',
]),
null,
[],
[
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
$o[0],
$o[0],
],
[]
);

View File

@ -1,25 +1,21 @@
<?php
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
\Symfony\Component\VarExporter\Internal\Registry::push([
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\SplObjectStorage::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\SplObjectStorage::class, true, true),
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\stdClass::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\stdClass::class, true, true),
], [
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\SplObjectStorage::class],
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\stdClass::class],
], [
]),
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
$o = [
clone (($p =& \Symfony\Component\VarExporter\Internal\Registry::$prototypes)[\SplObjectStorage::class] ?? \Symfony\Component\VarExporter\Internal\Registry::p(\SplObjectStorage::class, true)),
clone ($p[\stdClass::class] ?? \Symfony\Component\VarExporter\Internal\Registry::p(\stdClass::class, true)),
],
null,
[
\SplObjectStorage::class => [
"\0" => [
[
\Symfony\Component\VarExporter\Internal\Registry::$objects[1],
$o[1],
345,
],
],
],
],
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
$o[0],
[]
);

View File

@ -1,28 +1,28 @@
<?php
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
\Symfony\Component\VarExporter\Internal\Registry::push([
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\Symfony\Component\VarExporter\Tests\MyWakeup::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\Symfony\Component\VarExporter\Tests\MyWakeup::class, true, true),
], [
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\Symfony\Component\VarExporter\Tests\MyWakeup::class],
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\Symfony\Component\VarExporter\Tests\MyWakeup::class],
], [
]),
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
$o = [
clone (($p =& \Symfony\Component\VarExporter\Internal\Registry::$prototypes)[\Symfony\Component\VarExporter\Tests\MyWakeup::class] ?? \Symfony\Component\VarExporter\Internal\Registry::p(\Symfony\Component\VarExporter\Tests\MyWakeup::class, true)),
clone $p[\Symfony\Component\VarExporter\Tests\MyWakeup::class],
],
null,
[
\Symfony\Component\VarExporter\Tests\MyWakeup::class => [
'*' => [
'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,
]
);

View File

@ -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 @@ class VarExporterTest extends TestCase
{
$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 @@ class VarExporterTest extends TestCase
$this->assertSame($serializedValue, serialize($value));
$dump = "<?php\n\nreturn ".$marshalledValue.";\n";
$dump = str_replace(var_export(__FILE__, true), "\\dirname(__DIR__).\\DIRECTORY_SEPARATOR.'VarExporterTest.php'", $dump);
$fixtureFile = __DIR__.'/Fixtures/'.$testName.'.php';
$this->assertStringEqualsFile($fixtureFile, $dump);
if ('incomplete-class' === $testName) {
if ('incomplete-class' === $testName || 'external-references' === $testName) {
return;
}
$marshalledValue = include $fixtureFile;
@ -146,6 +147,24 @@ class VarExporterTest extends TestCase
$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);
}
}

View File

@ -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 @@ final class VarExporter
} 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 @@ final class VarExporter
}
}
$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);
}