Skip to content

Repository files navigation

di

Build StatusScrutinizer Code QualityCode ClimatePHP 7Latest Stable VersionLicense

Slim, powerful and full compatible PSR-11 dependency injection library for PHP.

It builds upon the versatile phoole/config library and supports object decorating, object scope and more. It requires PHP 7.2+. It is compliant with PSR-1, PSR-4, PSR-11 and PSR-12.

Installation

Install via the composer utility.

composer require "phoole/di"

or add the following lines to your composer.json

{
"require": {
"phoole/di": "1.*"
}
}

Usage

  • With configuration from files or definition array

    usePhoole\Di\Container;
    usePhoole\Config\Config;
    usePhoole\Cache\Cache;
    usePhoole\Cache\Adaptor\FileAdaptor;
    $configData = [
    // service definitions'di.service' => [
    // classname & constructor arguments'cache' => [
    'class' => Cache::class,
    'args' => ['${#cacheDriver}'] // optional
    ],
    // use classname directly'cacheDriver' => FileAdaptor::class
    ],
    // methods to run after each object initiation'di.after' => [
    // a callable, takes THE object as parameterfunction($obj) { echo"ok"; },
    // will be converted to $obj->setLogger($logger)'setLogger',
    ]
    ];
    // inject configurations into container$container = newContainer(newConfig($configData));
    // get service by id 'cache' (di.service.cache)$cache = $container->get('cache');

    Container related configurations are under the node di and service definitions are under the di.service node.

