入口文件 indx.php
true
"router" => true
"App\Http\Kernel" => true
"Illuminate\Contracts\Http\Kernel" => true
]
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
# 最后,内核处理相应的请求
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
# 将请求发送给客户端
$response->send();
# 最后处理下结束中间件,注意如果在request中的terminate方法里面 dd(),或者echo什么的操作你是看不到的,因为响应已经在上一个方法中返回了,这里只能用 \Log 来记录。
$kernel->terminate($request, $response);
进入 bootstrap/app.php
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;
代码深入
$app = new Illuminate\Foundation\Application(
dirname(__DIR__)
);
上面代码做了以下的几件事
-
设置app的根目录以及绑定其他文件夹的目录
setBasePath->bindPathsInContainer
-
先来看看 Application 类中内置的属性
# 保证整个应用单一实例的单例,static 后期静态绑定 protected static $instance; # 记录运行到当前这一刻,已经实例化的类 resolved: array:4 [▼ "events" => true "router" => true "App\Http\Kernel" => true "Illuminate\Contracts\Http\Kernel" => true ] protected $resolved = []; # 记录接口和类的生成闭包,以及是否共享 protected $bindings = []; /** * The container's method bindings. * * @var array */ protected $methodBindings = []; # 存的都是具体的实例化类和系统路径 protected $instances = []; 'aliases' => [ "Illuminate\Foundation\Application" => "app" "Illuminate\Contracts\Container\Container" => "app" "Illuminate\Contracts\Foundation\Application" => "app" "Psr\Container\ContainerInterface" => "app" ] protected $aliases = []; /** * The registered aliases keyed by the abstract name. * * @var array */ protected $abstractAliases = []; /** * The extension closures for services. * * @var array */ protected $extenders = []; /** * All of the registered tags. * * @var array */ protected $tags = []; # 用来临时储存当前需要实例化的类,实例化完就清空了 buildStack: array:1 [▼ 0 => "App\Http\Kernel" ] protected $buildStack = []; # 用来储存当前实例化的类所需要的参数,实例化完就清空了 protected $with = []; /** * The contextual binding map. * * @var array */ public $contextual = []; /** * All of the registered rebound callbacks. * * @var array */ protected $reboundCallbacks = []; /** * All of the global resolving callbacks. * * @var array */ protected $globalResolvingCallbacks = []; /** * All of the global after resolving callbacks. * * @var array */ protected $globalAfterResolvingCallbacks = []; /** * All of the resolving callbacks by class type. * * @var array */ protected $resolvingCallbacks = []; /** * All of the after resolving callbacks by class type. * * @var array */ protected $afterResolvingCallbacks = [];
调用了 Application 的
instance
方法-
# abstractAliases 中的格式类似这样 "app" => array:4 [ 0 => "Illuminate\Foundation\Application" 1 => "Illuminate\Contracts\Container\Container" 2 => "Illuminate\Contracts\Foundation\Application" 3 => "Psr\Container\ContainerInterface" ] public function instance($abstract, $instance) { # dd($abstract, $instance); path , /var/www/html/app $this->removeAbstractAlias($abstract); $isBound = $this->bound($abstract); # 上面删除了 属性 abstractAliases 中,$abstract 对应的别名,这里要把 $abstract 键值也删除 unset($this->aliases[$abstract]); # 删除之后重新绑定,即 $instance => ['path'=>'/var/www/html/app'] $this->instances[$abstract] = $instance; if ($isBound) { # 取消bingings instances 和 aliases中的绑定 $this->rebound($abstract); } return $instance; }
# $searched => path # 先去搜索 alias 里面的 path 键值,不存在则返回,存在则删除存在的值 protected function removeAbstractAlias($searched) { 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]); } } } }
# $abstract => path # 去判断 bingings instances 和 aliases 中是否存在 path 键值 # 显然这里返回 false public function bound($abstract) { return isset($this->bindings[$abstract]) || isset($this->instances[$abstract]) || $this->isAlias($abstract); }
protected function rebound($abstract) { # 这一步不贴代码了,它具体做了以下几个操作 # 1. 去 alias 中找是否存在键值, # 2. 去看 buildStack[] 是否创建过这个实例 $instance = $this->make($abstract); foreach ($this->getReboundCallbacks($abstract) as $callback) { call_user_func($callback, $this, $instance); } }
# 这个方法的递归主要是为了找到根实例 # 因为 $aliases 中格式可能是这样的 # 'aliases' => [ # 'a' => 'b', # 'b' => 'c', # ] # a 的别名是 b,b 才对应到 c,c才是最终能找到实例的别名类 public function getAlias($abstract) { if (! isset($this->aliases[$abstract])) { return $abstract; } if ($this->aliases[$abstract] === $abstract) { throw new LogicException("[{$abstract}] is aliased to itself."); } return $this->getAlias($this->aliases[$abstract]); }
tips
true || $a = 'a'; // 后半句不会执行 echo $a; // Undefined variable: a
public function __construct($basePath = null) { # 这一步绑定了所有目录 array:9 [▼ "path" => "/var/www/html/app" "path.base" => "/var/www/html" "path.lang" => "/var/www/html/resources/lang" "path.config" => "/var/www/html/config" "path.public" => "/var/www/html/public" "path.storage" => "/var/www/html/storage" "path.database" => "/var/www/html/database" "path.resources" => "/var/www/html/resources" "path.bootstrap" => "/var/www/html/bootstrap" ] if ($basePath) { $this->setBasePath($basePath); } # 这一步做了 # 设置了static::$instance,保证了运行中的唯一单例 # 绑定了 $instance['app'] = $this # 绑定了 $instance['Illuminate\Container\Container'] = $this # 绑定了 $instance['Illuminate\Foundation\PackageManifest'] = PackageManifest 实例(大概是启动的时候需要给配置文件做缓存,所以优先级比较高) array:12 [▼ "path" => "/var/www/html/app" "path.base" => "/var/www/html" "path.lang" => "/var/www/html/resources/lang" "path.config" => "/var/www/html/config" "path.public" => "/var/www/html/public" "path.storage" => "/var/www/html/storage" "path.database" => "/var/www/html/database" "path.resources" => "/var/www/html/resources" "path.bootstrap" => "/var/www/html/bootstrap" "app" => Application {#2 ▶} "Illuminate\Container\Container" => Application {#2 ▶} "Illuminate\Foundation\PackageManifest" => PackageManifest {#4 ▶} ] $this->registerBaseBindings(); # 添加系统最基础最重要的服务 $this->registerBaseServiceProviders(); # 注册系统核心服务 $aliases 和 $abstractAliases 属性改变了 aliases => [ "Illuminate\Foundation\Application" => "app" "Illuminate\Contracts\Container\Container" => "app" "Illuminate\Contracts\Foundation\Application" => "app" "Psr\Container\ContainerInterface" => "app" ] abstractAliases: array:35 [▼ "app" => array:4 [▼ 0 => "Illuminate\Foundation\Application" 1 => "Illuminate\Contracts\Container\Container" 2 => "Illuminate\Contracts\Foundation\Application" 3 => "Psr\Container\ContainerInterface" ] $this->registerCoreContainerAliases(); }
protected function registerBaseServiceProviders() { $this->register(new EventServiceProvider($this)); $this->register(new LogServiceProvider($this)); $this->register(new RoutingServiceProvider($this)); }
public function register($provider, $force = false) { # getProvider 去获取 serviceProviders 数组中的 provider # 显然之前并没有注册过,return false if (($registered = $this->getProvider($provider)) && ! $force) { return $registered; } # 这里传入的是实例,所以 false if (is_string($provider)) { $provider = $this->resolveProvider($provider); } # 调用 EventServiceProvider,LogServiceProvider, RoutingServiceProvider 的 register 方法,绑定了系统日志,事件和路由 bindings: array:9 [▼ "events" => array:2 [▶] "log" => array:2 [▶] "router" => array:2 [▶] "url" => array:2 [▶] "redirect" => array:2 [▶] "Psr\Http\Message\ServerRequestInterface" => array:2 [▼ "concrete" => Closure {#14 ▶} "shared" => false ] "Psr\Http\Message\ResponseInterface" => array:2 [▶] "Illuminate\Contracts\Routing\ResponseFactory" => array:2 [▶] "Illuminate\Routing\Contracts\ControllerDispatcher" => array:2 [▶] ] if (method_exists($provider, 'register')) { $provider->register(); } ## 下面两个可以解释为什么在 serverProvider 中可以使用 $bindings 和 $singletons 绑定属性 # 解析provider中的 bingings 属性 if (property_exists($provider, 'bindings')) { foreach ($provider->bindings as $key => $value) { # bind 方法用到了反射,通过反射拿到对应实例的构造函数 $this->bind($key, $value); } } # 解析provider中的 singletons 属性 if (property_exists($provider, 'singletons')) { foreach ($provider->singletons as $key => $value) { $this->singleton($key, $value); } } # 把解析过的 serviceProvider 添加到 $serviceProviders 和 $loadedProviders 属性中 serviceProviders: array:3 [▼ 0 => EventServiceProvider {#6 ▶} 1 => LogServiceProvider {#8 ▶} 2 => RoutingServiceProvider {#10 ▶} ] loadedProviders: array:3 [▼ "Illuminate\Events\EventServiceProvider" => true "Illuminate\Log\LogServiceProvider" => true "Illuminate\Routing\RoutingServiceProvider" => true ] $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 this developer's application logic. if ($this->booted) { $this->bootProvider($provider); } return $provider; }
# EventServiceProvider public function register() { # $this->app 在这里把 Application 实例传进去了 -> $this->register(new EventServiceProvider($this)); # 然后 在 ServiceProvider 的构造函数中把 实例赋值给了 app属性 $this->app->singleton('events', function ($app) { return (new Dispatcher($app))->setQueueResolver(function () use ($app) { return $app->make(QueueFactoryContract::class); }); }); } # LogServiceProvider public function register() { $this->app->singleton('log', function () { return new LogManager($this->app); }); }
public function singleton($abstract, $concrete = null) { $this->bind($abstract, $concrete, true); } public function bind($abstract, $concrete = null, $shared = false) { # 如果已经绑定过instance,或存过 aliases 则删除 $this->dropStaleInstances($abstract); if (is_null($concrete)) { $concrete = $abstract; } # 不是闭包则获取闭包 if (! $concrete instanceof Closure) { $concrete = $this->getClosure($abstract, $concrete); } # 绑定实例 bindings: array:1 [▼ "events" => array:2 [▼ "concrete" => Closure {#7 ▶} # 这里存了生成实例的闭包 "shared" => true ] ] $this->bindings[$abstract] = compact('concrete', 'shared'); # 最后还不忘解绑已经实例化的抽象属性 if ($this->resolved($abstract)) { $this->rebound($abstract); } }
-
-
紧接着绑定了三个单例,高度解耦,把抽象和实例进行绑定
$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\Http\Kernel::class 实例 $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
make
方法最终会走到build
,这里注意框架内部用反射,拿到构造函数以及参数.protected function resolve($abstract, $parameters = []) { $abstract = $this->getAlias($abstract); $needsContextualBuild = ! empty($parameters) || ! is_null( $this->getContextualConcrete($abstract) ); # 判断是否已经实例化过类,有的话直接返回该实例 if (isset($this->instances[$abstract]) && ! $needsContextualBuild) { return $this->instances[$abstract]; } $this->with[] = $parameters; $concrete = $this->getConcrete($abstract); # 实例化 if ($this->isBuildable($concrete, $abstract)) { $object = $this->build($concrete); } else { $object = $this->make($concrete); } # 类似于装饰器模式,为某个类添加装饰器,返回装饰后的类,怎么用请看下面附言 foreach ($this->getExtenders($abstract) as $extender) { $object = $extender($object, $this); } # 判断是否是单例 if ($this->isShared($abstract) && ! $needsContextualBuild) { $this->instances[$abstract] = $object; } $this->fireResolvingCallbacks($abstract, $object); # 走到这里说明实例化完成,所以把该实例化的抽象类加到 $resolved 属性中 $this->resolved[$abstract] = true; array_pop($this->with); return $object; }
public function build($concrete) { // If the concrete type is actually a Closure, we will just execute it and // hand back the results of the functions, which allows functions to be // used as resolvers for more fine-tuned resolution of these objects. if ($concrete instanceof Closure) { return $concrete($this, $this->getLastParameterOverride()); } $reflector = new ReflectionClass($concrete); // If the type is not instantiable, the developer is attempting to resolve // an abstract type such as an Interface of Abstract Class and there is // no binding registered for the abstractions so we need to bail out. if (! $reflector->isInstantiable()) { return $this->notInstantiable($concrete); } $this->buildStack[] = $concrete; $constructor = $reflector->getConstructor(); // If there are no constructors, that means there are no dependencies then // we can just resolve the instances of the objects right away, without // resolving any other types or dependencies out of these containers. 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); }
反射类知识
getConstructor()->getParameters(); foreach ($c as $key) { var_dump($key->getClass()->name); // Duc\B } # 第二个报错,所以源码用来 try catch # 源码 protected function resolveClass(ReflectionParameter $parameter) { try { return $this->make($parameter->getClass()->name); } catch (BindingResolutionException $e) { if ($parameter->isOptional()) { return $parameter->getDefaultValue(); } throw $e; } }
-
内核构造函数
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); } }
-
-
最后执行
$response = $kernel->handle( $request = Illuminate\Http\Request::capture() );
public function handle($request) { try { $request->enableHttpMethodParameterOverride(); $response = $this->sendRequestThroughRouter($request); } catch (Exception $e) { $this->reportException($e); $response = $this->renderException($request, $e); } catch (Throwable $e) { $this->reportException($e = new FatalThrowableError($e)); $response = $this->renderException($request, $e); } $this->app['events']->dispatch( new Events\RequestHandled($request, $response) ); return $response; }
protected function sendRequestThroughRouter($request) { $this->app->instance('request', $request); Facade::clearResolvedInstance('request'); # 到这一刻系统才算启动成功 $this->bootstrap(); return (new Pipeline($this->app)) ->send($request) # $this->app->shouldSkipMiddleware() 在测试时用来跳过中间件的 ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); }
其中bootstrap方法:
加载系统环境变量,加载配置文件,异常处理,注册facads,注册app.php里面的系统的providers,注意这里最后一个
BootProviders
,会将启动属性改为 true,$this->booted = true;
,并且会调用所有已经注册的providers中的boot方法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, ];
附言
关于 extend 的使用
# 我这里给 config 加了一层扩展,输出一句 'use extend'。
# 这个方法仅限于扩展系统内部的服务类,类似于代理模式,或者装饰模式,代理可能更加好点
Route::get('/', function () {
app()->extend('config', function ($config, $app) {
echo 'use extend';
return $config;
});
dd(config('app.name'));
}
# 输出:
"Laravel"
use extend