Symfony2 学习笔记之服务容器

现在的PHP应用程序都是面向对象开发,所以主要是由对象构成。有的对象可以方便的分发邮件信息而有的可能帮你把信息写入到数据库中。在你的应用程序中,你可能创建一个对象用于管理你的产品库存,或者另外一个对象处理来自第三方API的数据。重要的是现在应用程序要做的这些事情都是被组织到许许多多的对象中来处理它的每一项任务的。

我们将套路一下Symfony2中一个特殊的PHP对象,它帮助我们实例化,组织和获取你应用程序汇总的许多对象。这个对象叫做服务容器,它可以帮助你使用标准统一的方式来创建你程序中的对象。它能简化你的繁杂的初始对象工作,并且拥有超快的执行速度,强调该框架提高了可重用性和降低了代码耦合。
因为所有的Symfony2的类都是用了该容器,所以你需要了解怎样去扩展,配置和使用对象。
从大的方面说,服务容器对Symfony2的速度和可扩展性是一个最大的贡献者。

最后,配置和使用一个服务容器是非常简单的。在本章的最后,你将能通过容器和来自第三方Bundle的自定义对象很舒适的创建你自己的对象。你将能够写出更加具有重用性,可测试性以及松散耦合的代码。

什么是服务?
简而言之,一个服务就是任何一个执行一些全局任务的PHP对象。
在计算机科学中它是一个通用名字用来描述一个对象被创建用来满足特定目的。
每个服务都会应用于你整个应用程序,无论何时你需要他们的时候,他们都能提供。

你不需要做任何特别的是来制造服务,仅仅写一个PHP类,在类中定义一些完成某种功能的代码即可。

有一个规则,当一个PHP对象被在整个应用程序中全局使用的时候,该PHP对象就是一个服务了。
一个单独的Mailer服务用于整个应用程序的邮件信息发送,而许多Message对象被分发这不是服务。
同样的,Product对象不是一个服务,但是一个对象能够持久化Product到数据库,那么这个对象就是服务了。

那么接下来还有什么重要的事呢?
考虑服务的一个优点就是说明你开始考虑从你的应用程序中分离某一功能到一些列的服务。因为每个服务只做一件工作。
当你需要的时候你可以很容易的访问每个服务和使用它们的功能。
每个服务也必须能很容易的测试和配置,因为它们是从你应用程序的其他的功能中分离了出来的。
这种主意叫做面向服务架构。 围绕着一些列相对独立的服务构造你的应用程序是一个非常著名和可信任的面向对象实践。
这也是能成为一个优秀开发者的必备。

什么是服务容器?
一个服务容器,也叫做依赖注入容器,仅仅是一个PHP对象,用于管理服务的实例化。
比如,假设我们有一个简单的PHP类分发Email信息。 没有服务容器,我们必须在使用它的时候手动的创建这个对象:

use Acme\HelloBundle\Mailer;
$mailer = new Mailer(‘sendmail‘);
$mailer->send(‘[email protected]‘,...);

这看上去很容易,假设Mailer类允许我们配置用于分发邮件信息的方法(sendmail,smtp等)。但是如果我们想在别的地方使用mailer服务? 我们又不想重复的在每一个使用Mailer的地方配置它。如果我们需要改变邮件发送的地址怎么办?我们需要找到每一个Mailer配置的然后修改代码。

在容器中 创建/配置服务

一个更好的做法是让服务容器来为我们创建Mailer对象。为了能够实现这一做法,我们必须教会容器怎样去创建Mailer服务。这些工作是通过配置文件来实现的,配置文件你可以设置成YAML,XML或者PHP格式均可。

YAML格式:

#app/config/config.yml
services:
    my_mailer:
        class:     Acme\HelloBundle\Mailer
        arguments: [sendmail]

XML格式:

<!-- app/config/config.xml-->
<services>
    <service id="my_mailer" class="Acme\HelloBundle\Mailer">
        <argument>sendmail</argument>
    </service>
</services>

PHP代码格式:

//app/config/config.php
use Symfony\Component\DependencyInjection\Definition;
$container->setDefinition(‘my_mailer‘,new Definition(
    ‘Acme\HelloBundle\Mailer‘,
        array(‘sendmail‘)
));

