PluginHandler to load configuration and callbacks for plugins
Another feature is to load plugin configuration files automatically.
Main features of this component
- Load all plugin configuration files automaticaly
- Trigger a plugin callback method before any controller action
Changes
1.4- Removed and explained Routes configuration from autoloading
- Fixed a bug related to object storing in ClassRegistry
- Removed a method which was used to make unique setting keys, related to bug
Component Class:
Download code
<?php
//File: /app/controllers/components/plugin_handler.php
/**
* PluginHandler component adds a basic functionality
* required for the plugin development. Main features
* are plugin configuration autoloading and callbacks
* from the controller.
*
* @author Sky_l3ppard
* @version 1.4
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
* @category Components
*/
class PluginHandlerComponent extends Object {
/**
* Reference to the controller
*
* @var object
* @access private
*/
var $__controller = null;
/**
* Plugin Settings, available options:
* autoload - array of configuration files to be loaded
* permanently - true to load configuration files before any action,
* false - loaded only for a plugin's controller actions
* Notice: bootstrap is loaded then the component initialize method is
* fired and for the same reason routes will not work. If you want to include
* then from the plugin. Use the app bootstrap to scan plugins for routes
*
* @var array
* @access private
*/
var $__settings = array(
'autoload' => array(
'bootstrap',
'core',
'inflections'
),
'permanently' => true
);
/**
* Initializes component by loading all configuration files from
* all plugins found in application. Configuration files should be
* placed in \app\plugins\your_plugin\config\ directory. Be careful,
* it will overwrite all settings loaded from \app\config if the
* setting name matches.
* At the end it will execute an 'initialize' callback method loaded
* from \plugins\your_plugin\{your_plugin}_auto_loader.php file
*
* @param object $controller - reference to the controller
* @param array $settings - component settings, list of autoload files
* @return void
* @access public
*/
function initialize(&$controller, $settings = array()) {
$this->__controller = $controller;
$this->__settings = array_merge_recursive($this->__settings, (array)$settings);
foreach (App::objects('plugin') as $plugin) {
$is_parent_class = strpos(get_parent_class($controller), Inflector::classify($plugin)) !== false;
if ($this->__settings['permanently'] || (!$this->__settings['permanently'] && $is_parent_class)) {
foreach ($this->__settings['autoload'] as $type) {
App::import(
'Plugin',
Inflector::classify("{$plugin}_{$type}"),
array('file' => Inflector::underscore($plugin).DS.'config'.DS.$type.'.php')
);
}
}
}
$this->loaderExecute('initialize');
}
/**
* Executes a 'beforeFilter' callback method loaded
* from \plugins\your_plugin\{your_plugin}_auto_loader.php file
*
* @param object $controller - reference to the controller
* @return void
* @access public
*/
function startup(&$controller) {
$this->loaderExecute('beforeFilter');
}
/**
* Executes a 'beforeRender' callback method loaded
* from \plugins\your_plugin\{your_plugin}_auto_loader.php file
*
* @param object $controller - reference to the controller
* @return void
* @access public
*/
function beforeRender(&$controller) {
$this->loaderExecute('beforeRender');
}
/**
* Initializes \plugins\your_plugin\{your_plugin}_auto_loader.php file
* and executes specified callback $method from AutoLoader class for
* all plugins found in application.
*
* @param string $method - name of the method to execute
* @return void
* @access public
*/
function loaderExecute($method) {
foreach (App::objects('plugin') as $plugin) {
$loader_file = Inflector::underscore($plugin).'_auto_loader';
$loader_class = Inflector::classify($loader_file);
$loader_instance = null;
if (!ClassRegistry::isKeySet($loader_class)) {
App::import('Plugin', $loader_class, Inflector::underscore($plugin).DS.$loader_file.'.php');
if (class_exists($loader_class)) {
ClassRegistry::addObject($loader_class, new $loader_class());
}
} else {
$loader_instance =& ClassRegistry::getObject($loader_class);
}
if (!empty($loader_instance) && in_array($method, get_class_methods($loader_class))) {
$loader_instance->{$method}($this->__controller);
}
}
}
}
?>
Here is a tutorial on how to use this component
Using PluginHandler component settings
There are cases then you need some additional options like plugin priority, additional configuration file or to set this component to execute after another one. Here is the usage example:
Download code
<?php
var $components = array(
'PluginHandler' => array(
'autoload' => array('conf_file', 'another'),
'priority' => array('MyPlugin', 'AnotherPlugin', 'Third'),
'primary' => true,
'permanently' => true
)
);
?>
autoload is the list of configuration files to be scanned then initializing this component. Default are: bootstrap, core, inflections. These plugin configuration files must be located in /app/plugins/your_plugin/config directory and in all cases they are executed after app config files so be careful, you can easily override default setting values
Notice: routes cannot be loaded from this component, because they must be invoked before Dispatcher is called. And these configurations are loaded on component initialize method
A tip on how you can include your routes from plugins
To do that you should scan all plugins in your main application bootstrap.php file and import them as usual.
priority is the list of plugins which will setup the execution order for these plugins, ones what were not included automatically will be added at the end of the list. This is advantage if some plugin callbacks must be executed after or before another, same as configuration files
primary if this option is set to true the first time this component is called it will set it`s priority to be executed before all other (e.g.: Auth, Session) components
permanently if this setting is set to true PluginHandler component will load configuration settings before any controller action no matter if it belongs to this plugin or not. In the other case, it will load configuration files only for the plugin which action is currently called.
Here is a directory tree for the example used:
Download code
/app
/plugins
/my_plugin
/config
bootstrap.php
conf_file.php
another.php
/controllers
/models
...
my_plugin_auto_loader.php
my_plugin_app_controller.php
...
/another_plugin
/config
conf_file.php
...
/third
...
Any improvements and ideas are very welcome, enjoy.
Comments
Comment
1 A nice job!
I'm planning a CakePHP extension named CakePOWER (http://cakepower.org) and I'm looking for a similar solution.
In my mind plugins expose their configuration settings BEFORE the application. Application can overwrite every plugins settings...
I'm doing this via bootstrap and routes custom inclusion. I use a "config" folder into plugin's folder exactly like your plugin...
Your solution is better than mine because you don't have to touch any application configuration to work... just include the component into AppController.
Very nice job!
... after a while ....
I'm trying to use your plugin. It work well for bootstrap and other configurations but it does not work with routes rules.
Did you try to set customized routing for plugins? It seems not to work.
Bug
2 Does Not Work With Router Files
Great piece of code. Just letting you know that this doesn't work for router files. It runs after the dispatcher code so it doesn't take any effect. The only place I've seen where you can fool around with routes is include a file in your app's bootstrap file and then do:
App::import('Core', 'Router');Then just add your routes under that.
Comment
3 foreach
invoke the App::objects method on each and every run?
dont know how slow this function is, but
wouldnt it be better (and faster) to use
$plugins = App::objects('plugin');
foreach ($plugins as $plugin) {}
? But I guess I am wrong - and just got mixed up with for ($i...) loops
Comment
4 A quick and simple way to include routes.php
Sorry guys, I'm very busy recently, get_class_methods function is really very slow and a big brake. It should be and will be cached on next update. If you planning to use it on production version, better cache it..
I'll place this component on the github also, you'll have link on next update
<?php
class PluginConfigure {
/**
* Load config files for all plugins
* example in the config/bootstrap.php include this class and
* do PluginConfigure::Load('routes.php', 'core.php', 'bootstrap.php');
* searching in config directory
*
* @return Void
* @access Public
*/
static function load() {
$args = func_get_args();
if (empty($args)) {
return;
}
$pluginsAvailable = App::objects('plugin');
foreach ($args as $configType) {
if (empty($configType) || !is_string($configType)) {
continue;
}
foreach (App::objects('plugin') as $plugin) {
App::import(
'Plugin',
Inflector::classify("{$plugin}_{$configType}"),
array('file' => Inflector::underscore($plugin).DS.'config'.DS.Inflector::underscore($configType).'.php')
);
}
}
}
}
?>
Comment
5 Hi Mark, answer to foreach/for/while question
Comment
6 I believe this still won't work
Also, in CakePHP 1.3, things get a bit flipped around and the config/routes get loaded before the app/config/bootstrap.php so this definitely would not work.
The dispatcher loads the routes and checks for a match all at one time so there's really no point of insertion the anyone can make rather than putting what I suggested in the routes.php file.
Comment
7 Hi Cody, i'll give you step by step:
Wasn`t that simple enough?
Comment
8 Isn't that what I said??
*shrugs*
Bug
9 Very good!
---
One issue i ran into: the auto loader won't save the loader_instance on the first run (for initialize) so i've changed the loaderExecute method to:
and now it works.<?php
function loaderExecute($method) {
foreach (App::objects('plugin') as $plugin) {
$loader_file = Inflector::underscore($plugin).'_auto_loader';
$loader_class = Inflector::classify($loader_file);
$loader_instance = null;
if (!ClassRegistry::isKeySet($loader_class)) {
App::import('Plugin', $loader_class, Inflector::underscore($plugin).DS.$loader_file.'.php');
if (class_exists($loader_class)) {
ClassRegistry::addObject($loader_class, new $loader_class());
}
}
$loader_instance =& ClassRegistry::getObject($loader_class);
if (!empty($loader_instance) && in_array($method, get_class_methods($loader_class))) {
$loader_instance->{$method}($this->__controller);
}
}
}
?>
thanks for sharing!
Comment
10 Thanks for the information.
Comment
11 thanks
Comment
12 ed
Comment
13 soap
tower defense
Comment
14 plugins
tower defense
Comment
15 Callback
I understand how the callback works, but I don't see what the advantage this callback gives. I mean, the plugin still works without it.
- Will
Virus Protection
Comment
16 handling
Dropship
Comment
17 dgsdgsdgd
tramadol online buy tramadol tramadol tramadol hydrochloride description online prescription tramadol ingredient tramadol tramadol hydrochloride dose cheap tramadol no prescription tramadol ingredient tramadol hydrochloride effects cheap tramadol prescriptions online pill price tramadol tramadol hydrochloride tablets no prescription tramadol tramadol effects tramadol hydrochloride ultram online tramadol prescriptions pharmacy tramadol tramadol hydrocodone tramadol 100 mg no prescription discount tramadol online tramadol indications tramadol cheap no prescription tramadol 100mg tramadol info tramadol no prescription symptom tramadol withdrawal tramadol ingredients tramadol no prescription needed tablet tramadol tramadol long term use tramadol prescription online lowest cost tramadol tramadol withdrawls tramadol cheap tramadol 37.5 soma tramadol fioricet order tramadols tramadol avinza drug interaction order tramadol order cheap tramadol tramadol order