Skip to content

Commit 3dae76d

Browse files
[DI] Add getter injection
1 parent d614193 commit 3dae76d

28 files changed

+274
-16
lines changed

src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php

+2
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ public function process(ContainerBuilder $container)
7575

7676
if (!$this->onlyConstructorArguments) {
7777
$this->processArguments($definition->getMethodCalls());
78+
$this->processArguments($definition->getOverriddenGetters());
7879
$this->processArguments($definition->getProperties());
7980
if ($definition->getConfigurator()) {
8081
$this->processArguments(array($definition->getConfigurator()));
@@ -108,6 +109,7 @@ private function processArguments(array $arguments)
108109
} elseif ($argument instanceof Definition) {
109110
$this->processArguments($argument->getArguments());
110111
$this->processArguments($argument->getMethodCalls());
112+
$this->processArguments($argument->getOverriddenGetters());
111113
$this->processArguments($argument->getProperties());
112114

113115
if (is_array($argument->getFactory())) {

src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ private function processDefinition(Definition $definition)
4141
{
4242
$this->processReferences($definition->getArguments());
4343
$this->processReferences($definition->getMethodCalls());
44+
$this->processReferences($definition->getOverriddenGetters());
4445
$this->processReferences($definition->getProperties());
4546
}
4647

src/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public function process(ContainerBuilder $container)
4747

4848
$this->validateReferences($definition->getArguments());
4949
$this->validateReferences($definition->getMethodCalls());
50+
$this->validateReferences($definition->getOverriddenGetters());
5051
$this->validateReferences($definition->getProperties());
5152
}
5253
}

src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ private function inlineArguments(ContainerBuilder $container, array $arguments,
8484
} elseif ($argument instanceof Definition) {
8585
$argument->setArguments($this->inlineArguments($container, $argument->getArguments()));
8686
$argument->setMethodCalls($this->inlineArguments($container, $argument->getMethodCalls()));
87+
$argument->setOverriddenGetters($this->inlineArguments($container, $argument->getOverriddenGetters()));
8788
$argument->setProperties($this->inlineArguments($container, $argument->getProperties()));
8889

8990
$configurator = $this->inlineArguments($container, array($argument->getConfigurator()));

src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public function process(ContainerBuilder $container)
7676
foreach ($container->getDefinitions() as $definitionId => $definition) {
7777
$definition->setArguments($this->updateArgumentReferences($replacements, $definitionId, $definition->getArguments()));
7878
$definition->setMethodCalls($this->updateArgumentReferences($replacements, $definitionId, $definition->getMethodCalls()));
79+
$definition->setOverriddenGetters($this->updateArgumentReferences($replacements, $definitionId, $definition->getOverriddenGetters()));
7980
$definition->setProperties($this->updateArgumentReferences($replacements, $definitionId, $definition->getProperties()));
8081
$definition->setFactory($this->updateFactoryReference($replacements, $definition->getFactory()));
8182
}

src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php

+7
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ private function resolveArguments(ContainerBuilder $container, array $arguments,
7272
}
7373
$argument->setArguments($this->resolveArguments($container, $argument->getArguments()));
7474
$argument->setMethodCalls($this->resolveArguments($container, $argument->getMethodCalls()));
75+
$argument->setOverriddenGetters($this->resolveArguments($container, $argument->getOverriddenGetters()));
7576
$argument->setProperties($this->resolveArguments($container, $argument->getProperties()));
7677

7778
$configurator = $this->resolveArguments($container, array($argument->getConfigurator()));
@@ -131,6 +132,7 @@ private function doResolveDefinition(ContainerBuilder $container, ChildDefinitio
131132
$def->setClass($parentDef->getClass());
132133
$def->setArguments($parentDef->getArguments());
133134
$def->setMethodCalls($parentDef->getMethodCalls());
135+
$def->setOverriddenGetters($parentDef->getOverriddenGetters());
134136
$def->setProperties($parentDef->getProperties());
135137
$def->setAutowiringTypes($parentDef->getAutowiringTypes());
136138
if ($parentDef->isDeprecated()) {
@@ -203,6 +205,11 @@ private function doResolveDefinition(ContainerBuilder $container, ChildDefinitio
203205
$def->setMethodCalls(array_merge($def->getMethodCalls(), $calls));
204206
}
205207

208+
// merge overridden getters
209+
foreach ($definition->getOverriddenGetters() as $k => $v) {
210+
$def->setOverriddenGetter($k, $v);
211+
}
212+
206213
// merge autowiring types
207214
foreach ($definition->getAutowiringTypes() as $autowiringType) {
208215
$def->addAutowiringType($autowiringType);

src/Symfony/Component/DependencyInjection/Compiler/ResolveInvalidReferencesPass.php

+11
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,17 @@ public function process(ContainerBuilder $container)
5353
}
5454
$definition->setMethodCalls($calls);
5555

56+
$getters = array();
57+
foreach ($definition->getOverriddenGetters() as $name => $value) {
58+
try {
59+
$value = $this->processArguments(array($value), true);
60+
$getters[$name] = reset($value);
61+
} catch (RuntimeException $e) {
62+
// this call is simply removed
63+
}
64+
}
65+
$definition->setOverriddenGetters($getters);
66+
5667
$properties = array();
5768
foreach ($definition->getProperties() as $name => $value) {
5869
try {

src/Symfony/Component/DependencyInjection/Compiler/ResolveParameterPlaceHoldersPass.php

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public function process(ContainerBuilder $container)
5151
}
5252
$definition->setMethodCalls($calls);
5353

54+
$definition->setOverriddenGetters($parameterBag->resolveValue($definition->getOverriddenGetters()));
5455
$definition->setProperties($parameterBag->resolveValue($definition->getProperties()));
5556
} catch (ParameterNotFoundException $e) {
5657
$e->setSourceId($id);

src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public function process(ContainerBuilder $container)
4141

4242
$definition->setArguments($this->processArguments($definition->getArguments()));
4343
$definition->setMethodCalls($this->processArguments($definition->getMethodCalls()));
44+
$definition->setOverriddenGetters($this->processArguments($definition->getOverriddenGetters()));
4445
$definition->setProperties($this->processArguments($definition->getProperties()));
4546
$definition->setFactory($this->processFactory($definition->getFactory()));
4647
}

src/Symfony/Component/DependencyInjection/ContainerBuilder.php

+63-2
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,9 @@ private function createService(Definition $definition, $id, $tryProxy = true)
887887
$arguments = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())));
888888

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

908-
$service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments);
909-
910911
if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ")) {
911912
@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);
912913
}
914+
if ($definition->getOverriddenGetters()) {
915+
$service = eval(sprintf('return new class(...$arguments) extends %s { private $c0nt41n3r; private $g3ttErV4lu35; %s };', $r->name, $this->addOverriddenGetters($id, $definition, $r)));
916+
call_user_func(\Closure::bind(function ($c, $v) { $this->c0nt41n3r = $c; $this->g3ttErV4lu35 = $v; }, $service, $service), $this, $definition->getOverriddenGetters());
917+
} else {
918+
$service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments);
919+
}
913920
}
914921

