diff --git a/src/Symfony/Components/DependencyInjection/AnnotatedContainerInterface.php b/src/Symfony/Components/DependencyInjection/AnnotatedContainerInterface.php new file mode 100644 index 000000000000..4946123e7efa --- /dev/null +++ b/src/Symfony/Components/DependencyInjection/AnnotatedContainerInterface.php @@ -0,0 +1,31 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * AnnotatedContainerInterface is the interface implemented when a container knows how to deals with annotations. + * + * @package symfony + * @subpackage dependency_injection + * @author Fabien Potencier + */ +interface AnnotatedContainerInterface +{ + /** + * Returns service ids for a given annotation. + * + * @param string $name The annotation name + * + * @return array An array of annotations + */ + public function findAnnotatedServiceIds($name); +} diff --git a/src/Symfony/Components/DependencyInjection/Builder.php b/src/Symfony/Components/DependencyInjection/Builder.php index 53f4b1738323..2082e184c30e 100644 --- a/src/Symfony/Components/DependencyInjection/Builder.php +++ b/src/Symfony/Components/DependencyInjection/Builder.php @@ -18,7 +18,7 @@ * @subpackage dependency_injection * @author Fabien Potencier */ -class Builder extends Container +class Builder extends Container implements AnnotatedContainerInterface { protected $definitions = array(); protected $aliases = array(); @@ -481,6 +481,27 @@ public function resolveServices($value) return $value; } + /** + * Returns service ids for a given annotation. + * + * @param string $name The annotation name + * + * @return array An array of annotations + */ + public function findAnnotatedServiceIds($name) + { + $annotations = array(); + foreach ($this->getDefinitions() as $id => $definition) + { + if ($definition->getAnnotation($name)) + { + $annotations[$id] = $definition->getAnnotation($name); + } + } + + return $annotations; + } + static public function getServiceConditionals($value) { $services = array(); diff --git a/src/Symfony/Components/DependencyInjection/Definition.php b/src/Symfony/Components/DependencyInjection/Definition.php index 9411e010aec5..f62420347891 100644 --- a/src/Symfony/Components/DependencyInjection/Definition.php +++ b/src/Symfony/Components/DependencyInjection/Definition.php @@ -27,6 +27,7 @@ class Definition protected $arguments; protected $calls; protected $configurator; + protected $annotations; /** * Constructor. @@ -40,6 +41,7 @@ public function __construct($class, array $arguments = array()) $this->arguments = $arguments; $this->calls = array(); $this->shared = true; + $this->annotations = array(); } /** @@ -171,6 +173,53 @@ public function getMethodCalls() return $this->calls; } + /** + * Returns all annotations. + * + * @return array An array of annotations + */ + public function getAnnotations() + { + return $this->annotations; + } + + /** + * Gets an annotation by name. + * + * @param string $name The annotation name + * + * @return array $attributes An array of attributes + */ + public function getAnnotation($name) + { + if (!isset($this->annotations[$name])) + { + $this->annotations[$name] = array(); + } + + return $this->annotations[$name]; + } + + /** + * Adds an annotation for this definition. + * + * @param string $name The annotation name + * @param array $attributes An array of attributes + * + * @return Definition The current instance + */ + public function addAnnotation($name, array $attributes = array()) + { + if (!isset($this->annotations[$name])) + { + $this->annotations[$name] = array(); + } + + $this->annotations[$name][] = $attributes; + + return $this; + } + /** * Sets a file to require before creating the service. * diff --git a/src/Symfony/Components/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Components/DependencyInjection/Dumper/PhpDumper.php index 09e7c1096408..79541db02103 100644 --- a/src/Symfony/Components/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Components/DependencyInjection/Dumper/PhpDumper.php @@ -48,6 +48,7 @@ public function dump(array $options = array()) $this->startClass($options['class'], $options['base_class']). $this->addConstructor(). $this->addServices(). + $this->addAnnotations(). $this->addDefaultParametersMethod(). $this->endClass() ; @@ -246,6 +247,42 @@ protected function addServices() return $code; } + protected function addAnnotations() + { + $annotations = array(); + foreach ($this->container->getDefinitions() as $id => $definition) + { + foreach ($definition->getAnnotations() as $name => $ann) + { + if (!isset($annotations[$name])) + { + $annotations[$name] = array(); + } + + $annotations[$name][$id] = $ann; + } + } + $annotations = var_export($annotations, true); + + return <<isShared() ? ' shared="false"' : '' ); + foreach ($definition->getAnnotations() as $name => $annotations) + { + foreach ($annotations as $attributes) + { + $att = array(); + foreach ($attributes as $key => $value) + { + $att[] = sprintf('%s="%s"', $key, $value); + } + $att = $att ? ' '.implode(' ', $att) : ''; + + $code .= sprintf(" \n", $name, $att); + } + } + if ($definition->getFile()) { $code .= sprintf(" %s\n", $definition->getFile()); diff --git a/src/Symfony/Components/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Components/DependencyInjection/Dumper/YamlDumper.php index b69bb323d86d..c02b28a48200 100644 --- a/src/Symfony/Components/DependencyInjection/Dumper/YamlDumper.php +++ b/src/Symfony/Components/DependencyInjection/Dumper/YamlDumper.php @@ -42,6 +42,26 @@ protected function addService($id, $definition) $code = " $id:\n"; $code .= sprintf(" class: %s\n", $definition->getClass()); + $annotationsCode = ''; + foreach ($definition->getAnnotations() as $name => $annotations) + { + foreach ($annotations as $attributes) + { + $att = array(); + foreach ($attributes as $key => $value) + { + $att[] = sprintf('%s: %s', YAML::dump($key), YAML::dump($value)); + } + $att = $att ? ', '.implode(' ', $att) : ''; + + $annotationsCode .= sprintf(" - { name: %s%s }\n", YAML::dump($name), $att); + } + } + if ($annotationsCode) + { + $code .= " annotations:\n".$annotationsCode; + } + if ($definition->getFile()) { $code .= sprintf(" file: %s\n", $definition->getFile()); diff --git a/src/Symfony/Components/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Components/DependencyInjection/Loader/XmlFileLoader.php index 3541cf76bf41..b15e484df004 100644 --- a/src/Symfony/Components/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Components/DependencyInjection/Loader/XmlFileLoader.php @@ -178,6 +178,22 @@ protected function parseDefinition(BuilderConfiguration $configuration, $id, $se $definition->addMethodCall((string) $call['method'], $call->getArgumentsAsPhp('argument')); } + foreach ($service->annotation as $annotation) + { + $parameters = array(); + foreach ($annotation->attributes() as $name => $value) + { + if ('name' === $name) + { + continue; + } + + $parameters[$name] = SimpleXMLElement::phpize($value); + } + + $definition->addAnnotation((string) $annotation['name'], $parameters); + } + $configuration->setDefinition($id, $definition); } diff --git a/src/Symfony/Components/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Components/DependencyInjection/Loader/YamlFileLoader.php index d10f9e03469c..96b94600b736 100644 --- a/src/Symfony/Components/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Components/DependencyInjection/Loader/YamlFileLoader.php @@ -177,6 +177,17 @@ protected function parseDefinition(BuilderConfiguration $configuration, $id, $se } } + if (isset($service['annotations'])) + { + foreach ($service['annotations'] as $annotation) + { + $name = $annotation['name']; + unset($annotation['name']); + + $definition->addAnnotation($name, $annotation); + } + } + $configuration->setDefinition($id, $definition); } diff --git a/src/Symfony/Components/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Components/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd index dad9e50eaaf1..ede72cc75bf1 100644 --- a/src/Symfony/Components/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd +++ b/src/Symfony/Components/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd @@ -84,6 +84,7 @@ + @@ -92,6 +93,11 @@ + + + + + diff --git a/tests/fixtures/Symfony/Components/DependencyInjection/containers/container9.php b/tests/fixtures/Symfony/Components/DependencyInjection/containers/container9.php index 0afbe3ef2789..b7ac845b7173 100644 --- a/tests/fixtures/Symfony/Components/DependencyInjection/containers/container9.php +++ b/tests/fixtures/Symfony/Components/DependencyInjection/containers/container9.php @@ -10,6 +10,8 @@ $container = new Builder(); $container-> register('foo', 'FooClass')-> + addAnnotation('foo', array('foo' => 'foo'))-> + addAnnotation('foo', array('bar' => 'bar'))-> setConstructor('getInstance')-> setArguments(array('foo', new Reference('foo.baz'), array('%foo%' => 'foo is %foo%', 'bar' => '%foo%'), true, new Reference('service_container')))-> setFile(realpath(__DIR__.'/../includes/foo.php'))-> diff --git a/tests/fixtures/Symfony/Components/DependencyInjection/php/services1-1.php b/tests/fixtures/Symfony/Components/DependencyInjection/php/services1-1.php index 770103b58918..bb00fbff64a7 100644 --- a/tests/fixtures/Symfony/Components/DependencyInjection/php/services1-1.php +++ b/tests/fixtures/Symfony/Components/DependencyInjection/php/services1-1.php @@ -13,4 +13,19 @@ class Container extends AbstractContainer { protected $shared = array(); + + /** + * Returns service ids for a given annotation. + * + * @param string $name The annotation name + * + * @return array An array of annotations + */ + public function findAnnotatedServiceIds($name) + { + static $annotations = array ( +); + + return isset($annotations[$name]) ? $annotations[$name] : array(); + } } diff --git a/tests/fixtures/Symfony/Components/DependencyInjection/php/services1.php b/tests/fixtures/Symfony/Components/DependencyInjection/php/services1.php index b4413fbb4836..81951d060de9 100644 --- a/tests/fixtures/Symfony/Components/DependencyInjection/php/services1.php +++ b/tests/fixtures/Symfony/Components/DependencyInjection/php/services1.php @@ -13,4 +13,19 @@ class ProjectServiceContainer extends Container { protected $shared = array(); + + /** + * Returns service ids for a given annotation. + * + * @param string $name The annotation name + * + * @return array An array of annotations + */ + public function findAnnotatedServiceIds($name) + { + static $annotations = array ( +); + + return isset($annotations[$name]) ? $annotations[$name] : array(); + } } diff --git a/tests/fixtures/Symfony/Components/DependencyInjection/php/services8.php b/tests/fixtures/Symfony/Components/DependencyInjection/php/services8.php index d592a6fee85e..b8c7f6afc7ab 100644 --- a/tests/fixtures/Symfony/Components/DependencyInjection/php/services8.php +++ b/tests/fixtures/Symfony/Components/DependencyInjection/php/services8.php @@ -24,6 +24,21 @@ public function __construct() $this->parameters = $this->getDefaultParameters(); } + /** + * Returns service ids for a given annotation. + * + * @param string $name The annotation name + * + * @return array An array of annotations + */ + public function findAnnotatedServiceIds($name) + { + static $annotations = array ( +); + + return isset($annotations[$name]) ? $annotations[$name] : array(); + } + /** * Gets the default parameters. * diff --git a/tests/fixtures/Symfony/Components/DependencyInjection/php/services9.php b/tests/fixtures/Symfony/Components/DependencyInjection/php/services9.php index 1ea12578b3ef..2307c8b857d2 100644 --- a/tests/fixtures/Symfony/Components/DependencyInjection/php/services9.php +++ b/tests/fixtures/Symfony/Components/DependencyInjection/php/services9.php @@ -139,6 +139,35 @@ protected function getAliasForFooService() return $this->getFooService(); } + /** + * Returns service ids for a given annotation. + * + * @param string $name The annotation name + * + * @return array An array of annotations + */ + public function findAnnotatedServiceIds($name) + { + static $annotations = array ( + 'foo' => + array ( + 'foo' => + array ( + 0 => + array ( + 'foo' => 'foo', + ), + 1 => + array ( + 'bar' => 'bar', + ), + ), + ), +); + + return isset($annotations[$name]) ? $annotations[$name] : array(); + } + /** * Gets the default parameters. * diff --git a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services9.xml b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services9.xml index 15ec9120d916..a89e56f50ba1 100644 --- a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services9.xml +++ b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services9.xml @@ -10,6 +10,8 @@ + + %path%/foo.php foo diff --git a/tests/fixtures/Symfony/Components/DependencyInjection/yaml/services9.yml b/tests/fixtures/Symfony/Components/DependencyInjection/yaml/services9.yml index aafde657fb63..cfa08a301798 100644 --- a/tests/fixtures/Symfony/Components/DependencyInjection/yaml/services9.yml +++ b/tests/fixtures/Symfony/Components/DependencyInjection/yaml/services9.yml @@ -6,6 +6,9 @@ parameters: services: foo: class: FooClass + annotations: + - { name: foo, foo: foo } + - { name: foo, bar: bar } file: %path%/foo.php constructor: getInstance arguments: [foo, '@foo.baz', { '%foo%': 'foo is %foo%', bar: '%foo%' }, true, '@service_container'] diff --git a/tests/unit/Symfony/Components/DependencyInjection/BuilderTest.php b/tests/unit/Symfony/Components/DependencyInjection/BuilderTest.php index 46f7eedce615..216082f2a056 100644 --- a/tests/unit/Symfony/Components/DependencyInjection/BuilderTest.php +++ b/tests/unit/Symfony/Components/DependencyInjection/BuilderTest.php @@ -17,7 +17,7 @@ $fixturesPath = __DIR__.'/../../../../fixtures/Symfony/Components/DependencyInjection/'; -$t = new LimeTest(59); +$t = new LimeTest(61); // ->setDefinitions() ->addDefinitions() ->getDefinitions() ->setDefinition() ->getDefinition() ->hasDefinition() $t->diag('->setDefinitions() ->addDefinitions() ->getDefinitions() ->setDefinition() ->getDefinition() ->hasDefinition()'); @@ -290,3 +290,20 @@ $config->setDefinition('foo', new Definition('BazClass')); $container->merge($config); $t->is($container->getDefinition('foo')->getClass(), 'BazClass', '->merge() overrides already defined services'); + +// ->findAnnotatedServiceIds() +$t->diag('->findAnnotatedServiceIds()'); +$builder = new Builder(); +$builder + ->register('foo', 'FooClass') + ->addAnnotation('foo', array('foo' => 'foo')) + ->addAnnotation('bar', array('bar' => 'bar')) + ->addAnnotation('foo', array('foofoo' => 'foofoo')) +; +$t->is($builder->findAnnotatedServiceIds('foo'), array( + 'foo' => array( + array('foo' => 'foo'), + array('foofoo' => 'foofoo'), + ) +), '->findAnnotatedServiceIds() returns an array of service ids and its annotation attributes'); +$t->is($builder->findAnnotatedServiceIds('foobar'), array(), '->findAnnotatedServiceIds() returns an empty array if there is annotated services'); diff --git a/tests/unit/Symfony/Components/DependencyInjection/DefinitionTest.php b/tests/unit/Symfony/Components/DependencyInjection/DefinitionTest.php index d9da8643755b..1cb1f8d77719 100644 --- a/tests/unit/Symfony/Components/DependencyInjection/DefinitionTest.php +++ b/tests/unit/Symfony/Components/DependencyInjection/DefinitionTest.php @@ -12,7 +12,7 @@ use Symfony\Components\DependencyInjection\Definition; -$t = new LimeTest(21); +$t = new LimeTest(25); // __construct() $t->diag('__construct()'); @@ -69,3 +69,16 @@ $def = new Definition('stdClass'); $t->is(spl_object_hash($def->setConfigurator('foo')), spl_object_hash($def), '->setConfigurator() implements a fluent interface'); $t->is($def->getConfigurator(), 'foo', '->getConfigurator() returns the configurator'); + +// ->getAnnotations() ->getAnnotation() ->addAnnotation() +$t->diag('->getAnnotations() ->getAnnotation() ->addAnnotation()'); +$def = new Definition('stdClass'); +$t->is(spl_object_hash($def->addAnnotation('foo')), spl_object_hash($def), '->addAnnotation() implements a fluent interface'); +$t->is($def->getAnnotation('foo'), array(array()), '->getAnnotation() returns attributes for an annotation name'); +$def->addAnnotation('foo', array('foo' => 'bar')); +$t->is($def->getAnnotation('foo'), array(array(), array('foo' => 'bar')), '->addAnnotation() can adds the same annotation several times'); +$def->addAnnotation('bar', array('bar' => 'bar')); +$t->is($def->getAnnotations(), array( + 'foo' => array(array(), array('foo' => 'bar')), + 'bar' => array(array('bar' => 'bar')), +), '->getAnnotations() returns all annotations');