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
相關文章
相關標籤/搜索