diff --git a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php index a1f0de7dcaefd..f086064a33914 100644 --- a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php @@ -16,6 +16,7 @@ use Psr\Log\LoggerAwareTrait; use Psr\Log\LoggerInterface; use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; /** * @author Nicolas Grekas @@ -32,9 +33,17 @@ abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface private $createCacheItem; private $mergeByLifetime; + /** + * @var int|null The maximum length to enforce for identifiers or null when no limit applies + */ + protected $maxIdLength; + protected function __construct($namespace = '', $defaultLifetime = 0) { - $this->namespace = '' === $namespace ? '' : $this->getId($namespace); + $this->namespace = '' === $namespace ? '' : $this->getId($namespace).':'; + if (null !== $this->maxIdLength && strlen($namespace) > $this->maxIdLength - 24) { + throw new InvalidArgumentException(sprintf('Namespace must be %d chars max, %d given ("%s")', $this->maxIdLength - 24, strlen($namespace), $namespace)); + } $this->createCacheItem = \Closure::bind( function ($key, $value, $isHit) use ($defaultLifetime) { $item = new CacheItem(); @@ -401,7 +410,14 @@ private function getId($key) { CacheItem::validateKey($key); - return $this->namespace.$key; + if (null === $this->maxIdLength) { + return $this->namespace.$key; + } + if (strlen($id = $this->namespace.$key) > $this->maxIdLength) { + $id = $this->namespace.substr_replace(base64_encode(md5($key, true)), ':', -2); + } + + return $id; } private function generateItems($items, &$keys) diff --git a/src/Symfony/Component/Cache/Adapter/ApcuAdapter.php b/src/Symfony/Component/Cache/Adapter/ApcuAdapter.php index d0af11395700d..67afd5c72a89e 100644 --- a/src/Symfony/Component/Cache/Adapter/ApcuAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/ApcuAdapter.php @@ -37,9 +37,9 @@ public function __construct($namespace = '', $defaultLifetime = 0, $version = nu if (null !== $version) { CacheItem::validateKey($version); - if (!apcu_exists($version.':'.$namespace)) { + if (!apcu_exists($version.'@'.$namespace)) { $this->clear($namespace); - apcu_add($version.':'.$namespace, null); + apcu_add($version.'@'.$namespace, null); } } } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/MaxIdLengthAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/MaxIdLengthAdapterTest.php new file mode 100644 index 0000000000000..8c96e7f969a7c --- /dev/null +++ b/src/Symfony/Component/Cache/Tests/Adapter/MaxIdLengthAdapterTest.php @@ -0,0 +1,56 @@ + + * + * 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 Symfony\Component\Cache\Adapter\AbstractAdapter; + +class MaxIdLengthAdapterTest extends \PHPUnit_Framework_TestCase +{ + public function testLongKey() + { + $cache = $this->getMockBuilder(MaxIdLengthAdapter::class) + ->setConstructorArgs(array(str_repeat('-', 10))) + ->setMethods(array('doHave', 'doFetch', 'doDelete', 'doSave', 'doClear')) + ->getMock(); + + $cache->expects($this->exactly(2)) + ->method('doHave') + ->withConsecutive( + array($this->equalTo('----------:nWfzGiCgLczv3SSUzXL3kg:')), + array($this->equalTo('----------:---------------------------------------')) + ); + + $cache->hasItem(str_repeat('-', 40)); + $cache->hasItem(str_repeat('-', 39)); + } + + /** + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage Namespace must be 26 chars max, 40 given ("----------------------------------------") + */ + public function testTooLongNamespace() + { + $cache = $this->getMockBuilder(MaxIdLengthAdapter::class) + ->setConstructorArgs(array(str_repeat('-', 40))) + ->getMock(); + } +} + +abstract class MaxIdLengthAdapter extends AbstractAdapter +{ + protected $maxIdLength = 50; + + public function __construct($ns) + { + parent::__construct($ns); + } +}