首先打开index.php
define('LARAVEL_START', microtime(true)); //常量定义
require __DIR__.'/../vendor/autoload.php'; //引入自动加载
$app = require_once __DIR__.'/../bootstrap/app.php'; //获取app实例
打开bootstrap/app.php
首先new了一个Application类实例,传入了路径参数,初始文件所处目录的上级目录,末尾也不带斜杠,也可以通过APP_BASE_PATH设置
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__) //项目根目录,例如blog
);
进入Application类中,首先执行的是构造方法
public function __construct($basePath = null)
{
if ($basePath) {
$this->setBasePath($basePath);
}
$this->registerBaseBindings();
$this->registerBaseServiceProviders();
$this->registerCoreContainerAliases();
}
首先看$this->setBasePath($basePath);
public function setBasePath($basePath)
{
$this->basePath = rtrim($basePath, '\/'); //这里赋值项目根目录,例如D:\www\blog
$this->bindPathsInContainer();
return $this;
}
接着执行$this->bindPathsInContainer();
/**
* Bind all of the application paths in the container.
*
* @return void
*/
protected function bindPathsInContainer()
{
$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());
}
这里$this->path();,也就是返回了app目录,例如D:\www\blog\app
/**
* Get the path to the application "app" directory.
*
* @param string $path
* @return string
*/
public function path($path = '')
{
$appPath = $this->appPath ?: $this->basePath.DIRECTORY_SEPARATOR.'app';
return $appPath.($path ? DIRECTORY_SEPARATOR.$path : $path);
}
其他路径也类似,例如configPath();
public function configPath($path = '')
{
return $this->basePath.DIRECTORY_SEPARATOR.'config'.($path ? DIRECTORY_SEPARATOR.$path : $path);
}
接着看$this->instance('path', $this->path())方法,这里走到了container类中去了,因为Application类是继承了container类的
/**
* Register an existing instance as shared in the container.
*
* @param string $abstract
* @param mixed $instance
* @return mixed
*/
public function instance($abstract, $instance)
{
$this->removeAbstractAlias($abstract);
$isBound = $this->bound($abstract);
unset($this->aliases[$abstract]);
// We'll check to determine if this type has been bound before, and if it has
// we will fire the rebound callbacks registered with the container and it
// can be updated with consuming classes that have gotten resolved here.
$this->instances[$abstract] = $instance;
if ($isBound) {
$this->rebound($abstract);
}
return $instance;
}
接着走到$this->removeAbstractAlias($abstract);方法,传入的是'path',这里主要是从Application实例的aliases变量中清除缓存,通过代码很容易看出,不存在就直接返回,存在就遍历数组从中清除掉
/**
* Remove an alias from the contextual binding alias cache.
*
* @param string $searched
* @return void
*/
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]);
}
}
}
}
那么后面的configPath()也都一样,清除缓存
总结,第一个方法的作用是设置项目根目录和清除$alias中的缓存
if ($basePath) {
$this->setBasePath($basePath);
}
所以回到了Application的构造方法里继续向下执行$this->registerBaseBindings();方法,根据翻译大概知道是注册绑定一个基础的容器
/**
* Register the basic bindings into the container.
*
* @return void
*/
protected function registerBaseBindings()
{
static::setInstance($this);
$this->instance('app', $this);
$this->instance(Container::class, $this);
$this->singleton(Mix::class);
$this->singleton(PackageManifest::class, function () {
return new PackageManifest(
new Filesystem, $this->basePath(), $this->getCachedPackagesPath()
);
});
}
执行static::setInstance($this);,这里把$this赋给了$instance
/**
* Set the shared instance of the container.
*
* @param \Illuminate\Contracts\Container\Container|null $container
* @return \Illuminate\Contracts\Container\Container|static
*/
public static function setInstance(ContainerContract $container = null)
{
return static::$instance = $container;
}
然后$this->instance('app', $this);
/**
* Register an existing instance as shared in the container.
*
* @param string $abstract
* @param mixed $instance
* @return mixed
*/
public function instance($abstract, $instance)
{
$this->removeAbstractAlias($abstract);
$isBound = $this->bound($abstract);
unset($this->aliases[$abstract]);
// We'll check to determine if this type has been bound before, and if it has
// we will fire the rebound callbacks registered with the container and it
// can be updated with consuming classes that have gotten resolved here.
$this->instances[$abstract] = $instance;
if ($isBound) {
$this->rebound($abstract);
}
return $instance;
}
然后$this->removeAbstractAlias($abstract);上面说过,是清除$this->alias['app']的缓存
然后$isBound = $this->bound($abstract);,判断$bindings,instances,alias中是否存在'app'这个键值,存在则为true,所以第一次运行这里为false
/**
* Determine if the given abstract type has been bound.
*
* @param string $abstract
* @return bool
*/
public function bound($abstract)
{
return isset($this->bindings[$abstract]) ||
isset($this->instances[$abstract]) ||
$this->isAlias($abstract);
}
/**
* Determine if a given string is an alias.
*
* @param string $name
* @return bool
*/
public function isAlias($name)
{
return isset($this->aliases[$name]);
}
接着unset($this->aliases[$abstract]);,从aliases中unset掉'app'
然后$this->instances[$abstract] = $instance;,把$this赋给instance['app']
if ($isBound) {
$this->rebound($abstract);
}
当然这里由于是false不执行,但还是看一下$this->rebound($abstract);
/**
* Fire the "rebound" callbacks for the given abstract type.
*
* @param string $abstract
* @return void
*/
protected function rebound($abstract)
{
$instance = $this->make($abstract);
foreach ($this->getReboundCallbacks($abstract) as $callback) {
call_user_func($callback, $this, $instance);
}
}
这里$instance = $this->make($abstract);,记住传入的是'app'
/**
* Resolve the given type from the container.
*
* @param string $abstract
* @param array $parameters
* @return mixed
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function make($abstract, array $parameters = [])
{
return $this->resolve($abstract, $parameters);
}
然后$this->resolve($abstract, $parameters);,传入了'app'和空数组,这段代码有点长
/**
* Resolve the given type from the container.
*
* @param string $abstract
* @param array $parameters
* @param bool $raiseEvents
* @return mixed
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
protected function resolve($abstract, $parameters = [], $raiseEvents = true)
{
$abstract = $this->getAlias($abstract); //这里获取$this->alias['app'],为null,因为前面没有设置,而且unset掉了
$concrete = $this->getContextualConcrete($abstract);
$needsContextualBuild = ! empty($parameters) || ! is_null($concrete);
// 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.
if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
return $this->instances[$abstract];
}
$this->with[] = $parameters;
if (is_null($concrete)) {
$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.
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.
if ($this->isShared($abstract) && ! $needsContextualBuild) {
$this->instances[$abstract] = $object;
}
if ($raiseEvents) {
$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;
}
看下$this->getContextualConcrete($abstract);方法
/**
* Get the contextual concrete binding for the given abstract.
*
* @param string $abstract
* @return \Closure|string|array|null
*/
protected function getContextualConcrete($abstract)
{
if (! is_null($binding = $this->findInContextualBindings($abstract))) {
return $binding;
}
// Next we need to see if a contextual binding might be bound under an alias of the
// given abstract type. So, we will need to check if any aliases exist with this
// type and then spin through them and check for contextual bindings on these.
if (empty($this->abstractAliases[$abstract])) {
return;
}
foreach ($this->abstractAliases[$abstract] as $alias) {
if (! is_null($binding = $this->findInContextualBindings($alias))) {
return $binding;
}
}
}
再接着$this->findInContextualBindings($abstract)方法,传入的也是'app',返回的是一个contextual数组里的值,起初没有设置,那么这里返回null
/**
* Find the concrete binding for the given abstract in the contextual binding array.
*
* @param string $abstract
* @return \Closure|string|null
*/
protected function findInContextualBindings($abstract)
{
return $this->contextual[end($this->buildStack)][$abstract] ?? null;
}
那么$needsContextualBuild = ! empty($parameters) || ! is_null($concrete);这里就是true,也就是需要构建这个啥数组值
所以后面直接返回了$this->instances[$abstract],后面就先不看了
接着回到Application中的registerBaseBindings()中的$this->instance(Container::class, $this);方法,把Container::class类添加到$instance,
这就是$this->instances[Container::class] 和 $this->instances['app']都是this
接着$this->singleton(Mix::class);
/**
* Register a shared binding in the container.
*
* @param string $abstract
* @param \Closure|string|null $concrete
* @return void
*/
public function singleton($abstract, $concrete = null)
{
$this->bind($abstract, $concrete, true);
}
/**
* Register a binding with the container.
*
* @param string $abstract
* @param \Closure|string|null $concrete
* @param bool $shared
* @return void
*/
public function bind($abstract, $concrete = null, $shared = false)
{
$this->dropStaleInstances($abstract);
// If no concrete type was given, we will simply set the concrete type to the
// abstract type. After that, the concrete type to be registered as shared
// without being forced to state their classes in both of the parameters.
if (is_null($concrete)) {
$concrete = $abstract;
}
// If the factory is not a Closure, it means it is just a class name which is
// bound into this container to the abstract type and we will just wrap it
// up inside its own Closure to give us more convenience when extending.
if (! $concrete instanceof Closure) {
if (! is_string($concrete)) {
throw new \TypeError(self::class.'::bind(): Argument #2 ($concrete) must be of type Closure|string|null');
}
$concrete = $this->getClosure($abstract, $concrete);
}
$this->bindings[$abstract] = compact('concrete', 'shared');
// If the abstract type was already resolved in this container we'll fire the
// rebound listener so that any objects which have already gotten resolved
// can have their copy of the object updated via the listener callbacks.
if ($this->resolved($abstract)) {
$this->rebound($abstract);
}
}
$this->dropStaleInstances($abstract);,传入了Mix::class,清除了数组中的键值
**
* Drop all of the stale instances and aliases.
*
* @param string $abstract
* @return void
*/
protected function dropStaleInstances($abstract)
{
unset($this->instances[$abstract], $this->aliases[$abstract]);
}
PHP Closure 类是用于代表匿名函数的类,匿名函数(在 PHP 5.3 中被引入)会产生这个类型的对象,Closure类摘要如下:Closure解释
Closure {
__construct ( void )
public static Closure bind (Closure $closure , object $newthis [, mixed $newscope = 'static' ])
public Closure bindTo (object $newthis [, mixed $newscope = 'static' ])
}
方法说明:
Closure::__construct — 用于禁止实例化的构造函数
Closure::bind — 复制一个闭包,绑定指定的$this对象和类作用域。
Closure::bindTo — 复制当前闭包对象,绑定指定的$this对象和类作用域。
除了此处列出的方法,还有一个 __invoke 方法。这是为了与其他实现了 __invoke()魔术方法 的对象保持一致性,但调用闭包对象的过程与它无关。
然后执行了这里$concrete = $this->getClosure($abstract, $concrete);,传入了两个Mix::class
/**
* Get the Closure to be used when building a type.
*
* @param string $abstract
* @param string $concrete
* @return \Closure
*/
protected function getClosure($abstract, $concrete)
{
return function ($container, $parameters = []) use ($abstract, $concrete) {
if ($abstract == $concrete) {
return $container->build($concrete);
}
return $container->resolve(
$concrete, $parameters, $raiseEvents = false
);
};
}
执行了$container->build($concrete);
/**
* Instantiate a concrete instance of the given type.
*
* @param \Closure|string $concrete
* @return mixed
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
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());
}
try {
$reflector = new ReflectionClass($concrete); //实例化了一个反射类
} catch (ReflectionException $e) {
throw new BindingResolutionException("Target class [$concrete] does not exist.", 0, $e);
}
// If the type is not instantiable, the developer is attempting to resolve
// an abstract type such as an Interface or 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;//没有构造方法就直接new
}
$dependencies = $constructor->getParameters();//获取构造方法的参数
// Once we have all the constructor's parameters we can create each of the
// dependency instances and then use the reflection instances to make a
// new instance of this class, injecting the created dependencies in.
try {
$instances = $this->resolveDependencies($dependencies);
} catch (BindingResolutionException $e) {
array_pop($this->buildStack);
throw $e;
}
array_pop($this->buildStack);
return $reflector->newInstanceArgs($instances);
}
/**
* Get the last parameter override.
*
* @return array
*/
protected function getLastParameterOverride()
{
return count($this->with) ? end($this->with) : []; //获取最后一个值
}
抛出类不可以实例化的异常
/**
* Throw an exception that the concrete is not instantiable.
*
* @param string $concrete
* @return void
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
protected function notInstantiable($concrete)
{
if (! empty($this->buildStack)) {
$previous = implode(', ', $this->buildStack);
$message = "Target [$concrete] is not instantiable while building [$previous].";
} else {
$message = "Target [$concrete] is not instantiable.";
}
throw new BindingResolutionException($message);
}
获取构造器方法$constructor = $reflector->getConstructor();,以及获取参数$constructor->getParameters();
接着$instances = $this->resolveDependencies($dependencies);这里获取类的参数,以及类型是self,parent等等,解析类
protected function resolveDependencies(array $dependencies)
{
$results = [];
foreach ($dependencies as $dependency) {
// If the dependency has an override for this particular build we will use
// that instead as the value. Otherwise, we will continue with this run
// of resolutions and let reflection attempt to determine the result.
if ($this->hasParameterOverride($dependency)) {
$results[] = $this->getParameterOverride($dependency);
continue;
}
// If the class is null, it means the dependency is a string or some other
// primitive type which we can not resolve since it is not a class and
// we will just bomb out with an error since we have no-where to go.
$result = is_null(Util::getParameterClassName($dependency))
? $this->resolvePrimitive($dependency)
: $this->resolveClass($dependency);
if ($dependency->isVariadic()) {
$results = array_merge($results, $result);
} else {
$results[] = $result;
}
}
return $results;
}
/**
* Get the class name of the given parameter's type, if possible.
*
* From Reflector::getParameterClassName() in Illuminate\Support.
*
* @param \ReflectionParameter $parameter
* @return string|null
*/
public static function getParameterClassName($parameter)
{
$type = $parameter->getType();
if (! $type instanceof ReflectionNamedType || $type->isBuiltin()) {
return;
}
$name = $type->getName();
if (! is_null($class = $parameter->getDeclaringClass())) {
if ($name === 'self') {
return $class->getName();
}
if ($name === 'parent' && $parent = $class->getParentClass()) {
return $parent->getName();
}
}
return $name;
}
由于我对方法的使用不熟,方法和返回都不太清楚,这里就当是直接实例化对象这样理解return $reflector->newInstanceArgs($instances);
然后$this->bindings[$abstract] = compact('concrete', 'shared');,把返回的实例化对象放到了bindings数组中,也就是Mix类的实例
接着$this->rebound($abstract);,上面讲过,后面把$abstract对应的类实例放到了$instance数组中了
if ($this->resolved($abstract)) {
$this->rebound($abstract);
}
再接着把PackageManifest类实例放进$instance中
$this->singleton(PackageManifest::class, function () {
return new PackageManifest(
new Filesystem, $this->basePath(), $this->getCachedPackagesPath()
);
});
再回到Application类构造方法中的$this->registerBaseServiceProviders();,注册基础服务
/**
* Register all of the base service providers.
*
* @return void
*/
protected function registerBaseServiceProviders()
{
$this->register(new EventServiceProvider($this));
$this->register(new LogServiceProvider($this));
$this->register(new RoutingServiceProvider($this));
}
再接着$this->registerCoreContainerAliases();,绑定基础的对象实例
回到app.php,接着又绑定了三个对象实例,分别用来处理相应的请求以及异常
$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
);
再回到index.php中,$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);,这里传入的是interface,实际绑定的类是Illuminate\Foundation\Http\Kernel
核心就在laravel\framework\src\Illuminate\Foundation\Http\Kernel.php,该类实现了Illuminate\Contracts\Http\Kernel接口
/**
* Resolve the given type from the container.
*
* @param string $abstract
* @param array $parameters
* @return mixed
*/
public function make($abstract, array $parameters = [])
{
$this->loadDeferredProviderIfNeeded($abstract = $this->getAlias($abstract));
return parent::make($abstract, $parameters);
}
延迟加载$this->loadDeferredProviderIfNeeded($abstract = $this->getAlias($abstract));
再接着parent::make($abstract, $parameters);,把Illuminate\Foundation\Http\Kernel类实例绑定到容器中
然后index.php中
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
这里$request = Illuminate\Http\Request::capture(),作为传入参数
/**
* Create a new Illuminate HTTP request from server variables.
*
* @return static
*/
public static function capture()
{
static::enableHttpMethodParameterOverride();
return static::createFromBase(SymfonyRequest::createFromGlobals());
}
public static function enableHttpMethodParameterOverride()
{
self::$httpMethodParameterOverride = true;
}
然后SymfonyRequest::createFromGlobals(),获取请求信息
/**
* Creates a new request with values from PHP's super globals.
*
* @return static
*/
public static function createFromGlobals()
{
$request = self::createRequestFromFactory($_GET, $_POST, [], $_COOKIE, $_FILES, $_SERVER);
if ($_POST) {
$request->request = new InputBag($_POST);
} elseif (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
&& \in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), ['PUT', 'DELETE', 'PATCH'])
) {
parse_str($request->getContent(), $data);
$request->request = new InputBag($data);
}
return $request;
}
然后static::createFromBase(SymfonyRequest::createFromGlobals());,框架封装请求信息
/**
* Create an Illuminate request from a Symfony instance.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* @return static
*/
public static function createFromBase(SymfonyRequest $request)
{
$newRequest = (new static)->duplicate(
$request->query->all(), $request->request->all(), $request->attributes->all(),
$request->cookies->all(), $request->files->all(), $request->server->all()
);
$newRequest->headers->replace($request->headers->all());
$newRequest->content = $request->content;
$newRequest->request = $newRequest->getInputSource();
return $newRequest;
}
这里调用了handle方法
/**
* Handle an incoming HTTP request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function handle($request)
{
try {
$request->enableHttpMethodParameterOverride();
$response = $this->sendRequestThroughRouter($request);
} catch (Throwable $e) {
$this->reportException($e);
$response = $this->renderException($request, $e);
}
$this->app['events']->dispatch(
new RequestHandled($request, $response)
);
return $response;
}
再接着$response = $this->sendRequestThroughRouter($request);
/**
* Send the given request through the middleware / router.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
protected function sendRequestThroughRouter($request)
{
$this->app->instance('request', $request);
Facade::clearResolvedInstance('request');
$this->bootstrap();
return (new Pipeline($this->app))
->send($request)
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
->then($this->dispatchToRouter());
}
这里调用$this->bootstrap();
/**
* Bootstrap the application for HTTP requests.
*
* @return void
*/
public function bootstrap()
{
if (! $this->app->hasBeenBootstrapped()) {
$this->app->bootstrapWith($this->bootstrappers());
}
}
然后调用$this->app->bootstrapWith($this->bootstrappers());,将基础服务类都绑定到容器中了
$this->bootstrappers()
/**
* Get the bootstrap classes for the application.
*
* @return array
*/
protected function bootstrappers()
{
return $this->bootstrappers;
}
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,
];
将上面的类实例全绑定到容器中了
/**
* Run the given array of bootstrap classes.
*
* @param string[] $bootstrappers
* @return void
*/
public function bootstrapWith(array $bootstrappers)
{
$this->hasBeenBootstrapped = true;
foreach ($bootstrappers as $bootstrapper) {
$this['events']->dispatch('bootstrapping: '.$bootstrapper, [$this]);
$this->make($bootstrapper)->bootstrap($this);
$this['events']->dispatch('bootstrapped: '.$bootstrapper, [$this]);
}
}
通过链式调用最终执行send()
/**
* Set the object being sent through the pipeline.
*
* @param mixed $passable
* @return $this
*/
public function send($passable)
{
$this->passable = $passable;
return $this;
}
最后$kernel->terminate($request, $response);,里面有调用中间件,可以随时终止执行并响应
/**
* Call the terminate method on any terminable middleware.
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Http\Response $response
* @return void
*/
public function terminate($request, $response)
{
$this->terminateMiddleware($request, $response);
$this->app->terminate();
}
/**
* Terminate the application.
*
* @return void
*/
public function terminate()
{
foreach ($this->terminatingCallbacks as $terminating) {
$this->call($terminating);
}
}