915922
if ($tryProxy || !$definition->isLazy()) {
@@ -1114,6 +1121,60 @@ public function getEnvCounters()
11141121
return $this->envCounters;
11151122
}
11161123

1124+
private function addOverriddenGetters($id, Definition $definition, \ReflectionClass $class)
1125+
{
1126+
if (PHP_VERSION_ID < 70000) {
1127+
throw new RuntimeException(sprintf('Getter-based injection requires PHP 7 or more as used for service "%s".', $id));
1128+
}
1129+
if ($class->isFinal()) {
1130+
throw new RuntimeException(sprintf('Invalid getter for service "%s": class "%s" is final.', $id, $class->name));
1131+
}
1132+
$getters = '';
1133+
foreach ($definition->getOverriddenGetters() as $name => $returnValue) {
1134+
if (!$class->hasMethod($name)) {
1135+
throw new RuntimeException(sprintf('Invalid getter for service "%s": method "%s::%s" does not exist.', $id, $class->name, $name));
1136+
}
1137+
if (($r = $class->getMethod($name))->isPrivate()) {
1138+
throw new RuntimeException(sprintf('Invalid getter for service "%s": method "%s::%s" is private.', $id, $class->name, $name));
1139+
}
1140+
if ($r->isFinal()) {
1141+
throw new RuntimeException(sprintf('Invalid getter for service "%s": method "%s::%s" is final.', $id, $class->name, $name));
1142+
}
1143+
if ($r->isInternal()) {
1144+
throw new RuntimeException(sprintf('Invalid getter for service "%s": method "%s::%s" is internal.', $id, $class->name, $name));
1145+
}
1146+
if ($r->returnsReference()) {
1147+
throw new RuntimeException(sprintf('Invalid getter for service "%s": method "%s::%s" returns by reference.', $id, $class->name, $name));
1148+
}
1149+
if (0 < $r->getNumberOfParameters()) {
1150+
throw new RuntimeException(sprintf('Invalid getter for service "%s": method "%s::%s" has parameters.', $id, $class->name, $name));
1151+
}
1152+
if ($type = $r->getReturnType()) {
1153+
$type = ': '.($type->allowsNull() ? '?' : '').$this->generateTypeHint($type, $r);
1154+
}
1155+
$visibility = $r->isProtected() ? 'protected' : 'public';
1156+
$nameExport = var_export($name, true);
1157+
$getters .= <<<EOF
1158+
1159+
$visibility function $name()$type {
1160+
\$c = \$this->cOnt4In3r;
1161+
\$b = \$c->getParameterBag();
1162+
\$v = \$this->g3ttErV4lu35[$nameExport];
1163+
1164+
foreach (\$c->getServiceConditionals(\$v) as \$s) {
1165+
if (!\$c->has(\$s)) {
1166+
return parent::$name();
1167+
}
1168+
}
1169+
1170+
return \$c->resolveServices(\$b->unescapeValue(\$b->resolveValue(\$v)));
1171+
}
1172+
EOF;
1173+
}
1174+
1175+
return $getters;
1176+
}
1177+
11171178
/**
11181179
* Returns the Service Conditionals.
11191180
*

src/Symfony/Component/DependencyInjection/Definition.php

+23
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class Definition
2929
private $deprecationTemplate = 'The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed.';
3030
private $properties = array();
3131
private $calls = array();
32+
private $getters = array();
3233
private $configurator;
3334
private $tags = array();
3435
private $public = true;
@@ -319,6 +320,28 @@ public function getMethodCalls()
319320
return $this->calls;
320321
}
321322

323+
public function setOverriddenGetter($name, $returnValue)
324+
{
325+
if (!$name) {
326+
throw new InvalidArgumentException(sprintf('Getter name cannot be empty.'));
327+
}
328+
$this->getters[$name] = $returnValue;
329+
330+
return $this;
331+
}
332+
333+
public function setOverriddenGetters(array $getters)
334+
{
335+
$this->getters = $getters;
336+
337+
return $this;
338+
}
339+
340+
public function getOverriddenGetters()
341+
{
342+
return $this->getters;
343+
}
344+
322345
/**
323346
* Sets tags for this definition.
324347
*

src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php

+7
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ public function dump(array $options = array())
7979
$this->findEdges($id, $call[1], false, $call[0].'()')
8080
);
8181
}
82+
83+
foreach ($definition->getOverriddenGetters() as $name => $value) {
84+
$this->edges[$id] = array_merge(
85+
$this->edges[$id],
86+
$this->findEdges($id, $value, false, $name.'()')
87+
);
88+
}
8289
}
8390

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

0 commit comments

Comments
 (0)