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 77fb641

Browse filesBrowse files
[DI] Add getter injection
1 parent 84a5483 commit 77fb641
Copy full SHA for 77fb641

28 files changed

+807
-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+
* [EXPERIMENTAL] added support for getter-injection
78
* added support for omitting the factory class name in a service definition if the definition class is set
89
* deprecated case insensitivity of service identifiers
910
* added "iterator" argument type for lazy iteration over a set of values and services

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ protected function processValue($value, $isRoot = false)
6161
} elseif ($value instanceof Definition) {
6262
$value->setArguments($this->processValue($value->getArguments()));
6363
$value->setProperties($this->processValue($value->getProperties()));
64+
$value->setOverriddenGetters($this->processValue($value->getOverriddenGetters()));
6465
$value->setMethodCalls($this->processValue($value->getMethodCalls()));
6566

6667
if ($v = $value->getFactory()) {

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ private function doResolveDefinition(ChildDefinition $definition)
8989
$def->setClass($parentDef->getClass());
9090
$def->setArguments($parentDef->getArguments());
9191
$def->setMethodCalls($parentDef->getMethodCalls());
92+
$def->setOverriddenGetters($parentDef->getOverriddenGetters());
9293
$def->setProperties($parentDef->getProperties());
9394
$def->setAutowiringTypes($parentDef->getAutowiringTypes());
9495
if ($parentDef->isDeprecated()) {
@@ -161,6 +162,11 @@ private function doResolveDefinition(ChildDefinition $definition)
161162
$def->setMethodCalls(array_merge($def->getMethodCalls(), $calls));
162163
}
163164

165+
// merge overridden getters
166+
foreach ($definition->getOverriddenGetters() as $k => $v) {
167+
$def->setOverriddenGetter($k, $v);
168+
}
169+
164170
// merge autowiring types
165171
foreach ($definition->getAutowiringTypes() as $autowiringType) {
166172
$def->addAutowiringType($autowiringType);

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/ResolveInvalidReferencesPass.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ private function processValue($value, $rootLevel = 0, $level = 0)
5959
}
6060
$value->setArguments($this->processValue($value->getArguments(), 0));
6161
$value->setProperties($this->processValue($value->getProperties(), 1));
62+
$value->setOverriddenGetters($this->processValue($value->getOverriddenGetters(), 1));
6263
$value->setMethodCalls($this->processValue($value->getMethodCalls(), 2));
6364
} elseif (is_array($value)) {
6465
$i = 0;

‎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

@@ -890,6 +891,9 @@ private function createService(Definition $definition, $id, $tryProxy = true)
890891
$arguments = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())));
891892

892893
if (null !== $factory = $definition->getFactory()) {
894+
if ($definition->getOverriddenGetters()) {
895+
throw new RuntimeException(sprintf('Cannot create service "%s": factories and overridden getters are incompatible with each other.', $id));
896+
}
893897
if (is_array($factory)) {
894898
$factory = array($this->resolveServices($parameterBag->resolveValue($factory[0])), $factory[1]);
895899
} elseif (!is_string($factory)) {
@@ -908,11 +912,31 @@ private function createService(Definition $definition, $id, $tryProxy = true)
908912
} else {
909913
$r = new \ReflectionClass($parameterBag->resolveValue($definition->getClass()));
910914

911-
$service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments);
912-
913915
if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ")) {
914916
@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);
915917
}
918+
if ($definition->getOverriddenGetters()) {
919+
static $salt;
920+
if (null === $salt) {
921+
$salt = str_replace('.', '', uniqid('', true));
922+
}
923+
$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);
924+
if (!class_exists($proxyClass = 'SymfonyProxy_'.md5($service), false)) {
925+
eval(sprintf('class %s extends %s', $proxyClass, $service));
926+
}
927+
$r = new \ReflectionClass($proxyClass);
928+
$constructor = $r->getConstructor();
929+
if ($constructor && !defined('HHVM_VERSION') && $constructor->getDeclaringClass()->isInternal()) {
930+
$constructor = null;
931+
}
932+
$service = $constructor ? $r->newInstanceWithoutConstructor() : $r->newInstanceArgs($arguments);
933+
call_user_func(\Closure::bind(function ($c, $v, $s) { $this->{'container'.$s} = $c; $this->{'values'.$s} = $v; }, $service, $service), $this, $definition->getOverriddenGetters(), $salt);
934+
if ($constructor) {
935+
$constructor->invokeArgs($service, $arguments);
936+
}
937+
} else {
938+
$service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments);
939+
}
916940
}
917941

