From 714b916d5d55c899a18f1bcc8462ba4dcb97c3a1 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 4 Apr 2016 09:11:03 +0200 Subject: [PATCH] [FrameworkBundle] Add & use Psr6CacheClearer --- .../Compiler/CachePoolPass.php | 31 ++++++--- .../DependencyInjection/Configuration.php | 15 +++-- .../FrameworkExtension.php | 4 +- .../Resources/config/cache_pools.xml | 18 +++-- .../Resources/config/schema/symfony-1.0.xsd | 5 +- .../Compiler/CachePoolPassTest.php | 2 +- .../Fixtures/php/cache.php | 13 ++-- .../Fixtures/xml/cache.xml | 8 +-- .../Fixtures/yml/cache.yml | 13 ++-- .../FrameworkExtensionTest.php | 1 - .../Tests/Functional/CachePoolsTest.php | 66 +++++++++++++++++++ .../Functional/app/CachePools/bundles.php | 18 +++++ .../Functional/app/CachePools/config.yml | 8 +++ .../app/CachePools/redis_config.yml | 22 +++++++ .../Bundle/FrameworkBundle/composer.json | 3 +- .../Cache/Adapter/FilesystemAdapter.php | 3 + .../Cache/Tests/Adapter/FilesystemTest.php | 2 +- .../Compiler/PassConfig.php | 5 +- .../ResolveDefinitionTemplatesPass.php | 6 +- .../CacheClearer/Psr6CacheClearer.php | 37 +++++++++++ 20 files changed, 228 insertions(+), 52 deletions(-) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolsTest.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/bundles.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/config.yml create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/redis_config.yml create mode 100644 src/Symfony/Component/HttpKernel/CacheClearer/Psr6CacheClearer.php diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php index 4c56a58781..8e1e90cfac 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php @@ -27,36 +27,47 @@ class CachePoolPass implements CompilerPassInterface public function process(ContainerBuilder $container) { $attributes = array( - 'provider_service', + 'provider', 'namespace', 'default_lifetime', - 'directory', ); foreach ($container->findTaggedServiceIds('cache.pool') as $id => $tags) { $adapter = $pool = $container->getDefinition($id); - $tags[0] += array('namespace' => $this->getNamespace($id)); - + if ($pool->isAbstract()) { + continue; + } + if (!isset($tags[0]['namespace'])) { + $tags[0]['namespace'] = $this->getNamespace($id); + } while ($adapter instanceof DefinitionDecorator) { $adapter = $container->findDefinition($adapter->getParent()); if ($t = $adapter->getTag('cache.pool')) { $tags[0] += $t[0]; } } - if ($pool->isAbstract()) { - continue; + if (isset($tags[0]['clearer'])) { + $clearer = $container->getDefinition($tags[0]['clearer']); + } else { + $clearer = null; } - if (isset($tags[0]['provider_service']) && is_string($tags[0]['provider_service'])) { - $tags[0]['provider_service'] = new Reference($tags[0]['provider_service']); + unset($tags[0]['clearer']); + + if (isset($tags[0]['provider']) && is_string($tags[0]['provider'])) { + $tags[0]['provider'] = new Reference($tags[0]['provider']); } $i = 0; foreach ($attributes as $attr) { if (isset($tags[0][$attr])) { $pool->replaceArgument($i++, $tags[0][$attr]); - unset($tags[0][$attr]); } + unset($tags[0][$attr]); } if (!empty($tags[0])) { - throw new \InvalidArgumentException(sprintf('Invalid "cache.pool" tag for service "%s": accepted attributes are "provider_service", "namespace", "default_lifetime" and "directory", found "%s".', $id, implode('", "', array_keys($tags[0])))); + throw new \InvalidArgumentException(sprintf('Invalid "cache.pool" tag for service "%s": accepted attributes are "provider", "namespace" and "default_lifetime", found "%s".', $id, implode('", "', array_keys($tags[0])))); + } + + if (null !== $clearer) { + $clearer->addMethodCall('addPool', array(new Reference($id))); } } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 20a9e8b299..e125e3096a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -561,14 +561,19 @@ class Configuration implements ConfigurationInterface ->useAttributeAsKey('name') ->prototype('array') ->children() - ->scalarNode('adapter_service') - ->info('The cache pool service to use as template definition.') + ->scalarNode('adapter') + ->info('The cache pool adapter service to use as template definition.') ->defaultValue('cache.adapter.default') ->end() ->booleanNode('public')->defaultFalse()->end() - ->integerNode('default_lifetime')->defaultNull()->end() - ->scalarNode('provider_service')->defaultNull()->end() - ->scalarNode('directory')->defaultNull()->end() + ->integerNode('default_lifetime')->end() + ->scalarNode('provider') + ->info('The service name to use as provider when the specified adapter needs one.') + ->end() + ->scalarNode('namespace') + ->info('The namespace where cached items are stored. Auto-generated by default. Set to false to disable namespacing.') + ->end() + ->scalarNode('clearer')->defaultValue('cache.default_pools_clearer')->end() ->end() ->end() ->end() diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index f00c2a42ae..ae9d79c414 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -1027,9 +1027,9 @@ class FrameworkExtension extends Extension } foreach ($config['pools'] as $name => $poolConfig) { - $poolDefinition = new DefinitionDecorator($poolConfig['adapter_service']); + $poolDefinition = new DefinitionDecorator($poolConfig['adapter']); $poolDefinition->setPublic($poolConfig['public']); - unset($poolConfig['adapter_service'], $poolConfig['public']); + unset($poolConfig['adapter'], $poolConfig['public']); $poolDefinition->addTag('cache.pool', $poolConfig); $container->setDefinition('cache.pool.'.$name, $poolDefinition); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache_pools.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache_pools.xml index bea42c651e..eef88060cf 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache_pools.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache_pools.xml @@ -6,33 +6,39 @@ + + + + - - + + + + %kernel.cache_dir%/pools + + - - - + + - %kernel.cache_dir% diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd index 26fed333de..d8fc67ae29 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -212,10 +212,11 @@ - + - + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php index f69c97ddc2..3fc66cff09 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php @@ -49,7 +49,7 @@ class CachePoolPassTest extends \PHPUnit_Framework_TestCase $container = new ContainerBuilder(); $cachePool = new Definition(); $cachePool->addTag('cache.pool', array( - 'provider_service' => 'foobar', + 'provider' => 'foobar', 'default_lifetime' => 3, )); $cachePool->addArgument(null); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php index 2cbd3b51eb..7cf634b92e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php @@ -4,23 +4,22 @@ $container->loadFromExtension('framework', array( 'cache' => array( 'pools' => array( 'foo' => array( - 'adapter_service' => 'cache.adapter.apcu', + 'adapter' => 'cache.adapter.apcu', 'default_lifetime' => 30, ), 'bar' => array( - 'adapter_service' => 'cache.adapter.doctrine', + 'adapter' => 'cache.adapter.doctrine', 'default_lifetime' => 5, - 'provider_service' => 'app.doctrine_cache_provider', + 'provider' => 'app.doctrine_cache_provider', ), 'baz' => array( - 'adapter_service' => 'cache.adapter.filesystem', + 'adapter' => 'cache.adapter.filesystem', 'default_lifetime' => 7, - 'directory' => 'app/cache/psr', ), 'foobar' => array( - 'adapter_service' => 'cache.adapter.psr6', + 'adapter' => 'cache.adapter.psr6', 'default_lifetime' => 10, - 'provider_service' => 'app.cache_pool', + 'provider' => 'app.cache_pool', ), 'def' => array( 'default_lifetime' => 11, diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache.xml index 13b05f6718..d6f472716f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache.xml @@ -7,10 +7,10 @@ - - - - + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml index f3c6048a77..395009f18a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml @@ -2,19 +2,18 @@ framework: cache: pools: foo: - adapter_service: cache.adapter.apcu + adapter: cache.adapter.apcu default_lifetime: 30 bar: - adapter_service: cache.adapter.doctrine + adapter: cache.adapter.doctrine default_lifetime: 5 - provider_service: app.doctrine_cache_provider + provider: app.doctrine_cache_provider baz: - adapter_service: cache.adapter.filesystem + adapter: cache.adapter.filesystem default_lifetime: 7 - directory: app/cache/psr foobar: - adapter_service: cache.adapter.psr6 + adapter: cache.adapter.psr6 default_lifetime: 10 - provider_service: app.cache_pool + provider: app.cache_pool def: default_lifetime: 11 diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 6508fe005e..000ec8e6ff 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -683,7 +683,6 @@ abstract class FrameworkExtensionTest extends TestCase break; } - $this->assertTrue($adapterDefinition->hasTag('cache.pool'), sprintf('Service definition "%s" is tagged with the "cache.pool" tag.', $adapterId)); $this->assertTrue($adapterDefinition->isAbstract(), sprintf('Service definition "%s" is abstract.', $adapterId)); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolsTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolsTest.php new file mode 100644 index 0000000000..b35eebdb31 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolsTest.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; + +use Symfony\Component\Cache\Adapter\FilesystemAdapter; +use Symfony\Component\Cache\Adapter\RedisAdapter; + +class CachePoolsTest extends WebTestCase +{ + public function testCachePools() + { + $this->doTestCachePools(array(), FilesystemAdapter::class); + } + + /** + * @requires extension redis + */ + public function testRedisCachePools() + { + try { + $this->doTestCachePools(array('root_config' => 'redis_config.yml', 'environment' => 'redis_cache'), RedisAdapter::class); + } catch (\PHPUnit_Framework_Error_Warning $e) { + if (0 !== strpos($e->getMessage(), 'unable to connect to 127.0.0.1')) { + throw $e; + } + $this->markTestSkipped($e->getMessage()); + } + } + + public function doTestCachePools($options, $adapterClass) + { + static::bootKernel($options); + $container = static::$kernel->getContainer(); + + $pool = $container->get('cache.pool.test'); + $this->assertInstanceOf($adapterClass, $pool); + + $key = 'foobar'; + $pool->deleteItem($key); + $item = $pool->getItem($key); + $this->assertFalse($item->isHit()); + + $item->set('baz'); + $pool->save($item); + $item = $pool->getItem($key); + $this->assertTrue($item->isHit()); + + $container->get('cache_clearer')->clear($container->getParameter('kernel.cache_dir')); + $item = $pool->getItem($key); + $this->assertFalse($item->isHit()); + } + + protected static function createKernel(array $options = array()) + { + return parent::createKernel(array('test_case' => 'CachePools') + $options); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/bundles.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/bundles.php new file mode 100644 index 0000000000..a73987bcc9 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; + +return array( + new FrameworkBundle(), + new TestBundle(), +); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/config.yml new file mode 100644 index 0000000000..25aff9cbcb --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/config.yml @@ -0,0 +1,8 @@ +imports: + - { resource: ../config/default.yml } + +framework: + cache: + pools: + test: + public: true diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/redis_config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/redis_config.yml new file mode 100644 index 0000000000..1bafa08c7c --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CachePools/redis_config.yml @@ -0,0 +1,22 @@ +imports: + - { resource: ../config/default.yml } + +services: + cache.adapter.redis.connection: + public: false + class: Redis + calls: + - [connect, [127.0.0.1]] + + cache.adapter.default: + abstract: true + parent: cache.adapter.redis + tags: + - name: cache.pool + provider: cache.adapter.redis.connection + +framework: + cache: + pools: + test: + public: true diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 689aa7d6b6..ba60449cfc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -19,7 +19,7 @@ "php": ">=5.5.9", "symfony/asset": "~2.8|~3.0", "symfony/class-loader": "~2.8|~3.0", - "symfony/dependency-injection": "~2.8|~3.0", + "symfony/dependency-injection": "~3.1", "symfony/config": "~2.8|~3.0", "symfony/event-dispatcher": "~2.8|~3.0", "symfony/http-foundation": "~3.1", @@ -38,6 +38,7 @@ }, "require-dev": { "symfony/browser-kit": "~2.8|~3.0", + "symfony/cache": "~3.1", "symfony/console": "~2.8|~3.0", "symfony/css-selector": "~2.8|~3.0", "symfony/dom-crawler": "~2.8|~3.0", diff --git a/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php b/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php index 1fac6c3f65..874dec0b87 100644 --- a/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php @@ -28,6 +28,9 @@ class FilesystemAdapter extends AbstractAdapter $directory = sys_get_temp_dir().'/symfony-cache'; } if (isset($namespace[0])) { + if (preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) { + throw new InvalidArgumentException(sprintf('FilesystemAdapter namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0])); + } $directory .= '/'.$namespace; } if (!file_exists($dir = $directory.'/.')) { diff --git a/src/Symfony/Component/Cache/Tests/Adapter/FilesystemTest.php b/src/Symfony/Component/Cache/Tests/Adapter/FilesystemTest.php index 28786501a8..15956b424b 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/FilesystemTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/FilesystemTest.php @@ -25,6 +25,6 @@ class FilesystemAdapterTest extends CachePoolTest $this->skippedTests['testDeferredSaveWithoutCommit'] = 'Fails on HHVM'; } - return new FilesystemAdapter(sys_get_temp_dir().DIRECTORY_SEPARATOR.'sf-cache'); + return new FilesystemAdapter('sf-cache'); } } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php index 246529d865..e201a56b8a 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php @@ -58,8 +58,8 @@ class PassConfig $this->removingPasses = array( new RemovePrivateAliasesPass(), - new RemoveAbstractDefinitionsPass(), new ReplaceAliasByActualDefinitionPass(), + new RemoveAbstractDefinitionsPass(), new RepeatedPass(array( new AnalyzeServiceReferencesPass(), new InlineServiceDefinitionsPass(), @@ -102,8 +102,7 @@ class PassConfig throw new InvalidArgumentException(sprintf('Invalid type "%s".', $type)); } - $passes = &$this->$property; - $passes[] = $pass; + $this->{$property}[] = $pass; } /** diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php index 2f94df971a..82e2925572 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php @@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; /** * This replaces all DefinitionDecorator instances with their equivalent fully @@ -96,11 +97,12 @@ class ResolveDefinitionTemplatesPass implements CompilerPassInterface */ private function resolveDefinition(ContainerBuilder $container, DefinitionDecorator $definition) { - if (!$container->hasDefinition($parent = $definition->getParent())) { + try { + $parentDef = $container->findDefinition($parent = $definition->getParent()); + } catch (ServiceNotFoundException $e) { throw new RuntimeException(sprintf('The parent definition "%s" defined for definition "%s" does not exist.', $parent, $this->currentId)); } - $parentDef = $container->getDefinition($parent); if ($parentDef instanceof DefinitionDecorator) { $id = $this->currentId; $this->currentId = $parent; diff --git a/src/Symfony/Component/HttpKernel/CacheClearer/Psr6CacheClearer.php b/src/Symfony/Component/HttpKernel/CacheClearer/Psr6CacheClearer.php new file mode 100644 index 0000000000..30261b3f7c --- /dev/null +++ b/src/Symfony/Component/HttpKernel/CacheClearer/Psr6CacheClearer.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\CacheClearer; + +use Psr\Cache\CacheItemPoolInterface; + +/** + * @author Nicolas Grekas + */ +class Psr6CacheClearer implements CacheClearerInterface +{ + private $pools = array(); + + public function addPool(CacheItemPoolInterface $pool) + { + $this->pools[] = $pool; + } + + /** + * {@inheritdoc} + */ + public function clear($cacheDir) + { + foreach ($this->pools as $pool) { + $pool->clear(); + } + } +}