Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 2df7320

Browse filesBrowse files
bug #28188 [Cache] make PhpMarshaller handle hard references (nicolas-grekas)
This PR was merged into the 4.2-dev branch. Discussion ---------- [Cache] make PhpMarshaller handle hard references | Q | A | ------------- | --- | Branch? | master | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - This PR makes the interface and behavior of `PhpMarshaller` cleaner and bullet-proof. While a bug fix at this stage, I'd like to propose splitting it to a new `VarExporter` component all goes well. Commits ------- bc5d208 [Cache] make PhpMarshaller handle hard references
2 parents df5525f + bc5d208 commit 2df7320
Copy full SHA for 2df7320
Expand file treeCollapse file tree

30 files changed

+536
-541
lines changed

‎src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php
-18Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
use Symfony\Component\Cache\CacheInterface;
1717
use Symfony\Component\Cache\CacheItem;
1818
use Symfony\Component\Cache\Exception\InvalidArgumentException;
19-
use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
2019
use Symfony\Component\Cache\PruneableInterface;
2120
use Symfony\Component\Cache\ResettableInterface;
2221
use Symfony\Component\Cache\Traits\GetTrait;
@@ -35,7 +34,6 @@ class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInte
3534
use GetTrait;
3635

3736
private $createCacheItem;
38-
private $marshaller;
3937

