资讯专栏INFORMATION COLUMN

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

wemall / 2238人阅读

摘要:服务通过匿名函数定义,返回一个对象的实例定义一些服务请注意,匿名函数可以访问当前容器实例,从而允许引用其他服务或参数。如果要为所有调用返回不同的实例,请使用方法包装你的匿名函数。

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

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

安装

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

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

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

创建一个容器实例

use PimpleContainer;

$container = new Container();

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

定义服务

服务是一个对象,它可以作为一个庞大系统的一部分,一些服务的例子:数据库连接,模板引擎,邮件服务。几乎所有的全局对象都可以成为一项服务。

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

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

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

请注意,匿名函数可以访问当前容器实例,从而允许引用其他服务或参数。
由于只有在获取对象时才创建对象,因此定义的顺序无关紧要。

使用定义的服务也非常简单:

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

// 上述调用大致等同于以下代码:
// $storage = new SessionStorage("SESSION_ID");
// $session = new Session($storage);
定义工厂服务

默认情况下,每次获得服务时,Pimple 都会返回相同的实例。如果要为所有调用返回不同的实例,请使用 factory() 方法包装你的匿名函数。

$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;
});

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

扩展容器

如果你反复使用相同的库,可能希望将一个项目中的某些服务重用到下一个项目,通过实现 PimpleServiceProviderInterface 接口,打包你的服务到 Provider 程序中

use PimpleContainer;

class FooProvider implements PimpleServiceProviderInterface
{
    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 容器类

PimplePsr11Container 类允许你使用 PsrContainerContainerInterface 方法访问 Pimple 容器的内容:

use PimpleContainer;
use PimplePsr11Container 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 MonologLogger;
use PimplePsr11ServiceLocator;
use PsrContainerContainerInterface;
use SymfonyComponentEventDispatcherEventDispatcher;

class MyService
{
    /**
     * "logger" must be an instance of PsrLogLoggerInterface
     * "event_dispatcher" must be an instance of SymfonyComponentEventDispatcherEventDispatcherInterface
     */
    private $services;

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

$container["logger"] = function ($c) {
    return new MonologLogger();
};
$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 PimpleContainer;
use PimpleServiceIterator;

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/28634.html

相关文章

  • 一个强大PHP5.3依赖注入容器

    摘要:现在我们就可以在构造函数或者任何其他通过服务容器注入依赖项的地方使用类型提示注入接口创建一个新的类实例,此处将注入的实例。自动解析构造函数所需的依赖的服务容器实现了接口。 简单的服务容器 一个简单的 php 5.3 依赖注入容器。 项目地址:https://github.com/godruoyi/easy-container Why 目前比较流行的 PHP 容器: Pimple La...

    sf190404 评论0 收藏0
  • PHP - Pimple 源码笔记(下)

    摘要:服务容器接口是的简写,由组织制定的规范,是开发的实践标准。实现的容器类源码很简单,主要是传入变量,然后设置这个两个方法。原创文章,欢迎转载。原文链接地址作者发表日期 接着上篇 还有一些内容没有写,上篇已经把关于 Pimple 最主要的代码分析了一下,这篇主要是关于 PSR-11 兼容性的分析。 PSR-11 服务容器接口 PSR PSR 是 PHP Standard Recommend...

    KunMinX 评论0 收藏0
  • Pimple相关源码

    摘要:已经有了非常好的的相关解析,建议先看下一个简单的依赖注入容器读源码笔记上读源码笔记下这里通过例子补充下核心方法的说明相关的类型服务类似单例工厂服务多个实例参数仅仅是保存一些变量保护参数匿名函数都会被认为服务,但是如果仅仅是想作为一个 已经有了非常好的Pimple的相关解析,建议先看下:Pimple - 一个简单的 PHP 依赖注入容器读 PHP - Pimple 源码笔记(上)读 PH...

    MSchumi 评论0 收藏0
  • Laravel深入学习2 - 控制反转容器

    摘要:控制反转容器控制反转使依赖注入变得更加便捷。有瑕疵控制反转容器是实现的控制翻转容器的一种替代方案。容器的独立使用即使没有使用框架,我们仍然可以在项目中使用安装组件来使用的控制反转容器。在没有给定任何信息的情况下,容器是无法实例化相关依赖的。 声明:本文并非博主原创,而是来自对《Laravel 4 From Apprentice to Artisan》阅读的翻译和理解,当然也不是原汁原味...

    worldligang 评论0 收藏0
  • PHP程序员如何理解依赖注入容器(dependency injection container)

    摘要:代码这就是控制反转模式。是变量有默认值则设置默认值是一个类,递归解析有默认值则返回默认值从容器中取得以上代码的原理参考官方文档反射,具有完整的反射,添加了对类接口函数方法和扩展进行反向工程的能力。 PHP程序员如何理解依赖注入容器(dependency injection container) 背景知识 传统的思路是应用程序用到一个Foo类,就会创建Foo类并调用Foo类的方法,假如这...

    Coding01 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<