当Symfony2 初始化时,它会默认根据应用程序配置(app/config/config.yml)创建服务容器。真正的服务容器配置文件是AppKernel::registerContainerConfiguration()方法加载的一个环境特定配置文件,config_dev.yml 用于开发阶段,config_prod.yml用于运营阶段。
Symfony2配置启动后,Acme\HelloBundle\Mailer的对象实例就能够通过服务容器使用了。服务容器可以在任何传统的Symfony2的controller中使用,通过简写方法 get()调用。

class HelloController extends Controller
{
   //...
   public function sendEmailAction()
   {
      //...
      $mailer = $this->get(‘my_mailer‘);
      $mailer->send(‘[email protected]‘,...);
   }
}

当我们从服务容器中获取Mailer实例时,使用my_mailer. 容器会创建这个对象并返回它。有另外一个好处,服务直到用到它时才会创建。这样能够节省内存提高程序效率。

服务参数:

通过服务容器创建一个新服务很简单,参数的设置会使定义更加有组织性和有适应性。

YAML格式:

#app/config/config.yml
parameters:
   my_mailer.class:     Acme\HelloBundle\Mailer
   my_mailer.transport: sendmail

services:
   my_mailer:
      class:     %my_mailer.class%
      arguments: [%my_mailer.transport%]

XML格式:

<!-- app/config/config.xml -->
<parameters>
    <parameter key="my_mailer.class">Acme\HelloBundle\Mailer</parameter>
    <parameter key="my_mailer.transport">sendmail</parameter>
</parameters>

<services>
    <service id="my_mailer" class="%my_mailer.class%">
        <argument>%my_mailer.transport%</argument>
    </service>
</services>

PHP代码格式:

// app/config/config.php
use Symfony\Component\DependencyInjection\Definition;

$container->setParameter(‘my_mailer.class‘, ‘Acme\HelloBundle\Mailer‘);
$container->setParameter(‘my_mailer.transport‘, ‘sendmail‘);

$container->setDefinition(‘my_mailer‘, new Definition(
    ‘%my_mailer.class%‘,
    array(‘%my_mailer.transport%‘)
));

上面配置文件中,%% 是参数定义方式,当容器创建完成后就会查找参数定义,如果%参数或者变量作为字符串一部分时,需要添加另一个%进行转换:

<argument type="string">http://symfony.com/?foo=%%s&bar=%%d</argument>

参数的目的就是把信息传入服务,当然,不定义任何参数也是没有问题的。

定义参数会具有某些优势:
在一个参数键parameters中分离和组织服务的所有可选项。
参数值可以用于多个服务的定义。
在一个bundle中创建一个服务,使用参数可以很容易的在你的应用程序中进行自定义化服务。

当然,用不用参数完全取决于你的决定。高质量的第三方bundles总是使用参数,当他们把服务放入容器中使其更具可配置性。当然,在你的应用程序中你可能不需要这样的配置性。

数组参数:

参数不一定都是普通字符串,也有可能是数组。如果是写在XML格式的配置文件中,那么你需要为数组参数定义type="collection" 属性。

YAML格式:

# app/config/config.yml

parameters:

    my_mailer.gateways:

        - mail1

        - mail2

        - mail3

    my_multilang.language_fallback:

        en:

            - en

            - fr

        fr:

            - fr

            - en

XML格式:

<!-- app/config/config.xml -->
<parameters>
    <parameter key="my_mailer.gateways" type="collection">
        <parameter>mail1</parameter>
        <parameter>mail2</parameter>
        <parameter>mail3</parameter>
    </parameter>
    <parameter key="my_multilang.language_fallback" type="collection">
        <parameter key="en" type="collection">
            <parameter>en</parameter>
            <parameter>fr</parameter>
        </parameter>
        <parameter key="fr" type="collection">
            <parameter>fr</parameter>
            <parameter>en</parameter>
        </parameter>
    </parameter>
</parameters>

PHP代码格式:

// app/config/config.php
use Symfony\Component\DependencyInjection\Definition;

