說明
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