Browse Source

[DependencyInjection] added a way to retrieve loaded resources

Fabien Potencier 15 years ago
parent
commit
53980bb55e

+ 26 - 0
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);
+    }
   }
 
   /**

+ 66 - 0
src/Symfony/Components/DependencyInjection/FileResource.php

@@ -0,0 +1,66 @@
+<?php
+
+namespace Symfony\Components\DependencyInjection;
+
+/*
+ * This file is part of the symfony framework.
+ *
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * 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 <fabien.potencier@symfony-project.com>
+ */
+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;
+  }
+}

+ 11 - 0
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))

+ 4 - 5
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)

+ 9 - 11
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');
   }

+ 7 - 9
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)

+ 38 - 0
src/Symfony/Components/DependencyInjection/ResourceInterface.php

@@ -0,0 +1,38 @@
+<?php
+
+namespace Symfony\Components\DependencyInjection;
+
+/*
+ * This file is part of the symfony framework.
+ *
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * 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 <fabien.potencier@symfony-project.com>
+ */
+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();
+}

+ 16 - 1
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');

+ 33 - 0
tests/unit/Symfony/Components/DependencyInjection/FileResourceTest.php

@@ -0,0 +1,33 @@
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ * 
+ * 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');

+ 16 - 13
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');