$container->setParameter(‘my_mailer.gateways‘, array(‘mail1‘, ‘mail2‘, ‘mail3‘));
$container->setParameter(‘my_multilang.language_fallback‘,
                         array(‘en‘ => array(‘en‘, ‘fr‘),
                               ‘fr‘ => array(‘fr‘, ‘en‘),
                        ));

导入其它容器配置资源:

在这里我们把服务配置文件看成资源。这里就是想说明一个事实,几乎所有的配置资源都是文件,如YAML,XML,PHP等。
Symfony2 非常灵活,它的配置可以放到任何地方,比如数据库更或者外部的一个webservice。

服务容器默认情况下用一个单一的配置资源创建(app/config/config.yml)。其它所有的服务配置必须从这个文件中一次或者多次导入。这包括Symfony2核心配置和第三方bundle配置。这给你的应用程序在服务上有了相对的灵活性。

扩展的服务配置可以通过两种方式导入:第一,我们最常用的是 imports 命令。接下来我们会介绍第二种方法,它是更灵活并且是导入第三方bundles中的服务配置的首选。

1. 通过imports导入配置

到目前为止,我们已经把my_mailer服务的配置直接定义到了应用程序配置文件(app/config/config.yml)中了。当然,因为Mailer本身就在AcmeHelloBundle中,其实把my_mailer的容器定义放到bundle中也可以。

首先,我们把my_mailer的容器定义移到一个新的容器资源文件中把它放到AcmeHelloBundle之外。如果Resources或者Resources/config 目录不存在,我们创建它。

YAML格式:

# src/Acme/HelloBundle/Resources/config/services.yml
parameters:
    my_mailer.class:      Acme\HelloBundle\Mailer
    my_mailer.transport:  sendmail

services:
    my_mailer:
        class:        %my_mailer.class%
        arguments:    [%my_mailer.transport%]

XML格式:

<!-- src/Acme/HelloBundle/Resources/config/services.xml -->
<parameters>
    <parameter key="my_mailer.class">Acme\HelloBundle\Mailer</parameter>
    <parameter key="my_mailer.transport">sendmail</parameter>
</parameters>

<services>
    <service id="my_mailer" class="%my_mailer.class%">
        <argument>%my_mailer.transport%</argument>
    </service>
</services>

PHP代码格式:

// src/Acme/HelloBundle/Resources/config/services.php
use Symfony\Component\DependencyInjection\Definition;

$container->setParameter(‘my_mailer.class‘, ‘Acme\HelloBundle\Mailer‘);
$container->setParameter(‘my_mailer.transport‘, ‘sendmail‘);

$container->setDefinition(‘my_mailer‘, new Definition(
    ‘%my_mailer.class%‘,
    array(‘%my_mailer.transport%‘)
));

配置定义的本身没有发生变化,只是挪了个位置。当然,服务容器不知道我们的资源文件新位置。幸运的是我们可以利用imports 键在应用程序的配置文件中很容易的导入它们。

YAML格式:

# app/config/config.yml
imports:
    - { resource: @AcmeHelloBundle/Resources/config/services.yml }

XML格式:

<!-- app/config/config.xml -->
<imports>
    <import resource="@AcmeHelloBundle/Resources/config/services.xml"/>
</imports>

PHP代码格式:

// app/config/config.php
$this->import(‘@AcmeHelloBundle/Resources/config/services.php‘);

imports命令允许你的应用程序从其他地方获取服务容器的配置资源(一般是从一些bundle中)。resource的位置,对于文件资源,是绝对路径。@AcmeHello语法决定了AcmeHelloBundle 的路径。这使得你指定资源路径的时候不用担心以后移动AcmeHelloBundle到别的目录的问题。

2. 使用容器扩展导入配置

在使用Symfony2 开发过程中,你将经常用到imports命令来从你创建的bundle中导入容器配置。而第三方的bundle容器配置,包括Symfony2的核心服务在内,通常使用另外一种方法,它更灵活更易于配置。

它是如何工作的呢?
实际上,每个bundle在定义自己的服务配置是都是跟到目前为止我看到的是一样的。换句话说,一个bundle使用一个或者多个配置资源文件(通常是XML)来指定bundle所需要的参数和服务。然而,我们不直接在配置文件中使用imports命令导入它们,而是仅仅在bundle中调用一个服务容器扩展来为我们做同样的工作。

