Laravel源码-入口初始化

laravel 入口文件/laravel/public/index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

//项目开始运行时间
define('LARAVEL_START', microtime(true));
//加载项目依赖
require __DIR__.'/../vendor/autoload.php';
//创建Laravel应用实例(服务容器),项目初始化包括:注册项目基础服务,注册服务提供别名,注册路径等一系列注册工作
$app = require_once __DIR__.'/../bootstrap/app.php';
//接受请求并响应
//通过APP容器实例化Kernel类 注册中间件组合中间件别名
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
//处理请求
$response = $kernel->handle(
//通过Symfony Request类 创建请求实例
$request = Illuminate\Http\Request::capture()
);
//发送响应由 Illuminate\Http\Response 父类 Symfony\Component\HttpFoundation\Response 中的 send() 方法完成
$response->send();
//发送响应之后,如果有终止中间件的话,则执行终止中间件 Illuminate/Foundation/Http/Kernel.php的terminate方法
$kernel->terminate($request, $response);

Composer 自动加载

composer将通过命名空间/文件目录的形式存储到/laravel/vendor/composer/autoload_classmap.php中,自动加载时通过命名空间寻找类文件,并实现加载

创建app实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php

//创建应用实例 注册服务,路径,别名到APP容器中
$app = new Illuminate\Foundation\Application(
realpath(__DIR__.'/../')
);


//绑定kernel到容器中 web请求 加载中间件,通过bootstrappers数组完成环境监测,配置加载,异常处理,Facades注册,服务提供者注册,启动服务
$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
);

//返回APP容器
return $app;

创建应用实例

/laravel/vendor/laravel/framework/src/Illuminate/Foundation/Application.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public function __construct($basePath = null)
{
//绑定路径,将路径添加到容器的instances属性数组中
if ($basePath) {
//定义app目录路径,根目录,语言路径,配置路径,public路径,storage路径,databases路径,resource路径,bootstrap路径,写入到属性instance中
$this->setBasePath($basePath);
}

//将application本身以及父类Container写入到instance中,并分别起了2个名字:app,Illuminate\Container\Container
//同时将Illuminate\Foundation\PackageManifest添加到容器instances属性数组,并且
$this->registerBaseBindings();

//注册EventService,LogService,RouterService服务
$this->registerBaseServiceProviders();

//注册核心类的别名,以key/value形式存储记录
$this->registerCoreContainerAliases();
}

总体来说app初始化的工作为: 1、设置路径 2、绑定了app对象和packages包的实例 3、注册了基本服务提供者 4、增加了核心类的别名

绑定核心接口到容器

主要的动作为绑定一些重要的接口到容器,以便之后可以直接使用.

此过程中加载中间件,通过bootstrappers数组完成环境监测,配置加载,异常处理,Facades注册,服务提供者注册,启动服务

singleton方法 将传入的类名路径转换为一个启动服务的闭包,并保存在容器的bindings属性中

通过APP容器实例化Kernel类

  • 通过make方法解析kernel类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     public function make($abstract, array $parameters = [])
    {
    //获取之前创建app实例中的别名,这个时候还没有别名,直接返回类路径
    $abstract = $this->getAlias($abstract);

    //不是延迟加载的服务
    if (isset($this->deferredServices[$abstract]) && ! isset($this->instances[$abstract])) {
    $this->loadDeferredProvider($abstract);
    }
    //通过父类make方法解析kernel类
    return parent::make($abstract, $parameters);
    }
  • 父类make方法解析

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    public function make($abstract, array $parameters = [])
    {
    return $this->resolve($abstract, $parameters);
    }

    protected function resolve($abstract, $parameters = [])
    {
    $abstract = $this->getAlias($abstract);

    //检测上下文绑定,这个属于契约接口的动态调用
    $needsContextualBuild = ! empty($parameters) || ! is_null(
    $this->getContextualConcrete($abstract)
    );

    // If an instance of the type is currently being managed as a singleton we'll
    // just return an existing instance instead of instantiating new instances
    // so the developer can keep using the same objects instance every time.
    //instances数组中有该类,并且不需要构建上下文的话,便直接返回该类实例
    if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
    return $this->instances[$abstract];
    }

    //将实例化类的参数存入数组
    $this->with[] = $parameters;

    //获取该类闭包,若无则还是返回类名字符串
    $concrete = $this->getConcrete($abstract);

    // We're ready to instantiate an instance of the concrete type registered for
    // the binding. This will instantiate the types, as well as resolve any of
    // its "nested" dependencies recursively until all have gotten resolved.
    //如果当前解析的类没有上下文绑定,并且是一个闭包则直接进行构建,否则递归make方法获得该契约绑定的类
    if ($this->isBuildable($concrete, $abstract)) {
    $object = $this->build($concrete);
    } else {
    $object = $this->make($concrete);
    }

    // If we defined any extenders for this type, we'll need to spin through them
    // and apply them to the object being built. This allows for the extension
    // of services, such as changing configuration or decorating the object.
    foreach ($this->getExtenders($abstract) as $extender) {
    $object = $extender($object, $this);
    }

    // If the requested type is registered as a singleton we'll want to cache off
    // the instances in "memory" so we can return it later without creating an
    // entirely new instance of an object on each subsequent request for it.
    //若该类绑定时设置为共享,则缓存至instances单例数组
    if ($this->isShared($abstract) && ! $needsContextualBuild) {
    $this->instances[$abstract] = $object;
    }

    $this->fireResolvingCallbacks($abstract, $object);

    // Before returning, we will also set the resolved flag to "true" and pop off
    // the parameter overrides for this build. After those two things are done
    // we will be ready to return back the fully constructed class instance.
    $this->resolved[$abstract] = true;

    array_pop($this->with);

    return $object;
    }
    • 加载中间件

      解析kernel类时,最终会构建在实例化app时绑定的kernel实现(App\Http\kernel),通过构造方法加载中间,中间件组,以及路由中间件.

处理请求

解析kernel后,在index.php文件中利用kernel的handle方法,传入了一个request对象,来处理这次的网页url请求。此处是通过使用管道模式进行路由分发,定位到控制器,被response类包装成响应对象返回至index.php,通过send方法发送至浏览器.