Pimple - 一个简单的 PHP 依赖注入容器

连接
官网 WebSite
GitHub - Pimple
这是 Pimple 3.x 的文档。若是你正在使用 Pimple 1.x ,请查看 Pimple 1.x 文档
阅读 Pimple 1.x 代码也是学习更多关于如何建立简单的依赖注入容器的好方法,新版本的 Pimple 更加关注性能。

Pimple - 一个简单的 PHP 依赖注入容器php

安装

在你的项目中使用 Pimple 以前,将其添加到你的 composer.json 文件中:
$ ./composer.phar require pimple/pimple ~3.0 git

另外,Pimple 也可做为 PHP C 扩展使用:github

$ git clone https://github.com/silexphp/Pimple  
$ cd Pimple/ext/pimple  
$ phpize  
$ ./configure  
$ make  
$ make install

使用

建立一个容器实例shell

use Pimple\Container;

$container = new Container();

与许多其余依赖注入容器同样,Pimple 管理两种不一样类型的数据:服务参数数据库

定义服务

服务是一个对象,它能够做为一个庞大系统的一部分,一些服务的例子:数据库链接,模板引擎,邮件服务。几乎全部的全局对象均可以成为一项服务。json

服务经过匿名函数定义,返回一个对象的实例数组

// 定义一些服务
$container['session_storage'] = function ($c) {
    return new SessionStorage('SESSION_ID');
};

$container['session'] = function ($c) {
    return new Session($c['session_storage']);
};

请注意,匿名函数能够访问当前容器实例,从而容许引用其余服务或参数。
因为只有在获取对象时才建立对象,所以定义的顺序可有可无。cookie

使用定义的服务也很是简单:session

// 获取 session 对象
$session = $container['session'];

// 上述调用大体等同于如下代码:
// $storage = new SessionStorage('SESSION_ID');
// $session = new Session($storage);

定义工厂服务

默认状况下,每次得到服务时,Pimple 都会返回相同的实例。若是要为全部调用返回不一样的实例,请使用 factory() 方法包装你的匿名函数。composer

$container['session'] = $container->factory(function ($c) {
    return new Session($c['session_storage']);
});

如今,每次调用 $container['session'] 会返回一个新的 session 实例。

定义参数

定义参数容许从外部简化容器的配置并存储全局值

// 定义一些参数
$container['cookie_name'] = 'SESSION_ID';
$container['session_storage_class'] = 'SessionStorage';

你如今能够很轻松的经过重写 session_storage_class 参数而不是从新定义服务定义来更改 cookie 名称。

保护参数

因为 Pimple 将匿名函数看做服务定义,所以须要使用 protect() 方法将匿名函数包装为参数:

$container['random_func'] = $container->protect(function () {
    return rand();
});

修改已经定义的服务

在某些状况下,你可能须要在定义服务定义后修改它。在你的服务被建立后,你可使用 extend() 方法添加额外的代码:

$container['session_storage'] = function ($c) {
    return new $c['session_storage_class']($c['cookie_name']);
};

$container->extend('session_storage', function ($storage, $c) {
    $storage->...();

    return $storage;
});

第一个参数是要扩展的服务的名称,第二个参数是访问对象实例和容器的函数。

扩展容器

若是你反复使用相同的库,可能但愿将一个项目中的某些服务重用到下一个项目,经过实现 Pimple\ServiceProviderInterface 接口,打包你的服务到 Provider 程序中

use Pimple\Container;

class FooProvider implements Pimple\ServiceProviderInterface
{
    public function register(Container $pimple)
    {
        // register some services and parameters
        // on $pimple
    }
}

而后,在容器上注册 Provider

$pimple->register(new FooProvider());

获取服务建立方法

当你访问一个对象时,Pimple 自动调用你定义的匿名函数,为你建立服务对象。若是你想得到这个函数的原始访问权限,你可使用 raw()方法:

$container['session'] = function ($c) {
    return new Session($c['session_storage']);
};

$sessionFunction = $container->raw('session');

PSR-11 兼容性

因为历史缘由,Container 类没有实现 PSR-11 ContainerInterface。然而,Pimple 提供了一个辅助类,它可让你从 Pimple 容器类中解耦你的代码

PSR-11 容器类

Pimple\Psr11\Container 类容许你使用 Psr\Container\ContainerInterface 方法访问 Pimple 容器的内容:

use Pimple\Container;
use Pimple\Psr11\Container as PsrContainer;

$container = new Container();
$container['service'] = function ($c) {
    return new Service();
};
$psr11 = new PsrContainer($container);

$controller = function (PsrContainer $container) {
    $service = $container->get('service');
};
$controller($psr11);

使用 PSR-11 服务定位

有时候,服务须要访问其余几个服务,而没必要肯定全部这些服务都将被实际使用。在这些状况下,你可能但愿懒加载这些服务。

传统的解决方案是注入整个服务容器来得到真正须要的服务。可是,这不被推荐,由于它使服务对应用程序的其余部分的访问过于宽泛,而且隐藏了它们的实际依赖关系。

ServiceLocator 旨在经过访问一组预约义的服务来解决此问题,同时仅在实际须要时才实例化它们。
它还容许你以不一样于用于注册的名称提供服务。例如,你可能但愿使用一个对象,该对象指望 EventDispatcherInterface 实例在名称 event_dispatcher 下可用,而你的事件分派器已在名称 dispatcher 下注册

use Monolog\Logger;
use Pimple\Psr11\ServiceLocator;
use Psr\Container\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;

class MyService
{
    /**
     * "logger" must be an instance of Psr\Log\LoggerInterface
     * "event_dispatcher" must be an instance of Symfony\Component\EventDispatcher\EventDispatcherInterface
     */
    private $services;

    public function __construct(ContainerInterface $services)
    {
        $this->services = $services;
    }
}

$container['logger'] = function ($c) {
    return new Monolog\Logger();
};
$container['dispatcher'] = function () {
    return new EventDispatcher();
};

$container['service'] = function ($c) {
    $locator = new ServiceLocator($c, array('logger', 'event_dispatcher' => 'dispatcher'));

    return new MyService($locator);
};

懒懒的引用一系列服务

在数组中传递一组服务实例可能会致使效率低下,由于若是使用集合的类只须要在稍后调用它的方法时对其进行迭代便可。若是集合中存储的其中一个服务与使用该服务的类之间存在循环依赖关系,则也会致使问题。

ServiceIterator 类能够帮助你解决这些问题。它在实例化过程当中接收服务名称列表,并在迭代时检索服务

use Pimple\Container;
use Pimple\ServiceIterator;

class AuthorizationService
{
    private $voters;

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

    public function canAccess($resource)
    {
        foreach ($this->voters as $voter) {
            if (true === $voter->canAccess($resource) {
                return true;
            }
        }

        return false;
    }
}

$container = new Container();

$container['voter1'] = function ($c) {
    return new SomeVoter();
}
$container['voter2'] = function ($c) {
    return new SomeOtherVoter($c['auth']);
}
$container['auth'] = function ($c) {
    return new AuthorizationService(new ServiceIterator($c, array('voter1', 'voter2'));
}

谁在支持 Pimple ?

Pimple 是由 Symfony 框架的建立者 Fabien Potencier 写的 ,Pimple 是在 MIT 协议下发布的。

原创文章,欢迎转载。转载请注明出处,谢谢。
原文连接地址: http://dryyun.com/2018/04/17/...
做者: dryyun 发表日期: 2018-04-17 14:30:29
相关文章
相关标签/搜索