Plugins are great but they are never what you exactly wanted. When they are designed properly, the best way to customize them is to extend them instead of directly editing them.
Now, imagine I have:1
# Penguin.class.php class Penguin { public function __construct() { echo "Windows is bad\n"; } } |
# Herd.class.php class Herd { public function __construct() { while ($i++ < 42) new Penguin(); } } # test new Herd(); |
I want to change Penguin
‘s behavior to something a bit more positive. However I don’t want to extend Herd
especially if it’s tied to Penguin
everywhere… I’m stuck with the Penguin
class and can’t use another one.
With some languages you can alter classes dynamically, and it can be referred as monkeypatching. While it is actually possible in PHP, it is ugly at best; you end up typing code in character strings, losing proper syntax highlighting and the opcode caching. Unless there is a very good language support for these methods it’s best to avoid them.
A handful of plugins for Symfony, like sfGuardPlugin (one of the most popular plugins) already have some kind of solution. They come with two classes, a dummy one and a real one:
# Penguin.class.php class Penguin extends pluginPenguin { } |
# pluginPenguin.class.php class pluginPenguin { public function __construct() { echo "Windows is bad\n"; } } |
Which means you just have to edit the (almost) blank Penguin
class.
This is a clean, object-oriented way to solve the problem.
However, there are practicals problems that will arise.
- You still edit an existing file; your modifications would be erased by upgrading to a newer version of the plugin
- It will confuse version control systems if you use one to retrieve the plugin
- Your custom code is in the plugin directory, which is just illogical
- You could chose no to ship any of the blank files, but the user would have to create all of them
There is a very simple solution to overcome all that: the Symfony autoloader will pick classes from local folders (like the lib
folder of your project) first.
Which means you can just copy the Penguin.class.php
file and customize it:
# Penguin.class.php your project's lib/ folder class Penguin extends pluginPenguin { public function __construct() { echo "Linux is good\n"; } } |
If you don’t use Symfony and/or a similar autoloader, there is another solution:
# Penguin.class.php in the sfHerdPlugin directory require dirname(__FILE__).'/plugin/plugin'.basename(__FILE__); if (is_readable(dirname(__FILE__).'/../sfHerdPluginCustom/'.basename(__FILE__)): require dirname(__FILE__).'/../sfHerdPluginCustom/'.basename(__FILE__); else: class Penguin extends pluginPenguin { } endif; |
# Penguin.class.php in the sfHerdPluginCustom directory (optional) class Penguin extends pluginPenguin { public function __construct() { echo "Linux is good\n"; } } |
The cool aspect is that if you don’t create any corresponding files in the sfHerdPluginCustom
directory, it will still work perfectly.