Skip to content

[WIP] update PSR6Cache for support @ and / characters #30485

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 1 commit into from
Closed
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
7 changes: 6 additions & 1 deletion src/Symfony/Component/Cache/Adapter/ArrayAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\ResettableInterface;
use Symfony\Component\Cache\Traits\ArrayTrait;
use Symfony\Component\Cache\Traits\KeyTrait;
use Symfony\Contracts\Cache\CacheInterface;

/**
Expand All @@ -24,6 +25,7 @@
class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInterface, ResettableInterface
{
use ArrayTrait;
use KeyTrait;

private $createCacheItem;

Expand Down Expand Up @@ -70,6 +72,7 @@ public function get(string $key, callable $callback, float $beta = null, array &
*/
public function getItem($key)
{
$key = $this->encodeKey($key);
if (!$isHit = $this->hasItem($key)) {
$this->values[$key] = $value = null;
} else {
Expand All @@ -86,6 +89,7 @@ public function getItem($key)
public function getItems(array $keys = [])
{
foreach ($keys as $key) {
$key = $this->encodeKey($key);
if (!\is_string($key) || !isset($this->expiries[$key])) {
CacheItem::validateKey($key);
}
Expand All @@ -100,6 +104,7 @@ public function getItems(array $keys = [])
public function deleteItems(array $keys)
{
foreach ($keys as $key) {
$key = $this->encodeKey($key);
$this->deleteItem($key);
}

Expand Down Expand Up @@ -158,6 +163,6 @@ public function commit()
*/
public function delete(string $key): bool
{
return $this->deleteItem($key);
return $this->deleteItem($this->encodeKey($key));
}
}
11 changes: 10 additions & 1 deletion src/Symfony/Component/Cache/Adapter/TraceableAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\ResettableInterface;
use Symfony\Component\Cache\Traits\KeyTrait;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Service\ResetInterface;

Expand All @@ -27,6 +28,8 @@
*/
class TraceableAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface
{
use KeyTrait;

protected $pool;
private $calls = [];

Expand All @@ -44,6 +47,7 @@ public function get(string $key, callable $callback, float $beta = null, array &
throw new \BadMethodCallException(sprintf('Cannot call "%s::get()": this class doesn\'t implement "%s".', \get_class($this->pool), CacheInterface::class));
}

$key = $this->encodeKey($key);
$isHit = true;
$callback = function (CacheItem $item) use ($callback, &$isHit) {
$isHit = $item->isHit();
Expand Down Expand Up @@ -73,6 +77,7 @@ public function get(string $key, callable $callback, float $beta = null, array &
public function getItem($key)
{
$event = $this->start(__FUNCTION__);
$key = $this->encodeKey($key);
try {
$item = $this->pool->getItem($key);
} finally {
Expand All @@ -83,7 +88,6 @@ public function getItem($key)
} else {
++$event->misses;
}

return $item;
}

Expand All @@ -92,6 +96,7 @@ public function getItem($key)
*/
public function hasItem($key)
{
$key = $this->encodeKey($key);
$event = $this->start(__FUNCTION__);
try {
return $event->result[$key] = $this->pool->hasItem($key);
Expand All @@ -105,6 +110,7 @@ public function hasItem($key)
*/
public function deleteItem($key)
{
$key = $this->encodeKey($key);
$event = $this->start(__FUNCTION__);
try {
return $event->result[$key] = $this->pool->deleteItem($key);
Expand Down Expand Up @@ -153,6 +159,7 @@ public function getItems(array $keys = [])
$f = function () use ($result, $event) {
$event->result = [];
foreach ($result as $key => $item) {
$key = $this->encodeKey($key);
if ($event->result[$key] = $item->isHit()) {
++$event->hits;
} else {
Expand Down Expand Up @@ -184,6 +191,7 @@ public function clear()
public function deleteItems(array $keys)
{
$event = $this->start(__FUNCTION__);
$keys = array_map([$this, 'encodeKey'], $keys);
$event->result['keys'] = $keys;
try {
return $event->result['result'] = $this->pool->deleteItems($keys);
Expand Down Expand Up @@ -243,6 +251,7 @@ public function reset()
public function delete(string $key): bool
{
$event = $this->start(__FUNCTION__);
$key = $this->encodeKey($key);
try {
return $event->result[$key] = $this->pool->deleteItem($key);
} finally {
Expand Down
8 changes: 4 additions & 4 deletions src/Symfony/Component/Cache/CacheItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,8 @@ public function tag($tags): ItemInterface
if ('' === $tag) {
throw new InvalidArgumentException('Cache tag length must be greater than zero');
}
if (false !== strpbrk($tag, '{}()/\@:')) {
throw new InvalidArgumentException(sprintf('Cache tag "%s" contains reserved characters {}()/\@:', $tag));
if (false !== strpbrk($tag, '{}()\:')) {
throw new InvalidArgumentException(sprintf('Cache tag "%s" contains reserved characters {}()\:', $tag));
}
$this->newMetadata[self::METADATA_TAGS][$tag] = $tag;
}
Expand Down Expand Up @@ -171,8 +171,8 @@ public static function validateKey($key)
if ('' === $key) {
throw new InvalidArgumentException('Cache key length must be greater than zero');
}
if (false !== strpbrk($key, '{}()/\@:')) {
throw new InvalidArgumentException(sprintf('Cache key "%s" contains reserved characters {}()/\@:', $key));
if (false !== strpbrk($key, '{}()\:')) {
throw new InvalidArgumentException(sprintf('Cache key "%s" contains reserved characters {}()\:', $key));
}

return $key;
Expand Down
12 changes: 11 additions & 1 deletion src/Symfony/Component/Cache/Simple/Psr6Cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\ResettableInterface;
use Symfony\Component\Cache\Traits\KeyTrait;
use Symfony\Component\Cache\Traits\ProxyTrait;

/**
Expand All @@ -28,6 +29,7 @@
class Psr6Cache implements CacheInterface, PruneableInterface, ResettableInterface
{
use ProxyTrait;
use KeyTrait;

private const METADATA_EXPIRY_OFFSET = 1527506807;

Expand Down Expand Up @@ -89,6 +91,7 @@ public function get($key, $default = null)
*/
public function set($key, $value, $ttl = null)
{
$key = $this->encodeKey($key);
try {
if (null !== $f = $this->createCacheItem) {
$item = $f($key, $value);
Expand All @@ -113,6 +116,7 @@ public function set($key, $value, $ttl = null)
public function delete($key)
{
try {
$key = $this->encodeKey($key);
return $this->pool->deleteItem($key);
} catch (SimpleCacheException $e) {
throw $e;
Expand Down Expand Up @@ -151,13 +155,15 @@ public function getMultiple($keys, $default = null)

if (!$this->pool instanceof AdapterInterface) {
foreach ($items as $key => $item) {
$key = $this->encodeKey($key);
$values[$key] = $item->isHit() ? $item->get() : $default;
}

return $values;
}

foreach ($items as $key => $item) {
$key = $this->encodeKey($key);
if (!$item->isHit()) {
$values[$key] = $default;
continue;
Expand Down Expand Up @@ -192,11 +198,13 @@ public function setMultiple($values, $ttl = null)
if (null !== $f = $this->createCacheItem) {
$valuesIsArray = false;
foreach ($values as $key => $value) {
$key = $this->encodeKey($key);
$items[$key] = $f($key, $value, true);
}
} elseif ($valuesIsArray) {
$items = [];
foreach ($values as $key => $value) {
$key = $this->encodeKey($key);
$items[] = (string) $key;
}
$items = $this->pool->getItems($items);
Expand All @@ -205,6 +213,7 @@ public function setMultiple($values, $ttl = null)
if (\is_int($key)) {
$key = (string) $key;
}
$key = $this->encodeKey($key);
$items[$key] = $this->pool->getItem($key)->set($value);
}
}
Expand All @@ -217,7 +226,7 @@ public function setMultiple($values, $ttl = null)

foreach ($items as $key => $item) {
if ($valuesIsArray) {
$item->set($values[$key]);
$item->set($values[$this->encodeKey($key)]);
}
if (null !== $ttl) {
$item->expiresAfter($ttl);
Expand Down Expand Up @@ -254,6 +263,7 @@ public function deleteMultiple($keys)
public function has($key)
{
try {
$key = $this->encodeKey($key);
return $this->pool->hasItem($key);
} catch (SimpleCacheException $e) {
throw $e;
Expand Down
27 changes: 27 additions & 0 deletions src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,33 @@ protected function setUp()
}
}

/**
* Data provider for invalid keys.
* @todo : Override method from cache/integration-tests for remove 2 keys (with @ and /) ! See how to update it without override the method ?
*
* @return array
*/
public static function invalidKeys()
{
return [
[true],
[false],
[null],
[2],
[2.5],
['{str'],
['rand{'],
['rand{str'],
['rand}str'],
['rand(str'],
['rand)str'],
['rand\\str'],
['rand:str'],
[new \stdClass()],
[['array']],
];
}

public function testGet()
{
if (isset($this->skippedTests[__FUNCTION__])) {
Expand Down
2 changes: 0 additions & 2 deletions src/Symfony/Component/Cache/Tests/CacheItemTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ public function provideInvalidKey()
['}'],
['('],
[')'],
['/'],
['\\'],
['@'],
[':'],
[true],
[null],
Expand Down
28 changes: 28 additions & 0 deletions src/Symfony/Component/Cache/Tests/Simple/CacheTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,34 @@ protected function setUp()
}
}

/**
* Data provider for invalid keys.
* @todo : Override method from cache/integration-tests for remove 2 keys (with @ and /) ! See how to update it without override the method ?
*
* @return array
*/
public static function invalidKeys()
{
return [
[''],
[true],
[false],
[null],
[2],
[2.5],
['{str'],
['rand{'],
['rand{str'],
['rand}str'],
['rand(str'],
['rand)str'],
['rand\\str'],
['rand:str'],
[new \stdClass()],
[['array']],
];
}

public static function validKeys()
{
return array_merge(parent::validKeys(), [["a\0b"]]);
Expand Down
17 changes: 17 additions & 0 deletions src/Symfony/Component/Cache/Traits/KeyTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Symfony\Component\Cache\Traits;

trait KeyTrait
{
/**
* Encode a key if contain @ and / character and not already encoded with rawurlencode() function
*/
public function encodeKey($key)
{
if (\is_string($key) && '' !== $key && false === strpos('%', $key) && false !== strpbrk($key, '@/')) {
return rawurlencode($key);
}
return $key;
}
}