Forráskód Böngészése

[DependencyInjection] refactored loaders

 * refactored the import mechanism for better flexibility
 * added two methods to LoaderInterface: supports() and setResolver()
 * added a LoaderResolver interface
 * added a Loader base class
 * added new loaders: DelegatingLoader, PhpFileLoader, and ClosureLoader
Fabien Potencier 15 éve
szülő
commit
60c6827f23
21 módosított fájl, 775 hozzáadás és 52 törlés
  1. 46 0
      src/Symfony/Components/DependencyInjection/Loader/ClosureLoader.php
  2. 68 0
      src/Symfony/Components/DependencyInjection/Loader/DelegatingLoader.php
  3. 17 0
      src/Symfony/Components/DependencyInjection/Loader/FileLoader.php
  4. 12 0
      src/Symfony/Components/DependencyInjection/Loader/IniFileLoader.php
  5. 56 0
      src/Symfony/Components/DependencyInjection/Loader/Loader.php
  6. 23 0
      src/Symfony/Components/DependencyInjection/Loader/LoaderInterface.php
  7. 76 0
      src/Symfony/Components/DependencyInjection/Loader/LoaderResolver.php
  8. 31 0
      src/Symfony/Components/DependencyInjection/Loader/LoaderResolverInterface.php
  9. 56 0
      src/Symfony/Components/DependencyInjection/Loader/PhpFileLoader.php
  10. 14 25
      src/Symfony/Components/DependencyInjection/Loader/XmlFileLoader.php
  11. 14 25
      src/Symfony/Components/DependencyInjection/Loader/YamlFileLoader.php
  12. 3 0
      tests/Symfony/Tests/Components/DependencyInjection/Fixtures/php/simple.php
  13. 44 0
      tests/Symfony/Tests/Components/DependencyInjection/Loader/ClosureLoaderTest.php
  14. 84 0
      tests/Symfony/Tests/Components/DependencyInjection/Loader/DelegatingLoaderTest.php
  15. 5 0
      tests/Symfony/Tests/Components/DependencyInjection/Loader/FileLoaderTest.php
  16. 11 0
      tests/Symfony/Tests/Components/DependencyInjection/Loader/IniFileLoaderTest.php
  17. 55 0
      tests/Symfony/Tests/Components/DependencyInjection/Loader/LoaderResolverTest.php
  18. 76 0
      tests/Symfony/Tests/Components/DependencyInjection/Loader/LoaderTest.php
  19. 44 0
      tests/Symfony/Tests/Components/DependencyInjection/Loader/PhpFileLoaderTest.php
  20. 20 1
      tests/Symfony/Tests/Components/DependencyInjection/Loader/XmlFileLoaderTest.php
  21. 20 1
      tests/Symfony/Tests/Components/DependencyInjection/Loader/YamlFileLoaderTest.php

+ 46 - 0
src/Symfony/Components/DependencyInjection/Loader/ClosureLoader.php

@@ -0,0 +1,46 @@
+<?php
+
+namespace Symfony\Components\DependencyInjection\Loader;
+
+/*
+ * 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.
+ */
+
+/**
+ * ClosureLoader loads service definitions from a PHP closure.
+ *
+ * The Closure has access to the container as its first argument.
+ *
+ * @package    Symfony
+ * @subpackage Components_DependencyInjection
+ * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
+ */
+class ClosureLoader extends Loader
+{
+    /**
+     * Loads a Closure.
+     *
+     * @param \Closure $resource The resource
+     */
+    public function load($closure)
+    {
+        call_user_func($closure, $this->container);
+    }
+
+    /**
+     * Returns true if this class supports the given resource.
+     *
+     * @param  mixed $resource A resource
+     *
+     * @return Boolean true if this class supports the given resource, false otherwise
+     */
+    public function supports($resource)
+    {
+        return $resource instanceof \Closure;
+    }
+}

+ 68 - 0
src/Symfony/Components/DependencyInjection/Loader/DelegatingLoader.php

