<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Jamie Nay &#187; plugins</title>
	<atom:link href="http://jamienay.com/tag/plugins/feed/" rel="self" type="application/rss+xml" />
	<link>http://jamienay.com</link>
	<description>A PHP web developer writing about the web.</description>
	<lastBuildDate>Sat, 21 Aug 2010 21:11:22 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>An Easy Plugin Callback Component for CakePHP 1.2</title>
		<link>http://jamienay.com/2009/11/an-easy-plugin-callback-component-for-cakephp-1-2/</link>
		<comments>http://jamienay.com/2009/11/an-easy-plugin-callback-component-for-cakephp-1-2/#comments</comments>
		<pubDate>Sat, 21 Nov 2009 22:06:42 +0000</pubDate>
		<dc:creator>Jamie</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[plugins]]></category>
		<category><![CDATA[web development]]></category>

		<guid isPermaLink="false">http://jamienay.com/?p=434</guid>
		<description><![CDATA[Most people who do serious, large scale development with CakePHP would agree that keeping the bulk of a project&#8217;s code segregated in plugins is the best way to keep things organized. Sometimes, however, we want to use a plugin&#8217;s functionality outside of the scope of that plugin&#8217;s controllers. For example, we might have a products plugin with a &#8216;feature product&#8217; [...]]]></description>
			<content:encoded><![CDATA[<p>Most people who do serious, large scale development with CakePHP would agree that keeping the bulk of a project&#8217;s code segregated in plugins is the best way to keep things organized. Sometimes, however, we want to use a plugin&#8217;s functionality outside of the scope of that plugin&#8217;s controllers. For example, we might have a products plugin with a &#8216;feature product&#8217; capability, which we might want to display on our site&#8217;s homepage. So what are the options? We could just stick some code in the AppController (which I covered in a previous post &#8211; <a href="a-quick-dirty-and-useful-widget-component-for-cakephp">my old Widget Component</a>), or you could use the requestAction() function to call a method from another controller. But, both of those options have their faults: putting all of that code in the AppController can quickly add up, while requestAction() eats up a lot of resources since it initiates an entirely new request.</p>
<p>So, we need a solution that keeps the plugin code in the plugin directory and doesn&#8217;t use a ton of resources. And that&#8217;s where plugin controller callbacks come in. The Cake core really should have built-in callback functionality for plugin controllers, so developers can, for example, set a certain plugin action to fire after every beforeFilter(), during every beforeRender(), etc. But until it&#8217;s a part of the core, the community needs to fend for itself. There are a few implementations of plugin callbacks/hooks floating around in the community, but none of them suited my purposes exactly. So, I figured what the hell, I&#8217;ll try doing my own! And just like that, my RegisterCallbacksComponent was born. <img src='http://jamienay.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  I used a bakery article called &#8220;<a href="http://bakery.cakephp.org/articles/view/pluginhandler-to-load-configuration-and-callbacks-for-plugins" target="_blank">PluginHandler to load configuration and callbacks for plugins</a>&#8221; by Gediminas Morkevicius (sky_l3ppard) as a starting point, since I liked the approach he used. I boiled it down, however, to make it simpler and just for callbacks (he handles other auto-loading aspects as well, such as routes).</p>
<p><strong>The component is at the bottom of the post, after the instructions. You can also download it from <a href="http://github.com/jamienay/register_callbacks_component">its repository</a> in my brand-spankin&#8217;-new <a href="http://github.com/jamienay">Github account</a>.</strong></p>
<p>The basic idea is that each plugin has a &#8220;Callback&#8221; class &#8211; <strong>CamelizedPluginNameCallback </strong>(for example, PhotoGalleriesCallback if you&#8217;re working with a PhotoGalleries plugin). Within that class are your callback functions which are executed by the component.</p>
<p>Now, I&#8217;ve only done some cursory testing at this point, but the component is pretty straightforward and everything seems to be working. To use:</p>
<ul>
<li>Attach the component to your AppController: <code>var $components = array('RegisterCallbacks');</code></li>
<li>If you want certain plugin callbacks to fire before others, you can specify a partial or complete order using the &#8216;priority&#8217; setting: <code>var $components = array('RegisterCallbacks' =&gt; array('priority' =&gt; array('ImportantPluginOne, 'ImportantPluginTwo')));</code></li>
<li>Any plugins not specified in the &#8216;priority&#8217; array are loaded as per <code>Configure::listobjects('plugin')</code></li>
<li>For each plugin you want to include in the callback system, create a <strong>plugin_name_callback.php</strong> file in the plugin&#8217;s root directory (app/plugins/plugin_name). Within that file define a <strong>CamelizedPluginNameCallback</strong> class.</li>
<li>The <strong>CamelizedPluginNameCallback </strong>class can contain any or all of the following functions:
<ul>
<li>initialize(&amp;$controller): Called before the controller&#8217;s beforeFilter method.</li>
<li>beforeFilter(&amp;$controller): Called after the controller&#8217;s beforeFilter() method but before the controller executes the current action handler. Uses &#8216;beforeFilter&#8217; instead of &#8216;startup&#8217; to make the action name more consistent with the controller name.</li>
<li>beforeRender(&amp;$controller): Called after the controller&#8217;s beforeRender method but before the controller renders views and layout.</li>
<li>shutdown(&amp;$controller): Called before output is sent to the browser.</li>
<li>beforeRedirect(&amp;$controller, $url, $status = null, $exit = true): Called when the controller&#8217;s redirect method is called but before any further action.</li>
</ul>
</li>
</ul>
<p>And that&#8217;s basically it. Now you can keep your application-wide plugin code where it belongs &#8211; with the plugins. Here&#8217;s the full component code, current as of the time of writing. For up-to-date code, use my <a href="http://github.com/jamienay">Github repository</a>.</p>
<pre class="brush: php">
&lt;?php
/**
* RegisterCallbacksComponent class file
*
* Executes callback functions for each plugin before each controller callback action.
* Files should be located in: /plugins/[plugin_name]/[plugin_name]_callback.php
*
* Inspiration, architecture, and some code from:
* http://bakery.cakephp.org/articles/view/pluginhandler-to-load-configuration-and-callbacks-for-plugins
* &#039;PluginHandler to load configuration and callbacks for plugin&#039; - Gediminas Morkevicius (sky_l3ppard)
*
* @filesource
* @author          Jamie Nay
* @copyright       Jamie Nay
* @license         http://www.opensource.org/licenses/mit-license.php The MIT License
* @link            http://jamienay.com/2009/11/an-easy-plugin-callback-component-for-cakephp-12/
*/
class RegisterCallbacksComponent extends Object {
/**
* Controller object
*
* @var object
* @access private
*/
var $__controller = null;

/**
* Component settings
*
* @access public
* @var array
*/
public $settings = array();

/**
* Default values for settings.
* - priority (optional): the order in which callbacks should be executed. If
* priority is left empty, or if some plugins are left out of the list, the
* plugins are just added in the order in which they&#039;re loaded via Configure.
*
* @access private
* @var array
*/
private $__defaults = array(
&#039;priority&#039; =&gt; array()
);

/**
* Registered plugins - plugins that have a PluginNameCallback class.
*
* @access private
* @var array
*/
private $__registered = array();

/**
* Configuration method.
*
* In addition to configuring the settings (see $__defaults above for settings explanation),
* this function also loops through the installed plugins and &#039;registers&#039; those that have a
* PluginNameCallback class.
*
* @param object $controller    Controller object
* @param array $settings       Component settings
* @access public
* @return void
*/
public function initialize(&amp;amp;amp;$controller, $settings = array()) {
$this-&gt;__controller = &amp;amp;amp;$controller;
$this-&gt;settings = array_merge($this-&gt;__defaults, $settings);

if (empty($this-&gt;settings[&#039;priority&#039;])) {
$this-&gt;settings[&#039;priority&#039;] = Configure::listobjects(&#039;plugin&#039;);
} else {
foreach (Configure::listobjects(&#039;plugin&#039;) as $plugin) {
if (!in_array($plugin, $this-&gt;settings[&#039;priority&#039;])) {
array_push($this-&gt;settings[&#039;priority&#039;], $plugin);
}
}
}

foreach ($this-&gt;settings[&#039;priority&#039;] as $plugin) {
$file = Inflector::underscore($plugin).&#039;_callback&#039;;
$className = $plugin.&#039;Callback&#039;;
if (App::import(&#039;File&#039;, $className, true, array(APP . &#039;plugins&#039; . DS . Inflector::underscore($plugin)), $file.&#039;.php&#039;)) {
if (class_exists($className)) {
$class = new $className();
ClassRegistry::addObject($className, $class);
$this-&gt;__registered[] = $className;
}
}
}

/**
* Called before the controller&#039;s beforeFilter method.
*/
$this-&gt;executeCallbacks(&#039;initialize&#039;);
}

/**
* Executes beforeFilter() methods in the callback classes.
* Called after the controller&#039;s beforeFilter() method but before
* the controller executes the current action handler.
* Uses &#039;beforeFilter&#039; instead of &#039;startup&#039; to make the action name
* more consistent with the controller name.
*
* @param object $controller    Controller object
* @access public
* @return void
*/
public function startup(&amp;amp;amp;$controller) {
$this-&gt;executeCallbacks(&#039;beforeFilter&#039;);
}

/**
* Executes beforeRender() methods in the callback classes.
* Called after the controller&#039;s beforeRender method but before the
* controller renders views and layout.
*
* @param object $controller    Controller object
* @access public
* @return void
*/
public function beforeRender(&amp;amp;amp;$controller) {
$this-&gt;executeCallbacks(&#039;beforeRender&#039;);
}

/**
* Executes shutdown() methods in the callback classes.
* Called before output is sent to the browser.
*
* @param object $controller    Controller object
* @access public
* @return void
*/
public function shutdown(&amp;amp;amp;$controller) {
$this-&gt;executeCallbacks(&#039;shutdown&#039;);
}

/**
* Executes beforeRedirect() methods in the callback classes.
* Called when the controller&#039;s redirect method is called but
* before any further action.
*
* @param object $controller    Controller object
* @param string $url
* @param string $status
* @param boolean $exit
* @access public
* @return void
*/
public function beforeRedirect(&amp;amp;amp;$controller, $url, $status = null, $exit = true) {
$this-&gt;executeCallbacks(&#039;beforeRedirect&#039;, array($url, $status, $exit));
}

/**
* Executes the requested method in each Callback class, in order
* of priority, if that method exists in the class. Also sends any
* arguments, with the $this-&gt;__controller always being the first
* argument.
*
* @param string $method    Method name
* @param array $args       Optional arguments
* @access public
* @return void
*/
public function executeCallbacks($method, $args = array()) {
foreach ($this-&gt;__registered as $callback) {
$class = ClassRegistry::init($callback);
if ($class &amp;amp;amp;&amp;amp;amp; in_array($method, get_class_methods($class))) {
call_user_func_array(array($class, $method), array_merge(array($this-&gt;__controller), $args));
}
}
}

}
?&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://jamienay.com/2009/11/an-easy-plugin-callback-component-for-cakephp-1-2/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
