Here is a proposition of enhancement for jClasses' service handling. It is inspired from Stubbles' inversion of control container but with less features and different but inspired from it. I tried to respect the "philosphy" of jelix or at least the vision I have of it. I think it is lightweight, fast, and not too much intrusive.
The goal of this feature is to choose the implementation behind an interface through jelix config file or through a special method call or through a special constant of the specified interface.
Here is a basic sample :
Jelix config file :
[Bindings]
modulename1*itfname = modulename2~targetclassname
Source code to use it :
$service = jClasses::getBindedService('modulename1~itfname');
Now suppose you are in a special environment and you want to use another implementation of the service without changing your source code. Then simply change the jelix config file to :
[Bindings]
;modulename1~itfname = modulename2~targetclassname
modulename1~itfname = modulename3~newimplementation
Now suppose you want to pass your unit test. If you use this service, a lot of things in your project are changed and you don't want it. Simply create a mock object with your unit test library (probably simpletest or PHPUnit) and use it for the service without even changing jelix config file:
jClasses::inc('modulename2~targetclassname');
$moke = createMoke('targetclassname');
jClasses::bind('modulename1~itfname')->toInstance($moke);
These are some basic sample. There are more subtleties about it.
I have used this feature in my own project at work for 1 year and it is very useful especially for unit tests. I reworked it to be more general and to "fit" more with jelix.
Here is a patch with the feature added to jClasses, a new jBinding class, unit tests corresponding to it and a patch corresponding to ticket 522 (I needed it). I added also a parameter to jSelectorFactory::create to specify a default type of selector. I also updated the manifests. This patch is for the trunk (version 833).
At the moment, I'm not entirely satisfied with jelix config file handling. "~" characters are forbidden for keys in ini files. I had to replace it with a "*".
There are a lot of possible enhancements for it :
- Make it work not only for services but for all classes, too.
- Make real dependency injection by contructor, setter, but this needs the use of annotations (and then reflection which is slow), or other special constants (but I don't like it). See the stubbles implementation.
- Use a specific config file and parser to allow "~" in keys. And this special config files could be compiled and cached.
- This could be integrated in existing jClasses methods (create, getService) with one more parameter to it.
This is my first real attempt to enhance jelix. What do you think of it ? Any ideas ?