Features

  • References

    References in the form of '${reference}' can be used to refer to predefined parameters from the config or services in the container.

    Characters of '$', '{', '}', '.' are not allowed in reference name. Characters of '#', '@' have special meanings, such that should not be part of normal service names.

    • Parameter references like ${system.tempdir}

      $config = [
      ...
      // use predefined 'sytem.tmpdir' in arguments etc.'di.service.cacheDriver' => [
      'class' => FileAdaptor::class,
      'args' => ['${system.tmpdir}'],
      ],
      ...
      ];

      See phoole/config reference for detail. Parameter references are read from configuration files or array.

    • Service references like ${#cache}

      Service object reference in the form of ${#serviceId} can be used to referring a service instance in the container.

      $configData = [
      ...
      'di.service' => [
      'cache' => [
      'class' => Cache::class,
      'args' => ['${#cacheDriver}'] // object reference
      ],
      'cacheDriver' => ...
      ...

      Two reserved service references are ${#container} and ${#config}. These two are referring the container instance itself and the config instance it is using. These two can be used just like other service references.

    • Using references

      References can be used anywhere in the configuration.

      $confData = [
      // methods executed after ALL object initiation'di.after' => [
      [['${#logger}', 'notice'], ['object created using ${log.facility}']]
      ]
      ];
  • Object decorating

    Object decorating is to apply decorating changes (executing methods etc.) right before or after the instantiation of a service instance.

    • Decorating methods for individual instance only

      $config = [
      'di.service' => [
      ...
      'cache', [
      'class' => '${cache.class}',
      'args' => ['${#cachedriver}'], // constructor arguments'before' => [
      [['${#logger}', 'info'], ['before initiating cache']], // $logger->info(...)
      ],
      'after' => [
      'clearCache', // $cache->clearCache() method
      ['setLogger', ['${#logger}']], // $cache->setLogger($logger), argument is optional
      [['${#logger}', 'info'], ['just a info']], // $logger->info(...)function($cache) { // a callable takes object in parameter
      }, ]
      ],
      ...
      ]
      ];

      By adding before or after section into the cache service definition in the form of [callableOrMethodName, OptionalArgumentArray], these methods will be executed right before/after cache instantiation.

      callableOrMethodName here can be,

      • method name of initiated object

        ...'after' => [
        // $obj->setLogger($logger), $logger will be injected automatically'setLogger', // object implementing 'LoggerAwareInterface'
        ],
        ...
      • a valid callable which takes initiated object as parameter

        ...'after' => [
        // callable takes initiated object as parameterfunction($obj) {
        },
        ],
        ...
      • a pseudo callable with references (after resolving the references, it is a valid callable).

        ...'after' => [
        // a pseudo callable with references
        [['${#logger}', 'info'], ['just a info']], // $logger->info(...)
        ],
        ...

      OptionalArgumentArray here can be,

      • empty

      • array of values or references

    • Common decorating methods for all instances

      $configData = [
      // before all instances initiated'di.before' => [
      [['${#logger}', 'info'], ['before create']],
      ],
      // after methods for all instances'di.after' => [
      ['setLogger', ['${#logger}']], // arguments are optional'setDispatcher', // simple enough, set event dispatcher
      ],
      ];

      Common methods can be configured in the 'di.before' or 'di.after' node to apply to all the instances right before or after their instantiation.

  • Object scope

    • Shared objects and new objects

      By default, service instances in the container are shared inside the container. If users want different instance each time, they may just append '@' to the service id.

      // cache service by default is in shared scope$cache1 = $container->get('cache');
      // get again$cache2 = $container->get('cache');
      // samevar_dump($cache1 === $cache2); // true// get a NEW cache instance$cache3 = $container->get('cache@');
      // different instancesvar_dump($cache1 !== $cache3); // true// but both share the same cacheDriver dependent servicevar_dump($cache1->getAdaptor() === $cache3->getAdaptor()); // true
    • Object scope

      You may get an instance in your own scope as follows

      // no scope$cache1 = $container->get('cache');
      // in `myScope`$cache2 = $container->get('cache@myScope');
      // different instancesvar_dump($cache1 !== $cache2); // true// shared in myScope$cache3 = $container->get('cache@myScope');
      var_dump($cache2 === $cache2); // true

      Service references can also have scope defined as follows,

      $container->set('cache', [
      'class' => Cache::class,
      'args' => ['${#driver@myScope}'] // use driver of myScope
      ]);
  • Static access

    • Access predefined services statically

      Services in the container can also be access through a static way. But get and has are reserved.

      // after container initiated$container = newContainer(newConfig(...));
      // equals to $cache = $container->get('cache')$cache = Container::cache();
      // if myservice defined and invokable$obj = Container::myservice('test');
    • Initiating object by taking advantage of dependency injection

      usePhoole\Cache\Cache;
      usePsr\Log\LoggerAwareTrait;
      usePsr\Log\LoggerAwareInterface;
      class MyClass implements LoggerAwareInterface
      {
      use LoggerAwareTrait;
      publicfunction__construct(Cache$cache)
      {
      }
      }
      // $cache will be injected automatically$obj = Container::create(MyClass::class);
      // also 'setLogger' will be executed if defined in 'di.after' section$logger = $obj->getLogger();
  • Autowiring and auto injection

    • Parameter autowiring (resolving)

      Parameters of a constructor/callable will be resolved by looking

      • exists in the classmap (service objects created already) ?

      • classname known to the script (class defined already) ?

    • Auto injection

      Instead of using 'annotation', we encourage of using *AwareInterface for your own classes' dependency injection.

      usePsr\Log\LoggerAwareTrait;
      usePsr\Log\LoggerAwareInterface;
      class MyOwnClass implements LoggerAwareInterface
      {
      use LoggerAwareTrait;
      ...
      }
      // create your object with arguments$obj = Container::create(MyOnwClass::class, [...]);
      // $logger injected by the container automatically$logger = $obj->getLogger();

      Container has all the common injection predefined in the di.after section

      $config = [
      'di.after' => [
      'setLogger', // logger aware'setCache', // cache aware'setDispatcher', // event aware'setContainer', // container aware
      ...
      ],
      ];
      ...
  • ContainerAWareInterface

    Both ContainerAWareInterface and ContainerAWareTrait available.

APIs

  • Container related

    • get(string $id): object from ContainerInterface

    • has(string $id): bool from ContainerInterface

      $id may have @ or @scope appended.

Testing

$ composer test

Dependencies

  • PHP >= 7.2.0

  • phoole/config >= 1.*

License

About

Slim, powerful and full compatible PSR-11 dependency injection library for PHP

Topics

Resources

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages