Skip to content

Commit

Permalink
[DI] Add getter injection
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolas-grekas committed Dec 19, 2016
1 parent dd1fd31 commit d6ae0af
Show file tree
Hide file tree
Showing 29 changed files with 295 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public function process(ContainerBuilder $container)

if (!$this->onlyConstructorArguments) {
$this->processArguments($definition->getMethodCalls());
$this->processArguments($definition->getOverriddenGetters());
$this->processArguments($definition->getProperties());
if ($definition->getConfigurator()) {
$this->processArguments(array($definition->getConfigurator()));
Expand Down Expand Up @@ -108,6 +109,7 @@ private function processArguments(array $arguments)
} elseif ($argument instanceof Definition) {
$this->processArguments($argument->getArguments());
$this->processArguments($argument->getMethodCalls());
$this->processArguments($argument->getOverriddenGetters());
$this->processArguments($argument->getProperties());

if (is_array($argument->getFactory())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ private function processDefinition(Definition $definition)
{
$this->processReferences($definition->getArguments());
$this->processReferences($definition->getMethodCalls());
$this->processReferences($definition->getOverriddenGetters());
$this->processReferences($definition->getProperties());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public function process(ContainerBuilder $container)

$this->validateReferences($definition->getArguments());
$this->validateReferences($definition->getMethodCalls());
$this->validateReferences($definition->getOverriddenGetters());
$this->validateReferences($definition->getProperties());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ private function inlineArguments(ContainerBuilder $container, array $arguments,
} elseif ($argument instanceof Definition) {
$argument->setArguments($this->inlineArguments($container, $argument->getArguments()));
$argument->setMethodCalls($this->inlineArguments($container, $argument->getMethodCalls()));
$argument->setOverriddenGetters($this->inlineArguments($container, $argument->getOverriddenGetters()));
$argument->setProperties($this->inlineArguments($container, $argument->getProperties()));

$configurator = $this->inlineArguments($container, array($argument->getConfigurator()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ public function process(ContainerBuilder $container)
foreach ($container->getDefinitions() as $definitionId => $definition) {
$definition->setArguments($this->updateArgumentReferences($replacements, $definitionId, $definition->getArguments()));
$definition->setMethodCalls($this->updateArgumentReferences($replacements, $definitionId, $definition->getMethodCalls()));
$definition->setOverriddenGetters($this->updateArgumentReferences($replacements, $definitionId, $definition->getOverriddenGetters()));
$definition->setProperties($this->updateArgumentReferences($replacements, $definitionId, $definition->getProperties()));
$definition->setFactory($this->updateFactoryReference($replacements, $definition->getFactory()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ private function resolveArguments(ContainerBuilder $container, array $arguments,
}
$argument->setArguments($this->resolveArguments($container, $argument->getArguments()));
$argument->setMethodCalls($this->resolveArguments($container, $argument->getMethodCalls()));
$argument->setOverriddenGetters($this->resolveArguments($container, $argument->getOverriddenGetters()));
$argument->setProperties($this->resolveArguments($container, $argument->getProperties()));

$configurator = $this->resolveArguments($container, array($argument->getConfigurator()));
Expand Down Expand Up @@ -131,6 +132,7 @@ private function doResolveDefinition(ContainerBuilder $container, ChildDefinitio
$def->setClass($parentDef->getClass());
$def->setArguments($parentDef->getArguments());
$def->setMethodCalls($parentDef->getMethodCalls());
$def->setOverriddenGetters($parentDef->getOverriddenGetters());
$def->setProperties($parentDef->getProperties());
$def->setAutowiringTypes($parentDef->getAutowiringTypes());
if ($parentDef->isDeprecated()) {
Expand Down Expand Up @@ -203,6 +205,11 @@ private function doResolveDefinition(ContainerBuilder $container, ChildDefinitio
$def->setMethodCalls(array_merge($def->getMethodCalls(), $calls));
}

// merge overridden getters
foreach ($definition->getOverriddenGetters() as $k => $v) {
$def->setOverriddenGetter($k, $v);
}

// merge autowiring types
foreach ($definition->getAutowiringTypes() as $autowiringType) {
$def->addAutowiringType($autowiringType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,17 @@ public function process(ContainerBuilder $container)
}
$definition->setMethodCalls($calls);

$getters = array();
foreach ($definition->getOverriddenGetters() as $name => $value) {
try {
$value = $this->processArguments(array($value), true);
$getters[$name] = reset($value);
} catch (RuntimeException $e) {
// this call is simply removed
}
}
$definition->setOverriddenGetters($getters);

$properties = array();
foreach ($definition->getProperties() as $name => $value) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public function process(ContainerBuilder $container)
}
$definition->setMethodCalls($calls);

$definition->setOverriddenGetters($parameterBag->resolveValue($definition->getOverriddenGetters()));
$definition->setProperties($parameterBag->resolveValue($definition->getProperties()));
} catch (ParameterNotFoundException $e) {
$e->setSourceId($id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public function process(ContainerBuilder $container)

$definition->setArguments($this->processArguments($definition->getArguments()));
$definition->setMethodCalls($this->processArguments($definition->getMethodCalls()));
$definition->setOverriddenGetters($this->processArguments($definition->getOverriddenGetters()));
$definition->setProperties($this->processArguments($definition->getProperties()));
$definition->setFactory($this->processFactory($definition->getFactory()));
}
Expand Down
64 changes: 62 additions & 2 deletions src/Symfony/Component/DependencyInjection/ContainerBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,9 @@ private function createService(Definition $definition, $id, $tryProxy = true)
$arguments = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())));

if (null !== $factory = $definition->getFactory()) {
if ($definition->getOverriddenGetters()) {
throw new RuntimeException(sprintf('Cannot dump definition: The "%s" service has both a factory and overridden getters but these are incompatible.', $id));
}
if (is_array($factory)) {
$factory = array($this->resolveServices($parameterBag->resolveValue($factory[0])), $factory[1]);
} elseif (!is_string($factory)) {
Expand All @@ -905,11 +908,16 @@ private function createService(Definition $definition, $id, $tryProxy = true)
} else {
$r = new \ReflectionClass($parameterBag->resolveValue($definition->getClass()));

$service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments);

if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ")) {
@trigger_error(sprintf('The "%s" service relies on the deprecated "%s" class. It should either be deprecated or its implementation upgraded.', $id, $r->name), E_USER_DEPRECATED);
}
if ($definition->getOverriddenGetters()) {
$salt = str_replace('.', '', uniqid('', true));
$service = eval(sprintf('return new class(...$arguments) extends %s { private $container%3$s; private $values%3$s; %s };', $r->name, $this->addOverriddenGetters($id, $definition, $r, $salt), $salt));
call_user_func(\Closure::bind(function ($c, $v, $s) { $this->{'container'.$s} = $c; $this->{'values'.$s} = $v; }, $service, $service), $this, $definition->getOverriddenGetters(), $salt);
} else {
$service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments);
}
}

if ($tryProxy || !$definition->isLazy()) {
Expand Down Expand Up @@ -1114,6 +1122,58 @@ public function getEnvCounters()
return $this->envCounters;
}

private function addOverriddenGetters($id, Definition $definition, \ReflectionClass $class, $salt)
{
if (PHP_VERSION_ID < 70000) {
throw new RuntimeException(sprintf('Getter-based injection requires PHP 7 or more as used for service "%s".', $id));
}
if ($class->isFinal()) {
throw new RuntimeException(sprintf('Invalid getter for service "%s": class "%s" is final.', $id, $class->name));
}
$getters = '';
foreach ($definition->getOverriddenGetters() as $name => $returnValue) {
if (!$class->hasMethod($name)) {
throw new RuntimeException(sprintf('Invalid getter for service "%s": method "%s::%s" does not exist.', $id, $class->name, $name));
}
$r = $class->getMethod($name);
if ($r->isPrivate()) {
throw new RuntimeException(sprintf('Invalid getter for service "%s": method "%s::%s" is private.', $id, $class->name, $name));
}
if ($r->isFinal()) {
throw new RuntimeException(sprintf('Invalid getter for service "%s": method "%s::%s" is final.', $id, $class->name, $name));
}
if ($r->returnsReference()) {
throw new RuntimeException(sprintf('Invalid getter for service "%s": method "%s::%s" returns by reference.', $id, $class->name, $name));
}
if (0 < $r->getNumberOfParameters()) {
throw new RuntimeException(sprintf('Invalid getter for service "%s": method "%s::%s" has parameters.', $id, $class->name, $name));
}
if ($type = $r->getReturnType()) {
$type = ': '.($type->allowsNull() ? '?' : '').$this->generateTypeHint($type, $r);
}
$visibility = $r->isProtected() ? 'protected' : 'public';
$nameExport = var_export($name, true);
$getters .= <<<EOF
{$visibility} function {$name}(){$type} {
\$c = \$this->container$salt;
\$b = \$c->getParameterBag();
\$v = \$this->values{$salt}[{$nameExport}];
foreach (\$c->getServiceConditionals(\$v) as \$s) {
if (!\$c->has(\$s)) {
return parent::{$name}();
}
}
return \$c->resolveServices(\$b->unescapeValue(\$b->resolveValue(\$v)));
}
EOF;
}

return $getters;
}

/**
* Returns the Service Conditionals.
*
Expand Down
23 changes: 23 additions & 0 deletions src/Symfony/Component/DependencyInjection/Definition.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class Definition
private $deprecationTemplate = 'The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed.';
private $properties = array();
private $calls = array();
private $getters = array();
private $configurator;
private $tags = array();
private $public = true;
Expand Down Expand Up @@ -319,6 +320,28 @@ public function getMethodCalls()
return $this->calls;
}

public function setOverriddenGetter($name, $returnValue)
{
if (!$name) {
throw new InvalidArgumentException(sprintf('Getter name cannot be empty.'));
}
$this->getters[$name] = $returnValue;

return $this;
}

public function setOverriddenGetters(array $getters)
{
$this->getters = $getters;

return $this;
}

public function getOverriddenGetters()
{
return $this->getters;
}

/**
* Sets tags for this definition.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ public function dump(array $options = array())
$this->findEdges($id, $call[1], false, $call[0].'()')
);
}

foreach ($definition->getOverriddenGetters() as $name => $value) {
$this->edges[$id] = array_merge(
$this->edges[$id],
$this->findEdges($id, $value, false, $name.'()')
);
}
}

return $this->container->resolveEnvPlaceholders($this->startDot().$this->addNodes().$this->addEdges().$this->endDot(), '__ENV_%s__');
Expand Down
Loading

0 comments on commit d6ae0af

Please sign in to comment.