未加星标

symfony源码分析之容器的生成与使用

字体大小 | |
[开发(php) 所属分类 开发(php) | 发布者 店小二03 | 时间 2018 | 作者 红领巾 ] 0人收藏点击收藏
symfony 的容器是有一个编译过程的,框架初始化的时候会执行Symfony\Component\HttpKernel\Kernel::initializationContainer,这个方法会对代码进行检查,看是否需要生成新的容器代码。如果需要 Symfony 会将各个类的依赖关系通过编译生成静态的类并存储在缓存文件中var/cache/[ENV]/appProjectContainer。

其中很多是框架自己的依赖关系,这些依赖关系类似java的方式,通过xml文件的形式进行声明,这些xml存在与框架的代码中,Syfmony\Bundle\FrameworkBundle\Resource\config\*.xml。

0. 代码流程

容器相关的代码大致如下流程

# Symfony\Component\HttpKernel\Kernel protected function initializeContainer() { // 获取生成的容器代码文件 类名 $class = $this->getContainerClass(); $cache = new ConfigCache($this->getCacheDir().'/'.$class.'.php', $this->debug); $fresh = true; // 检查是否需要重新生成 容器 缓存类 if (!$cache->isFresh()) { // ..... try { $container = null; // 重新生成容器缓存类 // 实例化生成容器缓存代码的类 $container = $this->buildContainer(); // 调用方法开始生成容器代码 $container->compile(); } finally { // ...... } // 将生成的代码写入文件 $this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass()); $fresh = false; } //加载生成的容器文件 实例化容器类 并且复制给 Kernel::$container require_once $cache->getPath(); $this->container = new $class(); $this->container->set('kernel', $this); // ...... }

下面着重分析 容器代码生成类的创建过程

