PhpRiot
News Archive
PhpRiot Newsletter
Your Email Address:

More information

Dependency Injection for static methods

Note: This article was originally published at Planet PHP on 8 November 2010.
Planet PHP
Developers who are familiar with the topic of clean code know that static methods are a problem, especially if it comes to testability. Static makes the code global, and if there is some kind of state involved it is even more a problem because this is global state then - both a maintenance and testability nightmare.

Unfortunately, sometimes you don't get around of using such code. This could be due to a third party library you have to use, or even because of limitations of PHP itself. (Ever wanted to configure stream wrappers if stream context is not an option? Your only chance are static methods and therefore global state.) How can we keep our code clean and testable, at best without having a strongly coupled dependency to the class hosting the static method? Enter dependency injection for static methods.

Luckily PHP 5.3 comes with a new feature: dynamic access to static methods. Basically this means if you have a class with a static method you can call it like this:
$classname::myMethod();

Let's use this feature to get a more loose coupling between our own class and the static class. Suppose we have the following code:
class FooFromSomeLibrary { public static function doAwesomeStuffInNotSoAwesomeMethod($value) { // do something with $value } } class Bar { protected $value; public function __construct($value) { $this-value = $value; } public function process() { FooFromSomeLibrary::doAwesomeStuffInNotSoAwesomeMethod($this-value); // ... more code here to process the real thing ... } }

For some reason the method call can not be moved to another place where we could get rid of the direct dependency. But if we change the code of the Bar class things look different:

class Bar { protected $value; protected $fooClassName; public function __construct($value, $fooClassName = 'FooFromSomeLibrary') { $this-value = $value; $this-fooClassName = $fooClassName; } public function process() { // copy name to tmp var because of PHP parser limitations $fooClassName = $this-fooClassName; $fooClassName::doAwesomeStuffInNotSoAwesomeMethod($this-value); // ... more code here to process the real thing ... } }

The class still works as before, but we can change the name of the class the static method is called on. This is great, because in a test we can substitute the FooFromSomeLibrary class with our own mock implementation:

class MyMockedFooClass { public static function doAwesomeStuffInNotSoAwesomeMethod($value) { // intentionally empty } } class BarTestCase extends PHPUnit_Framework_TestCase { public function testProcessing() { $bar = new Bar('yeah', 'MyMockedFooClass'); $bar-process(); // ... some kind of assertions here ... } }

Our own Bar class is now only loosely coupled to FooFromSomeLibrary. While I don't suggest to use your own code in this way it is a helpful opportunity when working with code you don't have influence on.