国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Laravel學習筆記之bootstrap源碼解析

xiaoxiaozi / 1236人閱讀

摘要:總結本文主要學習了啟動時做的七步準備工作環境檢測配置加載日志配置異常處理注冊注冊啟動。

說明:Laravel在把Request通過管道Pipeline送入中間件Middleware和路由Router之前,還做了程序的啟動Bootstrap工作,本文主要學習相關源碼,看看Laravel啟動程序做了哪些具體工作,并將個人的研究心得分享出來,希望對別人有所幫助。Laravel在入口index.php時先加載Composer加載器:Laravel學習筆記之Composer自動加載,然后進行Application的實例化:Laravel學習筆記之IoC Container實例化源碼解析,得到實例化后的Application對象再從容器中解析出Kernel服務,然后進行Request實例化(Request實例化下次再聊),然后進行Bootstrap操作啟動程序,再通過Pipeline送到Middleware:Laravel學習筆記之Middleware源碼解析,然后經過路由映射找到對該請求的操作action(以后再聊),生成Response對象經過Kernel的send()發送給Client。本文主要聊下程序的啟動操作,主要做了哪些準備工作。

開發環境:Laravel5.3 + PHP7 + OS X 10.11

在Laravel學習筆記之Middleware源碼解析聊過,Kernel中的sendRequestThroughRouter()處理Request,并把Request交給Pipeline送到Middleware和Router中,看源碼:

    protected function sendRequestThroughRouter($request)
    {
        $this->app->instance("request", $request);

        Facade::clearResolvedInstance("request");

        /* 依次執行$bootstrappers中每一個bootstrapper的bootstrap()函數,做了幾件準備事情:
        1. 環境檢測 DetectEnvironment
        2. 配置加載 LoadConfiguration
        3. 日志配置 ConfigureLogging
        4. 異常處理 HandleException
        5. 注冊Facades RegisterFacades
        6. 注冊Providers RegisterProviders
        7. 啟動Providers BootProviders
         protected $bootstrappers = [
            "IlluminateFoundationBootstrapDetectEnvironment",
            "IlluminateFoundationBootstrapLoadConfiguration",
            "IlluminateFoundationBootstrapConfigureLogging",
            "IlluminateFoundationBootstrapHandleExceptions",
            "IlluminateFoundationBootstrapRegisterFacades",
            "IlluminateFoundationBootstrapRegisterProviders",
            "IlluminateFoundationBootstrapBootProviders",
        ];*/
        $this->bootstrap();

        return (new Pipeline($this->app))
                    ->send($request)
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());
    }

在Request被Pipeline送到Middleware前還有一步操作bootstrap()操作,這步操作就是啟動程序,看下IlluminateFoundationHttpKernel中的bootstrap()源碼:

    protected $hasBeenBootstrapped = false;
    
    ...
    
    /**
     * Bootstrap the application for HTTP requests.
     *
     * @return void
     */
    public function bootstrap()
    {
        // 檢查程序是否已經啟動
        if (! $this->app->hasBeenBootstrapped()) {
            $this->app->bootstrapWith($this->bootstrappers());
        }
    }
    
    public function hasBeenBootstrapped()
    {
        return $this->hasBeenBootstrapped;
    }
    
    protected function bootstrappers()
    {
        return $this->bootstrappers;
    }
    
    protected $bootstrappers = [
        "IlluminateFoundationBootstrapDetectEnvironment",
        "IlluminateFoundationBootstrapLoadConfiguration",
        "IlluminateFoundationBootstrapConfigureLogging",
        "IlluminateFoundationBootstrapHandleExceptions",
        "IlluminateFoundationBootstrapRegisterFacades",
        "IlluminateFoundationBootstrapRegisterProviders",
        "IlluminateFoundationBootstrapBootProviders",
    ];

從以上源碼可知道,程序將會依次bootstrapWith()數組$bootstrappers中各個bootstrapper,看下容器中的bootstrapWith()源碼:

    public function bootstrapWith(array $bootstrappers)
    {
        $this->hasBeenBootstrapped = true;

        foreach ($bootstrappers as $bootstrapper) {
            $this["events"]->fire("bootstrapping: ".$bootstrapper, [$this]);

            $this->make($bootstrapper)->bootstrap($this);

            $this["events"]->fire("bootstrapped: ".$bootstrapper, [$this]);
        }
    }

