فهرست منبع

[DependencyInjection] Interface Injection implementation

Bulat Shakirzyanov 14 سال پیش
والد
کامیت
73331cf1c1
24فایلهای تغییر یافته به همراه1108 افزوده شده و 6 حذف شده
  1. 60 1
      src/Symfony/Component/DependencyInjection/ContainerBuilder.php
  2. 39 0
      src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
  3. 33 1
      src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php
  4. 18 1
      src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php
  5. 22 0
      src/Symfony/Component/DependencyInjection/Exception/Exception.php
  6. 18 0
      src/Symfony/Component/DependencyInjection/Exception/InvalidArgumentException.php
  7. 169 0
      src/Symfony/Component/DependencyInjection/InterfaceInjector.php
  8. 25 1
      src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
  9. 28 2
      src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
  10. 19 0
      src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd
  11. 58 0
      tests/Symfony/Tests/Component/DependencyInjection/ContainerBuilderTest.php
  12. 40 0
      tests/Symfony/Tests/Component/DependencyInjection/Dumper/PhpDumperTest.php
  13. 38 0
      tests/Symfony/Tests/Component/DependencyInjection/Dumper/XmlDumperTest.php
  14. 30 0
      tests/Symfony/Tests/Component/DependencyInjection/Dumper/YamlDumperTest.php
  15. 23 0
      tests/Symfony/Tests/Component/DependencyInjection/Fixtures/containers/interfaces1.php
  16. 34 0
      tests/Symfony/Tests/Component/DependencyInjection/Fixtures/containers/interfaces2.php
  17. 70 0
      tests/Symfony/Tests/Component/DependencyInjection/Fixtures/php/services_interfaces-1-1.php
  18. 82 0
      tests/Symfony/Tests/Component/DependencyInjection/Fixtures/php/services_interfaces-1.php
  19. 83 0
      tests/Symfony/Tests/Component/DependencyInjection/Fixtures/php/services_interfaces-2.php
  20. 17 0
      tests/Symfony/Tests/Component/DependencyInjection/Fixtures/xml/interfaces1.xml
  21. 6 0
      tests/Symfony/Tests/Component/DependencyInjection/Fixtures/yaml/interfaces1.yml
  22. 172 0
      tests/Symfony/Tests/Component/DependencyInjection/InterfaceInjectorTest.php
  23. 12 0
      tests/Symfony/Tests/Component/DependencyInjection/Loader/XmlFileLoaderTest.php
  24. 12 0
      tests/Symfony/Tests/Component/DependencyInjection/Loader/YamlFileLoaderTest.php

+ 60 - 1
src/Symfony/Component/DependencyInjection/ContainerBuilder.php

@@ -5,6 +5,7 @@ namespace Symfony\Component\DependencyInjection;
 use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
 use Symfony\Component\DependencyInjection\Resource\ResourceInterface;
 use Symfony\Component\DependencyInjection\Resource\FileResource;
+use Symfony\Component\DependencyInjection\InterfaceInjector;
 
 /*
  * This file is part of the Symfony framework.
@@ -29,6 +30,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
     protected $loading          = array();
     protected $resources        = array();
     protected $extensionConfigs = array();
+    protected $injectors        = array();
 
     /**
      * Registers an extension.
@@ -130,9 +132,15 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      *
      * @param string $id      The service identifier
      * @param object $service The service instance
+     *
+     * @throws BadMethodCallException
      */
     public function set($id, $service)
     {
+        if ($this->isFrozen()) {
+            throw new \BadMethodCallException('Setting service on a frozen container is not allowed');
+        }
+
         unset($this->definitions[$id]);
         unset($this->aliases[$id]);
 
@@ -287,12 +295,25 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
 
             $this->merge($container);
         }
-        $this->extensionConfigs = array();
 
+        $this->extensionConfigs = array();
         $this->addDefinitions($definitions);
         $this->addAliases($aliases);
         $this->parameterBag->add($parameters);
 
+        foreach ($this->definitions as $definition) {
+            foreach ($this->injectors as $injector) {
+                if (null !== $definition->getFactoryService()) {
+                    continue;
+                }
+                $defClass = $this->parameterBag->resolveValue($definition->getClass());
+                $definition->setClass($defClass);
+                if ($injector->supports($defClass)) {
+                    $injector->processDefinition($definition);
+                }
+            }
+        }
+
         parent::freeze();
     }
 
@@ -392,6 +413,34 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
         return $this->aliases[$id];
     }
 
+    public function addInterfaceInjectors(array $injectors)
+    {
+        foreach ($injectors as $injector) {
+            $this->addInterfaceInjector($injector);
+        }
+    }
+
+    public function addInterfaceInjector(InterfaceInjector $injector)
+    {
+        $class = $injector->getClass();
+        if (isset($this->injectors[$class])) {
+            return $this->injectors[$class]->merge($injector);
+        }
+
+        $this->injectors[$class] = $injector;
+    }
+
+    public function getInterfaceInjectors($service = null)
+    {
+        if (null === $service) {
+            return $this->injectors;
+        }
+
+        return array_filter($this->injectors, function(InterfaceInjector $injector) use ($service) {
+            return $injector->supports($service);
+        });
+    }
+
     /**
      * Registers a service definition.
      *
@@ -446,9 +495,15 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      *
      * @param  string     $id         The service identifier
      * @param  Definition $definition A Definition instance
+     *
+     * @throws BadMethodCallException
      */
     public function setDefinition($id, Definition $definition)
     {
+        if ($this->isFrozen()) {
+            throw new \BadMethodCallException('Adding definition to a frozen container is not allowed');
+        }
+
         unset($this->aliases[$id]);
 
         return $this->definitions[$id] = $definition;
@@ -536,6 +591,10 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
             $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments);
         }
 