一个服务容器扩展bundle的作者创建的是一个PHP类,它主要完成两件事情:
为该bundle配置服务导入需要的所有的服务容器资源。
提供语法上简介配置,让bundle能够直接被配置,而不需要再与bundle的服务容器配置参数交互。

换句话说,一个服务容器扩展会帮你配置好它的bundle所需的服务。

让我们看看FrameworkBundle是如何做的。
FrameworkBundle是Symfony2框架bundle,下面的代码显示了在你的应用程序配置中调用FrameworkBundle中的服务容器扩展。

YAML格式:

# app/config/config.yml
framework:
    secret:          xxxxxxxxxx
    charset:         UTF-8
    form:            true
    csrf_protection: true
    router:        { resource: "%kernel.root_dir%/config/routing.yml" }
    # ...

XML格式:

<!-- app/config/config.xml -->
<framework:config charset="UTF-8" secret="xxxxxxxxxx">
    <framework:form />
    <framework:csrf-protection />
    <framework:router resource="%kernel.root_dir%/config/routing.xml" />
    <!-- ... -->
</framework>

PHP代码格式:

// app/config/config.php
$container->loadFromExtension(‘framework‘, array(
    ‘secret‘          => ‘xxxxxxxxxx‘,
    ‘charset‘         => ‘UTF-8‘,
    ‘form‘            => array(),
    ‘csrf-protection‘ => array(),
    ‘router‘          => array(‘resource‘ => ‘%kernel.root_dir%/config/routing.php‘),
    // ...
));

当这个配置被解析时,容器会查找一个可以处理framework配置命令的扩展。这个扩展在FrameworkBundle中,它会被调用来为FrameworkBundle加载服务配置。如果你从你的配置文件中完全去掉framework键,Symfony 核心服务将不会被加载。这完全是由你控制的。

当然,你可以做更多,而不只是激活FrameworkBundle的服务容器扩展。每个扩展都允许你很容易的个性化bundle,而不用关系其内部服务是怎么定义的。

比如你可以个性化charset,error_handler,csrf_protection,router等配置。实际上,FrameworkBundle使用的这里指定的项目来配置服务于它自身的服务配置。bundle负责为服务容器创建所有需要的parameters和services,同时依然允许大量的配置可以被个性化。作为一个额外的好处,大部分服务容器扩展能够执行校验,通知你那些选项丢失或者数据类型不正确。当安装或者配置一个bundle时应该看看bundle的说明,了解一下如何安装和配置它需要的服务。

注意:服务容器天生能够识别parameters,services 和imports命令,其它的命令则需要服务容器扩展来处理。

引用(注入)服务:
到目前为止,我们创建的my_mailer服务非常简单:它仅仅在它的构造函数中接受一个参数,非常容易配置。只有当我们需要创建一个服务而它又依赖于一个或者多个其它服务时,我们才能看到服务容器的真正威力。

让我们来看个例子:
假设我们有个新服务,NewsletterManager,它用于管理准备和分发一个邮件信息到一组地址。当然,my_mailer已经能够发送邮件信息了,所以我们将在NewsletterManager内部使用它。
假设它的类内容如下:

namespace Acme\HelloBundle\Newsletter;

use Acme\HelloBundle\Mailer;

class NewsletterManager
{
    protected $mailer;

    public function __construct(Mailer $mailer)
    {
        $this->mailer = $mailer;
    }

    // ...
}

上面的例子代码中没有使用服务容器,我们可以很容易的在controller内创建一个新的NewsletterManager实例。

public function sendNewsletterAction()
{
    $mailer = $this->get(‘my_mailer‘);
    $newsletter = new Acme\HelloBundle\Newsletter\NewsletterManager($mailer);
    // ...
}

这种方式是好的,但是如果我们决定NewsletterManager类需要第二个或者第三个构造函数参数呢?
我们可以重写代码并重新命名该类来实现,可是我们需要找到所有的使用NewsletterManager的地方修改它。这是很痛苦的事情,这时候服务容器就成了一个很诱人的选择。

YAML格式:

# src/Acme/HelloBundle/Resources/config/services.yml
parameters:
    # ...
    newsletter_manager.class: Acme\HelloBundle\Newsletter\NewsletterManager

