-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[ExpressionLanguage] Making cache PSR6 compliant #19741
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,7 +11,9 @@ | |
|
||
namespace Symfony\Component\ExpressionLanguage; | ||
|
||
use Symfony\Component\ExpressionLanguage\ParserCache\ArrayParserCache; | ||
use Psr\Cache\CacheItemPoolInterface; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should be first (alpha ordering) |
||
use Symfony\Component\Cache\Adapter\ArrayAdapter; | ||
use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheAdapter; | ||
use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface; | ||
|
||
/** | ||
|
@@ -22,7 +24,7 @@ | |
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)); | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. missing case when |
||
|
||
$this->cache = $cache ?: new ArrayAdapter(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. symfony/cache is for now a required dependency because of this very line (which maps to the removed one above). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should keep it as is. Previously, some sort of cache would always have been used. If we made the cache optional now, it would be possible that the performance decreased for some use cases. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about wiring a cache pool in FrameworkBundle instead? That would mean less performance for those benefiting from the array cache and not using FrameworkBundle. But no perf drop for the other. WDYT? |
||
$this->registerFunctions(); | ||
foreach ($providers as $provider) { | ||
$this->registerProvider($provider); | ||
|
@@ -91,13 +102,14 @@ public function parse($expression, $names) | |
$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()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you should check the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know if it does make a difference, $parsedExpression has to be set anyway, and will be set to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with @ReDnAxE there is no need for isHit here and allows writting the code on a single line. Simpler to me. |
||
$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; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should be first |
||
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you should not implement getItem, save nor __construct, but implement the protected do* method instead. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OR change again and implement a subset of psr6 directly, like you did before, which was fine for an internal class. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No problem ;) I will opt for an implementation of the PSR6 CacheItemPoolInterface directly. Less code, less dependencies, less work ^^ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok! |
||
{ | ||
$value = $this->pool->fetch($key); | ||
$f = $this->createCacheItem; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this necessary? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, not working with |
||
|
||
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'); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DoctrineParserCache should also be deprecated (and this whole test marked as legacy, no need for assertDeprecationsAreTriggered)