让我们看一个例子:

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 a\TestA;
use b\TestB; // 要调用的类
$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 b\TestB;
use face\Test; 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 face\Test;
use c\TestC; 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 face\Test;

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

依赖注入demo的更多相关文章

  1. 该项目的建设maven片:4.协调和依赖,spring依赖注入demo

    源码下载 协调 <groupId>com.demo.animal</groupId> <artifactId>animal-core</artifactId& ...

  2. PHP 通过构造器进行依赖注入 demo

    class A{ public $b; public $f; function __construct( B $b , $f = 1 ){ $this->b = $b; $this->f ...

  3. 【Java】 Spring依赖注入小试牛刀:编写第一个Spring ApplicationContext Demo

    0  Spring的依赖注入大致是这样工作的: 将对象如何构造(ID是什么?是什么类型?给属性设置什么值?给构造函数传入什么值?)写入外部XML文件里.在调用者需要调用某个类时,不自行构造该类的对象, ...

  4. Spring之IOC/DI(反转控制/依赖注入)_入门Demo

    在平时的java应用开发中,我们要实现某一个功能或者说是完成某个业务逻辑时至少需要两个或以上的对象来协作完成,在没有使用Spring的时候,每个对象在需要使用他的合作对象时,自己均要使用像new ob ...

  5. angular2系列教程(八)In-memory web api、HTTP服务、依赖注入、Observable

    大家好,今天我们要讲是angular2的http功能模块,这个功能模块的代码不在angular2里面,需要我们另外引入: index.html <script src="lib/htt ...

  6. angularjs 依赖注入--自己学着实现

    在用angular依赖注入时,感觉很好用,他的出现是 为了"削减计算机程序的耦合问题" ,我怀着敬畏与好奇的心情,轻轻的走进了angular源码,看看他到底是怎么实现的,我也想写个 ...

  7. 查看.NET Core源代码通过Autofac实现依赖注入到Controller属性

    一.前言 在之前的文章[ASP.NET Core 整合Autofac和Castle实现自动AOP拦截]中,我们讲过除了ASP.NETCore自带的IOC容器外,如何使用Autofac来接管IServi ...

  8. C# 依赖注入

      http://www.cnblogs.com/leoo2sk/archive/2009/06/17/1504693.html 这篇文章真的非常非常好···绝对值得收藏学习.     目录 目录 1 ...

  9. 采用dom4j和反射模拟Spring框架的依赖注入功能

    Spring的依赖注入是指将对象的创建权交给Spring框架,将对象所依赖的属性注入进来的行为.在学习了dom4j后,其实也可以利用dom4j和反射做一个小Demo模拟Spring框架的这种功能.下面 ...

随机推荐

  1. SparkContext初始化之SparkScheduler初始化

  2. java框架之SpringCloud(5)-Hystrix服务熔断、降级与监控

    前言 分布式系统面临的问题 复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败.不做任何处理的情况下,很容易导致服务雪崩. 服务雪崩:多个微服务之间调用的时候,假设 ...

  3. ndarray的用法总结

    #发现ndarray的一维,二维都可以用[i][j], 它们都是下标索引的连用, 比如j表示第j个元素;#二维ndarray可以用[m, n]来进行行列的操作,类似matlab中的用法.取某一列是[: ...

  4. XgBoost推导与总结

    一 .机器学习算法中GBDT和XGBOOST的区别有哪些?(转自知乎https://www.zhihu.com/question/41354392/answer/98658997) xgboost相比 ...

  5. swagger:API在线文档自动生成框架

    传统的API从开发测试开始我们经常借用类似Postman.fiddle等等去做接口测试等等工具:Swagger 为API的在线测试.在线文档提供了一个新的简便的解决方案: NET 使用Swagger ...

  6. cocos2d-x JS 富文本

    var str1 = "兑换成功后,系统会生成“";var str2 = "红包兑换码";var str3 = "”,请复制该兑换码,并粘贴在&quo ...

  7. IdentityServer4授权和认证对接数据库

    接着上一篇讲:https://www.cnblogs.com/nsky/p/10352678.html 我们之前都是用in-men的方式把数据添加到内存了,目的是为了测试方便, 现在我们把所有配置都添 ...

  8. SV class

    SV中的class通过new来创建实例,new函数可以传递多个参数: packet P1:  P1 = new:创建一个P1的对象: P2 = P1:P2也指向同一个对象: P2 = new P1:P ...

  9. 用mpvue构建微信小程序

    背景 由于机器人协会进行鼓励大家多读书的活动,所以为了可以更好的.更有效果,所以我跟会长提了一个建议,做一个微信小程序,那么为什么是微信小程序呢? 1.现在微信小程序比较好,用户也比较多:利用微信小程 ...

  10. keepalived weight正负值问题(实现主服务器nginx故障后迅速切换到备服务器)

    有两台负载均衡,lb01,lb02.  lb02, priority值为100 编辑keepalived配置文件   vim /etc/keepalived/keepalived.conf ! Con ...