laravel深入-IOC容器、一次请求过程、中间件

这篇文章只是我的学习笔记,很多代码都是参考别人的,但参考文章比较多,也就不贴原文链接了。

一、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

猜你喜欢

转载自blog.csdn.net/qq_34881718/article/details/77968943
今日推荐