本文通过阅读分析Symfony2的源码,了解Symfony2启动过程中完成哪些工作,从阅读源码了解Symfony2框架。
Symfony2的核心本质是把Request转换成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等等)构造Request对象 6 $request = Request::createFromGlobals(); 7 //Symfony2框架核心工作就是把Request对象转换成Response对象 8 $response = $kernel->handle($request); 9 //向客户端输出Response对象 10 $response->send(); 11 //完成一些耗时的后台操作,例如邮件发送,图片裁剪等等耗时工作 12 $kernel->terminate($request, $response);
Symfony2框架通过客户端的请求信息来决定生成并返回响应的数据,我们下面的Symfony2源码分析重点就是AppKernel::handle方法。
AppKernel::handle的实现继承于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 //初始化container 16 //加载、缓存配置数据和路由数据、编译container容器等,为后面事件处理做准备。 17 $this->boot(); 18 } 19 20 //开启事件处理,Symfony2内核的请求处理过程本质是一系列的事件处理过程 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 bundles 12 //初始化注册到AppKernel里的所有bundle(AppKernel::registerBundles) 13 $this->initializeBundles(); 14 15 // init container 16 //初始化并编译缓存container,包括载入配置信息、编译信息、service等 17 //Symfony2的核心组件的加载,和各个组件之间的关联关系都在container容器初始化中完成,所以这会是下面详细描述 18 $this->initializeContainer(); 19 20 //把bundle注入到container,并启动bundle 21 foreach ($this->getBundles() as $bundle) { 22 $bundle->setContainer($this->container); 23 $bundle->boot(); 24 } 25 26 //标记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(即关闭debug的情况下)只要container缓存文件存在,那么就认为 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做准备,我们可以通过compiler pass修改已经注册到container的service的属性 17 //compiler pass的官方文档http://symfony.com/doc/current/cookbook/service_container/compiler_passes.html 18 $container = $this->buildContainer(); 19 //执行compiler pass 的process方法,container的compile过程主要是执行上一步保存到container内的compiler pass 的process方法 20 $container->compile(); 21 //生成container的缓存(appDevDebugProjectContainer.php),该container包含了service的获取方法、别名的映射关系 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必需实现Extension接口 6 if ($extension = $bundle->getContainerExtension()) { 7 $container->registerExtension($extension); 8 $extensions[] = $extension->getAlias(); 9 } 10 11 //开启debug的情况下,把bundles添加到recourses 12 if ($this->debug) { 13 $container->addObjectResource($bundle); 14 } 15 } 16 foreach ($this->bundles as $bundle) { 17 //通常用来添加compiler pass 18 $bundle->build($container); 19 } 20 21 // ensure these extensions are implicitly loaded 22 $container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass($extensions)); 23 }
从AppKernel::initializeContainer可以看出Bundle和container是Symfony2框架的基础核心,container是Symfony2框架的所有组件的统一管理中心,Bundle就是一个功能模块的组织。
如果你好奇service、配置参数是怎样被加载的,可以详细去了解Symfony2的Extension;如果你好奇怎么对已经加载了的service进一步完善和修改,可有详细了解Symfony2的compiler pass。
到了这一步,Symfony2框架启动几乎完成,为后面的内核事件处理EventDispatcher::dispatch做好了准备。
下一篇讲解Symfony2框架的内核事件处理。