This article is part of a series where I will go into great detail about Dependency Injection (DI) and how it applies to PHP. I will layout exactly how I think DI should be used in PHP with the creation of a new code project as the result.
In this article I will go over what I am looking for in terms of functionality and characteristics in an approach to applying DI in PHP.
Where Do You Draw The Line?
Dependency Injection (DI) can be implemented with many different levels of granularity. From the simple choice of saying users of this one class must pass dependencies to it. To the use of a full DI framework that you use to create your application from the ground up.
Also what you decide is a dependency is a granular choice. It can be coarse grained to a sub systems like authentication, logging or data access. To the fine grained; for example, let’s say you have a class that performs a calculation. You could consider the operator to be a dependency and have that injected.
Where Do I Draw The Line?
I believe if you prescribe to the notion that DI is good then, you need to do it thought your whole application.
If you are going to do it thought your whole application I believe you need to use a framework.
I want code isolation, to allow for true unit tests. So my dependencies are as granular as to allow me code isolation.
I want to avoid arbitrarily having to extend a base class to gain functionality for reasons of code usability. So my dependencies are as granular as to allow for composition over inheritance.
What Do I Want From a DI Framework?
Do It the PHP Way
I believe that there is a PHP way of doing things. PHP developers have a style all of their own. This should be reflected in a PHP DI framework. A good example is PHP developers in general don’t like large XML configuration files or being required to “build” there code.
Manageability
All the features listed here are possible without a DI framework. But it is not possible while keeping everything manageable for large projects. This framework will be successful if it is easier and faster to write your applications with it than without it.
A DI Framework, Not an Application/Web Framework
A DI framework will provide a well defined way to do DI with a set of generic and reusable libraries and its scope will be limited to that.
Testability
Unit Testing
I want to be able to write my code in a way that allows for true code isolation. The framework will also be useful in assisting testing.
Integration Testing
The framework will support me in testing the integration point between classes with all other dependencies being mocked.
System Testing
The framework will support the running of the complete application while still keeping the application isolated from the global namespace.
Stay Out Of My Code, If I Want
Allow me to use the framework in my code without having to have any hint of it in my code.
To save me from having to manage a large configuration file allow be to add as much meta-data to my classes in place of having it in a configuration.
Reduce Boilerplate Code
Boilerplate code is code that seen in many places but changes very little.
Help me from having to write the same code over and over or creating near empty files just to support DI or a DI framework.
High Quality Errors
In a project that adds magic you have to give me as much information as possible for when things do go wrong.
Standalone
The framework must be completely independent with no external dependencies required to use it.
DI Now, AOP Later
Aspect-oriented programming (AOP) is awesome. You often see AOP implemented as a feature of DI frameworks. That said I think it’s much more important to focus on DI functionality first then implement AOP once a solid DI foundation is built.
Performance
The framework must have the goal of having the smallest impact of performance as possible.
What Next?
In the next article I will go over each of the above items and give my reasoning’s.
This article is part of a series where I will go into great detail about Dependency Injection (DI) and how it applies to PHP. I will layout exactly how I think DI should be used in PHP with the creation of a new code project as the result.
Audience
This series is for people who understand the principles of DI but wants to start examining what an implementation should look like and why it should look that way.
Where is this all coming from?
So why do I feel the need to layout in great detail what I want from DI in PHP. It all stems from testing. Testing became a passion of mine while working at a company that had become fully committed to adding unit tests to all of its applications before I even joined.
Developers had all took brave stabs at adding test to the application but nothing stuck. The problem was that the code was so tightly coupled that unit testing became a non-starter.
Realizing that the tight coupling was the thing holding everyone back I looked for solutions. In my research I found that by far the best way to de-couple was the principle of dependency injection.
One problem was that hand coding all the dependencies plumbing would become un-manageable quickly in the large applications we were working on. To compound that problem there were no frameworks out in the wild that were suitable to help keep the plumbing manageable.
So I started to create my own DI framework. I was very encouraged by the Google Guice project. Only problem was that it is for Java. Using Guice as inspiration I created a PHP project using Guice as a philosophical inspiration only. I never looked at a line the Guice source code; as such I feel I created a PHP centric implementation.
I did a lot of research and worked very hard, the resulting project was very promising and everyone I showed it to got excited. Plans were in the works to start using it in all our applications.
I no longer work at that company and I can’t use the code as it was written on company time.
Getting It off My Chest
I want to share the knowledge I gained in the process of trying to solve the challenges that DI presents for real applications large and small. As well as show that the Guice approach is much more in line with PHP development values than say a Spring approach. This is all in the hopes that it will drive the community further to getting a true PHP style DI solution.
Advice vs. Opinion
This series I will be putting forward my honest opinion. The thinking will be based around the use of DI in PHP shops with many projects of differing sizes. As such my opinion will not be what everyone needs. This will not be a tutorial but I will provide links so that people new to the concepts can look them up.
When it comes to unit testing and reusablity code isolation is king. Code isolation in my mind means that an object is not required to reach out into the global namespace to do its’ work. Let’s look at some example code.
class Foo
{
private $_microtime;
public function __construct()
{
$this->_microtime = microtime(true);
}
public function bar()
{
return md5($this->_microtime);
}
}
This class is untestable because I can’t consistently know what should be returned from the method Foo::bar(). Here is a pragmatic way to solve this problem.
class Foo
{
private $_microtime;
public function __construct($microtime = null)
{
if($microtime === null)
{
$microtime = microtime();
}
$this->_microtime = $microtime;
}
public function bar()
{
return md5($this->_microtime);
}
}
And the test would look like:
class FooTest extends PHPUnit_Framework_TestCase
{
public function testBar()
{
$foo = new Foo(0.43019800);
$this->assertSame('4b8b3610138fe3fe8db433858ff18165', $foo->bar());
}
}
The result is a class that is now testable and equally easy to use in production. Also, the added code is new functionality and not tests specific.
This class would still be testable in integration tests but would become untestable in system tests without adding test specific code to the application. In my opinion, system tests that know the specific return value of a method like this would be moving into the world of White Box Testing.
Let’s take a look at a slightly different verion of our original code.
class Foo
{
private $_magic_key;
public function __construct()
{
$this->_magic_key = AppConfig::getInstance()->magic_key;
}
public function bar()
{
return md5($this->_magic_key);
}
}
The problem here is not that the code is untestable, assuming that AppConfig is easily manipulated, it’s that this simple utility class is needlessly coupled to the AppConfig class. We could simply fix this just like we have above.
class Foo
{
private $_magic_key;
public function __construct($magic_key)
{
$this->_magic_key = $magic_key;
}
public function bar()
{
return md5($this->_magic_key);
}
}
Its’ use would look like:
$foo = new Foo(AppConfig::getInstance()->magic_key);
echo $foo->bar();
Assuming this code is needed in a number of places, I dislike this solution. While it is more reusable, for a number of reasons, this code is now less usable to a developer. Therefore, I would advocate the use of a factory along with the above changes.
class FooFactory
{
public static function get()
{
return new Foo(AppConfig::getInstance()->magic_key);
}
}
Now the usage looks like:
echo FooFactory::get()->bar();
Application level factories are a great place, in my opinion, to place class coupling code. This helps you to keep you framework level code free of application specific details and your business logic free from framework knowledge.
I believe that patterns like these will help improve the testability and re-usability of code without sacrificing usability. The only way to improve upon these types of pattens is with the use of application wide Dependency Injection. This would only be usable if used with a Dependency Injection framework, an area in which the PHP community needs to improve.
Audio clip: Adobe Flash Player (version 9 or above) is required to play this audio clip. Download the latest version here. You also need to have JavaScript enabled in your browser.
#
Title
Artist
Label
1
MARTA Girl (Original Bmore Club Version)
Ludachrist
2
Sound Boy Massive - Original Mix
Machines Don’t Care
Machines Don’t Care
3
Cross The Dancefloor (Captain Crunk Remix)
Treasure Fingers
4
Fancy Footwork (Crookers Remix)
Chromeo
5
Everyone Nose (Douster Remix)
N.E.R.D.
6
Who’s That (Jack Beats Remix)
Trip
7
Mars - Original Mix
Fake Blood
Cheap Thrills
8
La Gente
DiscoTech
9
Afro Jacker - Original Mix
Machines Don’t Care
Machines Don’t Care
10
Up in here (Mightyfools remix)
DMX
11
Day ‘N’ Nite - Crookers Remix
Kid Cudi
Fool’s Gold Records
12
The Cricket Scores - Original Mix
Boy 8-Bit
Mad Decent
13
Stuck on Repeat (Fake Blood remix)
Little Boots
14
Let’s Go To Work (Douster Don’t Go To Work Remix)
Bryan Cox
15
Drop It To The Floor - Original Mix
Machines Don’t Care
Machines Don’t Care
16
The Things That Dreams Are Made Of - Kissy Sell Out Mix
I really liked the status feature on Facebook so I thought I’d take a look at Twitter. Now I was under the impression that I needed a fancy phone to use Twitter. This really annoyed me and made me wish even more that I could have an iPhone. Well I was wrong, I can send updates via SMS, So I’m going to start making updates and hopefully find other people to follow too. You can follow me at http://twitter.com/SamHennessy.
This is one crazy set of collaboration’s including Mr I’m Hot Right Now “Fake Blood”. I’m really digging the sound of this album. P.S. Beatport is the company I work for.
So as well as being the God of unit testing in PHP it seems that Sebastian Bergmann ahas created a new project called BugMiner. It looks to be a very cool tool that will use your version control repository to ascertain metrics about risk and complexity. I’ve not had a chance to play with it yet but I’m always impresses with Sebastian so I have high hopes.
Over on Greg Beaver’s Blog he talks about a patch he very quickly got into the PHP 5.3 and HEAD repository that will allow you to import a class into the global name space and override the internal PHP classes if the name matches. This means you could fix a bug in one of the PHP classes if you needed to or even use it for unit testing. I wounder if you could over load PDO?
So I’ve been getting very heavily into QA here in my new job at Beatport. As a result I’ve been doing alot of work with PHPUnit and I came across two problems one same and one large. The first was that the very useful iniSet function that will let you change an PHP setting like error_reporting and then restore it for you after the test has run. The problem I has was it required the value to be a string, which while not a show stopper was a pain. So I was very excited in a very nerdy way when my suggestion was implemented in the code and made it into a release.
The next problem was a biggie as far as I can tell the DBUnit port into PHPUnit know as the Database extension doesn’t support MySql 4. I outlined the problem in a ticket and also posted a patch that will get everything working for MySql 4 and 5.
Mike Lively the creator and maintainer of the DBUnit port it yet to take any visible action as of yet. And as I used to work with Mike I may have to drop him a line to get his opinion.
I’d love to hear from anyone else who is also having this problem with MySql 4.