Fixes #149
[akelos.git] / lib / AkPlugin.php
blob8cd204f94c425153b2be83e4b86a67e8fc4e895e
1 <?php
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
5 // +----------------------------------------------------------------------+
6 // | Akelos Framework - http://www.akelos.org |
7 // +----------------------------------------------------------------------+
8 // | Copyright (c) 2002-2006, Akelos Media, S.L. & Bermi Ferrer Martinez |
9 // | Released under the GNU Lesser General Public License, see LICENSE.txt|
10 // +----------------------------------------------------------------------+
14 defined('AK_PLUGINS_DIR') ? null : define('AK_PLUGINS_DIR', AK_APP_DIR.DS.'vendor'.DS.'plugins');
15 defined('AK_PLUGINS') ? null : define('AK_PLUGINS', 'auto');
17 /**
18 * Base class that all Akelos plugins should extend
20 * @package Plugins
21 * @subpackage Base
22 * @author Bermi Ferrer <bermi a.t akelos c.om> 2007
23 * @copyright Copyright (c) 2002-2007, Akelos Media, S.L. http://www.akelos.org
24 * @license GNU Lesser General Public License <http://www.gnu.org/copyleft/lesser.html>
26 class AkPlugin
29 /**
30 * Plugin priority
31 * @var integer
32 * @access public
34 var $priority = 100;
36 /**
37 * This method will add the functionality of the code available at $path_to_code which
38 * inherits from $class_name to a new class named Extensible$class_name
40 * You can extend the same object from multiple plugins. So you can doo something like
42 * Example:
44 * finder_on_steroids
46 * @ app/vendor/plugins/finder_on_steroids/init.php
48 * class FinderOnSteroidsPlugin extends AkPlugin {
49 * function load(){
50 * $this->extendClassWithCode('AkActiveRecord', 'lib/FinderOnSteroids.php');
51 * }
52 * }
54 * @ app/vendor/plugins/finder_on_steroids/lib/FinderOnSteroids.php
56 * class FinderOnSteroids extends AkActiveRecord {
57 * function findSteroids(){
58 * //
59 * }
60 * }
62 * This will create a new class named ExtensibleAkActiveRecord class you can use
63 * as parent of your ActiveRecord class at app/shared_model.php
65 * @param string $class_name Class name to extend
66 * @param string $path_to_code Path to the source code file relative to your plugin base path.
67 * @priority int $priority Multiple plugins can chain methods for extending classes.
68 * A higher priority will will take precedence over a low priority.
70 function extendClassWithCode($class_name, $path_to_code, $priority = 100)
72 if(empty($this->PluginManager->ClassExtender)){
73 require_once(AK_LIB_DIR.DS.'AkClassExtender.php');
74 $this->PluginManager->ClassExtender =& new AkClassExtender();
77 $this->PluginManager->ClassExtender->extendClassWithSource($class_name, $this->getPath().DS.ltrim($path_to_code, './\\'), $priority);
81 function observeModel($model_name, &$Observer, $priority = 100)
86 function addHelper($helper_name, $helper_path = null)
88 $helper_name = AkInflector::camelize($helper_name);
89 $helper_path = empty($helper_path) ? $this->getPath().DS.'lib'.DS.AkInflector::underscore($helper_name).'.php' : $helper_path;
90 AkActionController::addPluginHelper($helper_name, array('path' => $helper_path));
93 /**
94 * Gets the base path for a given plugin
96 * @return string Plugin path
97 * @access public
99 function getPath()
101 return $this->PluginManager->getBasePath($this->name);
107 * The Plugin loader inspects for plugins, loads them in order and instantiates them.
109 * @package Plugins
110 * @subpackage Loader
111 * @author Bermi Ferrer <bermi a.t akelos c.om> 2007
112 * @copyright Copyright (c) 2002-2007, Akelos Media, S.L. http://www.akelos.org
113 * @license GNU Lesser General Public License <http://www.gnu.org/copyleft/lesser.html>
115 class AkPluginLoader
119 * Base path for plugins
120 * @var string
121 * @access public
123 var $plugins_path = AK_PLUGINS_DIR;
126 * List of available plugins
127 * @var array
128 * @access private
130 var $_available_plugins = array();
133 * Plugin instances
134 * @var array
135 * @access private
137 var $_plugin_instances = array();
140 * Priority plugins
141 * @var array
142 * @access private
144 var $_priorized_plugins = array();
147 * Goes trough the plugins directory and loads them.
149 * @return void
150 * @access public
152 function loadPlugins()
154 $this->instantiatePlugins();
155 $Plugins =& $this->_getPriorizedPlugins();
156 foreach (array_keys($Plugins) as $k) {
157 if(method_exists($Plugins[$k], 'load')){
158 $Plugins[$k]->load();
162 $this->extendClasses();
166 * Extends core classes with plugin code. EXPERIMENTAL
168 * @return void
169 * @access public
171 function extendClasses()
173 if(isset($this->ClassExtender)){
174 $this->ClassExtender->extendClasses();
180 * Short description for function
182 * Long description (if any) ...
184 * @return void
185 * @access public
187 function instantiatePlugins()
189 foreach ($this->getAvailablePlugins() as $plugin){
190 $this->instantiatePlugin($plugin);
195 * Instantiates a plugin
197 * If the plugin has a init.php file in its root path with a PluginNamePlugin class, it will instantiate the plugin
198 * and add it to the plugin instance stack
200 * @param string $plugin_name Plugin name
201 * @return boolean Returns true if can instantiate the plugin and false if the plugin could not be intantiated.
202 * @access public
204 function instantiatePlugin($plugin_name)
206 $init_path = $this->getBasePath($plugin_name).DS.'init.php';
207 if(file_exists($init_path)){
208 $plugin_class_name = AkInflector::camelize($plugin_name).'Plugin';
209 require_once($init_path);
210 if(class_exists($plugin_class_name)){
211 $Plugin =& new $plugin_class_name();
212 $Plugin->name = $plugin_name;
213 $Plugin->priority = empty($Plugin->priority) ? 10 : $Plugin->priority;
214 $Plugin->PluginManager =& $this;
215 $this->_plugin_instances[$Plugin->priority][] =& $Plugin;
216 return true;
217 }else{
218 trigger_error(Ak::t('"%name" class does not exist and it\'s needed by the "%plugin_name" plugin. ', array('%name'=>$plugin_class_name, '%plugin_name'=>$plugin_name)), E_USER_WARNING);
222 return false;
226 * Gets a list of available plugins.
228 * If AK_PLUGINS is set to 'auto' it will get a list of existing directories at AK_PLUGINS_DIR
230 * @return array Array of existing plugins
231 * @access public
233 function getAvailablePlugins()
235 if(empty($this->_available_plugins)){
236 if(AK_PLUGINS == 'auto'){
237 $this->_findPlugins();
238 }else{
239 $this->_available_plugins = AK_PLUGINS === false ? array() : Ak::toArray(AK_PLUGINS);
242 return $this->_available_plugins;
246 * Gets a plugin base path.) ...
248 * @param string $plugin_name Plugins name
249 * @return string Plugin root path
250 * @access public
252 function getBasePath($plugin_name)
254 return AK_PLUGINS_DIR.DS.Ak::sanitize_include($plugin_name);
258 * Gets a priorized list of plugins, where the priority is defined by the var $priority attribute
260 * @return array Priorized plugins
261 * @access private
263 function &_getPriorizedPlugins()
265 if(!empty($this->_plugin_instances) && empty($this->_priorized_plugins)){
266 ksort($this->_plugin_instances);
267 foreach (array_keys($this->_plugin_instances) as $priority){
268 foreach (array_keys($this->_plugin_instances[$priority]) as $k){
269 $this->_priorized_plugins[] =& $this->_plugin_instances[$priority][$k];
273 return $this->_priorized_plugins;
277 * Loads a list of existing plugins to $this->_available_plugins by inspecting the plugins directory.
279 * @return void
280 * @access private
282 function _findPlugins()
284 $plugin_dirs = Ak::dir(AK_PLUGINS_DIR, array('dirs' => true, 'files' => false));
285 $this->_available_plugins = array();
286 foreach ($plugin_dirs as $plugin_dir){
287 $plugin_dir = array_pop($plugin_dir);
288 if($plugin_dir[0] != '.'){
289 $this->_available_plugins[] = $plugin_dir;