作者:南丞 时间:2017-11-10 04:03
依赖注入的实质就是把一个类不可更换的部分和可更换的部分分离开来 通过注入的方式使用 从而达到解耦的目的
1.简单的依赖注入实现:
<?php
/**
* 一个数据库连接类
*/
class Mysql{
private $host;
private $port;
private $username;
private $password;
private $db_name;
public function __construct()
{
$this->host = '127.0.0.1';
$this->port = '3306';
$this->username = 'root';
$this->password = 'root';
$this->db_name = 'laravel5';
}
public function connect() {
return mysqli_connect($this->host,$this->username ,$this->password,$this->db_name,$this->port);
}
}
/**
* 使用
*/
$db = new Mysql();
$con = $db->connect();
//上面是我们的一个简单的数据库操作类 显然,数据库的配置是可以更换的部分 因此我们需要把它拎出来。
// 依赖注入的写法
/**
* 配置类
* Class MysqlConfiguration
*/
class MysqlConfiguration
{
private $host;
private $port;
private $username;
private $password;
private $db_name;
public function __construct( $host, $port, $username, $password, $db_name)
{
$this->host = $host;
$this->port = $port;
$this->username = $username;
$this->password = $password;
$this->db_name = $db_name;
}
public function getHost()
{
return $this->host;
}
public function getPort()
{
return $this->port;
}
public function getUsername()
{
return $this->username;
}
public function getPassword()
{
return $this->password;
}
public function getDbName()
{
return $this->db_name;
}
}
/**
* 数据库操作类
*/
class Mysql1
{
private $configuration;
public function __construct($config)
{
$this->configuration = $config;
}
public function connect(){
return mysqli_connect($this->configuration->getHost(),$this->configuration->getUsername() ,$this->configuration->getPassword,$this->configuration->getDbName(),$this->configuration->getPort());
}
}
/**
* 使用的时候
*/
$config = new MysqlConfiguration('127.0.0.1','root','','my_db',22);
$db = new Mysql1($config);
$con = $db->connect();
//$config是注入Mysql的,这就是所谓的依赖注入。
2.上面是两个类的注入,在来看看三个类的注入:
<?php
class Bim{
public function doSomething() {
echo __METHOD__,'|';
}
}
class Bar{
public function doSomething(){
$bim = new Bim();
$bim->doSomething();
echo __METHOD__,'|';
}
}
class Foo{
public function doSomething(){
$bar = new Bar();
$bar->doSomething();
echo __METHOD__;
}
}
/**
* $foo = new Foo();
* $foo->doSomething();
*/
//Bim::doSomething|Bar::doSomething|Foo::doSomething
/**
* 使用依赖注入的思路是应用程序用到Foo类,
* Foo类需要Bar类,
* Bar类需要Bim类,
* 那么先创建Bim类,
* 再创建Bar类并把Bim注入,
* 再创建Foo类,并把Bar类注入,
* 再调用Foo方法,Foo调用Bar方法,
* 接着做些其它工作。
*/
class Bim
{
public function doSomething()
{
echo __METHOD__, '|';
}
}
class Bar
{
private $bim;
public function __construct(Bim $bim)
{
$this->bim = $bim;
}
public function doSomething()
{
$this->bim->doSomething();
echo __METHOD__, '|';
}
}
class Foo
{
private $bar;
public function __construct(Bar $bar)
{
$this->bar = $bar;
}
public function doSomething()
{
$this->bar->doSomething();
echo __METHOD__;
}
}
$foo = new Foo(new Bar(new Bim()));
$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething
/**
* 这就是控制反转模式。
* 依赖关系的控制反转到调用链的起点。
* 这样你可以完全控制依赖关系,通过调整不同的注入对象,来控制程序的行为
*
*/
3,上面的三个类互相依赖 使用依赖注入容器后的思路是应用程序需要到Foo类,就从容器内取得Foo类,容器创建Bim类,再创建Bar类并把Bim注入,再创建Foo类,并把Bar注入,应用程序调用Foo方法,Foo调用Bar方法,接着做些其它工作.这样就可以控制依赖关系,通过调整不同的注入对象,来控制程序的行为。例如Foo类用到了memcache,可以在不修改Foo类代码的情况下,改用redis。 总之容器负责实例化,注入依赖,处理依赖关系等工作。代码实现如下
class Container
{
private $s = array();
function __set($k, $c)
{
$this->s[$k] = $c;
}
function __get($k)
{
return $this->s[$k]($this);
}
}
//使用魔术方法,在给不可访问属性赋值时,__set() 会被调用。读取不可访问属性的值时,__get() 会被调用。
//调用
$c = new Container();
$c->bim = function () {
return new Bim();
};
$c->bar = function ($c) {
return new Bar($c->bim);
};
$c->foo = function ($c) {
return new Foo($c->bar);
};
// 从容器中取得Foo
$foo = $c->foo;
$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething
4, 使用静态化的容器代码来实现容器:
/**
* 静态的注入容器
* Class Ioc
*/
class Ioc{
protected static $registry = [];
public static function bind($name, $resolver){
static::$registry[$name] = $resolver;
}
public static function make($name){
if(isset(self::$registry[$name])) {
$resolver = self::$registry[$name];
return $resolver();
}
throw new Exception('容器中没哟这个类');
}
}
/**
* 使用容器
*/
IoC::bind('bim', function () {
return new Bim();
});
IoC::bind('bar', function () {
return new Bar(IoC::make('bim'));
});
IoC::bind('foo', function () {
return new Foo(IoC::make('bar'));
});
// 从容器中取得Foo
$foo = IoC::make('foo');
$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething
5, 从上面代码中我们会发现 我们所涉及到的类 直接实例化 没有 构造方法也没有参数 所以得写一个解析 如下代码:
<?php
/**
* 依赖注入的高级操作 自动绑定
*/
/**
* 如下的三个基础类互相依赖
*/
class Bim
{
public function doSomething()
{
echo __METHOD__, '|';
}
}
class Bar
{
private $bim;
public function __construct(Bim $bim)
{
$this->bim = $bim;
}
public function doSomething()
{
$this->bim->doSomething();
echo __METHOD__, '|';
}
}
class Foo
{
private $bar;
public function __construct(Bar $bar)
{
$this->bar = $bar;
}
public function doSomething()
{
$this->bar->doSomething();
echo __METHOD__;
}
}
/**
* 做一个依赖容器
*/
class Container{
private $s = [];
public function __set($k, $c)
{
$this->s[$k]=$c;
}
public function __get($k)
{
// return $this->s[$k]($this); 直接实例化这个类
return $this->build($this->s[$k]);
}
/**
* 自动绑定(Autowiring)自动解析(Automatic Resolution)
*/
public function build($className) {
// 如果是匿名函数(Anonymous functions),也叫闭包函数(closures)
if ($className instanceof Closure) {
// 执行闭包函数,并将结果
return $className($this);
}
//得到一个反射类
$reflector = new ReflectionClass($className);
/** 获取类的构造函数 */
$constructor = $reflector->getConstructor();
// 若无构造函数,直接实例化并返回
if (is_null($constructor)) {
return new $className;
}
// 取构造函数参数,通过 ReflectionParameter 数组返回参数列表
$parameters = $constructor->getParameters();
// 递归解析构造函数的参数
$dependencies = $this->getDependencies($parameters);
// 创建一个类的新实例,给出的参数将传递到类的构造函数。
return $reflector->newInstanceArgs($dependencies);
}
/**
* 解析带参数的类
* @param $parameters
* @return array
*/
public function getDependencies($parameters)
{
$dependencies = [];
/** 循环所有的参数 查看类型 */
foreach ($parameters as $parameter) {
$dependency = $parameter->getClass();
if (is_null($dependency)) {
// 是变量,有默认值则设置默认值
$dependencies[] = $this->resolveNonClass($parameter);
} else {
// 是一个类,递归解析
$dependencies[] = $this->build($dependency->name);
}
}
return $dependencies;
}
/**
* 给参数设置默认值
* @param $parameter
* @return mixed
* @throws Exception
*/
public function resolveNonClass($parameter)
{
// 有默认值则返回默认值
if ($parameter->isDefaultValueAvailable()) {
return $parameter->getDefaultValue();
}
throw new Exception('没有默认值');
}
}
/**
* 使用一哈哈
*/
$c = new Container();
$c->bar = 'Bar';
$c->foo = function ($c) {
return new Foo($c->bar);
};
// 从容器中取得Foo
$foo = $c->foo;
$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething
// ----
$di = new Container();
$di->foo = 'Foo';
/** @var Foo $foo */
$foo = $di->foo;
var_dump($foo);
注意: 反射的一些常识:
new ReflectionClass(类名) //对某个类做反射
ReflectionClass::__construct — 初始化 ReflectionClass 类
ReflectionClass::export — 导出一个类
ReflectionClass::getConstant — 获取定义过的一个常量
ReflectionClass::getConstants — 获取一组常量
ReflectionClass::getConstructor — 获取类的构造函数
ReflectionClass::getDefaultProperties — 获取默认属性
ReflectionClass::getDocComment — 获取文档注释
ReflectionClass::getEndLine — 获取最后一行的行数
ReflectionClass::getExtension — 根据已定义的类获取所在扩展的 ReflectionExtension 对象
ReflectionClass::getExtensionName — 获取定义的类所在的扩展的名称
ReflectionClass::getFileName — 获取定义类的文件名
ReflectionClass::getInterfaceNames — 获取接口(interface)名称
ReflectionClass::getInterfaces — 获取接口
ReflectionClass::getMethod — 获取一个类方法的 ReflectionMethod。
ReflectionClass::getMethods — 获取方法的数组
ReflectionClass::getModifiers — 获取类的修饰符
ReflectionClass::getName — 获取类名
ReflectionClass::getNamespaceName — 获取命名空间的名称
ReflectionClass::getParentClass — 获取父类
ReflectionClass::getProperties — 获取一组属性
ReflectionClass::getProperty — 获取类的一个属性的 ReflectionProperty
ReflectionClass::getShortName — 获取短名
ReflectionClass::getStartLine — 获取起始行号
ReflectionClass::getStaticProperties — 获取静态(static)属性
ReflectionClass::getStaticPropertyValue — 获取静态(static)属性的值
ReflectionClass::getTraitAliases — 返回 trait 别名的一个数组
ReflectionClass::getTraitNames — 返回这个类所使用 traits 的名称的数组
ReflectionClass::getTraits — 返回这个类所使用的 traits 数组
ReflectionClass::hasConstant — 检查常量是否已经定义
ReflectionClass::hasMethod — 检查方法是否已定义
ReflectionClass::hasProperty — 检查属性是否已定义
ReflectionClass::implementsInterface — 接口的实现
ReflectionClass::inNamespace — 检查是否位于命名空间中
ReflectionClass::isAbstract — 检查类是否是抽象类(abstract)
ReflectionClass::isAnonymous — 检查类是否是匿名类
ReflectionClass::isCloneable — 返回了一个类是否可复制
ReflectionClass::isFinal — 检查类是否声明为 final
ReflectionClass::isInstance — 检查类的实例
ReflectionClass::isInstantiable — 检查类是否可实例化
ReflectionClass::isInterface — 检查类是否是一个接口(interface)
ReflectionClass::isInternal — 检查类是否由扩展或核心在内部定义
ReflectionClass::isIterateable — 检查是否可迭代(iterateable)
ReflectionClass::isSubclassOf — 检查是否为一个子类
ReflectionClass::isTrait — 返回了是否为一个 trait
ReflectionClass::isUserDefined — 检查是否由用户定义的
ReflectionClass::newInstance — 从指定的参数创建一个新的类实例
ReflectionClass::newInstanceArgs — 从给出的参数创建一个新的类实例。
ReflectionClass::newInstanceWithoutConstructor — 创建一个新的类实例而不调用它的构造函数
ReflectionClass::setStaticPropertyValue — 设置静态属性的值
ReflectionClass::__toString — 返回 ReflectionClass 对象字符串的表示形式。