Skip to content
Permalink
Browse files

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
  • Loading branch information...
falkenhawk committed Apr 26, 2019
1 parent 65987ec commit 59a22168215d68cb57b32b5cb715439bec773b4f
@@ -56,6 +56,11 @@ class ContainerBuilder
*/
private $useAnnotations = false;
/**
* @var int
*/
private $annotationsFlags = 0;
/**
* @var bool
*/
@@ -126,7 +131,7 @@ public function build()
$sources = array_reverse($this->definitionSources);
if ($this->useAnnotations) {
$autowiring = new AnnotationBasedAutowiring($this->ignorePhpDocErrors);
$autowiring = new AnnotationBasedAutowiring($this->ignorePhpDocErrors, $this->annotationsFlags);
$sources[] = $autowiring;
} elseif ($this->useAutowiring) {
$autowiring = new ReflectionBasedAutowiring;
@@ -239,11 +244,12 @@ public function useAutowiring(bool $bool) : self
*
* @return $this
*/
public function useAnnotations(bool $bool) : self
public function useAnnotations(bool $bool, int $flags = 0) : self
{
$this->ensureNotLocked();
$this->useAnnotations = $bool;
$this->annotationsFlags = $flags;
return $this;
}
@@ -9,4 +9,26 @@
*/
class AutowireDefinition extends ObjectDefinition
{
/**
* @var bool|null
*/
protected $useAnnotations;
/**
* Enable/disable reading annotations for this definition, regardless of a container configuration.
* @param bool $flag
*/
public function useAnnotations(bool $flag = true)
{
$this->useAnnotations = $flag;
}
/**
* Returns boolean if the useAnnotation flag was explicitly set, otherwise null.
* @return bool|null
*/
public function isUsingAnnotations()
{
return $this->useAnnotations;
}
}
@@ -5,6 +5,7 @@
namespace DI\Definition\Helper;
use DI\Definition\AutowireDefinition;
use DI\Definition\Definition;
/**
* Helps defining how to create an instance of a class using autowiring.
@@ -15,6 +16,8 @@ class AutowireDefinitionHelper extends CreateDefinitionHelper
{
const DEFINITION_CLASS = AutowireDefinition::class;
protected $useAnnotations;
/**
* Defines a value for a specific argument of the constructor.
*
@@ -69,4 +72,31 @@ public function methodParameter(string $method, $parameter, $value)
return $this;
}
/**
* Define if entry should use annotation reader for reading dependencies.
* This is turned off by default if autowire() helper is used, and turned on if entry is not defined explicitly in the di config.
* @param bool $useAnnotations
* @return $this
*/
public function useAnnotations(bool $useAnnotations = true)
{
$this->useAnnotations = $useAnnotations;
return $this;
}
/**
* @return AutowireDefinition
*/
public function getDefinition(string $entryName) : Definition
{
/** @var AutowireDefinition $definition */
$definition = parent::getDefinition($entryName);
if ($this->useAnnotations !== null) {
$definition->useAnnotations($this->useAnnotations);
}
return $definition;
}
}
@@ -6,6 +6,7 @@
use DI\Annotation\Inject;
use DI\Annotation\Injectable;
use DI\Definition\AutowireDefinition;
use DI\Definition\Exception\InvalidAnnotation;
use DI\Definition\ObjectDefinition;
use DI\Definition\ObjectDefinition\MethodInjection;
@@ -32,6 +33,25 @@
*/
class AnnotationBasedAutowiring implements DefinitionSource, Autowiring
{
// Annotations configuration flags:
// enable on implicit definitions
const IMPLICIT = 1;
// enable on all autowire definitions (which are written in DI config) by default
const EXPLICIT = 2;
// read @Injectable annotations for classes
const INJECTABLE = 4;
// read @Inject annotations for properties
const PROPERTIES = 8;
// read @Inject annotations for methods' parameters
const METHODS = 16;
// all options enabled
const ALL = 31;
/**
* @var int
*/
private $flags;
/**
* @var Reader
*/
@@ -47,9 +67,10 @@ class AnnotationBasedAutowiring implements DefinitionSource, Autowiring
*/
private $ignorePhpDocErrors;
public function __construct($ignorePhpDocErrors = false)
public function __construct($ignorePhpDocErrors = false, int $flags = 0)
{
$this->ignorePhpDocErrors = (bool) $ignorePhpDocErrors;
$this->flags = $flags > 0 ? $flags : self::ALL; // all flags turned on by default
}
public function autowire(string $name, ObjectDefinition $definition = null)
@@ -61,16 +82,35 @@ public function autowire(string $name, ObjectDefinition $definition = null)
}
$definition = $definition ?: new ObjectDefinition($name);
$useAnnotations = $definition instanceof AutowireDefinition
? ($definition->isUsingAnnotations() ?? ($this->flags & self::EXPLICIT))
: ($this->flags & self::IMPLICIT);
$class = new ReflectionClass($className);
$class = null;
if ($useAnnotations && $this->flags >= self::INJECTABLE) {
$class = new ReflectionClass($className);
$this->readInjectableAnnotation($class, $definition);
if ($this->flags & self::INJECTABLE) {
$this->readInjectableAnnotation($class, $definition);
}
// Browse the class properties looking for annotated properties
$this->readProperties($class, $definition);
// Browse the class properties looking for annotated properties
if ($this->flags & self::PROPERTIES) {
$this->readProperties($class, $definition);
}
// Browse the object's methods looking for annotated methods
$this->readMethods($class, $definition);
// Browse the object's methods looking for annotated methods
if ($this->flags & self::METHODS) {
$this->readMethods($class, $definition);
}
}
// constructor parameters should always be read, even if annotations are disabled (completely or i.a. for methods)
// so that it behaves at least as ReflectionBasedAutowiring
if (!$useAnnotations || !($this->flags & self::METHODS)) {
$class = $class ?? new ReflectionClass($className);
$this->readConstructor($class, $definition);
}
return $definition;
}
@@ -166,6 +206,28 @@ private function readMethods(ReflectionClass $class, ObjectDefinition $objectDef
}
}
/**
* Browse the object's constructor parameters and inject dependencies.
*/
private function readConstructor(ReflectionClass $class, ObjectDefinition $definition)
{
if (!($constructor = $class->getConstructor()) || !$constructor->isPublic()) {
return;
}
$parameters = [];
foreach ($constructor->getParameters() as $index => $parameter) {
$entryName = $this->getMethodParameter($index, $parameter, []);
if ($entryName !== null) {
$parameters[$index] = new Reference($entryName);
}
}
$constructorInjection = MethodInjection::constructor($parameters);
$definition->completeConstructorInjection($constructorInjection);
}
/**
* @param ReflectionMethod $method
*

0 comments on commit 59a2216

Please sign in to comment.
You can’t perform that action at this time.