From 53980bb55e9f2b5c321d24e40f8491b72708a154 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 17 Jan 2010 11:21:31 +0100 Subject: [PATCH] [DependencyInjection] added a way to retrieve loaded resources --- .../BuilderConfiguration.php | 26 ++++++++ .../DependencyInjection/FileResource.php | 66 +++++++++++++++++++ .../DependencyInjection/Loader/FileLoader.php | 11 ++++ .../Loader/IniFileLoader.php | 9 ++- .../Loader/XmlFileLoader.php | 20 +++--- .../Loader/YamlFileLoader.php | 16 ++--- .../DependencyInjection/ResourceInterface.php | 38 +++++++++++ .../BuilderConfigurationTest.php | 17 ++++- .../DependencyInjection/FileResourceTest.php | 33 ++++++++++ .../Loader/XmlFileLoaderTest.php | 29 ++++---- 10 files changed, 226 insertions(+), 39 deletions(-) create mode 100644 src/Symfony/Components/DependencyInjection/FileResource.php create mode 100644 src/Symfony/Components/DependencyInjection/ResourceInterface.php create mode 100644 tests/unit/Symfony/Components/DependencyInjection/FileResourceTest.php diff --git a/src/Symfony/Components/DependencyInjection/BuilderConfiguration.php b/src/Symfony/Components/DependencyInjection/BuilderConfiguration.php index e74b445f88..c345be2ec9 100644 --- a/src/Symfony/Components/DependencyInjection/BuilderConfiguration.php +++ b/src/Symfony/Components/DependencyInjection/BuilderConfiguration.php @@ -23,6 +23,7 @@ class BuilderConfiguration protected $definitions = array(); protected $parameters = array(); protected $aliases = array(); + protected $resources = array(); public function __construct(array $definitions = array(), array $parameters = array()) { @@ -30,6 +31,26 @@ class BuilderConfiguration $this->setParameters($parameters); } + /** + * Returns an array of resources loaded to build this configuration. + * + * @return array An array of resources + */ + public function getResources() + { + return $this->resources; + } + + /** + * Adds a resource for this configuration. + * + * @param ResourceInterface $resource A resource instance + */ + public function addResource(ResourceInterface $resource) + { + $this->resources[] = $resource; + } + /** * Merges a BuilderConfiguration with the current one. * @@ -45,6 +66,11 @@ class BuilderConfiguration $this->addDefinitions($configuration->getDefinitions()); $this->addAliases($configuration->getAliases()); $this->addParameters($configuration->getParameters()); + + foreach ($configuration->getResources() as $resource) + { + $this->addResource($resource); + } } /** diff --git a/src/Symfony/Components/DependencyInjection/FileResource.php b/src/Symfony/Components/DependencyInjection/FileResource.php new file mode 100644 index 0000000000..13ccf26d2f --- /dev/null +++ b/src/Symfony/Components/DependencyInjection/FileResource.php @@ -0,0 +1,66 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * FileResource represents a resource stored on the filesystem. + * + * @package symfony + * @subpackage dependency_injection + * @author Fabien Potencier + */ +class FileResource implements ResourceInterface +{ + protected $resource; + + /** + * Constructor. + * + * @param string $resource The file path to the resource + */ + public function __construct($resource) + { + $this->resource = $resource; + } + + /** + * Returns the resource tied to this Resource. + * + * @return mixed The resource + */ + public function getResource() + { + return $this->resource; + } + + /** + * Returns true if the resource has not been updated since the given timestamp. + * + * @param timestamp $timestamp The last time the resource was loaded + * + * @return Boolean true if the resource has not been updated, false otherwise + */ + public function isUptodate($timestamp = null) + { + if (!file_exists($this->resource)) + { + return false; + } + + if (null === $timestamp) + { + $timestamp = time(); + } + + return filemtime($this->resource) < $timestamp; + } +} diff --git a/src/Symfony/Components/DependencyInjection/Loader/FileLoader.php b/src/Symfony/Components/DependencyInjection/Loader/FileLoader.php index 0ba57263ef..cc2ac126fc 100644 --- a/src/Symfony/Components/DependencyInjection/Loader/FileLoader.php +++ b/src/Symfony/Components/DependencyInjection/Loader/FileLoader.php @@ -37,6 +37,17 @@ abstract class FileLoader extends Loader $this->paths = $paths; } + protected function findFile($file) + { + $path = $this->getAbsolutePath($file); + if (!file_exists($path)) + { + throw new \InvalidArgumentException(sprintf('The file "%s" does not exist (in: %s).', $file, implode(', ', $this->paths))); + } + + return realpath($path); + } + protected function getAbsolutePath($file, $currentPath = null) { if (self::isAbsolutePath($file)) diff --git a/src/Symfony/Components/DependencyInjection/Loader/IniFileLoader.php b/src/Symfony/Components/DependencyInjection/Loader/IniFileLoader.php index e4e1d5e383..6df27729fb 100644 --- a/src/Symfony/Components/DependencyInjection/Loader/IniFileLoader.php +++ b/src/Symfony/Components/DependencyInjection/Loader/IniFileLoader.php @@ -3,6 +3,7 @@ namespace Symfony\Components\DependencyInjection\Loader; use Symfony\Components\DependencyInjection\BuilderConfiguration; +use Symfony\Components\DependencyInjection\FileResource; /* * This file is part of the symfony framework. @@ -31,13 +32,11 @@ class IniFileLoader extends FileLoader */ public function load($file) { + $path = $this->findFile($file); + $configuration = new BuilderConfiguration(); - $path = $this->getAbsolutePath($file); - if (!file_exists($path)) - { - throw new \InvalidArgumentException(sprintf('The %s file does not exist.', $file)); - } + $configuration->addResource(new FileResource($path)); $result = parse_ini_file($path, true); if (false === $result || array() === $result) diff --git a/src/Symfony/Components/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Components/DependencyInjection/Loader/XmlFileLoader.php index e4ea85e899..c1dea4d6c5 100644 --- a/src/Symfony/Components/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Components/DependencyInjection/Loader/XmlFileLoader.php @@ -6,6 +6,7 @@ use Symfony\Components\DependencyInjection\Definition; use Symfony\Components\DependencyInjection\Reference; use Symfony\Components\DependencyInjection\BuilderConfiguration; use Symfony\Components\DependencyInjection\SimpleXMLElement; +use Symfony\Components\DependencyInjection\FileResource; /* * This file is part of the symfony framework. @@ -34,9 +35,13 @@ class XmlFileLoader extends FileLoader */ public function load($file) { + $path = $this->findFile($file); + + $xml = $this->parseFile($path); + $configuration = new BuilderConfiguration(); - $xml = $this->loadFile($file); + $configuration->addResource(new FileResource($path)); // anonymous services $xml = $this->processAnonymousServices($configuration, $xml, $file); @@ -165,25 +170,18 @@ class XmlFileLoader extends FileLoader $configuration->setDefinition($id, $definition); } - protected function loadFile($file) + protected function parseFile($file) { - $path = $this->getAbsolutePath($file); - - if (!file_exists($path)) - { - throw new \InvalidArgumentException(sprintf('The service file "%s" does not exist (in: %s).', $file, implode(', ', $this->paths))); - } - $dom = new \DOMDocument(); libxml_use_internal_errors(true); - if (!$dom->load(realpath($path), LIBXML_COMPACT)) + if (!$dom->load($file, LIBXML_COMPACT)) { throw new \InvalidArgumentException(implode("\n", $this->getXmlErrors())); } $dom->validateOnParse = true; $dom->normalizeDocument(); libxml_use_internal_errors(false); - $this->validate($dom, $path); + $this->validate($dom, $file); return simplexml_import_dom($dom, 'Symfony\\Components\\DependencyInjection\\SimpleXMLElement'); } diff --git a/src/Symfony/Components/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Components/DependencyInjection/Loader/YamlFileLoader.php index 8dbafff51a..221503f6f4 100644 --- a/src/Symfony/Components/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Components/DependencyInjection/Loader/YamlFileLoader.php @@ -6,6 +6,7 @@ use Symfony\Components\DependencyInjection\Container; use Symfony\Components\DependencyInjection\Definition; use Symfony\Components\DependencyInjection\Reference; use Symfony\Components\DependencyInjection\BuilderConfiguration; +use Symfony\Components\DependencyInjection\FileResource; use Symfony\Components\YAML\YAML; /* @@ -37,10 +38,14 @@ class YamlFileLoader extends FileLoader */ public function load($file) { - $content = $this->loadFile($file); + $path = $this->findFile($file); + + $content = $this->loadFile($path); $configuration = new BuilderConfiguration(); + $configuration->addResource(new FileResource($path)); + if (!$content) { return $configuration; @@ -166,14 +171,7 @@ class YamlFileLoader extends FileLoader protected function loadFile($file) { - $path = $this->getAbsolutePath($file); - - if (!file_exists($path)) - { - throw new \InvalidArgumentException(sprintf('The service file "%s" does not exist (in: %s).', $file, implode(', ', $this->paths))); - } - - return $this->validate(YAML::load($path), $path); + return $this->validate(YAML::load($file), $file); } protected function validate($content, $file) diff --git a/src/Symfony/Components/DependencyInjection/ResourceInterface.php b/src/Symfony/Components/DependencyInjection/ResourceInterface.php new file mode 100644 index 0000000000..010541e6f9 --- /dev/null +++ b/src/Symfony/Components/DependencyInjection/ResourceInterface.php @@ -0,0 +1,38 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * ResourceInterface is the interface that must be implemented by all Resource classes. + * + * @package symfony + * @subpackage dependency_injection + * @author Fabien Potencier + */ +interface ResourceInterface +{ + /** + * Returns true if the resource has not been updated since the given timestamp. + * + * @param timestamp $timestamp The last time the resource was loaded + * + * @return Boolean true if the resource has not been updated, false otherwise + */ + function isUptodate($timestamp = null); + + /** + * Returns the resource tied to this Resource. + * + * @return mixed The resource + */ + function getResource(); +} diff --git a/tests/unit/Symfony/Components/DependencyInjection/BuilderConfigurationTest.php b/tests/unit/Symfony/Components/DependencyInjection/BuilderConfigurationTest.php index 1fdc4fd039..414b7cdbfd 100644 --- a/tests/unit/Symfony/Components/DependencyInjection/BuilderConfigurationTest.php +++ b/tests/unit/Symfony/Components/DependencyInjection/BuilderConfigurationTest.php @@ -14,10 +14,11 @@ use Symfony\Components\DependencyInjection\Builder; use Symfony\Components\DependencyInjection\BuilderConfiguration; use Symfony\Components\DependencyInjection\Definition; use Symfony\Components\DependencyInjection\Reference; +use Symfony\Components\DependencyInjection\FileResource; $fixturesPath = __DIR__.'/../../../../fixtures/Symfony/Components/DependencyInjection/'; -$t = new LimeTest(37); +$t = new LimeTest(39); // __construct() $t->diag('__construct()'); @@ -62,6 +63,13 @@ $config->setDefinition('foo', new Definition('BazClass')); $configuration->merge($config); $t->is($configuration->getDefinition('foo')->getClass(), 'BazClass', '->merge() overrides already defined services'); +$configuration = new BuilderConfiguration(); +$configuration->addResource($a = new FileResource('foo.xml')); +$config = new BuilderConfiguration(); +$config->addResource($b = new FileResource('foo.yml')); +$configuration->merge($config); +$t->is($configuration->getResources(), array($a, $b), '->merge() merges resources'); + // ->setParameters() ->getParameters() $t->diag('->setParameters() ->getParameters()'); @@ -175,3 +183,10 @@ $configuration = new BuilderConfiguration(array('foo' => $definition = new Defin $configuration->setAlias('bar', 'foo'); $configuration->setAlias('foobar', 'bar'); $t->is($configuration->findDefinition('foobar'), $definition, '->findDefinition() returns a Definition'); + +// ->addResource() ->getResources() +$t->diag('->addResource() ->getResources()'); +$configuration = new BuilderConfiguration(); +$configuration->addResource($a = new FileResource('foo.xml')); +$configuration->addResource($b = new FileResource('foo.yml')); +$t->is($configuration->getResources(), array($a, $b), '->getResources() returns an array of resources read for the current configuration'); diff --git a/tests/unit/Symfony/Components/DependencyInjection/FileResourceTest.php b/tests/unit/Symfony/Components/DependencyInjection/FileResourceTest.php new file mode 100644 index 0000000000..50382e4024 --- /dev/null +++ b/tests/unit/Symfony/Components/DependencyInjection/FileResourceTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require_once __DIR__.'/../../../bootstrap.php'; + +use Symfony\Components\DependencyInjection\FileResource; + +$t = new LimeTest(5); + +// ->getResource() +$t->diag('->getResource()'); +$file = sys_get_temp_dir().'/tmp.xml'; +touch($file); +$resource = new FileResource($file); +$t->is($resource->getResource(), $file, '->getResource() returns the path to the resource'); + +// ->isUptodate() +$t->diag('->isUptodate()'); +sleep(1); +$t->ok($resource->isUptodate(), '->isUptodate() returns true if the resource has not changed'); +$t->ok($resource->isUptodate(time() + 10), '->isUptodate() returns true if the resource has not changed'); +$t->ok(!$resource->isUptodate(time() - 86400), '->isUptodate() returns false if the resource has been updated'); +unlink($file); + +$resource = new FileResource('/____foo/foobar'.rand(1, 999999)); +$t->ok(!$resource->isUptodate(), '->isUptodate() returns false if the resource does not exist'); diff --git a/tests/unit/Symfony/Components/DependencyInjection/Loader/XmlFileLoaderTest.php b/tests/unit/Symfony/Components/DependencyInjection/Loader/XmlFileLoaderTest.php index 364b56467e..07bcbb0483 100644 --- a/tests/unit/Symfony/Components/DependencyInjection/Loader/XmlFileLoaderTest.php +++ b/tests/unit/Symfony/Components/DependencyInjection/Loader/XmlFileLoaderTest.php @@ -24,20 +24,20 @@ require_once $fixturesPath.'/includes/ProjectExtension.php'; class ProjectLoader extends XmlFileLoader { - public function loadFile($file) + public function parseFile($file) { - return parent::loadFile($file); + return parent::parseFile($file); } } -// ->loadFile() -$t->diag('->loadFile()'); +// ->load() +$t->diag('->load()'); $loader = new ProjectLoader($fixturesPath.'/ini'); try { - $loader->loadFile('foo.xml'); + $loader->load('foo.xml'); $t->fail('->load() throws an InvalidArgumentException if the loaded file does not exist'); } catch (InvalidArgumentException $e) @@ -45,30 +45,33 @@ catch (InvalidArgumentException $e) $t->pass('->load() throws an InvalidArgumentException if the loaded file does not exist'); } +// ->parseFile() +$t->diag('->parseFile()'); + try { - $loader->loadFile('parameters.ini'); - $t->fail('->load() throws an InvalidArgumentException if the loaded file is not a valid XML file'); + $loader->parseFile($fixturesPath.'/ini/parameters.ini'); + $t->fail('->parseFile() throws an InvalidArgumentException if the loaded file is not a valid XML file'); } catch (InvalidArgumentException $e) { - $t->pass('->load() throws an InvalidArgumentException if the loaded file is not a valid XML file'); + $t->pass('->parseFile() throws an InvalidArgumentException if the loaded file is not a valid XML file'); } $loader = new ProjectLoader($fixturesPath.'/xml'); try { - $loader->loadFile('nonvalid.xml'); - $t->fail('->load() throws an InvalidArgumentException if the loaded file does not validate the XSD'); + $loader->parseFile($fixturesPath.'/xml/nonvalid.xml'); + $t->fail('->parseFile() throws an InvalidArgumentException if the loaded file does not validate the XSD'); } catch (InvalidArgumentException $e) { - $t->pass('->load() throws an InvalidArgumentException if the loaded file does not validate the XSD'); + $t->pass('->parseFile() throws an InvalidArgumentException if the loaded file does not validate the XSD'); } -$xml = $loader->loadFile('services1.xml'); -$t->is(get_class($xml), 'Symfony\\Components\\DependencyInjection\\SimpleXMLElement', '->loadFile() returns an SimpleXMLElement object'); +$xml = $loader->parseFile($fixturesPath.'/xml/services1.xml'); +$t->is(get_class($xml), 'Symfony\\Components\\DependencyInjection\\SimpleXMLElement', '->parseFile() returns an SimpleXMLElement object'); // ->load() # parameters $t->diag('->load() # parameters');