8000 [Cache] Add a Chain adapter by dunglas · Pull Request #17556 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Cache] Add a Chain adapter #17556

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

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
[Cache] Add a Chain adapter
  • Loading branch information
dunglas committed Jan 27, 2016
commit ca6ea5db133d0231961b329ace5f1c89f57d500e
3 changes: 1 addition & 2 deletions src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
namespace Symfony\Component\Cache\Adapter;

use Psr\Cache\CacheItemInterface;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Symfony\Component\Cache\CacheItem;
Expand All @@ -21,7 +20,7 @@
/**
* @author Nicolas Grekas <p@tchwork.com>
*/
abstract class AbstractAdapter implements CacheItemPoolInterface, LoggerAwareInterface
abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface
{
use LoggerAwareTrait;

Expand Down
23 changes: 23 additions & 0 deletions src/Symfony/Component/Cache/Adapter/AdapterInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?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\Adapter;

use Psr\Cache\CacheItemPoolInterface;

/**
* Marker interface for adapters managing \Symfony\Component\Cache\CacheItem instances.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
interface AdapterInterface extends CacheItemPoolInterface
Copy link
Member

Choose a reason for hiding this comment

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

imo this interface deserves a dedicated PR

Copy link
Member

Choose a reason for hiding this comment

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

in which ProxyAdapter is changed to use it to detect compatible CacheItems

{
}
3 changes: 1 addition & 2 deletions src/Symfony/Component/Cache/Adapter/ArrayAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
namespace Symfony\Component\Cache\Adapter;

use Psr\Cache\CacheItemInterface;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Symfony\Component\Cache\CacheItem;
Expand All @@ -21,7 +20,7 @@
/**
* @author Nicolas Grekas <p@tchwork.com>
*/
class ArrayAdapter implements CacheItemPoolInterface, LoggerAwareInterface
class ArrayAdapter implements AdapterInterface, LoggerAwareInterface
{
use LoggerAwareTrait;

Expand Down
173 changes: 173 additions & 0 deletions src/Symfony/Component/Cache/Adapter/ChainAdapter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
<?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\Adapter;

use Psr\Cache\CacheItemInterface;
use Symfony\Component\Cache\Exception\InvalidArgumentException;

/**
* Chains adapters together.
*
* Saves, deletes and clears all registered adapter.
* Gets data from the first chained adapter having it in cache.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class ChainAdapter implements AdapterInterface
{
private $adapters;

/**
* @param AdapterInterface[] $adapters
*/
public function __construct(array $adapters)
{
if (2 < count($adapters)) {
throw new InvalidArgumentException('At least two adapters must be chained.');
}

foreach ($adapters as $adapter) {
if (!$adapter instanceof AdapterInterface) {
throw new InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.', get_class($adapter), AdapterInterface::class));
}
}

$this->adapters = $adapters;
}

/**
* {@inheritdoc}
*/
public function getItem($key)
{
foreach ($this->adapters as $adapter) {
$item = $adapter->getItem($key);

if ($item->isHit()) {
return $item;
Copy link
Member

Choose a reason for hiding this comment

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

I'm wondering: when an item is found in a lower level adapter, shouldn't it be stored in the upper level adapters?

}
}

return $item;
}

/**
* {@inheritdoc}
*/
public function getItems(array $keys = array())
{
$items = array();
foreach ($keys as $key) {
$items[$key] = $this->getItem($key);
Copy link
Member

Choose a reason for hiding this comment

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

This implementation is naïve and prevents efficient retrieval of multiple items. We should use getItems() on chained adapters instead, until all of the items are found

}

return $items;
}

/**
* {@inheritdoc}
*/
public function hasItem($key)
{
foreach ($this->adapters as $adapter) {
if ($adapter->hasItem($key)) {
return true;
}
}

return false;
}

/**
* {@inheritdoc}
*/
public function clear()
{
$cleared = true;

foreach ($this->adapters as $adapter) {
$cleared = $adapter->clear() && $cleared;
}

return $cleared;
}

/**
* {@inheritdoc}
*/
public function deleteItem($key)
{
$deleted = true;

foreach ($this->adapters as $adapter) {
$deleted = $adapter->deleteItem($key) && $deleted;
}

return $deleted;
}

/**
* {@inheritdoc}
*/
public function deleteItems(array $keys)
{
$deleted = true;

foreach ($this->adapters as $adapter) {
$deleted = $adapter->deleteItems($keys) && $deleted;
}

return $deleted;
}

/**
* {@inheritdoc}
*/
public function save(CacheItemInterface $item)
{
$saved = true;

foreach ($this->adapters as $adapter) {
$saved = $adapter->save($item) && $saved;
}

return $saved;
}

/**
* {@inheritdoc}
*/
public function saveDeferred(CacheItemInterface $item)
{
$saved = true;

foreach ($this->adapters as $adapter) {
$saved = $adapter->saveDeferred($item) && $saved;
}

return $saved;
}

/**
* {@inheritdoc}
*/
public function commit()
{
$committed = true;

foreach ($this->adapters as $adapter) {
$committed = $adapter->commit() && $committed;
}

return $committed;
}
}
2 changes: 1 addition & 1 deletion src/Symfony/Component/Cache/Adapter/ProxyAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
/**
* @author Nicolas Grekas <p@tchwork.com>
*/
class ProxyAdapter implements CacheItemPoolInterface
class ProxyAdapter implements AdapterInterface
{
private $pool;
private $createCacheItem;
Expand Down
41 changes: 41 additions & 0 deletions src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?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\Adapter;

use Cache\IntegrationTests\CachePoolTest;
use Symfony\Component\Cache\Adapter\ApcuAdapter;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\ChainAdapter;

/**
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class ChainAdapterTest extends CachePoolTest
{
protected $skippedTests = array(
'testDeferredSaveWithoutCommit' => 'Assumes a shared cache which ArrayAdapter is not.',
Copy link
Member

Choose a reason for hiding this comment

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

this looks suspicious to me here

Copy link
Member

Choose a reason for hiding this comment

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

I agree, same for the two others below, there are not needed anymore I think

'testSaveWithoutExpire' => 'Assumes a shared cache which ArrayAdapter is not.',
'testDeferredExpired' => 'Failing for now, needs to be fixed.',
);

public function createCachePool()
{
if (defined('HHVM_VERSION')) {
$this->skippedTests['testDeferredSaveWithoutCommit'] = 'Fails on HHVM';
}
if (!function_exists('apcu_fetch') || !ini_get('apc.enabled') || ('cli' === PHP_SAPI && !ini_get('apc.enable_cli'))) {
$this->markTestSkipped('APCu extension is required.');
}

re 3D15 turn new ChainAdapter(array(new ArrayAdapter(), new ApcuAdapter(__CLASS__)));
}
}
0