I just thought of this and I haven’t really tried this out and I might be missing something very obvious but here it goes anyway.
Problem:
We want to subclass a core class and make the rest of the app use the new subclass transparently instead of the original core class.
Kohana Framework Solution:
They achieve this by doing 3 things.
a. Creating blank subclasses of core classes. Something like
class CController extends Kohana_CoreController {}
b. Having a cascading file system. E.i. the directory system of the core is replicated replicated by all modules.
c. Documenting only the blank subclasses and requiring the application code to derive from those classes instead of directly from the core classes.
So if a module wants to change the behavior of CController (Kohana_CoreController really) it simply creates its own CController class and places it in it’s own directory tree corresponding to the location of the original file. When the class needs to be created (loaded), Kohana scans the module directories first before it’s own and will therefore find the modules version of CController and load that. So existing code that was using the old CController class is now automatically using the new CController class.
Drawback of Kohana’s Cascading Filesystem:
-
A number of directories have to be scanned for each core file whether or not the core classes are being extended.
-
Only one module can modify any given core class. Kohana loads whichever one it finds first.
Disclosure: I haven’t actually used Kohana (or any other php framework other than Yii) and the above is based on some quick reading a few weeks ago.
I think we can use ‘eval’ an a ‘when needed’ basis to make Yii more extensible. That is, be able to derive from core classes and transparently making those core
Here’s how it would work:
-
When a module needs to enhance a core class it’ll create a ‘injectClass.ini’ in the config folder, if it doesn’t already exists, else it will add to it.
-
The ‘injectClass.ini’ will contain lines in the form
coreclassname = path.to.new.classfile
-
The index.php will load a bootstrapper which will check for existence of this ‘injectClass.ini’ file and if the file is not found, everything remains same. If the file is found, it’ll mark the Yii class list (Yii uses an array to find it’s core classes).
-
The autoloader, before loading a core class will check if a new class needs to be injected into the core.
-
If, say, a module wants to enhance CController, the autoloader would load the class file using a ‘class injector’.
-
The class injector will read the core file into a string, change the classname to a unique name and ‘eval’ the string.
-
The ‘class injector’ would then read the new version of the CController, change the classnames (before and after the ‘extends’ keyword) and then ‘eval’ that code. Now we have a CController class that’s been provided by the subclass which extends from CControllerNNN which was the original class.
-
If there are more than one listing (in injectClass.ini) for CController, then the ‘class injector’ would accordingly name the intermediate classes and only the final class will have the name CController.
-
Obviously this wouldn’t work for the ‘class injector’ class since that’s the class that would make it happen.
Workaround for Yiilite.php
-
If yiilite.php is used without modificatio then this scheme will not work.
-
Modify all class names to the form OriginalName_001.
-
Create another file YiiliteChangeable.php that contains declarations such as
class OriginalName extends OriginalName_001 {}
-
If injectClass.ini does not exist, include YiiliteChangeable.php
-
Otherwise, read YiiliteChangeable.php into a string, change the class names of classes that need to be extended and eval that.
-
The ‘class injector’ then loads and ‘evals’ the extended classes appropriately.
What do you guys think?