More extensible than Kohana's Cascading File System

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:

  1. A number of directories have to be scanned for each core file whether or not the core classes are being extended.

  2. 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:

  1. 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.

  2. The ‘injectClass.ini’ will contain lines in the form

coreclassname = path.to.new.classfile

  1. 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).

  2. The autoloader, before loading a core class will check if a new class needs to be injected into the core.

  3. If, say, a module wants to enhance CController, the autoloader would load the class file using a ‘class injector’.

  4. The class injector will read the core file into a string, change the classname to a unique name and ‘eval’ the string.

  5. 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.

  6. 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.

  7. Obviously this wouldn’t work for the ‘class injector’ class since that’s the class that would make it happen.

Workaround for Yiilite.php

  1. If yiilite.php is used without modificatio then this scheme will not work.

  2. Modify all class names to the form OriginalName_001.

  3. Create another file YiiliteChangeable.php that contains declarations such as

class OriginalName extends OriginalName_001 {}

  1. If injectClass.ini does not exist, include YiiliteChangeable.php

  2. Otherwise, read YiiliteChangeable.php into a string, change the class names of classes that need to be extended and eval that.

  3. The ‘class injector’ then loads and ‘evals’ the extended classes appropriately.

What do you guys think?

Found a framework that uses ‘eval’ for class extensibility. Check out AgilePHP at http://www.makeabyte.com/products_agilephp.html

They however, always inject a proxy class (instead of the client class) and then uses an event like system to pass on control to the client class. (This statement is not quite accurate but gives you the idea.)

Yii allows you to use your own classes for it’s components. You specify the class you want to use in your config

eg: user component




...

'user'=>array(

    'class'=>'MyUser',

),

...



Yes, it does indeed. But it doesn’t have a mechanism for changing the behavior of core classes if other modules are using them. For example, I wrote a RBAC module and I would like to subclass CController and have all the other controllers in the application use my RbacController. This, however, entails changing the source code of all the controllers in the application even when some of the controllers are part of other modules. Using the scheme I’ve laid out, changing source code of other modules isn’t necessary. There are many other situations where this can come in handy.