让我们看一个例子:

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. bugfree3.0.1-导入excel测试用例

    大多数项目里只用BugFree做缺陷管理工具,其实还可以通过该工具导入测试用例,记录测试结果,最后获得统计结果. 难点 1.导入文件要求XML格式: 2.一般我们的测试用例都是用excle文件存取,很 ...

  2. java框架之SpringBoot(15)-安全及整合SpringSecurity

    SpringSecurity介绍 Spring Security 是针对 Spring 项目的安全框架,也是 Spring Boot 底层安全模块默认的技术选型.它可以实现强大的 Web 安全控制.对 ...

  3. [js]js中变量带var和不带var的区别

    上图已说的很清晰了. 下面代码是赘述 <script> //带var和不带var的区别: // 1.只有带var的才可以预解释,所以在赋值的前操作不会报错. console.log(num ...

  4. [js]js设计模式-原型模式

    构造函数模型- 对象的属性和方法每人一份 function createJs(name, age) { this.name = name; this.age = age; this.writeJs = ...

  5. 关于${pageContext.request.contextPath}的理解 (转载)

    ${pageContext.request.contextPath}是JSP取得绝对路径的方法,等价于<%=request.getContextPath()%> . 也就是取出部署的应用程 ...

  6. 利用spring实现服务启动就自动执行某些操作的2种方式

    第一种方式,用bean的init-method属性 <bean class="com.emax.paycenter.log.LogBridge" init-method=&q ...

  7. 《linux就该这么学》第五节课,shell脚本的各种语句!

    第四章shell语句 (据课本和虚拟机实验排版,借鉴请改动)               4.2:shell脚本   脚本包括:脚本声明,脚本注释,脚本内容和命令 例:#!/bin/bash      ...

  8. windows程序设计 创建一个新的窗口

    #include <windows.h> LRESULT CALLBACK myProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(H ...

  9. IT人员如何开好站立会议

    一.来由 软件开发的过程却又是一个离不开协作.沟通的过程.一个缺乏良好协作,沟通.理解和目标一致的软件团队,是很难高质高效的交付的. 敏捷的众多实践中,有一个为了提升团队协作的经典实践:站立会议 二. ...

  10. python assert断言函数

    python assert断言是声明布尔值必须为真的判定,如果发生异常就说明表达式为假. 可以理解assert断言语句为raise-if-not,用来测试表示式,其返回值为假,就会触发异常. self ...