services:
    my_mailer:
        # ...
    newsletter_manager:
        class:     %newsletter_manager.class%
        arguments: [@my_mailer]

XML格式:

<!-- src/Acme/HelloBundle/Resources/config/services.xml -->
<parameters>
    <!-- ... -->
    <parameter key="newsletter_manager.class">Acme\HelloBundle\Newsletter\NewsletterManager</parameter>
</parameters>

<services>
    <service id="my_mailer" ... >
      <!-- ... -->
    </service>
    <service id="newsletter_manager" class="%newsletter_manager.class%">
        <argument type="service" id="my_mailer"/>
    </service>
</services>

PHP代码格式:

// src/Acme/HelloBundle/Resources/config/services.php
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

// ...
$container->setParameter(‘newsletter_manager.class‘, ‘Acme\HelloBundle\Newsletter\NewsletterManager‘);

$container->setDefinition(‘my_mailer‘, ... );
$container->setDefinition(‘newsletter_manager‘, new Definition(
    ‘%newsletter_manager.class%‘,
    array(new Reference(‘my_mailer‘))
));

在YAML配置文件中,@my_mailer告诉容器去查找一个名叫my_mailer的服务对象并把它传递给NewsletterManager的构造函数。在这种情况下,指定的服务my_mailer必须存在。如果不存在,将会抛出异常。你可以把你的依赖标记为可选,接下来说明。

可选依赖:setter注入
通过构造函数参数方式注入一个依赖在依赖已经存在并可用的情况下是一个完美的方式。但是当一个类的依赖是可选的时候,setter注入就成了更好的选择。

namespace Acme\HelloBundle\Newsletter;

use Acme\HelloBundle\Mailer;

class NewsletterManager
{
    protected $mailer;

    public function setMailer(Mailer $mailer)
    {
        $this->mailer = $mailer;
    }

    // ...
}

相应的配置上只需要一点改动:

YAML格式:

# src/Acme/HelloBundle/Resources/config/services.yml
parameters:
    # ...
    newsletter_manager.class: Acme\HelloBundle\Newsletter\NewsletterManager

services:
    my_mailer:
        # ...
    newsletter_manager:
        class:     %newsletter_manager.class%
        calls:
            - [ setMailer, [ @my_mailer ] ]

XML格式:

<!-- src/Acme/HelloBundle/Resources/config/services.xml -->
<parameters>
    <!-- ... -->
    <parameter key="newsletter_manager.class">Acme\HelloBundle\Newsletter\NewsletterManager</parameter>
</parameters>

<services>
    <service id="my_mailer" ... >
      <!-- ... -->
    </service>
    <service id="newsletter_manager" class="%newsletter_manager.class%">
        <call method="setMailer">
             <argument type="service" id="my_mailer" />
        </call>
    </service>
</services>

PHP代码格式:

// src/Acme/HelloBundle/Resources/config/services.php
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

// ...
$container->setParameter(‘newsletter_manager.class‘, ‘Acme\HelloBundle\Newsletter\NewsletterManager‘);

$container->setDefinition(‘my_mailer‘, ... );
$container->setDefinition(‘newsletter_manager‘, new Definition(
    ‘%newsletter_manager.class%‘
))->addMethodCall(‘setMailer‘, array(
    new Reference(‘my_mailer‘)
));

设置可选引用:
有时候,你的应用可能有一个可选的依赖,这就意味着这个依赖对于你的服务运行不是必须的。上面例子中,my_mailer服务必须存在,所以没有它是会抛出异常。我们来修改newsletter_manager服务定义,让这个依赖变为可选依赖。这样当它存在是容器会注入它,如果不存在时,什么也不做。

YAML格式:

# src/Acme/HelloBundle/Resources/config/services.yml
parameters:
    # ...

services:
    newsletter_manager:
        class:     %newsletter_manager.class%
        arguments: [@?my_mailer]

XML格式:

<!-- src/Acme/HelloBundle/Resources/config/services.xml -->

<services>
    <service id="my_mailer" ... >
      <!-- ... -->
    </service>
    <service id="newsletter_manager" class="%newsletter_manager.class%">
        <argument type="service" id="my_mailer" on-invalid="ignore" />
    </service>
