这篇文章只是我的学习笔记,很多代码都是参考别人的,但参考文章比较多,也就不贴原文链接了。
一、IOC容器
先介绍下IOC容器的简单用法,代码如下
//支付类接口
interface Pay
{
public function pay();
}
//支付宝支付
class Alipay implements Pay
{
public function pay()
{
echo 'pay bill by alipay';
}
}
//付款
class PayBill
{
private $payMethod;
public function __construct(Pay $payMethod)
{
$this->payMethod = $payMethod;
}
public function payMyBill()
{
$this->payMethod->pay();
}
}
不使用IOC容器,要调用payMyBill(),需要
$pay = new PayBill(new Alipay);
$pay->payMyBill();
这样一来,payBill对AlliPay的依赖要手动注入,如果有了IOC容器,代码变成
$app = new Container();
$app->bind("Pay", "Alipay");
$paybill = $app->make('PayBill');
$paybill->payMyBill();
也就是事先将Alipay绑定好,等到要用的时候才make出来PayBill类,Alipay这个依赖会自动注入了。
Container这个类的实现,可以参考《laravel框架关键技术解析》中给出的代码:
class Container
{
//用于装提供实例的回调函数,真正的容器还会装实例等其他内容
//从而实现单例等高级功能
protected $bindings = [];
//绑定接口和生成相应实例的回调函数
public function bind($abstract, $concrete = null, $shared = false)
{
//如果提供的参数不是回调函数,则产生默认的回调函数
if (!$concrete instanceof Closure) {
$concrete = $this->getClosure($abstract, $concrete);
}
//shared变量的意义在这里没有体现
$this->bindings[$abstract] = compact('concrete', 'shared');
}
//默认生成实例的回调函数
protected function getClosure($abstract, $concrete)
{
return function ($c) use ($abstract, $concrete) {
//这个闭包方法在下面的build方法中$concrete($this)中才真正调用
$method = ($abstract == $concrete) ? 'build' : 'make';
return $c->$method($concrete);
};
}
public function make($abstract)
{
//PayBill
$concrete = $this->getConcrete($abstract);
//isBuildable函数判断$abstract是否是$concrete的别名
if ($this->isBuildable($concrete, $abstract)) {
$object = $this->build($concrete);
} else {
$object = $this->make($concrete);
}
return $object;
}
protected function isBuildable($concrete, $abstract)
{
return $concrete === $abstract || $concrete instanceof Closure;
}
//获取绑定的回调函数
protected function getConcrete($abstract)
{
if (!isset($this->bindings[$abstract])) {
return $abstract;
}
return $this->bindings[$abstract]['concrete'];
}
//实例化对象
public function build($concrete)
{
//如果是闭包,说明已找到依赖的关系
if ($concrete instanceof Closure) {
$data = $concrete($this);
return $data;
}
$reflector = new ReflectionClass($concrete);
if (!$reflector->isInstantiable()) {
echo $message = "Target [$concrete] is not instantiable";
}
$constructor = $reflector->getConstructor();
if (is_null($constructor)) {
return new $concrete;
}
//name => 'payMethod'
$dependencies = $constructor->getParameters();
$instances = $this->getDependencies($dependencies);
$result = $reflector->newInstanceArgs($instances);
return $result;
}
//解决通过反射机制实例化对象时的依赖
protected function getDependencies($parameters)
{
$dependencies = [];
foreach ($parameters as $parameter) {
$dependency = $parameter->getClass();
if (is_null($dependency)) {
$dependencies[] = NULL;
} else {
$dependencies[] = $this->resolveClass($parameter);
}
}
return (array)$dependencies;
}
protected function resolveClass(ReflectionParameter $parameter)
{
//Pay
return $this->make($parameter->getClass()->name);
}
}
上面的Container类比较简单,laravel中的Container类\(vendor\laravel\framework\src\Illuminate\Container\Container.php),添加了alias,defer,share等属性。
laravel中bind和singleton都用于绑定类,但后者在一次请求中,一次绑定可以多次使用。
二、一次请求过程
index.php中
//1.引入自动加载
require __DIR__.'/../bootstrap/autoload.php';
//2.实例化IOC容器中绑定的内容
$app = require_once __DIR__.'/../bootstrap/app.php';
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
//3.处理请求
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
$response->send();
//4.请求结束,回调请求结束后执行的中间件代码
$kernel->terminate($request, $response);
分析一下上面的index.php代码
第一步 引入的autoload.php中
require __DIR__.'/../vendor/autoload.php';
也就是使用composer提供的自动加载
第二步 引入的app.php中
//Application继承了Container类,生成了一个IOC容器
$app = new Illuminate\Foundation\Application(
realpath(__DIR__.'/../')
);
//绑定
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
第四步 回调请求涉及到中间件,应用到设计模式中的装饰模式
三、中间件
中间件原理如下。
首先看个例子
<?php
interface Middleware
{
public static function handle(Closure $next);
}
class VerifyCsrfToken implements Middleware
{
public static function handle(Closure $next)
{
echo "验证csrf-token.......";
$next();
}
}
class ShareErrorsFromSession implements Middleware
{
public static function handle(Closure $next)
{
echo "如果session中有errors变量,共享他....";
$next();
}
}
class StartSession implements Middleware
{
public static function handle(Closure $next)
{
echo "开启session,获取数据....";
$next();
echo "保存数据,关闭session..";
}
}
class AddQueuedCookiesToResponse implements Middleware
{
public static function handle(Closure $next)
{
$next();
echo "添加下一次请求需要的cookie...";
}
}
class EncryptCookies implements Middleware
{
public static function handle(Closure $next)
{
echo "对输入请求的cookie进行解密...";
$next();
echo "对输出响应的cookie进行加密...";
}
}
class CheckForMaintenanceMode implements Middleware
{
public static function handle(Closure $next)
{
echo "确定当前程序是否处于维护状态...";
$next();
}
}
function getSlice()
{
return function ($stack, $pipe) {
return function () use ($stack, $pipe) {
return $pipe::handle($stack);
};
};
}
function then()
{
$pipes= [
"CheckForMaintenanceMode",
"EncryptCookies",
"AddQueuedCookiesToResponse",
"StartSession",
"ShareErrorsFromSession",
"VerifyCsrfToken"
];
$firstSlice = function () {
echo "请求向路由器传递,返回响应....";
};
$pipes = array_reverse($pipes);
call_user_func(
//这里如果写成'getSlice',需要将上面的getSlice()函数更改为跟pipeline_simplify.php中的那种套路
array_reduce($pipes, getSlice(), $firstSlice)
);
}
then();
输出结果为:
确定当前程序是否处于维护状态...对输入请求的cookie进行解密...开启session,获取数据....如果session中有errors变量,共享他....验证csrf-token.......请求向路由器传递,返回响应....保存数据,关闭session..添加下一次请求需要的cookie...对输出响应的cookie进行加密...
生成结果的流程图:
扫描二维码关注公众号,回复:
2763014 查看本文章
laravel的中间件也是这个原理,但laravel将上面例子中$next()函数后面的代码,放到terminate()方法了。
写个例子
//route.php
Route::get('/', function () {
return "hello <br/>";
})->middleware('test_mid');
//Test中间件
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class Test
{
public function handle($request, Closure $next, $guard = null)
{
echo "this is middleware named test <br/>";
return $next($request);
}
public function terminate($request, $response)
{
echo "this is terminate middleware named test <br/>";
}
}
//kernel.php中引入
protected $routeMiddleware = [
'test_mid' => \App\Http\Middleware\Test::class,
];
浏览器访问该路由,输出:
this is middleware named test
hello
this is terminate middleware named test