phossa / phossa-di
A fast and full-fledged dependency injection library for PHP. It supports auto wiring, container delegation, object decorating, definition provider, definition tags, service scope and more.
Installs: 38
Dependents: 0
Suggesters: 0
Security: 0
Stars: 2
Watchers: 1
Forks: 0
Open Issues: 0
pkg:composer/phossa/phossa-di
Requires
- php: >=5.4.0
- container-interop/container-interop: ~1.0
- phossa/phossa-config: ^1.0.7
- phossa/phossa-shared: >=1.0.6
Requires (Dev)
- phpunit/phpunit: 4.*
Suggests
- phossa/phossa-cache: If you want to use config cache
This package is not auto-updated.
Last update: 2025-10-26 00:09:14 UTC
README
See new lib at phoole/di Introduction
Phossa-di is a fast, feature-rich and full-fledged dependency injection library for PHP. It supports auto wiring, container delegation, object decorating, definition provider, definition tagging, object scope and more.
It requires PHP 5.4 and supports PHP 7.0+, HHVM. It is compliant with PSR-1, PSR-2, PSR-4 and coming PSR-5, PSR-11.
Getting started
-
Installation
Install via the
composerutility.composer require "phossa/phossa-di=1.*"or add the following lines to your
composer.json{ "require": { "phossa/phossa-di": "^1.0.6" } } -
Simple usage
You might have serveral simple classes like these or third party libraries, and want to make avaiable as services.
class MyCache { private $driver; public function __construct(MyCacheDriver $driver) { $this->driver = $driver; } // ... } class MyCacheDriver { // ... }
Now do the following,
use Phossa\Di\Container; $container = new Container(); // use the 'MyCache' classname as the service id if ($container->has('MyCache')) { $cache = $container->get('MyCache'); }
With auto wiring is turned on by default, the container will look for the
MyCacheclass and resolves its dependency injection automatically when creating the$cacheinstance. -
Use with definitions
Complex situations may need extra configurations. Definition related methods
add(),set(),map(),addMethod()andsetScope()etc. can be used to configure services.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');
-
Service definitions
Serviceis defined using APIadd($id, $classOrClosure, array $arguments)and later can be refered in other definition with service reference@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 reference in the format of
@service_id@can be used anywhere where an object is appropriate, such as in the argument array or construct a pseudo callable,// will resolve this ['@cache@', 'setLogger'] to a real callable $container->run(['@cache@', 'setLogger'], ['@logger@']);
-
Parameter definitions
Parameter can be set with API
set($name, $value). Parameter reference is '%parameter.name%'. Parameter reference can point to a string, another parameter or even a service reference.// 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%' ] );
-
-
Callable instead of class name
Callable can be used instead of class name to instantiate a service.
// ... $container->add('cacheDriver', function() { return new \MyCacheDriver(); });
-
Definition files
Instead of configuring
$containerin the code, you may put your service and parameter definitions into one definition file or several seperated files (seperating parameter definitions from service definitions will give you the benefit of loading different parameters base on different situations.).PHP, JSON, XML file formats are supported, and will be detected automatically base on the filename suffixes.
A service definition file
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 // ... ];
The parameter definition file
definition.param.php<?php /* file name '*.p*.php' indicating PARAMETER definitions in PHP format */ return [ 'tmp.dir' => '/var/local/tmp', 'cache.root' => '%tmp.dir%', // ... ];
Or you may combine these files into one
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', // ... ], ];
You may load definitions from files now,
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');
Features
-
Auto wiring is the ability of container instantiating objects and resolving its dependencies automatically. The base for auto wiring is the PHP function parameter type-hinting.
By reflecting on the class, constructor and methods, phossa-di is able to find the right class for the object (user need to use the classname as the service id) and right class for the dependencies (type-hinted with the right classnames).
To fully explore the auto wiring feature, users may map interfaces to classnames or service ids as follows,
// 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%');
Or load mapping files,
$container->load('./defintion.map.php');
Auto wiring can be turned on/off. Turn off auto wiring will enable user to check any defintion errors without automatically loading.
// turn off auto wiring $container->auto(false); // turn on auto wiring $container->auto(true);
-
According to Interop Container Delegate Lookup, container may register a delegate container (the delegator), and
-
Calls to the
get()method should only return an entry if the entry is part of the container. If the entry is not part of the container, an exception should be thrown (as requested by theContainerInterface). -
Calls to the
has()method should only return true if the entry is part of the container. If the entry is not part of the container, false should be returned. -
If the fetched entry has dependencies, instead of performing the dependency lookup in the container, the lookup is performed on the delegate container (delegator).
-
Important By default, the lookup SHOULD be performed on the delegate container only, not on the container itself.
phossa-di fully supports the delegate feature.
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');
-
-
Object decorating is to apply decorating changes (run methods etc.) right after the instantiation of a service object base on certain criteria such as it implements an interface.
// any object implementing 'LoggerAwareInterface' should be decorated $container->addDecorate( 'setlogger', // rule name 'Psr\\Log\\LoggerAwareInterface', // NO leading backslash ['setLogger', ['@logger@']] // run this method );
Object decorating saves user a lot of definition duplications and will apply to future service definitions. Phossa-di also supports a tester callable and a decorate callable as follows,
$container->addDecorate('setlogger', function($object) { return $object instanceof \Psr\Log\LoggerAwareInterface; }, function($object) use($container) { $object->setLogger($container->get('logger')); } );
-
Most developers use different defintions or configurations for development or production environment. This is achieved by put definitions in different files and load these files base on the container tags.
Tag is also used in definition provider.
// 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'); }
-
Definition provider is used to wrap logic related definitions into one entity. These definitions will be loaded into container automaitcally if a call to container's
has()orget()and found the definition in this provider.<?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' ]); } }
The provider
ProductionDbProvidershould be added into container before any calls tohas()orget().// 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');
Or during the container instantiation
$container = new Container('./defintions.php', [ ProductionDbProvider::class, TestDbProvider::class ]);
-
By default, service objects in the container is shared inside the container, namely they have the scope of
Container::SCOPE_SHARED. If users want different instance each time, they may either use the methodone()or define the service withContainer::SCOPE_SINGLEscope.// 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
Or define it as
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
To make all service objects non-shared, set the container's default scope to
Container::SCOPE_SINGLEas follows,// 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
Public APIs
-
PSR-11 compliant APIs
-
get(string $id): objectGetting the named service from the container.
-
has(string $id): boolCheck for the named service's existence in the container.
-
-
Extended APIs by phossa-di
-
__construct(string|array $definitionArrayOrFile = '', array $definitionProviders = [])$defintionArrayOrFilecan be a defintion file or definition array.$definitionProviderscan be array ofProviderAbstractobjects or provider classnames. -
get(string $id, array $constructorArguments = [], string $inThisScope = ''): objectIf extra arguments are provided, new instance will be generated even if it was configured with a
Container::SCOPE_SHAREDscope.If
$inThisScopeis not empty, new instance will be specificly shared in the provided scope.Arguments may contain references like
@service_id@or%parameter%. -
has(string $id, bool $withAutowiring = CONTAINER_DEFAULT_VALUE)If
$withAutowiringis explicitly set totrueorfalse, auto registering of this service $id if classname matches will be turned ON or OFF for this specific checking. Otherwise, use the container's auto wiring setting. -
one(string $id, array $constructorArguments = []): objectGet a new instance even if it is configured as a shared service with or without new arguments.
-
run(callable|array $callable, array $callableArguments = []): mixedExecute a callable with the provided arguments. Pseudo callable like
['@cacheDriver@', '%cache.setroot.method%']is supported.
-
-
Definition related APIs
-
add(string|array $id, string|callable $classOrClosure, array $constructorArguments = []): thisAdd a service definition or definitions(array) into the container. Callable can be used instead of classname to create an instance.
$constructorArgumentsis for the constructor.Aliasing can be achieved by define
$classOrClosureas a service reference, namely@serviceId@. -
set(string|array $nameOrArray, string|array $valueStringOrArray = ''): thisSet a parameter or parameters(array) into the container. Parameter name can be the format of 'parameter.name.string', it will be converted into multi-dimention array.
-
map(string|array $nameOrArray, string $toName = ''): thisMap an interface name to a classname. Also mapping of a classname to another classname (child class), map to a service reference or to a parameter reference is ok. Batch mode if
$nameOrArrayis an array.Note No leading backslash for the
$nameOrArray, if it is a classname or interface name. -
load(string|array $fileOrArray): thisLoad a definition array or definition file into the container. Definition filename with the format of
*.s*.phpwill be considered as a service definition file in PHP format.*.p*.phpis a parameter file in PHP format.*.m*.phpis a mapping file.File suffixes '.php|.json|.xml' are known to this library.
-
share(bool $status = true): thisSet container-wide default scope.
trueto set toContainer::SCOPE_SHAREDandfalseset toContainer::SCOPE_SINGLE -
auto(bool $switchOn): thisTurn on (true) or turn off (false) auto wiring.
-
addMethod(string $methodName, array $methodArguments = []): thisExecute this
$methodNameright after the service instantiation. ThisaddMethod()has to follow aadd()or anotheraddMethod()orsetScope()call. MultipleaddMethod()s can be chained together.$methodNamecan be a parameter reference.$methodArgumentscan have parameter or service references. -
setScope(string $scope): thisSet scope for the previous added service in the chain of
add()oraddMethod(). There are two predefined scope contants, shared scopeContainer::SCOPE_SHAREDand single scopeContainer::SCOPE_SINGLE. -
dump(bool $toScreen = true): true|stringWill print out all the definitions and mappings or return the output.
-
-
Extension related APIs
-
addExtension(ExtensionAbstract $extension): thisExplicitly load an extension into the container.
Note Calling extension related methods will automatically load corresponding extensions.
-
setTag(string|array $tagOrTagArray): thisTaggableExtension set/replace container tag(s). Tags can be used to selectly load definition files or definition providers.
-
hasTag(string|array $tagOrTagArray): boolTaggableExtension check the existence of tag(s) in the container. One tag match will return
true, otherwise returnfalseif ($container->hasTag('PRODUCTION')) { $container->load('./productDefinitions.php'); } else { $container->load('./developDefinitions.php'); }
-
setDelegate(DelegatorInterface $delegator): thisDelegateExtension set the delegator. Dependency will be looked up in the delegator instead of in the container. The container itself will be injected into delegator's container pool.
Since auto wiring is conflict with the delegation design, auto wiring will be turned off automatically for containers in the pool except for the last one.
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): thisDecorateExtension adding object decorating rules to the container.
-
addProvider(string|ProviderAbstract $providerOrClass): thisProviderExtension add definition provider to the container either by provider classname or a provider object.
-
Dependencies
-
PHP >= 5.4.0
-
phossa/phossa-shared >= 1.0.6
-
container-interop/container-interop ~1.0