From adb4493886fadec4f98f6b20c0d0d93580b22ae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Wed, 9 Dec 2015 08:14:29 +0100 Subject: [PATCH 1/6] [PropertyInfo] Cache support --- .../PropertyInfo/PropertyInfoExtractor.php | 34 +++++++++++++++++-- .../Tests/PropertyInfoExtractorTest.php | 10 ++++++ .../Component/PropertyInfo/composer.json | 4 ++- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php b/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php index 942318af605d8..1cb960ab66945 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php +++ b/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php @@ -11,6 +11,8 @@ namespace Symfony\Component\PropertyInfo; +use Doctrine\Common\Cache\Cache; + /** * Default {@see PropertyInfoExtractorInterface} implementation. * @@ -38,18 +40,30 @@ class PropertyInfoExtractor implements PropertyInfoExtractorInterface */ private $accessExtractors; + /** + * @var Cache + */ + private $cache; + + /** + * @var array + */ + private $arrayCache = array(); + /** * @param PropertyListExtractorInterface[] $listExtractors * @param PropertyTypeExtractorInterface[] $typeExtractors * @param PropertyDescriptionExtractorInterface[] $descriptionExtractors * @param PropertyAccessExtractorInterface[] $accessExtractors + * @param Cache|null $cache */ - public function __construct(array $listExtractors = array(), array $typeExtractors = array(), array $descriptionExtractors = array(), array $accessExtractors = array()) + public function __construct(array $listExtractors = array(), array $typeExtractors = array(), array $descriptionExtractors = array(), array $accessExtractors = array(), Cache $cache = null) { $this->listExtractors = $listExtractors; $this->typeExtractors = $typeExtractors; $this->descriptionExtractors = $descriptionExtractors; $this->accessExtractors = $accessExtractors; + $this->cache = $cache; } /** @@ -111,11 +125,27 @@ public function isWritable($class, $property, array $context = array()) */ private function extract(array $extractors, $method, array $arguments) { + $key = $method.serialize($arguments); + + if (isset($this->arrayCache[$key])) { + return $this->arrayCache[$key]; + } + + if ($this->cache && $value = $this->cache->fetch($key)) { + return $this->arrayCache[$key] = $value; + } + foreach ($extractors as $extractor) { $value = call_user_func_array(array($extractor, $method), $arguments); if (null !== $value) { - return $value; + break; } } + + if ($this->cache) { + $this->cache->save($key, $value); + } + + return $this->arrayCache[$key] = $value; } } diff --git a/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorTest.php index c7375d3006e0b..844fb7ba83d5d 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\PropertyInfo\Tests; +use Doctrine\Common\Cache\ArrayCache; use Symfony\Component\PropertyInfo\PropertyInfoExtractor; use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyExtractor; use Symfony\Component\PropertyInfo\Tests\Fixtures\NullExtractor; @@ -69,4 +70,13 @@ public function testGetProperties() { $this->assertEquals(array('a', 'b'), $this->propertyInfo->getProperties('Foo')); } + + public function testCache() + { + $extractors = array(new NullExtractor(), new DummyExtractor()); + $this->propertyInfo = new PropertyInfoExtractor($extractors, $extractors, $extractors, $extractors, new ArrayCache()); + + $this->assertSame('short', $this->propertyInfo->getShortDescription('Foo', 'bar', array())); + $this->assertSame('short', $this->propertyInfo->getShortDescription('Foo', 'bar', array())); + } } diff --git a/src/Symfony/Component/PropertyInfo/composer.json b/src/Symfony/Component/PropertyInfo/composer.json index b730caad9cf31..4a34abb5eb33c 100644 --- a/src/Symfony/Component/PropertyInfo/composer.json +++ b/src/Symfony/Component/PropertyInfo/composer.json @@ -28,12 +28,14 @@ "require-dev": { "symfony/serializer": "~2.8|~3.0", "phpdocumentor/reflection": "^1.0.7", - "doctrine/annotations": "~1.0" + "doctrine/annotations": "~1.0", + "doctrine/cache": "~1.0" }, "conflict": { "phpdocumentor/reflection": "<1.0.7" }, "suggest": { + "doctrine/cache": "To cache results", "symfony/doctrine-bridge": "To use Doctrine metadata", "phpdocumentor/reflection": "To use the PHPDoc", "symfony/serializer": "To use Serializer metadata" From 6cc44455fbf792f71dbcb2afe308066025f4dfef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Wed, 9 Dec 2015 22:17:46 +0100 Subject: [PATCH 2/6] [PropertyInfo] Cache Decorator --- .../PropertyInfo/PropertyInfoExtractor.php | 34 +---- .../PropertyInfoExtractorCacheDecorator.php | 117 ++++++++++++++++++ .../AbstractPropertyInfoExtractorTest.php | 72 +++++++++++ .../Extractors/SerializerExtractorTest.php | 2 +- ...ropertyInfoExtractorCacheDecoratorTest.php | 34 +++++ .../Tests/PropertyInfoExtractorTest.php | 65 +--------- 6 files changed, 227 insertions(+), 97 deletions(-) create mode 100644 src/Symfony/Component/PropertyInfo/PropertyInfoExtractorCacheDecorator.php create mode 100644 src/Symfony/Component/PropertyInfo/Tests/AbstractPropertyInfoExtractorTest.php create mode 100644 src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorCacheDecoratorTest.php diff --git a/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php b/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php index 1cb960ab66945..942318af605d8 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php +++ b/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php @@ -11,8 +11,6 @@ namespace Symfony\Component\PropertyInfo; -use Doctrine\Common\Cache\Cache; - /** * Default {@see PropertyInfoExtractorInterface} implementation. * @@ -40,30 +38,18 @@ class PropertyInfoExtractor implements PropertyInfoExtractorInterface */ private $accessExtractors; - /** - * @var Cache - */ - private $cache; - - /** - * @var array - */ - private $arrayCache = array(); - /** * @param PropertyListExtractorInterface[] $listExtractors * @param PropertyTypeExtractorInterface[] $typeExtractors * @param PropertyDescriptionExtractorInterface[] $descriptionExtractors * @param PropertyAccessExtractorInterface[] $accessExtractors - * @param Cache|null $cache */ - public function __construct(array $listExtractors = array(), array $typeExtractors = array(), array $descriptionExtractors = array(), array $accessExtractors = array(), Cache $cache = null) + public function __construct(array $listExtractors = array(), array $typeExtractors = array(), array $descriptionExtractors = array(), array $accessExtractors = array()) { $this->listExtractors = $listExtractors; $this->typeExtractors = $typeExtractors; $this->descriptionExtractors = $descriptionExtractors; $this->accessExtractors = $accessExtractors; - $this->cache = $cache; } /** @@ -125,27 +111,11 @@ public function isWritable($class, $property, array $context = array()) */ private function extract(array $extractors, $method, array $arguments) { - $key = $method.serialize($arguments); - - if (isset($this->arrayCache[$key])) { - return $this->arrayCache[$key]; - } - - if ($this->cache && $value = $this->cache->fetch($key)) { - return $this->arrayCache[$key] = $value; - } - foreach ($extractors as $extractor) { $value = call_user_func_array(array($extractor, $method), $arguments); if (null !== $value) { - break; + return $value; } } - - if ($this->cache) { - $this->cache->save($key, $value); - } - - return $this->arrayCache[$key] = $value; } } diff --git a/src/Symfony/Component/PropertyInfo/PropertyInfoExtractorCacheDecorator.php b/src/Symfony/Component/PropertyInfo/PropertyInfoExtractorCacheDecorator.php new file mode 100644 index 0000000000000..0b75940fce12a --- /dev/null +++ b/src/Symfony/Component/PropertyInfo/PropertyInfoExtractorCacheDecorator.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyInfo; + +use Doctrine\Common\Cache\Cache; + +/** + * Adds a cache layer on top of an extractor. + * + * @author Kévin Dunglas + */ +class PropertyInfoExtractorCacheDecorator implements PropertyInfoExtractorInterface +{ + /** + * @var PropertyInfoExtractorInterface + */ + private $propertyInfoExtractor; + + /** + * @var Cache + */ + private $cache; + + /** + * @var array + */ + private $arrayCache = array(); + + public function __construct(PropertyInfoExtractorInterface $propertyInfoExtractor, Cache $cache) + { + $this->propertyInfoExtractor = $propertyInfoExtractor; + $this->cache = $cache; + } + + /** + * {@inheritdoc} + */ + public function isReadable($class, $property, array $context = array()) + { + return $this->extract('isReadable', array($class, $property, $context)); + } + + /** + * {@inheritdoc} + */ + public function isWritable($class, $property, array $context = array()) + { + return $this->extract('isWritable', array($class, $property, $context)); + } + + /** + * {@inheritdoc} + */ + public function getShortDescription($class, $property, array $context = array()) + { + return $this->extract('getShortDescription', array($class, $property, $context)); + } + + /** + * {@inheritdoc} + */ + public function getLongDescription($class, $property, array $context = array()) + { + return $this->extract('getLongDescription', array($class, $property, $context)); + } + + /** + * {@inheritdoc} + */ + public function getProperties($class, array $context = array()) + { + return $this->extract('getProperties', array($class, $context)); + } + + /** + * {@inheritdoc} + */ + public function getTypes($class, $property, array $context = array()) + { + return $this->extract('getTypes', array($class, $context)); + } + + /** + * Retrieves the cached data if applicable or delegates to the decorated extractor. + * + * @param string $method + * @param array $arguments + * + * @return mixed + */ + private function extract($method, array $arguments) + { + $key = $method.serialize($arguments); + + if (isset($this->arrayCache[$key])) { + return $this->arrayCache[$key]; + } + + if ($value = $this->cache->fetch($key)) { + return $this->arrayCache[$key] = $value; + } + + $value = call_user_func_array(array($this->propertyInfoExtractor, $method), $arguments); + $this->cache->save($key, $value); + + return $this->arrayCache[$key] = $value; + } +} diff --git a/src/Symfony/Component/PropertyInfo/Tests/AbstractPropertyInfoExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/AbstractPropertyInfoExtractorTest.php new file mode 100644 index 0000000000000..dd65e308781eb --- /dev/null +++ b/src/Symfony/Component/PropertyInfo/Tests/AbstractPropertyInfoExtractorTest.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyInfo\Tests; + +use Symfony\Component\PropertyInfo\PropertyInfoExtractor; +use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyExtractor; +use Symfony\Component\PropertyInfo\Tests\Fixtures\NullExtractor; +use Symfony\Component\PropertyInfo\Type; + +/** + * @author Kévin Dunglas + */ +class AbstractPropertyInfoExtractorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var PropertyInfoExtractor + */ + protected $propertyInfo; + + public function setUp() + { + $extractors = array(new NullExtractor(), new DummyExtractor()); + $this->propertyInfo = new PropertyInfoExtractor($extractors, $extractors, $extractors, $extractors); + } + + public function testInstanceOf() + { + $this->assertInstanceOf('Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface', $this->propertyInfo); + $this->assertInstanceOf('Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface', $this->propertyInfo); + $this->assertInstanceOf('Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface', $this->propertyInfo); + $this->assertInstanceOf('Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface', $this->propertyInfo); + } + + public function testGetShortDescription() + { + $this->assertSame('short', $this->propertyInfo->getShortDescription('Foo', 'bar', array())); + } + + public function testGetLongDescription() + { + $this->assertSame('long', $this->propertyInfo->getLongDescription('Foo', 'bar', array())); + } + + public function testGetTypes() + { + $this->assertEquals(array(new Type(Type::BUILTIN_TYPE_INT)), $this->propertyInfo->getTypes('Foo', 'bar', array())); + } + + public function testIsReadable() + { + $this->assertTrue($this->propertyInfo->isReadable('Foo', 'bar', array())); + } + + public function testIsWritable() + { + $this->assertTrue($this->propertyInfo->isWritable('Foo', 'bar', array())); + } + + public function testGetProperties() + { + $this->assertEquals(array('a', 'b'), $this->propertyInfo->getProperties('Foo')); + } +} diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractors/SerializerExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractors/SerializerExtractorTest.php index c170f2237add9..267dd21a11db5 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractors/SerializerExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractors/SerializerExtractorTest.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\PropertyInfo\PropertyInfo\Tests\Extractors; +namespace Symfony\Component\PropertyInfo\Tests\Extractors; use Doctrine\Common\Annotations\AnnotationReader; use Symfony\Component\PropertyInfo\Extractor\SerializerExtractor; diff --git a/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorCacheDecoratorTest.php b/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorCacheDecoratorTest.php new file mode 100644 index 0000000000000..0112dde217141 --- /dev/null +++ b/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorCacheDecoratorTest.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyInfo\Tests; + +use Doctrine\Common\Cache\ArrayCache; +use Symfony\Component\PropertyInfo\PropertyInfoExtractorCacheDecorator; + +/** + * @author Kévin Dunglas + */ +class PropertyInfoExtractorCacheDecoratorTest extends AbstractPropertyInfoExtractorTest +{ + public function setUp() + { + parent::setUp(); + + $this->propertyInfo = new PropertyInfoExtractorCacheDecorator($this->propertyInfo, new ArrayCache()); + } + + public function testCache() + { + $this->assertSame('short', $this->propertyInfo->getShortDescription('Foo', 'bar', array())); + $this->assertSame('short', $this->propertyInfo->getShortDescription('Foo', 'bar', array())); + } +} diff --git a/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorTest.php index 844fb7ba83d5d..53c1b1d8a5c22 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorTest.php @@ -11,72 +11,9 @@ namespace Symfony\Component\PropertyInfo\Tests; -use Doctrine\Common\Cache\ArrayCache; -use Symfony\Component\PropertyInfo\PropertyInfoExtractor; -use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyExtractor; -use Symfony\Component\PropertyInfo\Tests\Fixtures\NullExtractor; -use Symfony\Component\PropertyInfo\Type; - /** * @author Kévin Dunglas */ -class PropertyInfoExtractorTest extends \PHPUnit_Framework_TestCase +class PropertyInfoExtractorTest extends AbstractPropertyInfoExtractorTest { - /** - * @var PropertyInfoExtractor - */ - private $propertyInfo; - - public function setUp() - { - $extractors = array(new NullExtractor(), new DummyExtractor()); - $this->propertyInfo = new PropertyInfoExtractor($extractors, $extractors, $extractors, $extractors); - } - - public function testInstanceOf() - { - $this->assertInstanceOf('Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface', $this->propertyInfo); - $this->assertInstanceOf('Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface', $this->propertyInfo); - $this->assertInstanceOf('Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface', $this->propertyInfo); - $this->assertInstanceOf('Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface', $this->propertyInfo); - } - - public function testGetShortDescription() - { - $this->assertSame('short', $this->propertyInfo->getShortDescription('Foo', 'bar', array())); - } - - public function testGetLongDescription() - { - $this->assertSame('long', $this->propertyInfo->getLongDescription('Foo', 'bar', array())); - } - - public function testGetTypes() - { - $this->assertEquals(array(new Type(Type::BUILTIN_TYPE_INT)), $this->propertyInfo->getTypes('Foo', 'bar', array())); - } - - public function testIsReadable() - { - $this->assertTrue($this->propertyInfo->isReadable('Foo', 'bar', array())); - } - - public function testIsWritable() - { - $this->assertTrue($this->propertyInfo->isWritable('Foo', 'bar', array())); - } - - public function testGetProperties() - { - $this->assertEquals(array('a', 'b'), $this->propertyInfo->getProperties('Foo')); - } - - public function testCache() - { - $extractors = array(new NullExtractor(), new DummyExtractor()); - $this->propertyInfo = new PropertyInfoExtractor($extractors, $extractors, $extractors, $extractors, new ArrayCache()); - - $this->assertSame('short', $this->propertyInfo->getShortDescription('Foo', 'bar', array())); - $this->assertSame('short', $this->propertyInfo->getShortDescription('Foo', 'bar', array())); - } } From 681bc86f47c8eb221c98c1cc53b6513dd5529559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Thu, 31 Dec 2015 11:30:57 +0100 Subject: [PATCH 3/6] [PropertyInfo] Deal with not serializable context --- .../PropertyInfo/PropertyInfoExtractorCacheDecorator.php | 9 ++++++++- .../Tests/PropertyInfoExtractorCacheDecoratorTest.php | 5 +++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/PropertyInfo/PropertyInfoExtractorCacheDecorator.php b/src/Symfony/Component/PropertyInfo/PropertyInfoExtractorCacheDecorator.php index 0b75940fce12a..479954b008f67 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyInfoExtractorCacheDecorator.php +++ b/src/Symfony/Component/PropertyInfo/PropertyInfoExtractorCacheDecorator.php @@ -99,7 +99,14 @@ public function getTypes($class, $property, array $context = array()) */ private function extract($method, array $arguments) { - $key = $method.serialize($arguments); + try { + $serializedArguments = serialize($arguments); + } catch (\Exception $exception) { + // If arguments are not serializable, skip the cache + return call_user_func_array(array($this->propertyInfoExtractor, $method), $arguments); + } + + $key = $method.$serializedArguments; if (isset($this->arrayCache[$key])) { return $this->arrayCache[$key]; diff --git a/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorCacheDecoratorTest.php b/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorCacheDecoratorTest.php index 0112dde217141..213242aef3e8d 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorCacheDecoratorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorCacheDecoratorTest.php @@ -31,4 +31,9 @@ public function testCache() $this->assertSame('short', $this->propertyInfo->getShortDescription('Foo', 'bar', array())); $this->assertSame('short', $this->propertyInfo->getShortDescription('Foo', 'bar', array())); } + + public function testNotSerializableContext() + { + $this->assertSame('short', $this->propertyInfo->getShortDescription('Foo', 'bar', array('foo' => function () {}))); + } } From 40081309ab0dbc62c1bcb08b8f31c16ff3324137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Tue, 26 Jan 2016 12:16:37 +0100 Subject: [PATCH 4/6] wip --- ...tor.php => PropertyInfoCacheExtractor.php} | 29 ++++++---- .../Tests/PropertyInfoCacheExtractorTest.php | 53 +++++++++++++++++++ ...ropertyInfoExtractorCacheDecoratorTest.php | 39 -------------- 3 files changed, 72 insertions(+), 49 deletions(-) rename src/Symfony/Component/PropertyInfo/{PropertyInfoExtractorCacheDecorator.php => PropertyInfoCacheExtractor.php} (78%) create mode 100644 src/Symfony/Component/PropertyInfo/Tests/PropertyInfoCacheExtractorTest.php delete mode 100644 src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorCacheDecoratorTest.php diff --git a/src/Symfony/Component/PropertyInfo/PropertyInfoExtractorCacheDecorator.php b/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php similarity index 78% rename from src/Symfony/Component/PropertyInfo/PropertyInfoExtractorCacheDecorator.php rename to src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php index 479954b008f67..46979e2104009 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyInfoExtractorCacheDecorator.php +++ b/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php @@ -11,14 +11,14 @@ namespace Symfony\Component\PropertyInfo; -use Doctrine\Common\Cache\Cache; +use Psr\Cache\CacheItemPoolInterface; /** - * Adds a cache layer on top of an extractor. + * Adds a PSR-6 cache layer on top of an extractor. * * @author Kévin Dunglas */ -class PropertyInfoExtractorCacheDecorator implements PropertyInfoExtractorInterface +class PropertyInfoCacheExtractor implements PropertyInfoExtractorInterface { /** * @var PropertyInfoExtractorInterface @@ -26,19 +26,19 @@ class PropertyInfoExtractorCacheDecorator implements PropertyInfoExtractorInterf private $propertyInfoExtractor; /** - * @var Cache + * @var CacheItemPoolInterface */ - private $cache; + private $cacheItemPool; /** * @var array */ private $arrayCache = array(); - public function __construct(PropertyInfoExtractorInterface $propertyInfoExtractor, Cache $cache) + public function __construct(PropertyInfoExtractorInterface $propertyInfoExtractor, CacheItemPoolInterface $cacheItemPool) { $this->propertyInfoExtractor = $propertyInfoExtractor; - $this->cache = $cache; + $this->cacheItemPool = $cacheItemPool; } /** @@ -106,19 +106,28 @@ private function extract($method, array $arguments) return call_user_func_array(array($this->propertyInfoExtractor, $method), $arguments); } - $key = $method.$serializedArguments; + $key = $this->escape($method.'.'.$serializedArguments); if (isset($this->arrayCache[$key])) { return $this->arrayCache[$key]; } - if ($value = $this->cache->fetch($key)) { + if ($value = $this->cacheItemPool->getItem($key)) { return $this->arrayCache[$key] = $value; } $value = call_user_func_array(array($this->propertyInfoExtractor, $method), $arguments); - $this->cache->save($key, $value); + $this->cacheItemPool->save($key, $value); return $this->arrayCache[$key] = $value; } + + private function escape($key) + { + str_replace( + array('_', '{', '}', '(', ')', '/', '\\', '@', ':'), + array('_95', '_123', '_125', '_40', '_41', '_47', '_92', '_64', '_58'), + $key + ); + } } diff --git a/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoCacheExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoCacheExtractorTest.php new file mode 100644 index 0000000000000..9c0c6b62c1931 --- /dev/null +++ b/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoCacheExtractorTest.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyInfo\Tests; + +use Symfony\Component\PropertyInfo\PropertyInfoCacheExtractor; + +/** + * @author Kévin Dunglas + */ +class PropertyInfoCacheExtractorTest extends AbstractPropertyInfoExtractorTest +{ + public function setUp() + { + parent::setUp(); + + $this->propertyInfo = new PropertyInfoCacheExtractor($this->propertyInfo, new ArrayCac); + } + + public function testCache() + { + $this->assertSame('short', $this->propertyInfo->getShortDescription('Foo', 'bar', array())); + $this->assertSame('short', $this->propertyInfo->getShortDescription('Foo', 'bar', array())); + } + + public function testNotSerializableContext() + { + $this->assertSame('short', $this->propertyInfo->getShortDescription('Foo', 'bar', array('foo' => function () {}))); + } + + public function testEscape() + { + $reflectionMethod = new \ReflectionMethod($this->propertyInfo, 'escape'); + $reflectionMethod->setAccessible(true); + + $this->assertSame('foo_bar', $this->propertyInfo->escape('foo_95bar')); + $this->assertSame('foo_95bar', $this->propertyInfo->escape('foo_9595bar')); + $this->assertSame('foo{bar}', $this->propertyInfo->escape('foo_123bar_125')); + $this->assertSame('foo(bar)', $this->propertyInfo->escape('foo_40bar_41')); + $this->assertSame('foo/bar', $this->propertyInfo->escape('foo_47bar')); + $this->assertSame('foo\bar', $this->propertyInfo->escape('foo_92bar')); + $this->assertSame('foo@bar', $this->propertyInfo->escape('foo_64bar')); + $this->assertSame('foo:bar', $this->propertyInfo->escape('foo_58bar')); + } +} diff --git a/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorCacheDecoratorTest.php b/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorCacheDecoratorTest.php deleted file mode 100644 index 213242aef3e8d..0000000000000 --- a/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorCacheDecoratorTest.php +++ /dev/null @@ -1,39 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\PropertyInfo\Tests; - -use Doctrine\Common\Cache\ArrayCache; -use Symfony\Component\PropertyInfo\PropertyInfoExtractorCacheDecorator; - -/** - * @author Kévin Dunglas - */ -class PropertyInfoExtractorCacheDecoratorTest extends AbstractPropertyInfoExtractorTest -{ - public function setUp() - { - parent::setUp(); - - $this->propertyInfo = new PropertyInfoExtractorCacheDecorator($this->propertyInfo, new ArrayCache()); - } - - public function testCache() - { - $this->assertSame('short', $this->propertyInfo->getShortDescription('Foo', 'bar', array())); - $this->assertSame('short', $this->propertyInfo->getShortDescription('Foo', 'bar', array())); - } - - public function testNotSerializableContext() - { - $this->assertSame('short', $this->propertyInfo->getShortDescription('Foo', 'bar', array('foo' => function () {}))); - } -} From 9396a19843e4a793d4b91a9e3cf3851a4ebba3b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Tue, 26 Jan 2016 13:05:31 +0100 Subject: [PATCH 5/6] [PropertyInfo] PSR-6 cache --- .../PropertyInfoCacheExtractor.php | 35 ++++++++++++++----- .../Tests/PropertyInfoCacheExtractorTest.php | 31 ++++++++++------ .../Component/PropertyInfo/composer.json | 6 ++-- 3 files changed, 51 insertions(+), 21 deletions(-) diff --git a/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php b/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php index 46979e2104009..b7d5c6d372ca6 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php +++ b/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php @@ -112,22 +112,41 @@ private function extract($method, array $arguments) return $this->arrayCache[$key]; } - if ($value = $this->cacheItemPool->getItem($key)) { - return $this->arrayCache[$key] = $value; + $item = $this->cacheItemPool->getItem($key); + + if ($item->isHit()) { + return $this->arrayCache[$key] = $item->get(); } $value = call_user_func_array(array($this->propertyInfoExtractor, $method), $arguments); - $this->cacheItemPool->save($key, $value); + $item->set($value); + $this->cacheItemPool->save($item); return $this->arrayCache[$key] = $value; } + /** + * Escapes a key according to PSR-6. + * + * Replaces characters forbidden by PSR-6 and the _ char by the _ char followed by the ASCII + * code of the escaped char. + * + * @param string $key + * + * @return string + */ private function escape($key) { - str_replace( - array('_', '{', '}', '(', ')', '/', '\\', '@', ':'), - array('_95', '_123', '_125', '_40', '_41', '_47', '_92', '_64', '_58'), - $key - ); + return strtr($key, array( + '{' => '_123', + '}' => '_125', + '(' => '_40', + ')' => '_41', + '/' => '_47', + '\\' => '_92', + '@' => '_64', + ':' => '_58', + '_' => '_95', + )); } } diff --git a/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoCacheExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoCacheExtractorTest.php index 9c0c6b62c1931..5f09fb041a986 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoCacheExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoCacheExtractorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\PropertyInfo\Tests; +use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\PropertyInfo\PropertyInfoCacheExtractor; /** @@ -22,7 +23,7 @@ public function setUp() { parent::setUp(); - $this->propertyInfo = new PropertyInfoCacheExtractor($this->propertyInfo, new ArrayCac); + $this->propertyInfo = new PropertyInfoCacheExtractor($this->propertyInfo, new ArrayAdapter()); } public function testCache() @@ -36,18 +37,28 @@ public function testNotSerializableContext() $this->assertSame('short', $this->propertyInfo->getShortDescription('Foo', 'bar', array('foo' => function () {}))); } - public function testEscape() + /** + * @dataProvider escapeDataProvider + */ + public function testEscape($toEscape, $expected) { $reflectionMethod = new \ReflectionMethod($this->propertyInfo, 'escape'); $reflectionMethod->setAccessible(true); - $this->assertSame('foo_bar', $this->propertyInfo->escape('foo_95bar')); - $this->assertSame('foo_95bar', $this->propertyInfo->escape('foo_9595bar')); - $this->assertSame('foo{bar}', $this->propertyInfo->escape('foo_123bar_125')); - $this->assertSame('foo(bar)', $this->propertyInfo->escape('foo_40bar_41')); - $this->assertSame('foo/bar', $this->propertyInfo->escape('foo_47bar')); - $this->assertSame('foo\bar', $this->propertyInfo->escape('foo_92bar')); - $this->assertSame('foo@bar', $this->propertyInfo->escape('foo_64bar')); - $this->assertSame('foo:bar', $this->propertyInfo->escape('foo_58bar')); + $this->assertSame($expected, $reflectionMethod->invoke($this->propertyInfo, $toEscape)); + } + + public function escapeDataProvider() + { + return array( + array('foo_bar', 'foo_95bar'), + array('foo_95bar', 'foo_9595bar'), + array('foo{bar}', 'foo_123bar_125'), + array('foo(bar)', 'foo_40bar_41'), + array('foo/bar', 'foo_47bar'), + array('foo\bar', 'foo_92bar'), + array('foo@bar', 'foo_64bar'), + array('foo:bar', 'foo_58bar'), + ); } } diff --git a/src/Symfony/Component/PropertyInfo/composer.json b/src/Symfony/Component/PropertyInfo/composer.json index 4a34abb5eb33c..fa7c72b0d9f81 100644 --- a/src/Symfony/Component/PropertyInfo/composer.json +++ b/src/Symfony/Component/PropertyInfo/composer.json @@ -27,15 +27,15 @@ }, "require-dev": { "symfony/serializer": "~2.8|~3.0", + "symfony/cache": "~3.1", "phpdocumentor/reflection": "^1.0.7", - "doctrine/annotations": "~1.0", - "doctrine/cache": "~1.0" + "doctrine/annotations": "~1.0" }, "conflict": { "phpdocumentor/reflection": "<1.0.7" }, "suggest": { - "doctrine/cache": "To cache results", + "symfony/cache": "To cache results", "symfony/doctrine-bridge": "To use Doctrine metadata", "phpdocumentor/reflection": "To use the PHPDoc", "symfony/serializer": "To use Serializer metadata" From 43f344652705e010011382a9b092d85b8be1f5b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Tue, 26 Jan 2016 18:46:58 +0100 Subject: [PATCH 6/6] Replace symfony/cache by psr/cache-implementation in suggest --- src/Symfony/Component/PropertyInfo/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/PropertyInfo/composer.json b/src/Symfony/Component/PropertyInfo/composer.json index fa7c72b0d9f81..b484a6fe3c626 100644 --- a/src/Symfony/Component/PropertyInfo/composer.json +++ b/src/Symfony/Component/PropertyInfo/composer.json @@ -35,7 +35,7 @@ "phpdocumentor/reflection": "<1.0.7" }, "suggest": { - "symfony/cache": "To cache results", + "psr/cache-implementation": "To cache results", "symfony/doctrine-bridge": "To use Doctrine metadata", "phpdocumentor/reflection": "To use the PHPDoc", "symfony/serializer": "To use Serializer metadata"