让我们看一个例子:

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. [qemu][kvm] 在kvm嵌套kvm的虚拟机里启动kvm加速

    常规情况下,如果在kvm的虚拟机里,又想使用kvm的虚拟机,会报如下的错误信息: [root@host0 nlb]# Could not access KVM kernel module: No su ...

  2. Windows 10 家庭版/专业版 彻底关闭windows update自动更新

    转载: https://blog.csdn.net/u014162133/article/details/84973426# https://blog.csdn.net/qq_40820862/art ...

  3. jQuery 学习笔记(4)(文本值相关方法、操控CSS方法、位置和尺寸方法)

    1.文本值相关方法 .html() == .innerHTML $("div").html("<span> ...</span>") / ...

  4. 【托业】新托业全真题库---TEST1

    clearly indicate ——clearly可以修饰indicate(表明:暗示:指示) recently只用于现在完成时和过去完成时中 municipal gallery 市立美术馆 per ...

  5. Centos下搭建邮件服务器

    一.协议 SMTP:用于发送邮件 POP3:用于接收邮件,接收后会将服务器上邮件删除 IMAP:用于接收邮件,接收后不会删除服务器邮件 二.几个重要的角色 MUA:可以理解为收取邮件的工具,比如thu ...

  6. block,inline和inline-block概念和区别(转载)

    转自: http://www.cnblogs.com/KeithWang/p/3139517.html 总体概念 block和inline这两个概念是简略的说法,完整确切的说应该是 block-lev ...

  7. [转] Mac系统Robot Framework环境搭建

    一.由于Mac系统下自带python,所以不需要再进行安装了 二.关闭mac电脑的sip, 1.重启 Mac并长按 Cmd + R 2.打开终端,执行csrutil disable命令 3.重启电脑 ...

  8. supervisor 守护进程

    一.supervisor 安装 1.yum -y install epel-release 2.yum -y install supervisor 二.supervisor 配置文件详解 三.supe ...

  9. vue项目中postcss-pxtorem的使用及webpack中的配置 css中单位px和em,rem的区别

    移动手机版要求我们在制作嵌入h5的时候去适配不同的手机.适配有多重模式,有flex.百分比等.字体大小的控制也有px.百分比.rem等单位,webpack中 px转rem. vue项目中postcss ...

  10. .net core webapi+vue 跨域访问

    最近在做一个前后端分离的示例,以下代码完美解决跨域的问题 一.后端服务 1.首先我们建一个.net core webapi的项目 2.项目引用Microsoft.AspNetCore.Cors 包 3 ...