From 7d9f0191e52cf2259bfa062ad64f4aae83e5b614 Mon Sep 17 00:00:00 2001 From: Charles-Henri Bruyand Date: Fri, 29 May 2015 12:43:45 +0200 Subject: [PATCH] [TwigBundle] Reconfigure twig paths when they are updated Directory's content and modification time are not considered any more for freshness check. Only it's creation or deletion are now checked. This addresses performance issues (#14771, #14768), and also improves the cases were container is reconfigured as directory creation needed clearing the cache manually (#14262). --- .../DependencyInjection/TwigExtension.php | 19 +++-- .../Config/Resource/FileExistenceResource.php | 78 +++++++++++++++++++ .../Resource/FileExistenceResourceTest.php | 70 +++++++++++++++++ 3 files changed, 159 insertions(+), 8 deletions(-) create mode 100644 src/Symfony/Component/Config/Resource/FileExistenceResource.php create mode 100644 src/Symfony/Component/Config/Tests/Resource/FileExistenceResourceTest.php diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php index a8c89f3b9db2c..78eb5374e4846 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php @@ -12,7 +12,7 @@ namespace Symfony\Bundle\TwigBundle\DependencyInjection; use Symfony\Component\Config\FileLocator; -use Symfony\Component\Config\Resource\DirectoryResource; +use Symfony\Component\Config\Resource\FileExistenceResource; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; @@ -67,27 +67,30 @@ public function load(array $configs, ContainerBuilder $container) } else { $twigFilesystemLoaderDefinition->addMethodCall('addPath', array($path, $namespace)); } - $container->addResource(new DirectoryResource($path)); + $container->addResource(new FileExistenceResource($path)); } // register bundles as Twig namespaces foreach ($container->getParameter('kernel.bundles') as $bundle => $class) { - if (is_dir($dir = $container->getParameter('kernel.root_dir').'/Resources/'.$bundle.'/views')) { + $dir = $container->getParameter('kernel.root_dir').'/Resources/'.$bundle.'/views'; + if (is_dir($dir)) { $this->addTwigPath($twigFilesystemLoaderDefinition, $dir, $bundle); - $container->addResource(new DirectoryResource($dir)); } + $container->addResource(new FileExistenceResource($dir)); $reflection = new \ReflectionClass($class); - if (is_dir($dir = dirname($reflection->getFilename()).'/Resources/views')) { + $dir = dirname($reflection->getFilename()).'/Resources/views'; + if (is_dir($dir)) { $this->addTwigPath($twigFilesystemLoaderDefinition, $dir, $bundle); - $container->addResource(new DirectoryResource($dir)); } + $container->addResource(new FileExistenceResource($dir)); } - if (is_dir($dir = $container->getParameter('kernel.root_dir').'/Resources/views')) { + $dir = $container->getParameter('kernel.root_dir').'/Resources/views'; + if (is_dir($dir)) { $twigFilesystemLoaderDefinition->addMethodCall('addPath', array($dir)); - $container->addResource(new DirectoryResource($dir)); } + $container->addResource(new FileExistenceResource($dir)); if (!empty($config['globals'])) { $def = $container->getDefinition('twig'); diff --git a/src/Symfony/Component/Config/Resource/FileExistenceResource.php b/src/Symfony/Component/Config/Resource/FileExistenceResource.php new file mode 100644 index 0000000000000..8a574acc35ddf --- /dev/null +++ b/src/Symfony/Component/Config/Resource/FileExistenceResource.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * FileExistenceResource represents a resource stored on the filesystem. + * Freshness is only evaluated against resource creation or deletion. + * + * The resource can be a file or a directory. + * + * @author Charles-Henri Bruyand + */ +class FileExistenceResource implements ResourceInterface, \Serializable +{ + private $resource; + + private $exists; + + /** + * Constructor. + * + * @param string $resource The file path to the resource + */ + public function __construct($resource) + { + $this->resource = $resource; + $this->exists = file_exists($resource); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return (string) $this->resource; + } + + /** + * {@inheritdoc} + */ + public function getResource() + { + return $this->resource; + } + + /** + * {@inheritdoc} + */ + public function isFresh($timestamp) + { + return file_exists($this->resource) === $this->exists; + } + + /** + * {@inheritdoc} + */ + public function serialize() + { + return serialize(array($this->resource, $this->exists)); + } + + /** + * {@inheritdoc} + */ + public function unserialize($serialized) + { + list($this->resource, $this->exists) = unserialize($serialized); + } +} diff --git a/src/Symfony/Component/Config/Tests/Resource/FileExistenceResourceTest.php b/src/Symfony/Component/Config/Tests/Resource/FileExistenceResourceTest.php new file mode 100644 index 0000000000000..56ee584b1a59b --- /dev/null +++ b/src/Symfony/Component/Config/Tests/Resource/FileExistenceResourceTest.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Resource; + +use Symfony\Component\Config\Resource\FileExistenceResource; + +class FileExistenceResourceTest extends \PHPUnit_Framework_TestCase +{ + protected $resource; + protected $file; + protected $time; + + protected function setUp() + { + $this->file = realpath(sys_get_temp_dir()).'/tmp.xml'; + $this->time = time(); + $this->resource = new FileExistenceResource($this->file); + } + + protected function tearDown() + { + if (file_exists($this->file)) { + unlink($this->file); + } + } + + public function testToString() + { + $this->assertSame($this->file, (string) $this->resource); + } + + public function testGetResource() + { + $this->assertSame($this->file, $this->resource->getResource(), '->getResource() returns the path to the resource'); + } + + public function testIsFreshWithExistingResource() + { + touch($this->file, $this->time); + $serialized = serialize(new FileExistenceResource($this->file)); + + $resource = unserialize($serialized); + $this->assertTrue($resource->isFresh($this->time), '->isFresh() returns true if the resource is still present'); + + unlink($this->file); + $resource = unserialize($serialized); + $this->assertFalse($resource->isFresh($this->time), '->isFresh() returns false if the resource has been deleted'); + } + + public function testIsFreshWithAbsentResource() + { + $serialized = serialize(new FileExistenceResource($this->file)); + + $resource = unserialize($serialized); + $this->assertTrue($resource->isFresh($this->time), '->isFresh() returns true if the resource is still absent'); + + touch($this->file, $this->time); + $resource = unserialize($serialized); + $this->assertFalse($resource->isFresh($this->time), '->isFresh() returns false if the resource has been created'); + } +}