[Cache] Fix double fetch in ProxyAdapter

This commit is contained in:
Nicolas Grekas 2016-06-13 16:28:45 +02:00
parent c87c175ad5
commit 040f53d3e6
6 changed files with 69 additions and 28 deletions

View File

@ -42,7 +42,7 @@ abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface
return $item; return $item;
}, },
$this, null,
CacheItem::class CacheItem::class
); );
$this->mergeByLifetime = \Closure::bind( $this->mergeByLifetime = \Closure::bind(
@ -63,7 +63,7 @@ abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface
return $byLifetime; return $byLifetime;
}, },
$this, null,
CacheItem::class CacheItem::class
); );
} }

View File

@ -45,7 +45,7 @@ class ArrayAdapter implements AdapterInterface, LoggerAwareInterface
return $item; return $item;
}, },
$this, null,
CacheItem::class CacheItem::class
); );
} }
@ -132,9 +132,9 @@ class ArrayAdapter implements AdapterInterface, LoggerAwareInterface
return false; return false;
} }
$item = (array) $item; $item = (array) $item;
$key = $item[CacheItem::CAST_PREFIX.'key']; $key = $item["\0*\0key"];
$value = $item[CacheItem::CAST_PREFIX.'value']; $value = $item["\0*\0value"];
$expiry = $item[CacheItem::CAST_PREFIX.'expiry']; $expiry = $item["\0*\0expiry"];
if (null !== $expiry && $expiry <= time()) { if (null !== $expiry && $expiry <= time()) {
$this->deleteItem($key); $this->deleteItem($key);

View File

@ -62,7 +62,7 @@ class ChainAdapter implements AdapterInterface
$adapter->save($item); $adapter->save($item);
$item->defaultLifetime = $origDefaultLifetime; $item->defaultLifetime = $origDefaultLifetime;
}, },
$this, null,
CacheItem::class CacheItem::class
); );
} }

View File

@ -24,23 +24,28 @@ class ProxyAdapter implements AdapterInterface
private $namespace; private $namespace;
private $namespaceLen; private $namespaceLen;
private $createCacheItem; private $createCacheItem;
private $poolHash;
public function __construct(CacheItemPoolInterface $pool, $namespace = '', $defaultLifetime = 0) public function __construct(CacheItemPoolInterface $pool, $namespace = '', $defaultLifetime = 0)
{ {
$this->pool = $pool; $this->pool = $pool;
$this->poolHash = $poolHash = spl_object_hash($pool);
$this->namespace = '' === $namespace ? '' : $this->getId($namespace); $this->namespace = '' === $namespace ? '' : $this->getId($namespace);
$this->namespaceLen = strlen($namespace); $this->namespaceLen = strlen($namespace);
$this->createCacheItem = \Closure::bind( $this->createCacheItem = \Closure::bind(
function ($key, $value, $isHit) use ($defaultLifetime) { function ($key, $innerItem) use ($defaultLifetime, $poolHash) {
$item = new CacheItem(); $item = new CacheItem();
$item->key = $key; $item->key = $key;
$item->value = $value; $item->value = $innerItem->get();
$item->isHit = $isHit; $item->isHit = $innerItem->isHit();
$item->defaultLifetime = $defaultLifetime; $item->defaultLifetime = $defaultLifetime;
$item->innerItem = $innerItem;
$item->poolHash = $poolHash;
$innerItem->set(null);
return $item; return $item;
}, },
$this, null,
CacheItem::class CacheItem::class
); );
} }
@ -53,7 +58,7 @@ class ProxyAdapter implements AdapterInterface
$f = $this->createCacheItem; $f = $this->createCacheItem;
$item = $this->pool->getItem($this->getId($key)); $item = $this->pool->getItem($this->getId($key));
return $f($key, $item->get(), $item->isHit()); return $f($key, $item);
} }
/** /**
@ -138,12 +143,12 @@ class ProxyAdapter implements AdapterInterface
return false; return false;
} }
$item = (array) $item; $item = (array) $item;
$expiry = $item[CacheItem::CAST_PREFIX.'expiry']; $expiry = $item["\0*\0expiry"];
$poolItem = $this->pool->getItem($this->namespace.$item[CacheItem::CAST_PREFIX.'key']); $innerItem = $item["\0*\0poolHash"] === $this->poolHash ? $item["\0*\0innerItem"] : $this->pool->getItem($this->namespace.$item["\0*\0key"]);
$poolItem->set($item[CacheItem::CAST_PREFIX.'value']); $innerItem->set($item["\0*\0value"]);
$poolItem->expiresAt(null !== $expiry ? \DateTime::createFromFormat('U', $expiry) : null); $innerItem->expiresAt(null !== $expiry ? \DateTime::createFromFormat('U', $expiry) : null);
return $this->pool->$method($poolItem); return $this->pool->$method($innerItem);
} }
private function generateItems($items) private function generateItems($items)
@ -155,7 +160,7 @@ class ProxyAdapter implements AdapterInterface
$key = substr($key, $this->namespaceLen); $key = substr($key, $this->namespaceLen);
} }
yield $key => $f($key, $item->get(), $item->isHit()); yield $key => $f($key, $item);
} }
} }

View File

@ -20,16 +20,13 @@ use Symfony\Component\Cache\Exception\InvalidArgumentException;
*/ */
final class CacheItem implements CacheItemInterface final class CacheItem implements CacheItemInterface
{ {
/** protected $key;
* @internal protected $value;
*/ protected $isHit;
const CAST_PREFIX = "\0Symfony\Component\Cache\CacheItem\0"; protected $expiry;
protected $defaultLifetime;
private $key; protected $innerItem;
private $value; protected $poolHash;
private $isHit;
private $expiry;
private $defaultLifetime;
/** /**
* {@inheritdoc} * {@inheritdoc}

View File

@ -12,8 +12,10 @@
namespace Symfony\Component\Cache\Tests\Adapter; namespace Symfony\Component\Cache\Tests\Adapter;
use Cache\IntegrationTests\CachePoolTest; use Cache\IntegrationTests\CachePoolTest;
use Psr\Cache\CacheItemInterface;
use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\ProxyAdapter; use Symfony\Component\Cache\Adapter\ProxyAdapter;
use Symfony\Component\Cache\CacheItem;
/** /**
* @group time-sensitive * @group time-sensitive
@ -29,4 +31,41 @@ class ProxyAdapterTest extends CachePoolTest
{ {
return new ProxyAdapter(new ArrayAdapter()); return new ProxyAdapter(new ArrayAdapter());
} }
/**
* @expectedException Exception
* @expectedExceptionMessage OK bar
*/
public function testProxyfiedItem()
{
$item = new CacheItem();
$pool = new ProxyAdapter(new TestingArrayAdapter($item));
$proxyItem = $pool->getItem('foo');
$this->assertFalse($proxyItem === $item);
$pool->save($proxyItem->set('bar'));
}
}
class TestingArrayAdapter extends ArrayAdapter
{
private $item;
public function __construct(CacheItemInterface $item)
{
$this->item = $item;
}
public function getItem($key)
{
return $this->item;
}
public function save(CacheItemInterface $item)
{
if ($item === $this->item) {
throw new \Exception('OK '.$item->get());
}
}
} }