[Cache] serialize objects using native arrays when possible
This commit is contained in:
parent
3ccbec3497
commit
866420e2eb
@ -20,6 +20,7 @@ return PhpCsFixer\Config::create()
|
|||||||
->append(array(__FILE__))
|
->append(array(__FILE__))
|
||||||
->exclude(array(
|
->exclude(array(
|
||||||
// directories containing files with content that is autogenerated by `var_export`, which breaks CS in output code
|
// directories containing files with content that is autogenerated by `var_export`, which breaks CS in output code
|
||||||
|
'Symfony/Component/Cache/Tests/Marshaller/Fixtures',
|
||||||
'Symfony/Component/DependencyInjection/Tests/Fixtures',
|
'Symfony/Component/DependencyInjection/Tests/Fixtures',
|
||||||
'Symfony/Component/Routing/Tests/Fixtures/dumper',
|
'Symfony/Component/Routing/Tests/Fixtures/dumper',
|
||||||
// fixture templates
|
// fixture templates
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
<argument /> <!-- namespace -->
|
<argument /> <!-- namespace -->
|
||||||
<argument>0</argument> <!-- default lifetime -->
|
<argument>0</argument> <!-- default lifetime -->
|
||||||
<argument>%kernel.cache_dir%/pools</argument>
|
<argument>%kernel.cache_dir%/pools</argument>
|
||||||
|
<argument>true</argument>
|
||||||
<call method="setLogger">
|
<call method="setLogger">
|
||||||
<argument type="service" id="logger" on-invalid="ignore" />
|
<argument type="service" id="logger" on-invalid="ignore" />
|
||||||
</call>
|
</call>
|
||||||
|
@ -87,16 +87,21 @@ class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInte
|
|||||||
if (null === $this->values) {
|
if (null === $this->values) {
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
}
|
}
|
||||||
if (null === $value = $this->values[$key] ?? null) {
|
if (!isset($this->keys[$key])) {
|
||||||
if ($this->pool instanceof CacheInterface) {
|
if ($this->pool instanceof CacheInterface) {
|
||||||
return $this->pool->get($key, $callback, $beta);
|
return $this->pool->get($key, $callback, $beta);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->doGet($this->pool, $key, $callback, $beta ?? 1.0);
|
return $this->doGet($this->pool, $key, $callback, $beta ?? 1.0);
|
||||||
}
|
}
|
||||||
|
$value = $this->values[$this->keys[$key]];
|
||||||
|
|
||||||
if ('N;' === $value) {
|
if ('N;' === $value) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if ($value instanceof \Closure) {
|
||||||
|
return $value();
|
||||||
|
}
|
||||||
if (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
if (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
||||||
return unserialize($value);
|
return unserialize($value);
|
||||||
}
|
}
|
||||||
@ -115,15 +120,22 @@ class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInte
|
|||||||
if (null === $this->values) {
|
if (null === $this->values) {
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
}
|
}
|
||||||
if (!isset($this->values[$key])) {
|
if (!isset($this->keys[$key])) {
|
||||||
return $this->pool->getItem($key);
|
return $this->pool->getItem($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
$value = $this->values[$key];
|
$value = $this->values[$this->keys[$key]];
|
||||||
$isHit = true;
|
$isHit = true;
|
||||||
|
|
||||||
if ('N;' === $value) {
|
if ('N;' === $value) {
|
||||||
$value = null;
|
$value = null;
|
||||||
|
} elseif ($value instanceof \Closure) {
|
||||||
|
try {
|
||||||
|
$value = $value();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$value = null;
|
||||||
|
$isHit = false;
|
||||||
|
}
|
||||||
} elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
} elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
||||||
try {
|
try {
|
||||||
$value = unserialize($value);
|
$value = unserialize($value);
|
||||||
@ -167,7 +179,7 @@ class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInte
|
|||||||
$this->initialize();
|
$this->initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
return isset($this->values[$key]) || $this->pool->hasItem($key);
|
return isset($this->keys[$key]) || $this->pool->hasItem($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -182,7 +194,7 @@ class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInte
|
|||||||
$this->initialize();
|
$this->initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
return !isset($this->values[$key]) && $this->pool->deleteItem($key);
|
return !isset($this->keys[$key]) && $this->pool->deleteItem($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -198,7 +210,7 @@ class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInte
|
|||||||
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', is_object($key) ? get_class($key) : gettype($key)));
|
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', is_object($key) ? get_class($key) : gettype($key)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($this->values[$key])) {
|
if (isset($this->keys[$key])) {
|
||||||
$deleted = false;
|
$deleted = false;
|
||||||
} else {
|
} else {
|
||||||
$fallbackKeys[] = $key;
|
$fallbackKeys[] = $key;
|
||||||
@ -224,7 +236,7 @@ class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInte
|
|||||||
$this->initialize();
|
$this->initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
return !isset($this->values[$item->getKey()]) && $this->pool->save($item);
|
return !isset($this->keys[$item->getKey()]) && $this->pool->save($item);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -236,7 +248,7 @@ class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInte
|
|||||||
$this->initialize();
|
$this->initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
return !isset($this->values[$item->getKey()]) && $this->pool->saveDeferred($item);
|
return !isset($this->keys[$item->getKey()]) && $this->pool->saveDeferred($item);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -253,11 +265,17 @@ class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInte
|
|||||||
$fallbackKeys = array();
|
$fallbackKeys = array();
|
||||||
|
|
||||||
foreach ($keys as $key) {
|
foreach ($keys as $key) {
|
||||||
if (isset($this->values[$key])) {
|
if (isset($this->keys[$key])) {
|
||||||
$value = $this->values[$key];
|
$value = $this->values[$this->keys[$key]];
|
||||||
|
|
||||||
if ('N;' === $value) {
|
if ('N;' === $value) {
|
||||||
yield $key => $f($key, null, true);
|
yield $key => $f($key, null, true);
|
||||||
|
} elseif ($value instanceof \Closure) {
|
||||||
|
try {
|
||||||
|
yield $key => $f($key, $value(), true);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
yield $key => $f($key, null, false);
|
||||||
|
}
|
||||||
} elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
} elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
||||||
try {
|
try {
|
||||||
yield $key => $f($key, unserialize($value), true);
|
yield $key => $f($key, unserialize($value), true);
|
||||||
|
@ -20,10 +20,14 @@ class PhpFilesAdapter extends AbstractAdapter implements PruneableInterface
|
|||||||
use PhpFilesTrait;
|
use PhpFilesTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param $appendOnly Set to `true` to gain extra performance when the items stored in this pool never expire.
|
||||||
|
* Doing so is encouraged because it fits perfectly OPcache's memory model.
|
||||||
|
*
|
||||||
* @throws CacheException if OPcache is not enabled
|
* @throws CacheException if OPcache is not enabled
|
||||||
*/
|
*/
|
||||||
public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null)
|
public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null, bool $appendOnly = false)
|
||||||
{
|
{
|
||||||
|
$this->appendOnly = $appendOnly;
|
||||||
self::$startTime = self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? time();
|
self::$startTime = self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? time();
|
||||||
parent::__construct('', $defaultLifetime);
|
parent::__construct('', $defaultLifetime);
|
||||||
$this->init($namespace, $directory);
|
$this->init($namespace, $directory);
|
||||||
|
184
src/Symfony/Component/Cache/Marshaller/PhpMarshaller.php
Normal file
184
src/Symfony/Component/Cache/Marshaller/PhpMarshaller.php
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
<?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;
|
||||||
|
|
||||||
|
use Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator;
|
||||||
|
use Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference;
|
||||||
|
use Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*
|
||||||
|
* PhpMarshaller allows serializing PHP data structures using var_export()
|
||||||
|
* while preserving all the semantics associated to serialize().
|
||||||
|
*
|
||||||
|
* By leveraging OPcache, the generated PHP code is faster than doing the same with unserialize().
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class PhpMarshaller
|
||||||
|
{
|
||||||
|
public static function marshall($value, int &$objectsCount)
|
||||||
|
{
|
||||||
|
if (!\is_object($value) && !\is_array($value)) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
$objectsPool = new \SplObjectStorage();
|
||||||
|
$value = array($value);
|
||||||
|
$objectsCount = self::doMarshall($value, $objectsPool);
|
||||||
|
|
||||||
|
$classes = array();
|
||||||
|
$values = array();
|
||||||
|
$wakeups = array();
|
||||||
|
foreach ($objectsPool as $i => $v) {
|
||||||
|
list(, $classes[], $values[], $wakeup) = $objectsPool[$v];
|
||||||
|
if ($wakeup) {
|
||||||
|
$wakeups[$wakeup] = $i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ksort($wakeups);
|
||||||
|
$properties = array();
|
||||||
|
foreach ($values as $i => $vars) {
|
||||||
|
foreach ($vars as $class => $values) {
|
||||||
|
foreach ($values as $name => $v) {
|
||||||
|
$properties[$class][$name][$i] = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$classes) {
|
||||||
|
return $value[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Configurator(new Registry($classes), $properties, $value[0], $wakeups);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,119 @@
|
|||||||
|
<?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 Configurator
|
||||||
|
{
|
||||||
|
public static $configurators = array();
|
||||||
|
|
||||||
|
public function __construct(Registry $registry, array $properties, $value, array $wakeups)
|
||||||
|
{
|
||||||
|
$this->{0} = $registry;
|
||||||
|
$this->{1} = $properties;
|
||||||
|
$this->{2} = $value;
|
||||||
|
$this->{3} = $wakeups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function __set_state($state)
|
||||||
|
{
|
||||||
|
$objects = Registry::$objects;
|
||||||
|
Registry::$objects = \array_pop(Registry::$stack);
|
||||||
|
list(, $properties, $value, $wakeups) = $state;
|
||||||
|
|
||||||
|
foreach ($properties as $class => $vars) {
|
||||||
|
(self::$configurators[$class] ?? self::getConfigurator($class))($vars, $objects);
|
||||||
|
}
|
||||||
|
foreach ($wakeups as $i) {
|
||||||
|
$objects[$i]->__wakeup();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getConfigurator($class)
|
||||||
|
{
|
||||||
|
$classReflector = Registry::$reflectors[$class] ?? Registry::getClassReflector($class);
|
||||||
|
|
||||||
|
if (!$classReflector->isInternal()) {
|
||||||
|
return self::$configurators[$class] = \Closure::bind(function ($properties, $objects) {
|
||||||
|
foreach ($properties as $name => $values) {
|
||||||
|
foreach ($values as $i => $v) {
|
||||||
|
$objects[$i]->$name = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, null, $class);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($class) {
|
||||||
|
case 'ArrayIterator':
|
||||||
|
case 'ArrayObject':
|
||||||
|
$constructor = $classReflector->getConstructor();
|
||||||
|
|
||||||
|
return self::$configurators[$class] = static function ($properties, $objects) use ($constructor) {
|
||||||
|
foreach ($properties as $name => $values) {
|
||||||
|
if ("\0" !== $name) {
|
||||||
|
foreach ($values as $i => $v) {
|
||||||
|
$objects[$i]->$name = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($properties["\0"] as $i => $v) {
|
||||||
|
$constructor->invokeArgs($objects[$i], $v);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
case 'SplObjectStorage':
|
||||||
|
return self::$configurators[$class] = static function ($properties, $objects) {
|
||||||
|
foreach ($properties as $name => $values) {
|
||||||
|
if ("\0" === $name) {
|
||||||
|
foreach ($values as $i => $v) {
|
||||||
|
for ($j = 0; $j < \count($v); ++$j) {
|
||||||
|
$objects[$i]->attach($v[$j], $v[++$j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
foreach ($values as $i => $v) {
|
||||||
|
$objects[$i]->$name = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
$propertyReflectors = array();
|
||||||
|
foreach ($classReflector->getProperties(\ReflectionProperty::IS_PROTECTED | \ReflectionProperty::IS_PRIVATE) as $propertyReflector) {
|
||||||
|
if (!$propertyReflector->isStatic()) {
|
||||||
|
$propertyReflector->setAccessible(true);
|
||||||
|
$propertyReflectors[$propertyReflector->name] = $propertyReflector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$configurators[$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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
<?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 Reference
|
||||||
|
{
|
||||||
|
public function __construct(int $id)
|
||||||
|
{
|
||||||
|
$this->{0} = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function __set_state($state)
|
||||||
|
{
|
||||||
|
return Registry::$objects[$state[0]];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
<?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 Registry
|
||||||
|
{
|
||||||
|
public static $stack = array();
|
||||||
|
public static $objects = array();
|
||||||
|
public static $reflectors = array();
|
||||||
|
public static $prototypes = array();
|
||||||
|
|
||||||
|
public function __construct(array $classes)
|
||||||
|
{
|
||||||
|
foreach ($classes as $i => $class) {
|
||||||
|
$this->$i = $class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function __set_state($classes)
|
||||||
|
{
|
||||||
|
self::$stack[] = self::$objects;
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getClassReflector($class)
|
||||||
|
{
|
||||||
|
$reflector = new \ReflectionClass($class);
|
||||||
|
|
||||||
|
if (!$reflector->hasMethod('__clone')) {
|
||||||
|
self::$prototypes[$class] = $reflector->newInstanceWithoutConstructor();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$reflectors[$class] = $reflector;
|
||||||
|
}
|
||||||
|
}
|
@ -66,15 +66,21 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
|
|||||||
if (null === $this->values) {
|
if (null === $this->values) {
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
}
|
}
|
||||||
if (!isset($this->values[$key])) {
|
if (!isset($this->keys[$key])) {
|
||||||
return $this->pool->get($key, $default);
|
return $this->pool->get($key, $default);
|
||||||
}
|
}
|
||||||
|
$value = $this->values[$this->keys[$key]];
|
||||||
$value = $this->values[$key];
|
|
||||||
|
|
||||||
if ('N;' === $value) {
|
if ('N;' === $value) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if ($value instanceof \Closure) {
|
||||||
|
try {
|
||||||
|
return $value();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
if (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
||||||
try {
|
try {
|
||||||
return unserialize($value);
|
return unserialize($value);
|
||||||
@ -120,7 +126,7 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
|
|||||||
$this->initialize();
|
$this->initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
return isset($this->values[$key]) || $this->pool->has($key);
|
return isset($this->keys[$key]) || $this->pool->has($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -135,7 +141,7 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
|
|||||||
$this->initialize();
|
$this->initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
return !isset($this->values[$key]) && $this->pool->delete($key);
|
return !isset($this->keys[$key]) && $this->pool->delete($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -155,7 +161,7 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
|
|||||||
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', is_object($key) ? get_class($key) : gettype($key)));
|
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', is_object($key) ? get_class($key) : gettype($key)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($this->values[$key])) {
|
if (isset($this->keys[$key])) {
|
||||||
$deleted = false;
|
$deleted = false;
|
||||||
} else {
|
} else {
|
||||||
$fallbackKeys[] = $key;
|
$fallbackKeys[] = $key;
|
||||||
@ -184,7 +190,7 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
|
|||||||
$this->initialize();
|
$this->initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
return !isset($this->values[$key]) && $this->pool->set($key, $value, $ttl);
|
return !isset($this->keys[$key]) && $this->pool->set($key, $value, $ttl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -204,7 +210,7 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
|
|||||||
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', is_object($key) ? get_class($key) : gettype($key)));
|
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', is_object($key) ? get_class($key) : gettype($key)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($this->values[$key])) {
|
if (isset($this->keys[$key])) {
|
||||||
$saved = false;
|
$saved = false;
|
||||||
} else {
|
} else {
|
||||||
$fallbackValues[$key] = $value;
|
$fallbackValues[$key] = $value;
|
||||||
@ -223,11 +229,17 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
|
|||||||
$fallbackKeys = array();
|
$fallbackKeys = array();
|
||||||
|
|
||||||
foreach ($keys as $key) {
|
foreach ($keys as $key) {
|
||||||
if (isset($this->values[$key])) {
|
if (isset($this->keys[$key])) {
|
||||||
$value = $this->values[$key];
|
$value = $this->values[$this->keys[$key]];
|
||||||
|
|
||||||
if ('N;' === $value) {
|
if ('N;' === $value) {
|
||||||
yield $key => null;
|
yield $key => null;
|
||||||
|
} elseif ($value instanceof \Closure) {
|
||||||
|
try {
|
||||||
|
yield $key => $value();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
yield $key => $default;
|
||||||
|
}
|
||||||
} elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
} elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
||||||
try {
|
try {
|
||||||
yield $key => unserialize($value);
|
yield $key => unserialize($value);
|
||||||
|
@ -20,10 +20,14 @@ class PhpFilesCache extends AbstractCache implements PruneableInterface
|
|||||||
use PhpFilesTrait;
|
use PhpFilesTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param $appendOnly Set to `true` to gain extra performance when the items stored in this pool never expire.
|
||||||
|
* Doing so is encouraged because it fits perfectly OPcache's memory model.
|
||||||
|
*
|
||||||
* @throws CacheException if OPcache is not enabled
|
* @throws CacheException if OPcache is not enabled
|
||||||
*/
|
*/
|
||||||
public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null)
|
public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null, bool $appendOnly = false)
|
||||||
{
|
{
|
||||||
|
$this->appendOnly = $appendOnly;
|
||||||
self::$startTime = self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? time();
|
self::$startTime = self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? time();
|
||||||
parent::__construct('', $defaultLifetime);
|
parent::__construct('', $defaultLifetime);
|
||||||
$this->init($namespace, $directory);
|
$this->init($namespace, $directory);
|
||||||
|
@ -107,16 +107,32 @@ class PhpArrayAdapterTest extends AdapterTestCase
|
|||||||
|
|
||||||
public function testStoredFile()
|
public function testStoredFile()
|
||||||
{
|
{
|
||||||
$expected = array(
|
$data = array(
|
||||||
'integer' => 42,
|
'integer' => 42,
|
||||||
'float' => 42.42,
|
'float' => 42.42,
|
||||||
'boolean' => true,
|
'boolean' => true,
|
||||||
'array_simple' => array('foo', 'bar'),
|
'array_simple' => array('foo', 'bar'),
|
||||||
'array_associative' => array('foo' => 'bar', 'foo2' => 'bar2'),
|
'array_associative' => array('foo' => 'bar', 'foo2' => 'bar2'),
|
||||||
);
|
);
|
||||||
|
$expected = array(
|
||||||
|
array(
|
||||||
|
'integer' => 0,
|
||||||
|
'float' => 1,
|
||||||
|
'boolean' => 2,
|
||||||
|
'array_simple' => 3,
|
||||||
|
'array_associative' => 4,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
0 => 42,
|
||||||
|
1 => 42.42,
|
||||||
|
2 => true,
|
||||||
|
3 => array('foo', 'bar'),
|
||||||
|
4 => array('foo' => 'bar', 'foo2' => 'bar2'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
$adapter = $this->createCachePool();
|
$adapter = $this->createCachePool();
|
||||||
$adapter->warmUp($expected);
|
$adapter->warmUp($data);
|
||||||
|
|
||||||
$values = eval(substr(file_get_contents(self::$file), 6));
|
$values = eval(substr(file_get_contents(self::$file), 6));
|
||||||
|
|
||||||
@ -126,12 +142,16 @@ class PhpArrayAdapterTest extends AdapterTestCase
|
|||||||
|
|
||||||
class PhpArrayAdapterWrapper extends PhpArrayAdapter
|
class PhpArrayAdapterWrapper extends PhpArrayAdapter
|
||||||
{
|
{
|
||||||
|
protected $data = array();
|
||||||
|
|
||||||
public function save(CacheItemInterface $item)
|
public function save(CacheItemInterface $item)
|
||||||
{
|
{
|
||||||
call_user_func(\Closure::bind(function () use ($item) {
|
call_user_func(\Closure::bind(function () use ($item) {
|
||||||
$this->values[$item->getKey()] = $item->get();
|
$key = $item->getKey();
|
||||||
$this->warmUp($this->values);
|
$this->keys[$key] = $id = \count($this->values);
|
||||||
$this->values = eval(substr(file_get_contents($this->file), 6));
|
$this->data[$key] = $this->values[$id] = $item->get();
|
||||||
|
$this->warmUp($this->data);
|
||||||
|
list($this->keys, $this->values) = eval(substr(file_get_contents($this->file), 6));
|
||||||
}, $this, PhpArrayAdapter::class));
|
}, $this, PhpArrayAdapter::class));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||||
|
'0' =>
|
||||||
|
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||||
|
'0' => 'ArrayIterator',
|
||||||
|
)),
|
||||||
|
'1' =>
|
||||||
|
array (
|
||||||
|
'ArrayIterator' =>
|
||||||
|
array (
|
||||||
|
'' . "\0" . '' =>
|
||||||
|
array (
|
||||||
|
0 =>
|
||||||
|
array (
|
||||||
|
0 =>
|
||||||
|
array (
|
||||||
|
0 => 123,
|
||||||
|
),
|
||||||
|
1 => 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'2' =>
|
||||||
|
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||||
|
'3' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
));
|
@ -0,0 +1,30 @@
|
|||||||
|
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||||
|
'0' =>
|
||||||
|
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||||
|
'0' => 'ArrayIterator',
|
||||||
|
)),
|
||||||
|
'1' =>
|
||||||
|
array (
|
||||||
|
'ArrayIterator' =>
|
||||||
|
array (
|
||||||
|
'' . "\0" . '' =>
|
||||||
|
array (
|
||||||
|
0 =>
|
||||||
|
array (
|
||||||
|
0 =>
|
||||||
|
array (
|
||||||
|
0 => 123,
|
||||||
|
),
|
||||||
|
1 => 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'2' =>
|
||||||
|
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||||
|
'0' => 0,
|
||||||
|
)),
|
||||||
|
'3' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
));
|
@ -0,0 +1,28 @@
|
|||||||
|
<?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 (
|
||||||
|
),
|
||||||
|
));
|
@ -0,0 +1,30 @@
|
|||||||
|
<?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\Reference::__set_state(array(
|
||||||
|
'0' => 0,
|
||||||
|
)),
|
||||||
|
'3' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
));
|
@ -0,0 +1,36 @@
|
|||||||
|
<?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 (
|
||||||
|
),
|
||||||
|
));
|
@ -0,0 +1,42 @@
|
|||||||
|
<?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\Reference::__set_state(array(
|
||||||
|
'0' => 0,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
1 => 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'foo' =>
|
||||||
|
array (
|
||||||
|
0 =>
|
||||||
|
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||||
|
'0' => 1,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'2' =>
|
||||||
|
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||||
|
'0' => 0,
|
||||||
|
)),
|
||||||
|
'3' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
));
|
@ -0,0 +1 @@
|
|||||||
|
<?php return true;
|
@ -0,0 +1,20 @@
|
|||||||
|
<?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 (
|
||||||
|
),
|
||||||
|
));
|
@ -0,0 +1,24 @@
|
|||||||
|
<?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\Reference::__set_state(array(
|
||||||
|
'0' => 0,
|
||||||
|
)),
|
||||||
|
1 =>
|
||||||
|
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||||
|
'0' => 1,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
'3' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
));
|
@ -0,0 +1,30 @@
|
|||||||
|
<?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,
|
||||||
|
),
|
||||||
|
));
|
@ -0,0 +1,32 @@
|
|||||||
|
<?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\Reference::__set_state(array(
|
||||||
|
'0' => 0,
|
||||||
|
)),
|
||||||
|
'3' =>
|
||||||
|
array (
|
||||||
|
1 => 0,
|
||||||
|
),
|
||||||
|
));
|
@ -0,0 +1,39 @@
|
|||||||
|
<?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 (
|
||||||
|
),
|
||||||
|
));
|
@ -0,0 +1,43 @@
|
|||||||
|
<?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\Reference::__set_state(array(
|
||||||
|
'0' => 0,
|
||||||
|
)),
|
||||||
|
1 =>
|
||||||
|
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||||
|
'0' => 1,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
'3' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
));
|
@ -0,0 +1,19 @@
|
|||||||
|
<?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}',
|
||||||
|
)),
|
||||||
|
'1' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
'2' =>
|
||||||
|
array (
|
||||||
|
0 =>
|
||||||
|
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||||
|
1 =>
|
||||||
|
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||||
|
),
|
||||||
|
'3' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
));
|
@ -0,0 +1,23 @@
|
|||||||
|
<?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}',
|
||||||
|
)),
|
||||||
|
'1' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
'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,0 +1,7 @@
|
|||||||
|
<?php return array (
|
||||||
|
0 => 123,
|
||||||
|
1 =>
|
||||||
|
array (
|
||||||
|
0 => 'abc',
|
||||||
|
),
|
||||||
|
);
|
@ -0,0 +1,27 @@
|
|||||||
|
<?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 (
|
||||||
|
),
|
||||||
|
));
|
@ -0,0 +1,31 @@
|
|||||||
|
<?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\Reference::__set_state(array(
|
||||||
|
'0' => 1,
|
||||||
|
)),
|
||||||
|
1 => 345,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'2' =>
|
||||||
|
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||||
|
'0' => 0,
|
||||||
|
)),
|
||||||
|
'3' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
));
|
@ -0,0 +1,30 @@
|
|||||||
|
<?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,
|
||||||
|
),
|
||||||
|
));
|
@ -0,0 +1,34 @@
|
|||||||
|
<?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\Reference::__set_state(array(
|
||||||
|
'0' => 1,
|
||||||
|
)),
|
||||||
|
1 => 123,
|
||||||
|
),
|
||||||
|
'baz' =>
|
||||||
|
array (
|
||||||
|
1 => 123,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'2' =>
|
||||||
|
Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference::__set_state(array(
|
||||||
|
'0' => 0,
|
||||||
|
)),
|
||||||
|
'3' =>
|
||||||
|
array (
|
||||||
|
1 => 1,
|
||||||
|
2 => 0,
|
||||||
|
),
|
||||||
|
));
|
@ -0,0 +1,169 @@
|
|||||||
|
<?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\Tests\Marshaller;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\Cache\Marshaller\PhpMarshaller;
|
||||||
|
use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
|
||||||
|
|
||||||
|
class DoctrineProviderTest extends TestCase
|
||||||
|
{
|
||||||
|
use VarDumperTestTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideMarshall
|
||||||
|
*/
|
||||||
|
public function testMarshall(string $testName, $value, int $expectedObjectsCount)
|
||||||
|
{
|
||||||
|
$objectsCount = 0;
|
||||||
|
$marshalledValue = PhpMarshaller::marshall($value, $objectsCount);
|
||||||
|
|
||||||
|
$this->assertSame($expectedObjectsCount, $objectsCount);
|
||||||
|
|
||||||
|
$dump = '<?php return '.var_export($marshalledValue, true).";\n";
|
||||||
|
$fixtureFile = __DIR__.'/Fixtures/'.$testName.'.php';
|
||||||
|
$this->assertStringEqualsFile($fixtureFile, $dump);
|
||||||
|
|
||||||
|
if ($objectsCount) {
|
||||||
|
$marshalledValue = include $fixtureFile;
|
||||||
|
$this->assertDumpEquals($value, $marshalledValue);
|
||||||
|
|
||||||
|
$dump = PhpMarshaller::optimize($dump);
|
||||||
|
$fixtureFile = __DIR__.'/Fixtures/'.$testName.'.optimized.php';
|
||||||
|
$this->assertStringEqualsFile($fixtureFile, $dump);
|
||||||
|
|
||||||
|
$marshalledValue = include $fixtureFile;
|
||||||
|
$this->assertDumpEquals($value, $marshalledValue);
|
||||||
|
} else {
|
||||||
|
$this->assertSame($value, $marshalledValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideMarshall()
|
||||||
|
{
|
||||||
|
yield ['bool', true, 0];
|
||||||
|
yield ['simple-array', [123, ['abc']], 0];
|
||||||
|
yield ['datetime', \DateTime::createFromFormat('U', 0), 1];
|
||||||
|
|
||||||
|
$value = new \ArrayObject();
|
||||||
|
$value[0] = 1;
|
||||||
|
$value->foo = new \ArrayObject();
|
||||||
|
$value[1] = $value;
|
||||||
|
|
||||||
|
yield ['array-object', $value, 3];
|
||||||
|
|
||||||
|
yield array('array-iterator', new \ArrayIterator(array(123), 1), 1);
|
||||||
|
yield array('array-object-custom', new MyArrayObject(array(234)), 1);
|
||||||
|
|
||||||
|
$value = new MySerializable();
|
||||||
|
|
||||||
|
yield ['serializable', array($value, $value), 2];
|
||||||
|
|
||||||
|
$value = new MyWakeup();
|
||||||
|
$value->sub = new MyWakeup();
|
||||||
|
$value->sub->sub = 123;
|
||||||
|
$value->sub->bis = 123;
|
||||||
|
$value->sub->baz = 123;
|
||||||
|
|
||||||
|
yield ['wakeup', $value, 2];
|
||||||
|
|
||||||
|
yield ['clone', array(new MyCloneable(), new MyNotCloneable()), 2];
|
||||||
|
|
||||||
|
yield ['private', array(new MyPrivateValue(123, 234), new MyPrivateChildValue(123, 234)), 2];
|
||||||
|
|
||||||
|
$value = new \SplObjectStorage();
|
||||||
|
$value[new \stdClass()] = 345;
|
||||||
|
|
||||||
|
yield ['spl-object-storage', $value, 2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MySerializable implements \Serializable
|
||||||
|
{
|
||||||
|
public function serialize()
|
||||||
|
{
|
||||||
|
return '123';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unserialize($data)
|
||||||
|
{
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyWakeup
|
||||||
|
{
|
||||||
|
public $sub;
|
||||||
|
public $bis;
|
||||||
|
public $baz;
|
||||||
|
public $def = 234;
|
||||||
|
|
||||||
|
public function __sleep()
|
||||||
|
{
|
||||||
|
return array('sub', 'baz');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __wakeup()
|
||||||
|
{
|
||||||
|
if (123 === $this->sub) {
|
||||||
|
$this->bis = 123;
|
||||||
|
$this->baz = 123;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyCloneable
|
||||||
|
{
|
||||||
|
public function __clone()
|
||||||
|
{
|
||||||
|
throw new \Exception('__clone should never be called');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyNotCloneable
|
||||||
|
{
|
||||||
|
private function __clone()
|
||||||
|
{
|
||||||
|
throw new \Exception('__clone should never be called');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyPrivateValue
|
||||||
|
{
|
||||||
|
protected $prot;
|
||||||
|
private $priv;
|
||||||
|
|
||||||
|
public function __construct($prot, $priv)
|
||||||
|
{
|
||||||
|
$this->prot = $prot;
|
||||||
|
$this->priv = $priv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyPrivateChildValue extends MyPrivateValue
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyArrayObject extends \ArrayObject
|
||||||
|
{
|
||||||
|
private $unused = 123;
|
||||||
|
|
||||||
|
public function __construct(array $array)
|
||||||
|
{
|
||||||
|
parent::__construct($array, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setFlags($flags)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Calling MyArrayObject::setFlags() is forbidden');
|
||||||
|
}
|
||||||
|
}
|
@ -95,16 +95,32 @@ class PhpArrayCacheTest extends CacheTestCase
|
|||||||
|
|
||||||
public function testStoredFile()
|
public function testStoredFile()
|
||||||
{
|
{
|
||||||
$expected = array(
|
$data = array(
|
||||||
'integer' => 42,
|
'integer' => 42,
|
||||||
'float' => 42.42,
|
'float' => 42.42,
|
||||||
'boolean' => true,
|
'boolean' => true,
|
||||||
'array_simple' => array('foo', 'bar'),
|
'array_simple' => array('foo', 'bar'),
|
||||||
'array_associative' => array('foo' => 'bar', 'foo2' => 'bar2'),
|
'array_associative' => array('foo' => 'bar', 'foo2' => 'bar2'),
|
||||||
);
|
);
|
||||||
|
$expected = array(
|
||||||
|
array(
|
||||||
|
'integer' => 0,
|
||||||
|
'float' => 1,
|
||||||
|
'boolean' => 2,
|
||||||
|
'array_simple' => 3,
|
||||||
|
'array_associative' => 4,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
0 => 42,
|
||||||
|
1 => 42.42,
|
||||||
|
2 => true,
|
||||||
|
3 => array('foo', 'bar'),
|
||||||
|
4 => array('foo' => 'bar', 'foo2' => 'bar2'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
$cache = new PhpArrayCache(self::$file, new NullCache());
|
$cache = new PhpArrayCache(self::$file, new NullCache());
|
||||||
$cache->warmUp($expected);
|
$cache->warmUp($data);
|
||||||
|
|
||||||
$values = eval(substr(file_get_contents(self::$file), 6));
|
$values = eval(substr(file_get_contents(self::$file), 6));
|
||||||
|
|
||||||
@ -114,12 +130,14 @@ class PhpArrayCacheTest extends CacheTestCase
|
|||||||
|
|
||||||
class PhpArrayCacheWrapper extends PhpArrayCache
|
class PhpArrayCacheWrapper extends PhpArrayCache
|
||||||
{
|
{
|
||||||
|
protected $data = array();
|
||||||
|
|
||||||
public function set($key, $value, $ttl = null)
|
public function set($key, $value, $ttl = null)
|
||||||
{
|
{
|
||||||
call_user_func(\Closure::bind(function () use ($key, $value) {
|
call_user_func(\Closure::bind(function () use ($key, $value) {
|
||||||
$this->values[$key] = $value;
|
$this->data[$key] = $value;
|
||||||
$this->warmUp($this->values);
|
$this->warmUp($this->data);
|
||||||
$this->values = eval(substr(file_get_contents($this->file), 6));
|
list($this->keys, $this->values) = eval(substr(file_get_contents($this->file), 6));
|
||||||
}, $this, PhpArrayCache::class));
|
}, $this, PhpArrayCache::class));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -132,10 +150,10 @@ class PhpArrayCacheWrapper extends PhpArrayCache
|
|||||||
}
|
}
|
||||||
call_user_func(\Closure::bind(function () use ($values) {
|
call_user_func(\Closure::bind(function () use ($values) {
|
||||||
foreach ($values as $key => $value) {
|
foreach ($values as $key => $value) {
|
||||||
$this->values[$key] = $value;
|
$this->data[$key] = $value;
|
||||||
}
|
}
|
||||||
$this->warmUp($this->values);
|
$this->warmUp($this->data);
|
||||||
$this->values = eval(substr(file_get_contents($this->file), 6));
|
list($this->keys, $this->values) = eval(substr(file_get_contents($this->file), 6));
|
||||||
}, $this, PhpArrayCache::class));
|
}, $this, PhpArrayCache::class));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -52,11 +52,14 @@ trait ApcuTrait
|
|||||||
protected function doFetch(array $ids)
|
protected function doFetch(array $ids)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
$values = array();
|
||||||
foreach (apcu_fetch($ids, $ok) ?: array() as $k => $v) {
|
foreach (apcu_fetch($ids, $ok) ?: array() as $k => $v) {
|
||||||
if (null !== $v || $ok) {
|
if (null !== $v || $ok) {
|
||||||
yield $k => $v;
|
$values[$k] = $v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $values;
|
||||||
} catch (\Error $e) {
|
} catch (\Error $e) {
|
||||||
throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
|
throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Cache\Traits;
|
|||||||
|
|
||||||
use Symfony\Component\Cache\CacheItem;
|
use Symfony\Component\Cache\CacheItem;
|
||||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||||
|
use Symfony\Component\Cache\Marshaller\PhpMarshaller;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||||
@ -25,6 +26,7 @@ trait PhpArrayTrait
|
|||||||
use ProxyTrait;
|
use ProxyTrait;
|
||||||
|
|
||||||
private $file;
|
private $file;
|
||||||
|
private $keys;
|
||||||
private $values;
|
private $values;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,55 +56,66 @@ trait PhpArrayTrait
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$dumpedValues = '';
|
||||||
|
$dumpedMap = array();
|
||||||
$dump = <<<'EOF'
|
$dump = <<<'EOF'
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
// This file has been auto-generated by the Symfony Cache Component.
|
// This file has been auto-generated by the Symfony Cache Component.
|
||||||
|
|
||||||
return array(
|
return array(array(
|
||||||
|
|
||||||
|
|
||||||
EOF;
|
EOF;
|
||||||
|
|
||||||
foreach ($values as $key => $value) {
|
foreach ($values as $key => $value) {
|
||||||
CacheItem::validateKey(\is_int($key) ? (string) $key : $key);
|
CacheItem::validateKey(\is_int($key) ? (string) $key : $key);
|
||||||
|
$objectsCount = 0;
|
||||||
|
|
||||||
if (null === $value || \is_object($value)) {
|
if (null === $value) {
|
||||||
try {
|
$value = 'N;';
|
||||||
$value = serialize($value);
|
} elseif (\is_object($value) || \is_array($value)) {
|
||||||
} catch (\Exception $e) {
|
|
||||||
throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, get_class($value)), 0, $e);
|
|
||||||
}
|
|
||||||
} elseif (\is_array($value)) {
|
|
||||||
try {
|
try {
|
||||||
|
$e = null;
|
||||||
$serialized = serialize($value);
|
$serialized = serialize($value);
|
||||||
$unserialized = unserialize($serialized);
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable array value.', $key), 0, $e);
|
|
||||||
}
|
}
|
||||||
// Store arrays serialized if they contain any objects or references
|
if (null !== $e || false === $serialized) {
|
||||||
if ($unserialized !== $value || (false !== strpos($serialized, ';R:') && preg_match('/;R:[1-9]/', $serialized))) {
|
throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, \is_object($value) ? get_class($value) : 'array'), 0, $e);
|
||||||
$value = $serialized;
|
|
||||||
}
|
}
|
||||||
|
// Keep value serialized if it contains any internal references
|
||||||
|
$value = false !== strpos($serialized, ';R:') ? $serialized : PhpMarshaller::marshall($value, $objectsCount);
|
||||||
} elseif (\is_string($value)) {
|
} elseif (\is_string($value)) {
|
||||||
// Serialize strings if they could be confused with serialized objects or arrays
|
// Wrap strings if they could be confused with serialized objects or arrays
|
||||||
if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) {
|
if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) {
|
||||||
$value = serialize($value);
|
++$objectsCount;
|
||||||
}
|
}
|
||||||
} elseif (!\is_scalar($value)) {
|
} elseif (!\is_scalar($value)) {
|
||||||
throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, gettype($value)));
|
throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, gettype($value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
$dump .= var_export($key, true).' => '.var_export($value, true).",\n";
|
$value = var_export($value, true);
|
||||||
|
if ($objectsCount) {
|
||||||
|
$value = PhpMarshaller::optimize($value);
|
||||||
|
$value = "static function () {\nreturn {$value};\n}";
|
||||||
|
}
|
||||||
|
$hash = hash('md5', $value);
|
||||||
|
|
||||||
|
if (null === $id = $dumpedMap[$hash] ?? null) {
|
||||||
|
$id = $dumpedMap[$hash] = \count($dumpedMap);
|
||||||
|
$dumpedValues .= "{$id} => {$value},\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
$dump .= "\n);\n";
|
$dump .= var_export($key, true)." => {$id},\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$dump .= "\n), array(\n\n{$dumpedValues}\n));\n";
|
||||||
|
|
||||||
$tmpFile = uniqid($this->file, true);
|
$tmpFile = uniqid($this->file, true);
|
||||||
|
|
||||||
file_put_contents($tmpFile, $dump);
|
file_put_contents($tmpFile, $dump);
|
||||||
@chmod($tmpFile, 0666 & ~umask());
|
@chmod($tmpFile, 0666 & ~umask());
|
||||||
unset($serialized, $unserialized, $value, $dump);
|
unset($serialized, $value, $dump);
|
||||||
|
|
||||||
@rename($tmpFile, $this->file);
|
@rename($tmpFile, $this->file);
|
||||||
|
|
||||||
@ -114,7 +127,7 @@ EOF;
|
|||||||
*/
|
*/
|
||||||
public function clear()
|
public function clear()
|
||||||
{
|
{
|
||||||
$this->values = array();
|
$this->keys = $this->values = array();
|
||||||
|
|
||||||
$cleared = @unlink($this->file) || !file_exists($this->file);
|
$cleared = @unlink($this->file) || !file_exists($this->file);
|
||||||
|
|
||||||
@ -126,6 +139,17 @@ EOF;
|
|||||||
*/
|
*/
|
||||||
private function initialize()
|
private function initialize()
|
||||||
{
|
{
|
||||||
$this->values = file_exists($this->file) ? (include $this->file ?: array()) : array();
|
if (!file_exists($this->file)) {
|
||||||
|
$this->keys = $this->values = array();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$values = (include $this->file) ?: array(array(), array());
|
||||||
|
|
||||||
|
if (2 !== \count($values) || !isset($values[0], $values[1])) {
|
||||||
|
$this->keys = $this->values = array();
|
||||||
|
} else {
|
||||||
|
list($this->keys, $this->values) = $values;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Cache\Traits;
|
|||||||
|
|
||||||
use Symfony\Component\Cache\Exception\CacheException;
|
use Symfony\Component\Cache\Exception\CacheException;
|
||||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||||
|
use Symfony\Component\Cache\Marshaller\PhpMarshaller;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Piotr Stankowski <git@trakos.pl>
|
* @author Piotr Stankowski <git@trakos.pl>
|
||||||
@ -23,9 +24,15 @@ use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
|||||||
*/
|
*/
|
||||||
trait PhpFilesTrait
|
trait PhpFilesTrait
|
||||||
{
|
{
|
||||||
use FilesystemCommonTrait;
|
use FilesystemCommonTrait {
|
||||||
|
doClear as private doCommonClear;
|
||||||
|
doDelete as private doCommonDelete;
|
||||||
|
}
|
||||||
|
|
||||||
private $includeHandler;
|
private $includeHandler;
|
||||||
|
private $appendOnly;
|
||||||
|
private $values = array();
|
||||||
|
private $files = array();
|
||||||
|
|
||||||
private static $startTime;
|
private static $startTime;
|
||||||
|
|
||||||
@ -65,35 +72,58 @@ trait PhpFilesTrait
|
|||||||
*/
|
*/
|
||||||
protected function doFetch(array $ids)
|
protected function doFetch(array $ids)
|
||||||
{
|
{
|
||||||
$values = array();
|
if ($this->appendOnly) {
|
||||||
|
$now = 0;
|
||||||
|
$missingIds = array();
|
||||||
|
} else {
|
||||||
$now = time();
|
$now = time();
|
||||||
|
$missingIds = $ids;
|
||||||
|
$ids = array();
|
||||||
|
}
|
||||||
|
$values = array();
|
||||||
|
|
||||||
|
begin:
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
if (null === $value = $this->values[$id] ?? null) {
|
||||||
|
$missingIds[] = $id;
|
||||||
|
} elseif ('N;' === $value) {
|
||||||
|
$values[$id] = null;
|
||||||
|
} elseif ($value instanceof \Closure) {
|
||||||
|
$values[$id] = $value();
|
||||||
|
} elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
||||||
|
$values[$id] = parent::unserialize($value);
|
||||||
|
} else {
|
||||||
|
$values[$id] = $value;
|
||||||
|
}
|
||||||
|
if (!$this->appendOnly) {
|
||||||
|
unset($this->values[$id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$missingIds) {
|
||||||
|
return $values;
|
||||||
|
}
|
||||||
|
|
||||||
set_error_handler($this->includeHandler);
|
set_error_handler($this->includeHandler);
|
||||||
try {
|
try {
|
||||||
foreach ($ids as $id) {
|
foreach ($missingIds as $k => $id) {
|
||||||
try {
|
try {
|
||||||
$file = $this->getFile($id);
|
$file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
|
||||||
list($expiresAt, $values[$id]) = include $file;
|
list($expiresAt, $this->values[$id]) = include $file;
|
||||||
if ($now >= $expiresAt) {
|
if ($now >= $expiresAt) {
|
||||||
unset($values[$id]);
|
unset($this->values[$id], $missingIds[$k]);
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
continue;
|
unset($missingIds[$k]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
restore_error_handler();
|
restore_error_handler();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($values as $id => $value) {
|
$ids = $missingIds;
|
||||||
if ('N;' === $value) {
|
$missingIds = array();
|
||||||
$values[$id] = null;
|
goto begin;
|
||||||
} elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
|
||||||
$values[$id] = parent::unserialize($value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $values;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -101,7 +131,25 @@ trait PhpFilesTrait
|
|||||||
*/
|
*/
|
||||||
protected function doHave($id)
|
protected function doHave($id)
|
||||||
{
|
{
|
||||||
return (bool) $this->doFetch(array($id));
|
if ($this->appendOnly && $this->values[$id]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_error_handler($this->includeHandler);
|
||||||
|
try {
|
||||||
|
$file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
|
||||||
|
list($expiresAt, $value) = include $file;
|
||||||
|
} finally {
|
||||||
|
restore_error_handler();
|
||||||
|
}
|
||||||
|
if ($this->appendOnly) {
|
||||||
|
$now = 0;
|
||||||
|
$this->values[$id] = $value;
|
||||||
|
} else {
|
||||||
|
$now = time();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $now < $expiresAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -110,35 +158,47 @@ trait PhpFilesTrait
|
|||||||
protected function doSave(array $values, $lifetime)
|
protected function doSave(array $values, $lifetime)
|
||||||
{
|
{
|
||||||
$ok = true;
|
$ok = true;
|
||||||
$data = array($lifetime ? time() + $lifetime : PHP_INT_MAX, '');
|
$expiry = $lifetime ? time() + $lifetime : 'PHP_INT_MAX';
|
||||||
$allowCompile = self::isSupported();
|
$allowCompile = self::isSupported();
|
||||||
|
|
||||||
foreach ($values as $key => $value) {
|
foreach ($values as $key => $value) {
|
||||||
if (null === $value || \is_object($value)) {
|
unset($this->values[$key]);
|
||||||
$value = serialize($value);
|
$objectsCount = 0;
|
||||||
} elseif (\is_array($value)) {
|
if (null === $value) {
|
||||||
|
$value = 'N;';
|
||||||
|
} elseif (\is_object($value) || \is_array($value)) {
|
||||||
|
try {
|
||||||
|
$e = null;
|
||||||
$serialized = serialize($value);
|
$serialized = serialize($value);
|
||||||
$unserialized = parent::unserialize($serialized);
|
} catch (\Exception $e) {
|
||||||
// Store arrays serialized if they contain any objects or references
|
|
||||||
if ($unserialized !== $value || (false !== strpos($serialized, ';R:') && preg_match('/;R:[1-9]/', $serialized))) {
|
|
||||||
$value = $serialized;
|
|
||||||
}
|
}
|
||||||
|
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)) {
|
} elseif (\is_string($value)) {
|
||||||
// Serialize strings if they could be confused with serialized objects or arrays
|
// Wrap strings if they could be confused with serialized objects or arrays
|
||||||
if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) {
|
if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) {
|
||||||
$value = serialize($value);
|
++$objectsCount;
|
||||||
}
|
}
|
||||||
} elseif (!\is_scalar($value)) {
|
} elseif (!\is_scalar($value)) {
|
||||||
throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, gettype($value)));
|
throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, gettype($value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
$data[1] = $value;
|
$value = var_export($value, true);
|
||||||
$file = $this->getFile($key, true);
|
if ($objectsCount) {
|
||||||
|
$value = PhpMarshaller::optimize($value);
|
||||||
|
$value = "static function () {\n\nreturn {$value};\n\n}";
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $this->files[$key] = $this->getFile($key, true);
|
||||||
// Since OPcache only compiles files older than the script execution start, set the file's mtime in the past
|
// Since OPcache only compiles files older than the script execution start, set the file's mtime in the past
|
||||||
$ok = $this->write($file, '<?php return '.var_export($data, true).';', self::$startTime - 10) && $ok;
|
$ok = $this->write($file, "<?php return array({$expiry}, {$value});\n", self::$startTime - 10) && $ok;
|
||||||
|
|
||||||
if ($allowCompile) {
|
if ($allowCompile) {
|
||||||
@opcache_invalidate($file, true);
|
@opcache_invalidate($file, true);
|
||||||
|
@opcache_compile_file($file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,6 +209,28 @@ trait PhpFilesTrait
|
|||||||
return $ok;
|
return $ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doClear($namespace)
|
||||||
|
{
|
||||||
|
$this->values = array();
|
||||||
|
|
||||||
|
return $this->doCommonClear($namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doDelete(array $ids)
|
||||||
|
{
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
unset($this->values[$id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->doCommonDelete($ids);
|
||||||
|
}
|
||||||
|
|
||||||
protected function doUnlink($file)
|
protected function doUnlink($file)
|
||||||
{
|
{
|
||||||
if (self::isSupported()) {
|
if (self::isSupported()) {
|
||||||
|
@ -29,7 +29,8 @@
|
|||||||
"cache/integration-tests": "dev-master",
|
"cache/integration-tests": "dev-master",
|
||||||
"doctrine/cache": "~1.6",
|
"doctrine/cache": "~1.6",
|
||||||
"doctrine/dbal": "~2.4",
|
"doctrine/dbal": "~2.4",
|
||||||
"predis/predis": "~1.0"
|
"predis/predis": "~1.0",
|
||||||
|
"symfony/var-dumper": "^4.1.1"
|
||||||
},
|
},
|
||||||
"conflict": {
|
"conflict": {
|
||||||
"symfony/var-dumper": "<3.4"
|
"symfony/var-dumper": "<3.4"
|
||||||
|
Reference in New Issue
Block a user