diff --git a/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php b/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php new file mode 100644 index 0000000000000..b7d5c6d372ca6 --- /dev/null +++ b/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php @@ -0,0 +1,152 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyInfo; + +use Psr\Cache\CacheItemPoolInterface; + +/** + * Adds a PSR-6 cache layer on top of an extractor. + * + * @author Kévin Dunglas + */ +class PropertyInfoCacheExtractor implements PropertyInfoExtractorInterface +{ + /** + * @var PropertyInfoExtractorInterface + */ + private $propertyInfoExtractor; + + /** + * @var CacheItemPoolInterface + */ + private $cacheItemPool; + + /** + * @var array + */ + private $arrayCache = array(); + + public function __construct(PropertyInfoExtractorInterface $propertyInfoExtractor, CacheItemPoolInterface $cacheItemPool) + { + $this->propertyInfoExtractor = $propertyInfoExtractor; + $this->cacheItemPool = $cacheItemPool; + } + + /** + * {@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) + { + 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 = $this->escape($method.'.'.$serializedArguments); + + if (isset($this->arrayCache[$key])) { + return $this->arrayCache[$key]; + } + + $item = $this->cacheItemPool->getItem($key); + + if ($item->isHit()) { + return $this->arrayCache[$key] = $item->get(); + } + + $value = call_user_func_array(array($this->propertyInfoExtractor, $method), $arguments); + $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) + { + return strtr($key, array( + '{' => '_123', + '}' => '_125', + '(' => '_40', + ')' => '_41', + '/' => '_47', + '\\' => '_92', + '@' => '_64', + ':' => '_58', + '_' => '_95', + )); + } +} 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/PropertyInfoCacheExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoCacheExtractorTest.php new file mode 100644 index 0000000000000..5f09fb041a986 --- /dev/null +++ b/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoCacheExtractorTest.php @@ -0,0 +1,64 @@ + + * + * 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\Cache\Adapter\ArrayAdapter; +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 ArrayAdapter()); + } + + 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 () {}))); + } + + /** + * @dataProvider escapeDataProvider + */ + public function testEscape($toEscape, $expected) + { + $reflectionMethod = new \ReflectionMethod($this->propertyInfo, 'escape'); + $reflectionMethod->setAccessible(true); + + $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/Tests/PropertyInfoExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorTest.php index c7375d3006e0b..53c1b1d8a5c22 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorTest.php @@ -11,62 +11,9 @@ 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 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')); - } } diff --git a/src/Symfony/Component/PropertyInfo/composer.json b/src/Symfony/Component/PropertyInfo/composer.json index b730caad9cf31..b484a6fe3c626 100644 --- a/src/Symfony/Component/PropertyInfo/composer.json +++ b/src/Symfony/Component/PropertyInfo/composer.json @@ -27,6 +27,7 @@ }, "require-dev": { "symfony/serializer": "~2.8|~3.0", + "symfony/cache": "~3.1", "phpdocumentor/reflection": "^1.0.7", "doctrine/annotations": "~1.0" }, @@ -34,6 +35,7 @@ "phpdocumentor/reflection": "<1.0.7" }, "suggest": { + "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" pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy