让我们看一个例子:
class UserProvider{ protected $connection; public function __construct(){ $this->connection = new Connection; } public function retrieveByCredentials( array $credentials ){ $user = $this->connection ->where( 'email', $credentials['email']) ->where( 'password', $credentials['password']) ->first(); return $user; } }
如果你要测试或者维护这个类,你必须访问数据库的实例来进行一些查询。为了避免必须这样做,你可以将此类与其他类进行 解耦 ,你有三个选项之一,可以将 Connection
类注入而不需要直接使用它。
将组件注入类时,可以使用以下三个选项之一:
构造方法注入
class UserProvider{ protected $connection; public function __construct( Connection $con ){ $this->connection = $con; } }
Setter 方法注入
同样,我们也可以使用 Setter 方法注入依赖关系:
class UserProvider{ protected $connection; public function __construct(){ ... } public function setConnection( Connection $con ){ $this->connection = $con; } }
接口注入
interface ConnectionInjector{ public function injectConnection( Connection $con ); } class UserProvider implements ConnectionInjector{ protected $connection; public function __construct(){ ... } public function injectConnection( Connection $con ){ $this->connection = $con; } }
当一个类实现了我们的接口时,我们定义了 injectConnection
方法来解决依赖关系。
优势
现在,当测试我们的类时,我们可以模拟依赖类并将其作为参数传递。每个类必须专注于一个特定的任务,而不应该关心解决它们的依赖性。这样,你将拥有一个更专注和可维护的应用程序。
======================= 华丽的分割线 ===================
目录结构:
部分代码:
index.php
<?php spl_autoload_register('autoLoad', true, true); use aTestA; use bTestB; // 要调用的类 $class = TestA::class; // 调用类中的方法 $method = 'hello'; $obj = createObj($class); $methodParam = parseMethod(TestA::class, $method); call_user_func_array([$obj, $method], $methodParam); /** * 根据类名 创建对象 并自动注入 * @param string $className * @return object * @throws ReflectionException */ function createObj(string $className): object { $reflction = new ReflectionClass($className); $paramList = parseConstructor($reflction); $obj = $reflction->newInstanceArgs($paramList); return $obj; } /** * 解析类的构造函数,返回参数列表 * @param ReflectionClass $reflction * @return array */ function parseConstructor(ReflectionClass $reflction): array { $paramList = []; // 如果构造方法存在,返回 object(ReflectionMethod) 反之返回 null $constructor = $reflction->getConstructor(); if (!is_null($constructor)) { // 返回数组 object(ReflectionParameter) 参数列表 $params = $constructor->getParameters(); foreach ($params as $param) { // 获取参数的类型提示类,类型为 object(ReflectionClass) 若没有返回 null if (!is_null($param->getClass())) { // 获取类名 $paramList[] = createObj($param->getClass()->getName()); } else { // 获取默认值 $paramList[] = $param->getDefaultValue(); } } } return $paramList; } /** * 解析返回类的某方法参数 * * @param string $className * @param string $methodName * @return array * @throws ReflectionException */ function parseMethod(string $className, string $methodName) { $paramList = []; // object(ReflectionMethod) $reflection = new ReflectionMethod($className, $methodName); $params = $reflection->getParameters(); foreach ($params as $param) { // 获取参数的类型提示类,类型为 object(ReflectionClass) 若没有返回 null if (!is_null($param->getClass())) { // 获取类名 $paramList[] = createObj($param->getClass()->getName()); } else { // 获取默认值 $paramList[] = $param->getDefaultValue(); } } return $paramList; } function autoLoad($className) { require_once __DIR__.'/'.$className.'.php'; } function dump($data) { echo '<pre>'; var_dump($data); exit(); }
class TestA
<?php namespace a; use bTestB; use faceTest; class TestA implements Test { const VERSION = 'A'; public $name; public $objB; public function __construct(TestB $b, $name='aaa') { $this->objB = $b; $this->name = $name; } public function hello(TestB $b) { var_dump($b); } }
class TestB
<?php namespace b; use faceTest; use cTestC; class TestB implements Test { const VERSION = 'B'; public $name; public $objC; public function __construct($name = 'bbb', TestC $c) { $this->objC = $c; $this->name = $name; } }
class TestC
<?php namespace c; use faceTest; class TestC implements Test { const VERSION = 'C'; public function __construct($name = 'ccc') { } }
class Face
<?php namespace face; interface Test { }
另一个实例:
<?php class Point { public $x; public $y; public function __construct($x = 0, $y = 0) { $this->x = $x; $this->y = $y; } } class Circle { protected $radius; protected $center; public function __construct(Point $point, $raduis = 1) { $this->center = $point; $this->radius = $raduis; } public function printCenter() { printf('center coordinate is (%d, %d)', $this->center->x, $this->center->y); } public function area() { return 3.14 * pow($this->radius, 2); } } class DI { public function make($className) { if (class_exists($className)) { $reflectionClass = new ReflectionClass($className); $constructor = $reflectionClass->getConstructor(); if (is_null($constructor)) { return $reflectionClass->newInstance(); } else { $parameters = $constructor->getParameters(); $dependencies = $this->getDependencies($parameters); return $reflectionClass->newInstanceArgs($dependencies); } } else { printf('%s', 'class is not exist'); } } // 依赖解析 protected function getDependencies($parameters) { $dependencies = []; foreach ($parameters as $parameter) { $dependency = $parameter->getClass(); if (is_null($dependency)) { if ($parameter->isDefaultValueAvailable()) { $dependencies[] = $parameter->getDefaultValue(); } else { //不是可选参数的为了简单直接赋值为字符串0 //针对构造方法的必须参数这个情况 //laravel是通过service provider注册closure到IocContainer, //在closure里可以通过return new Class($param1, $param2)来返回类的实例 //然后在make时回调这个closure即可解析出对象 //具体细节我会在另一篇文章里面描述 $dependencies[] = '0'; } } else { //递归解析出依赖类的对象 $dependencies[] = $this->make($parameter->getClass()->name); } } return $dependencies; } } echo '<pre>'; $obj = (new DI)->make(Circle::class); $obj->printCenter();
参考文档:
https://juejin.im/post/5cac58ecf265da03a85a9f9a#heading-0
https://juejin.im/post/5ae292e7518825673446c1fe
https://www.insp.top/learn-laravel-container
https://github.com/kevinyan815/Learning_Laravel_Kernel/blob/master/articles/reflection.md