Skip to content

Commit 9d6e0b3

Browse files
falkenhawkpartikus
authored andcommitted
AnnotationBasedAutowiring with additional options for performance tweaks
- adds possibility to disable features which are not used - useAnnotations(boolean) toggle on autowire() helper to enable/disable reading annotations on specific definitions
1 parent 28d15cb commit 9d6e0b3

File tree

4 files changed

+126
-10
lines changed

4 files changed

+126
-10
lines changed

src/ContainerBuilder.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ class ContainerBuilder
5252

5353
private bool $useAutowiring = true;
5454

55+
private int $annotationsFlags = 0;
56+
57+
private bool $ignorePhpDocErrors = false;
58+
5559
private bool $useAttributes = false;
5660

5761
/**
@@ -146,7 +150,7 @@ public function build()
146150
$this->compileToDirectory,
147151
$containerClass,
148152
$this->containerParentClass,
149-
$this->useAutowiring
153+
$this->useAutowiring || $this->useAttributes
150154
);
151155
// Only load the file if it hasn't been already loaded
152156
// (the container can be created multiple times in the same process)
@@ -216,11 +220,12 @@ public function useAutowiring(bool $bool) : self
216220
*
217221
* @return $this
218222
*/
219-
public function useAttributes(bool $bool) : self
223+
public function useAttributes(bool $bool, int $flags = 0) : self
220224
{
221225
$this->ensureNotLocked();
222226

223227
$this->useAttributes = $bool;
228+
$this->annotationsFlags = $flags;
224229

225230
return $this;
226231
}

src/Definition/AutowireDefinition.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,26 @@
99
*/
1010
class AutowireDefinition extends ObjectDefinition
1111
{
12+
/**
13+
* @var bool|null
14+
*/
15+
protected $useAnnotations;
16+
17+
/**
18+
* Enable/disable reading annotations for this definition, regardless of a container configuration.
19+
* @param bool $flag
20+
*/
21+
public function useAnnotations(bool $flag = true)
22+
{
23+
$this->useAnnotations = $flag;
24+
}
25+
26+
/**
27+
* Returns boolean if the useAnnotation flag was explicitly set, otherwise null.
28+
* @return bool|null
29+
*/
30+
public function isUsingAnnotations()
31+
{
32+
return $this->useAnnotations;
33+
}
1234
}

src/Definition/Helper/AutowireDefinitionHelper.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace DI\Definition\Helper;
66

77
use DI\Definition\AutowireDefinition;
8+
use DI\Definition\Definition;
89

910
/**
1011
* Helps defining how to create an instance of a class using autowiring.
@@ -15,6 +16,8 @@ class AutowireDefinitionHelper extends CreateDefinitionHelper
1516
{
1617
public const DEFINITION_CLASS = AutowireDefinition::class;
1718

19+
protected $useAnnotations;
20+
1821
/**
1922
* Defines a value for a specific argument of the constructor.
2023
*
@@ -69,4 +72,31 @@ public function methodParameter(string $method, string|int $parameter, mixed $va
6972

7073
return $this;
7174
}
75+
76+
/**
77+
* Define if entry should use annotation reader for reading dependencies.
78+
* This is turned off by default if autowire() helper is used, and turned on if entry is not defined explicitly in the di config.
79+
* @param bool $useAnnotations
80+
* @return $this
81+
*/
82+
public function useAnnotations(bool $useAnnotations = true)
83+
{
84+
$this->useAnnotations = $useAnnotations;
85+
86+
return $this;
87+
}
88+
89+
/**
90+
* @return AutowireDefinition
91+
*/
92+
public function getDefinition(string $entryName) : Definition
93+
{
94+
/** @var AutowireDefinition $definition */
95+
$definition = parent::getDefinition($entryName);
96+
if ($this->useAnnotations !== null) {
97+
$definition->useAnnotations($this->useAnnotations);
98+
}
99+
100+
return $definition;
101+
}
72102
}

