PHP Dependency – A PHP Dependency Injection Framework

by Ryan on September 9, 2010

PHP-Dependency (Pd) is a dependency injection framework and container written in PHP. One of the main features of PHP-Dependency is that it supports class reflection, which means you do not have to maintain ANY configuration files.

Class MyExample() {
 
    private $_database;
 
    /**
     * This is the constructor, the database is automatically injected
     * due to the PdInject DocBlock.
     *
     * @PdInject database
     */
    public function __construct($database) {
        $this->_database = $database;
    }
 
}

That’s it. No configuration other than that one line Doc Block.

Documentation

Download

PHP-Dependency.tar.gz
PHP-Dependency on GitHub

Setup

You need to make sure that the Pd library is on your include path.

set_include_path(
    get_include_path() . PATH_SEPARATOR .
    '/path/to/php-dependency/library/'
);

Autoloader
If you are using a PHP framework with an autoloader then just make sure the library/Pd is on the include path and tell the autoloader to use ‘Pd’ as the class prefix/namespace.

No Autoloader
If you are not using an autoloader then you can just require the class. All classes follow the naming conventions from Zend Coding Standards. See Below.

require_once 'Pd/Container.php';
$container = Pd_Container::get();

The Container

The container holds all of your dependencies. Adding dependencies to the container is very simple.

$database = new Database("mysql://user:password@server/database");
 
$container = Pd_Container::get();
$container->dependencies()->set('database', $database);

You can put anything into the container. Objects, arrays, strings, and even anonymous functions.

Your Classes

Class dependencies are defined by PHP DocBlocks. Every Pd command begins with @PdInject and is followed by a key:value type syntax. The most common command will be injecting a dependency by name, which just requires the dependency name.

Class Book {
 
    private $_database;
 
    /**
     * @PdInject database
     */
    public function setDatabase($database) {
        $this->_database = $database;
    }
 
}

Key:Value Commands and Examples

Key:Value commands can be used with the @PdInject DocBlock.

new:ClassName – This will create a brand new instance of ClassName and inject it into the object. Example

method:MethodName – This will use MethodName to inject the dependency. Example

property:PropertyName – This will inject the dependency as PropertyName. Example

constructor:number – Used for constructor injection. Example

force:true – Used to force injection. Example

Creating Objects

To create objects use the Pd_Make class.

/* @var $book Book */
$book = Pd_Make::name('Book');

This is the same as doing

$book = new Book();
$book->setDatabase($database);

Note: Use the @var doc line above the Pd_Make command. This will tell your IDE that $book is an instance of the Book class, which will allow the IDE to auto complete any calls/usages of $book in your code.

Non Reflective Map Building

If you do not want to use class reflection the Pd library contains a map builder that works off arrays. The commands are somewhat different from the key:value syntax, but they are self explanatory and easy to follow. Here is an example:

$builder = new Pd_Map_Builder_Array();
 
$builder->add(array(
    'dependencyName' => 'database',
    'injectWith' => 'method',
    'injectAs' => 'setDatabase',
));
$builder->add(array(
    'dependencyName' => 'apple',
    'injectWith' => 'constructor',
    'injectAs' => 1
));
$builder->add(array(
    'injectWith' => 'property',
    'injectAs' => 'theService',
    'force' => true,
    'newClass' => 'Service_Class',
));
 
Pd_Container::get()->maps()->set('MyClass', $builder->map());

This type of map building should only be used when needed.

Unit Testing

Pd includes a test suite that will unit test the library. Running this test is simple:

[ryan@localhost]$ cd /path/to/php-dependency/tests/
[ryan@localhost]$ phpunit AllTests
PHPUnit 3.4.11 by Sebastian Bergmann.
 
....................................................
 
Time: 0 seconds, Memory: 7.00Mb
 
OK (52 tests, 59 assertions)

Testing within your application
If you would like to test the library within your application, then inside of your test suite you can include the Pd test suite. To do this your AllTests file should look something like this:

require_once '/usr/share/php/php-dependency/tests/AllTests.php';
 
class AllTests extends PHPUnit_Framework_TestSuite {
 
