Dependency Injection(DI), also known as Inversion of Control, is a design pattern that promotes loose coupling between objects , it doesn't directly create dependencies in the class , but relies on external mechanisms to provide dependencies . Many people look at the name and think it is a very high-end thing. Today we come to solve the mystery of it. Not much to say, directly on the code:
class Ioc {
public static function getInstance($className) {
$paramArr = self::getMethodParams($className);
return (new ReflectionClass($className))->newInstanceArgs($paramArr);
}
public static function make($className, $methodName, $params = []) {
$instance = self::getInstance($className);
$paramArr = self::getMethodParams($className, $methodName);
return $instance->{$methodName}(...array_merge($paramArr, $params));
}
protected static function getMethodParams($className, $methodsName = '__construct') {
$class = new ReflectionClass($className);
$paramArr = [];
if ($class->hasMethod($methodsName)) {
$construct = $class->getMethod($methodsName);
$params = $construct->getParameters();
if (count($params) > 0) {
foreach ($params as $key => $param) {
if ($paramClass = $param->getClass()) {
$paramClassName = $paramClass->getName();
$args = self::getMethodParams($paramClassName);
$paramArr[] = (new ReflectionClass($paramClass->getName()))->newInstanceArgs($args);
}
}
}
}
return $paramArr;
}
}
The above codes use php's reflection functions to create a container class that is used to implement dependency injection functionality for other classes. The above dependency injection is divided into two types, one is constructor dependency injection and the other is method dependency injection. We use the following three classes to do the next test.
class A {
protected $cObj;
public function __construct(C $c) {
$this->cObj = $c;
}
public function aa() {
echo 'this is A->test';
}
public function aac() {
$this->cObj->cc();
}
}
class B {
protected $aObj;
public function __construct(A $a) {
$this->aObj = $a;
}
public function bb(C $c, $b) {
$c->cc();
echo "\r\n";
echo 'params:' . $b;
}
public function bbb() {
$this->aObj->aac();
}
}
class C {
public function cc() {
echo 'this is C->cc';
}
}
Tests for constructor dependency injection:
$bObj = Ioc::getInstance('B');
$bObj->bbb(); // output:this is C->cc , means dependency injection was successful。
// print $bObj
var_dump($bObj);
// Print the results, you can see that there is an instance of A in B and an instance of C in A, indicating that dependency injection was successful.
object(B)#3 (1) {
["aObj":protected]=>
object(A)#7 (1) {
["cObj":protected]=>
object(C)#10 (0) {
}
}
}
Test method dependency injection:
Ioc::make('B', 'bb', ['this is param b']);
// print the results,we can see dependency injection was successful。
this is C->cc
params:this is param b
As we can see from the two examples above, when we create an object or call a method, we don't even need to know which class or method it depends on. Reflection makes it easy for us to automatically inject the classes we need.