@@ -0,0 +1,68 @@
+<?php
+
+namespace Symfony\Components\DependencyInjection\Loader;
+
+/*
+ * 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.
+ */
+
+/**
+ * DelegatingLoader delegates loading to other loaders using a loader resolver.
+ *
+ * @package    Symfony
+ * @subpackage Components_DependencyInjection
+ * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
+ */
+class DelegatingLoader extends Loader
+{
+    protected $resolver;
+
+    /**
+     * Constructor.
+     *
+     * @param \Symfony\Components\DependencyInjection\Loader\LoaderResolverInterface $resolver A LoaderResolverInterface instance
+     */
+    public function __construct(LoaderResolverInterface $resolver)
+    {
+        $this->resolver = $resolver;
+    }
+
+    /**
+     * Loads a resource.
+     *
+     * @param mixed $resource A resource
+     */
+    public function load($resource)
+    {
+        $loader = $this->resolver->resolve($resource);
+
+        if (false === $loader) {
+            throw new \InvalidArgumentException(sprintf('Unable to load the "%s" container resource.', is_string($resource) ? $resource : (is_object($resource) ? get_class($resource) : 'RESOURCE')));
+        }
+
+        return $loader->load($resource);
+    }
+
+    /**
+     * Returns true if this class supports the given resource.
+     *
+     * @param  mixed $resource A resource
+     *
+     * @return Boolean true if this class supports the given resource, false otherwise
+     */
+    public function supports($resource)
+    {
+        foreach ($this->resolver->getLoaders() as $loader) {
+            if ($loader->supports($resource)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}

+ 17 - 0
src/Symfony/Components/DependencyInjection/Loader/FileLoader.php

@@ -22,6 +22,7 @@ use Symfony\Components\DependencyInjection\ContainerBuilder;
  */
 abstract class FileLoader extends Loader
 {
+    protected $currentDir;
     protected $paths;
 
     /**
@@ -41,6 +42,22 @@ abstract class FileLoader extends Loader
         $this->paths = $paths;
     }
 
+    /**
+     * Adds definitions and parameters from a resource.
+     *
+     * @param mixed $resource A Resource
+     */
+    public function import($resource)
+    {
+        $loader = $this->resolve($resource);
+
+        if ($loader instanceof FileLoader && null !== $this->currentDir) {
+            $resource = $this->getAbsolutePath($resource, $this->currentDir);
+        }
+
+        $loader->load($resource);
+    }
+
     /**
      * @throws \InvalidArgumentException When provided file does not exist
      */

+ 12 - 0
src/Symfony/Components/DependencyInjection/Loader/IniFileLoader.php

@@ -47,4 +47,16 @@ class IniFileLoader extends FileLoader
             }
         }
     }
+
+    /**
+     * Returns true if this class supports the given resource.
+     *
+     * @param  mixed $resource A resource
+     *
+     * @return Boolean true if this class supports the given resource, false otherwise
+     */
+    public function supports($resource)
+    {
+        return is_string($resource) && 'ini' === pathinfo($resource, PATHINFO_EXTENSION);
+    }
 }

+ 56 - 0
src/Symfony/Components/DependencyInjection/Loader/Loader.php

@@ -23,6 +23,7 @@ use Symfony\Components\DependencyInjection\ContainerBuilder;
 abstract class Loader implements LoaderInterface
 {
     protected $container;
+    protected $resolver;
 
     /**
      * Constructor.
@@ -33,4 +34,59 @@ abstract class Loader implements LoaderInterface
     {
         $this->container = $container;
     }
+
+    /**
+     * Gets the loader resolver.
+     *
+     * @return \Symfony\Components\DependencyInjection\Loader\LoaderResolver A LoaderResolver instance
+     */
+    public function getResolver()
+    {
+        return $this->resolver;
+    }
+
+    /**
+     * Sets the loader resolver.
+     *
+     * @param \Symfony\Components\DependencyInjection\Loader\LoaderResolver $resolver A LoaderResolver instance
+     */
+    public function setResolver(LoaderResolver $resolver)
+    {
+        $this->resolver = $resolver;
+    }
+
+    /**
+     * Adds definitions and parameters from a resource.
+     *
+     * @param mixed $resource A Resource
+     */
+    public function import($resource)
+    {
+        $this->resolve($resource)->load($resource);
+    }
+
+    /**
+     * Finds a loader able to load an imported resource.
+     *
+     * @param mixed $resource A Resource
+     *
+     * @return Symfony\Components\DependencyInjection\Loader\LoaderInterface A LoaderInterface instance
+     *
+     * @throws \InvalidArgumentException if no loader is found
+     */
+    public function resolve($resource)
+    {
+        $loader = false;
+        if ($this->supports($resource)) {
+            $loader = $this;
+        } elseif (null !== $this->resolver) {
+            $loader = $this->resolver->resolve($resource);
+        }
+
+        if (false === $loader) {
+            throw new \InvalidArgumentException(sprintf('Unable to load the "%s" container resource.', is_string($resource) ? $resource : (is_object($resource) ? get_class($resource) : 'RESOURCE')));
+        }
+
+        return $loader;
+    }
 }