首先觸發"bootstrapping: ".$bootstrapper事件,告知將要啟動該bootstrapper,然后從容器中make($bootstrapper)出該$bootstrapper,并執行該$bootstrapper中的bootstrap()方法,最后在觸發事件:"bootstrapped: ".$bootstrapper,告知該$bootstrapper已經啟動OK了。啟動的bootstrappers就是數組$bootstrappers中的7個bootstrapper,看下程序做了哪些啟動工作。

1. 環境檢測

查看IlluminateFoundationBootstrapDetectEnvironment中的bootstrap()源碼:

    public function bootstrap(Application $app)
    {
        // 查看bootstrap/cache/config.php緩存文件是否存在
        // php artisan config:cache來生成配置緩存文件,就是把config/下的所有文件放在一個緩存文件內,提高性能
        // 這里假設沒有緩存配置文件
        if (! $app->configurationIsCached()) {
            $this->checkForSpecificEnvironmentFile($app);

            try {
                $env = $_ENV; // 調試添加的,此時為空
                // 這里把.env文件值取出存入$_ENV內
                (new Dotenv($app->environmentPath(), $app->environmentFile()))->load();
                // 這里$_ENV數組有值了
                $env = $_ENV;
            } catch (InvalidPathException $e) {
                //
            }
        }
    }
    
    protected function checkForSpecificEnvironmentFile($app)
    {
        // 讀取$_ENV全局變量中"APP_ENV"值,此時是空
        if (! env("APP_ENV")) {
            return;
        }

        $file = $app->environmentFile().".".env("APP_ENV"); // .env.local

        if (file_exists($app->environmentPath()."/".$file)) {
            $app->loadEnvironmentFrom($file);
        }
    }

環境監測核心就是把.env文件內值存入到$_ENV全局變量中DotenvDotenv::load()函數實現了這個功能,具體不詳述了。可以通過Xdebug調試查看:

2. 配置加載

配置加載就是讀取config/文件夾下的所有配置值,然后存入IlluminateConfigRepository對象中,而環境檢測是讀取.env文件存入$_ENV全局變量中,加載環境配置主要是使用SymfonyComponentFinderFinder這個組件進行文件查找,看下LoadConfiguration::bootstrap()的源碼:

    public function bootstrap(Application $app)
    {        
        $items = [];
        // 查看config有沒有緩存文件,緩存文件是在bootstrap/cache/config.php
        // 通過php artisan config:cache命令來生成緩存文件,把config/下的所有配置文件打包成一個文件,提高程序執行速度
        // 這里假設沒有緩存文件
        if (file_exists($cached = $app->getCachedConfigPath())) {
            $items = require $cached;

            $loadedFromCache = true;
        }
        // 綁定服務"config",服務是IlluminateConfigRepository對象
        $app->instance("config", $config = new Repository($items));

        if (! isset($loadedFromCache)) {
            // 加載config/*.php所有配置文件,把所有配置存入Repository對象中
            $this->loadConfigurationFiles($app, $config);
        }
        // 檢查"APP_ENV"環境設置,一般也就是"dev","stg","prd"三個環境,即"development", "staging", "production"
        $app->detectEnvironment(function () use ($config) {
            return $config->get("app.env", "production");
        });

        // 設置時區,$config["app.timezone"]就是調用Repository::get("app.timezone"),因為Repository實現了ArrayAccess Interface,
        // "."語法讀取是Arr::get()實現的,很好用的一個方法
        date_default_timezone_set($config["app.timezone"]);

        mb_internal_encoding("UTF-8");
    }

