laravel的http请求入口是public目录下的index.php。
首先从这个文件进行分析,代码流程走完以后,会出一个逻辑流程图:
<?php
//定义LARAVEL_START常量
define('LARAVEL_START', microtime(true));
//使用composer的自动加载机制(后续分析自动加载是如何实现的)
require __DIR__.'/../vendor/autoload.php';
//创建laravel应用
$app = require_once __DIR__.'/../bootstrap/app.php';
//获取Http内核
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
//处理请求
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
//将请求结果返回给客户端
$response->send();
//调用http内核的terminate方法,销毁资源
$kernel->terminate($request, $response);
进入当前目录下的bootstrap/app.php文件中,代码段如下:
<?php
//实例化Application,以base_path作为参入传递给构造函数
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
//绑定三种接口的实现 ,在http服务中,只会用到第一种接口的实现
//第二种和第三种是命令行模式和调试模式的接口实现。
//为什么要绑定,因为laravel使用的应用容器模式实现类的依赖注入,为了降低代码的耦合,
//依赖类常常使用接口类或者抽象类作为参数,通过绑定接口类(抽象类)和具体接口的实现,当从容器中
//获取该接口的实现时,通过类型提示即可获取该接口的具体实现,降低了代码的耦合。
$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
);
return $app;
找到 Illuminate\Foundation\Application,该类继承自Container(laravel最重要的容器类)
也就是说,一个laravel应用就是一个laravel容器
其构造函数代码如下:
public function __construct($basePath = null)
{
//设置应用基础目录
if ($basePath) {
$this->setBasePath($basePath);
}
//绑定基础类
$this->registerBaseBindings();
//注册基础服务提供者。
$this->registerBaseServiceProviders();
//设置框架核心类的短名。
$this->registerCoreContainerAliases();
}
public function setBasePath($basePath)
{
$this->basePath = rtrim($basePath, '\/');
//绑定路径到容器中。
$this->bindPathsInContainer();
return $this;
}
protected function bindPathsInContainer()
{
//$this->path(),$this->basePath()等都是返回框架设定的目录,以全路径形式返回。
$this->instance('path', $this->path());
$this->instance('path.base', $this->basePath());
$this->instance('path.lang', $this->langPath());
$this->instance('path.config', $this->configPath());
$this->instance('path.public', $this->publicPath());
$this->instance('path.storage', $this->storagePath());
$this->instance('path.database', $this->databasePath());
$this->instance('path.resources', $this->resourcePath());
$this->instance('path.bootstrap', $this->bootstrapPath());
}
当前实例的instance方法,代码段如下
public function instance($abstract, $instance)
{
/*removeAbstractAlias 方法
if (! isset($this->aliases[$searched])) {
return;
}
foreach ($this->abstractAliases as $abstract => $aliases) {
foreach ($aliases as $index => $alias) {
if ($alias == $searched) {
unset($this->abstractAliases[$abstract][$index]);
}
}
}
*/
//removeAbstractAlias从方法名命名来解释:移除抽象别名
//上面的实现为:删除当前容器的abstractAliases数组中保存的所有以$abstract为
//键名的所有成员,保证注册的唯一性。
$this->removeAbstractAlias($abstract);
/*bound 方法
return isset($this->bindings[$abstract]) ||
isset($this->instances[$abstract]) ||
$this->isAlias($abstract);
*/
//在bindings/instances/aliases中,如果有一个存在,则该返回值为真。
$isBound = $this->bound($abstract);
//删除aliases中保存的当前abstract对应的值。
unset($this->aliases[$abstract]);
//保存进instances数组,(instances:实例,实体)。
$this->instances[$abstract] = $instance;
if ($isBound) {
/*rebound
$instance = $this->make($abstract);
foreach ($this->getReboundCallbacks($abstract) as $callback) {
call_user_func($callback, $this, $instance);
}
*/
//若$isBound为真,也就是曾经向容器中保存过该$abstract
//则需要重新make(实际调用的是resolve方法)一个实体出来,
//然后调用rebound注册的回调函数
//重新触发或者通知相关依赖类
$this->rebound($abstract);
}
return $instance;
}
resolve方法代码段:
protected function resolve($abstract, $parameters = [], $raiseEvents = true)
{
//getAlias方法是一个递归调用的方法,用于获取已经保存在
//aliases数组中该别名的最终对应abstract。
$abstract = $this->getAlias($abstract);
//从上下文关系中可以推断出,$needsContextualBuild的值为空
$needsContextualBuild = (! empty($parameters) || ! is_null(
$this->getContextualConcrete($abstract)
));
//如果在单例管理数组中查找到该abstract的对应绑定,且不需要重新构建上下文,则
//直接返回该instance(实例)
if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
return $this->instances[$abstract];
}
//当前迭代层参数入栈。
$this->with[] = $parameters;
//获取该abstract的具体实现
//从bindings/contextual中获取该abstract的具体实现,如果未获取到,则返回其自身
$concrete = $this->getConcrete($abstract);
//判断该concrete 是否可以build,
//$concrete与$abstract相等,$concrete 是一个闭包(匿名函数),二者满足其一即可。
if ($this->isBuildable($concrete, $abstract)) {
$object = $this->build($concrete);
} else {
//否则调用make(递归调用resolve),非闭包,且是一个接口的实现。
$object = $this->make($concrete);
}
//对已经生成的object调用extend中已经注册的函数,扩展作用于该object之上
//可以改变其object的属性
foreach ($this->getExtenders($abstract) as $extender) {
$object = $extender($object, $this);
}
//该abstract的shared属性是否为真,且不需要重新构建上下文,如果都为真,
//则将该object保存进
//容器管理的单例集中去。
if ($this->isShared($abstract) && ! $needsContextualBuild) {
$this->instances[$abstract] = $object;
}
//该参数默认为true,调用resolve所注册的callback
if ($raiseEvents) {
$this->fireResolvingCallbacks($abstract, $object);
}
//将当前abstract的resolved属性设置为true
$this->resolved[$abstract] = true;
//当前迭代层的参数出栈
array_pop($this->with);
//返回该object
return $object;
}
getContextualConcrete方法代码段:
protected function getContextualConcrete($abstract)
{
//若有直接找到该abstract的binding,则直接诶返回
if (! is_null($binding = $this->findInContextualBindings($abstract))) {
return $binding;
}
//检查在abstractAliases中是否有该abstract的相关绑定,若无,直接返回
if (empty($this->abstractAliases[$abstract])) {
return;
}
//遍历abstractAliases中该abstract所有的alias,调用findInContextualBindings
foreach ($this->abstractAliases[$abstract] as $alias) {
if (! is_null($binding = $this->findInContextualBindings($alias))) {
return $binding;
}
}
}
/**
* Find the concrete binding for the given abstract in the contextual binding array.
* 在上下文绑定数组中找到给定抽象的具体绑定。
* @param string $abstract
* @return \Closure|string|null
*/
protected function findInContextualBindings($abstract)
{
//end:输出数组中的最后一个元素的值:
//buildStack是当前容器的虚拟栈,用于在递归调用make或者build
//时的函数参数信息上下文。
if (isset($this->contextual[end($this->buildStack)][$abstract])) {
return $this->contextual[end($this->buildStack)][$abstract];
}
}
从上面代码实现可以看出,resolve最终都是要调用build生成object的,先看build方法,后面在分析在什么情况下会递归调用make(resolve)进行实例生成。
build方法代码段:
public function build($concrete)
{
//如果传参试一个闭包(匿名函数),则直接调用该闭包,并返回
if ($concrete instanceof Closure) {
return $concrete($this, $this->getLastParameterOverride());
}
//新建反射类 ,反射类可以获取该类的一些基本参数。
$reflector = new ReflectionClass($concrete);
//如果类不可以实例化,则给上层抛出一个异常。
if (! $reflector->isInst