根目录下文件
Controller.php
<?php
//本页面 是服务容器的控制反转,将接口下的类,控制器的类反转到容器中控制
//根目录use调用,第一个一定是 \文件夹名\..\php的文件名
use \view\iVehicle;
use \view\Car;
use \view\Bus;
require __DIR__ . '/Autoload.php';
//容器类
class Container
{
// 闭包数组
private static $binds = [];
// 实例数组
private static $instance = [];
// 将闭包或实例绑定到对应的容器中
public function bind($abstract,$concrete)
{
// 将类实例化的闭包绑定到闭包数组中
// 按照约定,闭包是用来创建对象的
// 重要编码约定: 它的构造方法第一个参数必须是 "容器对象", 便于统一管理 "对象依赖关系"
// 如果第二个参数是闭包类型, 说明该类实例并未创建,应该将实例化过程/闭包绑定到容器中
if($concrete instanceof Closure){
static::$binds[$abstract] = $concrete;
}else{
// 否则,表示该类已被实例化,直接将 "类实例" 绑定到实例数组中即可
static::$instance[$abstract] = $concrete;
}
}
// 从容器中取出指定实例, $parameters是实例的依赖对象(数组形式提供)
public function make($abstract,$parameters = [])
{
// 如果容器中已有指定类实例,则直接返回
if(isset(static::$instance[$abstract])){
return static::$instance[$abstract];
}else{
// 如果没有, 则执行闭包容器中的闭包方法,完成实例化工作,并返回该实例
// 因为所有依赖都使用容器统一管理,所以需要将容器对象注入到所有依赖对象的参数列表中
// 默认容器必须是依赖对象参数列表中的第一个参数,使用array_unshift()就可以
// 注意, $this 是第一个参数, 即当前容器对象,这里就是本服务容器Container
array_unshift($parameters, $this);
// 执行闭包, 如果闭包生成的对象依赖于其它对象,可通过$parameters设置
return call_user_func_array(static::$binds[$abstract],$parameters);
}
}
}
//控制器
class Controller
{
// 交通工具
private static $Traffic;
// 构造方法:接口 接入,第二个$traff就是接口下的对象类:Car,Bus 是数组,通过外部服务容器传参
public function __construct(iVehicle $traff)
{
static::$Traffic = $traff;
}
public function travelMode()
{
return static::$Traffic->drive();
}
}
//容器对象
$container = new Container();
//将汽车类绑定到闭包数组中
//将$container做为闭包参数, 主要用来管理依赖对象的,本例中除调用类外,其它类并未使用到依赖对象
$container->bind('car', function(Container $container){return new Car();});
$container->bind('bus', function(Container $container){return new Bus();});
// 同时将调用类:控制器类也绑定到容器中
// Controller构造方法需要一个实现了iVehicle接口的对象,而该对象需要使用容器中的make()方法获取
// 根据make($container, $parameters)方法签名, 该闭包签名的第一个参数是容器对象,第二个参数是依赖的对象
// 注意, 为了简化, 约定将依赖对象,通过构造方式注入到当前对象中
$container->bind('controller', function(Container $container, $traffi){
return new Controller($container->make($traffi));
});
//客户端
//从容器中取出已实例化的容器类对象 (此时依赖对象已经通过构造方法注入到了控制器类中了)
$controller = $container->make('controller',['car']);
echo $controller->travelMode();//汽车
$controller = $container->make('controller',['car']);
echo $controller->travelMode();//公交车
//如果只想从容器中取出接口下的某一个对象类
$car = $container->make('car');
echo $car->drive();
// 为了便于后面调用, 我们将这四个对象打包到一个数组中管理
$vehicles = compact('car', 'train', 'plane', 'ship');
// 如果已经获取到了依赖对象, 那么就不必使用make()来调用Travel4对象了,直接实例化它就行
foreach ($vehicles as $vehicle) {
$travel = new Controller($vehicle);
echo $travel->travelMode(), '<br>';
}
// 到现在为止, 我们已经实现了依赖注入也控制反转
// 基本流程是:
// 1. 依赖注入: 如果一对象需要依赖另一个对象才能完成某项操作,则必须通过方法参数的形式将依赖对象注入当前到对象中
// 2. 控制反转: 依赖对象的实例化与该对象自身的依赖关系处理, 由一个叫服务容器的类统一处理,实现了对象控制权的转移
Autoload.php
<?php
spl_autoload_register(function (string $class) {
$file = str_replace('\\', '/', $class) . '.php';
if (!is_file($file)) {
throw new \Exception("file don't exists");
}
require $file;
});
根 \view文件夹
iVehicle.php 接口文件
<?php
namespace view;
interface iVehicle
{
public function drive();
}
Car.php
<?php
namespace view;
class Car implements iVehicle
{
public function drive()
{
return '小汽车';
}
}
Bus.php
<?php
namespace view;
class Bus implements iVehicle
{
public function drive()
{
return '公交车';
}
}