让我们看一个例子:

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. python->解析xml文件

    '''"D:\three_test\gpn_InternetGatewayDevice_v2.xml" <SOAP-ENV:Envelope> <SOAP-ENV ...

  2. python连接mysql-PyMySql模块

    安装 pip3 install pymysql 使用 输出mysql版本 import pymysql # 打开数据库连接 db = pymysql.connect("localhost&q ...

  3. 【UML】NO.70.EBook.9.UML.4.001-【PowerDesigner 16 从入门到精通】- 基础概念

    1.0.0 Summary Tittle:[UML]NO.70.EBook.9.UML.4.001-[PowerDesigner 16 从入门到精通]-  基础概念 Style:DesignPatte ...

  4. char 类型的操作函数

    1.内存充填 void *memset(void *s,int ch,size_t n); 是由C Run-time Library提供的提供的函数,作用是在一段内存块中填充某个给定的值,它是对较大的 ...

  5. utf8 vs utf8mb4

    UTF-8 is a variable-length encoding. In the case of UTF-8, this means that storing one code point re ...

  6. VS2015 新建 ASP.NET Web应用程序, 此模板尝试加载程序集‘Microsoft.VisualStudio.Web.Project’, 解决方案

    下载并安装Azure的SDK即可:

  7. angular中的MVC思想

    MVC是一种使用 MVC(Model View Controller 模型-视图-控制器)设计模式,该模型的理念也被许多框架所吸纳.在学习angular的过程中,我在网上查找关于angular MVC ...

  8. timer控件、三级联动、帐号激活权限设置

    一.Timer控件 Timer实际就是一个线程控件. 属性:Enabled    是否被启用 Interval     多长时间执行一次控件中的代码 事件: Tick     事件中放要执行的代码. ...

  9. fpga板制作调试过程记录

    2010-09-11 22:49:00 昨天淘宝准备买块fpga核心板学习,为了练习焊接,我让老板给我散料及pcb板自己焊接. 一,在物料到之前的准备: 我先设计了一下焊接测试计划 1,检查电路板:特 ...

  10. jquery改变字符串中部分字符的颜色

    //该方法改变字符串中中括号内(包括中括号)的字符串颜色为红色function changecolocer() { var zf = $('#YWFA').text(); if(zf.length&g ...