-
-
Notifications
You must be signed in to change notification settings - Fork 9.7k
[DependencyInjection] Add autowiring capabilities #15613
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
9828da2
195f6c0
8d23c9d
27f4de8
5575ec1
edce23d
07d475e
afe009a
7ab5b08
a7ca3f2
57c494c
e642aa8
d00c7e3
55bb42c
7806487
585616d
c717c69
3b7f553
936a16a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,22 +24,27 @@ | |
class AutowiringPass implements CompilerPassInterface | ||
{ | ||
private $container; | ||
private $definitions; | ||
private $reflectionClassesToId = array(); | ||
private $reflectionClasses = array(); | ||
private $definedTypes = array(); | ||
private $typesToId; | ||
private $notGuessableTypesToId = array(); | ||
private $types; | ||
private $notGuessableTypes = array(); | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function process(ContainerBuilder $container) | ||
{ | ||
$this->container = $container; | ||
$this->definitions = $container->getDefinitions(); | ||
foreach ($this->definitions as $id => $definition) { | ||
foreach ($container->getDefinitions() as $id => $definition) { | ||
$this->completeDefinition($id, $definition); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please reset all the internal state at the end of the method to release memory (many things are not needed at all anymore, and there is no reason to kep a circular reference between the ContainerBuilder and this pass) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why aren't you using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch, I'll take a look at that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because I need the definition instance and not the tags. |
||
|
||
// Free memory and remove circular reference to container | ||
$this->container = null; | ||
$this->reflectionClasses = array(); | ||
$this->definedTypes = array(); | ||
$this->types = null; | ||
$this->notGuessableTypes = array(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not quite sure if everything should be reset (I can understand why the reflection classes or the container, but the rest, as they are kind of execution indepedant...). Granted, this process should only be called once, but still... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Everything should be reseted to avoid memory leaks. In fact it can be called a lot of times, for instance when running test suites or using Behat. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Taluu anything which is specific to the container passed as argument should disappear from the class when leaving the method. Otherwise, processing a container has side effects on the state of this class. @dunglas your cleanup is broken in 1 case though: the processing can throw exceptions, and the cleanup should also be applied in such case (this is the perfect use case for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @stof right, I'll add a |
||
} | ||
|
||
/** | ||
|
@@ -62,7 +67,7 @@ private function completeDefinition($id, Definition $definition) | |
|
||
$arguments = $definition->getArguments(); | ||
foreach ($constructor->getParameters() as $index => $parameter) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. getParameters returns an array indexed by name IIRC, not by numbers, so the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The array returned by |
||
if (!($typeHint = $parameter->getClass()) || $parameter->isOptional()) { | ||
if (!($typeHint = $parameter->getClass())) { | ||
continue; | ||
} | ||
|
||
|
@@ -71,20 +76,28 @@ private function completeDefinition($id, Definition $definition) | |
continue; | ||
} | ||
|
||
if (null === $this->typesToId) { | ||
if (null === $this->types) { | ||
$this->populateAvailableTypes(); | ||
} | ||
|
||
if (isset($this->typesToId[$typeHint->name])) { | ||
$reference = new Reference($this->typesToId[$typeHint->name]); | ||
if (isset($this->types[$typeHint->name])) { | ||
$value = new Reference($this->types[$typeHint->name]); | ||
} else { | ||
$reference = $this->createAutowiredDefinition($typeHint); | ||
try { | ||
$value = $this->createAutowiredDefinition($typeHint); | ||
} catch (RuntimeException $e) { | ||
if (!$parameter->isDefaultValueAvailable()) { | ||
throw $e; | ||
} | ||
|
||
$value = $parameter->getDefaultValue(); | ||
} | ||
} | ||
|
||
if ($argumentExist) { | ||
$definition->replaceArgument($index, $reference); | ||
$definition->replaceArgument($index, $value); | ||
} else { | ||
$definition->addArgument($reference); | ||
$definition->addArgument($value); | ||
} | ||
} | ||
} | ||
|
@@ -94,9 +107,9 @@ private function completeDefinition($id, Definition $definition) | |
*/ | ||
private function populateAvailableTypes() | ||
{ | ||
$this->typesToId = array(); | ||
$this->types = array(); | ||
|
||
foreach ($this->definitions as $id => $definition) { | ||
foreach ($this->container->getDefinitions() as $id => $definition) { | ||
$this->populateAvailableType($id, $definition); | ||
} | ||
} | ||
|
@@ -115,7 +128,7 @@ private function populateAvailableType($id, Definition $definition) | |
|
||
foreach ($definition->getTypes() as $type) { | ||
$this->definedTypes[$type] = true; | ||
$this->typesToId[$type] = $id; | ||
$this->types[$type] = $id; | ||
} | ||
|
||
if ($reflectionClass = $this->getReflectionClass($id, $definition)) { | ||
|
@@ -162,22 +175,22 @@ private function extractAncestors($id, \ReflectionClass $reflectionClass) | |
*/ | ||
private function set($type, $value) | ||
{ | ||
if (isset($this->definedTypes[$type]) || isset($this->notGuessableTypesToId[$type])) { | ||
if (isset($this->definedTypes[$type]) || isset($this->notGuessableTypes[$type])) { | ||
return; | ||
} | ||
|
||
if (isset($this->typesToId[$type])) { | ||
if ($this->typesToId[$type] === $value) { | ||
if (isset($this->types[$type])) { | ||
if ($this->types[$type] === $value) { | ||
return; | ||
} | ||
|
||
unset($this->typesToId[$type]); | ||
$this->notGuessableTypesToId[$type] = true; | ||
unset($this->types[$type]); | ||
$this->notGuessableTypes[$type] = true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just out of curiosity ; why use an associative array here, and not a simple array ? Is it because it is faster this way (I think I read something on that matter a while ago) ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes it's for performance. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Taluu |
||
|
||
return; | ||
} | ||
|
||
$this->typesToId[$type] = $value; | ||
$this->types[$type] = $value; | ||
} | ||
|
||
/** | ||
|
@@ -200,7 +213,6 @@ private function createAutowiredDefinition(\ReflectionClass $typeHint) | |
$argumentDefinition = $this->container->register($argumentId, $typeHint->name); | ||
$argumentDefinition->setPublic(false); | ||
|
||
$this->definitions = $this->container->getDefinitions(); | ||
$this->populateAvailableType($argumentId, $argumentDefinition); | ||
$this->completeDefinition($argumentId, $argumentDefinition); | ||
|
||
|
@@ -217,16 +229,18 @@ private function createAutowiredDefinition(\ReflectionClass $typeHint) | |
*/ | ||
private function getReflectionClass($id, Definition $definition) | ||
{ | ||
if (isset($this->reflectionClassesToId[$id])) { | ||
return $this->reflectionClassesToId[$id]; | ||
if (isset($this->reflectionClasses[$id])) { | ||
return $this->reflectionClasses[$id]; | ||
} | ||
|
||
if (!$class = $definition->getClass()) { | ||
return; | ||
} | ||
|
||
$class = $this->container->getParameterBag()->resolveValue($class); | ||
|
||
try { | ||
return $this->reflectionClassesToId[$id] = new \ReflectionClass($class); | ||
return $this->reflectionClasses[$id] = new \ReflectionClass($class); | ||
} catch (\ReflectionException $e) { | ||
// Skip invalid classes definitions to keep BC | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,8 +50,8 @@ public function __construct() | |
new CheckDefinitionValidityPass(), | ||
new ResolveReferencesToAliasesPass(), | ||
new ResolveInvalidReferencesPass(), | ||
new AnalyzeServiceReferencesPass(true), | ||
new AutowiringPass(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this must be before the |
||
new AnalyzeServiceReferencesPass(true), | ||
new CheckCircularReferencesPass(), | ||
new CheckReferenceValidityPass(), | ||
); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why storing the container? Can't you just pass it explicitly to your private methods?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As this class already has a lot of states, it's just to avoid passing the container as argument of other methods.
I can rewrite this class without states but not sure it has any interest (and other passes also have states).