</services>

PHP代码格式:

// src/Acme/HelloBundle/Resources/config/services.php
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerInterface;

// ...
$container->setParameter(‘newsletter_manager.class‘, ‘Acme\HelloBundle\Newsletter\NewsletterManager‘);

$container->setDefinition(‘my_mailer‘, ... );
$container->setDefinition(‘newsletter_manager‘, new Definition(
    ‘%newsletter_manager.class%‘,
    array(new Reference(‘my_mailer‘, ContainerInterface::IGNORE_ON_INVALID_REFERENCE))
));

在YAML配置文件中,@? 语法标示告诉服务容器该依赖是可选的。当然,NewsletterManager类也需要相应的修改一下构造函数:

public function __construct(Mailer $mailer = null)
{
    // ...
}

Symfony核心和第三方bundle服务:
从Symfony2和所有第三方bundles的配置都通过容器获取他们的服务, 你可以很容易的访问他们或者在你自己的服务中使用他们。为了保持简洁,Symfoy2模式不需要controller也定义成服务。而是Symfony2把整个服务注入到所有的Controller中。比如,处理在用户Session中存在信息时,Symfony2 提供了一个session服务,你可以在一个标准controller中直接调用:

public function indexAction($bar)
{
    $session = $this->get(‘session‘);
    $session->set(‘foo‘, $bar);

    // ...
}

在Symfony2中,你将经常使用Symfoy或第三方bundles提供的服务来执行任务,比如渲染模板的templating, 发送邮件的mailer访问请求信息的request等。

我们可以进一步的在我们自己创建的服务中调用这些服务。让我们修改NewsletterManager使用真正的Symfony2 mailer服务。同时我们还为其传入模板引擎,让它通过一个模板生成邮件内容。

namespace Acme\HelloBundle\Newsletter;

use Symfony\Component\Templating\EngineInterface;

class NewsletterManager
{
    protected $mailer;

    protected $templating;

    public function __construct(\Swift_Mailer $mailer, EngineInterface $templating)
    {
        $this->mailer = $mailer;
        $this->templating = $templating;
    }

    // ...
}

配置服务容器:

YAML格式:

services:
    newsletter_manager:
        class:     %newsletter_manager.class%
        arguments: [@mailer, @templating]

XML格式:

<service id="newsletter_manager" class="%newsletter_manager.class%">
    <argument type="service" id="mailer"/>
    <argument type="service" id="templating"/>
</service>

PHP代码格式:

$container->setDefinition(‘newsletter_manager‘, new Definition(
    ‘%newsletter_manager.class%‘,
    array(
        new Reference(‘mailer‘),
        new Reference(‘templating‘)
    )
));

高级容器配置:

到此我们看到,在容器中定义一个服务非常简单,通常包含一个服务的配置键和一些参数。然而,容器还有许多其它的可用工具帮助标志(tag)服务为特定的功能。 以创建更复杂的服务,在容器建立后执行操作。

设置服务为public/private
在定义服务的时候,你通常想在应用程序范围内访问它们,这些服务叫做public服务。比如doctrine服务在使用DoctrineBundle注册时就是一个公共服务,你可以按照如下方式访问:

$doctrine = $container->get(‘doctrine‘);

然而,在某些情况下你不想一个服务变为公共的。这种情况通常出现在你创建某个服务只是为另外一个服务作为输入参数时出现。private 私有服务,这些服务在调用的时候只能在参数行内通过 new PrivateFooBar()形式引用。

简单的说:一个服务当你不想它被你的代码直接访问时,它就是私有的了。

配置形式如下:

YAML格式:

services:
   foo:
     class: Acme\HelloBundle\Foo
     public: false

XML格式:

<service id="foo" class="Acme\HelloBundle\Foo" public="false" />

PHP代码格式:

$definition = new Definition(‘Acme\HelloBundle\Foo‘);
$definition->setPublic(false);
$container->setDefinition(‘foo‘, $definition);

这时候你就不能再进行如此操作了:

$container->get(‘foo‘);

注意:服务默认情况下全部是公共的。如果一个服务被配置为private了,但是你还想引用它,那么你需要给它定义别名。