918942
if ($tryProxy || !$definition->isLazy()) {
@@ -1155,6 +1179,102 @@ public function getEnvCounters()
11551179
return $this->envCounters;
11561180
}
11571181

1182+
private function generateOverriddenGetters($id, Definition $definition, \ReflectionClass $class, $salt)
1183+
{
1184+
if ($class->isFinal()) {
1185+
throw new RuntimeException(sprintf('Unable to configure getter injection for service "%s": class "%s" cannot be marked as final.', $id, $class->name));
1186+
}
1187+
$getters = '';
1188+
foreach ($definition->getOverriddenGetters() as $name => $returnValue) {
1189+
$r = self::getGetterReflector($class, $name, $id, $type);
1190+
$visibility = $r->isProtected() ? 'protected' : 'public';
1191+
$name = var_export($name, true);
1192+
$getters .= <<<EOF
1193+
1194+
{$visibility} function {$r->name}(){$type} {
1195+
\$c = \$this->container{$salt};
1196+
\$b = \$c->getParameterBag();
1197+
\$v = \$this->values{$salt}[{$name}];
1198+
1199+
foreach (\$c->getServiceConditionals(\$v) as \$s) {
1200+
if (!\$c->has(\$s)) {
1201+
return parent::{$r->name}();
1202+
}
1203+
}
1204+
1205+
return \$c->resolveServices(\$b->unescapeValue(\$b->resolveValue(\$v)));
1206+
}
1207+
EOF;
1208+
}
1209+
1210+
return $getters;
1211+
}
1212+
1213+
/**
1214+
* @internal
1215+
*/
1216+
public static function getGetterReflector(\ReflectionClass $class, $name, $id, &$type)
1217+
{
1218+
if (!$class->hasMethod($name)) {
1219+
throw new RuntimeException(sprintf('Unable to configure getter injection for service "%s": method "%s::%s" does not exist.', $id, $class->name, $name));
1220+
}
1221+
$r = $class->getMethod($name);
1222+
if ($r->isPrivate()) {
1223+
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));
1224+
}
1225+
if ($r->isStatic()) {
1226+
throw new RuntimeException(sprintf('Unable to configure getter injection for service "%s": method "%s::%s" cannot be static.', $id, $class->name, $r->name));
1227+
}
1228+
if ($r->isFinal()) {
1229+
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));
1230+
}
1231+
if ($r->returnsReference()) {
1232+
throw new RuntimeException(sprintf('Unable to configure getter injection for service "%s": method "%s::%s" cannot return by reference.', $id, $class->name, $r->name));
1233+
}
1234+
if (0 < $r->getNumberOfParameters()) {
1235+
throw new RuntimeException(sprintf('Unable to configure getter injection for service "%s": method "%s::%s" cannot have any arguments.', $id, $class->name, $r->name));
1236+
}
1237+
if ($type = method_exists($r, 'getReturnType') ? $r->getReturnType() : null) {
1238+
$type = ': '.($type->allowsNull() ? '?' : '').self::generateTypeHint($type, $r);
1239+
}
1240+
1241+
return $r;
1242+
}
1243+
1244+
/**
1245+
* @internal
1246+
*/
1247+
public static function generateTypeHint($type, \ReflectionFunctionAbstract $r)
1248+
{
1249+
if (is_string($type)) {
1250+
$name = $type;
1251+
1252+
if ('callable' === $name || 'array' === $name) {
1253+
return $name;
1254+
}
1255+
} else {
1256+
$name = $type instanceof \ReflectionNamedType ? $type->getName() : $type->__toString();
1257+
1258+
if ($type->isBuiltin()) {
1259+
return $name;
1260+
}
1261+
}
1262+
$lcName = strtolower($name);
1263+
1264+
if ('self' !== $lcName && 'parent' !== $lcName) {
1265+
return '\\'.$name;
1266+
}
1267+
if (!$r instanceof \ReflectionMethod) {
1268+
return;
1269+
}
1270+
if ('self' === $lcName) {
1271+
return '\\'.$r->getDeclaringClass()->name;
1272+
}
1273+
if ($parent = $r->getDeclaringClass()->getParentClass()) {
1274+
return '\\'.$parent->name;
1275+
}
1276+
}
1277+
11581278
/**
11591279
* @internal
11601280
*/

‎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.