背景:断点调试寻找对应文件,忽略次要步骤,仅描述核心动作,‘/’表示index.php所在目录
地址:index.php
调用$app的make方法
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
实现思路
strat : $app->make()
then : Container Class reslove()
判断参数是否已经实例化该类,是则直接返回存储在container内的实例;
then: Container类getConcrete()
判断参数是否绑定的匿名函数,是则返回匿名函数,否则返回原字符串
then: Container类build()
判断参数是否是匿名函数,是则执行该匿名函数,否则通过 ReflectionClass解析该类的构造方法及依赖参数,并对依赖参数通过调用make()进行实例化
final return kernel_object;
实现过程
//class Application
//$abstract = 'Illuminate\Contracts\Http\Kernel';
public function make($abstract, array $parameters = [])
{
return parent::make($abstract, $parameters);
}
//class Container
public function make($abstract, array $parameters = [])
{
return $this->resolve($abstract, $parameters);
}
//class Container
protected function resolve($abstract, $parameters = [])
{
$this->with[] = $parameters;//入栈当前实例所需参数
$concrete = $this->getConcrete($abstract);//获取$abstract注册的匿名函数,不存在则返回源字符
$object = $this->build($concrete);//获取kernel实例
if ($this->isShared($abstract) && ! $needsContextualBuild) {
$this->instances[$abstract] = $object;//向容器内注册当前实例
}
$this->resolved[$abstract] = true;//向容器内声明已实现当前类
array_pop($this->with);//出栈当前实例所需参数
return $object;//返回kernel实例
}
kernel实例的具体信息
Kernel {#29 ▼
#middleware: array:5 [▶]
#middlewareGroups: array:2 [▶]
#routeMiddleware: array:6 [▶]
#app: Application {#2 ▶}
#router: Router {#25 ▶}
#bootstrappers: array:6 [▶]
#middlewarePriority: array:6 [▶]
}
其中app属性为已声明容器,router属性为route实例,这两个实例为kernel构造函数所需参数,依赖ReflectionClass解析创建,回到resolve方法中,其中getConcrete方法为之前容器中绑定的匿名函数:
//class Container
protected function getConcrete($abstract)
{
return $this->bindings[$abstract]['concrete'];
}
build()执行匿名函数
public function build($concrete)
{
return $concrete($this, $this->getLastParameterOverride());
}
注册的匿名函数
/**
* $abstract = 'Illuminate\Contracts\Http\Kernel';
* $concrete = 'App\Http\Kernel';
*/
function ($container, $parameters = []) use ($abstract, $concrete) {
return $container->make($concrete, $parameters);
};
当前容器执行make()的参数发生了变化,这里的动作语义是创建一个实现了Illuminate\Contracts\Http\Kernel接口的类的实例;
//class Container
protected function resolve($abstract, $parameters = [])
{
$this->with[] = $parameters;//入栈当前实例所需参数
$concrete = $this->getConcrete($abstract);//获取$abstract注册的匿名函数,不存在则返回源字符
$object = $this->build($concrete);//获取kernel实例
if ($this->isShared($abstract) && ! $needsContextualBuild) {
$this->instances[$abstract] = $object;//向容器内注册当前实例
}
$this->resolved[$abstract] = true;//向容器内声明已实现当前实例
array_pop($this->with);//出栈当前实例所需参数
return $object;//返回kernel实例
}
与之前执行的make动作相同,仅仅参数发生了更变,其中核心是build():
public function build($concrete)
{
//$concrete不是匿名函数,需要使用ReflectionClass获取目标类的有关信息
$reflector = new ReflectionClass($concrete);
//判断目标类是否可实例化
if (! $reflector->isInstantiable()) {
return $this->notInstantiable($concrete);
}
//向堆栈中注入当前注册类名
$this->buildStack[] = $concrete;
//获取该类的构造函数信息
$constructor = $reflector->getConstructor();
if (is_null($constructor)) {
array_pop($this->buildStack);
return new $concrete;
}
//获取该类构造函数的参数信息
$dependencies = $constructor->getParameters();
//实例依赖参数
$instances = $this->resolveDependencies(
$dependencies
);
//将当前类名出栈
array_pop($this->buildStack);
//创建实例
return $reflector->newInstanceArgs($instances);
}
有关于类的信息实例的获取及方法调用均使用php预定义ReflectionClass实现,具体细节请查看官网,核心步骤是实现参数依赖:
//class Container
protected function resolveDependencies(array $dependencies)
{
$results = [];
foreach ($dependencies as $dependency) {
//校验当前参数名称是否在之前入栈with中的参数,之前入栈参数为[],这里返回false
if ($this->hasParameterOverride($dependency)) {
$results[] = $this->getParameterOverride($dependency);
continue;
}
$results[] = $this->resolveClass($dependency);
}
return $results;
}
resolveClass方法常见依赖实例:
//class Container
protected function resolveClass(ReflectionParameter $parameter)
{
try {
return $this->make($parameter->getClass()->name);
}
if ($parameter->isOptional()) {
return $parameter->getDefaultValue();
}
throw $e;
}
}
再次调用make():
//class Application
public function make($abstract, array $parameters = [])
{
// $abstract由'Illuminate\Contracts\Foundation\Application'映射为‘app’
$abstract = $this->getAlias($abstract);
return parent::make($abstract, $parameters);
}
make()中调用resolve():
protected function resolve($abstract, $parameters = [])
{
if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
return $this->instances[$abstract];
}
}
这里已经注册过‘app’实例,直接返回;
我们继续循环看router实例的创建,这里直接类似的过程:
protected function resolve($abstract, $parameters = [])
{
$this->with[] = $parameters;
$concrete = $this->getConcrete($abstract);
$object = $this->build($concrete);
if ($this->isShared($abstract) && ! $needsContextualBuild) {
$this->instances[$abstract] = $object;
}
$this->fireResolvingCallbacks($abstract, $object);
$this->resolved[$abstract] = true;
array_pop($this->with);
return $object;
}
这里与之前类似,在容器的binding属性中已经绑定过router对应的匿名函数:
function ($app) {
return new Router($app['events'], $app);
}
这里先创建events参数,实现ArrayAccess接口的offsetGet()
//class Container
public function offsetGet($key)
{
return $this->make($key);
}
怎么老是你???不必再看了,我们之前已经在容器的binding属性中已经绑定过events对应的匿名函数:
function ($app) {
return (new Dispatcher($app))->setQueueResolver(function () use ($app) {
return $app->make(QueueFactoryContract::class);
}
额,吐槽一下,laravel的匿名函数用的真多;看一下Dispatcher的构造函数与setQueueResolver():
//class Dispatcher
public function __construct(ContainerContract $container = null)
{
$this->container = $container ?: new Container;
}
public function setQueueResolver(callable $resolver)
{
$this->queueResolver = $resolver;
return $this;
}
重点看serQueueResolver方法,这里的参数是匿名函数,并没有执行该匿名函数,将该匿名函数赋值给queueResolver属性,好啦终于可以去实例router了,不过在这之前先等一下把尾巴收完:
//class Container
protected function resolve($abstract, $parameters = [])
{
...
$object = build($concrete);
if ($this->isShared($abstract) && ! $needsContextualBuild) {
$this->instances[$abstract] = $object;//向容器内注册当前实例
}
$this->resolved[$abstract] = true;//向容器内声明已实现当前实例
array_pop($this->with);//出栈当前实例所需参数
return $object;//返回实例
}
每一个实例通过make创建后在要容器内更新相关信息,后面的实例就不再赘述该过程了
router:
//class Router
public function __construct(Dispatcher $events, Container $container = null)
{
$this->events = $events;
$this->routes = new RouteCollection;
$this->container = $container ?: new Container;
}
简单的为router初始化赋值,其中new RouteCollection无构造函数,
喘一口气,总算实现了kernel实例的参数依赖问题,回到kernel的build方法中
public function build($concrete)
{
...
$reflector = new ReflectionClass($concrete);
...
$dependencies = $constructor->getParameters();
$instances = $this->resolveDependencies(
$dependencies
);
//出栈App\Http\Kernel
array_pop($this->buildStack);
//创建实例,$instances为依赖参数的数组
return $reflector->newInstanceArgs($instances);
}
加油啊,就快结束了,kernel的类文件为\app\Http\Kernel.php,继承了Illuminate\Foundation\Http\Kernel,
Illuminate\Foundation\Http\Kernel又实现了Illuminate\Contracts\Http\Kernel接口
//class Illuminate\Foundation\Http\Kernel
protected $bootstrappers = [
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
\Illuminate\Foundation\Bootstrap\HandleExceptions::class,
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
\Illuminate\Foundation\Bootstrap\RegisterProviders::class,
\Illuminate\Foundation\Bootstrap\BootProviders::class,
];
protected $middlewarePriority = [
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\Illuminate\Auth\Middleware\Authenticate::class,
\Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Illuminate\Auth\Middleware\Authorize::class,
];
public function __construct(Application $app, Router $router)
{
$this->app = $app;
$this->router = $router;
$router->middlewarePriority = $this->middlewarePriority;
foreach ($this->middlewareGroups as $key => $middleware) {
$router->middlewareGroup($key, $middleware);
}
foreach ($this->routeMiddleware as $key => $middleware) {
$router->aliasMiddleware($key, $middleware);
}
}
//class App\Http\Kernel
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
\App\Http\Middleware\TrustProxies::class,
];
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
'throttle:60,1',
'bindings',
],
];
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
];
初始化属性赋值,为router实例的middlewarePriority赋值;我们web.php中使用的路由csrf验证的配置就在这里;
终于打完收工,使用debug一点点看其实也不难,只不过还不是很理解其中的要义,在代码的显示过程中一些没有执行且与当前执行不相关的方法并没有进行显示,在今后的源码阅读的过程中如果有出现的过程再进行弥补吧!
文中如果存在着错误,希望指正!谢谢!