2011-01-26 23:14:31 +00:00
|
|
|
<?php
|
|
|
|
|
2011-05-31 09:57:06 +01:00
|
|
|
/*
|
2012-03-31 22:00:32 +01:00
|
|
|
* This file is part of the Symfony package.
|
2011-05-31 09:57:06 +01:00
|
|
|
*
|
|
|
|
* (c) Fabien Potencier <fabien@symfony.com>
|
|
|
|
*
|
2012-03-31 22:00:32 +01:00
|
|
|
* For the full copyright and license information, please view the LICENSE
|
|
|
|
* file that was distributed with this source code.
|
2011-05-31 09:57:06 +01:00
|
|
|
*/
|
|
|
|
|
2011-01-26 23:14:31 +00:00
|
|
|
namespace Symfony\Component\DependencyInjection\Compiler;
|
|
|
|
|
2016-11-28 07:50:06 +00:00
|
|
|
use Symfony\Component\DependencyInjection\ChildDefinition;
|
2011-01-26 23:14:31 +00:00
|
|
|
use Symfony\Component\DependencyInjection\Definition;
|
2016-09-09 11:12:11 +01:00
|
|
|
use Symfony\Component\DependencyInjection\Exception\ExceptionInterface;
|
2011-12-04 23:51:22 +00:00
|
|
|
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
2018-09-16 13:04:45 +01:00
|
|
|
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
|
2011-01-26 23:14:31 +00:00
|
|
|
|
|
|
|
/**
|
2016-11-28 07:50:06 +00:00
|
|
|
* This replaces all ChildDefinition instances with their equivalent fully
|
2011-01-26 23:14:31 +00:00
|
|
|
* merged Definition instance.
|
|
|
|
*
|
|
|
|
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
2015-06-24 09:29:42 +01:00
|
|
|
* @author Nicolas Grekas <p@tchwork.com>
|
2011-01-26 23:14:31 +00:00
|
|
|
*/
|
2017-09-07 09:34:20 +01:00
|
|
|
class ResolveChildDefinitionsPass extends AbstractRecursivePass
|
2011-01-26 23:14:31 +00:00
|
|
|
{
|
2018-09-16 13:04:45 +01:00
|
|
|
private $currentPath;
|
|
|
|
|
2017-01-17 20:04:53 +00:00
|
|
|
protected function processValue($value, $isRoot = false)
|
2015-06-24 09:29:42 +01:00
|
|
|
{
|
2017-01-17 20:04:53 +00:00
|
|
|
if (!$value instanceof Definition) {
|
|
|
|
return parent::processValue($value, $isRoot);
|
|
|
|
}
|
|
|
|
if ($isRoot) {
|
|
|
|
// yes, we are specifically fetching the definition from the
|
|
|
|
// container to ensure we are not operating on stale data
|
|
|
|
$value = $this->container->getDefinition($this->currentId);
|
|
|
|
}
|
|
|
|
if ($value instanceof ChildDefinition) {
|
2019-01-16 09:39:14 +00:00
|
|
|
$this->currentPath = [];
|
2017-01-17 20:04:53 +00:00
|
|
|
$value = $this->resolveDefinition($value);
|
2015-06-24 09:29:42 +01:00
|
|
|
if ($isRoot) {
|
2017-01-17 20:04:53 +00:00
|
|
|
$this->container->setDefinition($this->currentId, $value);
|
2015-06-24 09:29:42 +01:00
|
|
|
}
|
2011-01-26 23:14:31 +00:00
|
|
|
}
|
2015-06-24 09:29:42 +01:00
|
|
|
|
2017-01-17 20:04:53 +00:00
|
|
|
return parent::processValue($value, $isRoot);
|
2011-01-26 23:14:31 +00:00
|
|
|
}
|
|
|
|
|
2011-02-13 18:06:41 +00:00
|
|
|
/**
|
2014-12-21 17:00:50 +00:00
|
|
|
* Resolves the definition.
|
2011-02-13 18:06:41 +00:00
|
|
|
*
|
|
|
|
* @return Definition
|
2012-09-07 23:56:45 +01:00
|
|
|
*
|
2017-01-17 20:04:53 +00:00
|
|
|
* @throws RuntimeException When the definition is invalid
|
2011-02-13 18:06:41 +00:00
|
|
|
*/
|
2017-01-17 20:04:53 +00:00
|
|
|
private function resolveDefinition(ChildDefinition $definition)
|
2016-09-09 11:12:11 +01:00
|
|
|
{
|
|
|
|
try {
|
2017-01-17 20:04:53 +00:00
|
|
|
return $this->doResolveDefinition($definition);
|
2018-09-16 13:04:45 +01:00
|
|
|
} catch (ServiceCircularReferenceException $e) {
|
|
|
|
throw $e;
|
2016-09-09 11:12:11 +01:00
|
|
|
} catch (ExceptionInterface $e) {
|
|
|
|
$r = new \ReflectionProperty($e, 'message');
|
|
|
|
$r->setAccessible(true);
|
|
|
|
$r->setValue($e, sprintf('Service "%s": %s', $this->currentId, $e->getMessage()));
|
|
|
|
|
|
|
|
throw $e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-17 20:04:53 +00:00
|
|
|
private function doResolveDefinition(ChildDefinition $definition)
|
2011-01-26 23:14:31 +00:00
|
|
|
{
|
2017-01-17 20:04:53 +00:00
|
|
|
if (!$this->container->has($parent = $definition->getParent())) {
|
2016-09-09 11:12:11 +01:00
|
|
|
throw new RuntimeException(sprintf('Parent definition "%s" does not exist.', $parent));
|
2011-01-26 23:14:31 +00:00
|
|
|
}
|
|
|
|
|
2018-09-16 13:04:45 +01:00
|
|
|
$searchKey = array_search($parent, $this->currentPath);
|
|
|
|
$this->currentPath[] = $parent;
|
|
|
|
|
|
|
|
if (false !== $searchKey) {
|
|
|
|
throw new ServiceCircularReferenceException($parent, \array_slice($this->currentPath, $searchKey));
|
|
|
|
}
|
|
|
|
|
2017-01-17 20:04:53 +00:00
|
|
|
$parentDef = $this->container->findDefinition($parent);
|
2016-11-28 07:50:06 +00:00
|
|
|
if ($parentDef instanceof ChildDefinition) {
|
2015-06-24 09:29:42 +01:00
|
|
|
$id = $this->currentId;
|
|
|
|
$this->currentId = $parent;
|
2017-01-17 20:04:53 +00:00
|
|
|
$parentDef = $this->resolveDefinition($parentDef);
|
|
|
|
$this->container->setDefinition($parent, $parentDef);
|
2015-06-24 09:29:42 +01:00
|
|
|
$this->currentId = $id;
|
2011-01-26 23:14:31 +00:00
|
|
|
}
|
|
|
|
|
2017-01-24 18:16:15 +00:00
|
|
|
$this->container->log($this, sprintf('Resolving inheritance for "%s" (parent: %s).', $this->currentId, $parent));
|
2011-01-26 23:14:31 +00:00
|
|
|
$def = new Definition();
|
|
|
|
|
|
|
|
// merge in parent definition
|
2017-05-02 20:00:06 +01:00
|
|
|
// purposely ignored attributes: abstract, shared, tags, autoconfigured
|
2011-01-26 23:14:31 +00:00
|
|
|
$def->setClass($parentDef->getClass());
|
|
|
|
$def->setArguments($parentDef->getArguments());
|
|
|
|
$def->setMethodCalls($parentDef->getMethodCalls());
|
2011-03-04 14:26:00 +00:00
|
|
|
$def->setProperties($parentDef->getProperties());
|
2015-09-09 10:35:13 +01:00
|
|
|
if ($parentDef->isDeprecated()) {
|
|
|
|
$def->setDeprecated(true, $parentDef->getDeprecationMessage('%service_id%'));
|
|
|
|
}
|
2014-12-23 17:59:20 +00:00
|
|
|
$def->setFactory($parentDef->getFactory());
|
2011-01-26 23:14:31 +00:00
|
|
|
$def->setConfigurator($parentDef->getConfigurator());
|
|
|
|
$def->setFile($parentDef->getFile());
|
|
|
|
$def->setPublic($parentDef->isPublic());
|
2013-07-25 06:55:15 +01:00
|
|
|
$def->setLazy($parentDef->isLazy());
|
2017-04-04 18:35:51 +01:00
|
|
|
$def->setAutowired($parentDef->isAutowired());
|
2017-04-07 17:38:55 +01:00
|
|
|
$def->setChanges($parentDef->getChanges());
|
2011-01-26 23:14:31 +00:00
|
|
|
|
2018-05-15 08:06:31 +01:00
|
|
|
$def->setBindings($definition->getBindings() + $parentDef->getBindings());
|
2017-03-27 19:16:27 +01:00
|
|
|
|
2011-01-26 23:14:31 +00:00
|
|
|
// overwrite with values specified in the decorator
|
|
|
|
$changes = $definition->getChanges();
|
|
|
|
if (isset($changes['class'])) {
|
|
|
|
$def->setClass($definition->getClass());
|
|
|
|
}
|
2014-04-13 16:18:39 +01:00
|
|
|
if (isset($changes['factory'])) {
|
|
|
|
$def->setFactory($definition->getFactory());
|
|
|
|
}
|
2011-01-26 23:14:31 +00:00
|
|
|
if (isset($changes['configurator'])) {
|
|
|
|
$def->setConfigurator($definition->getConfigurator());
|
|
|
|
}
|
|
|
|
if (isset($changes['file'])) {
|
|
|
|
$def->setFile($definition->getFile());
|
|
|
|
}
|
|
|
|
if (isset($changes['public'])) {
|
|
|
|
$def->setPublic($definition->isPublic());
|
2017-09-05 12:45:10 +01:00
|
|
|
} else {
|
2017-09-20 05:13:06 +01:00
|
|
|
$def->setPrivate($definition->isPrivate() || $parentDef->isPrivate());
|
2011-01-26 23:14:31 +00:00
|
|
|
}
|
2013-10-30 08:30:20 +00:00
|
|
|
if (isset($changes['lazy'])) {
|
2013-07-25 06:55:15 +01:00
|
|
|
$def->setLazy($definition->isLazy());
|
|
|
|
}
|
2015-09-09 10:35:13 +01:00
|
|
|
if (isset($changes['deprecated'])) {
|
|
|
|
$def->setDeprecated($definition->isDeprecated(), $definition->getDeprecationMessage('%service_id%'));
|
|
|
|
}
|
2017-02-25 16:12:16 +00:00
|
|
|
if (isset($changes['autowired'])) {
|
2017-04-04 18:35:51 +01:00
|
|
|
$def->setAutowired($definition->isAutowired());
|
2016-08-16 22:10:39 +01:00
|
|
|
}
|
2017-04-07 17:38:55 +01:00
|
|
|
if (isset($changes['shared'])) {
|
|
|
|
$def->setShared($definition->isShared());
|
|
|
|
}
|
2015-02-20 22:49:14 +00:00
|
|
|
if (isset($changes['decorated_service'])) {
|
|
|
|
$decoratedService = $definition->getDecoratedService();
|
|
|
|
if (null === $decoratedService) {
|
|
|
|
$def->setDecoratedService($decoratedService);
|
|
|
|
} else {
|
2016-05-03 10:49:32 +01:00
|
|
|
$def->setDecoratedService($decoratedService[0], $decoratedService[1], $decoratedService[2]);
|
2015-02-20 22:49:14 +00:00
|
|
|
}
|
|
|
|
}
|
2011-01-26 23:14:31 +00:00
|
|
|
|
|
|
|
// merge arguments
|
|
|
|
foreach ($definition->getArguments() as $k => $v) {
|
|
|
|
if (is_numeric($k)) {
|
|
|
|
$def->addArgument($v);
|
2017-04-10 15:38:35 +01:00
|
|
|
} elseif (0 === strpos($k, 'index_')) {
|
2018-07-26 09:45:46 +01:00
|
|
|
$def->replaceArgument((int) substr($k, \strlen('index_')), $v);
|
2017-04-10 15:38:35 +01:00
|
|
|
} else {
|
|
|
|
$def->setArgument($k, $v);
|
2011-01-26 23:14:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-04 14:26:00 +00:00
|
|
|
// merge properties
|
|
|
|
foreach ($definition->getProperties() as $k => $v) {
|
|
|
|
$def->setProperty($k, $v);
|
|
|
|
}
|
|
|
|
|
2011-01-26 23:14:31 +00:00
|
|
|
// append method calls
|
2016-12-28 09:58:04 +00:00
|
|
|
if ($calls = $definition->getMethodCalls()) {
|
2011-01-26 23:14:31 +00:00
|
|
|
$def->setMethodCalls(array_merge($def->getMethodCalls(), $calls));
|
|
|
|
}
|
2017-04-07 17:38:55 +01:00
|
|
|
|
2018-11-06 14:59:24 +00:00
|
|
|
$def->addError($parentDef);
|
|
|
|
$def->addError($definition);
|
2018-03-21 21:48:27 +00:00
|
|
|
|
2017-04-07 17:38:55 +01:00
|
|
|
// these attributes are always taken from the child
|
|
|
|
$def->setAbstract($definition->isAbstract());
|
|
|
|
$def->setTags($definition->getTags());
|
Not allowing autoconfigure, instanceofConditionals or defaults for ChildDefinition
Also, not allowing arguments or method calls for autoconfigure. This is a safety
mechanism, since we don't have merging logic. It will allow us to add this in the
future if we want to.
The reason is that parent-child definitions are a different mechanism for "inheritance"
than instanceofConditionas and defaults... creating some edge cases when trying to
figure out which settings "win". For example:
Suppose a child and parent definitions are defined in different YAML files. The
child receives public: false from its _defaults, and the parent receives public: true
from its _defaults. Should the final child definition be public: true (so the parent
overrides the child, even though it only came from _defaults) or public: false (where
the child wins... even though it was only set from its _defaults). Or, if the parent
is explicitly set to public: true, should that override the public: false of the
child (which it got from its _defaults)? On one hand, the parent is being explicitly
set. On the other hand, the child is explicitly in a file settings _defaults public
to false. There's no correct answer.
There are also problems with instanceof. The importance goes:
defaults < instanceof < service definition
But how does parent-child relationships fit into that? If a child has public: false
from an _instanceof, but the parent explicitly sets public: true, which wins? Should
we assume the parent definition wins because it's explicitly set? Or would the
_instanceof win, because that's being explicitly applied to the child definition's
class by an _instanceof that lives in the same file as that class (whereas the parent
definition may live in a different file).
Because of this, @nicolas-grekas and I (we also talked a bit to Fabien) decided that
the complexity was growing too much. The solution is to not allow any of these
new feature to be used by ChildDefinition objects. In other words, when you want some
sort of "inheritance" for your service, you should *either* giving your service a
parent *or* using defaults and instanceof. And instead of silently not applying
defaults and instanceof to child definitions, I think it's better to scream that it's
not supported.
2017-04-27 16:48:07 +01:00
|
|
|
// autoconfigure is never taken from parent (on purpose)
|
|
|
|
// and it's not legal on an instanceof
|
|
|
|
$def->setAutoconfigured($definition->isAutoconfigured());
|
2017-04-07 17:38:55 +01:00
|
|
|
|
|
|
|
return $def;
|
2011-01-26 23:14:31 +00:00
|
|
|
}
|
2011-03-09 13:38:54 +00:00
|
|
|
}
|