Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit ab094e2

Browse filesBrowse files
[DI] Add getter injection
1 parent 16d33e1 commit ab094e2
Copy full SHA for ab094e2

33 files changed

+822
-47
lines changed

‎src/Symfony/Component/DependencyInjection/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
3.3.0
55
-----
66

7+
* added support for getter-injection
78
* added "iterator" argument type for lazy iteration over a set of values and services
89
* added "closure-proxy" argument type for turning services' methods into lazy callables
910
* added file-wide configurable defaults for service attributes "public", "tags",

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public function process(ContainerBuilder $container)
7676

7777
if (!$this->onlyConstructorArguments) {
7878
$this->processArguments($definition->getMethodCalls());
79+
$this->processArguments($definition->getOverriddenGetters());
7980
$this->processArguments($definition->getProperties());
8081
if ($definition->getConfigurator()) {
8182
$this->processArguments(array($definition->getConfigurator()));
@@ -115,6 +116,7 @@ private function processArguments(array $arguments, $lazy = false)
115116
} elseif ($argument instanceof Definition) {
116117
$this->processArguments($argument->getArguments());
117118
$this->processArguments($argument->getMethodCalls());
119+
$this->processArguments($argument->getOverriddenGetters());
118120
$this->processArguments($argument->getProperties());
119121

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

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ private function processDefinition(Definition $definition)
4242
{
4343
$this->processReferences($definition->getArguments());
4444
$this->processReferences($definition->getMethodCalls());
45+
$this->processReferences($definition->getOverriddenGetters());
4546
$this->processReferences($definition->getProperties());
4647
}
4748

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public function process(ContainerBuilder $container)
4848

4949
$this->validateReferences($definition->getArguments());
5050
$this->validateReferences($definition->getMethodCalls());
51+
$this->validateReferences($definition->getOverriddenGetters());
5152
$this->validateReferences($definition->getProperties());
5253
}
5354
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ private function inlineArguments(ContainerBuilder $container, array $arguments,
8787
} elseif ($argument instanceof Definition) {
8888
$argument->setArguments($this->inlineArguments($container, $argument->getArguments()));
8989
$argument->setMethodCalls($this->inlineArguments($container, $argument->getMethodCalls()));
90+
$argument->setOverriddenGetters($this->inlineArguments($container, $argument->getOverriddenGetters()));
9091
$argument->setProperties($this->inlineArguments($container, $argument->getProperties()));
9192

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

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public function process(ContainerBuilder $container)
7777
foreach ($container->getDefinitions() as $definitionId => $definition) {
7878
$definition->setArguments($this->updateArgumentReferences($replacements, $definitionId, $definition->getArguments()));
7979
$definition->setMethodCalls($this->updateArgumentReferences($replacements, $definitionId, $definition->getMethodCalls()));
80+
$definition->setOverriddenGetters($this->updateArgumentReferences($replacements, $definitionId, $definition->getOverriddenGetters()));
8081
$definition->setProperties($this->updateArgumentReferences($replacements, $definitionId, $definition->getProperties()));
8182
$definition->setFactory($this->updateFactoryReference($replacements, $definition->getFactory()));
8283
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ private function resolveArguments(ContainerBuilder $container, array $arguments,
7575
}
7676
$argument->setArguments($this->resolveArguments($container, $argument->getArguments()));
7777
$argument->setMethodCalls($this->resolveArguments($container, $argument->getMethodCalls()));
78+
$argument->setOverriddenGetters($this->resolveArguments($container, $argument->getOverriddenGetters()));
7879
$argument->setProperties($this->resolveArguments($container, $argument->getProperties()));
7980

8081
$configurator = $this->resolveArguments($container, array($argument->getConfigurator()));
@@ -134,6 +135,7 @@ private function doResolveDefinition(ContainerBuilder $container, ChildDefinitio
134135
$def->setClass($parentDef->getClass());
135136
$def->setArguments($parentDef->getArguments());
136137
$def->setMethodCalls($parentDef->getMethodCalls());
138+
$def->setOverriddenGetters($parentDef->getOverriddenGetters());
137139
$def->setProperties($parentDef->getProperties());
138140
$def->setAutowiringTypes($parentDef->getAutowiringTypes());
139141
if ($parentDef->isDeprecated()) {
@@ -206,6 +208,11 @@ private function doResolveDefinition(ContainerBuilder $container, ChildDefinitio
206208
$def->setMethodCalls(array_merge($def->getMethodCalls(), $calls));
207209
}
208210

211+
// merge overridden getters
212+
foreach ($definition->getOverriddenGetters() as $k => $v) {
213+
$def->setOverriddenGetter($k, $v);
214+
}
215+
209216
// merge autowiring types
210217
foreach ($definition->getAutowiringTypes() as $autowiringType) {
211218
$def->addAutowiringType($autowiringType);

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/ResolveInvalidReferencesPass.php
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,17 @@ public function process(ContainerBuilder $container)
5454
}
5555
$definition->setMethodCalls($calls);
5656

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

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/ResolveParameterPlaceHoldersPass.php
+1Lines changed: 1 addition & 0 deletions
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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public function process(ContainerBuilder $container)
4242

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

‎src/Symfony/Component/DependencyInjection/ContainerBuilder.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/ContainerBuilder.php
+122-2Lines changed: 122 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use Symfony\Component\Config\Resource\ResourceInterface;
3030
use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface;
3131
use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator;
32+
use Symfony\Component\DependencyInjection\LazyProxy\GetterProxyInterface;
3233
use Symfony\Component\ExpressionLanguage\Expression;
3334
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
3435

@@ -923,6 +924,9 @@ private function createService(Definition $definition, $id, $tryProxy = true)
923924
$arguments = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())));
924925

925926
if (null !== $factory = $definition->getFactory()) {
927+
if ($definition->getOverriddenGetters()) {
928+
throw new RuntimeException(sprintf('Cannot create service "%s": factories and overridden getters are incompatible with each other.', $id));
929+
}
926930
if (is_array($factory)) {
927931
$factory = array($this->resolveServices($parameterBag->resolveValue($factory[0])), $factory[1]);
928932
} elseif (!is_string($factory)) {
@@ -941,11 +945,31 @@ private function createService(Definition $definition, $id, $tryProxy = true)
941945
} else {
942946
$r = new \ReflectionClass($parameterBag->resolveValue($definition->getClass()));
943947

944-
$service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments);
945-
946948
if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ")) {
947949
@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);
948950
}
951+
if ($definition->getOverriddenGetters()) {
952+
static $salt;
953+
if (null === $salt) {
954+
$salt = str_replace('.', '', uniqid('', true));
955+
}
956+
$service = sprintf('%s implements \\%s { private $container%4$s; private $values%4$s; %s }', $r->name, GetterProxyInterface::class, $this->generateOverriddenGetters($id, $definition, $r, $salt), $salt);
957+
if (!class_exists($proxyClass = 'SymfonyProxy_'.md5($service), false)) {
958+
eval(sprintf('class %s extends %s', $proxyClass, $service));
959+
}
960+
$r = new \ReflectionClass($proxyClass);
961+
$constructor = $r->getConstructor();
962+
if ($constructor && !defined('HHVM_VERSION') && $constructor->getDeclaringClass()->isInternal()) {
963+
$constructor = null;
964+
}
965+
$service = $constructor ? $r->newInstanceWithoutConstructor() : $r->newInstanceArgs($arguments);
966+
call_user_func(\Closure::bind(function ($c, $v, $s) { $this->{'container'.$s} = $c; $this->{'values'.$s} = $v; }, $service, $service), $this, $definition->getOverriddenGetters(), $salt);
967+
if ($constructor) {
968+
$constructor->invokeArgs($service, $arguments);
969+
}
970+
} else {
971+
$service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments);
972+
}
949973
}
950974

