[Cache] make PhpMarshaller handle hard references
This commit is contained in:
parent
f96753b9ab
commit
bc5d208584
|
@ -16,7 +16,6 @@ use Psr\Cache\CacheItemPoolInterface;
|
|||
use Symfony\Component\Cache\CacheInterface;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Component\Cache\Traits\GetTrait;
|
||||
|
@ -35,7 +34,6 @@ class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInte
|
|||
use GetTrait;
|
||||
|
||||
private $createCacheItem;
|
||||
private $marshaller;
|
||||
|
||||
/**
|
||||
* @param string $file The PHP file were values are cached
|
||||
|
@ -106,9 +104,6 @@ class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInte
|
|||
if ($value instanceof \Closure) {
|
||||
return $value();
|
||||
}
|
||||
if (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
||||
return ($this->marshaller ?? $this->marshaller = new DefaultMarshaller())->unmarshall($value);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
unset($this->keys[$key]);
|
||||
goto get_from_pool;
|
||||
|
@ -144,13 +139,6 @@ class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInte
|
|||
$value = null;
|
||||
$isHit = false;
|
||||
}
|
||||
} elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
||||
try {
|
||||
$value = unserialize($value);
|
||||
} catch (\Throwable $e) {
|
||||
$value = null;
|
||||
$isHit = false;
|
||||
}
|
||||
}
|
||||
|
||||
$f = $this->createCacheItem;
|
||||
|
@ -284,12 +272,6 @@ class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInte
|
|||
} catch (\Throwable $e) {
|
||||
yield $key => $f($key, null, false);
|
||||
}
|
||||
} elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
||||
try {
|
||||
yield $key => $f($key, $this->unserializeValue($value), true);
|
||||
} catch (\Throwable $e) {
|
||||
yield $key => $f($key, null, false);
|
||||
}
|
||||
} else {
|
||||
yield $key => $f($key, $value, true);
|
||||
}
|
||||
|
|
|
@ -12,8 +12,10 @@
|
|||
namespace Symfony\Component\Cache\Marshaller;
|
||||
|
||||
use Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator;
|
||||
use Symfony\Component\Cache\Marshaller\PhpMarshaller\Marshaller;
|
||||
use Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference;
|
||||
use Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry;
|
||||
use Symfony\Component\Cache\Marshaller\PhpMarshaller\Values;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
|
@ -27,14 +29,31 @@ use Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry;
|
|||
*/
|
||||
class PhpMarshaller
|
||||
{
|
||||
public static function marshall($value, int &$objectsCount)
|
||||
public static function marshall($value, bool &$isStaticValue = null): string
|
||||
{
|
||||
if (!\is_object($value) && !\is_array($value)) {
|
||||
return $value;
|
||||
$isStaticValue = true;
|
||||
|
||||
if (!\is_object($value) && !(\is_array($value) && $value) && !$value instanceof \__PHP_Incomplete_Class && !\is_resource($value)) {
|
||||
return var_export($value, true);
|
||||
}
|
||||
|
||||
$objectsPool = new \SplObjectStorage();
|
||||
$value = array($value);
|
||||
$objectsCount = self::doMarshall($value, $objectsPool);
|
||||
$refsPool = array();
|
||||
$objectsCount = 0;
|
||||
|
||||
try {
|
||||
$value = Marshaller::marshall(array($value), $objectsPool, $refsPool, $objectsCount, $isStaticValue)[0];
|
||||
} finally {
|
||||
$references = array();
|
||||
foreach ($refsPool as $i => $v) {
|
||||
$v[0] = $v[1];
|
||||
$references[1 + $i] = $v[2];
|
||||
}
|
||||
}
|
||||
|
||||
if ($isStaticValue) {
|
||||
return var_export($value, true);
|
||||
}
|
||||
|
||||
$classes = array();
|
||||
$values = array();
|
||||
|
@ -46,6 +65,7 @@ class PhpMarshaller
|
|||
}
|
||||
}
|
||||
ksort($wakeups);
|
||||
|
||||
$properties = array();
|
||||
foreach ($values as $i => $vars) {
|
||||
foreach ($vars as $class => $values) {
|
||||
|
@ -54,131 +74,14 @@ class PhpMarshaller
|
|||
}
|
||||
}
|
||||
}
|
||||
if (!$classes) {
|
||||
return $value[0];
|
||||
}
|
||||
|
||||
return new Configurator(new Registry($classes), $properties, $value[0], $wakeups);
|
||||
}
|
||||
$value = new Configurator($classes ? new Registry($classes) : null, $references ? new Values($references) : null, $properties, $value, $wakeups);
|
||||
$value = var_export($value, true);
|
||||
|
||||
public static function optimize(string $exportedValue)
|
||||
{
|
||||
return preg_replace(sprintf("{%s::__set_state\(array\(\s++'0' => (\d+),\s++\)\)}", preg_quote(Reference::class)), Registry::class.'::$objects[$1]', $exportedValue);
|
||||
}
|
||||
$regexp = sprintf("{%s::__set_state\(array\(\s++'id' => %%s(\d+),\s++\)\)}", preg_quote(Reference::class));
|
||||
$value = preg_replace(sprintf($regexp, ''), Registry::class.'::$objects[$1]', $value);
|
||||
$value = preg_replace(sprintf($regexp, '-'), '&'.Registry::class.'::$references[$1]', $value);
|
||||
|
||||
private static function doMarshall(array &$array, \SplObjectStorage $objectsPool): int
|
||||
{
|
||||
$objectsCount = 0;
|
||||
|
||||
foreach ($array as &$value) {
|
||||
if (\is_array($value) && $value) {
|
||||
$objectsCount += self::doMarshall($value, $objectsPool);
|
||||
}
|
||||
if (!\is_object($value)) {
|
||||
continue;
|
||||
}
|
||||
if (isset($objectsPool[$value])) {
|
||||
++$objectsCount;
|
||||
$value = new Reference($objectsPool[$value][0]);
|
||||
continue;
|
||||
}
|
||||
$class = \get_class($value);
|
||||
$properties = array();
|
||||
$sleep = null;
|
||||
$arrayValue = (array) $value;
|
||||
$proto = (Registry::$reflectors[$class] ?? Registry::getClassReflector($class))->newInstanceWithoutConstructor();
|
||||
|
||||
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.
|
||||
$reflector = $value instanceof \ArrayIterator ? 'ArrayIterator' : 'ArrayObject';
|
||||
$reflector = Registry::$reflectors[$reflector] ?? Registry::getClassReflector($reflector);
|
||||
|
||||
$properties = array(
|
||||
$arrayValue,
|
||||
$reflector->getMethod('getFlags')->invoke($value),
|
||||
$value instanceof \ArrayObject ? $reflector->getMethod('getIteratorClass')->invoke($value) : 'ArrayIterator',
|
||||
);
|
||||
|
||||
$reflector = $reflector->getMethod('setFlags');
|
||||
$reflector->invoke($proto, \ArrayObject::STD_PROP_LIST);
|
||||
|
||||
if ($properties[1] & \ArrayObject::STD_PROP_LIST) {
|
||||
$reflector->invoke($value, 0);
|
||||
$properties[0] = (array) $value;
|
||||
} else {
|
||||
$reflector->invoke($value, \ArrayObject::STD_PROP_LIST);
|
||||
$arrayValue = (array) $value;
|
||||
}
|
||||
$reflector->invoke($value, $properties[1]);
|
||||
|
||||
if (array(array(), 0, 'ArrayIterator') === $properties) {
|
||||
$properties = array();
|
||||
} else {
|
||||
if ('ArrayIterator' === $properties[2]) {
|
||||
unset($properties[2]);
|
||||
}
|
||||
$properties = array($reflector->class => array("\0" => $properties));
|
||||
}
|
||||
} elseif ($value instanceof \SplObjectStorage) {
|
||||
foreach (clone $value as $v) {
|
||||
$properties[] = $v;
|
||||
$properties[] = $value[$v];
|
||||
}
|
||||
$properties = array('SplObjectStorage' => array("\0" => $properties));
|
||||
} elseif ($value instanceof \Serializable) {
|
||||
++$objectsCount;
|
||||
$objectsPool[$value] = array($id = \count($objectsPool), serialize($value), array(), 0);
|
||||
$value = new Reference($id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (\method_exists($class, '__sleep')) {
|
||||
if (!\is_array($sleep = $value->__sleep())) {
|
||||
trigger_error('serialize(): __sleep should return an array only containing the names of instance-variables to serialize', E_USER_NOTICE);
|
||||
$value = null;
|
||||
continue;
|
||||
}
|
||||
$sleep = array_flip($sleep);
|
||||
}
|
||||
|
||||
$proto = (array) $proto;
|
||||
|
||||
foreach ($arrayValue as $name => $v) {
|
||||
$k = (string) $name;
|
||||
if ('' === $k || "\0" !== $k[0]) {
|
||||
$c = $class;
|
||||
} elseif ('*' === $k[1]) {
|
||||
$c = $class;
|
||||
$k = substr($k, 3);
|
||||
} else {
|
||||
$i = strpos($k, "\0", 2);
|
||||
$c = substr($k, 1, $i - 1);
|
||||
$k = substr($k, 1 + $i);
|
||||
}
|
||||
if (null === $sleep) {
|
||||
$properties[$c][$k] = $v;
|
||||
} elseif (isset($sleep[$k]) && $c === $class) {
|
||||
$properties[$c][$k] = $v;
|
||||
unset($sleep[$k]);
|
||||
}
|
||||
if (\array_key_exists($name, $proto) && $proto[$name] === $v) {
|
||||
unset($properties[$c][$k]);
|
||||
}
|
||||
}
|
||||
if ($sleep) {
|
||||
foreach ($sleep as $k => $v) {
|
||||
trigger_error(sprintf('serialize(): "%s" returned as member variable from __sleep() but does not exist', $k), E_USER_NOTICE);
|
||||
}
|
||||
}
|
||||
|
||||
$objectsPool[$value] = array($id = \count($objectsPool));
|
||||
$objectsCount += 1 + self::doMarshall($properties, $objectsPool);
|
||||
$objectsPool[$value] = array($id, $class, $properties, \method_exists($class, '__wakeup') ? $objectsCount : 0);
|
||||
|
||||
$value = new Reference($id);
|
||||
}
|
||||
|
||||
return $objectsCount;
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,19 +20,20 @@ class Configurator
|
|||
{
|
||||
public static $configurators = array();
|
||||
|
||||
public function __construct(Registry $registry, array $properties, $value, array $wakeups)
|
||||
public function __construct(?Registry $registry, ?Values $values, array $properties, $value, array $wakeups)
|
||||
{
|
||||
$this->{0} = $registry;
|
||||
$this->{1} = $properties;
|
||||
$this->{2} = $value;
|
||||
$this->{3} = $wakeups;
|
||||
$this->{1} = $values;
|
||||
$this->{2} = $properties;
|
||||
$this->{3} = $value;
|
||||
$this->{4} = $wakeups;
|
||||
}
|
||||
|
||||
public static function __set_state($state)
|
||||
{
|
||||
$objects = Registry::$objects;
|
||||
Registry::$objects = \array_pop(Registry::$stack);
|
||||
list(, $properties, $value, $wakeups) = $state;
|
||||
list(Registry::$objects, Registry::$references) = \array_pop(Registry::$stack);
|
||||
list(, , $properties, $value, $wakeups) = $state;
|
||||
|
||||
foreach ($properties as $class => $vars) {
|
||||
(self::$configurators[$class] ?? self::getConfigurator($class))($vars, $objects);
|
||||
|
|
|
@ -0,0 +1,212 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Cache\Marshaller\PhpMarshaller;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Marshaller
|
||||
{
|
||||
/**
|
||||
* Prepares an array of values for PhpMarshaller.
|
||||
*
|
||||
* For performance this method is public and has no type-hints.
|
||||
*
|
||||
* @param array &$values
|
||||
* @param \SplObjectStorage $objectsPool
|
||||
* @param array &$refsPool
|
||||
* @param int &$objectsCount
|
||||
* @param bool &$valuesAreStatic
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @throws \Exception When a value cannot be serialized
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function marshall($values, $objectsPool, &$refsPool, &$objectsCount, &$valuesAreStatic)
|
||||
{
|
||||
$refs = $values;
|
||||
foreach ($values as $k => $value) {
|
||||
if (\is_resource($value)) {
|
||||
throw new \Exception(sprintf("Serialization of '%s' resource is not allowed", \get_resource_type($value)));
|
||||
}
|
||||
$refs[$k] = $objectsPool;
|
||||
|
||||
if ($isRef = !$valueIsStatic = $values[$k] !== $objectsPool) {
|
||||
$values[$k] = &$value; // Break hard references to make $values completely
|
||||
unset($value); // independent from the original structure
|
||||
$refs[$k] = $value = $values[$k];
|
||||
if ($value instanceof Reference && 0 > $value->id) {
|
||||
$valuesAreStatic = false;
|
||||
continue;
|
||||
}
|
||||
$refsPool[] = array(&$refs[$k], $value, &$value);
|
||||
$refs[$k] = $values[$k] = new Reference(-\count($refsPool));
|
||||
}
|
||||
|
||||
if (\is_array($value)) {
|
||||
if ($value) {
|
||||
$value = self::marshall($value, $objectsPool, $refsPool, $objectsCount, $valueIsStatic);
|
||||
}
|
||||
goto handle_value;
|
||||
} elseif (!\is_object($value) && !$value instanceof \__PHP_Incomplete_Class) {
|
||||
goto handle_value;
|
||||
}
|
||||
|
||||
$valueIsStatic = false;
|
||||
if (isset($objectsPool[$value])) {
|
||||
++$objectsCount;
|
||||
$value = new Reference($objectsPool[$value][0]);
|
||||
goto handle_value;
|
||||
}
|
||||
|
||||
$class = \get_class($value);
|
||||
$properties = array();
|
||||
$sleep = null;
|
||||
$arrayValue = (array) $value;
|
||||
|
||||
if (!isset(Registry::$prototypes[$class])) {
|
||||
// Might throw Exception("Serialization of '...' is not allowed")
|
||||
Registry::getClassReflector($class);
|
||||
serialize(Registry::$prototypes[$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();
|
||||
$properties = self::getArrayObjectProperties($value, $arrayValue, $proto);
|
||||
} elseif ($value instanceof \SplObjectStorage) {
|
||||
// By implementing Serializable, SplObjectStorage breaks internal references,
|
||||
// let's deal with it on our own.
|
||||
foreach (clone $value as $v) {
|
||||
$properties[] = $v;
|
||||
$properties[] = $value[$v];
|
||||
}
|
||||
$properties = array('SplObjectStorage' => array("\0" => $properties));
|
||||
} elseif ($value instanceof \Serializable || $value instanceof \__PHP_Incomplete_Class) {
|
||||
++$objectsCount;
|
||||
$objectsPool[$value] = array($id = \count($objectsPool), serialize($value), array(), 0);
|
||||
$value = new Reference($id);
|
||||
goto handle_value;
|
||||
}
|
||||
|
||||
if (\method_exists($class, '__sleep')) {
|
||||
if (!\is_array($sleep = $value->__sleep())) {
|
||||
trigger_error('serialize(): __sleep should return an array only containing the names of instance-variables to serialize', E_USER_NOTICE);
|
||||
$value = null;
|
||||
goto handle_value;
|
||||
}
|
||||
$sleep = array_flip($sleep);
|
||||
}
|
||||
|
||||
$proto = (array) $proto;
|
||||
|
||||
foreach ($arrayValue as $name => $v) {
|
||||
$n = (string) $name;
|
||||
if ('' === $n || "\0" !== $n[0]) {
|
||||
$c = $class;
|
||||
} elseif ('*' === $n[1]) {
|
||||
$c = $class;
|
||||
$n = substr($n, 3);
|
||||
} 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 (\array_key_exists($name, $proto) && $proto[$name] === $v) {
|
||||
unset($properties[$c][$n]);
|
||||
}
|
||||
}
|
||||
if ($sleep) {
|
||||
foreach ($sleep as $n => $v) {
|
||||
trigger_error(sprintf('serialize(): "%s" returned as member variable from __sleep() but does not exist', $n), E_USER_NOTICE);
|
||||
}
|
||||
}
|
||||
|
||||
$objectsPool[$value] = array($id = \count($objectsPool));
|
||||
$properties = self::marshall($properties, $objectsPool, $refsPool, $objectsCount, $valueIsStatic);
|
||||
++$objectsCount;
|
||||
$objectsPool[$value] = array($id, $class, $properties, \method_exists($class, '__wakeup') ? $objectsCount : 0);
|
||||
|
||||
$value = new Reference($id);
|
||||
|
||||
handle_value:
|
||||
if ($isRef) {
|
||||
unset($value); // Break the hard reference created above
|
||||
} elseif (!$valueIsStatic) {
|
||||
$values[$k] = $value;
|
||||
}
|
||||
$valuesAreStatic = $valueIsStatic && $valuesAreStatic;
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function getArrayObjectProperties($value, &$arrayValue, $proto)
|
||||
{
|
||||
$reflector = $value instanceof \ArrayIterator ? 'ArrayIterator' : 'ArrayObject';
|
||||
$reflector = Registry::$reflectors[$reflector] ?? Registry::getClassReflector($reflector);
|
||||
|
||||
$properties = array(
|
||||
$arrayValue,
|
||||
$reflector->getMethod('getFlags')->invoke($value),
|
||||
$value instanceof \ArrayObject ? $reflector->getMethod('getIteratorClass')->invoke($value) : 'ArrayIterator',
|
||||
);
|
||||
|
||||
$reflector = $reflector->getMethod('setFlags');
|
||||
$reflector->invoke($proto, \ArrayObject::STD_PROP_LIST);
|
||||
|
||||
if ($properties[1] & \ArrayObject::STD_PROP_LIST) {
|
||||
$reflector->invoke($value, 0);
|
||||
$properties[0] = (array) $value;
|
||||
} else {
|
||||
$reflector->invoke($value, \ArrayObject::STD_PROP_LIST);
|
||||
$arrayValue = (array) $value;
|
||||
}
|
||||
$reflector->invoke($value, $properties[1]);
|
||||
|
||||
if (array(array(), 0, 'ArrayIterator') === $properties) {
|
||||
$properties = array();
|
||||
} else {
|
||||
if ('ArrayIterator' === $properties[2]) {
|
||||
unset($properties[2]);
|
||||
}
|
||||
$properties = array($reflector->class => array("\0" => $properties));
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
}
|
|
@ -18,13 +18,10 @@ namespace Symfony\Component\Cache\Marshaller\PhpMarshaller;
|
|||
*/
|
||||
class Reference
|
||||
{
|
||||
public $id;
|
||||
|
||||
public function __construct(int $id)
|
||||
{
|
||||
$this->{0} = $id;
|
||||
}
|
||||
|
||||
public static function __set_state($state)
|
||||
{
|
||||
return Registry::$objects[$state[0]];
|
||||
$this->id = $id;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,8 +20,11 @@ class Registry
|
|||
{
|
||||
public static $stack = array();
|
||||
public static $objects = array();
|
||||
public static $references = array();
|
||||
public static $reflectors = array();
|
||||
public static $prototypes = array();
|
||||
public static $cloneable = array();
|
||||
public static $instantiableWithoutConstructor = array();
|
||||
|
||||
public function __construct(array $classes)
|
||||
{
|
||||
|
@ -32,15 +35,33 @@ class Registry
|
|||
|
||||
public static function __set_state($classes)
|
||||
{
|
||||
self::$stack[] = self::$objects;
|
||||
$unserializeCallback = null;
|
||||
self::$stack[] = array(self::$objects, self::$references);
|
||||
self::$objects = $classes;
|
||||
foreach (self::$objects as &$class) {
|
||||
if (isset(self::$prototypes[$class])) {
|
||||
$class = clone self::$prototypes[$class];
|
||||
} elseif (':' === ($class[1] ?? null)) {
|
||||
$class = \unserialize($class);
|
||||
} else {
|
||||
$class = (self::$reflectors[$class] ?? self::getClassReflector($class))->newInstanceWithoutConstructor();
|
||||
self::$references = array();
|
||||
try {
|
||||
foreach (self::$objects as &$class) {
|
||||
if (':' === ($class[1] ?? null)) {
|
||||
if (null === $unserializeCallback) {
|
||||
$unserializeCallback = ini_set('unserialize_callback_func', __CLASS__.'::getClassReflector');
|
||||
}
|
||||
$class = \unserialize($class);
|
||||
continue;
|
||||
}
|
||||
$r = self::$reflectors[$class] ?? self::getClassReflector($class);
|
||||
|
||||
if (self::$cloneable[$class]) {
|
||||
$class = clone self::$prototypes[$class];
|
||||
} else {
|
||||
$class = self::$instantiableWithoutConstructor[$class] ? $r->newInstanceWithoutConstructor() : $r->newInstance();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
list(self::$objects, self::$references) = \array_pop(self::$stack);
|
||||
throw $e;
|
||||
} finally {
|
||||
if (null !== $unserializeCallback) {
|
||||
ini_set('unserialize_callback_func', $unserializeCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,8 +70,38 @@ class Registry
|
|||
{
|
||||
$reflector = new \ReflectionClass($class);
|
||||
|
||||
if (!$reflector->hasMethod('__clone')) {
|
||||
self::$prototypes[$class] = $reflector->newInstanceWithoutConstructor();
|
||||
if (self::$instantiableWithoutConstructor[$class] = !$reflector->isFinal() || !$reflector->isInternal()) {
|
||||
$proto = $reflector->newInstanceWithoutConstructor();
|
||||
} else {
|
||||
try {
|
||||
$proto = $reflector->newInstance();
|
||||
} catch (\Throwable $e) {
|
||||
throw new \Exception(sprintf("Serialization of '%s' is not allowed", $class), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
if ($proto instanceof \Reflector || $proto instanceof \ReflectionGenerator || $proto instanceof \ReflectionType) {
|
||||
if (!$proto instanceof \Serializable && !\method_exists($proto, '__wakeup')) {
|
||||
throw new \Exception(sprintf("Serialization of '%s' is not allowed", $class));
|
||||
}
|
||||
}
|
||||
|
||||
self::$prototypes[$class] = $proto;
|
||||
self::$cloneable[$class] = !$reflector->hasMethod('__clone');
|
||||
|
||||
if ($proto instanceof \Throwable) {
|
||||
static $trace;
|
||||
|
||||
if (null === $trace) {
|
||||
$trace = array(
|
||||
new \ReflectionProperty(\Error::class, 'trace'),
|
||||
new \ReflectionProperty(\Exception::class, 'trace'),
|
||||
);
|
||||
$trace[0]->setAccessible(true);
|
||||
$trace[1]->setAccessible(true);
|
||||
}
|
||||
|
||||
$trace[$proto instanceof \Exception]->setValue($proto, array());
|
||||
}
|
||||
|
||||
return self::$reflectors[$class] = $reflector;
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Cache\Marshaller\PhpMarshaller;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Values
|
||||
{
|
||||
public function __construct(array $values)
|
||||
{
|
||||
foreach ($values as $i => $v) {
|
||||
$this->$i = $v;
|
||||
}
|
||||
}
|
||||
|
||||
public static function __set_state($values)
|
||||
{
|
||||
foreach ($values as $i => $v) {
|
||||
Registry::$references[$i] = $v;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,7 +13,6 @@ namespace Symfony\Component\Cache\Simple;
|
|||
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Component\Cache\Traits\PhpArrayTrait;
|
||||
|
@ -29,8 +28,6 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
|
|||
{
|
||||
use PhpArrayTrait;
|
||||
|
||||
private $marshaller;
|
||||
|
||||
/**
|
||||
* @param string $file The PHP file were values are cached
|
||||
* @param CacheInterface $fallbackPool A pool to fallback on when an item is not hit
|
||||
|
@ -84,13 +81,6 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
|
|||
return $default;
|
||||
}
|
||||
}
|
||||
if (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
||||
try {
|
||||
return ($this->marshaller ?? $this->marshaller = new DefaultMarshaller())->unmarshall($value);
|
||||
} catch (\Throwable $e) {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
@ -243,12 +233,6 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
|
|||
} catch (\Throwable $e) {
|
||||
yield $key => $default;
|
||||
}
|
||||
} elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
||||
try {
|
||||
yield $key => unserialize($value);
|
||||
} catch (\Throwable $e) {
|
||||
yield $key => $default;
|
||||
}
|
||||
} else {
|
||||
yield $key => $value;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'ArrayIterator',
|
||||
)),
|
||||
'1' =>
|
||||
'1' => NULL,
|
||||
'2' =>
|
||||
array (
|
||||
'ArrayIterator' =>
|
||||
array (
|
||||
|
@ -20,11 +21,9 @@
|
|||
),
|
||||
),
|
||||
),
|
||||
'2' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||
'0' => 0,
|
||||
)),
|
||||
'3' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
'4' =>
|
||||
array (
|
||||
),
|
||||
));
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||
'0' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyArrayObject',
|
||||
)),
|
||||
'1' =>
|
||||
array (
|
||||
'ArrayObject' =>
|
||||
array (
|
||||
'' . "\0" . '' =>
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
0 => 234,
|
||||
),
|
||||
1 => 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
'2' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
'3' =>
|
||||
array (
|
||||
),
|
||||
));
|
|
@ -3,7 +3,8 @@
|
|||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyArrayObject',
|
||||
)),
|
||||
'1' =>
|
||||
'1' => NULL,
|
||||
'2' =>
|
||||
array (
|
||||
'ArrayObject' =>
|
||||
array (
|
||||
|
@ -20,11 +21,9 @@
|
|||
),
|
||||
),
|
||||
),
|
||||
'2' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||
'0' => 0,
|
||||
)),
|
||||
'3' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
'4' =>
|
||||
array (
|
||||
),
|
||||
));
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||
'0' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'ArrayObject',
|
||||
'1' => 'ArrayObject',
|
||||
)),
|
||||
'1' =>
|
||||
array (
|
||||
'ArrayObject' =>
|
||||
array (
|
||||
'' . "\0" . '' =>
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
0 => 1,
|
||||
1 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
),
|
||||
1 => 0,
|
||||
),
|
||||
),
|
||||
'foo' =>
|
||||
array (
|
||||
0 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[1],
|
||||
),
|
||||
),
|
||||
),
|
||||
'2' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
'3' =>
|
||||
array (
|
||||
),
|
||||
));
|
|
@ -4,7 +4,8 @@
|
|||
'0' => 'ArrayObject',
|
||||
'1' => 'ArrayObject',
|
||||
)),
|
||||
'1' =>
|
||||
'1' => NULL,
|
||||
'2' =>
|
||||
array (
|
||||
'ArrayObject' =>
|
||||
array (
|
||||
|
@ -16,9 +17,7 @@
|
|||
array (
|
||||
0 => 1,
|
||||
1 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||
'0' => 0,
|
||||
)),
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
),
|
||||
1 => 0,
|
||||
),
|
||||
|
@ -26,17 +25,13 @@
|
|||
'foo' =>
|
||||
array (
|
||||
0 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||
'0' => 1,
|
||||
)),
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[1],
|
||||
),
|
||||
),
|
||||
),
|
||||
'2' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||
'0' => 0,
|
||||
)),
|
||||
'3' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
'4' =>
|
||||
array (
|
||||
),
|
||||
));
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||
'0' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyCloneable',
|
||||
'1' => 'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyNotCloneable',
|
||||
)),
|
||||
'1' =>
|
||||
array (
|
||||
),
|
||||
'2' =>
|
||||
array (
|
||||
0 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
1 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[1],
|
||||
),
|
||||
'3' =>
|
||||
array (
|
||||
),
|
||||
));
|
|
@ -4,21 +4,18 @@
|
|||
'0' => 'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyCloneable',
|
||||
'1' => 'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyNotCloneable',
|
||||
)),
|
||||
'1' =>
|
||||
array (
|
||||
),
|
||||
'1' => NULL,
|
||||
'2' =>
|
||||
array (
|
||||
0 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||
'0' => 0,
|
||||
)),
|
||||
1 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||
'0' => 1,
|
||||
)),
|
||||
),
|
||||
'3' =>
|
||||
array (
|
||||
0 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
1 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[1],
|
||||
),
|
||||
'4' =>
|
||||
array (
|
||||
),
|
||||
));
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||
'0' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'DateTime',
|
||||
)),
|
||||
'1' =>
|
||||
array (
|
||||
'DateTime' =>
|
||||
array (
|
||||
'date' =>
|
||||
array (
|
||||
0 => '1970-01-01 00:00:00.000000',
|
||||
),
|
||||
'timezone_type' =>
|
||||
array (
|
||||
0 => 1,
|
||||
),
|
||||
'timezone' =>
|
||||
array (
|
||||
0 => '+00:00',
|
||||
),
|
||||
),
|
||||
),
|
||||
'2' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
'3' =>
|
||||
array (
|
||||
1 => 0,
|
||||
),
|
||||
));
|
|
@ -3,7 +3,8 @@
|
|||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'DateTime',
|
||||
)),
|
||||
'1' =>
|
||||
'1' => NULL,
|
||||
'2' =>
|
||||
array (
|
||||
'DateTime' =>
|
||||
array (
|
||||
|
@ -21,11 +22,9 @@
|
|||
),
|
||||
),
|
||||
),
|
||||
'2' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||
'0' => 0,
|
||||
)),
|
||||
'3' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
'4' =>
|
||||
array (
|
||||
1 => 0,
|
||||
),
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||
'0' => NULL,
|
||||
'1' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Values::__set_state(array(
|
||||
'1' =>
|
||||
array (
|
||||
0 =>
|
||||
&Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$references[1],
|
||||
),
|
||||
)),
|
||||
'2' =>
|
||||
array (
|
||||
),
|
||||
'3' =>
|
||||
array (
|
||||
0 =>
|
||||
&Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$references[1],
|
||||
),
|
||||
'4' =>
|
||||
array (
|
||||
),
|
||||
));
|
|
@ -1,19 +1,26 @@
|
|||
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||
'0' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'C:55:"Symfony\\Component\\Cache\\Tests\\Marshaller\\MySerializable":3:{123}',
|
||||
'0' => 'stdClass',
|
||||
)),
|
||||
'1' =>
|
||||
array (
|
||||
),
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Values::__set_state(array(
|
||||
'1' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
)),
|
||||
'2' =>
|
||||
array (
|
||||
0 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
1 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
),
|
||||
'3' =>
|
||||
array (
|
||||
0 =>
|
||||
&Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$references[1],
|
||||
1 =>
|
||||
&Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$references[1],
|
||||
2 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
),
|
||||
'4' =>
|
||||
array (
|
||||
),
|
||||
));
|
|
@ -1,28 +1,15 @@
|
|||
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||
'0' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'ArrayIterator',
|
||||
'0' => 'O:20:"SomeNotExistingClass":0:{}',
|
||||
)),
|
||||
'1' =>
|
||||
array (
|
||||
'ArrayIterator' =>
|
||||
array (
|
||||
'' . "\0" . '' =>
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
0 => 123,
|
||||
),
|
||||
1 => 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
'1' => NULL,
|
||||
'2' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
array (
|
||||
),
|
||||
'3' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
'4' =>
|
||||
array (
|
||||
),
|
||||
));
|
|
@ -1,39 +0,0 @@
|
|||
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||
'0' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyPrivateValue',
|
||||
'1' => 'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyPrivateChildValue',
|
||||
)),
|
||||
'1' =>
|
||||
array (
|
||||
'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyPrivateValue' =>
|
||||
array (
|
||||
'prot' =>
|
||||
array (
|
||||
0 => 123,
|
||||
),
|
||||
'priv' =>
|
||||
array (
|
||||
0 => 234,
|
||||
1 => 234,
|
||||
),
|
||||
),
|
||||
'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyPrivateChildValue' =>
|
||||
array (
|
||||
'prot' =>
|
||||
array (
|
||||
1 => 123,
|
||||
),
|
||||
),
|
||||
),
|
||||
'2' =>
|
||||
array (
|
||||
0 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
1 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[1],
|
||||
),
|
||||
'3' =>
|
||||
array (
|
||||
),
|
||||
));
|
|
@ -4,7 +4,8 @@
|
|||
'0' => 'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyPrivateValue',
|
||||
'1' => 'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyPrivateChildValue',
|
||||
)),
|
||||
'1' =>
|
||||
'1' => NULL,
|
||||
'2' =>
|
||||
array (
|
||||
'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyPrivateValue' =>
|
||||
array (
|
||||
|
@ -26,18 +27,14 @@
|
|||
),
|
||||
),
|
||||
),
|
||||
'2' =>
|
||||
'3' =>
|
||||
array (
|
||||
0 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||
'0' => 0,
|
||||
)),
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
1 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||
'0' => 1,
|
||||
)),
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[1],
|
||||
),
|
||||
'3' =>
|
||||
'4' =>
|
||||
array (
|
||||
),
|
||||
));
|
||||
|
|
|
@ -3,21 +3,18 @@
|
|||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'C:55:"Symfony\\Component\\Cache\\Tests\\Marshaller\\MySerializable":3:{123}',
|
||||
)),
|
||||
'1' =>
|
||||
array (
|
||||
),
|
||||
'1' => NULL,
|
||||
'2' =>
|
||||
array (
|
||||
0 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||
'0' => 0,
|
||||
)),
|
||||
1 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||
'0' => 0,
|
||||
)),
|
||||
),
|
||||
'3' =>
|
||||
array (
|
||||
0 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
1 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
),
|
||||
'4' =>
|
||||
array (
|
||||
),
|
||||
));
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||
'0' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'SplObjectStorage',
|
||||
'1' => 'stdClass',
|
||||
)),
|
||||
'1' =>
|
||||
array (
|
||||
'SplObjectStorage' =>
|
||||
array (
|
||||
'' . "\0" . '' =>
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
0 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[1],
|
||||
1 => 345,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
'2' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
'3' =>
|
||||
array (
|
||||
),
|
||||
));
|
|
@ -4,7 +4,8 @@
|
|||
'0' => 'SplObjectStorage',
|
||||
'1' => 'stdClass',
|
||||
)),
|
||||
'1' =>
|
||||
'1' => NULL,
|
||||
'2' =>
|
||||
array (
|
||||
'SplObjectStorage' =>
|
||||
array (
|
||||
|
@ -13,19 +14,15 @@
|
|||
0 =>
|
||||
array (
|
||||
0 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||
'0' => 1,
|
||||
)),
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[1],
|
||||
1 => 345,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
'2' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||
'0' => 0,
|
||||
)),
|
||||
'3' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
'4' =>
|
||||
array (
|
||||
),
|
||||
));
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||
'0' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyWakeup',
|
||||
'1' => 'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyWakeup',
|
||||
)),
|
||||
'1' =>
|
||||
array (
|
||||
'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyWakeup' =>
|
||||
array (
|
||||
'sub' =>
|
||||
array (
|
||||
0 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[1],
|
||||
1 => 123,
|
||||
),
|
||||
'baz' =>
|
||||
array (
|
||||
1 => 123,
|
||||
),
|
||||
),
|
||||
),
|
||||
'2' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
'3' =>
|
||||
array (
|
||||
1 => 1,
|
||||
2 => 0,
|
||||
),
|
||||
));
|
|
@ -4,16 +4,15 @@
|
|||
'0' => 'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyWakeup',
|
||||
'1' => 'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyWakeup',
|
||||
)),
|
||||
'1' =>
|
||||
'1' => NULL,
|
||||
'2' =>
|
||||
array (
|
||||
'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyWakeup' =>
|
||||
array (
|
||||
'sub' =>
|
||||
array (
|
||||
0 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||
'0' => 1,
|
||||
)),
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[1],
|
||||
1 => 123,
|
||||
),
|
||||
'baz' =>
|
||||
|
@ -22,11 +21,9 @@
|
|||
),
|
||||
),
|
||||
),
|
||||
'2' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||
'0' => 0,
|
||||
)),
|
||||
'3' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
'4' =>
|
||||
array (
|
||||
1 => 1,
|
||||
2 => 0,
|
||||
|
|
|
@ -13,35 +13,82 @@ namespace Symfony\Component\Cache\Tests\Marshaller;
|
|||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Cache\Marshaller\PhpMarshaller;
|
||||
use Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry;
|
||||
use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
|
||||
|
||||
class DoctrineProviderTest extends TestCase
|
||||
{
|
||||
use VarDumperTestTrait;
|
||||
|
||||
/**
|
||||
* @expectedException \ReflectionException
|
||||
* @expectedExceptionMessage Class SomeNotExistingClass does not exist
|
||||
*/
|
||||
public function testPhpIncompleteClassesAreForbidden()
|
||||
{
|
||||
$unserializeCallback = ini_set('unserialize_callback_func', 'var_dump');
|
||||
try {
|
||||
Registry::__set_state(array('O:20:"SomeNotExistingClass":0:{}'));
|
||||
} finally {
|
||||
$this->assertSame('var_dump', ini_set('unserialize_callback_func', $unserializeCallback));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideFailingSerialization
|
||||
* @expectedException \Exception
|
||||
* @expectedExceptionMessageRegexp Serialization of '.*' is not allowed
|
||||
*/
|
||||
public function testFailingSerialization($value)
|
||||
{
|
||||
$expectedDump = $this->getDump($value);
|
||||
try {
|
||||
PhpMarshaller::marshall($value);
|
||||
} finally {
|
||||
$this->assertDumpEquals(rtrim($expectedDump), $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function provideFailingSerialization()
|
||||
{
|
||||
yield array(hash_init('md5'));
|
||||
yield array(new \ReflectionClass('stdClass'));
|
||||
yield array((new \ReflectionFunction(function (): int {}))->getReturnType());
|
||||
yield array(new \ReflectionGenerator((function () { yield 123; })()));
|
||||
yield array(function () {});
|
||||
yield array(function () { yield 123; });
|
||||
yield array(new \SplFileInfo(__FILE__));
|
||||
yield array($h = fopen(__FILE__, 'r'));
|
||||
yield array(array($h));
|
||||
|
||||
$a = array(null, $h);
|
||||
$a[0] = &$a;
|
||||
|
||||
yield array($a);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideMarshall
|
||||
*/
|
||||
public function testMarshall(string $testName, $value, int $expectedObjectsCount)
|
||||
public function testMarshall(string $testName, $value, bool $staticValueExpected = false)
|
||||
{
|
||||
$objectsCount = 0;
|
||||
$marshalledValue = PhpMarshaller::marshall($value, $objectsCount);
|
||||
$serializedValue = serialize($value);
|
||||
$isStaticValue = true;
|
||||
$marshalledValue = PhpMarshaller::marshall($value, $isStaticValue);
|
||||
|
||||
$this->assertSame($expectedObjectsCount, $objectsCount);
|
||||
$this->assertSame($staticValueExpected, $isStaticValue);
|
||||
$this->assertSame($serializedValue, serialize($value));
|
||||
|
||||
$dump = '<?php return '.var_export($marshalledValue, true).";\n";
|
||||
$dump = '<?php return '.$marshalledValue.";\n";
|
||||
$fixtureFile = __DIR__.'/Fixtures/'.$testName.'.php';
|
||||
$this->assertStringEqualsFile($fixtureFile, $dump);
|
||||
|
||||
if ($objectsCount) {
|
||||
$marshalledValue = include $fixtureFile;
|
||||
$this->assertDumpEquals($value, $marshalledValue);
|
||||
if ('incomplete-class' === $testName) {
|
||||
return;
|
||||
}
|
||||
$marshalledValue = include $fixtureFile;
|
||||
|
||||
$dump = PhpMarshaller::optimize($dump);
|
||||
$fixtureFile = __DIR__.'/Fixtures/'.$testName.'.optimized.php';
|
||||
$this->assertStringEqualsFile($fixtureFile, $dump);
|
||||
|
||||
$marshalledValue = include $fixtureFile;
|
||||
if (!$isStaticValue) {
|
||||
$this->assertDumpEquals($value, $marshalledValue);
|
||||
} else {
|
||||
$this->assertSame($value, $marshalledValue);
|
||||
|
@ -50,23 +97,23 @@ class DoctrineProviderTest extends TestCase
|
|||
|
||||
public function provideMarshall()
|
||||
{
|
||||
yield array('bool', true, 0);
|
||||
yield array('simple-array', array(123, array('abc')), 0);
|
||||
yield array('datetime', \DateTime::createFromFormat('U', 0), 1);
|
||||
yield array('bool', true, true);
|
||||
yield array('simple-array', array(123, array('abc')), true);
|
||||
yield array('datetime', \DateTime::createFromFormat('U', 0));
|
||||
|
||||
$value = new \ArrayObject();
|
||||
$value[0] = 1;
|
||||
$value->foo = new \ArrayObject();
|
||||
$value[1] = $value;
|
||||
|
||||
yield array('array-object', $value, 3);
|
||||
yield array('array-object', $value);
|
||||
|
||||
yield array('array-iterator', new \ArrayIterator(array(123), 1), 1);
|
||||
yield array('array-object-custom', new MyArrayObject(array(234)), 1);
|
||||
yield array('array-iterator', new \ArrayIterator(array(123), 1));
|
||||
yield array('array-object-custom', new MyArrayObject(array(234)));
|
||||
|
||||
$value = new MySerializable();
|
||||
|
||||
yield array('serializable', array($value, $value), 2);
|
||||
yield array('serializable', array($value, $value));
|
||||
|
||||
$value = new MyWakeup();
|
||||
$value->sub = new MyWakeup();
|
||||
|
@ -74,16 +121,29 @@ class DoctrineProviderTest extends TestCase
|
|||
$value->sub->bis = 123;
|
||||
$value->sub->baz = 123;
|
||||
|
||||
yield array('wakeup', $value, 2);
|
||||
yield array('wakeup', $value);
|
||||
|
||||
yield array('clone', array(new MyCloneable(), new MyNotCloneable()), 2);
|
||||
yield array('clone', array(new MyCloneable(), new MyNotCloneable()));
|
||||
|
||||
yield array('private', array(new MyPrivateValue(123, 234), new MyPrivateChildValue(123, 234)), 2);
|
||||
yield array('private', array(new MyPrivateValue(123, 234), new MyPrivateChildValue(123, 234)));
|
||||
|
||||
$value = new \SplObjectStorage();
|
||||
$value[new \stdClass()] = 345;
|
||||
|
||||
yield array('spl-object-storage', $value, 2);
|
||||
yield array('spl-object-storage', $value);
|
||||
|
||||
yield array('incomplete-class', unserialize('O:20:"SomeNotExistingClass":0:{}'));
|
||||
|
||||
$value = array((object) array());
|
||||
$value[1] = &$value[0];
|
||||
$value[2] = $value[0];
|
||||
|
||||
yield array('hard-references', $value);
|
||||
|
||||
$value = array();
|
||||
$value[0] = &$value;
|
||||
|
||||
yield array('hard-references-recursive', $value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -70,33 +70,29 @@ EOF;
|
|||
|
||||
foreach ($values as $key => $value) {
|
||||
CacheItem::validateKey(\is_int($key) ? (string) $key : $key);
|
||||
$objectsCount = 0;
|
||||
$isStaticValue = true;
|
||||
|
||||
if (null === $value) {
|
||||
$value = 'N;';
|
||||
$value = "'N;'";
|
||||
} elseif (\is_object($value) || \is_array($value)) {
|
||||
try {
|
||||
$e = null;
|
||||
$serialized = serialize($value);
|
||||
$value = PhpMarshaller::marshall($value, $isStaticValue);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
if (null !== $e || false === $serialized) {
|
||||
throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, \is_object($value) ? \get_class($value) : 'array'), 0, $e);
|
||||
}
|
||||
// Keep value serialized if it contains any internal references
|
||||
$value = false !== strpos($serialized, ';R:') ? $serialized : PhpMarshaller::marshall($value, $objectsCount);
|
||||
} elseif (\is_string($value)) {
|
||||
// Wrap strings if they could be confused with serialized objects or arrays
|
||||
if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) {
|
||||
++$objectsCount;
|
||||
// Wrap "N;" in a closure to not confuse it with an encoded `null`
|
||||
if ('N;' === $value) {
|
||||
$isStaticValue = false;
|
||||
}
|
||||
$value = var_export($value, true);
|
||||
} elseif (!\is_scalar($value)) {
|
||||
throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, \gettype($value)));
|
||||
} else {
|
||||
$value = var_export($value, true);
|
||||
}
|
||||
|
||||
$value = var_export($value, true);
|
||||
if ($objectsCount) {
|
||||
$value = PhpMarshaller::optimize($value);
|
||||
if (!$isStaticValue) {
|
||||
$value = "static function () {\nreturn {$value};\n}";
|
||||
}
|
||||
$hash = hash('md5', $value);
|
||||
|
|
|
@ -13,7 +13,6 @@ namespace Symfony\Component\Cache\Traits;
|
|||
|
||||
use Symfony\Component\Cache\Exception\CacheException;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
|
||||
use Symfony\Component\Cache\Marshaller\PhpMarshaller;
|
||||
|
||||
/**
|
||||
|
@ -30,7 +29,6 @@ trait PhpFilesTrait
|
|||
doDelete as private doCommonDelete;
|
||||
}
|
||||
|
||||
private $marshaller;
|
||||
private $includeHandler;
|
||||
private $appendOnly;
|
||||
private $values = array();
|
||||
|
@ -92,8 +90,6 @@ trait PhpFilesTrait
|
|||
$values[$id] = null;
|
||||
} elseif ($value instanceof \Closure) {
|
||||
$values[$id] = $value();
|
||||
} elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
||||
$values[$id] = ($this->marshaller ?? $this->marshaller = new DefaultMarshaller())->unmarshall($value);
|
||||
} else {
|
||||
$values[$id] = $value;
|
||||
}
|
||||
|
@ -165,32 +161,28 @@ trait PhpFilesTrait
|
|||
|
||||
foreach ($values as $key => $value) {
|
||||
unset($this->values[$key]);
|
||||
$objectsCount = 0;
|
||||
$isStaticValue = true;
|
||||
if (null === $value) {
|
||||
$value = 'N;';
|
||||
$value = "'N;'";
|
||||
} elseif (\is_object($value) || \is_array($value)) {
|
||||
try {
|
||||
$e = null;
|
||||
$serialized = serialize($value);
|
||||
$value = PhpMarshaller::marshall($value, $isStaticValue);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
if (null !== $e || false === $serialized) {
|
||||
throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, \is_object($value) ? \get_class($value) : 'array'), 0, $e);
|
||||
}
|
||||
// Keep value serialized if it contains any internal references
|
||||
$value = false !== strpos($serialized, ';R:') ? $serialized : PhpMarshaller::marshall($value, $objectsCount);
|
||||
} elseif (\is_string($value)) {
|
||||
// Wrap strings if they could be confused with serialized objects or arrays
|
||||
if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) {
|
||||
++$objectsCount;
|
||||
// Wrap "N;" in a closure to not confuse it with an encoded `null`
|
||||
if ('N;' === $value) {
|
||||
$isStaticValue = false;
|
||||
}
|
||||
$value = var_export($value, true);
|
||||
} elseif (!\is_scalar($value)) {
|
||||
throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, \gettype($value)));
|
||||
} else {
|
||||
$value = var_export($value, true);
|
||||
}
|
||||
|
||||
$value = var_export($value, true);
|
||||
if ($objectsCount) {
|
||||
$value = PhpMarshaller::optimize($value);
|
||||
if (!$isStaticValue) {
|
||||
$value = "static function () {\n\nreturn {$value};\n\n}";
|
||||
}
|
||||
|
||||
|
|
Reference in New Issue