Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2df7320

Browse 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

30 files changed

+536
-541
lines changed

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

Lines 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

Lines 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 <[email protected]>
@@ -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

Lines 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)