consider this example:
class MyClass { public function doSomething() { $this->injected->getIt(); } }
so far so simple (apart from injected
is not injected). So, in full version:
class MyClass { /** * @var Injected */ private $injected; public function __constructor(Injected $injected) { $this->injected = $injected; } public function doSomething() { $this->injected->getIt(); } }
but I find it enoromous. Why to pollute my class with tons of code of DI? Lets split this into two entities:
trait MyClassTrait { /** * @var Injected */ private $injected; public function __constructor(Injected $injected) { $this->injected = $injected; } } class MyClass { use MyClassTrait; public function doSomething() { $this->injected->getIt(); } }
its much nicer although I never seen anybody using it like this. Is it a good approach?
Advertisement
Answer
For example like this:
<?php class Factory { private $services = []; public function __construct() { $this->services[self::class] = $this; } public function getByType($type){ if(isset($services[$type])){ return $services[$type]; } if(class_exists($type)){ $reflection = new ReflectionClass($type); $constructor = $reflection->getConstructor(); $parameters = []; if($constructor) foreach($constructor->getParameters() as $parameter){ if($parameter->getClass()) { $parameters[] = $this->getByType($parameter->getClass()->name); } else if($parameter->isDefaultValueAvailable()){ $parameters[] = $parameter->getDefaultValue(); } } return $services[$type] = $reflection->newInstanceArgs($parameters); } // else throw Exception... } } abstract class DI { public function __construct(Factory $factory) { $reflection = new ReflectionClass(get_class($this)); foreach($reflection->getProperties() as $property){ preg_match('/@var ([^ ]+) @inject/', $property->getDocComment(), $annotation); if($annotation){ $className = $annotation[1]; if(class_exists($className)){ $property->setAccessible(true); $property->setValue($this, $factory->getByType($className)); } // else throw Exception... } } } } class Injected { public function getIt($string){ echo $string.'<br />'; } } class DIByConstructor { /** @var Injected */ private $byConstructor; public function __construct(Injected $injected) { $this->byConstructor = $injected; } public function doSomething() { echo 'Class: '.self::class.'<br />'; $this->byConstructor->getIt('By Constructor'); echo '<br />'; } } class DIByAnnotation extends DI { /** @var Injected @inject */ private $byAnnotation; public function doSomething() { echo 'Class: '.self::class.'<br />'; $this->byAnnotation->getIt('By Annotation'); echo '<br />'; } } class DIBothMethods extends DI { /** @var Injected */ private $byConstructor; /** @var Injected @inject */ private $byAnnotation; public function __construct(Factory $factory, Injected $injected) { parent::__construct($factory); $this->byConstructor = $injected; } public function doSomething() { echo 'Class: '.self::class.'<br />'; $this->byConstructor->getIt('By Constructor'); $this->byAnnotation->getIt('By Annotation'); echo '<br />'; } } $factory = new Factory(); $DIByConstructor = $factory->getByType('DIByConstructor'); $DIByConstructor->doSomething(); $DIByAnnotation = $factory->getByType('DIByAnnotation'); $DIByAnnotation->doSomething(); $DIBothMethods = $factory->getByType('DIBothMethods'); $DIBothMethods->doSomething();