8000 [Cache] Fix double fetch in ProxyAdapter by nicolas-grekas · Pull Request #19042 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Cache] Fix double fetch in ProxyAdapter #19042

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 14, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
8000
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function ($key, $value, $isHit) use ($defaultLifetime) {

return $item;
},
$this,
null,
CacheItem::class
);
$this->mergeByLifetime = \Closure::bind(
Expand All @@ -63,7 +63,7 @@ function ($deferred, $namespace, &$expiredIds) {

return $byLifetime;
},
$this,
null,
CacheItem::class
);
}
Expand Down
8 changes: 4 additions & 4 deletions src/Symfony/Component/Cache/Adapter/ArrayAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function ($key, $value, $isHit) use ($defaultLifetime) {

return $item;
},
$this,
null,
CacheItem::class
);
}
Expand Down Expand Up @@ -132,9 +132,9 @@ public function save(CacheItemInterface $item)
return false;
}
$item = (array) $item;
$key = $item[CacheItem::CAST_PREFIX.'key'];
$value = $item[CacheItem::CAST_PREFIX.'value'];
$expiry = $item[CacheItem::CAST_PREFIX.'expiry'];
$key = $item["\0*\0key"];
$value = $item["\0*\0value"];
$expiry = $item["\0*\0expiry"];

if (null !== $expiry && $expiry <= time()) {
$this->deleteItem($key);
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Component/Cache/Adapter/ChainAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ function ($adapter, $item) use ($maxLifetime) {
$adapter->save($item);
$item->defaultLifetime = $origDefaultLifetime;
},
$this,
null,
CacheItem::class
);
}
Expand Down
27 changes: 16 additions & 11 deletions src/Symfony/Component/Cache/Adapter/ProxyAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,28 @@ class ProxyAdapter implements AdapterInterface
private $namespace;
private $namespaceLen;
private $createCacheItem;
private $poolHash;

public function __construct(CacheItemPoolInterface $pool, $namespace = '', $defaultLifetime = 0)
{
$this->pool = $pool;
$this->poolHash = $poolHash = spl_object_hash($pool);
$this->namespace = '' === $namespace ? '' : $this->getId($namespace);
$this->namespaceLen = strlen($namespace);
$this->createCacheItem = \Closure::bind(
function ($key, $value, $isHit) use ($defaultLifetime) {
function ($key, $innerItem) use ($defaultLifetime, $poolHash) {
$item = new CacheItem();
$item->key = $key;
$item->value = $value;
$item->isHit = $isHit;
$item->value = $innerItem->get();
$item->isHit = $innerItem->isHit();
$item->defaultLifetime = $defaultLifetime;
$item->innerItem = $innerItem;
$item->poolHash = $poolHash;
$innerItem->set(null);

return $item;
},
$this,
null,
CacheItem::class
);
}
Expand All @@ -53,7 +58,7 @@ public function getItem($key)
$f = $this->createCacheItem;
$item = $this->pool->getItem($this->getId($key));

return $f($key, $item->get(), $item->isHit());
return $f($key, $item);
}

/**
Expand Down Expand Up @@ -138,12 +143,12 @@ private function doSave(CacheItemInterface $item, $method)
return false;
}
$item = (array) $item;
$expiry = $item[CacheItem::CAST_PREFIX.'expiry'];
$poolItem = $this->pool->getItem($this->namespace.$item[CacheItem::CAST_PREFIX.'key']);
$poolItem->set($item[CacheItem::CAST_PREFIX.'value']);
$poolItem->expiresAt(null !== $expiry ? \DateTime::createFromFormat('U', $expiry) : null);
$expiry = $item["\0*\0expiry"];
$innerItem = $item["\0*\0poolHash"] === $this->poolHash ? $item["\0*\0innerItem"] : $this->pool->getItem($this->namespace.$item["\0*\0key"]);
$innerItem->set($item["\0*\0value"]);
$innerItem->expiresAt(null !== $expiry ? \DateTime::createFromFormat('U', $expiry) : null);

return $this->pool->$method($poolItem);
return $this->pool->$method($innerItem);
}

private function generateItems($items)
Expand All @@ -155,7 +160,7 @@ private function generateItems($items)
$key = substr($key, $this->namespaceLen);
}

yield $key => $f($key, $item->get(), $item->isHit());
yield $key => $f($key, $item);
}
}

Expand Down
17 changes: 7 additions & 10 deletions src/Symfony/Component/Cache/CacheItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,13 @@
*/
final class CacheItem implements CacheItemInterface
{
/**
* @internal
*/
const CAST_PREFIX = "\0Symfony\Component\Cache\CacheItem\0";

private $key;
private $value;
private $isHit;
private $expiry;
private $defaultLifetime;
protected $key;
Copy link
Member Author
@nicolas-grekas nicolas-grekas Jun 13, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the class is final, changing to protected here has no bc-promise-related impact.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the goal of using protected visibility in a final class ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shorter code, and const removal (to be sure nobody will use it)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think @stof meant that you could keep it private, since no other classes could possibly exist to access it.

protected $value;
protected $isHit;
protected $expiry;
protected $defaultLifetime;
protected $innerItem;
protected $poolHash;

/**
* {@inheritdoc}
Expand Down
39 changes: 39 additions & 0 deletions src/Symfony/Component/Cache/Tests/Adapter/ProxyAdapterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
namespace Symfony\Component\Cache\Tests\Adapter;

use Cache\IntegrationTests\CachePoolTest;
use Psr\Cache\CacheItemInterface;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\ProxyAdapter;
use Symfony\Component\Cache\CacheItem;

/**
* @group time-sensitive
Expand All @@ -29,4 +31,41 @@ public function createCachePool()
{
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());
}
}
}
0