Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit aee5731

Browse files
dunglasfabpot
authored andcommitted
[DependencyInjection] Add autowiring capabilities
1 parent bee1faa commit aee5731

17 files changed

+827
-0
lines changed
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Compiler;
13+
14+
use Symfony\Component\DependencyInjection\ContainerBuilder;
15+
use Symfony\Component\DependencyInjection\Definition;
16+
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
17+
use Symfony\Component\DependencyInjection\Reference;
18+
19+
/**
20+
* Guesses constructor arguments of services definitions and try to instantiate services if necessary.
21+
*
22+
* @author Kévin Dunglas <[email protected]>
23+
*/
24+
class AutowirePass implements CompilerPassInterface
25+
{
26+
private $container;
27+
private $reflectionClasses = array();
28+
private $definedTypes = array();
29+
private $types;
30+
private $notGuessableTypes = array();
31+
32+
/**
33+
* {@inheritdoc}
34+
*/
35+
public function process(ContainerBuilder $container)
36+
{
37+
$this->container = $container;
38+
foreach ($container->getDefinitions() as $id => $definition) {
39+
if ($definition->isAutowired()) {
40+
$this->completeDefinition($id, $definition);
41+
}
42+
}
43+
44+
// Free memory and remove circular reference to container
45+
$this->container = null;
46+
$this->reflectionClasses = array();
47+
$this->definedTypes = array();
48+
$this->types = null;
49+
$this->notGuessableTypes = array();
50+
}
51+
52+
/**
53+
* Wires the given definition.
54+
*
55+
* @param string $id
56+
* @param Definition $definition
57+
*
58+
* @throws RuntimeException
59+
*/
60+
private function completeDefinition($id, Definition $definition)
61+
{
62+
if (!$reflectionClass = $this->getReflectionClass($id, $definition)) {
63+
return;
64+
}
65+
66+
$this->container->addClassResource($reflectionClass);
67+
68+
if (!$constructor = $reflectionClass->getConstructor()) {
69+
return;
70+
}
71+
72+
$arguments = $definition->getArguments();
73+
foreach ($constructor->getParameters() as $index => $parameter) {
74+
$argumentExists = array_key_exists($index, $arguments);
75+
if ($argumentExists && '' !== $arguments[$index]) {
76+
continue;
77+
}
78+
79+
try {
80+
if (!$typeHint = $parameter->getClass()) {
81+
continue;
82+
}
83+
84+
if (null === $this->types) {
85+
$this->populateAvailableTypes();
86+
}
87+
88+
if (isset($this->types[$typeHint->name])) {
89+
$value = new Reference($this->types[$typeHint->name]);
90+
} else {
91+
try {
92+
$value = $this->createAutowiredDefinition($typeHint, $id);
93+
} catch (RuntimeException $e) {
94+
if (!$parameter->isDefaultValueAvailable()) {
95+
throw $e;
96+
}
97+
98+
$value = $parameter->getDefaultValue();
99+
}
100+
}
101+
} catch (\ReflectionException $reflectionException) {
102+
// Typehint against a non-existing class
103+
104+
if (!$parameter->isDefaultValueAvailable()) {
105+
continue;
106+
}
107+
108+
$value = $parameter->getDefaultValue();
109+
}
110+
111+
if ($argumentExists) {
112+
$definition->replaceArgument($index, $value);
113+
} else {
114+
$definition->addArgument($value);
115+
}
116+
}
117+
}
118+
119+
/**
120+
* Populates the list of available types.
121+
*/
122+
private function populateAvailableTypes()
123+
{
124+
$this->types = array();
125+
126+
foreach ($this->container->getDefinitions() as $id => $definition) {
127+
$this->populateAvailableType($id, $definition);
128+
}
129+
}
130+
131+
/**
132+
* Populates the list of available types for a given definition.
133+
*
134+
* @param string $id
135+
* @param Definition $definition
136+
*/
137+
private function populateAvailableType($id, Definition $definition)
138+
{
139+
if (!$definition->getClass()) {
140+
return;
141+
}
142+
143+
foreach ($definition->getAutowiringTypes() as $type) {
144+
$this->definedTypes[$type] = true;
145+
$this->types[$type] = $id;
146+
}
147+
148+
if ($reflectionClass = $this->getReflectionClass($id, $definition)) {
149+
$this->extractInterfaces($id, $reflectionClass);
150+
$this->extractAncestors($id, $reflectionClass);
151+
}
152+
}
153+
154+
/**
155+
* Extracts the list of all interfaces implemented by a class.
156+
*
157+
* @param string $id
158+
* @param \ReflectionClass $reflectionClass
159+
*/
160+
private function extractInterfaces($id, \ReflectionClass $reflectionClass)
161+
{
162+
foreach ($reflectionClass->getInterfaces() as $interfaceName => $reflectionInterface) {
163+
$this->set($interfaceName, $id);
164+
165+
$this->extractInterfaces($id, $reflectionInterface);
166+
}
167+
}
168+
169+
/**
170+
* Extracts all inherited types of a class.
171+
*
172+
* @param string $id
173+
* @param \ReflectionClass $reflectionClass
174+
*/
175+
private function extractAncestors($id, \ReflectionClass $reflectionClass)
176+
{
177+
$this->set($reflectionClass->name, $id);
178+
179+
if ($reflectionParentClass = $reflectionClass->getParentClass()) {
180+
$this->extractAncestors($id, $reflectionParentClass);
181+
}
182+
}
183+
184+
/**
185+
* Associates a type and a service id if applicable.
186+
*
187+
* @param string $type
188+
* @param string $id
189+
*/
190+
private function set($type, $id)
191+
{
192+
if (isset($this->definedTypes[$type]) || isset($this->notGuessableTypes[$type])) {
193+
return;
194+
}
195+
196+
if (isset($this->types[$type])) {
197+
if ($this->types[$type] === $id) {
198+
return;
199+
}
200+
201+
unset($this->types[$type]);
202+
$this->notGuessableTypes[$type] = true;
203+
204+
return;
205+
}
206+
207+
$this->types[$type] = $id;
208+
}
209+
210+
/**
211+
* Registers a definition for the type if possible or throws an exception.
212+
*
213+
* @param \ReflectionClass $typeHint
214+
* @param string $id
215+
*
216+
* @return Reference A reference to the registered definition
217+
*
218+
* @throws RuntimeException
219+
*/
220+
private function createAutowiredDefinition(\ReflectionClass $typeHint, $id)
221+
{
222+
if (!$typeHint->isInstantiable()) {
223+
throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s".', $typeHint->name, $id));
224+
}
225+
226+
$argumentId = sprintf('autowired.%s', $typeHint->name);
227+
228+
$argumentDefinition = $this->container->register($argumentId, $typeHint->name);
229+
$argumentDefinition->setPublic(false);
230+
231+
$this->populateAvailableType($argumentId, $argumentDefinition);
232+
$this->completeDefinition($argumentId, $argumentDefinition);
233+
234+
return new Reference($argumentId);
235+
}
236+
237+
/**
238+
* Retrieves the reflection class associated with the given service.
239+
*
240+
* @param string $id
241+
* @param Definition $definition
242+
*
243+
* @return \ReflectionClass|null
244+
*/
245+
private function getReflectionClass($id, Definition $definition)
246+
{
247+
if (isset($this->reflectionClasses[$id])) {
248+
return $this->reflectionClasses[$id];
249+
}
250+
251+
if (!$class = $definition->getClass()) {
252+
return;
253+
}
254+
255+
$class = $this->container->getParameterBag()->resolveValue($class);
256+
257+
try {
258+
return $this->reflectionClasses[$id] = new \ReflectionClass($class);
259+
} catch (\ReflectionException $reflectionException) {
260+
// return null
261+
}
262+
}
263+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public function __construct()
5050
new CheckDefinitionValidityPass(),
5151
new ResolveReferencesToAliasesPass(),
5252
new ResolveInvalidReferencesPass(),
53+
new AutowirePass(),
5354
new AnalyzeServiceReferencesPass(true),
5455
new CheckCircularReferencesPass(),
5556
new CheckReferenceValidityPass(),

src/Symfony/Component/DependencyInjection/Definition.php

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class Definition
4141
private $synchronized = false;
4242
private $lazy = false;
4343
private $decoratedService;
44+
private $autowired = false;
45+
private $autowiringTypes = array();
4446

4547
protected $arguments;
4648

@@ -818,4 +820,96 @@ public function getConfigurator()
818820
{
819821
return $this->configurator;
820822
}
823+
824+
/**
825+
* Sets types that will default to this definition.
826+
*
827+
* @param string[] $types
828+
*
829+
* @return Definition The current instance
830+
*/
831+
public function setAutowiringTypes(array $types)
832+
{
833+
$this->autowiringTypes = array();
834+
835+
foreach ($types as $type) {
836+
$this->autowiringTypes[$type] = true;
837+
}
838+
839+
return $this;
840+
}
841+
842+
/**
843+
* Is the definition autowired?
844+
*
845+
* @return bool
846+
*/
847+
public function isAutowired()
848+
{
849+
return $this->autowired;
850+
}
851+
852+
/**
853+
* Sets autowired.
854+
*
855+
* @param $autowired
856+
*
857+
* @return Definition The current instance
858+
*/
859+
public function setAutowired($autowired)
860+
{
861+
$this->autowired = $autowired;
862+
863+
return $this;
864+
}
865+
866+
/**
867+
* Gets autowiring types that will default to this definition.
868+
*
869+
* @return string[]
870+
*/
871+
public function getAutowiringTypes()
872+
{
873+
return array_keys($this->autowiringTypes);
874+
}
875+
876+
/**
877+
* Adds a type that will default to this definition.
878+
*
879+
* @param string $type
880+
*
881+
* @return Definition The current instance
882+
*/
883+
public function addAutowiringType($type)
884+
{
885+
$this->autowiringTypes[$type] = true;
886+
887+
return $this;
888+
}
889+
890+
/**
891+
* Removes a type.
892+
*
893+
* @param string $type
894+
*
895+
* @return Definition The current instance
896+
*/
897+
public function removeAutowiringType($type)
898+
{
899+
unset($this->autowiringTypes[$type]);
900+
901+
return $this;
902+
}
903+
904+
/**
905+
* Will this definition default for the given type?
906+
*
907+
* @param string $type
908+
*
909+
* @return bool
910+
*/
911+
public function hasAutowiringType($type)
912+
{
913+
return isset($this->autowiringTypes[$type]);
914+
}
821915
}

src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ private function parseDefinition(\DOMElement $service, $file)
157157
}
158158
}
159159

160+
if ($value = $service->getAttribute('autowire')) {
161+
$definition->setAutowired(XmlUtils::phpize($value));
162+
}
163+
160164
if ($value = $service->getAttribute('scope')) {
161165
$triggerDeprecation = 'request' !== (string) $service->getAttribute('id');
162166

@@ -247,6 +251,10 @@ private function parseDefinition(\DOMElement $service, $file)
247251
$definition->addTag($tag->getAttribute('name'), $parameters);
248252
}
249253

254+
foreach ($this->getChildren($service, 'autowiring-type') as $type) {
255+
$definition->addAutowiringType($type->textContent);
256+
}
257+
250258
if ($value = $service->getAttribute('decorates')) {
251259
$renameId = $service->hasAttribute('decoration-inner-name') ? $service->getAttribute('decoration-inner-name') : null;
252260
$priority = $service->hasAttribute('decoration-priority') ? $service->getAttribute('decoration-priority') : 0;

0 commit comments

Comments
 (0)