探索 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 这个配置文件中

<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
         backupStaticAttributes="false"
         bootstrap="bootstrap/autoload.php"
         colors="true"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         processIsolation="false"
         stopOnFailure="false">
    <testsuites>
        <testsuite name="Application Test Suite">
            <directory suffix="Test.php">./tests</directory>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist processUncoveredFilesFromWhitelist="true">
            <directory suffix=".php">./app</directory>
        </whitelist>
    </filter>
    <php>
        <env name="APP_ENV" value="testing"/>
        <env name="CACHE_DRIVER" value="array"/>
        <env name="SESSION_DRIVER" value="array"/>
        <env name="QUEUE_DRIVER" value="sync"/>
    </php>
</phpunit>

从这个配置文件里可以看出,先加载 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 依赖注入思想

image.png

http kernel

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容