Skip to content

[WIP] [DependencyInjection] added support for expressions in the service container #8850

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Symfony/Component/DependencyInjection/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
CHANGELOG
=========

2.4.0
-----

* added support for expressions in service definitions

2.2.0
-----

Expand Down
20 changes: 18 additions & 2 deletions src/Symfony/Component/DependencyInjection/ContainerBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -78,6 +79,11 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
*/
private $proxyInstantiator;

/**
* @var PropertyAccess|null
*/
private $propertyAccessor;

/**
* Sets the track resources flag.
*
Expand Down Expand Up @@ -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)
{
Expand All @@ -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;
Expand Down
17 changes: 17 additions & 0 deletions src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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

/**
Expand All @@ -732,6 +734,7 @@ private function startClass($class, $baseClass, $namespace)
*/
class $class extends $baseClass
{
private \$propertyAccessor;
EOF;
}

Expand Down Expand Up @@ -991,6 +994,18 @@ private function exportParameters($parameters, $path = '', $indent = 12)
private function endClass()
{
return <<<EOF

private function getPropertyAccessor()
{
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();
}

return \$this->propertyAccessor;
}
}

EOF;
Expand Down Expand Up @@ -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());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you need to escape quotes in the path to be safe (as you don't have any validation of the path before dumping). Otherwise, it could create some fatal error in the dumped code. I suggest using var_export to build the dumped string instead of just adding quotes around it

} elseif ($value instanceof Parameter) {
return $this->dumpParameter($value);
} elseif (true === $interpolate && is_string($value)) {
Expand Down
55 changes: 55 additions & 0 deletions src/Symfony/Component/DependencyInjection/Expression.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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 <fabien@symfony.com>
*/
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@
<xsd:attribute name="key" type="xsd:string" />
<xsd:attribute name="index" type="xsd:integer" />
<xsd:attribute name="on-invalid" type="xsd:string" />
<xsd:attribute name="path" type="xsd:string" />
<xsd:attribute name="strict" type="boolean" />
</xsd:complexType>

Expand All @@ -164,6 +165,7 @@
<xsd:restriction base="xsd:string">
<xsd:enumeration value="collection" />
<xsd:enumeration value="service" />
<xsd:enumeration value="expression" />
<xsd:enumeration value="string" />
<xsd:enumeration value="constant" />
</xsd:restriction>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down