UnionOfRAD/lithium — Issue #364: An overriding strategy for libraries
Author: jails
Created: 2012-03-05 00:03:47+00:00
State: open

## The context

Lithium is pretty flexible, it offers many way to override/replace core classes.

The tools are:
- The Filters which allow to change/override a behavior (Note : the method must be filterable)
- Libraries::locate() with Libraries::paths() && Libraries::_paths bring in a certain manner the
"cascading folder" strategy. You can override pretty every class by simply putting a file in
the right directory to make it used instead of the core's one.

**Is there rooms for improvements ?**

Ok Lithium is flexible, but what about the used Libraries ?
For example for Doctrine or other PSR-0 compatible library, it would be interesting to have also a way
to override/replace some part of them easily !

## Lithium to the Rescue :

### A - Replacing a core file by short circuiting the PSR-0 :

Adding a simple way to short circuit the PSR-0 would be enough for replacing any files.

Example :

Libraries::map(
'Doctrine\\ORM\\Query\\TreeWalkerAdapter' =>
LITHIUM_LIBRARY_PATH . '/_patch/Doctrine/TreeWalkerAdapter.php'
}

Adding a simple isset (like the following) in Libraries::load() would do to trick :

if(isset(static::$_maps[$class])){
$path = static::$_maps[$class];
}
else{
//Standard behavior
}

Advantage, you can patch temporarily a file until the official patch.
Since you can keep core files unmodified it's easier to maintain.
Indeed you're no longer have to remember what you have changed when you update some libraries,
all modified core files are in a single "patched" directory.

### B - Overriding the core :

If replacing any file is pretty easy, overriding is a little bit more complex.
Indeed how to extend a class from the original core class since the fully-namespaced
class name of the new class must be the same of the original class ?

A solution could be to "rebase" the namespace of classes of a library for making them overridable.

### Bellow the full example of how-to override a class of the "mylib" library

Consider the following filesystem structure for "mylib"

libraries/
mylib/
a/A.php // namespace : mylib\a
b/B.php // namespace : mylib\b
lithium/
..
..

Consider the following command :

$> li3 mirror mylib

the mirror command will be a simple script which copy all the files of a library in a directory called
"mirror" and rebase de namespace of each file according its new location (a simple example of the
mirror script is provided at the bottom of the file)

The result of the previous command will generate a new directory called "mirror" like the following :

libraries/
mylib/
a/A.php // namespace : mylib\a
b/B.php // namespace : mylib\b
mirror/a/A.php // namespace : mylib\mirror\a
mirror/b/B.php // namespace : mylib\mirror\b
lithium/
..
..

So to override the A class i've to put in a bootstrap file :

Libraries::map(
'mylib\\a\\A' =>
LITHIUM_LIBRARY_PATH . '/_patch/mylib/a.php'
}

then create the "_patch/mylib/a.php" file like the following :

namespace mylib\a;

class A extends \mirror\a\A{
...
}

That's all. Everything would work as expected.

## ANNEX

Script used for tests, but i a li3 command will be highly recommended :

#!/usr/bin/php5