+ 23 - 0
src/Symfony/Components/DependencyInjection/Loader/LoaderInterface.php

@@ -61,4 +61,27 @@ interface LoaderInterface
      * @param mixed $resource The resource
      */
     function load($resource);
+
+    /**
+     * Returns true if this class supports the given resource.
+     *
+     * @param  mixed $resource A resource
+     *
+     * @return Boolean true if this class supports the given resource, false otherwise
+     */
+    function supports($resource);
+
+    /**
+     * Gets the loader resolver.
+     *
+     * @return \Symfony\Components\Routing\Loader\LoaderResolver A LoaderResolver instance
+     */
+    function getResolver();
+
+    /**
+     * Sets the loader resolver.
+     *
+     * @param \Symfony\Components\Routing\Loader\LoaderResolver $resolver A LoaderResolver instance
+     */
+    function setResolver(LoaderResolver $resolver);
 }

+ 76 - 0
src/Symfony/Components/DependencyInjection/Loader/LoaderResolver.php

@@ -0,0 +1,76 @@
+<?php
+
+namespace Symfony\Components\DependencyInjection\Loader;
+
+/*
+ * 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.
+ */
+
+/**
+ * LoaderResolver selects a loader for a given resource..
+ *
+ * @package    Symfony
+ * @subpackage Components_DependencyInjection
+ * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
+ */
+class LoaderResolver implements LoaderResolverInterface
+{
+    protected $loaders;
+
+    /**
+     * Constructor.
+     *
+     * @param \Symfony\Components\DependencyInjection\Loader\LoaderInterface[] $loaders An array of loaders
+     */
+    public function __construct(array $loaders = array())
+    {
+        $this->loaders = array();
+        foreach ($loaders as $loader) {
+            $this->addLoader($loader);
+        }
+    }
+
+    /**
+     * Returns a loader able to load the resource.
+     *
+     * @param mixed  $resource A resource
+     *
+     * @return Symfony\Components\DependencyInjection\Loader\LoaderInterface A LoaderInterface instance
+     */
+    public function resolve($resource)
+    {
+        foreach ($this->loaders as $loader) {
+            if ($loader->supports($resource)) {
+                return $loader;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Sets a loader.
+     *
+     * @param \Symfony\Components\DependencyInjection\Loader\LoaderInterface $loader A LoaderInterface instance
+     */
+    public function addLoader(LoaderInterface $loader)
+    {
+        $this->loaders[] = $loader;
+        $loader->setResolver($this);
+    }
+
+    /**
+     * Returns the registered loaders.
+     *
+     * @return \Symfony\Components\DependencyInjection\Loader\LoaderInterface[] A array of LoaderInterface instances
+     */
+    public function getLoaders()
+    {
+        return $this->loaders;
+    }
+}

+ 31 - 0
src/Symfony/Components/DependencyInjection/Loader/LoaderResolverInterface.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace Symfony\Components\DependencyInjection\Loader;
+
+/*
+ * 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.
+ */
+
+/**
+ * LoaderResolverInterface selects a loader for a given resource.
+ *
+ * @package    Symfony
+ * @subpackage Components_DependencyInjection
+ * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
+ */
+interface LoaderResolverInterface
+{
+    /**
+     * Returns a loader able to load the resource.
+     *
+     * @param mixed  $resource A resource
+     *
+     * @return Symfony\Components\DependencyInjection\Loader\LoaderInterface A LoaderInterface instance
+     */
+    function resolve($resource);
+}

+ 56 - 0
src/Symfony/Components/DependencyInjection/Loader/PhpFileLoader.php

@@ -0,0 +1,56 @@
+<?php
+
+namespace Symfony\Components\DependencyInjection\Loader;
+
+use Symfony\Components\DependencyInjection\Resource\FileResource;
+
+/*
+ * 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.
+ */
+
+/**
+ * PhpFileLoader loads service definitions from a PHP file.
+ *
+ * The PHP file is required and the $container variable can be
+ * used form the file to change the container.
+ *
+ * @package    Symfony
+ * @subpackage Components_DependencyInjection
+ * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
+ */
+class PhpFileLoader extends FileLoader
+{
+    /**
+     * Loads an array of PHP files.
+     *
+     * @param mixed $resource The resource
+     */
+    public function load($file)
+    {
+        $container = $this->container;
+        $loader = $this;
+
+        $path = $this->findFile($file);
+        $this->currentDir = dirname($path);
+        $this->container->addResource(new FileResource($path));
+
+        include $path;
+    }
+
+    /**
+     * Returns true if this class supports the given resource.
+     *
+     * @param  mixed $resource A resource
+     *
+     * @return Boolean true if this class supports the given resource, false otherwise
+     */
+    public function supports($resource)
+    {
+        return is_string($resource) && 'php' === pathinfo($resource, PATHINFO_EXTENSION);
+    }
+}

+ 14 - 25
src/Symfony/Components/DependencyInjection/Loader/XmlFileLoader.php

@@ -55,6 +55,18 @@ class XmlFileLoader extends FileLoader
         $this->parseDefinitions($xml, $file);
     }
 
+    /**
+     * Returns true if this class supports the given resource.
+     *
+     * @param  mixed $resource A resource
+     *
+     * @return Boolean true if this class supports the given resource, false otherwise
+     */
+    public function supports($resource)
+    {
+        return is_string($resource) && 'xml' === pathinfo($resource, PATHINFO_EXTENSION);
+    }
+
     protected function parseParameters($xml, $file)
     {
         if (!$xml->parameters) {
@@ -71,34 +83,11 @@ class XmlFileLoader extends FileLoader
         }
 
         foreach ($xml->imports->import as $import) {
-            $this->parseImport($import, $file);
+            $this->currentDir = dirname($file);
+            $this->import((string) $import['resource']);
         }
     }
 
-    protected function parseImport($import, $file)
-    {
-        $class = null;
-        if (isset($import['class']) && $import['class'] !== get_class($this)) {
-            $class = (string) $import['class'];
-        } else {
-            // try to detect loader with the extension
-            switch (pathinfo((string) $import['resource'], PATHINFO_EXTENSION)) {
-                case 'yml':
-                    $class = 'Symfony\\Components\\DependencyInjection\\Loader\\YamlFileLoader';
-                    break;
-                case 'ini':
-                    $class = 'Symfony\\Components\\DependencyInjection\\Loader\\IniFileLoader';
-                    break;
-            }
-        }
-
-        $loader = null === $class ? $this : new $class($this->container, $this->paths);
-
-        $importedFile = $this->getAbsolutePath((string) $import['resource'], dirname($file));
-
-        $loader->load($importedFile);
-    }
-
     protected function parseDefinitions($xml, $file)
     {
         if (!$xml->services) {

+ 14 - 25
src/Symfony/Components/DependencyInjection/Loader/YamlFileLoader.php

@@ -63,6 +63,18 @@ class YamlFileLoader extends FileLoader
         $this->parseDefinitions($content, $file);
     }
 
+    /**
+     * Returns true if this class supports the given resource.
+     *
+     * @param  mixed $resource A resource
+     *
+     * @return Boolean true if this class supports the given resource, false otherwise
+     */
+    public function supports($resource)
+    {
+        return is_string($resource) && 'yml' === pathinfo($resource, PATHINFO_EXTENSION);
+    }
+
     protected function parseImports($content, $file)
     {
         if (!isset($content['imports'])) {
@@ -70,34 +82,11 @@ class YamlFileLoader extends FileLoader
         }
 
         foreach ($content['imports'] as $import) {
-            $this->parseImport($import, $file);
+            $this->currentDir = dirname($file);
+            $this->import($import['resource']);
         }
     }
 
-    protected function parseImport($import, $file)
-    {
-        $class = null;
-        if (isset($import['class']) && $import['class'] !== get_class($this)) {
-            $class = $import['class'];
-        } else {
-            // try to detect loader with the extension
-            switch (pathinfo($import['resource'], PATHINFO_EXTENSION)) {
-                case 'xml':
-                    $class = 'Symfony\\Components\\DependencyInjection\\Loader\\XmlFileLoader';
-                    break;
-                case 'ini':
-                    $class = 'Symfony\\Components\\DependencyInjection\\Loader\\IniFileLoader';
-                    break;
-            }
-        }
-
-        $loader = null === $class ? $this : new $class($this->container, $this->paths);
-
-        $importedFile = $this->getAbsolutePath($import['resource'], dirname($file));
-
-        $loader->load($importedFile);
-    }
-
     protected function parseDefinitions($content, $file)
     {
         if (!isset($content['services'])) {

+ 3 - 0
tests/Symfony/Tests/Components/DependencyInjection/Fixtures/php/simple.php

@@ -0,0 +1,3 @@
+<?php
+
+$container->setParameter('foo', 'foo');

+ 44 - 0
tests/Symfony/Tests/Components/DependencyInjection/Loader/ClosureLoaderTest.php

@@ -0,0 +1,44 @@
+<?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.
+ */
+
+namespace Symfony\Tests\Components\DependencyInjection\Loader;
+
+use Symfony\Components\DependencyInjection\ContainerBuilder;
+use Symfony\Components\DependencyInjection\Loader\LoaderResolver;
+use Symfony\Components\DependencyInjection\Loader\ClosureLoader;
+
+class ClosureLoaderTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @covers Symfony\Components\DependencyInjection\Loader\ClosureLoader::supports
+     */
+    public function testSupports()
+    {
+        $loader = new ClosureLoader(new ContainerBuilder());
+
+        $this->assertTrue($loader->supports(function ($container) {}), '->supports() returns true if the resource is loadable');
+        $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
+    }
+
+    /**
+     * @covers Symfony\Components\DependencyInjection\Loader\ClosureLoader::load
+     */
+    public function testLoad()
+    {
+        $loader = new ClosureLoader($container = new ContainerBuilder());
+
+        $loader->load(function ($container)
+        {
+            $container->setParameter('foo', 'foo');
+        });
+
+        $this->assertEquals('foo', $container->getParameter('foo'), '->load() loads a \Closure resource');
+    }
+}

+ 84 - 0
tests/Symfony/Tests/Components/DependencyInjection/Loader/DelegatingLoaderTest.php

@@ -0,0 +1,84 @@
+<?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.
+ */
+
+namespace Symfony\Tests\Components\DependencyInjection\Loader;
+
+use Symfony\Components\DependencyInjection\ContainerBuilder;
+use Symfony\Components\DependencyInjection\Loader\LoaderResolver;
+use Symfony\Components\DependencyInjection\Loader\DelegatingLoader;
+use Symfony\Components\DependencyInjection\Loader\IniFileLoader;
+use Symfony\Components\DependencyInjection\Loader\ClosureLoader;
+
+class DelegatingLoaderTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @covers Symfony\Components\DependencyInjection\Loader\DelegatingLoader::__construct
+     */
+    public function testConstructor()
+    {
+        $resolver = new LoaderResolver();
+        $loader = new DelegatingLoader($resolver);
+        $this->assertTrue(true, '__construct() takes a loader resolver as its first argument');
+    }
+
+    /**
+     * @covers Symfony\Components\DependencyInjection\Loader\DelegatingLoader::getResolver
+     * @covers Symfony\Components\DependencyInjection\Loader\DelegatingLoader::setResolver
+     */
+    public function testGetSetResolver()
+    {
+        $resolver = new LoaderResolver();
+        $loader = new DelegatingLoader($resolver);
+        $this->assertSame($resolver, $loader->getResolver(), '->getResolver() gets the resolver loader');
+        $loader->setResolver($resolver = new LoaderResolver());
+        $this->assertSame($resolver, $loader->getResolver(), '->setResolver() sets the resolver loader');
+    }
+
+    /**
+     * @covers Symfony\Components\DependencyInjection\Loader\DelegatingLoader::supports
+     */
+    public function testSupports()
+    {
+        $container = new ContainerBuilder();
+        $resolver = new LoaderResolver(array(
+            $ini = new IniFileLoader($container, array()),
+        ));
+        $loader = new DelegatingLoader($resolver);
+
+        $this->assertTrue($loader->supports('foo.ini'), '->supports() returns true if the resource is loadable');
+        $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
+    }
+
+    /**
+     * @covers Symfony\Components\DependencyInjection\Loader\DelegatingLoader::load
+     */
+    public function testLoad()
+    {
+        $container = new ContainerBuilder();
+        $resolver = new LoaderResolver(array(
+            new ClosureLoader($container),
+        ));
+        $loader = new DelegatingLoader($resolver);
+
+        $loader->load(function ($container)
+        {
+            $container->setParameter('foo', 'foo');
+        });
+
+        $this->assertEquals('foo', $container->getParameter('foo'), '->load() loads a resource using the loaders from the resolver');
+
+        try {
+            $loader->load('foo.foo');
+            $this->fail('->load() throws an \InvalidArgumentException if the resource cannot be loaded');
+        } catch (\Exception $e) {
+            $this->assertInstanceOf('\InvalidArgumentException', $e, '->load() throws an \InvalidArgumentException if the resource cannot be loaded');
+        }
+    }
+}

+ 5 - 0
tests/Symfony/Tests/Components/DependencyInjection/Loader/FileLoaderTest.php

@@ -54,6 +54,11 @@ class ProjectLoader extends FileLoader
     {
     }
 
+    public function supports($resource)
+    {
+        return true;
+    }
+
     public function getAbsolutePath($file, $currentPath = null)
     {
         return parent::getAbsolutePath($file, $currentPath);

+ 11 - 0
tests/Symfony/Tests/Components/DependencyInjection/Loader/IniFileLoaderTest.php

@@ -49,4 +49,15 @@ class IniFileLoaderTest extends \PHPUnit_Framework_TestCase
             $this->assertEquals('The nonvalid.ini file is not valid.', $e->getMessage(), '->load() throws an InvalidArgumentException if the loaded file is not parseable');
         }
     }
+
+    /**
+     * @covers Symfony\Components\DependencyInjection\Loader\IniFileLoader::supports
+     */
+    public function testSupports()
+    {
+        $loader = new IniFileLoader(new ContainerBuilder());
+
+        $this->assertTrue($loader->supports('foo.ini'), '->supports() returns true if the resource is loadable');
+        $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
+    }
 }

+ 55 - 0
tests/Symfony/Tests/Components/DependencyInjection/Loader/LoaderResolverTest.php

@@ -0,0 +1,55 @@
+<?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.
+ */
+
+namespace Symfony\Tests\Components\DependencyInjection\Loader;
+
+use Symfony\Components\DependencyInjection\ContainerBuilder;
+use Symfony\Components\DependencyInjection\Loader\LoaderResolver;
+use Symfony\Components\DependencyInjection\Loader\ClosureLoader;
+
+class LoaderResolverTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @covers Symfony\Components\DependencyInjection\Loader\LoaderResolver::__construct
+     */
+    public function testConstructor()
+    {
+        $resolver = new LoaderResolver(array(
+            $loader = new ClosureLoader(new ContainerBuilder()),
+        ));
+
+        $this->assertEquals(array($loader), $resolver->getLoaders(), '__construct() takes an array of loaders as its first argument');
+    }
+
+    /**
+     * @covers Symfony\Components\DependencyInjection\Loader\LoaderResolver::resolve
+     */
+    public function testResolve()
+    {
+        $resolver = new LoaderResolver(array(
+            $loader = new ClosureLoader(new ContainerBuilder()),
+        ));
+
+        $this->assertFalse($resolver->resolve('foo.foo'), '->resolve() returns false if no loader is able to load the resource');
+        $this->assertEquals($loader, $resolver->resolve(function () {}), '->resolve() returns the loader for the given resource');
+    }
+
+    /**
+     * @covers Symfony\Components\DependencyInjection\Loader\LoaderResolver::getLoaders
+     * @covers Symfony\Components\DependencyInjection\Loader\LoaderResolver::addLoader
+     */
+    public function testLoaders()
+    {
+        $resolver = new LoaderResolver();
+        $resolver->addLoader($loader = new ClosureLoader(new ContainerBuilder()));
+
+        $this->assertEquals(array($loader), $resolver->getLoaders(), 'addLoader() adds a loader');
+    }
+}

