diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php new file mode 100644 index 0000000000000..9598f537a0c62 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @author Nicolas Grekas + */ +class CachePoolPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + $attributes = array( + 'provider', + 'namespace', + 'default_lifetime', + ); + foreach ($container->findTaggedServiceIds('cache.pool') as $id => $tags) { + $adapter = $pool = $container->getDefinition($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 (isset($tags[0]['clearer'])) { + $clearer = $container->getDefinition($tags[0]['clearer']); + } else { + $clearer = null; + } + 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]); + } + if (!empty($tags[0])) { + throw new \InvalidArgumentException(sprintf('Invalid "cache.pool" tag for service "%s": accepted attributes are "clearer", "provider", "namespace" and "default_lifetime", found "%s".', $id, implode('", "', array_keys($tags[0])))); + } + + if (null !== $clearer) { + $clearer->addMethodCall('addPool', array(new Reference($id))); + } + } + } + + private function getNamespace($id) + { + return substr(str_replace('/', '-', base64_encode(md5('symfony.'.$id, true))), 0, 10); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 69c310a9204a0..e298b2609ce4d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -114,6 +114,7 @@ public function getConfigTreeBuilder() $this->addSerializerSection($rootNode); $this->addPropertyAccessSection($rootNode); $this->addPropertyInfoSection($rootNode); + $this->addCacheSection($rootNode); return $treeBuilder; } @@ -547,4 +548,39 @@ private function addPropertyInfoSection(ArrayNodeDefinition $rootNode) ->end() ; } + + private function addCacheSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('cache') + ->info('Cache configuration') + ->addDefaultsIfNotSet() + ->fixXmlConfig('pool') + ->children() + ->arrayNode('pools') + ->useAttributeAsKey('name') + ->prototype('array') + ->children() + ->scalarNode('adapter') + ->info('The cache pool adapter service to use as template definition.') + ->defaultValue('cache.adapter.shared') + ->end() + ->booleanNode('public')->defaultFalse()->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() + ->end() + ->end() + ->end() + ; + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 22b8dde6b3d56..014f447ba103a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -122,6 +122,7 @@ public function load(array $configs, ContainerBuilder $container) $this->registerFragmentsConfiguration($config['fragments'], $container, $loader); $this->registerTranslatorConfiguration($config['translator'], $container); $this->registerProfilerConfiguration($config['profiler'], $container, $loader); + $this->registerCacheConfiguration($config['cache'], $container, $loader); if ($this->isConfigEnabled($container, $config['router'])) { $this->registerRouterConfiguration($config['router'], $container, $loader); @@ -1016,6 +1017,28 @@ private function registerPropertyInfoConfiguration(array $config, ContainerBuild } } + private function registerCacheConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + $loader->load('cache_pools.xml'); + + foreach ($config['pools'] as $name => $poolConfig) { + $poolDefinition = new DefinitionDecorator($poolConfig['adapter']); + $poolDefinition->setPublic($poolConfig['public']); + unset($poolConfig['adapter'], $poolConfig['public']); + + $poolDefinition->addTag('cache.pool', $poolConfig); + $container->setDefinition('cache.pool.'.$name, $poolDefinition); + } + + $this->addClassesToCompile(array( + 'Psr\Cache\CacheItemInterface', + 'Psr\Cache\CacheItemPoolInterface', + 'Symfony\Component\Cache\Adapter\AdapterInterface', + 'Symfony\Component\Cache\Adapter\AbstractAdapter', + 'Symfony\Component\Cache\CacheItem', + )); + } + /** * Gets a hash of the kernel root directory. * diff --git a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php index 98002fb6b6a5d..7ed44c5bb84d2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php +++ b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php @@ -14,6 +14,7 @@ use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddValidatorInitializersPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConsoleCommandPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ControllerArgumentValueResolverPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass; @@ -89,6 +90,7 @@ public function build(ContainerBuilder $container) $container->addCompilerPass(new SerializerPass()); $container->addCompilerPass(new PropertyInfoPass()); $container->addCompilerPass(new ControllerArgumentValueResolverPass()); + $container->addCompilerPass(new CachePoolPass()); if ($container->getParameter('kernel.debug')) { $container->addCompilerPass(new UnusedTagsPass(), PassConfig::TYPE_AFTER_REMOVING); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache_pools.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache_pools.xml new file mode 100644 index 0000000000000..2874f41bf9d26 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache_pools.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %kernel.cache_dir%/pools + + + + + + + + + + + + + + + + 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 cead2295ed1ac..aa2c057d356ec 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 @@ -25,6 +25,7 @@ + @@ -202,4 +203,19 @@ + + + + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php new file mode 100644 index 0000000000000..3fc66cff09084 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\Reference; + +class CachePoolPassTest extends \PHPUnit_Framework_TestCase +{ + private $cachePoolPass; + + protected function setUp() + { + $this->cachePoolPass = new CachePoolPass(); + } + + public function testNamespaceArgumentIsReplaced() + { + $container = new ContainerBuilder(); + $adapter = new Definition(); + $adapter->setAbstract(true); + $adapter->addTag('cache.pool'); + $container->setDefinition('app.cache_adapter', $adapter); + $container->setAlias('app.cache_adapter_alias', 'app.cache_adapter'); + $cachePool = new DefinitionDecorator('app.cache_adapter_alias'); + $cachePool->addArgument(null); + $cachePool->addTag('cache.pool'); + $container->setDefinition('app.cache_pool', $cachePool); + + $this->cachePoolPass->process($container); + + $this->assertSame('yRnzIIVLvL', $cachePool->getArgument(0)); + } + + public function testArgsAreReplaced() + { + $container = new ContainerBuilder(); + $cachePool = new Definition(); + $cachePool->addTag('cache.pool', array( + 'provider' => 'foobar', + 'default_lifetime' => 3, + )); + $cachePool->addArgument(null); + $cachePool->addArgument(null); + $cachePool->addArgument(null); + $container->setDefinition('app.cache_pool', $cachePool); + + $this->cachePoolPass->process($container); + + $this->assertInstanceOf(Reference::class, $cachePool->getArgument(0)); + $this->assertSame('foobar', (string) $cachePool->getArgument(0)); + $this->assertSame('yRnzIIVLvL', $cachePool->getArgument(1)); + $this->assertSame(3, $cachePool->getArgument(2)); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid "cache.pool" tag for service "app.cache_pool": accepted attributes are + */ + public function testThrowsExceptionWhenCachePoolTagHasUnknownAttributes() + { + $container = new ContainerBuilder(); + $adapter = new Definition(); + $adapter->setAbstract(true); + $adapter->addTag('cache.pool'); + $container->setDefinition('app.cache_adapter', $adapter); + $cachePool = new DefinitionDecorator('app.cache_adapter'); + $cachePool->addTag('cache.pool', array('foobar' => 123)); + $container->setDefinition('app.cache_pool', $cachePool); + + $this->cachePoolPass->process($container); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index 88be0616033ff..9bcadc68102c0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -265,6 +265,9 @@ protected static function getBundleDefaultConfig() 'base_urls' => array(), 'packages' => array(), ), + 'cache' => array( + 'pools' => array(), + ), ); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php new file mode 100644 index 0000000000000..7cf634b92e394 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php @@ -0,0 +1,29 @@ +loadFromExtension('framework', array( + 'cache' => array( + 'pools' => array( + 'foo' => array( + 'adapter' => 'cache.adapter.apcu', + 'default_lifetime' => 30, + ), + 'bar' => array( + 'adapter' => 'cache.adapter.doctrine', + 'default_lifetime' => 5, + 'provider' => 'app.doctrine_cache_provider', + ), + 'baz' => array( + 'adapter' => 'cache.adapter.filesystem', + 'default_lifetime' => 7, + ), + 'foobar' => array( + 'adapter' => 'cache.adapter.psr6', + 'default_lifetime' => 10, + '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 new file mode 100644 index 0000000000000..d6f472716ff89 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml new file mode 100644 index 0000000000000..395009f18ad18 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml @@ -0,0 +1,19 @@ +framework: + cache: + pools: + foo: + adapter: cache.adapter.apcu + default_lifetime: 30 + bar: + adapter: cache.adapter.doctrine + default_lifetime: 5 + provider: app.doctrine_cache_provider + baz: + adapter: cache.adapter.filesystem + default_lifetime: 7 + foobar: + adapter: cache.adapter.psr6 + default_lifetime: 10 + 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 55d9a16e77e5c..000ec8e6ff385 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -13,6 +13,9 @@ use Symfony\Bundle\FrameworkBundle\Tests\TestCase; use Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension; +use Symfony\Component\Cache\Adapter\ApcuAdapter; +use Symfony\Component\Cache\Adapter\DoctrineAdapter; +use Symfony\Component\Cache\Adapter\FilesystemAdapter; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\Loader\ClosureLoader; @@ -568,6 +571,17 @@ public function testPropertyInfoEnabled() $this->assertTrue($container->has('property_info')); } + public function testCachePoolServices() + { + $container = $this->createContainerFromFile('cache'); + + $this->assertCachePoolServiceDefinitionIsCreated($container, 'foo', 'cache.adapter.apcu', 30); + $this->assertCachePoolServiceDefinitionIsCreated($container, 'bar', 'cache.adapter.doctrine', 5); + $this->assertCachePoolServiceDefinitionIsCreated($container, 'baz', 'cache.adapter.filesystem', 7); + $this->assertCachePoolServiceDefinitionIsCreated($container, 'foobar', 'cache.adapter.psr6', 10); + $this->assertCachePoolServiceDefinitionIsCreated($container, 'def', 'cache.adapter.filesystem', 11); + } + protected function createContainer(array $data = array()) { return new ContainerBuilder(new ParameterBag(array_merge(array( @@ -636,4 +650,39 @@ private function assertVersionStrategy(ContainerBuilder $container, Reference $r $this->assertEquals($format, $versionStrategy->getArgument(1)); } } + + private function assertCachePoolServiceDefinitionIsCreated(ContainerBuilder $container, $name, $adapter, $defaultLifetime) + { + $id = 'cache.pool.'.$name; + + $this->assertTrue($container->has($id), sprintf('Service definition "%s" for cache pool of type "%s" is registered', $id, $adapter)); + + $poolDefinition = $container->getDefinition($id); + + $this->assertInstanceOf(DefinitionDecorator::class, $poolDefinition, sprintf('Cache pool "%s" is based on an abstract cache pool.', $name)); + + $this->assertTrue($poolDefinition->hasTag('cache.pool'), sprintf('Service definition "%s" is tagged with the "cache.pool" tag.', $id)); + $this->assertFalse($poolDefinition->isAbstract(), sprintf('Service definition "%s" is not abstract.', $id)); + + $tag = $poolDefinition->getTag('cache.pool'); + $this->assertTrue(isset($tag[0]['default_lifetime']), 'The default lifetime is stored as an attribute of the "cache.pool" tag.'); + $this->assertSame($defaultLifetime, $tag[0]['default_lifetime'], 'The default lifetime is stored as an attribute of the "cache.pool" tag.'); + + $adapterId = $poolDefinition->getParent(); + $adapterDefinition = $container->findDefinition($adapterId); + + switch ($adapter) { + case 'cache.adapter.apcu': + $this->assertSame(ApcuAdapter::class, $adapterDefinition->getClass()); + break; + case 'cache.adapter.doctrine': + $this->assertSame(DoctrineAdapter::class, $adapterDefinition->getClass()); + break; + case 'cache.adapter.filesystem': + $this->assertSame(FilesystemAdapter::class, $adapterDefinition->getClass()); + break; + } + + $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 0000000000000..b35eebdb317f2 --- /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 0000000000000..a73987bcc986a --- /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 0000000000000..25aff9cbcbe15 --- /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 0000000000000..fb2510b6fa0d2 --- /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.shared: + 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 689aa7d6b65f9..8016bc3ea47c1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -18,8 +18,9 @@ "require": { "php": ">=5.5.9", "symfony/asset": "~2.8|~3.0", + "symfony/cache": "~3.1", "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", diff --git a/src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php b/src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php index 26dd8e3a86d85..855ae290500ae 100644 --- a/src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php @@ -20,7 +20,7 @@ class DoctrineAdapter extends AbstractAdapter { private $provider; - public function __construct(CacheProvider $provider, $defaultLifetime = 0, $namespace = '') + public function __construct(CacheProvider $provider, $namespace = '', $defaultLifetime = 0) { parent::__construct('', $defaultLifetime); $this->provider = $provider; diff --git a/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php b/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php index 22708584d8c3a..874dec0b87931 100644 --- a/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php @@ -20,7 +20,7 @@ class FilesystemAdapter extends AbstractAdapter { private $directory; - public function __construct($directory, $defaultLifetime = 0, $namespace = '') + public function __construct($namespace = '', $defaultLifetime = 0, $directory = null) { parent::__construct('', $defaultLifetime); @@ -28,6 +28,9 @@ public function __construct($directory, $defaultLifetime = 0, $namespace = '') $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/Adapter/ProxyAdapter.php b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php index 83c3f1baea055..e8befafac91f5 100644 --- a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php @@ -28,7 +28,7 @@ class ProxyAdapter implements AdapterInterface private $hits = 0; private $misses = 0; - public function __construct(CacheItemPoolInterface $pool, $defaultLifetime = 0, $namespace = '') + public function __construct(CacheItemPoolInterface $pool, $namespace = '', $defaultLifetime = 0) { $this->pool = $pool; $this->namespace = $this->getId($namespace, true); diff --git a/src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php index 230a4500f8702..64fb83ce45adb 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php @@ -28,7 +28,7 @@ public function createCachePool() $this->skippedTests['testDeferredSaveWithoutCommit'] = 'Fails on HHVM'; } - return new ChainAdapter(array(new ArrayAdapter(), new ExternalAdapter(), new FilesystemAdapter(null))); + return new ChainAdapter(array(new ArrayAdapter(), new ExternalAdapter(), new FilesystemAdapter())); } /** diff --git a/src/Symfony/Component/Cache/Tests/Adapter/FilesystemTest.php b/src/Symfony/Component/Cache/Tests/Adapter/FilesystemTest.php index 28786501a86ec..15956b424b3b6 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/FilesystemTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/FilesystemTest.php @@ -25,6 +25,6 @@ public function createCachePool() $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/Cache/Tests/Adapter/NamespacedProxyAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/NamespacedProxyAdapterTest.php index 28898e780ed6f..9b82d91a1af7e 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/NamespacedProxyAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/NamespacedProxyAdapterTest.php @@ -21,6 +21,6 @@ class NamespacedProxyAdapterTest extends ProxyAdapterTest { public function createCachePool() { - return new ProxyAdapter(new ArrayAdapter(), 0, 'foo'); + return new ProxyAdapter(new ArrayAdapter(), 'foo'); } } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterTest.php index a41514f37d4c4..fe3eaead39de3 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterTest.php @@ -37,7 +37,6 @@ public static function setupBeforeClass() $e = error_get_last(); self::markTestSkipped($e['message']); } - self::$redis->select(1993); } public static function tearDownAfterClass() diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php index 246529d865cd3..e201a56b8a93c 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php @@ -58,8 +58,8 @@ public function __construct() $this->removingPasses = array( new RemovePrivateAliasesPass(), - new RemoveAbstractDefinitionsPass(), new ReplaceAliasByActualDefinitionPass(), + new RemoveAbstractDefinitionsPass(), new RepeatedPass(array( new AnalyzeServiceReferencesPass(), new InlineServiceDefinitionsPass(), @@ -102,8 +102,7 @@ public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_O 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 2f94df971a1e0..4451e7c435f1a 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php @@ -96,11 +96,11 @@ private function resolveArguments(ContainerBuilder $container, array $arguments, */ private function resolveDefinition(ContainerBuilder $container, DefinitionDecorator $definition) { - if (!$container->hasDefinition($parent = $definition->getParent())) { + if (!$container->has($parent = $definition->getParent())) { throw new RuntimeException(sprintf('The parent definition "%s" defined for definition "%s" does not exist.', $parent, $this->currentId)); } - $parentDef = $container->getDefinition($parent); + $parentDef = $container->findDefinition($parent); if ($parentDef instanceof DefinitionDecorator) { $id = $this->currentId; $this->currentId = $parent; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php index adb7ba3bdc2b9..31663713d3ba6 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php @@ -309,6 +309,20 @@ public function testProcessMergeAutowiringTypes() $this->assertEquals(array('Foo', 'Bar'), $def->getAutowiringTypes()); } + public function testProcessResolvesAliases() + { + $container = new ContainerBuilder(); + + $container->register('parent', 'ParentClass'); + $container->setAlias('parent_alias', 'parent'); + $container->setDefinition('child', new DefinitionDecorator('parent_alias')); + + $this->process($container); + + $def = $container->getDefinition('child'); + $this->assertSame('ParentClass', $def->getClass()); + } + protected function process(ContainerBuilder $container) { $pass = new ResolveDefinitionTemplatesPass(); diff --git a/src/Symfony/Component/HttpKernel/CacheClearer/Psr6CacheClearer.php b/src/Symfony/Component/HttpKernel/CacheClearer/Psr6CacheClearer.php new file mode 100644 index 0000000000000..30261b3f7c660 --- /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(); + } + } +}