探索 laravel - 执行流程

为什么 laravel 用起来那么爽

两年前开始使用 laravel ,那个时候还只是吧 laravel 当做另一个 MVC 框架,受 ASP.NET 的影响对齐代码风格和事件模型比较钟爱。但是了解越多越发现 laravel 的优点不止于此。

有时候也会想 laravel 为什么用起来那么爽,用个框架都会有优越感?

上帝说要有光,于是便有了光

平时写代码的时候,有时为了了解某一个函数具体实现会去定位到相关位置去看一眼,但是整体了解整个框架结构的事儿虽然也看过,但大多半途而废。更多的试看看官方文档,然后在自己的应用里拿去用。终于拿出时间来思考这个事情,我觉得这个答案应该就是 “直接去用就好了,不用关心底层实现”。

业务模型与数据库表结构

你需要知道的东西

  1. 你需要使用过 laravel 框架,并对其有个基本的了解

  2. 文档都看过并对该框架的一些基本概念有所了解,比如 IOC

  3. 想更深入的了解框架的实现,以及项目代码的执行过程

入口

laravel 的启动入口一般情况无外乎 web 访问,artisan 命令,phpunit 单元测试。

先看 web 访问,入口在 public/index.php 这个文件,相关代码

require __DIR__.'/../bootstrap/autoload.php';
$app = require_once __DIR__.'/../bootstrap/app.php';

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

$response->send();
$kernel->terminate($request, $response);

artisan 命令入口是 根目录下的 artisan 文件,相关代码

require __DIR__.'/bootstrap/autoload.php';
$app = require_once __DIR__.'/bootstrap/app.php';

$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$status = $kernel->handle(
    $input = new Symfony\Component\Console\Input\ArgvInput,
    new Symfony\Component\Console\Output\ConsoleOutput
);

$kernel->terminate($input, $status);
exit($status);

phpunit 启动入口是 phpunit.xml 这个配置文件中



    
        
            ./tests
        
    
    
        
            ./app
        
    
    
        
        
        
        
    

从这个配置文件里可以看出,先加载 bootstrap,然后去 tests 目录下执行找 Test.php 后缀的文件执行测试,tests 目录下有一个 ExampleTest.php 刚好符合这个规则,ExampleTest,文件里只有一个简单的测试用例,而且又继承自 TestCase 这个类,TestCase 有这样一个方法

public function createApplication()
{
    $app = require __DIR__.'/../bootstrap/app.php';

    $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();

    return $app;
}

纵观来看三个入口都做了类似的行为

  1. require autoload
  2. require bootstrap/app 启动 aplication
  3. make(Kernel) 并 启动起来

但是 web 和 artisan 的入口 make kernel 后有handle 的行为,phpunit 的却没有,web 入口make 的 kernel 是 Illuminate\Contracts\Http\Kernel::class, 其余两个的 kernel 是Illuminate\Contracts\Console\Kernel::class,这又是为什么?,先留个问题在这暂不解决。

启动 application

把上面的问题放下先去 ‘bootstrap/app.php’ 看一眼返回 app这个 application 的时候都干了些什么。

$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
);

前三行把 Illuminate\Foundation\Application 实例化成了一个对象 $app,接下来执行了三次 $app->singleton() 操作。

singleton 方法看起来是生成一个单例对象的意思,但是不是呢?还是先看一眼 Illuminate\Foundation\Application 这个 class 吧。
打开 Illuminate\Foundation\Application这个 class 对应的文件,从上往下拉了一下,1千多行代码,“卧槽,太长不看!”,而且该 class 中也没有 singleton 这个方法的定义,很多时候我都在这个地方半途而废了,不是迷失在各种方法实现的细节中就是陷于各种调用中无法自拔。

现在再次梳理来到这个文件的目的:1. new Application 的时候都干了什么,2 $app上的 singleton方法什么意思。
先看 Application 的构造函数

    public function __construct($basePath = null)
    {
        $this->registerBaseBindings();
        $this->registerBaseServiceProviders();
        $this->registerCoreContainerAliases();
        if ($basePath) {
            $this->setBasePath($basePath);
        }
    }
    // registerBaseBindings 的实现
    protected function registerBaseBindings()
    {
        static::setInstance($this);
        $this->instance('app', $this);
        $this->instance('Illuminate\Container\Container', $this);
    }
    // registerBaseBindings 的实现
    protected function registerBaseServiceProviders()
    {
        $this->register(new EventServiceProvider($this));
        $this->register(new RoutingServiceProvider($this));
    }

    public function register($provider, $options = [], $force = false)
    {
        if (($registered = $this->getProvider($provider)) && ! $force) {
            return $registered;
        }

        // If the given "provider" is a string, we will resolve it, passing in the
        // application instance automatically for the developer. This is simply
        // a more convenient way of specifying your service provider classes.
        if (is_string($provider)) {
            $provider = $this->resolveProviderClass($provider);
        }

        if (method_exists($provider, 'register')) {
            $provider->register();
        }

        // Once we have registered the service we will iterate through the options
        // and set each of them on the application so they will be available on
        // the actual loading of the service objects and for developer usage.
        foreach ($options as $key => $value) {
            $this[$key] = $value;
        }
        $this->markAsRegistered($provider);

        // If the application has already booted, we will call this boot method on
        // the provider class so it has an opportunity to do its boot logic and
        // will be ready for any usage by the developer's application logics.
        if ($this->booted) {
            $this->bootProvider($provider);
        }
        return $provider;
    }

OK,从构造函数里可以看出来一共做了四件事情,

  1. 注册基础绑定
  2. 注册基础服务 provider(events,router)
  3. 注册容器核心别名
  4. 设置项目根目录
    这些基本可以根据方法名猜测到都做了些什么,猜不到的也可以点进去看一下函数的具体实现,比如 register 这个方法如果初读就会有一些疑问:这个方法是干什么的啊,它是怎么实现的啊,这个时候如果大概知道是做什么的就行不要继续深入去了解细节,毕竟我们更关系整个框架是怎么运行的。

到这个地方,我们已经搁置了好几个问题没有解决了,捡几个重要的拾回来

  1. singleton 是干什么的,怎么用?以及上文的 instance,和 self::setInstance,都分别什么意思?
  2. class Application extends Container implements ApplicationContract, HttpKernelInterface, 要去看一下 Container 怎么实现的么?
  3. baseBinding绑的对象都是$this,这应该没什么异议,但BaseProvider为什么是 Event 和 Routing.

这个时候如果上面的问题你都有答案,那么就可以跳出这块继续去看make kernel并 handle 的部分了如果不甚明白,那就去看看文档吧,毕竟文档里说明你是框架开发者希望你正确使用的方式
[1] 核心概念- 服务容器
[2] 核心概念- 服务容器提供者
[3] Laravel深入学习2 - 控制反转容器
[4] Laravel 依赖注入思想

探索 laravel - 执行流程_第1张图片
image.png

http kernel

你可能感兴趣的:(探索 laravel - 执行流程)