da/di-bundle

允许向依赖注入添加参数的包

安装: 13

依赖者: 0

建议者: 0

安全: 0

星标: 0

关注者: 2

分支: 0

公开问题: 0

类型:symfony-bundle

dev-master 2013-06-03 12:53 UTC

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.yamlme-file.parser.xml 必须实现 me-file.parser 层定义的 Me\FileBundle\Parser\ParserInterface 接口。服务 me-file.parser.php 覆盖了 interface 参数,并必须实现 Me\FileBundle\Parser\CodeParserInterface 接口。

构建器

参数 builder 仅是帮助区分 factory 参数和原生 factory_classfactory_methodfactory_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 来检查所有功能是否仍然正常。