Dependency Injection in Solar

By | June 27, 2006

Jeff Moore talks about dependency injection in PHP and opines as to how he’d like to see “the current crop of frameworks” adopt the technique. Jeff, your wish is granted! (At least for Solar. ;-)

Because it makes great use of dependecy injection, Solar has a standardized form of generating/retrieving dependency objects using its Solar::dependency() method.

The calling code sends the dependency params to the target object at construction time. The target object then passes those params through Solar::dependency() to retrieve or create the dependency object.

The params may be an object (the dependency object itself), or a string (treated as a registry key for Solar_Registry::get()), or an array (which are treated as params to create a new dependency object internally at the target using Solar::factory()).

UPDATE (2009-03-28): Updating documentation links.

7 thoughts on “Dependency Injection in Solar

  1. Hans

    Can you show an example of how you wire up your objects using this method? Are objects wired into the registry using DI? I assume they are, otherwise this sounds more like a service locator to me.

    Maybe an example would make this clear: Say I’m using the Solar framework and I want to swap out all my “real” drivers for “mock” versions (e.g. my SMTPMailSender gets swapped out for MockMailSender). What file(s) would I need to change to make this happen?

    -H

    Reply
  2. pmjones Post author

    Hi Hans,

    Here’s an example from the User object (see the __construct() method) —

    http://solarphp.com/svn/trunk/Solar/User.php

    The internal auth, access, and role objects are set up via the configuration params; as long as the passed objects (or object parameters) are descended from the proper class, the dependency is accepted for internal use.

    If you send a Mock_Solar_Auth object descended from Solar_Auth, it’ll work without changes to the User class. If you send a string that represents a Mock_Solar_Auth object in the registry, that’ll work too. Finally, if you decide to use a Solar_Auth_Adapter_Mock adapter in the facade Solar_Auth class, but want the User object to instantiate that object internally, that will work as well.

    With respect to storing objects in the registry via dependency injection: no, you register an actual created object and give it an identifying key string. Registry objects can lazy-load themselves, though, if you pass a class name and configuration array instead of an object; this will cause the object to be instantiated on first request from the registry.

    I hope this begins to answer your question; please let me know if it does not.

    Reply
  3. Travis Swicegood

    Technically, Paul, this don’t count as a full dependency injection. With dependency injection, your Solar_User object’s construct would look like:

    Solar_User::__construct(Solar_Auth $auth, Solar_Role, $role, Solar_Access $access);

    With that, you would request a User object (i.e., $dependencyManager::object(‘Solar_User’)) and the manager would take care of locating the dependencies, not the User object. The idea is that the object, other than offerring what it needs, has no clue on how to go about getting them. As soon as you add in the code within Solar_User, it moves from true DI to another very similiar pattern in outcome. Service Locator sounds right, but I’m not sure.

    Of course, for true DI, you would have to either re-think your implementation of Solar (i.e., no single $config parameter) or have some way to define what’s expected by an object (i.e., Solar_User::expectedParameters() : array()).

    Reply
  4. Anon.

    i would agree with you though there are many implementations of DI; The relationship between a Service Locator and true Dependency Injection is a close one.

    Reply
  5. Hans

    Yeah, I think perhaps that the alternative term “Inversion of Control” (which IIRC Fowler doesn’t like) illustrates a little better the difference between DI and Service Locator. With DI frameworks (that I’ve used in Java, anyway), the important difference is that the classes do not have any links/knowledge of any external registry (i.e. service locator). Everything is just a very simple object and the “dependencies” are set (injected) in by an external agent. This gives you lots & lots of control over both what is instantaited (i.e. which particular classes and what params they are passed) and how those classes are instantiated (e.g. singleton or new instance for every object). Another feature of a centralized DI/IoC framework is that you have a single place where AOP cutpoints, advice, etc. can be defined for your objects. A DI solution almost has a language-level feel about it (indeed, it feels like a bit of hack to me with frameworks like Spring, since one is essentially writing application code in XML).

    I still don’t see how a true DI solution would work in PHP, though (as I mention on Jeff’s blog). To really wire everything up would add an unnacceptable level of overhead to my PHP scripts — e.g. my framework may require that certain objects get wired with a desired ImageTransformer driver, but 90% of my page requests don’t need an ImageTransformer at all.

    This appears to me to be a problem that is inherent in trying to invert control for an environment like PHP. Because PHP environments must (generally) be recreated for every request, there is a good reason to keep control of the object setup on-demand within the service classes themselves — i.e. with PHP there is a good reason not to invert control.

    … Now, I suppose that this wouldn’t be the case if the IoC layer could somehow magically wire-on-demand. But any implementation I can think for that just starts to look like a service locator.

    -H

    Reply
  6. Dan Ports

    Hans is on point — loading a DI config for every page load would incur significant overhead. Other frameworks (I am thinking especially of MonoRail for ASP.NET, since that’s what I’m using at the moment) avoid this by loading the config on application startup and stashing it in some application-scoped storage. But I don’t see any easy way to do this with PHP, since it lacks the concept of an application, AFAIK. Perhaps you could cache the config by doing code gen. DI seems to be a prerequisite for any serious TDD effort.

    Re: the ImageTransformer example — most DI tools wouldn’t instantiate an object unless it was actually required as a transitive dependency, so this seems like a moot point. I think the real performance issue would be loading the DI config; i.e. how do you actually build an object of class XYZ.

    All this said, I applaud Solar for including at least rudimentary DI support. I haven’t seen much of this in PHP frameworks, and it’s a shame that few of them consider testability a worthy goal.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *