phossa/phossa-di

一个快速且功能全面的PHP依赖注入库。它支持自动装配、容器代理、对象装饰、定义提供者、定义标签、服务作用域等。

1.0.7 2016-06-10 09:21 UTC

This package is not auto-updated.

Last update: 2024-09-14 18:20:43 UTC


README

Build Status Latest Stable Version License

查看新库 phoole/di 简介

Phossa-di 是一个 快速功能丰富功能全面 的PHP依赖注入库。它支持 自动装配容器代理对象装饰定义提供者定义标签对象作用域 等功能。

它需要PHP 5.4,并支持PHP 7.0+、HHVM。它符合 PSR-1PSR-2PSR-4 以及即将推出的 PSR-5PSR-11

入门

  • 安装

    使用 composer 工具进行安装。

    composer require "phossa/phossa-di=1.*"
    

    或者将以下行添加到您的 composer.json

    {
        "require": {
          "phossa/phossa-di": "^1.0.6"
        }
    }
  • 简单使用

    您可能有一些像这样的简单类或第三方库,并希望将其作为服务提供。

    class MyCache
    {
        private $driver;
    
        public function __construct(MyCacheDriver $driver)
        {
            $this->driver = $driver;
        }
    
        // ...
    }
    
    class MyCacheDriver
    {
        // ...
    }

    现在执行以下操作,

    use Phossa\Di\Container;
    
    $container = new Container();
    
    // use the 'MyCache' classname as the service id
    if ($container->has('MyCache')) {
        $cache = $container->get('MyCache');
    }

    由于默认启用 自动装配,容器将在创建 $cache 实例时自动查找 MyCache 类并解决其依赖注入。

  • 使用定义

    复杂情况可能需要额外的配置。可以使用 add()set()map()addMethod()setScope() 等与定义相关的方法来配置服务。

    use Phossa\Di\Container;
    
    // turn off auto wiring
    $container = (new Container())->auto(false);
    
    // config service with id, classname and constructor arguments
    $container->add('cache', 'MyCache', [ '@cacheDriver@' ]);
    
    // add initialization methods
    $container->add('cacheDriver', 'MyCacheDriver')
              ->addMethod('setRoot', [ '%cache.root%' ]);
    
    // set a parameter which is referenced before
    $container->set('cache.root', '/var/local/tmp');
    
    // get cache service by its id
    $cache = $container->get('cache');
    • 服务定义

      使用API add($id, $classOrClosure, array $arguments) 定义 Service,之后可以在其他定义中使用服务引用 @service_id@

      $container = new Container();
      
      // add the 'cache' service definition
      $container->add('cache', \Phossa\Cache\CachePool::class, ['@cacheDriver@']);
      
      // add the 'cacheDriver' service definition
      $container->add('cacheDriver', \Phossa\Cache\Driver\FilesystemDriver);
      
      // get cache service
      $cache = $container->get('cache');

      格式为 @service_id@ 的服务引用可以在任何适当的地方使用,例如在参数数组中或构建伪可调用对象。

      // will resolve this ['@cache@', 'setLogger'] to a real callable
      $container->run(['@cache@', 'setLogger'], ['@logger@']);
    • 参数定义

      可以使用API set($name, $value) 设置参数。参数引用是 '%parameter.name%'。参数引用可以指向字符串、另一个参数或服务引用。

      // set system temp directory
      $container->set('system.tmpdir', '/var/tmp');
      
      // point cache dir to system temp
      $container->set('cache.dir', '%system.tmpdir%');
      
      // set with array of vales
      $container->set('logger', [
          'driver' => 'Phossa\Logger\Driver\StreamDriver',
          'level'  => 'warning'
      ]);
      
      // use parameter
      $container->add(
          'cacheDir',
          Phossa\Cache\Driver\Filesystem::class,
          [ '%cache.dir%' ]
      );
  • 使用可调用对象而不是类名

    可以使用可调用对象代替类名来实例化服务。

    // ...
    $container->add('cacheDriver', function() {
        return new \MyCacheDriver();
    });
  • 定义文件

    您可以将服务定义和参数定义放入一个定义文件或多个分离的文件中(将参数定义与服务定义分离将使您可以根据不同情况加载不同的参数),而不是在代码中配置 $container

    支持PHP、JSON、XML文件格式,并将根据文件扩展名自动检测。

    服务定义文件 definition.serv.php

    <?php
    /* file name '*.s*.php' indicating SERVICE definitions in PHP format */
    use Phossa\Di\Container;
    return [
        'cache' => [
            'class' => [ 'MyCache', [ '@cacheDriver@' ]],
            'scope' => Container::SCOPE_SHARED // default anyway
        ],
        'cacheDriver' => [
            'class'   => 'MyCacheDriver',
            'methods' => [
                [ 'setRoot', [ '%cache.root%' ] ],
                // ...
            ]
        ],
        'theDriver'  => '@cacheDriver@', // an alias
        // ...
    ];

    参数定义文件 definition.param.php

    <?php
    /* file name '*.p*.php' indicating PARAMETER definitions in PHP format */
    return [
        'tmp.dir' => '/var/local/tmp',
        'cache.root' => '%tmp.dir%',
        // ...
    ];

    或者您可以将这些文件合并成一个 definition.php

    <?php
    /* file name '*.php' indicating definitions in PHP format */
    use Phossa\Di\Container;
    return [
        // key 'services' indicating the service definitions
        'services' => [
            'cache' => [
                'class' => [ 'MyCache', [ '@cacheDriver@' ]],
                'scope' => Container::SCOPE_SHARED // default anyway
            ],
            'cacheDriver' => [
                'class'   => 'MyCacheDriver',
                'methods' => [
                    [ 'setRoot', [ '%cache.root%' ] ],
                    // ...
                ]
            ],
            // ...
        ],
    
        // key 'parameters' indicating the parameter definitions
        'parameters' => [
            'cache.root' => '/var/local/tmp',
            // ...
        ],
    
        // key 'mappings' indicating the mapping definitions
        'mappings' => [
            'Phossa\\Cache\\CachePoolInterface'  => 'Phossa\\Cache\\CachePool',
            // ...
        ],
    ];

    现在您可以加载定义文件了。

    use Phossa\Di\Container;
    
    $container = new Container();
    
    // load service definitions
    $container->load('./definition.serv.php');
    
    // load parameter definition
    $container->load('./definition.param.php');
    
    // you may load from one if you want to
    // $container->load('./definition.php');
    
    // getting what you've already defined
    $cache = $container->get('cache');

特性

  • 自动装配

    自动装配 是容器实例化对象并自动解决其依赖的能力。自动装配的基础是PHP函数参数的 类型提示

    通过反思类、构造函数和方法,phossa-di 能够找到对象(用户需要使用类名作为服务ID)的正确类,以及正确类名作为依赖项的正确类。

    为了充分探索自动装配功能,用户可以将接口映射到类名或服务ID,如下所示:

    // map an interface to a classname
    $container->map(
        'Phossa\\Cache\\CachePoolInterface', // MUST NO leading backslash
        'Phossa\\Cache\\CachePool' // leading backslash is optional
    );
    
    // map an interface to a service id, MUST NO leading backslash
    $container->map('Phossa\\Cache\\CachePoolInterface', '@cache@');
    
    // map an interface to a parameter, no leading backslash
    //$container->map('Phossa\\Cache\\CachePoolInterface', '%cache.class%');

    或者加载映射文件:

    $container->load('./defintion.map.php');

    自动装配可以开启或关闭。关闭自动装配将允许用户在不自动加载的情况下检查任何定义错误。

    // turn off auto wiring
    $container->auto(false);
    
    // turn on auto wiring
    $container->auto(true);
  • 容器代理

    根据 容器代理互操作性规范,容器可以注册一个代理容器(代理者),并且

    • get() 方法的调用应仅返回容器的一部分条目。如果条目不是容器的一部分,则应抛出异常(如 ContainerInterface 所请求的)。

    • has() 方法的调用应仅在条目是容器的一部分时返回 true。如果条目不是容器的一部分,则应返回 false。

    • 如果获取的条目有依赖项,则 而不是 在容器中执行依赖项查找,而是在代理容器(代理者)中执行查找。

    • 重要 默认情况下,查找 应该 仅在代理容器上执行,而不是在容器本身上。

    phossa-di 完全支持代理功能。

    use Phossa\Di\Delegator;
    
    // create delegator
    $delegator = new Delegator();
    
    // insert different containers
    $delegator->addContainer($otherContainer);
    
    // $contaner register with the delegator
    $container->setDelegate($delegator);
    
    // cacheDriver is now looked up through the $delegator
    $cache = $container->get('cache');
  • 对象装饰

    对象装饰 是根据某些标准(如实现接口)在服务对象实例化后立即应用装饰更改(运行方法等)。

    // any object implementing 'LoggerAwareInterface' should be decorated
    $container->addDecorate(
        'setlogger',  // rule name
        'Psr\\Log\\LoggerAwareInterface', // NO leading backslash
        ['setLogger', ['@logger@']] // run this method
    );

    对象装饰 可以节省用户大量定义重复,并将应用于未来的服务定义。Phossa-di 还支持测试调用和装饰调用,如下所示:

    $container->addDecorate('setlogger',
        function($object) {
            return $object instanceof \Psr\Log\LoggerAwareInterface;
        },
        function($object) use($container) {
            $object->setLogger($container->get('logger'));
        }
    );
  • 定义标记

    大多数开发者在开发或生产环境中使用不同的定义或配置。这是通过将定义放入不同的文件并根据容器标记加载这些文件来实现的。

    标记也用于 定义提供者

    // SYSTEM_CONST can be 'PRODUCTION' or 'DEVELOPMENT'
    $container->setTag(SYSTEM_CONST);
    
    // load different defintion base on container tags
    if ($container->hasTag('PRODUCTION')) {
        $container->load('./productDefinitions.php');
    } else {
        $container->load('./developDefinitions.php');
    }
  • 定义提供者

    定义提供者 用于将相关定义逻辑包装成一个实体。如果容器调用 has()get() 并在此提供者中找到定义,则这些定义将自动加载到容器中。

    <?php
    
    use Phossa\Di\Extension\Provider\ProviderAbstract;
    
    // Production related DB definitions here
    class ProductionDbProvider extends ProviderAbstract
    {
        // list of service ids we provide
        protected $provides = [ 'DbServer' ];
    
        // tags this provide has
        protected $tags = [ 'PRODUCTION' ];
    
        // the only method we need to implement
        protected function merge()
        {
            $container = $this->getContainer();
            $container->add('DbServer', '\\DbClass', [
                '192.168.0.12', 'myDbusername', 'thisIsApassword'
            ]);
        }
    }

    应在任何对 has()get() 的调用之前将提供者 ProductionDbProvider 添加到容器中。

    // SYSTEM_CONST is now 'PRODUCTION'
    $container->setTag(SYSTEM_CONST);
    
    // the provider will be loaded only if SYSTEM_CONST is PRODUCTION
    $container->addProvider(new ProductionDbProvider());
    
    // another provider will be loaded only if SYSTEM_CONST is TEST
    $container->addProvider(new TestDbProvider());
    
    // DB related definitions will be loaded here
    $db = $container->get('DbServer');

    或者在进行容器实例化时:

    $container = new Container('./defintions.php', [
        ProductionDbProvider::class,
        TestDbProvider::class
    ]);
  • 对象作用域

    默认情况下,容器中的服务对象是容器内部共享的,即它们具有 Container::SCOPE_SHARED 的作用域。如果用户希望每次都使用不同的实例,他们可以使用 one() 方法或使用具有 Container::SCOPE_SINGLE 作用域的服务定义。

    // a shared copy of cache service
    $cache1 = $container->get('cache');
    
    // a new cache instance
    $cache2 = $container->one('cache');
    
    // different instances
    var_dump($cache1 === $cache2);
    
    // but both share the same cacheDriver
    var_dump($cache1->getDriver() === $cache2->getDriver()); // true

    或者定义为 Container::SCOPE_SINGLE

    $container->add('cache', '\\Phossa\\Cache\\CachePool')
              ->setScope(Container::SCOPE_SINGLE);
    
    // each get() will return a new cache
    $cache1 = $container->get('cache');
    $cache2 = $container->get('cache');
    
    // different instances
    var_dump($cache1 === $cache2); // false
    
    // dependencies are shared
    var_dump($cache1->getDriver() === $cache->getDriver()); // true

    要使所有服务对象非共享,将容器的默认作用域设置为 Container::SCOPE_SINGLE,如下所示:

    // make everything non-shareable, set default scope to SCOPE_SINGLE
    $container->share(false);
    
    // this will return a new copy of cache service
    $cache1 = $container->get('cache');
    
    // this will return a new copy also
    $cache2 = $container->get('cache');
    
    // FALSE
    var_dump($cache1 === $cache2);
    
    // dependencies are different
    var_dump($cache1->getDriver() === $cache->getDriver()); // false