src/Definition/Source/AttributeBasedAutowiring.php

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use DI\Attribute\Inject;
88
use DI\Attribute\Injectable;
9+
use DI\Definition\AutowireDefinition;
910
use DI\Definition\Exception\InvalidAttribute;
1011
use DI\Definition\ObjectDefinition;
1112
use DI\Definition\ObjectDefinition\MethodInjection;
@@ -28,9 +29,25 @@
2829
*/
2930
class AttributeBasedAutowiring implements DefinitionSource, Autowiring
3031
{
31-
/**
32-
* @throws InvalidAttribute
33-
*/
32+
// Annotations configuration flags:
33+
// enable on implicit definitions
34+
const IMPLICIT = 1;
35+
// enable on all autowire definitions (which are written in DI config) by default
36+
const EXPLICIT = 2;
37+
// read @Injectable annotations for classes
38+
const INJECTABLE = 4;
39+
// read @Inject annotations for properties
40+
const PROPERTIES = 8;
41+
// read @Inject annotations for methods' parameters
42+
const METHODS = 16;
43+
// all options enabled
44+
const ALL = 31;
45+
46+
public function __construct(private int $flags = 0)
47+
{
48+
$this->flags = $flags > 0 ? $flags : self::ALL; // all flags turned on by default
49+
}
50+
3451
public function autowire(string $name, ObjectDefinition $definition = null) : ObjectDefinition|null
3552
{
3653
$className = $definition ? $definition->getClassName() : $name;
@@ -40,16 +57,36 @@ public function autowire(string $name, ObjectDefinition $definition = null) : Ob
4057
}
4158

4259
$definition = $definition ?: new ObjectDefinition($name);
60+
$useAnnotations = $definition instanceof AutowireDefinition
61+
? ($definition->isUsingAnnotations() ?? ($this->flags & self::EXPLICIT))
62+
: ($this->flags & self::IMPLICIT);
4363

44-
$class = new ReflectionClass($className);
64+
$class = null;
65+
if ($useAnnotations && $this->flags >= self::INJECTABLE) {
66+
$class = new ReflectionClass($className);
4567

4668
$this->readInjectableAttribute($class, $definition);
69+
if ($this->flags & self::INJECTABLE) {
70+
$this->readInjectableAnnotation($class, $definition);
71+
}
4772

48-
// Browse the class properties looking for annotated properties
49-
$this->readProperties($class, $definition);
73+
// Browse the class properties looking for annotated properties
74+
if ($this->flags & self::PROPERTIES) {
75+
$this->readProperties($class, $definition);
76+
}
5077

51-
// Browse the object's methods looking for annotated methods
52-
$this->readMethods($class, $definition);
78+
// Browse the object's methods looking for annotated methods
79+
if ($this->flags & self::METHODS) {
80+
$this->readMethods($class, $definition);
81+
}
82+
}
83+
84+
// constructor parameters should always be read, even if annotations are disabled (completely or i.a. for methods)
85+
// so that it behaves at least as ReflectionBasedAutowiring
86+
if (!$useAnnotations || !($this->flags & self::METHODS)) {
87+
$class = $class ?? new ReflectionClass($className);
88+
$this->readConstructor($class, $definition);
89+
}
5390

5491
return $definition;
5592
}
@@ -260,4 +297,26 @@ private function readInjectableAttribute(ReflectionClass $class, ObjectDefinitio
260297
$definition->setLazy($attribute->isLazy());
261298
}
262299
}
300+
301+
/**
302+
* Browse the object's constructor parameters and inject dependencies.
303+
*/
304+
private function readConstructor(ReflectionClass $class, ObjectDefinition $definition)
305+
{
306+
if (!($constructor = $class->getConstructor()) || !$constructor->isPublic()) {
307+
return;
308+
}
309+
310+
$parameters = [];
311+
foreach ($constructor->getParameters() as $index => $parameter) {
312+
$entryName = $this->getMethodParameter($index, $parameter, []);
313+
314+
if ($entryName !== null) {
315+
$parameters[$index] = new Reference($entryName);
316+
}
317+
}
318+
319+
$constructorInjection = MethodInjection::constructor($parameters);
320+
$definition->completeConstructorInjection($constructorInjection);
321+
}
263322
}

0 commit comments

Comments
 (0)