dev-master
9999999-dev http://github.com/catacgc/juice-di-containerSmall, fast, feature rich Dependency Injection for PHP 5.2
MIT
The Requires
- php >=5.2
by Catalin Costache
ioc dependency di injection
Small, fast, feature rich Dependency Injection for PHP 5.2
The container is comprised of a single file, src/Container.php, (*1)
For PHP 5.2 you can use curl / wget / git to download that file and simple require it, (*2)
For PHP 5.3 on, you can use composer:, (*3)
{ "catacgc/juice-di-container": "dev-master" }
After cloning the repo, you can run the tests with phpunit:, (*4)
phpunit -c phpunit.xml.dist
require 'src/Container.php'; $container = new JuiceContainer();
The container stores parameters as key => value, but it's main benefit is the runtime wiring of services, (*5)
The services are created using two special container values:, (*6)
$container['mysql_host'] = 'localhost'; $container['mysql_user'] = 'username'; $container['mysql_pass'] = 'password'; $container['mysql_port'] = 3306; echo $container['mysql_port'];
Because creating factory methods for every service is tedious, the container specially handles a JuiceDefinition type that is handy when creating complex services, (*7)
You can reference other services or parameters in a definition using @service_id notation in all of JuiceDefinition method arguments, (*8)
// simple class $container['conn'] = new JuiceDefinition('Connection'); // => new Connection() // or using the fluent interface to do the same $container['conn'] = JuiceDefinition::create('Connection') // replacing the class name $container['conn'] = JuiceDefinition::create('Connection')->className('ChangedMyMindConnection') // providing arguments JuiceDefinition::create('Connection', array('username', 'password')) // replacing arguments JuiceDefinition::create('Connection')->arguments(array('username1', 'password1')) // replacing specific argument JuiceDefinition::create('Connection')->argument(0, 'new_username') // calling methods JuiceDefinition::create('Connection', array('user', 'pass')) ->call('setDb', array('db_name'))
$container['conn'] = new JuiceDefinition('MysqlConnection', array('@mysql_user', '@mysql_password')); $container['dbal'] = new JuiceDefinition('Dbal', array('@conn'));
Now calling $dbal = $container['dbal']
is equivalent with calling, (*9)
$connection = new MysqlConnection('username', 'password'); $dbal = new Dbal($connection);
$container['conn'] = new JuiceDefinition('Connection'); $container['dbal'] = JuiceDefinition::create('Dbal')->call('setConnection', array('@conn'))
$container['conn'] = JuiceDefinition::create('MysqlConnection'); // now in another module, in its configuration $connDef = $container->raw('conn'); $connDef->className('MysqlWrapperConnection');
Every callable that is passed to the container will be called, when retrieved by the client, with the container instance as it's only parameter and the return value will represent the actual service / parameter for the associated id., (*10)
$container['db'] = array('Factory', 'createDbConnection'); $pdoObject = $container['db']; //actually calls the factory method
where the factory will look like:, (*11)
class Factory { public static function createDbConnection($container) // <- note the container parameter { return new PDO( sprintf('mysql:host=%s;dbname=%s', $container['mysql_host'], $container['mysql_dbname']), $container['mysql_user'], $container['mysql_pass'] ); } }
$container['mysql_default_connection'] = JuiceDefinition::create('MysqlConnection', array('@mysql_username', '@mysql_password')); $container['connection'] = '@mysql_default_connection';
Now $container['connection']
and $container['mysql_default_connection']
will refer to the same service instance, (*12)
There are times when your intent is to actually add a callback as a real parameter, not creating a service definition, (*13)
For this purpose there is a special wrapper type JuiceParam that you can use:, (*14)
$container['invalid_callback'] = 'strpos'; echo $container['invalid_callback'];
Gives an error because for php strpos is a callable type and the container will try to call it with strpos($container) to retrieve the supposed service instance, (*15)
$container['callback'] = new JuiceParam('strpos'); echo $container['callback']; //now echoes the expected 'strpos' string
After a parameter or a service is retrieved from the container, you will not be able to add, overwrite or extend services anymore. Eg:, (*16)
$container['param'] = 1; echo $container['param']; $container['param'] = 2; //throws exception
This is done on purpose to enforce the configuration before usage and stable behaviour: once you used a service, you can count that you have the same service throughout the application., (*17)
For testing purposes you can overwrite this behaviour by calling $container->unlock();
, (*18)
The default behaviour is to retrive the same service instance on each call., (*19)
If you want to create new service instances on the fly use the build method:, (*20)
$conn1 = $container->build($container->raw('conn')); $conn2 = $container->build($container->raw('conn'));
/** * Configuration */ $container = new JuiceContainer(); $container['cache_dir'] = '/tmp/cache'; $container['memcache_host'] = 'localhost'; $container['memcache_port'] = 11211; $container['main_cache'] = JuiceDefinition::create('Memcache') ->call('connect', array('@memcache_host', '@memcache_port')); $container['slow_cache'] = JuiceDefinition::create('FileCache') ->arguments(array('@cache_dir')); $container['two_level_cache'] = JuiceDefinition::create('TwoLevelCache') ->arguments(array('@main_cache', '@slow_cache')); $container['cache'] = '@two_level_cache'; /** * Usage */ $cache = $container['cache']; if ($data = $cache->load('expensive_operation_id')) { //cache hit return; } $cache->save('expensive_operation_id', do_expensive_operation(), 60);
Small, fast, feature rich Dependency Injection for PHP 5.2
MIT
ioc dependency di injection