Dependency Injection in PHP
June 26th, 2006The June issue of PHP Architect is out. My column this month is on dependency injection, a topic which I’ve been warming up to lately.
First there was CORBA. Then insane complexity of CORBA was supplanted by the intolerable complexity of EJB. Influenced by an agile mindset and the power of Unit testing, a group of java programmers began to construct simpler alternatives to EJB. Thus, the inversion of control frameworks were born. Martin Fowler came along, clarified and renamed the pattern dependency injection. This activity has originated in the Java world, but the pattern applies in PHP as well.
It is heartening to see an industry solve a problem over the course of a decade, moving from complex vendor driven middle-ware to simple patterns. The thing I like most about DI is how dead simple it really is.
Fowler’s article is a must read on the topic. However, I have two problems with most of the introductions to dependency injection. One is the use of irrelevant girl kisses boy style examples. The other is the over-emphasis on the container. The whole point of dependency injection is to move away from invasive component architectures, such as EJB. From my point of view, it is far more interesting to explore what impact dependency injection has on your design than what features your container has.
With that in mind, I tried to write an introduction to dependency injection that avoiding talking about DI containers and that tried to use real, relevant examples. For an example, I started with a typical, run of the mill dependency, torn from a popular PHP library that shall remain nameless, but easy to guess. Then I build on that with a plug-able backend in a common PHP style and then again using dependency injection.
For me dependency injection is relatively new territory, but one that I feel is an important technique. I’d like to see this technique become more widespread in PHP, especially in the current crop of frameworks.
I have a significant bias, but I think you should read the article. I hope you find it useful.
June 27th, 2006 at 5:12 am
(I don’t have access to the above-mentioned article, so I may be speaking at cross points or out of turn here.)
I was really interested to see this topic, as I had also been thinking about DI in PHP a lot lately. I have read the Fowler article in the past (just glanced over it again), and have some practical experience with Spring (and a bit of HiveMind) in Java. I like DI for the simplicity it brings to unit testing — by removing the dependency on some central service locator. While I think PHP could benefit from that aspect of DI, I keep running into the question of how DI would work in a way that would scale / perform decently in PHP.
PEAR and PEAR-style classes are replete with the factory-driven object instantiation which lends itself to a simple service locator approach (e.g. Horde loads config values and then calls factory() methods with driver names & params). This approach is a bit API limiting & encourages the use of associative arrays to pass params (which is bug-prone) but the advantage here is that the service locators only lookup & instantiate classes on demand. If I were using a Spring-style DI solution to wire up all of my objects, I’d have many, many more objects being instantiated than I would actually need to use for a given request.
So … I guess the question is what a lazy-loading DI solution would look like — and how different that would end up looking than a more “traditional” PHP service locator.
-Hans
June 27th, 2006 at 4:18 pm
Hans,
I think the default in spring is for the container to instantiate objects when the container is loaded. Obviously, that would be a terrible idea in PHP. However, there is no reason why the container can’t first be loaded with the information on how to instantiate the objects. Then, when an object is retrieved from the container, it and all of its dependencies can be instantiated as needed.
The more perplexing issue here is conditional dependencies. That can be done by passing a lazy loading proxy objects, with the knowledge that there are certain limitations in PHP to what these kinds of objects can do. (Creating an __implements magic method in PHP so an object can dynamically declare which interfaces it implements would go a long long way to better proxy objects.)
However, as I said in my post and in the article, I don’t believe you need a container to gain from dependency injection. The same object should be able to be used with any container, and therefor, without a container. To me, the important part of DI is its impact on design.
Regarding service locator, I don’t feel that’s a best practice. To me, its a half step into the world of DI. You’re still left with that big dependency on the service locator.
See Spring XML configuration best practices and Dependency injection anti-patterns.
As for a DI container in PHP, I’m waiting to see what happens with Marcus Baker’s Phemto project. I’m looking forward to seeing what he comes up with.
DI containers in PHP do have to be implemented differently because of the per-request life-cycle. We’ve already been experimenting with lazy loading and DI in WACT for quite some time. here is our current implementation, with lazy loading.
I already know that Marcus and I have slightly different ideas about what is needed for DI. I’m hoping that I’ll be able to contribute to Phemto and use it instead of what we did with WACT 0.2 without messing up Marcus’ intentions too much.
June 28th, 2006 at 5:46 am
Jeff,
Very interesting. And I completely agree that __implements would make a *huge* difference in proxy objects in PHP. At that point they would be essentially seamless, since typehints, SPL Iterators (etc.), and instanceof would work again.
What was really attractive to me about the Spring way of doing things was that the service objects themselves don’t need to have any knowledge of Spring. The dependency is truly “injected” — not “requested” or “located”. (And I think that Spring simply becomes a service locator when an app starts using the ApplicationContext to fetch beans.)
I think I need to read the article, but I don’t see the difference between what you describe as DI in WACT and the service locator pattern as Fowler describes it. It’s perhaps a subtle difference, since service locators also remove direct dependencies.
Anyway, thanks for the thought-provoking post. I’m gonna go get my company to renew our subscription to PHP|Architect
Cheers,
Hans
June 28th, 2006 at 7:41 am
Hans,
The file I linked to is for PHP 4, so no proxies. Therefor, an object must be “factory protocol aware” aware if lazy loading is to be used. The object that receives the handle must dereference it to get at the real object by calling _CreateObject(). Since there are no parameters, its not really a ServiceLocator, as I would describe it. But, since the object has to be aware that it is receiving a proxy, its not strictly DI, either. If you don’t nest Handles, though, it is the DI pattern because the ObjectRegistry will dereference the first Handle for you. The Handle name in WACT is historic.
I agree that the key aspect of DI is that the objects don’t need knowledge of their containers. In the PHP 5 version of WACT, I hope this will be true. (Actually, I hope to be able to use Phemto to make this true.)
Cool news on the subscription. You also can also download the electronic copy of this month’s article for 3.49 USD, which isn’t too bad. There’s a lot of other good stuff in this issue, too — like instructions for setting up PHP debugging in eclipse. Anyway, I would be interested to hear your perspective on DI and the back-end driver architecture from the article.
July 9th, 2006 at 11:12 pm
Hi all,
You may be interested in PHP version of Seasar2, which is a DI Container
with AOP.
http://www.seasar.org/en/php5/index.html
Cheers,
H.Ozawa
September 9th, 2006 at 3:27 am
Hi ..
I just want to ask about COM object .
To deal with this in PHP .. should I install OFFICE . or I can install just MS WORD COM for example from somewhere ?
Thanks alot for your help
June 16th, 2008 at 2:38 am
A framework with a spring-style dependency injection: lionframework.
Take a look at http://www.lionframework.org
Regards
Antonio