feature #27543 [Cache] serialize objects using native arrays when possible (nicolas-grekas)
This PR was merged into the 4.2-dev branch.
Discussion
----------
[Cache] serialize objects using native arrays when possible
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | no
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | -
| License | MIT
| Doc PR | -
This PR allows leveraging OPCache shared memory when storing objects in `Php*` pool storages (as done by default for all system caches). This improves performance a bit further when loading e.g. annotations, etc. (bench coming);
Instead of using native php serialization, this uses a marshaller that represents objects in plain static arrays. Unmarshalling these arrays is faster than unserializing the corresponding PHP strings (because it works with copy-on-write, while unserialize cannot.)
php-serialization is still a possible format because we have to use it when serializing structures with internal references or with objects implementing `Serializable`. The best serialization format is selected automatically so this is completely seamless.
ping @palex-fpt since you gave me the push to work on this, and are pursuing a similar goal in #27484. I'd be thrilled to get some benchmarks on your scenarios.
Commits
-------
866420e2eb
[Cache] serialize objects using native arrays when possible
This commit is contained in:
commit
c0ca2afbb3
@ -20,6 +20,7 @@ return PhpCsFixer\Config::create()
|
||||
->append(array(__FILE__))
|
||||
->exclude(array(
|
||||
// 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/Routing/Tests/Fixtures/dumper',
|
||||
// fixture templates
|
||||
|
@ -41,6 +41,7 @@
|
||||
<argument /> <!-- namespace -->
|
||||
<argument>0</argument> <!-- default lifetime -->
|
||||
<argument>%kernel.cache_dir%/pools</argument>
|
||||
<argument>true</argument>
|
||||
<call method="setLogger">
|
||||
<argument type="service" id="logger" on-invalid="ignore" />
|
||||
</call>
|
||||
|
@ -87,16 +87,21 @@ class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInte
|
||||
if (null === $this->values) {
|
||||
$this->initialize();
|
||||
}
|
||||
if (null === $value = $this->values[$key] ?? null) {
|
||||
if (!isset($this->keys[$key])) {
|
||||
if ($this->pool instanceof CacheInterface) {
|
||||
return $this->pool->get($key, $callback, $beta);
|
||||
}
|
||||
|
||||
return $this->doGet($this->pool, $key, $callback, $beta ?? 1.0);
|
||||
}
|
||||
$value = $this->values[$this->keys[$key]];
|
||||
|
||||
if ('N;' === $value) {
|
||||
return null;
|
||||
}
|
||||
if ($value instanceof \Closure) {
|
||||
return $value();
|
||||
}
|
||||
if (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
||||
return unserialize($value);
|
||||
}
|
||||
@ -115,15 +120,22 @@ class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInte
|
||||
if (null === $this->values) {
|
||||
$this->initialize();
|
||||
}
|
||||
if (!isset($this->values[$key])) {
|
||||
if (!isset($this->keys[$key])) {
|
||||
return $this->pool->getItem($key);
|
||||
}
|
||||
|
||||
$value = $this->values[$key];
|
||||
$value = $this->values[$this->keys[$key]];
|
||||
$isHit = true;
|
||||
|
||||
if ('N;' === $value) {
|
||||
$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]) {
|
||||
try {
|
||||
$value = unserialize($value);
|
||||
@ -167,7 +179,7 @@ class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInte
|
||||
$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();
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
if (isset($this->values[$key])) {
|
||||
if (isset($this->keys[$key])) {
|
||||
$deleted = false;
|
||||
} else {
|
||||
$fallbackKeys[] = $key;
|
||||
@ -224,7 +236,7 @@ class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInte
|
||||
$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();
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (isset($this->values[$key])) {
|
||||
$value = $this->values[$key];
|
||||
if (isset($this->keys[$key])) {
|
||||
$value = $this->values[$this->keys[$key]];
|
||||
|
||||
if ('N;' === $value) {
|
||||
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]) {
|
||||
try {
|
||||
yield $key => $f($key, unserialize($value), true);
|
||||
|
@ -20,10 +20,14 @@ class PhpFilesAdapter extends AbstractAdapter implements PruneableInterface
|
||||
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
|
||||
*/
|
||||
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();
|
||||
parent::__construct('', $defaultLifetime);
|
||||
$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) {
|
||||
$this->initialize();
|
||||
}
|
||||
if (!isset($this->values[$key])) {
|
||||
if (!isset($this->keys[$key])) {
|
||||
return $this->pool->get($key, $default);
|
||||
}
|
||||
|
||||
$value = $this->values[$key];
|
||||
$value = $this->values[$this->keys[$key]];
|
||||
|
||||
if ('N;' === $value) {
|
||||
return null;
|
||||
}
|
||||
if ($value instanceof \Closure) {
|
||||
try {
|
||||
return $value();
|
||||
} catch (\Throwable $e) {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
if (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
||||
try {
|
||||
return unserialize($value);
|
||||
@ -120,7 +126,7 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
|
||||
$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();
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
if (isset($this->values[$key])) {
|
||||
if (isset($this->keys[$key])) {
|
||||
$deleted = false;
|
||||
} else {
|
||||
$fallbackKeys[] = $key;
|
||||
@ -184,7 +190,7 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
|
||||
$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)));
|
||||
}
|
||||
|
||||
if (isset($this->values[$key])) {
|
||||
if (isset($this->keys[$key])) {
|
||||
$saved = false;
|
||||
} else {
|
||||
$fallbackValues[$key] = $value;
|
||||
@ -223,11 +229,17 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
|
||||
$fallbackKeys = array();
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (isset($this->values[$key])) {
|
||||
$value = $this->values[$key];
|
||||
if (isset($this->keys[$key])) {
|
||||
$value = $this->values[$this->keys[$key]];
|
||||
|
||||
if ('N;' === $value) {
|
||||
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]) {
|
||||
try {
|
||||
yield $key => unserialize($value);
|
||||
|
@ -20,10 +20,14 @@ class PhpFilesCache extends AbstractCache implements PruneableInterface
|
||||
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
|
||||
*/
|
||||
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();
|
||||
parent::__construct('', $defaultLifetime);
|
||||
$this->init($namespace, $directory);
|
||||
|
@ -107,16 +107,32 @@ class PhpArrayAdapterTest extends AdapterTestCase
|
||||
|
||||
public function testStoredFile()
|
||||
{
|
||||
$expected = array(
|
||||
$data = array(
|
||||
'integer' => 42,
|
||||
'float' => 42.42,
|
||||
'boolean' => true,
|
||||
'array_simple' => array('foo', 'bar'),
|
||||
'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->warmUp($expected);
|
||||
$adapter->warmUp($data);
|
||||
|
||||
$values = eval(substr(file_get_contents(self::$file), 6));
|
||||
|
||||
@ -126,12 +142,16 @@ class PhpArrayAdapterTest extends AdapterTestCase
|
||||
|
||||
class PhpArrayAdapterWrapper extends PhpArrayAdapter
|
||||
{
|
||||
protected $data = array();
|
||||
|
||||
public function save(CacheItemInterface $item)
|
||||
{
|
||||
call_user_func(\Closure::bind(function () use ($item) {
|
||||
$this->values[$item->getKey()] = $item->get();
|
||||
$this->warmUp($this->values);
|
||||
$this->values = eval(substr(file_get_contents($this->file), 6));
|
||||
$key = $item->getKey();
|
||||
$this->keys[$key] = $id = \count($this->values);
|
||||
$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));
|
||||
|
||||
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()
|
||||
{
|
||||
$expected = array(
|
||||
$data = array(
|
||||
'integer' => 42,
|
||||
'float' => 42.42,
|
||||
'boolean' => true,
|
||||
'array_simple' => array('foo', 'bar'),
|
||||
'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->warmUp($expected);
|
||||
$cache->warmUp($data);
|
||||
|
||||
$values = eval(substr(file_get_contents(self::$file), 6));
|
||||
|
||||
@ -114,12 +130,14 @@ class PhpArrayCacheTest extends CacheTestCase
|
||||
|
||||
class PhpArrayCacheWrapper extends PhpArrayCache
|
||||
{
|
||||
protected $data = array();
|
||||
|
||||
public function set($key, $value, $ttl = null)
|
||||
{
|
||||
call_user_func(\Closure::bind(function () use ($key, $value) {
|
||||
$this->values[$key] = $value;
|
||||
$this->warmUp($this->values);
|
||||
$this->values = eval(substr(file_get_contents($this->file), 6));
|
||||
$this->data[$key] = $value;
|
||||
$this->warmUp($this->data);
|
||||
list($this->keys, $this->values) = eval(substr(file_get_contents($this->file), 6));
|
||||
}, $this, PhpArrayCache::class));
|
||||
|
||||
return true;
|
||||
@ -132,10 +150,10 @@ class PhpArrayCacheWrapper extends PhpArrayCache
|
||||
}
|
||||
call_user_func(\Closure::bind(function () use ($values) {
|
||||
foreach ($values as $key => $value) {
|
||||
$this->values[$key] = $value;
|
||||
$this->data[$key] = $value;
|
||||
}
|
||||
$this->warmUp($this->values);
|
||||
$this->values = eval(substr(file_get_contents($this->file), 6));
|
||||
$this->warmUp($this->data);
|
||||
list($this->keys, $this->values) = eval(substr(file_get_contents($this->file), 6));
|
||||
}, $this, PhpArrayCache::class));
|
||||
|
||||
return true;
|
||||
|
@ -52,11 +52,14 @@ trait ApcuTrait
|
||||
protected function doFetch(array $ids)
|
||||
{
|
||||
try {
|
||||
$values = array();
|
||||
foreach (apcu_fetch($ids, $ok) ?: array() as $k => $v) {
|
||||
if (null !== $v || $ok) {
|
||||
yield $k => $v;
|
||||
$values[$k] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
} catch (\Error $e) {
|
||||
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\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\Marshaller\PhpMarshaller;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
@ -25,6 +26,7 @@ trait PhpArrayTrait
|
||||
use ProxyTrait;
|
||||
|
||||
private $file;
|
||||
private $keys;
|
||||
private $values;
|
||||
|
||||
/**
|
||||
@ -54,55 +56,66 @@ trait PhpArrayTrait
|
||||
}
|
||||
}
|
||||
|
||||
$dumpedValues = '';
|
||||
$dumpedMap = array();
|
||||
$dump = <<<'EOF'
|
||||
<?php
|
||||
|
||||
// This file has been auto-generated by the Symfony Cache Component.
|
||||
|
||||
return array(
|
||||
return array(array(
|
||||
|
||||
|
||||
EOF;
|
||||
|
||||
foreach ($values as $key => $value) {
|
||||
CacheItem::validateKey(\is_int($key) ? (string) $key : $key);
|
||||
$objectsCount = 0;
|
||||
|
||||
if (null === $value || \is_object($value)) {
|
||||
try {
|
||||
$value = serialize($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)) {
|
||||
if (null === $value) {
|
||||
$value = 'N;';
|
||||
} elseif (\is_object($value) || \is_array($value)) {
|
||||
try {
|
||||
$e = null;
|
||||
$serialized = serialize($value);
|
||||
$unserialized = unserialize($serialized);
|
||||
} 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 ($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)) {
|
||||
// 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])) {
|
||||
$value = serialize($value);
|
||||
++$objectsCount;
|
||||
}
|
||||
} elseif (!\is_scalar($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 .= var_export($key, true)." => {$id},\n";
|
||||
}
|
||||
|
||||
$dump .= "\n);\n";
|
||||
$dump .= "\n), array(\n\n{$dumpedValues}\n));\n";
|
||||
|
||||
$tmpFile = uniqid($this->file, true);
|
||||
|
||||
file_put_contents($tmpFile, $dump);
|
||||
@chmod($tmpFile, 0666 & ~umask());
|
||||
unset($serialized, $unserialized, $value, $dump);
|
||||
unset($serialized, $value, $dump);
|
||||
|
||||
@rename($tmpFile, $this->file);
|
||||
|
||||
@ -114,7 +127,7 @@ EOF;
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->values = array();
|
||||
$this->keys = $this->values = array();
|
||||
|
||||
$cleared = @unlink($this->file) || !file_exists($this->file);
|
||||
|
||||
@ -126,6 +139,17 @@ EOF;
|
||||
*/
|
||||
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\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\Marshaller\PhpMarshaller;
|
||||
|
||||
/**
|
||||
* @author Piotr Stankowski <git@trakos.pl>
|
||||
@ -23,9 +24,15 @@ use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
*/
|
||||
trait PhpFilesTrait
|
||||
{
|
||||
use FilesystemCommonTrait;
|
||||
use FilesystemCommonTrait {
|
||||
doClear as private doCommonClear;
|
||||
doDelete as private doCommonDelete;
|
||||
}
|
||||
|
||||
private $includeHandler;
|
||||
private $appendOnly;
|
||||
private $values = array();
|
||||
private $files = array();
|
||||
|
||||
private static $startTime;
|
||||
|
||||
@ -65,35 +72,58 @@ trait PhpFilesTrait
|
||||
*/
|
||||
protected function doFetch(array $ids)
|
||||
{
|
||||
if ($this->appendOnly) {
|
||||
$now = 0;
|
||||
$missingIds = array();
|
||||
} else {
|
||||
$now = time();
|
||||
$missingIds = $ids;
|
||||
$ids = array();
|
||||
}
|
||||
$values = array();
|
||||
$now = time();
|
||||
|
||||
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);
|
||||
try {
|
||||
foreach ($ids as $id) {
|
||||
foreach ($missingIds as $k => $id) {
|
||||
try {
|
||||
$file = $this->getFile($id);
|
||||
list($expiresAt, $values[$id]) = include $file;
|
||||
$file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
|
||||
list($expiresAt, $this->values[$id]) = include $file;
|
||||
if ($now >= $expiresAt) {
|
||||
unset($values[$id]);
|
||||
unset($this->values[$id], $missingIds[$k]);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
continue;
|
||||
unset($missingIds[$k]);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
foreach ($values as $id => $value) {
|
||||
if ('N;' === $value) {
|
||||
$values[$id] = null;
|
||||
} elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
||||
$values[$id] = parent::unserialize($value);
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
$ids = $missingIds;
|
||||
$missingIds = array();
|
||||
goto begin;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,7 +131,25 @@ trait PhpFilesTrait
|
||||
*/
|
||||
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)
|
||||
{
|
||||
$ok = true;
|
||||
$data = array($lifetime ? time() + $lifetime : PHP_INT_MAX, '');
|
||||
$expiry = $lifetime ? time() + $lifetime : 'PHP_INT_MAX';
|
||||
$allowCompile = self::isSupported();
|
||||
|
||||
foreach ($values as $key => $value) {
|
||||
if (null === $value || \is_object($value)) {
|
||||
$value = serialize($value);
|
||||
} elseif (\is_array($value)) {
|
||||
$serialized = serialize($value);
|
||||
$unserialized = parent::unserialize($serialized);
|
||||
// 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;
|
||||
unset($this->values[$key]);
|
||||
$objectsCount = 0;
|
||||
if (null === $value) {
|
||||
$value = 'N;';
|
||||
} elseif (\is_object($value) || \is_array($value)) {
|
||||
try {
|
||||
$e = null;
|
||||
$serialized = serialize($value);
|
||||
} 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)) {
|
||||
// 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])) {
|
||||
$value = serialize($value);
|
||||
++$objectsCount;
|
||||
}
|
||||
} elseif (!\is_scalar($value)) {
|
||||
throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, gettype($value)));
|
||||
}
|
||||
|
||||
$data[1] = $value;
|
||||
$file = $this->getFile($key, true);
|
||||
$value = var_export($value, 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
|
||||
$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) {
|
||||
@opcache_invalidate($file, true);
|
||||
@opcache_compile_file($file);
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,6 +209,28 @@ trait PhpFilesTrait
|
||||
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)
|
||||
{
|
||||
if (self::isSupported()) {
|
||||
|
@ -29,7 +29,8 @@
|
||||
"cache/integration-tests": "dev-master",
|
||||
"doctrine/cache": "~1.6",
|
||||
"doctrine/dbal": "~2.4",
|
||||
"predis/predis": "~1.0"
|
||||
"predis/predis": "~1.0",
|
||||
"symfony/var-dumper": "^4.1.1"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/var-dumper": "<3.4"
|
||||
|
Reference in New Issue
Block a user