Laravel源码剖析之bootstrap/app.php都做了什么(二)

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);

感兴趣的小伙伴可以关注一下,你的关注是我最大的动力,也可以私信我一起探讨,大家一起进步,人少可以走的更快,而人多才可以走的更远,咱们一起努力

猜你喜欢

转载自blog.csdn.net/Attitude_do_it/article/details/121494020