+ 76 - 0
tests/Symfony/Tests/Components/DependencyInjection/Loader/LoaderTest.php

@@ -0,0 +1,76 @@
+<?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.
+ */
+
+namespace Symfony\Tests\Components\DependencyInjection\Loader;
+
+use Symfony\Components\DependencyInjection\ContainerBuilder;
+use Symfony\Components\DependencyInjection\Loader\LoaderResolver;
+use Symfony\Components\DependencyInjection\Loader\Loader;
+use Symfony\Components\DependencyInjection\Loader\IniFileLoader;
+
+class LoaderTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @covers Symfony\Components\DependencyInjection\Loader\Loader::__construct
+     */
+    public function testConstructor()
+    {
+        $loader = new ProjectLoader1(new ContainerBuilder());
+        $this->assertTrue(true, '__construct() takes a container builder as its first argument');
+    }
+
+    /**
+     * @covers Symfony\Components\DependencyInjection\Loader\Loader::getResolver
+     * @covers Symfony\Components\DependencyInjection\Loader\Loader::setResolver
+     */
+    public function testGetSetResolver()
+    {
+        $resolver = new LoaderResolver();
+        $loader = new ProjectLoader1(new ContainerBuilder());
+        $loader->setResolver($resolver);
+        $this->assertSame($resolver, $loader->getResolver(), '->setResolver() sets the resolver loader');
+    }
+
+    /**
+     * @covers Symfony\Components\DependencyInjection\Loader\Loader::resolve
+     */
+    public function testResolve()
+    {
+        $container = new ContainerBuilder();
+
+        $resolver = new LoaderResolver(array(
+            $ini = new IniFileLoader($container, array()),
+        ));
+        $loader = new ProjectLoader1($container);
+        $loader->setResolver($resolver);
+
+        $this->assertSame($ini, $loader->resolve('foo.ini'), '->resolve() finds a loader');
+        $this->assertSame($loader, $loader->resolve('foo.foo'), '->resolve() finds a loader');
+
+        try {
+            $loader->resolve(new \stdClass());
+            $this->fail('->resolve() throws an \InvalidArgumentException if the resource cannot be loaded');
+        } catch (\Exception $e) {
+            $this->assertInstanceOf('\InvalidArgumentException', $e, '->resolve() throws an \InvalidArgumentException if the resource cannot be loaded');
+        }
+    }
+}
+
+class ProjectLoader1 extends Loader
+{
+    public function load($resource)
+    {
+    }
+
+    public function supports($resource)
+    {
+        return is_string($resource) && 'foo' === pathinfo($resource, PATHINFO_EXTENSION);
+    }
+}

