[ExpressionLanguage] Making cache PSR6 compliant

This commit is contained in:
Alexandre GESLIN 2016-08-26 09:29:50 +02:00 committed by Alexandre GESLIN
parent 669478db26
commit a7352ff975
11 changed files with 396 additions and 22 deletions

View File

@ -37,6 +37,13 @@ DependencyInjection
* Calling `get()` on a `ContainerBuilder` instance before compiling the
container is deprecated and will throw an exception in Symfony 4.0.
ExpressionLanguage
-------------------
* Passing a `ParserCacheInterface` instance to the `ExpressionLanguage` has been
deprecated and will not be supported in Symfony 4.0. You should use the
`CacheItemPoolInterface` interface instead.
Form
----

View File

@ -33,6 +33,13 @@ DependencyInjection
* Requesting a private service with the `Container::get()` method is no longer
supported.
ExpressionLanguage
----------
* The ability to pass a `ParserCacheInterface` instance to the `ExpressionLanguage`
class has been removed. You should use the `CacheItemPoolInterface` interface
instead.
Form
----

View File

@ -11,12 +11,16 @@
namespace Symfony\Bridge\Doctrine\ExpressionLanguage;
@trigger_error('The '.__NAMESPACE__.'\DoctrineParserCache class is deprecated since version 3.2 and will be removed in 4.0. Use the Symfony\Component\Cache\Adapter\DoctrineAdapter class instead.', E_USER_DEPRECATED);
use Doctrine\Common\Cache\Cache;
use Symfony\Component\ExpressionLanguage\ParsedExpression;
use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface;
/**
* @author Adrien Brault <adrien.brault@gmail.com>
*
* @deprecated DoctrineParserCache class is deprecated since version 3.2 and will be removed in 4.0. Use the Symfony\Component\Cache\Adapter\DoctrineAdapter class instead.
*/
class DoctrineParserCache implements ParserCacheInterface
{

View File

@ -13,6 +13,9 @@ namespace Symfony\Bridge\Doctrine\Tests\ExpressionLanguage;
use Symfony\Bridge\Doctrine\ExpressionLanguage\DoctrineParserCache;
/**
* @group legacy
*/
class DoctrineParserCacheTest extends \PHPUnit_Framework_TestCase
{
public function testFetch()

View File

@ -11,7 +11,9 @@
namespace Symfony\Component\ExpressionLanguage;
use Symfony\Component\ExpressionLanguage\ParserCache\ArrayParserCache;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheAdapter;
use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface;
/**
@ -22,7 +24,7 @@ use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface;
class ExpressionLanguage
{
/**
* @var ParserCacheInterface
* @var CacheItemPoolInterface
*/
private $cache;
private $lexer;
@ -32,12 +34,21 @@ class ExpressionLanguage
protected $functions = array();
/**
* @param ParserCacheInterface $cache
* @param CacheItemPoolInterface $cache
* @param ExpressionFunctionProviderInterface[] $providers
*/
public function __construct(ParserCacheInterface $cache = null, array $providers = array())
public function __construct($cache = null, array $providers = array())
{
$this->cache = $cache ?: new ArrayParserCache();
if (null !== $cache) {
if ($cache instanceof ParserCacheInterface) {
@trigger_error(sprintf('Passing an instance of %s as constructor argument for %s is deprecated as of 3.2 and will be removed in 4.0. Pass an instance of %s instead.', ParserCacheInterface::class, self::class, CacheItemPoolInterface::class), E_USER_DEPRECATED);
$cache = new ParserCacheAdapter($cache);
} elseif (!$cache instanceof CacheItemPoolInterface) {
throw new \InvalidArgumentException(sprintf('Cache argument has to implement %s.', CacheItemPoolInterface::class));
}
}
$this->cache = $cache ?: new ArrayAdapter();
$this->registerFunctions();
foreach ($providers as $provider) {
$this->registerProvider($provider);
@ -91,13 +102,14 @@ class ExpressionLanguage
$cacheKeyItems[] = is_int($nameKey) ? $name : $nameKey.':'.$name;
}
$key = $expression.'//'.implode('|', $cacheKeyItems);
$cacheItem = $this->cache->getItem(rawurlencode($expression.'//'.implode('|', $cacheKeyItems)));
if (null === $parsedExpression = $this->cache->fetch($key)) {
if (null === $parsedExpression = $cacheItem->get()) {
$nodes = $this->getParser()->parse($this->getLexer()->tokenize((string) $expression), $names);
$parsedExpression = new ParsedExpression((string) $expression, $nodes);
$this->cache->save($key, $parsedExpression);
$cacheItem->set($parsedExpression);
$this->cache->save($cacheItem);
}
return $parsedExpression;

View File

@ -11,10 +11,14 @@
namespace Symfony\Component\ExpressionLanguage\ParserCache;
@trigger_error('The '.__NAMESPACE__.'\ArrayParserCache class is deprecated since version 3.2 and will be removed in 4.0. Use the Symfony\Component\Cache\Adapter\ArrayAdapter class instead.', E_USER_DEPRECATED);
use Symfony\Component\ExpressionLanguage\ParsedExpression;
/**
* @author Adrien Brault <adrien.brault@gmail.com>
*
* @deprecated ArrayParserCache class is deprecated since version 3.2 and will be removed in 4.0. Use the Symfony\Component\Cache\Adapter\ArrayAdapter class instead.
*/
class ArrayParserCache implements ParserCacheInterface
{

View File

@ -0,0 +1,120 @@
<?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\ExpressionLanguage\ParserCache;
use Psr\Cache\CacheItemInterface;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Cache\CacheItem;
/**
* @author Alexandre GESLIN <alexandre@gesl.in>
*
* @internal This class should be removed in Symfony 4.0.
*/
class ParserCacheAdapter implements CacheItemPoolInterface
{
private $pool;
private $createCacheItem;
public function __construct(ParserCacheInterface $pool)
{
$this->pool = $pool;
$this->createCacheItem = \Closure::bind(
function ($key, $value, $isHit) {
$item = new CacheItem();
$item->key = $key;
$item->value = $value;
$item->isHit = $isHit;
return $item;
},
null,
CacheItem::class
);
}
/**
* {@inheritdoc}
*/
public function getItem($key)
{
$value = $this->pool->fetch($key);
$f = $this->createCacheItem;
return $f($key, $value, null !== $value);
}
/**
* {@inheritdoc}
*/
public function save(CacheItemInterface $item)
{
$this->pool->save($item->getKey(), $item->get());
}
/**
* {@inheritdoc}
*/
public function getItems(array $keys = array())
{
throw new \BadMethodCallException('Not implemented');
}
/**
* {@inheritdoc}
*/
public function hasItem($key)
{
throw new \BadMethodCallException('Not implemented');
}
/**
* {@inheritdoc}
*/
public function clear()
{
throw new \BadMethodCallException('Not implemented');
}
/**
* {@inheritdoc}
*/
public function deleteItem($key)
{
throw new \BadMethodCallException('Not implemented');
}
/**
* {@inheritdoc}
*/
public function deleteItems(array $keys)
{
throw new \BadMethodCallException('Not implemented');
}
/**
* {@inheritdoc}
*/
public function saveDeferred(CacheItemInterface $item)
{
throw new \BadMethodCallException('Not implemented');
}
/**
* {@inheritdoc}
*/
public function commit()
{
throw new \BadMethodCallException('Not implemented');
}
}

View File

@ -11,10 +11,14 @@
namespace Symfony\Component\ExpressionLanguage\ParserCache;
@trigger_error('The '.__NAMESPACE__.'\ParserCacheInterface interface is deprecated since version 3.2 and will be removed in 4.0. Use Psr\Cache\CacheItemPoolInterface instead.', E_USER_DEPRECATED);
use Symfony\Component\ExpressionLanguage\ParsedExpression;
/**
* @author Adrien Brault <adrien.brault@gmail.com>
*
* @deprecated since version 3.2, to be removed in 4.0. Use Psr\Cache\CacheItemPoolInterface instead.
*/
interface ParserCacheInterface
{

View File

@ -12,28 +12,77 @@
namespace Symfony\Component\ExpressionLanguage\Tests;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Symfony\Component\ExpressionLanguage\ParsedExpression;
use Symfony\Component\ExpressionLanguage\Tests\Fixtures\TestProvider;
class ExpressionLanguageTest extends \PHPUnit_Framework_TestCase
{
public function testCachedParse()
{
$cacheMock = $this->getMock('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface');
$cacheMock = $this->getMock('Psr\Cache\CacheItemPoolInterface');
$cacheItemMock = $this->getMock('Psr\Cache\CacheItemInterface');
$savedParsedExpression = null;
$expressionLanguage = new ExpressionLanguage($cacheMock);
$cacheMock
->expects($this->exactly(2))
->method('fetch')
->with('1 + 1//')
->method('getItem')
->with('1%20%2B%201%2F%2F')
->willReturn($cacheItemMock)
;
$cacheItemMock
->expects($this->exactly(2))
->method('get')
->will($this->returnCallback(function () use (&$savedParsedExpression) {
return $savedParsedExpression;
}))
;
$cacheItemMock
->expects($this->exactly(1))
->method('set')
->with($this->isInstanceOf(ParsedExpression::class))
->will($this->returnCallback(function ($parsedExpression) use (&$savedParsedExpression) {
$savedParsedExpression = $parsedExpression;
}))
;
$cacheMock
->expects($this->exactly(1))
->method('save')
->with('1 + 1//', $this->isInstanceOf('Symfony\Component\ExpressionLanguage\ParsedExpression'))
->with($cacheItemMock)
;
$parsedExpression = $expressionLanguage->parse('1 + 1', array());
$this->assertSame($savedParsedExpression, $parsedExpression);
$parsedExpression = $expressionLanguage->parse('1 + 1', array());
$this->assertSame($savedParsedExpression, $parsedExpression);
}
/**
* @group legacy
*/
public function testCachedParseWithDeprecatedParserCacheInterface()
{
$cacheMock = $this->getMock('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface');
$cacheItemMock = $this->getMock('Psr\Cache\CacheItemInterface');
$savedParsedExpression = null;
$expressionLanguage = new ExpressionLanguage($cacheMock);
$cacheMock
->expects($this->exactly(1))
->method('fetch')
->with('1%20%2B%201%2F%2F')
->willReturn($savedParsedExpression)
;
$cacheMock
->expects($this->exactly(1))
->method('save')
->with('1%20%2B%201%2F%2F', $this->isInstanceOf(ParsedExpression::class))
->will($this->returnCallback(function ($key, $expression) use (&$savedParsedExpression) {
$savedParsedExpression = $expression;
}))
@ -41,9 +90,16 @@ class ExpressionLanguageTest extends \PHPUnit_Framework_TestCase
$parsedExpression = $expressionLanguage->parse('1 + 1', array());
$this->assertSame($savedParsedExpression, $parsedExpression);
}
$parsedExpression = $expressionLanguage->parse('1 + 1', array());
$this->assertSame($savedParsedExpression, $parsedExpression);
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Cache argument has to implement Psr\Cache\CacheItemPoolInterface.
*/
public function testWrongCacheImplementation()
{
$cacheMock = $this->getMock('Psr\Cache\CacheItemSpoolInterface');
$expressionLanguage = new ExpressionLanguage($cacheMock);
}
public function testConstantFunction()
@ -116,22 +172,39 @@ class ExpressionLanguageTest extends \PHPUnit_Framework_TestCase
public function testCachingWithDifferentNamesOrder()
{
$cacheMock = $this->getMock('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface');
$cacheMock = $this->getMock('Psr\Cache\CacheItemPoolInterface');
$cacheItemMock = $this->getMock('Psr\Cache\CacheItemInterface');
$expressionLanguage = new ExpressionLanguage($cacheMock);
$savedParsedExpressions = array();
$cacheMock
->expects($this->exactly(2))
->method('fetch')
->will($this->returnCallback(function ($key) use (&$savedParsedExpressions) {
return isset($savedParsedExpressions[$key]) ? $savedParsedExpressions[$key] : null;
->method('getItem')
->with('a%20%2B%20b%2F%2Fa%7CB%3Ab')
->willReturn($cacheItemMock)
;
$cacheItemMock
->expects($this->exactly(2))
->method('get')
->will($this->returnCallback(function () use (&$savedParsedExpression) {
return $savedParsedExpression;
}))
;
$cacheItemMock
->expects($this->exactly(1))
->method('set')
->with($this->isInstanceOf(ParsedExpression::class))
->will($this->returnCallback(function ($parsedExpression) use (&$savedParsedExpression) {
$savedParsedExpression = $parsedExpression;
}))
;
$cacheMock
->expects($this->exactly(1))
->method('save')
->will($this->returnCallback(function ($key, $expression) use (&$savedParsedExpressions) {
$savedParsedExpressions[$key] = $expression;
}))
->with($cacheItemMock)
;
$expression = 'a + b';

View File

@ -0,0 +1,139 @@
<?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\ExpressionLanguage\Tests;
use Symfony\Component\ExpressionLanguage\ParsedExpression;
use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheAdapter;
use Symfony\Component\ExpressionLanguage\Node\Node;
/**
* @group legacy
*/
class ParserCacheAdapterTest extends \PHPUnit_Framework_TestCase
{
public function testGetItem()
{
$poolMock = $this->getMock('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface');
$key = 'key';
$value = 'value';
$parserCacheAdapter = new ParserCacheAdapter($poolMock);
$poolMock
->expects($this->once())
->method('fetch')
->with($key)
->willReturn($value)
;
$cacheItem = $parserCacheAdapter->getItem($key);
$this->assertEquals($cacheItem->get(), $value);
$this->assertEquals($cacheItem->isHit(), true);
}
public function testSave()
{
$poolMock = $this->getMock('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface');
$cacheItemMock = $this->getMock('Psr\Cache\CacheItemInterface');
$key = 'key';
$value = new ParsedExpression('1 + 1', new Node(array(), array()));
$parserCacheAdapter = new ParserCacheAdapter($poolMock);
$poolMock
->expects($this->once())
->method('save')
->with($key, $value)
;
$cacheItemMock
->expects($this->once())
->method('getKey')
->willReturn($key)
;
$cacheItemMock
->expects($this->once())
->method('get')
->willReturn($value)
;
$cacheItem = $parserCacheAdapter->save($cacheItemMock);
}
public function testGetItems()
{
$poolMock = $this->getMock('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface');
$parserCacheAdapter = new ParserCacheAdapter($poolMock);
$this->setExpectedException(\BadMethodCallException::class);
$parserCacheAdapter->getItems();
}
public function testHasItem()
{
$poolMock = $this->getMock('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface');
$key = 'key';
$parserCacheAdapter = new ParserCacheAdapter($poolMock);
$this->setExpectedException(\BadMethodCallException::class);
$parserCacheAdapter->hasItem($key);
}
public function testClear()
{
$poolMock = $this->getMock('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface');
$parserCacheAdapter = new ParserCacheAdapter($poolMock);
$this->setExpectedException(\BadMethodCallException::class);
$parserCacheAdapter->clear();
}
public function testDeleteItem()
{
$poolMock = $this->getMock('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface');
$key = 'key';
$parserCacheAdapter = new ParserCacheAdapter($poolMock);
$this->setExpectedException(\BadMethodCallException::class);
$parserCacheAdapter->deleteItem($key);
}
public function testDeleteItems()
{
$poolMock = $this->getMock('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface');
$keys = array('key');
$parserCacheAdapter = new ParserCacheAdapter($poolMock);
$this->setExpectedException(\BadMethodCallException::class);
$parserCacheAdapter->deleteItems($keys);
}
public function testSaveDeferred()
{
$poolMock = $this->getMock('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface');
$parserCacheAdapter = new ParserCacheAdapter($poolMock);
$cacheItemMock = $this->getMock('Psr\Cache\CacheItemInterface');
$this->setExpectedException(\BadMethodCallException::class);
$parserCacheAdapter->saveDeferred($cacheItemMock);
}
public function testCommit()
{
$poolMock = $this->getMock('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface');
$parserCacheAdapter = new ParserCacheAdapter($poolMock);
$this->setExpectedException(\BadMethodCallException::class);
$parserCacheAdapter->commit();
}
}

View File

@ -16,7 +16,8 @@
}
],
"require": {
"php": ">=5.5.9"
"php": ">=5.5.9",
"symfony/cache": "~3.1"
},
"autoload": {
"psr-4": { "Symfony\\Component\\ExpressionLanguage\\": "" },