公共API

  • PSR-11 兼容的API

    • get(string $id): object

      从容器中获取命名服务。

    • has(string $id): bool

      检查容器中是否存在命名服务。

  • phossa-di 扩展的API

    • __construct(string|array $definitionArrayOrFile = '', array $definitionProviders = [])

      $defintionArrayOrFile 可以是一个定义文件或定义数组。

      $definitionProviders 可以是 ProviderAbstract 对象的数组或提供者类名。

    • get(string $id, array $constructorArguments = [], string $inThisScope = ''): 对象

      如果提供了额外的参数,即使配置了Container::SCOPE_SHARED作用域,也会生成新的实例。

      如果$inThisScope不为空,则新实例将特别在提供的范围内共享。

      参数可能包含像@service_id@%parameter%这样的引用。.

    • has(string $id, bool $withAutowiring = CONTAINER_DEFAULT_VALUE)

      如果$withAutowiring明确设置为truefalse,则会打开或关闭此特定检查中的自动注册此服务id,如果类名匹配。否则,使用容器的自动连接设置。

    • one(string $id, array $constructorArguments = []): 对象

      即使配置为共享服务,带有或不带有新参数,也可以获取一个新的实例。

    • run(callable|array $callable, array $callableArguments = []): 混合型

      使用提供的参数执行可调用对象。支持伪可调用对象,如['@cacheDriver@', '%cache.setroot.method%']

  • 与定义相关的API

    • add(string|array $id, string|callable $classOrClosure, array $constructorArguments = []): this

      将服务定义或定义数组添加到容器中。可以使用可调用对象代替类名来创建实例。

      $constructorArguments用于构造函数。

      可以通过将$classOrClosure定义为服务引用(即@serviceId@)来实现别名。

    • set(string|array $nameOrArray, string|array $valueStringOrArray = ''): this

      将参数或参数数组设置到容器中。参数名称可以是'parameter.name.string'的格式,它将被转换为多维度数组。

    • map(string|array $nameOrArray, string $toName = ''): this

      将接口名称映射到类名。还支持将类名映射到另一个类名(子类),映射到服务引用或参数引用。如果$nameOrArray是数组,则支持批量模式。

      注意 如果$nameOrArray是类名或接口名,则不要以反斜杠开头。

    • load(string|array $fileOrArray): this

      将定义数组或定义文件加载到容器中。格式为*.s*.php的文件名将被视为PHP格式的服务定义文件。*.p*.php是PHP格式的参数文件。*.m*.php是映射文件。

      此库已知文件后缀'.php|.json|.xml'。

    • share(bool $status = true): this

      设置容器级别的默认作用域。true将设置为Container::SCOPE_SHAREDfalse设置为Container::SCOPE_SINGLE

    • auto(bool $switchOn): this

      打开(true)或关闭(false)自动连接

    • addMethod(string $methodName, array $methodArguments = []): this

      在服务实例化后立即执行此$methodName。此addMethod()必须跟在add()或其他addMethod()setScope()调用之后。可以链式调用多个addMethod()

      $methodName可以是参数引用。$methodArguments可以包含参数或服务引用。

    • setScope(string $scope): this

      为链中的上一个添加的服务设置作用域。有两个预定义的作用域常量,共享作用域Container::SCOPE_SHARED和单例作用域Container::SCOPE_SINGLE

    • dump(bool $toScreen = true): true|string

      将打印出所有定义和映射或返回输出。

  • 扩展相关API

    • addExtension(ExtensionAbstract $extension): this

      显式将扩展加载到容器中。

      注意 调用扩展相关方法将自动加载相应的扩展。

    • setTag(string|array $tagOrTagArray): this

      可标记扩展 设置/替换容器标签。标签可用于选择性地加载定义文件或定义提供者。

    • hasTag(string|array $tagOrTagArray): bool

      可标记扩展 检查容器中是否存在标签。如果一个标签匹配,则返回true,否则返回false

      if ($container->hasTag('PRODUCTION')) {
          $container->load('./productDefinitions.php');
      } else {
          $container->load('./developDefinitions.php');
      }
    • setDelegate(DelegatorInterface $delegator): this

      DelegateExtension 设置代理。依赖项将在代理中查找,而不是在容器中查找。容器本身将被注入到代理的容器池中。

      由于自动绑定与代理设计冲突,除了最后一个容器外,池中的容器将自动关闭自动绑定。

      use Phossa\Di\Delegator;
      
      // create the delegator
      $delegator = new Delegator();
      
      // other container register with the delegator
      $delegator->addContainer($otherContainer);
      
      /*
       * register $container with its auotwiring status unchanged (last container)
       * but $otherContainer's autowiring will be forced off
       */
      $container->setDelegate($delegator);
      
      // dependency will be resolved in the order of $otherContainer, $container
      // ...
    • addDecorate(string $ruleName, string|callable $interfaceOrClosure, array|callable $decorateCallable): this

      DecorateExtension 向容器添加对象装饰规则。

    • addProvider(string|ProviderAbstract $providerOrClass): this

      ProviderExtension 通过提供器类名或提供器对象向容器添加定义提供器。

依赖项

  • PHP >= 5.4.0

  • phossa/phossa-shared >= 1.0.6

  • container-interop/container-interop ~1.0

许可协议

MIT许可协议