+ 44 - 0
tests/Symfony/Tests/Components/DependencyInjection/Loader/PhpFileLoaderTest.php

@@ -0,0 +1,44 @@
+<?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.
+ */
+
+namespace Symfony\Tests\Components\DependencyInjection\Loader;
+
+use Symfony\Components\DependencyInjection\ContainerBuilder;
+use Symfony\Components\DependencyInjection\Reference;
+use Symfony\Components\DependencyInjection\Definition;
+use Symfony\Components\DependencyInjection\Loader\Loader;
+use Symfony\Components\DependencyInjection\Loader\PhpFileLoader;
+use Symfony\Components\DependencyInjection\Loader\LoaderResolver;
+
+class PhpFileLoaderTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @covers Symfony\Components\DependencyInjection\Loader\PhpFileLoader::supports
+     */
+    public function testSupports()
+    {
+        $loader = new PhpFileLoader(new ContainerBuilder());
+
+        $this->assertTrue($loader->supports('foo.php'), '->supports() returns true if the resource is loadable');
+        $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
+    }
+
+    /**
+     * @covers Symfony\Components\DependencyInjection\Loader\PhpFileLoader::load
+     */
+    public function testLoad()
+    {
+        $loader = new PhpFileLoader($container = new ContainerBuilder());
+
+        $loader->load(__DIR__.'/../Fixtures/php/simple.php');
+
+        $this->assertEquals('foo', $container->getParameter('foo'), '->load() loads a PHP file resource');
+    }
+}

