说明
Laravel的核心容器,有一部分就是利用反射来实现依赖注入时类的实例化。这是一个简化版示例。
代码
测试用的类
/**
* 坦克标准: 必须能开火
*/
interface Tank
{
public function fire();
}
/**
* 59坦克,使用100毫米炮
*/
class Tank59 implements Tank
{
private $gun = 100;
public function fire()
{
echo $this->gun . PHP_EOL;
}
}
/**
* 59加强版,可以使用100毫米炮或120毫米炮,默认120毫米炮
*/
class Tank59Plus implements Tank
{
private $gun;
public function __construct($gun = 120)
{
if($gun != 100 && $gun != 120) {
throw new Exception('口径不合适');
}
$this->gun = $gun;
}
public function fire()
{
echo $this->gun . PHP_EOL;
}
}
/**
* 59超级版,可以使用任意口径的炮,必须提供一门火炮
*/
class Tank59Super implements Tank
{
private $gun;
public function __construct($gun)
{
$this->gun = $gun;
}
public function fire()
{
echo $this->gun . PHP_EOL;
}
}
/**
* 坦克营,装备59和59加强版,必须提供59和59加强版
*/
class TankArmy implements Tank
{
private $tank59;
private $tank59Plus;
public function __construct(Tank59 $tank59, Tank59Plus $tank59Plus)
{
$this->tank59 = $tank59;
$this->tank59Plus = $tank59Plus;
}
public function fire()
{
echo $this->tank59->fire();
echo $this->tank59Plus->fire();
}
}
容器
/**
* 兵工厂
*/
class Factory
{
/**
* 创建实体对象
*/
public static function build($blueprint)
{
//反射,获取类细节
$reflector = new ReflectionClass($blueprint);
//无法创建的类,比如接口,抽象类
if(!$reflector->isInstantiable()) {
throw new Exception('提供的不是坦克图纸,不能制造');
}
//获取类的构造方法
$constructor = $reflector->getConstructor();
if (is_null($constructor)) {
//没有构造方法,直接new
return new $blueprint;
}
$parameters = [];
//获取构造方法依赖的参数
$dependencies = $constructor->getParameters();
foreach ($dependencies as $dependency) {
if(is_null($dependency->getClass())) {
//依赖参数不是对象
//如果构造方法参数有默认值,获取默认值
if($dependency->isDefaultValueAvailable()) {
$parameters[] = $dependency->getDefaultValue();
}
}
else {
//依赖参数是对象,递归创建
$parameters[] = self::build($dependency->getClass()->name);
}
}
if(!$parameters) {
//有构造方法且构造方法有参数,却没有可以提供的实参,无法创建
throw new Exception('坦克缺少必要的零件,不能制造');
}
else {
//使用反射提供的方法创建对象,传入参数
return $reflector->newInstanceArgs($parameters);
}
}
}
测试
Tank
$tank = Factory::build(Tank::class);
echo $tank->fire();
//输出
//PHP Fatal error: Uncaught Exception: 提供的不是坦克图纸,不能制造
Tank59
$tank = Factory::build(Tank59::class);
echo $tank->fire();
//输出
//100
Tank59Plus
$tank = Factory::build(Tank59Plus::class);
echo $tank->fire();
//输出
//120
Tank59Super
$tank = Factory::build(Tank59Super::class);
echo $tank->fire();
//输出
//Fatal error: Uncaught Exception: 坦克缺少必要的零件,不能制造
TankArmy
$tank = Factory::build(TankArmy::class);
echo $tank->fire();
//输出
//100
//120