protected function buildContainer() { // 创建容器缓存代码的目录 $container = $this->getContainerBuilder(); $container->addObjectResource($this); // 容器预处理,在开始编译容器前,会给容器添加一些 pass (这个问题就是在201phpcon大会上提过的,当时没解决,这里看代码解决了) // 这些pass主要处理框架配置中声明的容器关系 $this->prepareContainer($container); // 解析容器配置文件 我们自己注入容器的类就要声明在配置文件 app/config/services.yml 中 // 这里就是对这个配置文件进行解析,并将其中的关系生成代码到容器中 if (null !== $cont = $this->registerContainerConfiguration($this->getContainerLoader($container))) { $container->merge($cont); } $container->addCompilerPass(new AddAnnotatedClassesToCachePass($this)); $container->addResource(new EnvParametersResource('SYMFONY__')); return $container; }

如上的分析可以将容器代码生成的过程精简到如下3个步骤

# Symfony\Component\HttpKernel\Kernel protected function initializeContainer() { // ... try { $container = null; $container = $this->buildContainer(); 3. 编译生成容器代码 $container->compile(); } finally { // ...... } // ...... } protected function buildContainer() { // 1. 实例化创建容器代码类的时候,预处理一些内容 $this->prepareContainer($container); // 2. 加载项目容器的配置文件进行处理 app/config/services.yml if (null !== $cont = $this->registerContainerConfiguration($this->getContainerLoader($container))) { $container->merge($cont); } } 1. 容器预处理 # Symfony\Component\HttpKernel\Kernel protected function prepareContainer(ContainerBuilder $container) { $extensions = array(); // 这里的 bundles 是在 initializeBundles 方法中实例化AppKernel中注册的Bundles 进行的赋值 foreach ($this->bundles as $bundle) { // 这里将注册的Bundle进行循环处理 // 取出bundle的extension 并 注册到 container中 if ($extension = $bundle->getContainerExtension()) { $container->registerExtension($extension); $extensions[] = $extension->getAlias(); } if ($this->debug) { $container->addObjectResource($bundle); } } foreach ($this->bundles as $bundle) { // 触发bundle的build方法 $bundle->build($container); } $this->build($container); // 这里的代码是为了确定将 MergeExtensionConfigurationPass 这个Pass类添加到容器的Pass中 $container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass($extensions)); }

我们可以在AppKernel::registerBundles 中查看到注册的 Bundles

public function registerBundles() { $bundles = [ new Symfony\Bundle\FrameworkBundle\FrameworkBundle(), new Symfony\Bundle\SecurityBundle\SecurityBundle(), new Symfony\Bundle\TwigBundle\TwigBundle(), new Symfony\Bundle\MonologBundle\MonologBundle(), new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(), new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(), new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(), new AppBundle\AppBundle(), ]; return $bundles; }

我们以Symfony\Bundle\FrameworkBundle\FrameworkBundle 作为目标进行代码的继续跟踪

$extension = $bundle->getContainerExtension(); $container->registerExtension($extension);

跟踪方法 getContainerExtension , 在类 FrameworkBundle 所继承的类 Syfmony\Component\HttpKernel\Bundle\Bundle 中找到

# Syfmony\Component\HttpKernel\Bundle\Bundle public function getContainerExtension() { if (null === $this->extension) { // 获取extension $extension = $this->createContainerExtension(); if (null !== $extension) { // check naming convention $basename = preg_replace('/Bundle$/', '', $this->getName()); $expectedAlias = Container::underscore($basename); if ($expectedAlias != $extension->getAlias()) { throw new \LogicException(......); } $this->extension = $extension; } else { $this->extension = false; } } if ($this->extension) { return $this->extension; } }

以下是获取extension的代码逻辑

protected function createContainerExtension() { // 这里实例化一个类并返回 if (class_exists($class = $this->getContainerExtensionClass())) { return new $class(); } } protected function getContainerExtensionClass() { // 类名的规则 // 命名空间\DependencyInjection\(将类名的Bundle替换成Extension) $basename = preg_replace('/Bundle$/', '', $this->getName()); return $this->getNamespace().'\\DependencyInjection\\'.$basename.'Extension'; } # 以下两个方法主要还是当 $this->namespace $this->name 不存在的时候 # 通过方法 $this->parseClassName() 来解析出来 public function getNamespace() { if (null === $this->namespace) { $this->parseClassName(); } return $this->namespace; } final public function getName() { if (null === $this->name) { $this->parseClassName(); } return $this->name; } private function parseClassName() { # 命名空间 类型的截取 $pos = strrpos(static::class, '\\'); // 截取类的命名空间 $this->namespace = false === $pos ? '' : substr(static::class, 0, $pos); if (null === $this->name) { // 截取类的名称 $this->name = false === $pos ? static::class : substr(static::class, $pos + 1); } }

上代码解析出来的就是 Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension 。回到代码中

if ($extension = $bundle->getContainerExtension()) { $container->registerExtension($extension); $extensions[] = $extension->getAlias(); }

要执行容器的 registerExtension 方法

# Symfony\Component\DependencyInjection\ContainerBuilder public function registerExtension(ExtensionInterface $extension) { // $extension->getAlias() 这个方法在 Symfony\Component\DependencyInjection\Extension\Extension 中 // 主要的作用是将类名取出 将类名的Excention后缀去掉 $this->extensions[$extension->getAlias()] = $extension; if (false !== $extension->getNamespace()) { $this->extensionsByNs[$extension->getNamespace()] = $extension; } } 2. 解析容器配置文件

解析容器配置文件的代码

if (null !== $cont = $this->registerContainerConfiguration($this->getContainerLoader($container))) { $container->merge($cont); } 主要是执行了方法 registerContainerConfiguration 这个方法在 AppKernel 类中

本文开发(php)相关术语:php代码审计工具 php开发工程师 移动开发者大会 移动互联网开发 web开发工程师 软件开发流程 软件开发工程师

代码区博客精选文章
分页:12
转载请注明
本文标题:symfony源码分析之容器的生成与使用
本站链接:https://www.codesec.net/view/620992.html


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 开发(php) | 评论(0) | 阅读(54)