+        foreach ($this->getInterfaceInjectors($service) as $injector) {
+            $injector->processDefinition($definition, $service);
+        }
+
         if ($definition->isShared()) {
             $this->services[$id] = $service;
         }

+ 39 - 0
src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php

@@ -49,10 +49,45 @@ class PhpDumper extends Dumper
             $this->addServices().
             $this->addTags().
             $this->addDefaultParametersMethod().
+            $this->addInterfaceInjectors().
             $this->endClass()
         ;
     }
 
+    protected function addInterfaceInjectors()
+    {
+        if ($this->container->isFrozen() || 0 === count($this->container->getInterfaceInjectors())) {
+            return;
+        }
+
+        $code = <<<EOF
+
+    /**
+     * Applies all known interface injection calls
+     *
+     * @param Object \$instance
+     */
+    protected function applyIntrefaceInjectors(\$instance)
+    {
+
+EOF;
+        foreach ($this->container->getInterfaceInjectors() as $injector) {
+            $code .= sprintf("        if (\$instance instanceof \\%s) {\n", $injector->getClass());
+            foreach ($injector->getMethodCalls() as $call) {
+                foreach ($call[1] as $value) {
+                    $arguments[] = $this->dumpValue($value);
+                }
+                $code .= $this->wrapServiceConditionals($call[1], sprintf("            \$instance->%s(%s);\n", $call[0], implode(', ', $arguments)));
+            }
+            $code .= sprintf("        }\n");
+        }
+        $code .= <<<EOF
+    }
+
+EOF;
+        return $code;
+    }
+
     protected function addServiceInclude($id, $definition)
     {
         if (null !== $definition->getFile()) {
@@ -125,6 +160,10 @@ class PhpDumper extends Dumper
             $calls .= $this->wrapServiceConditionals($call[1], sprintf("        \$instance->%s(%s);\n", $call[0], implode(', ', $arguments)));
         }
 
+        if (!$this->container->isFrozen() && count($this->container->getInterfaceInjectors()) > 0) {
+            $calls = sprintf("\n        \$this->applyInterfaceInjection(\$instance);\n");
+        }
+
         return $calls;
     }
 

+ 33 - 1
src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php

@@ -5,6 +5,7 @@ namespace Symfony\Component\DependencyInjection\Dumper;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Parameter;
 use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\InterfaceInjector;
 
 /*
  * This file is part of the Symfony framework.
@@ -31,7 +32,7 @@ class XmlDumper extends Dumper
      */
     public function dump(array $options = array())
     {
-        return $this->startXml().$this->addParameters().$this->addServices().$this->endXml();
+        return $this->startXml().$this->addParameters().$this->addInterfaceInjectors().$this->addServices().$this->endXml();
     }
 
     protected function addParameters()
@@ -49,6 +50,37 @@ class XmlDumper extends Dumper
         return sprintf("  <parameters>\n%s  </parameters>\n", $this->convertParameters($parameters, 'parameter', 4));
     }
 
