PHP Dependency – A PHP Dependency Injection Framework
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
- Setup
- The Container
- Your Classes
- Key:Value Commands and Examples
- Creating Objects
- Non Reflection
- Unit Testing
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; } }

Hi,
I could *really* use an example app of this…
Ted
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.
This is pretty cool. Nice work.
I have today learned about dependency injection, AND a nifty library to facilitate it.
Thanks
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
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();
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.
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.
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
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.
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
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