Lavavel源码剖析之index.php介绍(一)_Attitude_do_it的博客-CSDN博客
从上边这篇对index.php介绍的文章可以看出
$app = require_once __DIR__.'/../bootstrap/app.php';
这行代码引入了框架的一些基础功能,接下来我们看看它到底做了什么。
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| The first thing we will do is create a new Laravel application instance
| which serves as the "glue" for all the components of Laravel, and is
| the IoC container for the system binding all of the various parts.
|
*/
创建应用
---------------------------------------------------------------------------
我们要做的第一件事是创建一个新的Laravel应用程序实例,它作为Laravel所有组件的“粘合剂”
,使系统绑定所有不同部分的IoC(控制反转)容器。
第一行代码:
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
创建了应用实例,传入了项目的基础路径,接下来进入这个Application类,仔细看看内部发生了什么
首先
class Application extends Container implements ApplicationContract, HttpKernelInterface
可以看出,Application继承了Container以及ApplicationContract,HttpKernelInterface,我们先不管后边这些继承是干什么的,先来看看它的构造函数:
/**
* Create a new Illuminate application instance.
* 创建一个Illuminate应用实例
* @param string|null $basePath
* @return void
*/
public function __construct($basePath = null)
{
if ($basePath) {
$this->setBasePath($basePath);
}
$this->registerBaseBindings();
$this->registerBaseServiceProviders();
$this->registerCoreContainerAliases();
}
可以看出,先执行了setBasePath这个函数,看字面意思应该是设置了一些基础路径,实际上就是这样:
/**
* Set the base path for the application.
* 为应用设置基础路径
* @param string $basePath
* @return $this
*/
public function setBasePath($basePath)
{
$this->basePath = rtrim($basePath, '\/');
$this->bindPathsInContainer();
return $this;
}
/**
* 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());
}
到最后可以发现,代码多处出现了instance这个函数,并且后边也会经常出现,所以我们应该意识到这个函数的重要性,我们需要知道它都做了什么!
/**
* 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]);
$this->instances[$abstract] = $instance;
.....
}
省略号代表与此篇文章无关,不影响项目的启动,不用在意,以下省略号意义相同!
instance具有两个参数,一个是$abstract,字符串类型,一个是$instance,为混合类型。
先来看看函数中执行的第一个方法removeAbstractAlias:
/**
* Remove an alias from the contextual binding alias cache.
* 从上下文绑定别名缓存中删除别名
* @param string $searched
* @return void
*/
protected function removeAbstractAlias($searched)
{
//检测别名是否已在缓存中,如果没有则return
if (! isset($this->aliases[$searched])) {
return;
}
//如果有,循环所有的别名,至于abstractAliases这个变量的结构,后边会有说明
foreach ($this->abstractAliases as $abstract => $aliases) {
foreach ($aliases as $index => $alias) {
//如果有这个别名,则删除
if ($alias == $searched) {
unset($this->abstractAliases[$abstract][$index]);
}
}
}
}
instance函数的第二行:
$isBound = $this->bound($abstract);
bound函数:
/**
* 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);
}
instance函数的第三行:
//删除别名
unset($this->aliases[$abstract]);
instance函数的第四行:
//把实例放到instances数组中
$this->instances[$abstract] = $instance;
运行到这里,项目所需要的instances实例就已经加载完毕了。此时可以打印instances看一下,在Application的构造函数内:
public function __construct($basePath = null)
{
if ($basePath) {
$this->setBasePath($basePath);
}
dd($this->instances);
$this->registerBaseBindings();
$this->registerBaseServiceProviders();
$this->registerCoreContainerAliases();
}
如图:
基础路径已经全部加载完成了,当然,这还没有结束,请往下看:
在构造函数内,第二个调用的函数:
/**
* 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->instance(PackageManifest::class, new PackageManifest(
new Filesystem, $this->basePath(), $this->getCachedPackagesPath()
));
}
通过前边对instance的解析,不难看出,这里只是增加了三个实例到instances数组内,但是,代码中出现了一个新的方法,singleton,不要慌,我们慢慢看:
/**
* 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);
}
接下来跟着去看bind方法都做了什么:
/**
* 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.
//如果$concrete不是一个闭包,则创建一个闭包,用这个类本身
if (! $concrete instanceof Closure) {
$concrete = $this->getClosure($abstract, $concrete);
}
//把实例放到bindings数组中
$this->bindings[$abstract] = compact('concrete', 'shared');
......
}
运行到这里,刚刚的singleton要加载的Mix类已经绑定到容器了,此时可以打印下bindings看下,在Application的构造函数内:
/**
* Create a new Illuminate application instance.
*
* @param string|null $basePath
* @return void
*/
public function __construct($basePath = null)
{
if ($basePath) {
$this->setBasePath($basePath);
}
$this->registerBaseBindings();
dd($this->bindings);
$this->registerBaseServiceProviders();
$this->registerCoreContainerAliases();
}
如图:
到这里基础的绑定就加载结束了,再往下看:
$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));
}
/**
* Register a service provider with the application.
* 为应用注册一个服务提供者
* @param \Illuminate\Support\ServiceProvider|string $provider
* @param bool $force
* @return \Illuminate\Support\ServiceProvider
*/
public function register($provider, $force = false)
{
//如果服务已经被注册,则返回注册实例
if (($registered = $this->getProvider($provider)) && ! $force) {
return $registered;
}
// If the given "provider" is a string, we will resolve it, passing in the
// application instance automatically for the developer. This is simply
// a more convenient way of specifying your service provider classes.
// 如果被给的peovider是一个字符串,通过应用实例自动解析它。这是一个具体方法指定
你的服务类
if (is_string($provider)) {
$provider = $this->resolveProvider($provider);
}
//这个register是咱们自定义的,可以在里边加载一些咱们想要的服务
$provider->register();
// If there are bindings / singletons set as properties on the provider we
// will spin through them and register them with the application, which
// serves as a convenience layer while registering a lot of bindings.
if (property_exists($provider, 'bindings')) {
foreach ($provider->bindings as $key => $value) {
$this->bind($key, $value);
}
}
if (property_exists($provider, 'singletons')) {
foreach ($provider->singletons as $key => $value) {
$this->singleton($key, $value);
}
}
//把服务放到serviceProviders数组中,并标记这个provider已经被加载过
$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->isBooted()) {
$this->bootProvider($provider);
}
return $provider;
}
/**
* Mark the given provider as registered.
* 标记注册过的服务
* @param \Illuminate\Support\ServiceProvider $provider
* @return void
*/
protected function markAsRegistered($provider)
{
//服务放到serviceProviders数组
$this->serviceProviders[] = $provider;
//标记为true表示已经注册过该服务
$this->loadedProviders[get_class($provider)] = true;
}
现在咱们可以打印一下serviceProviders数组
public function __construct($basePath = null)
{
if ($basePath) {
$this->setBasePath($basePath);
}
$this->registerBaseBindings();
$this->registerBaseServiceProviders();
dd($this->serviceProviders);
$this->registerCoreContainerAliases();
}
如图:
可以看到,框架需要的基础服务已经加载结束,现在终于到最后一步了:
$this->registerCoreContainerAliases();
/**
* Register the core class aliases in the container.
* 在这个容器中注册核心类别名
* @return void
*/
public function registerCoreContainerAliases()
{
foreach ([
'app' => [self::class, \Illuminate\Contracts\Container\Container::class, \Illuminate\Contracts\Foundation\Application::class, \Psr\Container\ContainerInterface::class],
'auth' => [\Illuminate\Auth\AuthManager::class, \Illuminate\Contracts\Auth\Factory::class],
'auth.driver' => [\Illuminate\Contracts\Auth\Guard::class],
'blade.compiler' => [\Illuminate\View\Compilers\BladeCompiler::class],
'cache' => [\Illuminate\Cache\CacheManager::class, \Illuminate\Contracts\Cache\Factory::class],
'cache.store' => [\Illuminate\Cache\Repository::class, \Illuminate\Contracts\Cache\Repository::class, \Psr\SimpleCache\CacheInterface::class],
'cache.psr6' => [\Symfony\Component\Cache\Adapter\Psr16Adapter::class, \Symfony\Component\Cache\Adapter\AdapterInterface::class, \Psr\Cache\CacheItemPoolInterface::class],
'config' => [\Illuminate\Config\Repository::class, \Illuminate\Contracts\Config\Repository::class],
'cookie' => [\Illuminate\Cookie\CookieJar::class, \Illuminate\Contracts\Cookie\Factory::class, \Illuminate\Contracts\Cookie\QueueingFactory::class],
'encrypter' => [\Illuminate\Encryption\Encrypter::class, \Illuminate\Contracts\Encryption\Encrypter::class],
'db' => [\Illuminate\Database\DatabaseManager::class, \Illuminate\Database\ConnectionResolverInterface::class],
'db.connection' => [\Illuminate\Database\Connection::class, \Illuminate\Database\ConnectionInterface::class],
'events' => [\Illuminate\Events\Dispatcher::class, \Illuminate\Contracts\Events\Dispatcher::class],
'files' => [\Illuminate\Filesystem\Filesystem::class],
'filesystem' => [\Illuminate\Filesystem\FilesystemManager::class, \Illuminate\Contracts\Filesystem\Factory::class],
'filesystem.disk' => [\Illuminate\Contracts\Filesystem\Filesystem::class],
'filesystem.cloud' => [\Illuminate\Contracts\Filesystem\Cloud::class],
'hash' => [\Illuminate\Hashing\HashManager::class],
'hash.driver' => [\Illuminate\Contracts\Hashing\Hasher::class],
'translator' => [\Illuminate\Translation\Translator::class, \Illuminate\Contracts\Translation\Translator::class],
'log' => [\Illuminate\Log\LogManager::class, \Psr\Log\LoggerInterface::class],
'mailer' => [\Illuminate\Mail\Mailer::class, \Illuminate\Contracts\Mail\Mailer::class, \Illuminate\Contracts\Mail\MailQueue::class],
'auth.password' => [\Illuminate\Auth\Passwords\PasswordBrokerManager::class, \Illuminate\Contracts\Auth\PasswordBrokerFactory::class],
'auth.password.broker' => [\Illuminate\Auth\Passwords\PasswordBroker::class, \Illuminate\Contracts\Auth\PasswordBroker::class],
'queue' => [\Illuminate\Queue\QueueManager::class, \Illuminate\Contracts\Queue\Factory::class, \Illuminate\Contracts\Queue\Monitor::class],
'queue.connection' => [\Illuminate\Contracts\Queue\Queue::class],
'queue.failer' => [\Illuminate\Queue\Failed\FailedJobProviderInterface::class],
'redirect' => [\Illuminate\Routing\Redirector::class],
'redis' => [\Illuminate\Redis\RedisManager::class, \Illuminate\Contracts\Redis\Factory::class],
'redis.connection' => [\Illuminate\Redis\Connections\Connection::class, \Illuminate\Contracts\Redis\Connection::class],
'request' => [\Illuminate\Http\Request::class, \Symfony\Component\HttpFoundation\Request::class],
'router' => [\Illuminate\Routing\Router::class, \Illuminate\Contracts\Routing\Registrar::class, \Illuminate\Contracts\Routing\BindingRegistrar::class],
'session' => [\Illuminate\Session\SessionManager::class],
'session.store' => [\Illuminate\Session\Store::class, \Illuminate\Contracts\Session\Session::class],
'url' => [\Illuminate\Routing\UrlGenerator::class, \Illuminate\Contracts\Routing\UrlGenerator::class],
'validator' => [\Illuminate\Validation\Factory::class, \Illuminate\Contracts\Validation\Factory::class],
'view' => [\Illuminate\View\Factory::class, \Illuminate\Contracts\View\Factory::class],
] as $key => $aliases) {
foreach ($aliases as $alias) {
$this->alias($key, $alias);
}
}
}
/**
* Alias a type to a different name.
* 将类型别名为其他名称
* @param string $abstract
* @param string $alias
* @return void
*
* @throws \LogicException
*/
public function alias($abstract, $alias)
{
if ($alias === $abstract) {
throw new LogicException("[{$abstract}] is aliased to itself.");
}
$this->aliases[$alias] = $abstract;
$this->abstractAliases[$abstract][] = $alias;
}
这一步就很简单了,加载了框架需要的一些核心类,并且用aliases和abstractAliases两个数组将这些核心类存储了起来,在这里我们也可以看到aliases和abstractAliases的区别了,aliases是以类名为键,别名为值,而abstractAliases是以别名为键,类名为值,如图:
到这里,Application就算是初始化完毕了,现在我们回到bootstrap/app.php文件,往下看:
/*
|--------------------------------------------------------------------------
| Bind Important Interfaces
绑定重要的接口
|--------------------------------------------------------------------------
|
| Next, we need to bind some important interfaces into the container so
| we will be able to resolve them when needed. The kernels serve the
| incoming requests to this application from both the web and CLI.
下一步,我们需要绑定一些重要的接口到这个容器,以至于当我们需要的时候可以能够解析他们,
kernels服务于从web和CLI进来的请求
|
*/
$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 The Application
返回这个应用
|--------------------------------------------------------------------------
|
| This script returns the application instance. The instance is given to
| the calling script so we can separate the building of the instances
| from the actual running of the application and sending responses.
| 这个脚本返回应用实例,实例被提供给调用脚本,因此我们可以将实例的构建与应用程序的
实际运行和发送响应分开
*/
return $app;
singleton这个方法我们上边已经讲过了,所以这三个singleton把请求的核心类给绑定到了容器上,现在我们可以看到bindings数组里已经有了这个类,如图:
到这里,bootstrap/app.php中发生的一系列框架需要准备的东西就算是全部加载完成了。
下篇咱们来讲下边这行代码中的make函数
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
感兴趣的小伙伴可以关注一下,你的关注是我最大的动力,也可以私信我一起探讨,大家一起进步,人少可以走的更快,而人多才可以走的更远,咱们一起努力