+ 20 - 1
tests/Symfony/Tests/Components/DependencyInjection/Loader/XmlFileLoaderTest.php

@@ -15,6 +15,9 @@ use Symfony\Components\DependencyInjection\Reference;
 use Symfony\Components\DependencyInjection\Definition;
 use Symfony\Components\DependencyInjection\Loader\Loader;
 use Symfony\Components\DependencyInjection\Loader\XmlFileLoader;
+use Symfony\Components\DependencyInjection\Loader\YamlFileLoader;
+use Symfony\Components\DependencyInjection\Loader\IniFileLoader;
+use Symfony\Components\DependencyInjection\Loader\LoaderResolver;
 
 class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
 {
@@ -81,7 +84,12 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
     public function testLoadImports()
     {
         $container = new ContainerBuilder();
-        $loader = new ProjectLoader2($container, self::$fixturesPath.'/xml');
+        $resolver = new LoaderResolver(array(
+            new IniFileLoader($container, self::$fixturesPath.'/xml'),
+            new YamlFileLoader($container, self::$fixturesPath.'/xml'),
+            $loader = new ProjectLoader2($container, self::$fixturesPath.'/xml'),
+        ));
+        $loader->setResolver($resolver);
         $loader->load('services4.xml');
 
         $actual = $container->getParameterBag()->all();
@@ -233,6 +241,17 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
             $this->assertStringStartsWith('The tag "projectwithxsd:foobar" is not defined in the "projectwithxsd" extension.', $e->getMessage(), '->load() throws an InvalidArgumentException if a tag is not valid for a given extension');
         }
     }