+    protected function addInterfaceInjector(InterfaceInjector $injector)
+    {
+        $code = \sprintf("    <interface class=\"%s\">\n", $injector->getClass());
+
+        foreach ($injector->getMethodCalls() as $call) {
+            if (count($call[1])) {
+                $code .= sprintf("      <call method=\"%s\">\n%s      </call>\n", $call[0], $this->convertParameters($call[1], 'argument', 8));
+            } else {
+                $code .= sprintf("      <call method=\"%s\" />\n", $call[0]);
+            }
+        }
+
+        $code .= "    </interface>\n";
+
+        return $code;
+    }
+
+    protected function addInterfaceInjectors()
+    {
+        if (!$this->container->getInterfaceInjectors()) {
+            return '';
+        }
+
+        $code = '';
+        foreach ($this->container->getInterfaceInjectors() as $injector) {
+            $code .= $this->addInterfaceInjector($injector);
+        }
+
+        return sprintf("  <interfaces>\n%s  </interfaces>\n", $code);
+    }
+
     protected function addService($id, $definition)
     {
         $code = sprintf("    <service id=\"%s\"%s%s%s%s>\n",

+ 18 - 1
src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php

@@ -32,7 +32,24 @@ class YamlDumper extends Dumper
      */
     public function dump(array $options = array())
     {
-        return $this->addParameters()."\n".$this->addServices();
+        return $this->addParameters().$this->addInterfaceInjectors()."\n".$this->addServices();
+    }
+
+    protected function addInterfaceInjectors()
+    {
+        if (!$this->container->getInterfaceInjectors()) {
+            return '';
+        }
+
+        $code = "\ninterfaces:\n";
+        foreach ($this->container->getInterfaceInjectors() as $injector) {
+            $code .= sprintf("    %s:\n", $injector->getClass());
+            if ($injector->getMethodCalls()) {
+                $code .= sprintf("        calls:\n          %s\n", str_replace("\n", "\n          ", Yaml::dump($this->dumpValue($injector->getMethodCalls()), 1)));
+            }
+        }
+
+        return $code;
     }
 
     protected function addService($id, $definition)

+ 22 - 0
src/Symfony/Component/DependencyInjection/Exception/Exception.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace Symfony\Component\DependencyInjection\Exception;
+
+/*
+ * 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.
+ */
+
+/**
+ * Exception
+ *
+ * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
+ * @author Bulat Shakirzyanov <bulat@theopenskyproject.com>
+ */
+interface Exception
+{
+}

+ 18 - 0
src/Symfony/Component/DependencyInjection/Exception/InvalidArgumentException.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace Symfony\Component\DependencyInjection\Exception;
+
+use \InvalidArgumentException as BaseInvalidArgumentException;
+
+/**
+ * InvalidArgumentException
+ *
+ * @package OpenSky Messaging
+ * @version $Id$
+ * @author Bulat Shakirzyanov <bulat@theopenskyproject.com>
+ * @copyright (c) 2010 OpenSky Project Inc
+ * @license http://www.gnu.org/licenses/agpl.txt GNU Affero General Public License
+ */
+class InvalidArgumentException extends BaseInvalidArgumentException implements Exception
+{
+}

+ 169 - 0
src/Symfony/Component/DependencyInjection/InterfaceInjector.php

@@ -0,0 +1,169 @@
+<?php
+
+namespace Symfony\Component\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+
+/*
+ * 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.
+ */
+
+/**
+ * InterfaceInjector is used for Interface Injection.
+ *
+ * @author     Bulat Shakirzyanov <mallluhuct@gmail.com>
+ */
+class InterfaceInjector
+{
+    protected $class;
+    protected $calls = array();
+    protected $processedDefinitions = array();
+
+    /**
+     * Contructs interface injector by specifying the target class name
+     *
+     * @param string $class
+     */
+    public function __construct($class)
+    {
+        $this->class = $class;
+    }
+
+    /**
+     * Returns the interface name
+     *
+     * @return string
+     */
+    public function getClass()
+    {
+        return $this->class;
+    }
+
+    /**
+     * Adds method calls if Definition is of required interface
+     *
+     * @param Definition $definition
+     * @return void
+     */
+    public function processDefinition(Definition $definition, $class = null)
+    {
+        if (in_array($definition, $this->processedDefinitions, true)) {
+            return;
+        }
+
+        $class = $class ?: $definition->getClass();
+
+        if (!$this->supports($class)) {
+            return;
+        }
+
+        foreach ($this->calls as $callback) {
+            list($method, $arguments) = $callback;
+            $definition->addMethodCall($method, $arguments);
+        }
+
+        $this->processedDefinitions[] = $definition;
+    }
+
+    /**
+     * Inspects if current interface injector is to be used with a given class
+     *
+     * @param string $object
+     * @return boolean
+     */
+    public function supports($object)
+    {
+        if (is_string($object)) {
+            $class = new \ReflectionClass($object);
+            $object = $class->newInstance();
+        }
+
+        if ( ! is_object($object)) {
+            throw new InvalidArgumentException(sprintf("%s expects class or object, %s given", __METHOD__, substr(str_replace("\n", '', var_export($object, true)), 0, 10)));
+        }
+
+        return is_a($object, $this->class);
+    }
+
+    /**
+     * Adds a method to call to be injected on any service implementing the interface.
+     *
+     * @param  string $method    The method name to call
+     * @param  array  $arguments An array of arguments to pass to the method call
+     *
+     * @return InterfaceInjector The current instance
+     */
+    public function addMethodCall($method, array $arguments = array())
+    {
+        $this->calls[] = array($method, $arguments);
+
+        return $this;
+    }
+
+    /**
+     * Removes a method to call after service initialization.
+     *
+     * @param  string $method    The method name to remove
+     *
+     * @return Definition The current instance
+     */
+    public function removeMethodCall($method)
+    {
+        foreach ($this->calls as $i => $call) {
+            if ($call[0] === $method) {
+                unset($this->calls[$i]);
+                break;
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Check if the current definition has a given method to call after service initialization.
+     *
+     * @param  string $method    The method name to search for
+     *
+     * @return boolean
+     */
+    public function hasMethodCall($method)
+    {
+        foreach ($this->calls as $i => $call) {
+            if ($call[0] === $method) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Gets the methods to call after service initialization.
+     *
+     * @return  array An array of method calls
+     */
+    public function getMethodCalls()
+    {
+        return $this->calls;
+    }
+
+    /**
+     * Merges another InterfaceInjector
+     *
+     * @param InterfaceInjector $injector
+     */
+    public function merge(InterfaceInjector $injector)
+    {
+        if ($this->class === $injector->getClass()) {
+            foreach ($injector->getMethodCalls() as $call) {
+                list ($method, $arguments) = $call;
+                $this->addMethodCall($method, $arguments);
+            }
+        }
+    }
+}

+ 25 - 1
src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php

@@ -2,6 +2,7 @@
 
 namespace Symfony\Component\DependencyInjection\Loader;
 
+use Symfony\Component\DependencyInjection\InterfaceInjector;
 use Symfony\Component\DependencyInjection\Definition;
 use Symfony\Component\DependencyInjection\Reference;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -49,6 +50,9 @@ class XmlFileLoader extends FileLoader
         // parameters
         $this->parseParameters($xml, $file);
 
+        // interface injectors
+        $this->parseInterfaceInjectors($xml, $file);
+
         // services
         $this->parseDefinitions($xml, $file);
     }
@@ -86,6 +90,26 @@ class XmlFileLoader extends FileLoader
         }
     }
 
+    protected function parseInterfaceInjectors($xml, $file)
+    {
+        if (!$xml->interfaces) {
+            return;
+        }
+
+        foreach ($xml->interfaces->interface as $interface) {
+            $this->parseInterfaceInjector((string) $interface['class'], $interface, $file);
+        }
+    }
+
+    protected function parseInterfaceInjector($class, $interface, $file)
+    {
+        $injector = new InterfaceInjector($class);
+        foreach ($interface->call as $call) {
+            $injector->addMethodCall((string) $call['method'], $call->getArgumentsAsPhp('argument'));
+        }
+        $this->container->addInterfaceInjector($injector);
+    }
+
     protected function parseDefinitions($xml, $file)
     {
         if (!$xml->services) {
@@ -286,7 +310,7 @@ EOF
     protected function validateExtensions($dom, $file)
     {
         foreach ($dom->documentElement->childNodes as $node) {
-            if (!$node instanceof \DOMElement || in_array($node->tagName, array('imports', 'parameters', 'services'))) {
+            if (!$node instanceof \DOMElement || in_array($node->tagName, array('imports', 'parameters', 'services', 'interfaces'))) {
                 continue;
             }
 

+ 28 - 2
src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php

@@ -3,6 +3,7 @@
 namespace Symfony\Component\DependencyInjection\Loader;
 
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\DependencyInjection\InterfaceInjector;
 use Symfony\Component\DependencyInjection\Definition;
 use Symfony\Component\DependencyInjection\Reference;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -57,6 +58,9 @@ class YamlFileLoader extends FileLoader
             }
         }
 
+        // interface injectors
+        $this->parseInterfaceInjectors($content, $file);
+
         // services
         $this->parseDefinitions($content, $file);
     }
@@ -85,6 +89,28 @@ class YamlFileLoader extends FileLoader
         }
     }
 
+    protected function parseInterfaceInjectors($content, $file)
+    {
+        if (!isset($content['interfaces'])) {
+            return;
+        }
+
+        foreach ($content['interfaces'] as $class => $interface) {
+            $this->parseInterfaceInjector($class, $interface, $file);
+        }
+    }
+
+    protected function parseInterfaceInjector($class, $interface, $file)
+    {
+        $injector = new InterfaceInjector($class);
+        if (isset($interface['calls'])) {
+            foreach ($interface['calls'] as $call) {
+                $injector->addMethodCall($call[0], $this->resolveServices($call[1]));
+            }
+        }
+        $this->container->addInterfaceInjector($injector);
+    }
+
     protected function parseDefinitions($content, $file)
     {
         if (!isset($content['services'])) {
@@ -175,7 +201,7 @@ class YamlFileLoader extends FileLoader
         }
 
         foreach (array_keys($content) as $key) {
-            if (in_array($key, array('imports', 'parameters', 'services'))) {
+            if (in_array($key, array('imports', 'parameters', 'services', 'interfaces'))) {
                 continue;
             }
 
@@ -211,7 +237,7 @@ class YamlFileLoader extends FileLoader
     protected function loadFromExtensions($content)
     {
         foreach ($content as $key => $values) {
-            if (in_array($key, array('imports', 'parameters', 'services'))) {
+            if (in_array($key, array('imports', 'parameters', 'services', 'interfaces'))) {
                 continue;
             }
 

+ 19 - 0
src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd

@@ -28,10 +28,22 @@
       <xsd:element name="imports" type="imports" minOccurs="0" maxOccurs="1" />
       <xsd:element name="parameters" type="parameters" minOccurs="0" maxOccurs="1" />
       <xsd:element name="services" type="services" minOccurs="0" maxOccurs="1" />
+      <xsd:element name="interfaces" type="interfaces" minOccurs="0" maxOccurs="1" />
       <xsd:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
     </xsd:sequence>
   </xsd:complexType>
 
+  <xsd:complexType name="interfaces">
+    <xsd:annotation>
+      <xsd:documentation><![CDATA[
+        Enclosing element for the definition of all interface injectors
+      ]]></xsd:documentation>
+    </xsd:annotation>
+    <xsd:sequence>
+      <xsd:element name="interface" type="interface" minOccurs="0" maxOccurs="unbounded" />
+    </xsd:sequence>
+  </xsd:complexType>
+
   <xsd:complexType name="services">
     <xsd:annotation>
       <xsd:documentation><![CDATA[
@@ -72,6 +84,13 @@
     <xsd:attribute name="function" type="xsd:string" />
   </xsd:complexType>
 
+  <xsd:complexType name="interface">
+    <xsd:sequence maxOccurs="unbounded">
+      <xsd:element name="call" type="call" minOccurs="1" maxOccurs="unbounded" />
+    </xsd:sequence>
+    <xsd:attribute name="class" type="xsd:string" use="required" />
+  </xsd:complexType>
+
   <xsd:complexType name="service">
     <xsd:choice maxOccurs="unbounded">
       <xsd:element name="file" type="xsd:string" minOccurs="0" maxOccurs="1" />

+ 58 - 0
tests/Symfony/Tests/Component/DependencyInjection/ContainerBuilderTest.php

@@ -406,4 +406,62 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
         $this->setExpectedException('LogicException');
         $container->getExtension('no_registered');
     }
+
+    /**
+     * @covers Symfony\Component\DependencyInjection\ContainerBuilder::addInterfaceInjector
+     * @covers Symfony\Component\DependencyInjection\ContainerBuilder::getInterfaceInjectors
+     * @covers Symfony\Component\DependencyInjection\ContainerBuilder::setDefinition
+     */
+    public function testInterfaceInjection()
+    {
+        $definition = new Definition('Symfony\Tests\Component\DependencyInjection\FooClass');
+
+        $injector = $this->getMockInterfaceInjector('Symfony\Tests\Component\DependencyInjection\FooClass', 1);
+        $injector2 = $this->getMockInterfaceInjector('Symfony\Tests\Component\DependencyInjection\FooClass', 0);
+
+        $container = new ContainerBuilder();
+        $container->addInterfaceInjector($injector);
+        $container->addInterfaceInjector($injector2);
+        $this->assertEquals(1, count($container->getInterfaceInjectors('Symfony\Tests\Component\DependencyInjection\FooClass')));
+
+        $container->setDefinition('test', $definition);
+        $test = $container->get('test');
+    }
+
+    /**
+     * @expectedException BadMethodCallException
+     */
+    public function testThrowsExceptionWhenSetServiceOnAFrozenContainer()
+    {
+        $container = new ContainerBuilder();
+        $container->freeze();
+        $container->set('a', new \stdClass());
+    }
+
+    /**
+     * @expectedException BadMethodCallException
+     */
+    public function testThrowsExceptionWhenSetDefinitionOnAFrozenContainer()
+    {
+        $container = new ContainerBuilder();
+        $container->freeze();
+        $container->setDefinition('a', new Definition());
+    }
+
+    /**
+     * @param string $class
+     * @param int $methodCallsCount
+     * @return Symfony\Component\DependencyInjection\InterfaceInjector
+     */
+    private function getMockInterfaceInjector($class, $methodCallsCount)
+    {
+        $injector = $this->getMock('Symfony\Component\DependencyInjection\InterfaceInjector', array('processDefinition'), array('Symfony\Tests\Component\DependencyInjection\FooClass'), '', true, false);
+        $injector->expects($this->exactly($methodCallsCount))
+            ->method('processDefinition')
+        ;
+        return $injector;
+    }
 }
+
+
+class FooClass {}

+ 40 - 0
tests/Symfony/Tests/Component/DependencyInjection/Dumper/PhpDumperTest.php

@@ -14,6 +14,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
 use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\InterfaceInjector;
 
 class PhpDumperTest extends \PHPUnit_Framework_TestCase
 {
@@ -91,4 +92,43 @@ class PhpDumperTest extends \PHPUnit_Framework_TestCase
 
         $this->assertSame($bar, $container->get('foo')->bar, '->set() overrides an already defined service');
     }
+
+    public function testInterfaceInjectors()
+    {
+        $interfaceInjector = new InterfaceInjector('FooClass');
+        $interfaceInjector->addMethodCall('setBar', array('someValue'));
+        $container = include self::$fixturesPath.'/containers/interfaces1.php';
+        $container->addInterfaceInjector($interfaceInjector);
+
+        $dumper = new PhpDumper($container);
+
+        $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_interfaces-1.php', $dumper->dump(), '->dump() dumps interface injectors');
+    }
+
+    public function testInterfaceInjectorsAndServiceFactories()
+    {
+        $interfaceInjector = new InterfaceInjector('BarClass');
+        $interfaceInjector->addMethodCall('setFoo', array('someValue'));
+        $container = include self::$fixturesPath.'/containers/interfaces2.php';
+        $container->addInterfaceInjector($interfaceInjector);
+
+        $dumper = new PhpDumper($container);
+
+        $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_interfaces-2.php', $dumper->dump(), '->dump() dumps interface injectors');
+    }
+
+    public function testFrozenContainerInterfaceInjectors()
+    {
+        $interfaceInjector = new InterfaceInjector('FooClass');
+        $interfaceInjector->addMethodCall('setBar', array('someValue'));
+        $container = include self::$fixturesPath.'/containers/interfaces1.php';
+        $container->addInterfaceInjector($interfaceInjector);
+        $container->freeze();
+
+        $dumper = new PhpDumper($container);
+
+        file_put_contents(self::$fixturesPath.'/php/services_interfaces-1-1.php', $dumper->dump());
+
+        $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_interfaces-1-1.php', $dumper->dump(), '->dump() dumps interface injectors');
+    }
 }

+ 38 - 0
tests/Symfony/Tests/Component/DependencyInjection/Dumper/XmlDumperTest.php

@@ -12,6 +12,7 @@ namespace Symfony\Tests\Component\DependencyInjection\Dumper;
 
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Dumper\XmlDumper;
+use Symfony\Component\DependencyInjection\InterfaceInjector;
 
 class XmlDumperTest extends \PHPUnit_Framework_TestCase
 {
@@ -62,4 +63,41 @@ class XmlDumperTest extends \PHPUnit_Framework_TestCase
             $this->assertEquals('Unable to dump a service container if a parameter is an object or a resource.', $e->getMessage(), '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources');
         }
     }
+
+    public function testInterfaceInjectors()
+    {
+        $interfaceInjector = new InterfaceInjector('FooClass');
+        $interfaceInjector->addMethodCall('setBar', array('someValue'));
+        $container = include self::$fixturesPath.'/containers/interfaces1.php';
+        $container->addInterfaceInjector($interfaceInjector);
+
+        $dumper = new XmlDumper($container);
+
+        $classBody = $dumper->dump();
+        //TODO: find a better way to test dumper
+        //var_dump($classBody);
+
+        $this->assertEquals("<?xml version=\"1.0\" ?>
+
+<container xmlns=\"http://www.symfony-project.org/schema/dic/services\"
+    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
+    xsi:schemaLocation=\"http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd\">
+  <parameters>
+    <parameter key=\"cla\">Fo</parameter>
+    <parameter key=\"ss\">Class</parameter>
+  </parameters>
+  <interfaces>
+    <interface class=\"FooClass\">
+      <call method=\"setBar\">
+        <argument>someValue</argument>
+      </call>
+    </interface>
+  </interfaces>
+  <services>
+    <service id=\"foo\" class=\"%cla%o%ss%\">
+    </service>
+  </services>
+</container>
+", $classBody);
+    }
 }

+ 30 - 0
tests/Symfony/Tests/Component/DependencyInjection/Dumper/YamlDumperTest.php

@@ -12,6 +12,7 @@ namespace Symfony\Tests\Component\DependencyInjection\Dumper;
 
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Dumper\YamlDumper;
+use Symfony\Component\DependencyInjection\InterfaceInjector;
 
 class YamlDumperTest extends \PHPUnit_Framework_TestCase
 {
@@ -55,4 +56,33 @@ class YamlDumperTest extends \PHPUnit_Framework_TestCase
             $this->assertEquals('Unable to dump a service container if a parameter is an object or a resource.', $e->getMessage(), '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources');
         }
     }
+
+    public function testInterfaceInjectors()
+    {
+        $interfaceInjector = new InterfaceInjector('FooClass');
+        $interfaceInjector->addMethodCall('setBar', array('someValue'));
+        $container = include self::$fixturesPath.'/containers/interfaces1.php';
+        $container->addInterfaceInjector($interfaceInjector);
+
+        $dumper = new YamlDumper($container);
+
+        $classBody = $dumper->dump();
+        //TODO: find a better way to test dumper
+        //var_dump($classBody);
+
+        $this->assertEquals("parameters:
+  cla: Fo
+  ss: Class
+
+interfaces:
+    FooClass:
+        calls:
+          - [setBar, [someValue]]
+          
+
+services:
+  foo:
+    class: %cla%o%ss%
+", $classBody);
+    }
 }

+ 23 - 0
tests/Symfony/Tests/Component/DependencyInjection/Fixtures/containers/interfaces1.php

@@ -0,0 +1,23 @@
+<?php
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+
+$container = new ContainerBuilder();
+$container->setParameter('cla', 'Fo');
+$container->setParameter('ss', 'Class');
+
+$definition = new Definition('%cla%o%ss%');
+$container->setDefinition('foo', $definition);
+
+return $container;
+
+class FooClass
+{
+    public $bar;
+
+    public function setBar($bar)
+    {
+        $this->bar = $bar;
+    }
+}

+ 34 - 0
tests/Symfony/Tests/Component/DependencyInjection/Fixtures/containers/interfaces2.php

@@ -0,0 +1,34 @@
+<?php
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+
+$container = new ContainerBuilder();
+
+$factoryDefinition = new Definition('BarClassFactory');
+$container->setDefinition('barFactory', $factoryDefinition);
+
+$definition = new Definition();
+$definition->setFactoryService('barFactory');
+$definition->setFactoryMethod('createBarClass');
+$container->setDefinition('bar', $definition);
+
+return $container;
+
+class BarClass
+{
+    public $foo;
+
+    public function setBar($foo)
+    {
+        $this->foo = $foo;
+    }
+}
+
+class BarClassFactory
+{
+    public function createBarClass()
+    {
+        return new BarClass();
+    }
+}

+ 70 - 0
tests/Symfony/Tests/Component/DependencyInjection/Fixtures/php/services_interfaces-1-1.php

@@ -0,0 +1,70 @@
+<?php
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\DependencyInjection\TaggedContainerInterface;
+use Symfony\Component\DependencyInjection\Container;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\Parameter;
+use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
+
+/**
+ * ProjectServiceContainer
+ *
+ * This class has been auto-generated
+ * by the Symfony Dependency Injection Component.
+ */
+class ProjectServiceContainer extends Container implements TaggedContainerInterface
+{
+    /**
+     * Constructor.
+     */
+    public function __construct()
+    {
+        parent::__construct(new FrozenParameterBag($this->getDefaultParameters()));
+    }
+
+    /**
+     * Gets the 'foo' service.
+     *
+     * This service is shared.
+     * This method always returns the same instance of the service.
+     *
+     * @return FooClass A FooClass instance.
+     */
+    protected function getFooService()
+    {
+        $this->services['foo'] = $instance = new \FooClass();
+        $instance->setBar('someValue');
+
+        return $instance;
+    }
+
+    /**
+     * Returns service ids for a given tag.
+     *
+     * @param string $name The tag name
+     *
+     * @return array An array of tags
+     */
+    public function findTaggedServiceIds($name)
+    {
+        static $tags = array(
+
+        );
+
+        return isset($tags[$name]) ? $tags[$name] : array();
+    }
+
+    /**
+     * Gets the default parameters.
+     *
+     * @return array An array of the default parameters
+     */
+    protected function getDefaultParameters()
+    {
+        return array(
+            'cla' => 'Fo',
+            'ss' => 'Class',
+        );
+    }
+}

+ 82 - 0
tests/Symfony/Tests/Component/DependencyInjection/Fixtures/php/services_interfaces-1.php

@@ -0,0 +1,82 @@
+<?php
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\DependencyInjection\TaggedContainerInterface;
+use Symfony\Component\DependencyInjection\Container;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\Parameter;
+use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
+
+/**
+ * ProjectServiceContainer
+ *
+ * This class has been auto-generated
+ * by the Symfony Dependency Injection Component.
+ */
+class ProjectServiceContainer extends Container implements TaggedContainerInterface
+{
+    /**
+     * Constructor.
+     */
+    public function __construct()
+    {
+        parent::__construct(new ParameterBag($this->getDefaultParameters()));
+    }
+
+    /**
+     * Gets the 'foo' service.
+     *
+     * This service is shared.
+     * This method always returns the same instance of the service.
+     *
+     * @return Object A %cla%o%ss% instance.
+     */
+    protected function getFooService()
+    {
+        $class = $this->getParameter('cla').'o'.$this->getParameter('ss');
+        return $this->services['foo'] = new $class();
+
+        $this->applyInterfaceInjection($instance);
+    }
+
+    /**
+     * Returns service ids for a given tag.
+     *
+     * @param string $name The tag name
+     *
+     * @return array An array of tags
+     */
+    public function findTaggedServiceIds($name)
+    {
+        static $tags = array(
+
+        );
+
+        return isset($tags[$name]) ? $tags[$name] : array();
+    }
+
+    /**
+     * Gets the default parameters.
+     *
+     * @return array An array of the default parameters
+     */
+    protected function getDefaultParameters()
+    {
+        return array(
+            'cla' => 'Fo',
+            'ss' => 'Class',
+        );
+    }
+
+    /**
+     * Applies all known interface injection calls
+     *
+     * @param Object $instance
+     */
+    protected function applyIntrefaceInjectors($instance)
+    {
+        if ($instance instanceof \FooClass) {
+            $instance->setBar('someValue');
+        }
+    }
+}

+ 83 - 0
tests/Symfony/Tests/Component/DependencyInjection/Fixtures/php/services_interfaces-2.php

@@ -0,0 +1,83 @@
+<?php
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\DependencyInjection\TaggedContainerInterface;
+use Symfony\Component\DependencyInjection\Container;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\Parameter;
+use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
+
+/**
+ * ProjectServiceContainer
+ *
+ * This class has been auto-generated
+ * by the Symfony Dependency Injection Component.
+ */
+class ProjectServiceContainer extends Container implements TaggedContainerInterface
+{
+    /**
+     * Constructor.
+     */
+    public function __construct()
+    {
+        parent::__construct(new ParameterBag($this->getDefaultParameters()));
+    }
+
+    /**
+     * Gets the 'barFactory' service.
+     *
+     * This service is shared.
+     * This method always returns the same instance of the service.
+     *
+     * @return BarClassFactory A BarClassFactory instance.
+     */
+    protected function getBarFactoryService()
+    {
+        return $this->services['barFactory'] = new \BarClassFactory();
+
+        $this->applyInterfaceInjection($instance);
+    }
+
+    /**
+     * Gets the 'bar' service.
+     *
+     * This service is shared.
+     * This method always returns the same instance of the service.
+     *
+     * @return Object An instance returned by barFactory::createBarClass().
+     */
+    protected function getBarService()
+    {
+        return $this->services['bar'] = $this->get('barFactory')->createBarClass();
+
+        $this->applyInterfaceInjection($instance);
+    }
+
+    /**
+     * Returns service ids for a given tag.
+     *
+     * @param string $name The tag name
+     *
+     * @return array An array of tags
+     */
+    public function findTaggedServiceIds($name)
+    {
+        static $tags = array(
+
+        );
+
+        return isset($tags[$name]) ? $tags[$name] : array();
+    }
+
+    /**
+     * Applies all known interface injection calls
+     *
+     * @param Object $instance
+     */
+    protected function applyIntrefaceInjectors($instance)
+    {
+        if ($instance instanceof \BarClass) {
+            $instance->setFoo('someValue');
+        }
+    }
+}

+ 17 - 0
tests/Symfony/Tests/Component/DependencyInjection/Fixtures/xml/interfaces1.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" ?>
+
+<container xmlns="http://www.symfony-project.org/schema/dic/services"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd">
+  
+  <services>
+    <service id="foo" class="FooClass" />
+  </services>
+  <interfaces>
+    <interface class="FooClass">
+      <call method="setBar">
+        <argument>correct</argument>
+      </call>
+    </interface>
+  </interfaces>
+</container>

+ 6 - 0
tests/Symfony/Tests/Component/DependencyInjection/Fixtures/yaml/interfaces1.yml

@@ -0,0 +1,6 @@
+services:
+  foo: { class: FooClass }
+interfaces:
+  FooClass:
+    calls:
+      - [ setBar, [ correct ] ]

+ 172 - 0
tests/Symfony/Tests/Component/DependencyInjection/InterfaceInjectorTest.php

@@ -0,0 +1,172 @@
+<?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\Component\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\InterfaceInjector;
+use Symfony\Component\DependencyInjection\Definition;
+
+class InterfaceInjectorTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @covers Symfony\Component\DependencyInjection\InterfaceInjector::addMethodCall
+     * @covers Symfony\Component\DependencyInjection\InterfaceInjector::hasMethodCall
+     * @covers Symfony\Component\DependencyInjection\InterfaceInjector::removeMethodCall
+     * @covers Symfony\Component\DependencyInjection\InterfaceInjector::getMethodCalls
+     *
+     * @dataProvider getMethodCalls
+     *
+     * @param string $method
+     * @param array $arguments
+     */
+    public function testAddRemoveGetMethodCalls($method, array $arguments = array())
+    {
+        $injector = new InterfaceInjector('stdClass');
+
+        $injector->addMethodCall($method, $arguments);
+        $this->assertTrue($injector->hasMethodCall($method), '->hasMethodCall() returns true for methods that were added on InterfaceInjector');
+
+        $methodCalls = $injector->getMethodCalls();
+        $this->assertEquals(1, count($methodCalls), '->getMethodCalls() returns array, where each entry is a method call');
+        $this->assertEquals(array($method, $arguments), $methodCalls[0], '->getMethodCalls() has all methods added to InterfaceInjector instance');
+
+        $injector->removeMethodCall($method);
+        $this->assertFalse($injector->hasMethodCall($method), '->removeMethodClass() deletes the method call from InterfaceInjector');
+    }
+
+    /**
+     * @covers Symfony\Component\DependencyInjection\InterfaceInjector::processDefinition
+     *
+     * @dataProvider getInjectorsAndDefintions
+     *
+     * @param InterfaceInjector $injector
+     * @param Definition $definition
+     * @param int $expectedMethodsCount
+     */
+    public function testProcessDefinition(InterfaceInjector $injector, Definition $definition)
+    {
+        $injector->processDefinition($definition);
+    }
+
+    /**
+     * @covers Symfony\Component\DependencyInjection\InterfaceInjector::supported
+     *
+     * @dataProvider getInjectorsAndClasses
+     *
+     * @param InterfaceInjector $injector
+     * @param string $class
+     * @param string $expectedResult
+     */
+    public function testSupported(InterfaceInjector $injector, $class, $expectedResult)
+    {
+        $this->assertEquals($expectedResult, $injector->supports($class), '->supports() must return true if injector is to be used on a class, false otherwise');
+    }
+
+    /**
+     * @covers Symfony\Component\DependencyInjection\InterfaceInjector::processDefinition
+     */
+    public function testProcessesDefinitionOnlyOnce()
+    {
+        $injector = new InterfaceInjector('Symfony\Tests\Component\DependencyInjection\Service');
+        $injector->addMethodCall('method');
+
+        $definition = $this->getMockDefinition('Symfony\Tests\Component\DependencyInjection\Service', 1);
+
+        $injector->processDefinition($definition);
+        $injector->processDefinition($definition);
+    }
+
+    /**
+     * @covers Symfony\Component\DependencyInjection\InterfaceInjector::merge
+     */
+    public function testMerge()
+    {
+        $injector1 = new InterfaceInjector('Symfony\Tests\Component\DependencyInjection\Service');
+        $injector1->addMethodCall('method_one');
+
+        $injector2 = new InterfaceInjector('Symfony\Tests\Component\DependencyInjection\Service');
+        $injector2->addMethodCall('method_two');
+
+        $injector1->merge($injector2);
+
+        $methodCalls = $injector1->getMethodCalls();
+        $this->assertEquals(2, count($methodCalls));
+        $this->assertEquals(array(
+            array('method_one', array()),
+            array('method_two', array()),
+        ), $methodCalls);
+    }
+
+    /**
+     * @expectedException Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
+     */
+    public function testSupportsThrowsExceptionOnInvalidArgument()
+    {
+        $injector = new InterfaceInjector('Symfony\Tests\Component\DependencyInjection\Service');
+        $injector->supports(array());
+    }
+
+    public function getMethodCalls()
+    {
+        return array(
+            array('method', array()),
+            array('method2', array('one', 'two')),
+            array('method3', array('single')),
+        );
+    }
+
+    public function getInjectorsAndDefintions()
+    {
+        $injector = new InterfaceInjector('Symfony\Tests\Component\DependencyInjection\Service');
+        $injector->addMethodCall('method');
+        $injector->addMethodCall('method');
+        $injector->addMethodCall('method');
+        $injector->addMethodCall('method');
+
+        $definition1 = $this->getMockDefinition('stdClass', 0);
+        $definition2 = $this->getMockDefinition('Symfony\Tests\Component\DependencyInjection\Service', 4);
+
+        return array(
+            array($injector, $definition1),
+            array($injector, $definition2),
+        );
+    }
+
+    public function getInjectorsAndClasses()
+    {
+        return array(
+            array(new InterfaceInjector('Symfony\Tests\Component\DependencyInjection\Service'), 'Symfony\Tests\Component\DependencyInjection\Service', true),
+            array(new InterfaceInjector('Symfony\Tests\Component\DependencyInjection\SubService'), 'Symfony\Tests\Component\DependencyInjection\Service', false),
+            array(new InterfaceInjector('Symfony\Tests\Component\DependencyInjection\Service'), 'Symfony\Tests\Component\DependencyInjection\SubService', true),
+            array(new InterfaceInjector('Symfony\Tests\Component\DependencyInjection\SubService'), 'Symfony\Tests\Component\DependencyInjection\SubService', true),
+        );
+    }
+
+    /**
+     * @param string $class
+     * @param int $methodCount
+     * @return Symfony\Component\DependencyInjection\Definition
+     */
+    private function getMockDefinition($class, $methodCount)
+    {
+        $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition');
+        $definition->expects($this->once())
+            ->method('getClass')
+            ->will($this->returnValue($class))
+        ;
+        $definition->expects($this->exactly($methodCount))
+            ->method('addMethodCall')
+        ;
+        return $definition;
+    }
+}
+
+class Service {}
+class SubService extends Service {}

+ 12 - 0
tests/Symfony/Tests/Component/DependencyInjection/Loader/XmlFileLoaderTest.php

@@ -26,6 +26,7 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
     static public function setUpBeforeClass()
     {
         self::$fixturesPath = realpath(__DIR__.'/../Fixtures/');
+        require_once self::$fixturesPath.'/includes/foo.php';
         require_once self::$fixturesPath.'/includes/ProjectExtension.php';
         require_once self::$fixturesPath.'/includes/ProjectWithXsdExtension.php';
     }
@@ -245,6 +246,17 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
         $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');
     }
+
+    public function testLoadInterfaceInjectors()
+    {
+        $container = new ContainerBuilder();
+        $loader = new ProjectLoader2($container, self::$fixturesPath.'/xml');
+        $loader->load('interfaces1.xml');
+        $interfaces = $container->getInterfaceInjectors('FooClass');
+        $this->assertEquals(1, count($interfaces), '->load() parses <interface> elements');
+        $interface = $interfaces['FooClass'];
+        $this->assertTrue($interface->hasMethodCall('setBar'), '->load() applies method calls correctly');
+    }
 }
 
 class ProjectLoader2 extends XmlFileLoader

+ 12 - 0
tests/Symfony/Tests/Component/DependencyInjection/Loader/YamlFileLoaderTest.php

@@ -26,6 +26,7 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
     static public function setUpBeforeClass()
     {
         self::$fixturesPath = realpath(__DIR__.'/../Fixtures/');
+        require_once self::$fixturesPath.'/includes/foo.php';
         require_once self::$fixturesPath.'/includes/ProjectExtension.php';
     }
 
@@ -155,6 +156,17 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
         $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');
     }
+
+    public function testLoadInterfaceInjectors()
+    {
+        $container = new ContainerBuilder();
+        $loader = new ProjectLoader3($container, self::$fixturesPath.'/yaml');
+        $loader->load('interfaces1.yml');
+        $interfaces = $container->getInterfaceInjectors('FooClass');
+        $this->assertEquals(1, count($interfaces), '->load() parses interfaces');
+        $interface = $interfaces['FooClass'];
+        $this->assertTrue($interface->hasMethodCall('setBar'), '->load() parses interfaces elements');
+    }
 }
 
 class ProjectLoader3 extends YamlFileLoader