服务的别名:
当使用核心或者第三方bundles提供的服务时,你可能想用更加方便快捷的形式调用某些服务。你可以通过给他们定义别名来实现,甚至给私有服务定义别名。

YAML格式:

services:
   foo:
     class: Acme\HelloBundle\Foo
   bar:
     alias: foo

XML格式:

<service id="foo" class="Acme\HelloBundle\Foo"/>
<service id="bar" alias="foo" />

PHP代码格式:

$definition = new Definition(‘Acme\HelloBundle\Foo‘);
$container->setDefinition(‘foo‘, $definition);

$containerBuilder->setAlias(‘bar‘, ‘foo‘);

这时你可以通过别名来直接调用以前的私有服务了,比如:

$container->get(‘bar‘); // 将返回以前私有服务 foo

要求必备文件:
有些情况下,你需要在加载服务前包含其它文件,这时你可以使用file命令实现:

YAML格式:

services:
   foo:
     class: Acme\HelloBundle\Foo\Bar
     file: %kernel.root_dir%/src/path/to/file/foo.php

PHP代码格式:

$definition = new Definition(‘Acme\HelloBundle\Foo\Bar‘);
$definition->setFile(‘%kernel.root_dir%/src/path/to/file/foo.php‘);
$container->setDefinition(‘foo‘, $definition);

XML格式:

<service id="foo" class="Acme\HelloBundle\Foo\Bar">
    <file>%kernel.root_dir%/src/path/to/file/foo.php</file>
</service>

注意:Symfony将在内部调用PHP函数require_once,这就意味着你的文件将每个请求都会被包括一次。

服务标签(Tags):
就像你在网络上发表博客可以设置标签“Symfony”或者“PHP”等一样,你在容器中配置的服务也可以被贴上标签。一个标签暗示这个服务是被用于特殊目的的。比如:

YAML格式:

services:
    foo.twig.extension:
        class: Acme\HelloBundle\Extension\FooExtension
        tags:
            -  { name: twig.extension }

XML格式:

<service id="foo.twig.extension" class="Acme\HelloBundle\Extension\FooExtension">
    <tag name="twig.extension" />
</service>

PHP代码格式:

$definition = new Definition(‘Acme\HelloBundle\Extension\FooExtension‘);
$definition->addTag(‘twig.extension‘);
$container->setDefinition(‘foo.twig.extension‘, $definition);

这里的twig.extension 标签就是一个专用标签,是TwigBundle在配置时使用的。通过给服务标注这个twig.extension标签,bundle就会知道foo.twig.extension 服务应该被注册为一个Twig的扩展。换句话说,Twig会查找所有标记为twig.extension的服务并自动把它们注册为扩展。

下面列出了Symfony2核心bundles的可用的标签:

assetic.filter
assetic.templating.php
data_collector
form.field_factory.guesser
kernel.cache_warmer
kernel.event_listener
monolog.logger
routing.loader
security.listener.factory
security.voter
templating.helper
twig.extension
translation.loader
validator.constraint_validator

以上就是有关Symfony2 中类似Spring容器的Services Container的基本知识。

参考URL:http://symfony.com/doc/current/book/service_container.html

Symfony2 学习笔记之服务容器

时间: 2024-10-11 20:53:11

Symfony2 学习笔记之服务容器的相关文章

Symfony2 学习笔记之插件格式

一个bundle类似于其它框架中的插件,但是比插件表现更好.它跟其它框架最主要的不同是在Symfony2中所有东西都是bundle,包括核心框架功能和你写的所有应用程序代码.Symfony2中,bundle可是一等公民.这给了你使用其它第三方开发的内容包或者分发你自己的bundle更多灵活性.你可以方便的选择哪些内容可以应用到你的程序中那些不用,来根据你的想法优化它们. 一个bundle就是一个目录,它具有很好的结构性,它能存放从类到controller和web资源等任何东西. 一个bundle

Symfony2学习笔记之事件分配器