加載配置文件,就是讀取/config/*.php文件,看下源碼:

    protected function loadConfigurationFiles(Application $app, RepositoryContract $repository)
    {
        foreach ($this->getConfigurationFiles($app) as $key => $path) {
            // 存入到Repository對象中,以"key => value"存入到$items[]屬性中
            $repository->set($key, require $path);
        }
    }
    
    protected function getConfigurationFiles(Application $app)
    {
        $files = [];
        // 就是"config/"這個路徑
        $configPath = realpath($app->configPath());
        // Finder鏈式接口讀取config/*.php所有文件,獲取所有文件名稱,然后依次遍歷
        foreach (Finder::create()->files()->name("*.php")->in($configPath) as $file) {
            $nesting = $this->getConfigurationNesting($file, $configPath);

            $files[$nesting.basename($file->getRealPath(), ".php")] = $file->getRealPath();
        }

        return $files;
    }

可以通過Xdebug調試知道$files的返回值是這樣的數組:

    $files = [
        "app"          => "/vagrant/config/app.php", //文件的絕對路徑
        "auth"         => "vagrant/config/auth.php",
        "broadcasting" => "/vagrant/config/broadcasting.php",
        "cache"        => "/vagrant/config/cache.php",
        "compile"      => "vagrant/config/compile.php",
        "database"     => "/vagrant/config/databse.php",
        "filesystems"  => "/vagrant/config/filesystems.php",
        "mail"         => "/vagrant/config/mail.php",
        "queue"        => "/vagrant/config/queue.php",
        "services"     => "/vagrant/config/services.php",
        "session"      => "/vagrant/config/session.php",
        "view"         => "/vagrant/config/view.php",
    ];

然后通過Application的detectEnvironment()方法把app.env的值即app.phpenv的值取出來存入Application對象的$env屬性中:

    public function detectEnvironment(Closure $callback)
    {
        $args = isset($_SERVER["argv"]) ? $_SERVER["argv"] : null;

        return $this["env"] = (new EnvironmentDetector())->detect($callback, $args);
    }
    
    public function detect(Closure $callback, $consoleArgs = null)
    {
        if ($consoleArgs) {
            return $this->detectConsoleEnvironment($callback, $consoleArgs);
        }

        return $this->detectWebEnvironment($callback);
    }
    
    protected function detectWebEnvironment(Closure $callback)
    {
        return call_user_func($callback);
    }

所以屬性檢查的時候就存到了$env屬性的值了,開發代碼中就可以App::environment()得到這個$env屬性然后進行一些操作,可以看下environment()的源碼,該方法有兩個feature:如果不傳入值則讀取$env值;如果傳入值則判斷該值是否與$env一樣。這里如果對Application沒有$env成員屬性定義有疑惑,是因為PHP可以后期添加屬性,如:

class ClassField
{
    
}

$class_field = new ClassField();
$class_field->name = "Laravel";
echo $class_field->name . PHP_EOL;

/* output:
Laravel
3. 日志配置

Laravel主要利用Monolog日志庫來做日志處理,IlluminateLogWriter相當于Monolog Bridge,把Monolog庫接入到Laravel中。看下ConfigureLogging::bootstrap()源碼:

    public function bootstrap(Application $app)
    {
        // 注冊"log"服務
        $log = $this->registerLogger($app);

        // 檢查是否已經注冊了Monolog
        // 這里假設開始沒有注冊
        if ($app->hasMonologConfigurator()) {
            call_user_func(
                $app->getMonologConfigurator(), $log->getMonolog()
            );
        } else {
            // 
            $this->configureHandlers($app, $log);
        }
    }
    
    protected function registerLogger(Application $app)
    {
        // 向容器中綁定"log"服務,即Writer對象
        $app->instance("log", $log = new Writer(
            new Monolog($app->environment()), $app["events"])
        );

        return $log;
    }
    

Laravel的Log模塊中已經內置了幾個類型的LogHandler:Single,Daily,Syslog,Errorlog.根據config/app.php文件中"log"的配置選擇其中一個handler,看下configureHandlers()源碼:

    protected function configureHandlers(Application $app, Writer $log)
    {
        $method = "configure".ucfirst($app["config"]["app.log"])."Handler";

        $this->{$method}($app, $log);
    }

configureHandlers()這方法也是一個技巧,找到方法名然后調用,這在Laravel中經常這么用,如Filesystem那一模塊中有"create".ucfirst(xxx)."Driver"這樣的源碼,是個不錯的設計。這里看下configureDailyHandler()的源碼,其余三個也類似:

    protected function configureDailyHandler(Application $app, Writer $log)
    {
        // 解析"config"服務
        $config = $app->make("config");
        // 默認沒有設置,就為null
        $maxFiles = $config->get("app.log_max_files");

        $log->useDailyFiles(
            $app->storagePath()."/logs/laravel.log", // storage/log/laravel.log
            is_null($maxFiles) ? 5 : $maxFiles, // 5
            $config->get("app.log_level", "debug") 
        );
    }
    
    // Writer.php
    public function useDailyFiles($path, $days = 0, $level = "debug")
    {
        $this->monolog->pushHandler(
            $handler = new RotatingFileHandler($path, $days, $this->parseLevel($level))
        );

        $handler->setFormatter($this->getDefaultFormatter());
    }

利用Mnolog的RotatingFileHandler()來往laravel.log里打印log值,當然在應用程序中經常Log::info(),Log::warning(),Log::debug()來打印變量值,即Writer類中定義的的方法。Log的facade是IlluminateSupportFacadesLog:

class Log extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return "log";
    }
}

而"log"服務在上文中bootstrap()源碼第一步registerLogger()就注冊了。當然,至于使用Facade來從容器中獲取服務也聊過,也不復雜,看下IlluminateSupportFacadesFacade的resolveFacadeInstance()源碼就知道了:

    protected static function resolveFacadeInstance($name)
    {
        if (is_object($name)) {
            return $name;
        }

        if (isset(static::$resolvedInstance[$name])) {
            return static::$resolvedInstance[$name];
        }

        return static::$resolvedInstance[$name] = static::$app[$name]; // 實際上就是使用$app["log"]來獲取服務
    }
4. 異常處理

異常處理是十分重要的,Laravel中異常處理類AppExceptionHandler中有一個方法report(),該方法可以用來向第三方服務(如Sentry)發送程序異常堆棧(以后在一起聊聊這個Sentry,效率神器),如Production Code線上環境報出個異常,可以很清楚整個堆棧,出錯在哪一行:

OK,看下異常設置的啟動源代碼,HandleExceptions::bootstrap()的源碼:

    public function bootstrap(Application $app)
    {
        $this->app = $app;

        error_reporting(-1);
        // 出現錯誤,拋出throw new ErrorException
        set_error_handler([$this, "handleError"]);
        // 處理異常,使用report()方法來報告,可集成第三方服務Sentry來作為異常報告處理器ExceptionReportHandler
        set_exception_handler([$this, "handleException"]);

        register_shutdown_function([$this, "handleShutdown"]);

        if (! $app->environment("testing")) {
            ini_set("display_errors", "Off");
        }
    }

這里重點看下handleException()的源碼:

    public function handleException($e)
    {
        if (! $e instanceof Exception) {
            $e = new FatalThrowableError($e);
        }
        // (new AppExceptionsHandler($container))->report($e)
        $this->getExceptionHandler()->report($e);

        if ($this->app->runningInConsole()) {
            $this->renderForConsole($e);
        } else {
            $this->renderHttpResponse($e);
        }
    }
    
    protected function getExceptionHandler()
    {
        // 解析出AppExceptionsHandler對象
        // 在boostrap/app.php中做過singleton()綁定
        return $this->app->make("IlluminateContractsDebugExceptionHandler");
    }
    
    protected function renderHttpResponse(Exception $e)
    {
        // 使用(new AppExceptionsHandler($container))->render(Request $request, $e)
        $this->getExceptionHandler()->render($this->app["request"], $e)->send();
    }

從源碼中知道,重點是使用AppExceptionsHandler的report()方法報告異常情況,如向Sentry報告異常堆棧和其他有用信息;AppExceptionsHandler的render()方法通過Request發送到瀏覽器。關于使用第三方服務Sentry來做異常報告以后詳聊,我司每天都在用這樣的效率神器,很好用,值得推薦下。

5. 注冊Facades

在路由文件中經常會出現Route::get()這樣的寫法,但實際上并沒有Route類,Route只是IlluminateSupportFacadesRoute::class外觀類的別名,這樣取個別名只是為了簡化作用,使用的是PHP內置函數class_alias(string $class, string $alias)來給類設置別名。看下RegisterFacades::bootstrap()的源碼:

    public function bootstrap(Application $app)
    {
        Facade::clearResolvedInstances();

        Facade::setFacadeApplication($app);

        AliasLoader::getInstance($app->make("config")->get("app.aliases", []))->register();
    }
    
    // IlluminateSupportFacadesFacade
    public static function clearResolvedInstances()
    {
        static::$resolvedInstance = [];
    }
    
    // IlluminateSupportFacadesFacade
    public static function setFacadeApplication($app)
    {
        static::$app = $app;
    }

$app->make("config")->get("app.aliases", [])是從config/app.php中讀取"aliases"的值,然后注冊外觀類的別名,注冊的外觀類有:

    "aliases" => [

        "App" => IlluminateSupportFacadesApp::class,
        "Artisan" => IlluminateSupportFacadesArtisan::class,
        "Auth" => IlluminateSupportFacadesAuth::class,
        "Blade" => IlluminateSupportFacadesBlade::class,
        "Cache" => IlluminateSupportFacadesCache::class,
        "Config" => IlluminateSupportFacadesConfig::class,
        "Cookie" => IlluminateSupportFacadesCookie::class,
        "Crypt" => IlluminateSupportFacadesCrypt::class,
        "DB" => IlluminateSupportFacadesDB::class,
        "Eloquent" => IlluminateDatabaseEloquentModel::class,
        "Event" => IlluminateSupportFacadesEvent::class,
        "File" => IlluminateSupportFacadesFile::class,
        "Gate" => IlluminateSupportFacadesGate::class,
        "Hash" => IlluminateSupportFacadesHash::class,
        "Lang" => IlluminateSupportFacadesLang::class,
        "Log" => IlluminateSupportFacadesLog::class,
        "Mail" => IlluminateSupportFacadesMail::class,
        "Notification" => IlluminateSupportFacadesNotification::class,
        "Password" => IlluminateSupportFacadesPassword::class,
        "Queue" => IlluminateSupportFacadesQueue::class,
        "Redirect" => IlluminateSupportFacadesRedirect::class,
        "Redis" => IlluminateSupportFacadesRedis::class,
        "Request" => IlluminateSupportFacadesRequest::class,
        "Response" => IlluminateSupportFacadesResponse::class,
        "Route" => IlluminateSupportFacadesRoute::class,
        "Schema" => IlluminateSupportFacadesSchema::class,
        "Session" => IlluminateSupportFacadesSession::class,
        "Storage" => IlluminateSupportFacadesStorage::class,
        "URL" => IlluminateSupportFacadesURL::class,
        "Validator" => IlluminateSupportFacadesValidator::class,
        "View" => IlluminateSupportFacadesView::class,

    ],

從以上外觀別名數組中知道RouteIlluminateSupportFacadesRoute::class的別名,所以Route::get()實際上就是IlluminateSupportFacadesRoute::get(),看下AliasLoader類的getInstance()和register()方法源碼:

    public static function getInstance(array $aliases = [])
    {
        if (is_null(static::$instance)) {
            // 這里$aliases就是上面傳進來的$aliases[],即config/app.php中"aliases"值
            return static::$instance = new static($aliases);
        }

        $aliases = array_merge(static::$instance->getAliases(), $aliases);

        static::$instance->setAliases($aliases);

        return static::$instance;
    }
    
    public function register()
    {
        if (! $this->registered) {
            $this->prependToLoaderStack();

            $this->registered = true;
        }
    }
    
    protected function prependToLoaderStack()
    {
        // 把AliasLoader::load()放入自動加載函數堆棧中,堆棧首的位置
        spl_autoload_register([$this, "load"], true, true);
    }

而loader()函數的源碼:

    public function load($alias)
    {
        if (isset($this->aliases[$alias])) {
            // @link http://php.net/manual/en/function.class-alias.php
            return class_alias($this->aliases[$alias], $alias);
        }
    }

就是通過class_alias()給外觀類設置一個別名。所以Route::get()的調用過程就是,首先發現沒有Route類,就去自動加載函數堆棧中通過AliasLoader::load()函數查找到RouteIlluminateSupportFacadesRoute的別名,那就調用IlluminateSupportFacadesRoute::get(),當然這里IlluminateSupportFacadesRoute沒有get()靜態方法,那就調用父類Facade__callStatic()來找到名為router的服務,名為"router"的服務那就是早就注冊到容器中的IlluminateRoutingRouter對象,所以最終就是調用IlluminateRoutingRouter::get()方法。這個過程主要使用了兩個技術:一個是外觀類的別名;一個是PHP的重載,可看這篇:Laravel學習筆記之PHP重載(overloading)。

6. 注冊Providers

外觀注冊是注冊config/app.php中的$aliases[ ]得值,Providers注冊就是注冊$providers[ ]的值。看下RegisterProviders::bootstrap()的源碼:

    public function bootstrap(Application $app)
    {
        $app->registerConfiguredProviders();
    }
    
    // Application.php
    public function registerConfiguredProviders()
    {
        // 查找bootstrap/cache/services.php有沒有這個緩存文件
        // services.php這個緩存文件存儲的是service providers的數組值:
        // return [
        //    "providers" => [],
        //    "eager"     => [],
        //    "deferred"  => [],
        //    "when"      => []
        // ];
        $manifestPath = $this->getCachedServicesPath();
        
        // 通過load()方法加載config/app.php中"$providers[ ]"數組值
        (new ProviderRepository($this, new Filesystem, $manifestPath))
                    ->load($this->config["app.providers"]);
    }

看下load()的源碼:

    public function load(array $providers)
    {
        // 查看bootstrap/cache/services.php有沒有這個緩存文件
        // 第一次啟動時是沒有的
        $manifest = $this->loadManifest();
        // 開始沒有這個緩存文件,那就把$providers[ ]里的值
        if ($this->shouldRecompile($manifest, $providers)) {
            // 然后根據$providers[ ]編譯出services.php這個緩存文件
            $manifest = $this->compileManifest($providers);
        }

        foreach ($manifest["when"] as $provider => $events) {
            // 注冊包含有事件監聽的service provider
            // 包含有事件監聽的service provider都要有when()函數返回
            $this->registerLoadEvents($provider, $events);
        }

        foreach ($manifest["eager"] as $provider) {
            // 把"eager"字段中service provider注冊進容器中,
            // 即遍歷每一個service provider,調用其中的register()方法
            // 向容器中注冊具體的服務
            $this->app->register($this->createProvider($provider));
        }

        // 注冊延遲的service provider,
        // deferred的service provider, 一是要設置$defer = true,二是要提供provides()方法返回綁定到容器中服務的名稱
        $this->app->addDeferredServices($manifest["deferred"]);
    }

看下編譯緩存文件compileManifest()方法的源碼:

    protected function compileManifest($providers)
    {
        $manifest = $this->freshManifest($providers);

        foreach ($providers as $provider) {
            $instance = $this->createProvider($provider);
            // 根據每一個service provider的defer屬性看是否是延遲加載的service provider
            if ($instance->isDeferred()) {
                // 延遲加載的,根據provides()方法提供的服務名稱,寫入到"deferred"字段里
                // 所以延遲加載的service provider都要提供provides()方法
                foreach ($instance->provides() as $service) {
                    $manifest["deferred"][$service] = $provider;
                }
                // 使用when()函數提供的值注冊下含有事件的service provider,
                $manifest["when"][$provider] = $instance->when();
            } else {
                // 不是延遲加載的,就放在"eager"字段里,用$this->app->register()來注冊延遲加載的service provider
                $manifest["eager"][] = $provider;
            }
        }
        // 最后寫入到services.php緩存文件中
        return $this->writeManifest($manifest);
    }
    
    protected function freshManifest(array $providers)
    {
        return ["providers" => $providers, "eager" => [], "deferred" => []];
    }

總之,注冊providers就是把config/app.php中$providers[ ]定義的所有service provider中,把不是defer的service provider中綁定的服務啟動起來,是defer的service provider等到需要里面綁定的服務時再執行綁定。

7. 啟動Providers

最后一步,就是啟動程序了,看下BootProviders::bootstrap()源碼:

    public function bootstrap(Application $app)
    {
        $app->boot();
    }
    
    public function boot()
    {
        // 如果程序已啟動則返回,顯然還沒啟動,還在booting狀態中
        if ($this->booted) {
            return;
        }
        // 執行之前Application實例化的時候在$bootingCallbacks[]注冊的回調
        $this->fireAppCallbacks($this->bootingCallbacks);
        // 之前凡是用Application::register()方法的service provider都寫入到了$serviceProviders[]中
        // 這里依次執行每一個service provider里的boot()方法,如果存在的話
        array_walk($this->serviceProviders, function ($p) {
            $this->bootProvider($p);
        });

        $this->booted = true;
        // 執行之前Application實例化的時候在$bootedCallbacks[]注冊的回調
        $this->fireAppCallbacks($this->bootedCallbacks);
    }
    
    protected function bootProvider(ServiceProvider $provider)
    {
        if (method_exists($provider, "boot")) {
            return $this->call([$provider, "boot"]);
        }
    }

從以上源碼中知道,第(7)步和第(6)步類似:第(6)是依次執行每一個不是defer的service provider的register()方法;第(7)步是依次執行每一個不是defer的service provider的boot()方法,如果存在的話。所以官網上service provider章節說了這么一句The Boot Method:

This method is called after all other service providers have been registered, meaning you have access to all other services that have been registered by the framework

這里就明白了為啥這句話的含義了。

之前聊過Application::register()方法時里面有個檢測程序是否已經啟動的代碼:

public function register($provider, $options = [], $force = false)
    {
        ...
        
        if ($this->booted) {
            $this->bootProvider($provider);
        }

        return $provider;
    }

剛剛開始實例化Application的時候還沒有啟動,在執行所有非defer的service provider boot()方法后程序就啟動了:$this->booted = true;

OK, 程序啟動所做的準備工作就聊完了,過程不復雜,只需一步步拆解就能基本清楚Laravel啟動時做了哪些具體工作。

總結:本文主要學習了Laravel啟動時做的七步準備工作:1. 環境檢測 DetectEnvironment; 2. 配置加載 LoadConfiguratio; 3. 日志配置 ConfigureLogging; 4. 異常處理 HandleException;5. 注冊Facades RegisterFacades;6. 注冊Providers RegisterProviders;7. 啟動Providers BootProviders。下次有好的技術再分享,到時見。

歡迎關注Laravel-China。

RightCapital招聘Laravel DevOps

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/30480.html

相關文章

  • Laravel學習筆記Middleware源碼解析

    摘要:學習筆記之已經聊過使用了來設計,看源碼發現其巧妙用了和的一些數組函數來設計。開發環境內置函數和看源碼之前,先看下這幾個內置函數的使用。學習筆記之實例化源碼解析已經聊過的實例化,得到中的變量,即的實例化對象。后面再學習下的源碼,到時見。 說明:本文主要學習Laravel的Middleware的源碼設計思想,并將學習心得分享出來,希望對別人有所幫助。Laravel學習筆記之Decorato...

    _Dreams 評論0 收藏0
  • Laravel學習筆記IoC Container實例化源碼解析

    摘要:說明本文主要學習容器的實例化過程,主要包括等四個過程。看下的源碼如果是數組,抽取別名并且注冊到中,上文已經討論實際上就是的。 說明:本文主要學習Laravel容器的實例化過程,主要包括Register Base Bindings, Register Base Service Providers , Register Core Container Aliases and Set the ...

    ningwang 評論0 收藏0
  • Laravel學習筆記Errors Tracking神器——Sentry

    摘要:中異常處理類主要包含兩個方法和,其中就是主要用來向第三方發送異常報告,這里選擇向這個神器發送異常報告,并使用通知開發人員。通過也能發現的執行流程。 說明:Laravel學習筆記之bootstrap源碼解析中聊異常處理時提到過Sentry這個神器,并打算以后聊聊這款神器,本文主要就介紹這款Errors Tracking神器Sentry,Sentry官網有一句話個人覺得帥呆了: Stop ...

    xiguadada 評論0 收藏0
  • Laravel學習筆記Container源碼解析

    摘要:實際上的綁定主要有三種方式且只是一種的,這些已經在學習筆記之實例化源碼解析聊過,其實現方法并不復雜。從以上源碼發現的反射是個很好用的技術,這里給出個,看下能干些啥打印結果太長了,就不粘貼了。 說明:本文主要學習Laravel中Container的源碼,主要學習Container的綁定和解析過程,和解析過程中的依賴解決。分享自己的研究心得,希望對別人有所幫助。實際上Container的綁...

    huayeluoliuhen 評論0 收藏0
  • Laravel學習筆記Session源碼解析(中)

    摘要:說明在上篇中學習了的啟動過程,主要分為兩步,一是的實例化,即的實例化二是從存儲介質中讀取的數據。第二步就是操作,包括對數據的增刪改查操作,本文也主要聊下相關操作源碼。下篇再學習下關閉,到時見。 說明:在上篇中學習了session的啟動過程,主要分為兩步,一是session的實例化,即IlluminateSessionStore的實例化;二是從session存儲介質redis中讀取id ...

    longshengwang 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<