+
+    /**
+     * @covers Symfony\Components\DependencyInjection\Loader\XmlFileLoader::supports
+     */
+    public function testSupports()
+    {
+        $loader = new XmlFileLoader(new ContainerBuilder());
+
+        $this->assertTrue($loader->supports('foo.xml'), '->supports() returns true if the resource is loadable');
+        $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
+    }
 }
 
 class ProjectLoader2 extends XmlFileLoader

+ 20 - 1
tests/Symfony/Tests/Components/DependencyInjection/Loader/YamlFileLoaderTest.php

@@ -14,7 +14,10 @@ use Symfony\Components\DependencyInjection\ContainerBuilder;
 use Symfony\Components\DependencyInjection\Reference;
 use Symfony\Components\DependencyInjection\Definition;
 use Symfony\Components\DependencyInjection\Loader\Loader;
+use Symfony\Components\DependencyInjection\Loader\XmlFileLoader;
 use Symfony\Components\DependencyInjection\Loader\YamlFileLoader;
+use Symfony\Components\DependencyInjection\Loader\IniFileLoader;
+use Symfony\Components\DependencyInjection\Loader\LoaderResolver;
 
 class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
 {
@@ -70,7 +73,12 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
     public function testLoadImports()
     {
         $container = new ContainerBuilder();
-        $loader = new ProjectLoader3($container, self::$fixturesPath.'/yaml');
+        $resolver = new LoaderResolver(array(
+            new IniFileLoader($container, self::$fixturesPath.'/yaml'),
+            new XmlFileLoader($container, self::$fixturesPath.'/yaml'),
+            $loader = new ProjectLoader3($container, self::$fixturesPath.'/yaml'),
+        ));
+        $loader->setResolver($resolver);
         $loader->load('services4.yml');
 
         $actual = $container->getParameterBag()->all();
@@ -136,6 +144,17 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
             $this->assertStringStartsWith('The "foobar" tag is not valid (in', $e->getMessage(), '->load() throws an InvalidArgumentException if an extension is not loaded');
         }
     }
+
+    /**
+     * @covers Symfony\Components\DependencyInjection\Loader\YamlFileLoader::supports
+     */
+    public function testSupports()
+    {
+        $loader = new YamlFileLoader(new ContainerBuilder());
+
+        $this->assertTrue($loader->supports('foo.yml'), '->supports() returns true if the resource is loadable');
+        $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
+    }
 }
 
 class ProjectLoader3 extends YamlFileLoader