From 4a01b5b1990347642c30972a0b577183524ce2dc Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 25 Aug 2013 10:53:27 +0200 Subject: [PATCH] [DependencyInjection] added support for expressions in the service container --- .../DependencyInjection/CHANGELOG.md | 5 ++ .../DependencyInjection/ContainerBuilder.php | 20 ++++++- .../DependencyInjection/Dumper/PhpDumper.php | 17 ++++++ .../DependencyInjection/Expression.php | 55 +++++++++++++++++++ .../Loader/YamlFileLoader.php | 6 ++ .../schema/dic/services/services-1.0.xsd | 2 + .../DependencyInjection/SimpleXMLElement.php | 3 + 7 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Expression.php diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index 4839bd8bee7b4..16986d9540946 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +2.4.0 +----- + + * added support for expressions in service definitions + 2.2.0 ----- diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 733d016d66085..b30a6a45be59a 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -24,6 +24,7 @@ use Symfony\Component\Config\Resource\ResourceInterface; use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface; use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator; +use Symfony\Component\PropertyAccess\PropertyAccess; /** * ContainerBuilder is a DI container that provides an API to easily describe services. @@ -78,6 +79,11 @@ class ContainerBuilder extends Container implements TaggedContainerInterface */ private $proxyInstantiator; + /** + * @var PropertyAccess|null + */ + private $propertyAccessor; + /** * Sets the track resources flag. * @@ -993,11 +999,12 @@ public function createService(Definition $definition, $id, $tryProxy = true) } /** - * Replaces service references by the real service instance. + * Replaces service references by the real service instance and evaluates expressions. * * @param mixed $value A value * - * @return mixed The same value with all service references replaced by the real service instances + * @return mixed The same value with all service references replaced by + * the real service instances and all expressions evaluated */ public function resolveServices($value) { @@ -1009,6 +1016,15 @@ public function resolveServices($value) $value = $this->get((string) $value, $value->getInvalidBehavior()); } elseif ($value instanceof Definition) { $value = $this->createService($value, null); + } elseif ($value instanceof Expression) { + if (null === $this->propertyAccessor) { + if (!class_exists('Symfony\Component\PropertyAccess\PropertyAccess')) { + throw new RuntimeException('Unable to use expressions as the Symfony PropertyAccess component is not installed.'); + } + $this->propertyAccessor = PropertyAccess::createPropertyAccessor(); + } + + $value = $this->propertyAccessor->getValue($this->get($value->getId()), $value->getPath()); } return $value; diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index e92960e21ed70..27621cdae0b6c 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -17,6 +17,7 @@ use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Expression; use Symfony\Component\DependencyInjection\Parameter; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; @@ -722,6 +723,7 @@ private function startClass($class, $baseClass, $namespace) use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\PropertyAccess\PropertyAccess; $bagClass /** @@ -732,6 +734,7 @@ private function startClass($class, $baseClass, $namespace) */ class $class extends $baseClass { + private \$propertyAccessor; EOF; } @@ -991,6 +994,18 @@ private function exportParameters($parameters, $path = '', $indent = 12) private function endClass() { return <<propertyAccessor) { + if (!class_exists('Symfony\Component\PropertyAccess\PropertyAccess')) { + throw new RuntimeException('Unable to use expressions as the Symfony PropertyAccess component is not installed.'); + } + \$this->propertyAccessor = PropertyAccess::createPropertyAccessor(); + } + + return \$this->propertyAccessor; + } } EOF; @@ -1197,6 +1212,8 @@ private function dumpValue($value, $interpolate = true) } return $this->getServiceCall((string) $value, $value); + } elseif ($value instanceof Expression) { + return sprintf("\$this->getPropertyAccessor()->getValue(%s, '%s')", $this->dumpValue(new Reference($value->getId())), $value->getPath()); } elseif ($value instanceof Parameter) { return $this->dumpParameter($value); } elseif (true === $interpolate && is_string($value)) { diff --git a/src/Symfony/Component/DependencyInjection/Expression.php b/src/Symfony/Component/DependencyInjection/Expression.php new file mode 100644 index 0000000000000..7c968ec3111ac --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Expression.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * @author Fabien Potencier + */ +class Expression +{ + private $id; + private $path; + + /** + * Constructor. + * + * @param string $id A service identifier + * @param string $path A PropertyAccess path + */ + public function __construct($id, $path) + { + $this->id = $id; + $this->path = $path; + } + + /** + * Gets the service identifier of the expression. + * + * @return string The service identifier + */ + public function getId() + { + return $this->id; + } + + /** + * Gets the property path. + * + * @return string The property path + */ + public function getPath() + { + return $this->path; + } +} diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index ce135fa81f929..0745bdffc3155 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -16,6 +16,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Expression; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Yaml\Parser as YamlParser; @@ -312,6 +313,11 @@ private function resolveServices($value) if (is_array($value)) { $value = array_map(array($this, 'resolveServices'), $value); } elseif (is_string($value) && 0 === strpos($value, '@')) { + // is it an expression? + if (false !== $pos = strpos($value, '::')) { + return new Expression(substr($value, 1, $pos - 1), substr($value, $pos + 2)); + } + if (0 === strpos($value, '@@')) { $value = substr($value, 1); $invalidBehavior = null; diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd index f1c2003c62258..b0d6b6678b401 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd +++ b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd @@ -140,6 +140,7 @@ + @@ -164,6 +165,7 @@ + diff --git a/src/Symfony/Component/DependencyInjection/SimpleXMLElement.php b/src/Symfony/Component/DependencyInjection/SimpleXMLElement.php index cc5e311987151..8190fdda2f238 100644 --- a/src/Symfony/Component/DependencyInjection/SimpleXMLElement.php +++ b/src/Symfony/Component/DependencyInjection/SimpleXMLElement.php @@ -77,6 +77,9 @@ public function getArgumentsAsPhp($name, $lowercase = true) $arguments[$key] = new Reference((string) $arg['id'], $invalidBehavior, $strict); break; + case 'expression': + $arguments[$key] = new Expression((string) $arg['id'], (string) $arg['path']); + break; case 'collection': $arguments[$key] = $arg->getArgumentsAsPhp($name, false); break;