本文通過閱讀分析Symfony2的源碼,了解Symfony2啟動過程中完成哪些工作,從閱讀源碼了解Symfony2框架。
Symfony2的核心本質(zhì)是把Request轉(zhuǎn)換成Response的一個過程。
我們大概看看入口文件(web_dev.php)的源碼,入口文件從總體上描述了Symfony2框架的工作的流程:
 1 require_once __DIR__.'/../app/AppKernel.php'; 2  3 $kernel = new AppKernel('dev', true); 4 $kernel->loadClassCache(); 5 //利用請求信息($_GET $_POST $_SERVER等等)構(gòu)造Request對象 6 $request = Request::createFromGlobals(); 7 //Symfony2框架核心工作就是把Request對象轉(zhuǎn)換成Response對象 8 $response = $kernel->handle($request); 9 //向客戶端輸出Response對象10 $response->send();11 //完成一些耗時的后臺操作,例如郵件發(fā)送,圖片裁剪等等耗時工作12 $kernel->terminate($request, $response);Symfony2框架通過客戶端的請求信息來決定生成并返回響應(yīng)的數(shù)據(jù),我們下面的Symfony2源碼分析重點就是AppKernel::handle方法。
AppKernel::handle的實現(xiàn)繼承于Kernel::handle
 1     /** 2      * 3      * @param Request $request Request對象實例 4      * @param int     $type    請求的類型(子請求 or 主請求) 5      * @param bool    $catch   是否捕捉異常 6      * 7      * @return Response Response對象實例 8      * 9      */    10     public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)11     {   12         //$this->booted Symfony2框架只啟動一次13         if (false === $this->booted) {14             //初始化并啟動所有注冊在AppKernel里面的所有bundles(AppKernel::registerBundles)15             //初始化container16             //加載、緩存配置數(shù)據(jù)和路由數(shù)據(jù)、編譯container容器等,為后面事件處理做準(zhǔn)備。17             $this->boot();18         }19 20         //開啟事件處理,Symfony2內(nèi)核的請求處理過程本質(zhì)是一系列的事件處理過程21         return $this->getHttpKernel()->handle($request, $type, $catch);22     }AppKernel::boot方法
 1     public function boot() 2     { 3         if (true === $this->booted) { 4             return; 5         } 6  7         if ($this->loadClassCache) { 8             $this->doLoadClassCache($this->loadClassCache[0], $this->loadClassCache[1]); 9         }10 11         // init bundles12         //初始化注冊到AppKernel里的所有bundle(AppKernel::registerBundles)13         $this->initializeBundles();14 15         // init container16         //初始化并編譯緩存container,包括載入配置信息、編譯信息、service等17         //Symfony2的核心組件的加載,和各個組件之間的關(guān)聯(lián)關(guān)系都在container容器初始化中完成,所以這會是下面詳細描述18         $this->initializeContainer();19 20         //把bundle注入到container,并啟動bundle21         foreach ($this->getBundles() as $bundle) {22             $bundle->setContainer($this->container);23             $bundle->boot();24         }25 26         //標(biāo)記Symfony2只啟動一次并啟動成功27         $this->booted = true;28     }AppKernel::initializeContainer源碼解析
1 PRotected function initializeContainer() 2 { 3 //檢查app/cache/dev[prod]緩存文件是否過期,以container緩存文件的最后修改時間為參考時間, 4 //如果app/cache/dev[prod]下的存在一個或者多個緩存文件的最后修改時間大于container緩存文件的 5 //最后修改時間,就判斷為緩存過期。 6 //另外,如果$this->debug為false(即關(guān)閉debug的情況下)只要container緩存文件存在,那么就認(rèn)為 7 //緩存不過期 8 $class = $this->getContainerClass(); 9 $cache = new ConfigCache($this->getCacheDir().'/'.$class.'.php', $this->debug);10 $fresh = true;11 if (!$cache->isFresh()) {12 //初始化一個ContainerBuilder對象實例;13 //自動加載所有注冊的Bundle的DependencyInjection下的所有extension,Bundle可以通過extension來加載屬于該Bundle配置(service的配置、14 //路由的配置等等)、Bundle的全局變量等15 //同時這些extension加載的信息都會被保存到container中;16 //加載并保存compiler pass到container,為下一步compile做準(zhǔn)備,我們可以通過compiler pass修改已經(jīng)注冊到container的service的屬性17 //compiler pass的官方文檔http://symfony.com/doc/current/cookbook/service_container/compiler_passes.html18 $container = $this->buildContainer();19 //執(zhí)行compiler pass 的process方法,container的compile過程主要是執(zhí)行上一步保存到container內(nèi)的compiler pass 的process方法20 $container->compile();21 //生成container的緩存(appDevDebugProjectContainer.php),該container包含了service的獲取方法、別名的映射關(guān)系22 $this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass());23 24 $fresh = false;25 }26 27 28 require_once $cache;29 30 $this->container = new $class();31 $this->container->set('kernel', $this);32 33 //...............34 if (!$fresh && $this->container->has('cache_warmer')) {35 $this->container->get('cache_warmer')->warmUp($this->container->getParameter('kernel.cache_dir'));36 }37 }
 1     protected function prepareContainer(ContainerBuilder $container) 2     { 3         $extensions = array(); 4         foreach ($this->bundles as $bundle) { 5             //加載DependencyInjection下的Extension,所有Extension必需實現(xiàn)Extension接口 6             if ($extension = $bundle->getContainerExtension()) { 7                 $container->registerExtension($extension); 8                 $extensions[] = $extension->getAlias(); 9             }10 11             //開啟debug的情況下,把bundles添加到recourses12             if ($this->debug) {13                 $container->addObjectResource($bundle);14             }15         }16         foreach ($this->bundles as $bundle) {17             //通常用來添加compiler pass18             $bundle->build($container);19         }20 21         // ensure these extensions are implicitly loaded22         $container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass($extensions));23     }從AppKernel::initializeContainer可以看出Bundle和container是Symfony2框架的基礎(chǔ)核心,container是Symfony2框架的所有組件的統(tǒng)一管理中心,Bundle就是一個功能模塊的組織。
如果你好奇service、配置參數(shù)是怎樣被加載的,可以詳細去了解Symfony2的Extension;如果你好奇怎么對已經(jīng)加載了的service進一步完善和修改,可有詳細了解Symfony2的compiler pass。
到了這一步,Symfony2框架啟動幾乎完成,為后面的內(nèi)核事件處理EventDispatcher::dispatch做好了準(zhǔn)備。
下一篇講解Symfony2框架的內(nèi)核事件處理。
新聞熱點
疑難解答