    public static function suite() {
 
        $suite = new AllTests();
 
        // your unit test suites here
 
        // test php-dependency
        $suite->addTestSuite('PdTests_AllTests');
 
        return $suite;
    }
}
12 Responses leave one →
  1. Theodore R. Smith permalink
    September 22, 2010

    Hi,

    I could *really* use an example app of this…

    Ted

  2. October 10, 2010

    Hey Theodore? What do you mean? Do you want to see more examples? Take a look under the section titled “Key:Value Commands ” in this blog post. It contains 5 or so different examples.

  3. Jason permalink
    November 4, 2010

    This is pretty cool. Nice work.
    I have today learned about dependency injection, AND a nifty library to facilitate it.
    Thanks :)

  4. Alan permalink
    November 13, 2010

    I’m obviously doing something stupid, but when I try and use this I get :
    Fatal error: Class ‘Pd_Container’ not found in /var/www/test.php on line 17
    afaict I’m just using the sample you pasted, so maybe it’s a problem of my PHP config.. shouldn’t I need to ‘include’ some file? Would appreciate if someone can point me in the right direction :)

    set_include_path(get_include_path()
    . PATH_SEPARATOR .’/var/include/PHP-Dependency/library/’);
    $database = new Database(‘localhost’,'web’,'password’);
    $container = Pd_Container::get();
    $container->dependencies()->set(‘database’, $database);

    I think it would be helpful to be able to see a ‘full’ example

  5. November 17, 2010

    Hi Alan,

    I have updated the code to show an example for those not using an Autoloader. What you want to do is

    require ‘Pd/Container.php’
    $container = Pd_Container::get();

  6. Taylor Otwell permalink
    November 23, 2010

    Very nice work! I’ve successfully implemented this in CodeIgniter 2.0 (as a library) without much tweaking, and I’m really digging it! I come from a .NET background and have used IoC containers in that environment, and I’ve really been wanting to bring it into my freelance PHP development.

    I’m going to try to do some slight hacking in the CodeIgniter framework to allow for constructor injection on the controllers themselves. I don’t think it should be too much work.

  7. Howard permalink
    December 24, 2010

    The simplicity of your DIF is beautiful, but I’m worried about Reflection performance while also loving the manner in which you document the dependencies when using Reflection. Is there any chance of having a utility that can build a static map from the reflection? We could use reflection during development and build the static map to run on the production servers.

  8. December 24, 2010

    Hey Howard,

    Thanks for the idea – I will add that to my list. I have been meaning to write this feature for a while. If you follow the project on github you’ll get an update for when I do add it. In the mean time, you can build your maps by using arrays (non reflective map building). This is ugly, but it will give you a performance boost if needed.

    The whole idea of this framework is to favor easy of use over speed, hence the reflection.

    Although reflection is one of the slower processes in PHP, I have this framework running on a few production sites (small, about 1kuniques/day) and responses times are fine (under 500ms).

    Ryan

  9. Howard permalink
    January 5, 2011

    Hi Ryan,

    I agree that DIF’s should favor development simplicity and code beauty over raw performance during development, but I hope to use your excellent framework (I find it far more digestible and sensible than the Symfony DI container) in a very high volume project (200k uniques per day) and we want to squeeze every last drop of performance possible (that’s why our autoload process uses an array map too instead of just the include_path).

    I’m currently only using the Reflection method because I really subscribe to the philosophy upon which it was built, and the way I figure it if you create a map generation from that then it behaves as a “drop in upgrade” and one single development path allows both performance and ease of use.

  10. August 30, 2011

    Thank you for this piece of software.

    It was really helpful for me for understanding the use of DI in detail.
    I’ve used this framework for a wordpress plugin that i have written and which i will publish soon.

    I’ve forked your repo at github and did some slightly changes.
    So maybe you want to merge my changes into the master.

    thanks again
    Benjamin Carl

  11. September 8, 2011

    Sounds good Benjamin. Just issue a pull requests and I’ll merge the code in.

    Make sure you write tests… I automatically reject any untested pull requests :)

Trackbacks and Pingbacks

  1. PHP Dependency Injection

Leave a Reply

Note: You can use basic XHTML in your comments. Your email address will never be published.

Subscribe to this comment feed via RSS