8000 [VarExporter] optimize dumped code in time and space · symfony/symfony@c467ec2 · GitHub
[go: up one dir, main page]

Skip to content

Commit c467ec2

Browse files
[VarExporter] optimize dumped code in time and space
1 parent a1aee05 commit c467ec2

20 files changed

+246
-165
lines changed

src/Symfony/Component/VarExporter/Internal/Configurator.php

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ public function __construct(?Registry $registry, ?Values $values, array $propert
3737

3838
public static function pop($objects, $values, $properties, $value, $wakeups)
3939
{
40- F438
list(Registry::$objects, Registry::$references) = \array_pop(Registry::$stack);
41-
4240
foreach ($properties as $class => $vars) {
4341
(self::$configurators[$class] ?? self::getConfigurator($class))($vars, $objects);
4442
}
@@ -51,16 +49,20 @@ public static function pop($objects, $values, $properties, $value, $wakeups)
5149

5250
public static function getConfigurator($class)
5351
{
54-
$classReflector = Registry::$reflectors[$class] ?? Registry::getClassReflector($class);
55-
56-
if (!$classReflector->isInternal()) {
57-
return self::$configurators[$class] = \Closure::bind(function ($properties, $objects) {
52+
if ('*' === $class) {
53+
return self::$configurators[$class] = static function ($properties, $objects) {
5854
foreach ($properties as $name => $values) {
5955
foreach ($values as $i => $v) {
6056
$objects[$i]->$name = $v;
6157
}
6258
}
63-
}, null, $class);
59+
};
60+
}
61+
62+
$classReflector = Registry::$reflectors[$class] ?? Registry::getClassReflector($class);
63+
64+
if (!$classReflector->isInternal()) {
65+
return self::$configurators[$class] = (self::$configurators['*'] ?? self::getConfigurator('*'))->bindTo(null, $class);
6466
}
6567

6668
switch ($class) {
@@ -81,6 +83,14 @@ public static function getConfigurator($class)
8183
}
8284
};
8385

86+
case 'ErrorException':
87+
return self::$configurators[$class] = (self::$configurators['*'] ?? self::getConfigurator('*'))->bindTo(null, new class() extends \ErrorException {
88+
});
89+
90+
case 'TypeError':
91+
return self::$configurators[$class] = (self::$configurators['*'] ?? self::getConfigurator('*'))->bindTo(null, new class() extends \Error {
92+
});
93+
8494
case 'SplObjectStorage':
8595
return self::$configurators[$class] = static function ($properties, $objects) {
8696
foreach ($properties as $name => $values) {
@@ -100,7 +110,7 @@ public static function getConfigurator($class)
100110
}
101111

102112
$propertyReflectors = array();
103-
foreach ($classReflector->getProperties(\ReflectionProperty::IS_PROTECTED | \ReflectionProperty::IS_PRIVATE) as $propertyReflector) {
113+
foreach ($classReflector->getProperties() as $propertyReflector) {
104114
if (!$propertyReflector->isStatic()) {
105115
$propertyReflector->setAccessible(true);
106116
$propertyReflectors[$propertyReflector->name] = $propertyReflector;
@@ -109,14 +119,9 @@ public static function getConfigurator($class)
109119

110120
return self::$configurators[$class] = static function ($properties, $objects) use ($propertyReflectors) {
111121
foreach ($properties as $name => $values) {
112-
if (isset($propertyReflectors[$name])) {
113-
foreach ($values as $i => $v) {
114-
$propertyReflectors[$name]->setValue($objects[$i], $v);
115-
}
116-
} else {
117-
foreach ($values as $i => $v) {
118-
$objects[$i]->$name = $v;
119-
}
122+
$p = $propertyReflectors[$name];
123+
foreach ($values as $i => $v) {
124+
$p->setValue($objects[$i], $v);
120125
}
121126
}
122127
};

src/Symfony/Component/VarExporter/Internal/Exporter.php

Lines changed: 68 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,11 @@ public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount
4848
$refs[$k] = $value = $values[$k];
4949
if ($value instanceof Reference && 0 > $value->id) {
5050
$valuesAreStatic = false;
51+
++$value->count;
5152
continue;
5253
}
5354
$refsPool[] = array(&$refs[$k], $value, &$value);
54-
$refs[$k] = $values[$k] = new Reference(-\count($refsPool));
55+
$refs[$k] = $values[$k] = new Reference(-\count($refsPool), $value);
5556
}
5657

5758
if (\is_array($value)) {
@@ -80,12 +81,13 @@ public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount
8081
Registry::getClassReflector($class);
8182
serialize(Registry::$prototypes[$class]);
8283
}
84+
$reflector = Registry::$reflectors[$class];
8385
$proto = Registry::$prototypes[$class];
8486

8587
if ($value instanceof \ArrayIterator || $value instanceof \ArrayObject) {
8688
// ArrayIterator and ArrayObject need special care because their "flags"
8789
// option changes the behavior of the (array) casting operator.
88-
$proto = Registry::$cloneable[$class] ? clone Registry::$prototypes[$class] : Registry::$reflectors[$class]->newInstanceWithoutConstructor();
90+
$proto = Registry::$cloneable[$class] ? clone Registry::$prototypes[$class] : $reflector->newInstanceWithoutConstructor();
8991
$properties = self::getArrayObjectProperties($value, $arrayValue, $proto);
9092
} elseif ($value instanceof \SplObjectStorage) {
9193
// By implementing Serializable, SplObjectStorage breaks internal references,
@@ -116,20 +118,29 @@ public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount
116118
foreach ($arrayValue as $name => $v) {
117119
$n = (string) $name;
118120
if ('' === $n || "\0" !== $n[0]) {
119-
$c = $class;
121+
$c = '*';
122+
$properties[$c][$n] = $v;
123+
unset($sleep[$n]);
120124
} elseif ('*' === $n[1]) {
121-
$c = $class;
122125
$n = substr($n, 3);
126+
$c = $reflector->getProperty($n)->class;
127+
if ('Error' === $c) {
128+
$c = 'TypeError';
129+
} elseif ('Exception' === $c) {
130+
$c = 'ErrorException';
131+
}
132+
$properties[$c][$n] = $v;
133+
unset($sleep[$n]);
123134
} else {
124135
$i = strpos($n, "\0", 2);
125136
$c = substr($n, 1, $i - 1);
126137
$n = substr($n, 1 + $i);
127-
}
128-
if (null === $sleep) {
129-
$properties[$c][$n] = $v;
130-
} elseif (isset($sleep[$n]) && $c === $class) {
131-
$properties[$c][$n] = $v;
132-
unset($sleep[$n]);
138+
if (null === $sleep) {
139+
$properties[$c][$n] = $v;
140+
} elseif (isset($sleep[$n]) && $c === $class) {
141+
$properties[$c][$n] = $v;
142+
unset($sleep[$n]);
143+
}
133144
}
134145
if (\array_key_exists($name, $proto) && $proto[$name] === $v) {
135146
unset($properties[$c][$n]);
@@ -173,11 +184,14 @@ public static function export($value, $indent = '')
173184

174185
if ($value instanceof Reference) {
175186
if (0 <= $value->id) {
176-
return '\\'.Registry::class.'::$objects['.$value->id.']';
187+
return '$o['.$value->id.']';
188+
}
189+
if (!$value->count) {
190+
return self::export($value->value, $indent);
177191
}
178192
$value = -$value->id;
179193

180-
return '&\\'.Registry::class.'::$references['.$value.']';
194+
return '&$r['.$value.']';
181195
}
182196
$subIndent = $indent.' ';
183197

@@ -224,9 +238,9 @@ public static function export($value, $indent = '')
224238
}
225239

226240
if ($value instanceof Values) {
227-
$code = '';
241+
$code = $subIndent."\$r = [],\n";
228242
foreach ($value->values as $k => $v) {
229-
$code .= $subIndent.'\\'.Registry::class.'::$references['.$k.'] = '.self::export($v, $subIndent).",\n";
243+
$code .= $subIndent.'$r['.$k.'] = '.self::export($v, $subIndent).",\n";
230244
}
231245

232246
return "[\n".$code.$indent.']';
@@ -236,42 +250,64 @@ public static function export($value, $indent = '')
236250
$code = '';
237251
$reflectors = array();
238252
$serializables = array();
253+
$seen = array();
254+
$prototypesAccess = 0;
255+
$factoriesAccess = 0;
256+
$r = '\\'.Registry::class;
239257

240258
foreach ($value as $k => $class) {
241259
if (':' === ($class[1] ?? null)) {
242260
$serializables[$k] = $class;
243261
continue;
244262
}
245-
$c = '\\'.$class.'::class';
246-
$reflectors[$class] = '\\'.Registry::class.'::$reflectors['.$c.'] ?? \\'.Registry::class.'::getClassReflector('.$c.', '
247-
.self::export(Registry::$instantiableWithoutConstructor[$class]).', '
248-
.self::export(Registry::$cloneable[$class])
249-
.')';
250-
251-
if (Registry::$cloneable[$class]) {
252-
$code .= $subIndent.'clone \\'.Registry::class.'::$prototypes['.$c."],\n";
253-
} elseif (Registry::$instantiableWithoutConstructor[$class]) {
254-
$code .= $subIndent.'\\'.Registry::class.'::$reflectors['.$c."]->newInstanceWithoutConstructor(),\n";
263+
$code .= $subIndent.$k.' => ';
264+
265+
$c = '[\\'.$class.'::class]';
266+
if ($seen[$class] ?? false) {
267+
if (Registry::$cloneable[$class]) {
268+
++$prototypesAccess;
269+
$code .= 'clone $p'.$c.",\n";
270+
} else {
271+
++$factoriesAccess;
272+
$code .= '$f'.$c."(),\n";
273+
}
255274
} else {
256-
$code .= $subIndent.'\\'.Registry::class.'::$reflectors['.$c."]->newInstance(),\n";
275+
$seen[$class] = true;
276+
$k = '('.substr($c, 1, -1).', '.self::export(Registry::$instantiableWithoutConstructor[$class]).'))';
277+
278+
if (Registry::$cloneable[$class]) {
279+
$code .= 'clone ('.($prototypesAccess++ ? '$p' : '($p =& '.$r.'::$prototypes)').$c.' ?? '.$r.'::p'.$k.",\n";
280+
} else {
281+
$code .= '('.($factoriesAccess++ ? '$f' : '($f =& '.$r.'::$factories)').$c.' ?? '.$r.'::f'.$k."(),\n";
282+
}
257283
}
258284
}
259285

260-
if ($reflectors) {
261-
$code = "[\n".$subIndent.implode(",\n".$subIndent, $reflectors).",\n".$indent."], [\n".$code.$indent.'], ';
262-
$code .= !$serializables ? "[\n".$indent.']' : self::export($serializables, $indent);
286+
if (1 === $prototypesAccess) {
287+
$code = str_replace('($p =& '.$r.'::$prototypes)', $r.'::$prototypes', $code);
288+
}
289+
if (1 === $factoriesAccess) {
290+
$code = str_replace('($f =& '.$r.'::$factories)', $r.'::$factories', $code);
291+
}
292+
293+
if ('' !== $code) {
294+
$code = "\n".$code.$indent;
295+
}
296+
297+
if ($serializables) {
298+
$code = $r.'::unserialize(['.$code.'], '.self::export($serializables, $indent).')';
263299
} else {
264-
$code = '[], []';
265-
$code .= ', '.self::export($serializables, $indent);
300+
$code = '['.$code.']';
266301
}
267302

268-
return '\\'.Registry::class.'::push('.$code.')';
303+
return '$o = '.$code;
269304
}
270305

271306
if ($value instanceof Configurator) {
272307
$code = '';
273308
foreach ($value->properties as $class => $properties) {
274-
$code .= $subIndent.' \\'.$class.'::class => '.self::export($properties, $subIndent.' ').",\n";
309+
$c = '*' !== $class ? '\\'.$class.'::class' : "'*'";
310+
$code .= $subIndent.' '.$c.' => '.self::export($properties, $subIndent.' ').",\n";
275311
}
276312

277313
$code = array(

src/Symfony/Component/VarExporter/Internal/Reference.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@
1919
class Reference
2020
{
2121
public $id;
22+
public $value;
23+
public $count = 0;
2224

23-
public function __construct(int $id)
25+
public function __construct(int $id, $value = null)
2426
{
2527
$this->id = $id;
28+
$this->value = $value;
2629
}
2730
}

src/Symfony/Component/VarExporter/Internal/Registry.php

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,9 @@
1818
*/
1919
class Registry
2020
{
21-
public static $stack = array();
22-
public static $objects = array();
23-
public static $references = array();
2421
public static $reflectors = array();
2522
public static $prototypes = array();
23+
public static $factories = array();
2624
public static $cloneable = array();
2725
public static $instantiableWithoutConstructor = array();
2826

@@ -33,28 +31,33 @@ public function __construct(array $classes)
3331
}
3432
}
3533

36-
public static function push($reflectors, $objects, $serializables)
34+
public static function unserialize($objects, $serializables)
3735
{
38-
self::$stack[] = array(self::$objects, self::$references);
39-
self::$references = array();
40-
41-
if (!$serializables) {
42-
return self::$objects = $objects;
43-
}
4436
$unserializeCallback = ini_set('unserialize_callback_func', __CLASS__.'::getClassReflector');
4537

4638
try {
4739
foreach ($serializables as $k => $v) {
4840
$objects[$k] = unserialize($v);
4941
}
50-
} catch (\Throwable $e) {
51-
list(self::$objects, self::$references) = array_pop(self::$stack);
52-
throw $e;
5342
} finally {
5443
ini_set('unserialize_callback_func', $unserializeCallback);
5544
}
5645

57-
return self::$objects = $objects;
46+
return $objects;
47+
}
48+
49+
public static function p($class, $instantiableWithoutConstructor)
50+
{
51+
self::getClassReflector($class, $instantiableWithoutConstructor, true);
52+
53+
return self::$prototypes[$class];
54+
}
55+
56+
public static function f($class, $instantiableWithoutConstructor)
57+
{
58+
$reflector = self::$reflectors[$class] ?? self::getClassReflector($class, $instantiableWithoutConstructor, false);
59+
60+
return self::$factories[$class] = \Closure::fromCallable(array($reflector, $instantiableWithoutConstructor ? 'newInstanceWithoutConstructor' : 'newInstance'));
5861
}
5962

6063
public static function getClassReflector($class, $instantiableWithoutConstructor = null, $cloneable = null)
Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
<?php
22

33
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
4-
\Symfony\Component\VarExporter\Internal\Registry::push([
5-
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\ArrayIterator::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\ArrayIterator::class, true, true),
6-
], [
7-
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\ArrayIterator::class],
8-
], [
9-
]),
4+
$o = [
5+
0 => clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes[\ArrayIterator::class] ?? \Symfony\Component\VarExporter\Internal\Registry::p(\ArrayIterator::class, true)),
6+
],
107
null,
118
[
129
\ArrayIterator::class => [
@@ -20,6 +17,6 @@
2017
],
2118
],
2219
],
23-
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
20+
$o[0],
2421
[]
2522
);
Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
<?php
22

33
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
4-
\Symfony\Component\VarExporter\Internal\Registry::push([
5-
\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),
6-
], [
7-
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\Symfony\Component\VarExporter\Tests\MyArrayObject::class],
8-
], [
9-
]),
4+
$o = [
5+
0 => 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)),
6+
],
107
null,
118
[
129
\ArrayObject::class => [
@@ -20,6 +17,6 @@
2017
],
2118
],
2219
],
23-
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
20+
$o[0],
2421
[]
2522
);

0 commit comments

Comments
 (0)
0