da / di-bundle
允许向依赖注入添加参数的包
Requires
- php: >=5.3.3
- symfony/config: >=2.1,<2.4
- symfony/dependency-injection: >=2.1,<2.4
Requires (Dev)
- phpunit/phpunit: 3.7.*
This package is auto-updated.
Last update: 2024-09-13 22:43:38 UTC
README
DaDiBundle 是一个允许使用高级依赖注入特性的 Symfony2 包。您可以在服务的配置中使用一些新参数(例如,例如 interface
),并实现自己的。
安装
将以下行添加到 composer.json 文件的 require 部分
"da/di-bundle": "dev-master"
运行 composer update 命令
composer update --dev
将以下行添加到您的 AppKernel.php 文件中
new Da\DiBundle\DaDiBundle(),
现在您应该能够使用 DaDiBundle。为了利用此包的功能,您需要在依赖注入中使用新的 FileLoader
// Me/MyBundle/DependencyInjection/MeMyBundleExtension.php namespace Me\MyBundle\DependencyInjection; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Da\DiBundle\DependencyInjection\Loader\YamlFileLoader; class MeMyBundleExtension extends Extension { public function load(array $configs, ContainerBuilder $container) { $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); $loader = YamlFileLoader::decorate($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('services.yml'); // ... } }
使用新参数
此包添加了一些新参数以帮助您,并展示了如何增强您对服务和依赖注入的体验。
接口
参数 interface
允许您检查服务是否实现了接口
parameters: router.class: Me\MyRoutingBundle\Router services: me-routing.router: interface: Me\MyRoutingBundle\RouterInterface class: %router.class%
工厂
参数 factory
允许您组织具有相同责任的服务
services: me-file.parser: interface: Me\FileBundle\Parser\ParserInterface factory: yaml: class: Me\FileBundle\Parser\YamlParser xml: class: Me\FileBundle\Parser\XmlParser php: interface: Me\FileBundle\Parser\CodeParserInterface class: Me\FileBundle\Parser\PhpParser
然后您可以通过 id me-file.parser.yaml
等实例访问 yaml 文件服务解析器。服务 me-file.parser
是一个工厂,您可以在控制器中使用它
$yamlParser = $this->container->get('me-file.parser')->get('yaml');
注意:所有工厂参数都应用于其生成的服务。在此示例中,服务
me-file.parser.yaml
和me-file.parser.xml
必须实现me-file.parser
层定义的Me\FileBundle\Parser\ParserInterface
接口。服务me-file.parser.php
覆盖了interface
参数,并必须实现Me\FileBundle\Parser\CodeParserInterface
接口。
构建器
参数 builder
仅是帮助区分 factory
参数和原生 factory_class
、factory_method
、factory_service
的语法糖
services: me-file.lexer: builder: method: build service: me-file.lexer.builder me-file.lexer.builder: class: Me\FileBundle\Lexer\Builder
等同于
services: me-file.lexer: builder: me-file.lexer.builder # build is the default name of the method. me-file.lexer.builder: class: Me\FileBundle\Lexer\Builder
等同于
services: me-file.lexer: factory_method: build factory_service: me-file.lexer.builder me-file.lexer.builder: class: Me\FileBundle\Lexer\Builder
实现您自己的参数
此包允许您以结构化方式定义自己的参数。让我们看看参数 interface
的示例。
创建一个额外的定义
// Da/DiBundle/DependencyInjection/Definition/InterfaceExtraDefinition.php namespace Da\DiBundle\DependencyInjection\Definition; class InterfaceExtraDefinition implements ExtraDefinitionInterface { private $name; public function setName($name) { $this->name = $name; } public function getName() { return $this->name; } }
此额外定义包含您参数所需的信息。
向 yaml 文件加载器添加装饰器
// Da/DiBundle/DependencyInjection/Loader/InterfaceYamlFileLoaderDecorator.php namespace Da\DiBundle\DependencyInjection\Loader; use Symfony\Component\DependencyInjection\Definition; use Da\DiBundle\DependencyInjection\Definition\InterfaceExtraDefinition; class InterfaceYamlFileLoaderDecorator extends AbstractYamlFileLoaderDecorator { public function parseExtraDefinition($id, $service, $file, Definition $definition) { $def = $definition; if (isset($service['interface'])) { // Parse the extra definition. $interfaceExtra = new InterfaceExtraDefinition(); $interfaceExtra->setName($service['interface']); // Add the extra definition to the definition. $def = $this->getDecoratedInstance()->getDefinitionExtra($definition); $def->setExtra('interface', $interfaceExtra); } return $this->getParent()->parseExtraDefinition($id, $service, $file, $def); } }
此装饰器(请参阅设计模式)考虑参数 interface
并将其添加到服务的定义中。您必须在您的包中声明它
// Da/DiBundle/DependencyInjection/Compiler/CheckInterfaceValidityPass.php namespace Da\DiBundle; use Symfony\Component\HttpKernel\Bundle\Bundle; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Da\DiBundle\DependencyInjection\Loader\YamlFileLoader; class DaDiBundle extends Bundle { public function build(ContainerBuilder $container) { parent::build($container); // ... YamlFileLoader::addDecorator('Da\DiBundle\DependencyInjection\Loader\InterfaceYamlFileLoaderDecorator'); } }
添加编译器传递
// Da/DiBundle/DependencyInjection/Compiler/CheckInterfaceValidityPass.php namespace Da\DiBundle\DependencyInjection\Compiler; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Da\DiBundle\DependencyInjection\Definition\DefinitionExtraInterface; class CheckInterfaceValidityPass implements CompilerPassInterface { private $container; public function process(ContainerBuilder $container) { $this->container = $container; foreach (array_keys($container->getDefinitions()) as $id) { // yes, we are specifically fetching the definition from the // container to ensure we are not operating on stale data $definition = $container->getDefinition($id); if (!$definition instanceof DefinitionExtraInterface || !$definition->getExtra('interface')) continue; $this->checkDefinition($id, $definition); } } private function checkDefinition($id, DefinitionExtraInterface $definition) { $interfaceName = $definition->getExtra('interface')->getName(); $className = $definition->getClass(); $class = new \ReflectionClass($className); if (!interface_exists($interfaceName)) throw new InvalidArgumentException('Interface "'.$interfaceName.'" not found.'); if (!$class->implementsInterface($interfaceName)) throw new RuntimeException('The class "'.$className.'" of the service "'.$id.'" should implement the interface "'.$interfaceName.'".'); } }
此编译器传递检查您的服务是否实现了定义的接口。您必须在您的包中声明它
// Da/DiBundle/DaDiBundle.php namespace Da\DiBundle; use Symfony\Component\HttpKernel\Bundle\Bundle; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Da\DiBundle\DependencyInjection\Loader\YamlFileLoader; use Da\DiBundle\DependencyInjection\Compiler\CheckInterfaceValidityPass; class DaDiBundle extends Bundle { public function build(ContainerBuilder $container) { parent::build($container); // ... YamlFileLoader::addDecorator('Da\DiBundle\DependencyInjection\Loader\InterfaceYamlFileLoaderDecorator'); $container->addCompilerPass(new CheckInterfaceValidityPass(), PassConfig::TYPE_OPTIMIZE); } }
限制
目前,所有功能都仅针对 yaml 配置文件开发。
测试
由于新参数的开发可能影响其他参数,您可以在开发自己的参数后运行 phpunit 来检查所有功能是否仍然正常。