951975
if ($tryProxy || !$definition->isLazy()) {
@@ -1188,6 +1212,102 @@ public function getEnvCounters()
11881212
return $this->envCounters;
11891213
}
11901214

1215+
private function generateOverriddenGetters($id, Definition $definition, \ReflectionClass $class, $salt)
1216+
{
1217+
if ($class->isFinal()) {
1218+
throw new RuntimeException(sprintf('Unable to configure getter injection for service "%s": class "%s" cannot be marked as final.', $id, $class->name));
1219+
}
1220+
$getters = '';
1221+
foreach ($definition->getOverriddenGetters() as $name => $returnValue) {
1222+
$r = self::getGetterReflector($class, $name, $id, $type);
1223+
$visibility = $r->isProtected() ? 'protected' : 'public';
1224+
$name = var_export($name, true);
1225+
$getters .= <<<EOF
1226+
1227+
{$visibility} function {$r->name}(){$type} {
1228+
\$c = \$this->container{$salt};
1229+
\$b = \$c->getParameterBag();
1230+
\$v = \$this->values{$salt}[{$name}];
1231+
1232+
foreach (\$c->getServiceConditionals(\$v) as \$s) {
1233+
if (!\$c->has(\$s)) {
1234+
return parent::{$r->name}();
1235+
}
1236+
}
1237+
1238+
return \$c->resolveServices(\$b->unescapeValue(\$b->resolveValue(\$v)));
1239+
}
1240+
EOF;
1241+
}
1242+
1243+
return $getters;
1244+
}
1245+
1246+
/**
1247+
* @internal
1248+
*/
1249+
public static function getGetterReflector(\ReflectionClass $class, $name, $id, &$type)
1250+
{
1251+
if (!$class->hasMethod($name)) {
1252+
throw new RuntimeException(sprintf('Unable to configure getter injection for service "%s": method "%s::%s" does not exist.', $id, $class->name, $name));
1253+
}
1254+
$r = $class->getMethod($name);
1255+
if ($r->isPrivate()) {
1256+
throw new RuntimeException(sprintf('Unable to configure getter injection for service "%s": method "%s::%s" must be public or protected.', $id, $class->name, $r->name));
1257+
}
1258+
if ($r->isStatic()) {
1259+
throw new RuntimeException(sprintf('Unable to configure getter injection for service "%s": method "%s::%s" cannot be static.', $id, $class->name, $r->name));
1260+
}
1261+
if ($r->isFinal()) {
1262+
throw new RuntimeException(sprintf('Unable to configure getter injection for service "%s": method "%s::%s" cannot be marked as final.', $id, $class->name, $r->name));
1263+
}
1264+
if ($r->returnsReference()) {
1265+
throw new RuntimeException(sprintf('Unable to configure getter injection for service "%s": method "%s::%s" cannot return by reference.', $id, $class->name, $r->name));
1266+
}
1267+
if (0 < $r->getNumberOfParameters()) {
1268+
throw new RuntimeException(sprintf('Unable to configure getter injection for service "%s": method "%s::%s" cannot have any arguments.', $id, $class->name, $r->name));
1269+
}
1270+
if ($type = method_exists($r, 'getReturnType') ? $r->getReturnType() : null) {
1271+
$type = ': '.($type->allowsNull() ? '?' : '').self::generateTypeHint($type, $r);
1272+
}
1273+
1274+
return $r;
1275+
}
1276+
1277+
/**
1278+
* @internal
1279+
*/
1280+
public static function generateTypeHint($type, \ReflectionFunctionAbstract $r)
1281+
{
1282+
if (is_string($type)) {
1283+
$name = $type;
1284+
1285+
if ('callable' === $name || 'array' === $name) {
1286+
return $name;
1287+
}
1288+
} else {
1289+
$name = $type instanceof \ReflectionNamedType ? $type->getName() : $type->__toString();
1290+
1291+
if ($type->isBuiltin()) {
1292+
return $name;
1293+
}
1294+
}
1295+
$lcName = strtolower($name);
1296+
1297+
if ('self' !== $lcName && 'parent' !== $lcName) {
1298+
return '\\'.$name;
1299+
}
1300+
if (!$r instanceof \ReflectionMethod) {
1301+
return;
1302+
}
1303+
if ('self' === $lcName) {
1304+
return '\\'.$r->getDeclaringClass()->name;
1305+
}
1306+
if ($parent = $r->getDeclaringClass()->getParentClass()) {
1307+
return '\\'.$parent->name;
1308+
}
1309+
}
1310+
11911311
/**
11921312
* Returns the Service Conditionals.
11931313
*

‎src/Symfony/Component/DependencyInjection/Definition.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Definition.php
+23Lines changed: 23 additions & 0 deletions
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[strtolower($name)] = $returnValue;
329+
330+
return $this;
331+
}
332+
333+
public function setOverriddenGetters(array $getters)
334+
{
335+
$this->getters = array_change_key_case($getters, CASE_LOWER);
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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,13 @@ public function dump(array $options = array())
8080
$this->findEdges($id, $call[1], false, $call[0].'()')
8181
);
8282
}
83+
84+
foreach ($definition->getOverriddenGetters() as $name => $value) {
85+
$this->edges[$id] = array_merge(
86+
$this->edges[$id],
87+
$this->findEdges($id, $value, false, $name.'()')
88+
);
89+
}
8390
}
8491

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

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.