----EventDispatcher组件使用 简介:       面向对象编程已经在确保代码的可扩展性方面走过了很长一段路.它是通过创建一些责任明确的类,让它们之间变得更加灵活,开发者可以通过继承这些类创建子类,来改变它们的行为.但是如果想将某个开发者的改变跟其它已经编写了自己子类的开发者共享,这种面向对象的继承就不再那么有用了. 举一个现实的实例,你想为你的项目提供一个插件系统.一个能够被添加到方法的插件,或者在方法执行的前后完成某些工作,而不会干扰到其它插件.这个通过单一的继承完成不是一个

Symfony2 学习笔记之控制器

一个controller是你创建的一个PHP函数,它接收HTTP请求(request)并创建和返回一个HTTP回复(Response).回复对象(Response)可以是一个HTML页面,一个XML文档,一个序列化的JSON数组,一个图片,一个重定向,一个404错误或者任何你想要的内容.controller中可以包含任何渲染你页面内容的所需要的逻辑. 下面是一个controller最简单的例子,仅仅打印一个Hello world! use Symfony\Component\HttpFounda

Symfony2学习笔记之数据校验

校验在web应用程序中是一个常见的任务.数据输入到表单需要被校验.数据在被写入数据库之前或者传入一个webservice时也需要被校验. Symfony2 配备了一个Validator 组件,它让校验工作变得简单易懂.该组件是基于JSR303 Bean校验规范.一个Java规范用在PHP中. 基本验证理解校验的最好方法是看它的表现.首先,假设你已经创建了一个用于你应用程序某个地方的PHP对象. //src/Acme/BlogBundle/Entity/Author.php namespace A

Symfony2学习笔记之HTTP Cache

富web应用程序的本质意味着它们的动态.无论你的应用程序多么有效率,每个请求比起静态文件来说总会存在很多的耗费.对于大多数web程序来说,这没什么. Symfony2非常的轻快,无论你做些严重超载的请求,每个请求将会得到很快的回复,而不会对你的服务器造成压力.但是随着你站点的成长,负载将成为一个严重的问题.对每个请求处理应该只被正常执行一次.这就是缓存真正要达成的目标. 站在巨人肩膀上的缓存:提高一个应用程序执行效率的最有效方法是缓存一个页面的所有输出然后让后续的请求绕开整个应用程序.当然,这对

Symfony2学习笔记之表单

对于一个Web开发者来说,处理HTML表单是一个最为普通又具挑战的任务.Symfony2集成了一个Form组件,让处理表单变的容易起来.在这一节里,我们将从基础开始创建一个复杂的表单,学习表单类库中最重要的内容. Symfony2 的Form组件是一个独立的类库,你可以在Symfony2项目之外使用它. 创建一个简单的表单:假设你要创建一个应用程序的todo列表,需要显示一些任务.因为你的用户需要编辑和创建任务,所以你需要创建一个表单.在你开始之前,首先来看通用的Task类,用来表示和存储一个单

Symfony2 学习笔记之系统路由

mfony2 学习笔记之系统路由 漂亮的URL绝对是一个严肃的web应用程序必须做到的,这种方式使index.php?article_id=57这类的丑陋URL被隐藏,由更受欢迎的像 /read/intro-to-symfony 来替代. 拥有灵活性更为重要,如果你要改变一个页面的URL,比如从/blog 到 /new 怎么办?有多少链接需要你找出来并更新呢? 如果你使用Symfony的router,这种改变将变得很简单. Symfony2 router让你定义更具创造力的URL,你可以map你

C++学习笔记:List容器

http://www.cplusplus.com/reference/list/list/ #include <list>list l:初始化一个0大小的表list l(10):初始化一个10个大小的表empty:是否为空 size:大小 max_size:可分配的最大值 front:第一个元素 back:最后一个元素 push_front:插入到第一个 pop_front:弹出第一个 push_back:插入到最后 pop_back:弹出最后一个 insert:插入到某个位置 clear:清

C++学习笔记:Vector容器

vector v:初始化一个0大小的向量vector v(10):初始化一个10个大小的向量push_back:增加一个元素 pop:删除一个元素,不返回 front:返回第一个元素 back:返回最后一个元素 at:返回特定位置的元素 capacity:vector的容量,会自动扩大  也可以直接通过v[x]操作元素 /*: Test.cpp */ #include <iostream> #include <vector> #include <string> #inc