Symfony 服务容器入门

译文首发于 Symfony 服务容器入门,转载请注明出处。

本文是依赖注入(Depeendency Injection)系列教程的第 3 篇文章,本系列教程主要讲解如何使用 PHP 实现一个轻量级服务容器,教程包括:php


术语

  • Depeendency Injection 译做 依赖注入
  • Depeendency Injection Container 译做 依赖注入容器
  • Container 译做 容器
  • Service Container 译做 服务容器
  • Session 译做 会话
  • Object-Oriented 译做 面向对象
  • mock 译做 模拟
  • anti-patterns 译做 反模式
  • hardcoded 译做 硬编码

从本系列的开篇到如今咱们基本仍是围绕「依赖注入」的基本概念展开的。前两篇入门的文章对于理解本文及后续教程相当重要。如今,是时候该去探索 Symfony 2 服务容器是如何实现这个主题了。html

Symfony 中的「依赖注入容器」定义的类名为「sfServiceContainer」。这是一个很是轻量级的类,实现了 [上一篇]() 文章中讲解到的基本功能。数组

Symfony 服务容器能够到官方 Svn 版本库中得到: http://svn.symfony-project.co...。注意, Symfony 组件依旧保持更新,这也意味着它的实现可能与本文有所出入。(译注: @todo)

在 Symfony 中,任何服务的实例都有容器管理。前一篇文章中提到的 Zend_Mail 实例中,就须要使用到两个服务:mailer 服务和 mail_transport 服务。性能优化

<?php

class Container
{
    static protected $sharged = array();

    protected $parameters = array();

    public function __construct(array $parameters = array())
    {
        $this->parameters = $parameters;
    }

    public function getMailTransport()
    {
        return new Zend_Mail_Transport_Smtp('smpt.gmail.com', array(
            'auth'     => 'login',
            'username' => $this->parameters['mailer.username'],
            'password' => $this->parameters['mailer.password'],
            'ssl'      => 'ssl',
            'port'     => 465,
        ));
    }

    public function getMailer()
    {
        if (isset(self::$shared['mailer'])) {
            return self::$shared['mailer'];
        }

        $class = $this->parameters['mailer.class'];

        $mailer = new $class();
        $mailer->setDefaultTransport($this->getMailTransport());

        return self::$shared['mailer'] = $mailer;
    }
}

让容器类 Container 继承 sfServiceContainer 类的话,能够有效精简代码:ide

<?php

class Container extends sfServiceContainer
{
    static protected $shared = array();

    protected function getMailTransportService()
    {
        return new Zend_Mail_Transport_Smtp('smpt.gmail.com', array(
            'auth'     => 'login',
            'username' => $this['mailer.username'],
            'password' => $this['mailer.password'],
            'ssl'      => 'ssl',
            'port'     => 465,
        ));
    }

    protected function getMailerService()
    {
        if (isset(self::$shared['mailer']))
        {
            return self::$shared['mailer'];
        }

        $class = $this['mailer.class'];

        $mailer = new $class();
        $mailer->setDefaultTransport($this->getMailTransportService());

        return self::$shared['mailer'] = $mailer;
    }
}

彷佛与以前相差无几,但经过继承 spServiceProvider 的容器类拥有更多功能而且代码更整洁。这里列几点主要的异同点:svn

  • 定义的方法名加上了 Service 后缀名。依据惯例优先原则,一个服务方法的定义由 get 前缀和 Service 缀名共同组成。每一个服务同时定义惟一的标识符,标识符命名规则为去除先后缀的方法名而且采用「下划线命名法」命名。好比咱们在容器中定义一个 getMailTransportServer() 方法,容器同时会定义一个名为 mail_transport 的服务标识符。
  • 全部定义的方法改成 protected 可见范围修饰符。稍后会讲解如何从容器获取相关服务。
  • 容器能够像数组同样直接获取参数值($this['mailer.class'])。

一个服务标识符必须惟一,而且仅能够包含字母、数字、下划线和 .(英文点号). 号在容器内的功能相似于「命名空间」(如 mail.mailermail.transport 实例)。性能

接下来是如何使用新的容器类:优化

<?php
require_once 'PATH/TO/sf/lib/sfServiceContainerAutoloader.php';
sfServiceContainerAutoloader::register();

$sc = new Container(array(
  'mailer.username' => 'foo',
  'mailer.password' => 'bar',
  'mailer.class'    => 'Zend_Mail',
));

$mailer = $sc->mailer;

如今,因为咱们继承 spServiceContainer 容器类,咱们能够使用更为整洁的接口功能:ui

  • 服务能够有统一的接口访问:
<?php

if ($sc->hasService('mailer')) {
    $mailer = $sc->getService('mailer'); 
}

$sc->setService('mailer', $mailer);
  • 或者,直接经过类的成员变量获取服务:
<?php
if (isset($sc->mailer)) {
    $mailer = $sc->mailer;
}

$sc->mailer = $mailer;
  • 参数名也能经过统一的接口访问:
<?php
if (!$sc->hasParameter('mailer_class')) {
    $sc->setParameter('mailer_class', 'Zend_Mail');
}

echo $sc->getParameter('mailer_class');

// 重写容器全部参数
$sc->setParameters($parameters);

// 向容器添加参数
$sc->addParameters($parameters);
  • 或者,直接经过类的成员变量已相似数组的方式获取:
<?ph
if (!isset($sc['mailer.class'])) {
    $sc['mailer.class'] = 'Zend_Mail';
}

$mailerClass = $sc['mailer.class'];
  • 还能够迭代获取容器内全部服务:
<?php
foreach ($sc as $id => $service)
{
    echo sprintf("Service %s is an instance of %s.\n", $id, get_class($service));
}

当项目容器须要管理不太多的服务时,经过继承 spServiceContainer 类是很是明智的选择;即便,这样依旧须要处理大量的基础工做或直接从已有项目中复制代码过来。而当系统引入大量的服务时,咱们就须要使用更好的方法来组织和管理这些服务。this

这就是为何多数时候咱们并不会直接使用 spServiceContainer 类的缘由。可是咱们花这个时间来说解 spServiceContainer 类的用法的理由是,它是 Symfony 依赖注入容器实现的基石。

下一篇文章,咱们未来看看能够简化服务定义过程的 sfServiceContainerBuilder 类。

原文: http://fabien.potencier.org/i...

相关文章
相关标签/搜索