4038
/**
4139
* @param string $file The PHP file were values are cached
@@ -106,9 +104,6 @@ public function get(string $key, callable $callback, float $beta = null)
106104
if ($value instanceof \Closure) {
107105
return $value();
108106
}
109-
if (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
110-
return ($this->marshaller ?? $this->marshaller = new DefaultMarshaller())->unmarshall($value);
111-
}
112107
} catch (\Throwable $e) {
113108
unset($this->keys[$key]);
114109
goto get_from_pool;
@@ -144,13 +139,6 @@ public function getItem($key)
144139
$value = null;
145140
$isHit = false;
146141
}
147-
} elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
148-
try {
149-
$value = unserialize($value);
150-
} catch (\Throwable $e) {
151-
$value = null;
152-
$isHit = false;
153-
}
154142
}
155143

156144
$f = $this->createCacheItem;
@@ -284,12 +272,6 @@ private function generateItems(array $keys): \Generator
284272
} catch (\Throwable $e) {
285273
yield $key => $f($key, null, false);
286274
}
287-
} elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
288-
try {
289-
yield $key => $f($key, $this->unserializeValue($value), true);
290-
} catch (\Throwable $e) {
291-
yield $key => $f($key, null, false);
292-
}
293275
} else {
294276
yield $key => $f($key, $value, true);
295277
}

‎src/Symfony/Component/Cache/Marshaller/PhpMarshaller.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Marshaller/PhpMarshaller.php
+31-128Lines changed: 31 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
namespace Symfony\Component\Cache\Marshaller;
1313

1414
use Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator;
15+
use Symfony\Component\Cache\Marshaller\PhpMarshaller\Marshaller;
1516
use Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference;
1617
use Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry;
18+
use Symfony\Component\Cache\Marshaller\PhpMarshaller\Values;
1719

1820
/**
1921
* @author Nicolas Grekas <p@tchwork.com>
@@ -27,14 +29,31 @@
2729
*/
2830
class PhpMarshaller
2931
{
30-
public static function marshall($value, int &$objectsCount)
32+
public static function marshall($value, bool &$isStaticValue = null): string
3133
{
32-
if (!\is_object($value) && !\is_array($value)) {
33-
return $value;
34+
$isStaticValue = true;
35+
36+
if (!\is_object($value) && !(\is_array($value) && $value) && !$value instanceof \__PHP_Incomplete_Class && !\is_resource($value)) {
37+
return var_export($value, true);
3438
}
39+
3540
$objectsPool = new \SplObjectStorage();
36-
$value = array($value);
37-
$objectsCount = self::doMarshall($value, $objectsPool);
41+
$refsPool = array();
42+
$objectsCount = 0;
43+
44+
try {
45+
$value = Marshaller::marshall(array($value), $objectsPool, $refsPool, $objectsCount, $isStaticValue)[0];
46+
} finally {
47+
$references = array();
48+
foreach ($refsPool as $i => $v) {
49+
$v[0] = $v[1];
50+
$references[1 + $i] = $v[2];
51+
}
52+
}
53+
54+
if ($isStaticValue) {
55+
return var_export($value, true);
56+
}
3857

3958
$classes = array();
4059
$values = array();
@@ -46,6 +65,7 @@ public static function marshall($value, int &$objectsCount)
4665
}
4766
}
4867
ksort($wakeups);
68+
4969
$properties = array();
5070
foreach ($values as $i => $vars) {
5171
foreach ($vars as $class => $values) {
@@ -54,131 +74,14 @@ public static function marshall($value, int &$objectsCount)
5474
}
5575
}
5676
}
57-
if (!$classes) {
58-
return $value[0];
59-
}
60-
61-
return new Configurator(new Registry($classes), $properties, $value[0], $wakeups);
62-
}
63-
64-
public static function optimize(string $exportedValue)
65-
{
66-
return preg_replace(sprintf("{%s::__set_state\(array\(\s++'0' => (\d+),\s++\)\)}", preg_quote(Reference::class)), Registry::class.'::$objects[$1]', $exportedValue);
67-
}
68-
69-
private static function doMarshall(array &$array, \SplObjectStorage $objectsPool): int
70-
{
71-
$objectsCount = 0;
72-
73-
foreach ($array as &$value) {
74-
if (\is_array($value) && $value) {
75-
$objectsCount += self::doMarshall($value, $objectsPool);
76-
}
77-
if (!\is_object($value)) {
78-
continue;
79-
}
80-
if (isset($objectsPool[$value])) {
81-
++$objectsCount;
82-
$value = new Reference($objectsPool[$value][0]);
83-
continue;
84-
}
85-
$class = \get_class($value);
86-
$properties = array();
87-
$sleep = null;
88-
$arrayValue = (array) $value;
89-
$proto = (Registry::$reflectors[$class] ?? Registry::getClassReflector($class))->newInstanceWithoutConstructor();
9077

91-
if ($value instanceof \ArrayIterator || $value instanceof \ArrayObject) {
92-
// ArrayIterator and ArrayObject need special care because their "flags"
93-
// option changes the behavior of the (array) casting operator.
94-
$reflector = $value instanceof \ArrayIterator ? 'ArrayIterator' : 'ArrayObject';
95-
$reflector = Registry::$reflectors[$reflector] ?? Registry::getClassReflector($reflector);
78+
$value = new Configurator($classes ? new Registry($classes) : null, $references ? new Values($references) : null, $properties, $value, $wakeups);
79+
$value = var_export($value, true);
9680

97-
$properties = array(
98-
$arrayValue,
99-
$reflector->getMethod('getFlags')->invoke($value),
100-
$value instanceof \ArrayObject ? $reflector->getMethod('getIteratorClass')->invoke($value) : 'ArrayIterator',
101-
);
102-
103-
$reflector = $reflector->getMethod('setFlags');
104-
$reflector->invoke($proto, \ArrayObject::STD_PROP_LIST);
105-
106-
if ($properties[1] & \ArrayObject::STD_PROP_LIST) {
107-
$reflector->invoke($value, 0);
108-
$properties[0] = (array) $value;
109-
} else {
110-
$reflector->invoke($value, \ArrayObject::STD_PROP_LIST);
111-
$arrayValue = (array) $value;
112-
}
113-
$reflector->invoke($value, $properties[1]);
114-
115-
if (array(array(), 0, 'ArrayIterator') === $properties) {
116-
$properties = array();
117-
} else {
118-
if ('ArrayIterator' === $properties[2]) {
119-
unset($properties[2]);
120-
}
121-
$properties = array($reflector->class => array("\0" => $properties));
122-
}
123-
} elseif ($value instanceof \SplObjectStorage) {
124-
foreach (clone $value as $v) {
125-
$properties[] = $v;
126-
$properties[] = $value[$v];
127-
}
128-
$properties = array('SplObjectStorage' => array("\0" => $properties));
129-
} elseif ($value instanceof \Serializable) {
130-
++$objectsCount;
131-
$objectsPool[$value] = array($id = \count($objectsPool), serialize($value), array(), 0);
132-
$value = new Reference($id);
133-
continue;
134-
}
135-
136-
if (\method_exists($class, '__sleep')) {
137-
if (!\is_array($sleep = $value->__sleep())) {
138-
trigger_error('serialize(): __sleep should return an array only containing the names of instance-variables to serialize', E_USER_NOTICE);
139-
$value = null;
140-
continue;
141-
}
142-
$sleep = array_flip($sleep);
143-
}
144-
145-
$proto = (array) $proto;
146-
147-
foreach ($arrayValue as $name => $v) {
148-
$k = (string) $name;
149-
if ('' === $k || "\0" !== $k[0]) {
150-
$c = $class;
151-
} elseif ('*' === $k[1]) {
152-
$c = $class;
153-
$k = substr($k, 3);
154-
} else {
155-
$i = strpos($k, "\0", 2);
156-
$c = substr($k, 1, $i - 1);
157-
$k = substr($k, 1 + $i);
158-
}
159-
if (null === $sleep) {
160-
$properties[$c][$k] = $v;
161-
} elseif (isset($sleep[$k]) && $c === $class) {
162-
$properties[$c][$k] = $v;
163-
unset($sleep[$k]);
164-
}
165-
if (\array_key_exists($name, $proto) && $proto[$name] === $v) {
166-
unset($properties[$c][$k]);
167-
}
168-
}
169-
if ($sleep) {
170-
foreach ($sleep as $k => $v) {
171-
trigger_error(sprintf('serialize(): "%s" returned as member variable from __sleep() but does not exist', $k), E_USER_NOTICE);
172-
}
173-
}
174-
175-
$objectsPool[$value] = array($id = \count($objectsPool));
176-
$objectsCount += 1 + self::doMarshall($properties, $objectsPool);
177-
$objectsPool[$value] = array($id, $class, $properties, \method_exists($class, '__wakeup') ? $objectsCount : 0);
178-
179-
$value = new Reference($id);
180-
}
81+
$regexp = sprintf("{%s::__set_state\(array\(\s++'id' => %%s(\d+),\s++\)\)}", preg_quote(Reference::class));
82+
$value = preg_replace(sprintf($regexp, ''), Registry::class.'::$objects[$1]', $value);
83+
$value = preg_replace(sprintf($regexp, '-'), '&'.Registry::class.'::$references[$1]', $value);
18184

182-
return $objectsCount;
85+
return $value;
18386
}
18487
}

‎src/Symfony/Component/Cache/Marshaller/PhpMarshaller/Configurator.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Marshaller/PhpMarshaller/Configurator.php
+7-6Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,20 @@ class Configurator
2020
{
2121
public static $configurators = array();
2222

23-
public function __construct(Registry $registry, array $properties, $value, array $wakeups)
23+
public function __construct(?Registry $registry, ?Values $values, array $properties, $value, array $wakeups)
2424
{
2525
$this->{0} = $registry;
26-
$this->{1} = $properties;
27-
$this->{2} = $value;
28-
$this->{3} = $wakeups;
26+
$this->{1} = $values;
27+
$this->{2} = $properties;
28+
$this->{3} = $value;
29+
$this->{4} = $wakeups;
2930
}
3031

3132
public static function __set_state($state)
3233
{
3334
$objects = Registry::$objects;
34-
Registry::$objects = \array_pop(Registry::$stack);
35-
list(, $properties, $value, $wakeups) = $state;
35+
list(Registry::$objects, Registry::$references) = \array_pop(Registry::$stack);
36+
list(, , $properties, $value, $wakeups) = $state;
3637

3738
foreach ($properties as $class => $vars) {
3839
(self::$configurators[$class] ?? self::getConfigurator($class))($vars, $objects);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.