Difference between revisions of "Plugin Developer Overview"

From Joomla! Documentation

(Several markup changes.)
 
(17 intermediate revisions by 5 users not shown)
Line 1: Line 1:
{{RightTOC}}
+
{{version|2.5,3.x}}
[[Category:Development]]
+
{{dablink|'''Version Note''' While this document pertains to Joomla! 2.5 and 3.x, JEventDispatcher does not exist in Joomla 2.5. In that case, change JEventDispatcher to JDispatcher. Using JDispatcher is possible in Joomla 3.x but will generate a deprecated notice.}}
[[Category:Framework]]
 
[[Category:Plugins]]
 
  
== Overview ==
+
Joomla! 1.5 introduced the ''JPlugin'' class. In the effort to move Joomla! toward a more efficient object-oriented framework, a new plugin system has been developed which follows the [[wikipedia:Observer pattern|Observer pattern]]. Plugins are observer classes that attach to a global event dispatcher object in the Joomla! core. This means that either the Joomla! core or a third party component or module can trigger an event which causes one or more plugins to execute some code.
 
 
New in Joomla! 1.5 is a <code>JPlugin</code> class. In the effort to move Joomla! toward a more efficient object-oriented framework a new plugin system has been developed. Plugins are observer classes that attach to a global event dispatcher object in the Joomla! core. What does this mean in english? It means that either the Joomla! core or a third party component or module can trigger an event which causes one or more plugins to execute some code. Isn’t this the same as a mambot? No, not exactly. It is similar in basic principle in that both a plugin and a mambot are triggered by an event and both execute code. One issue with a mambot is that it requires its functions to be declared in a global scope. Since a JPlugin is a class, the methods that handle the events can share variables and have private helper methods as well, which makes for a much cleaner system for handling events.  
 
  
 
== Implementation ==
 
== Implementation ==
 +
The implementation of the plugin system is that of an observer pattern. It has two parts, an observer class, ''JPlugin'', and an observable class, ''JEventDispatcher''.
  
The implementation of the plugin system is that of an observer pattern. It has two parts, an observer class, <code>JPlugin</code>, and an observable class, <code>JEventDispatcher</code>.
+
<syntaxhighlight lang="php">
 
+
/**
<source lang="php">/**
 
 
  * JPlugin Class
 
  * JPlugin Class
 
  *
 
  *
  * @package Joomla.Framework
+
  * @package     Joomla.Platform
  * @subpackage Application
+
  * @subpackage Plugin
  * @since 1.5
+
  * @since       11.1
 
  */
 
  */
class JPlugin extends JObserver
+
class JPlugin extends JEvent
 
{
 
{
 
/**
 
/**
 
* Constructor
 
* Constructor
*  
+
*
* For php4 compatability we must not use the __constructor as a constructor for plugins
+
* @param  object  &$subject  The object to observe
* because func_get_args ( void ) returns a copy of all passed arguments NOT references.
+
* @param  array  $config    An optional associative array of configuration settings.
* This causes problems with cross-referencing necessary for the observer design pattern.
+
*                             Recognized key values include 'name', 'group', 'params', 'language'
*  
+
*                             (this list is not meant to be comprehensive).
* @param object $subject The object to observe
+
*
* @since 1.5
+
* @since   11.1
 
*/
 
*/
function JPlugin(& $subject)
+
public function __construct(&$subject, $config = array())
 
{
 
{
parent::__construct($subject);
+
// Get the parameters.
}
+
if (isset($config['params']))
 +
{
 +
if ($config['params'] instanceof JRegistry)
 +
{
 +
$this->params = $config['params'];
 +
}
 +
else
 +
{
 +
$this->params = new JRegistry;
 +
$this->params->loadString($config['params']);
 +
}
 +
}
 +
 
 +
// Get the plugin name.
 +
if (isset($config['name']))
 +
{
 +
$this->_name = $config['name'];
 +
}
 +
 
 +
// Get the plugin type.
 +
if (isset($config['type']))
 +
{
 +
$this->_type = $config['type'];
 +
}
  
/**
+
// Load the language files if needed. Note whilst this method is in the
* Method to map events to handler methods
+
// JPlugin class it has been left out of the docs for code clarity.
*
+
if ($this->autoloadLanguage)
* @access public
+
{
* @param array Arguments
+
$this->loadLanguage();
* @return mixed Routine return value
 
* @since 1.1
 
*/
 
function update( &$args )
 
{
 
/*
 
* First lets get the event from the argument array. Next we will unset the
 
* event argument as it has no bearing on the method to handle the event.
 
*/
 
$event = $args['event'];
 
unset($args['event']);
 
 
/*
 
* If the method to handle an event exists, call it and return its return
 
* value.  If it does not exist, return a boolean true.
 
*/
 
if (method_exists($this, $event)) {
 
return call_user_func_array(array($this, $event), $args);
 
} else {
 
return true;
 
 
}
 
}
 +
 +
parent::__construct($subject);
 
}
 
}
}</source>
+
}</syntaxhighlight>
  
 
There are two important things that makes this class work.
 
There are two important things that makes this class work.
  
One is the constructor which actually gets executed by the parent class of this class <code>JObserver</code>. The following is what happens in the constructor:
+
One is the constructor which actually gets executed by the parent class of this class ''JEvent''. This what happens in the constructor:
  
<source lang=php">// Register the observer ($this) so we can be notified
+
<syntaxhighlight lang="php">// Register the observer ($this) so we can be notified
 
$subject->attach($this);
 
$subject->attach($this);
  
 
// Set the subject to observe
 
// Set the subject to observe
$this->_subject = &$subject;</source>
+
$this->_subject = &$subject;</syntaxhighlight>
  
This attaches the <code>JPlugin</code> to an observable object. In the case of plugins, they observe the <code>JEventDispatcher</code> object.
+
This attaches the ''JPlugin'' to an observable object. In the case of Plugins, they observe the ''JEventDispatcher'' object.
  
The second important thing to note is the update method. The update method is passed an array from its trigger. The array contains two elements - the event and the arguments. Once the update method receives this array it extracts the event and removes it from the arguments. It then calls a method of name ‘event’ (passing the arguments array) and returns its response.
+
The second important thing to note is the update method in the ''JEvent'' class. The update method is passed an array from its trigger. The array contains two elements - the event and the arguments. Once the update method receives this array, it extracts the event and removes it from the arguments. It then calls an ''event'' method (passing the arguments array) and returns its response.
  
 
== Third Party Usage ==
 
== Third Party Usage ==
  
<source lang="php"><?php
+
<syntaxhighlight lang="php">
 +
<?php
 
/**
 
/**
 
  * @version $Id: $
 
  * @version $Id: $
  * @package  
+
  * @package
  * @subpackage  
+
  * @subpackage
  * @copyright  
+
  * @copyright
  * @license  
+
  * @license
 
  */
 
  */
  
 
jimport('joomla.plugin');
 
jimport('joomla.plugin');
 
  
 
/**
 
/**
 
  * Example Plugin
 
  * Example Plugin
 
  *
 
  *
  * @author  
+
  * @author
  * @package  
+
  * @package
  * @subpackage  
+
  * @subpackage
  * @since  
+
  * @since
 
  */
 
  */
 
class ExamplePlugin extends JPlugin
 
class ExamplePlugin extends JPlugin
 
{
 
{
 
/**
 
/**
* Constructor
+
* This method handles the onIncrement fictional event.  It takes an integer input and
*
 
* @param object $subject The object to observe
 
* @since 1.1
 
*/
 
function ExamplePlugin( &$subject ) {
 
parent::__construct( $subject );
 
}
 
 
 
/**
 
* This method handles the onIncrement event.  It takes an integer input and  
 
 
* increments its value.
 
* increments its value.
*  
+
*
 +
* @param  integer  $input An integer to increment
 +
*
 +
* @return integer  Incremented integer
 +
*
 +
* @since 3.x
 
* @access public
 
* @access public
* @param int $input An integer to increment
 
* @return int Incremented integer
 
* @since 1.1
 
 
*/
 
*/
 
function onIncrement($input)
 
function onIncrement($input)
{
+
{
 
return $input++;
 
return $input++;
 
}
 
}
 
}
 
}
?></source>
+
?></syntaxhighlight>
 +
 
 +
As you can see, it is simple to create a ''JPlugin''. It is truly as simple as creating a class that extends ''JPlugin'' and writing a method for each event you want the plugin to handle.
 +
 
 +
== Example ==
 +
The Administrator module ''mod_quickicon'' is a good example of implementing the Observer Pattern. The module ''Quickicons'' shows several buttons in Administrator:
 +
 
 +
[[File:j3x-admin-cpanel-mod_quickicons.png|thumbnail|center]]
 +
 
 +
Thanks to the plugin system any programmer can add buttons to this module without modifying/hacking the module. How?
 +
 
 +
The ''mod_quickicons'' does this:
 +
 
 +
<syntaxhighlight lang="php">
 +
// Include buttons defined by published quickicon plugins
 +
JPluginHelper::importPlugin('quickicon');
 +
$app = JFactory::getApplication();
 +
$arrays = (array) $app->triggerEvent('onGetIcons', array($context));</syntaxhighlight>
 +
 
 +
see: https://github.com/joomla/joomla-cms/blob/3.10-dev/administrator/modules/mod_quickicon/helper.php#L149-L153
 +
 
 +
That means that the plugins in this folders are called:
 +
 
 +
https://github.com/joomla/joomla-cms/tree/staging/plugins/quickicon
 +
 
 +
And if they have a method ''onGetIcons'' they can inject their own icons. See how ''Quickicon/extensionupdate'' does it:
 +
 
 +
https://github.com/joomla/joomla-cms/blob/staging/plugins/quickicon/extensionupdate/extensionupdate.php#L40
 +
 
 +
This is a lot of code for just adding a button, however think of the flexibility that this solution provides: now Extension developers can attach buttons to that menu without hacking Joomla.
  
As you can see, it is quite simple to create a <code>JPlugin</code>. It is truly as simple as creating a class that extends <code>JPlugin</code> and writing a method for each event you want the plugin to handle.
+
<noinclude>[[Category:Plugin Development|Overview]]
 +
[[Category:Development Recommended Reading]]
 +
</noinclude>

Latest revision as of 10:40, 6 November 2022

Joomla! 1.5 introduced the JPlugin class. In the effort to move Joomla! toward a more efficient object-oriented framework, a new plugin system has been developed which follows the Observer pattern. Plugins are observer classes that attach to a global event dispatcher object in the Joomla! core. This means that either the Joomla! core or a third party component or module can trigger an event which causes one or more plugins to execute some code.

Implementation[edit]

The implementation of the plugin system is that of an observer pattern. It has two parts, an observer class, JPlugin, and an observable class, JEventDispatcher.

/**
 * JPlugin Class
 *
 * @package     Joomla.Platform
 * @subpackage  Plugin
 * @since       11.1
 */
class JPlugin extends JEvent
{
	/**
	 * Constructor
	 *
	 * @param   object  &$subject  The object to observe
	 * @param   array   $config    An optional associative array of configuration settings.
	 *                             Recognized key values include 'name', 'group', 'params', 'language'
	 *                             (this list is not meant to be comprehensive).
	 *
	 * @since   11.1
	 */
	public function __construct(&$subject, $config = array())
	{
		// Get the parameters.
		if (isset($config['params']))
		{
			if ($config['params'] instanceof JRegistry)
			{
				$this->params = $config['params'];
			}
			else
			{
				$this->params = new JRegistry;
				$this->params->loadString($config['params']);
			}
		}

		// Get the plugin name.
		if (isset($config['name']))
		{
			$this->_name = $config['name'];
		}

		// Get the plugin type.
		if (isset($config['type']))
		{
			$this->_type = $config['type'];
		}

		// Load the language files if needed. Note whilst this method is in the
		// JPlugin class it has been left out of the docs for code clarity.
		if ($this->autoloadLanguage)
		{
			$this->loadLanguage();
		}

		parent::__construct($subject);
	}
}

There are two important things that makes this class work.

One is the constructor which actually gets executed by the parent class of this class JEvent. This what happens in the constructor:

// Register the observer ($this) so we can be notified
$subject->attach($this);

// Set the subject to observe
$this->_subject = &$subject;

This attaches the JPlugin to an observable object. In the case of Plugins, they observe the JEventDispatcher object.

The second important thing to note is the update method in the JEvent class. The update method is passed an array from its trigger. The array contains two elements - the event and the arguments. Once the update method receives this array, it extracts the event and removes it from the arguments. It then calls an event method (passing the arguments array) and returns its response.

Third Party Usage[edit]

<?php
/**
 * @version $Id: $
 * @package
 * @subpackage
 * @copyright
 * @license
 */

jimport('joomla.plugin');

/**
 * Example Plugin
 *
 * @author
 * @package
 * @subpackage
 * @since
 */
class ExamplePlugin extends JPlugin
{
	/**
	 * This method handles the onIncrement fictional event.  It takes an integer input and
	 * increments its value.
	 *
	 * @param  integer  $input An integer to increment
	 *
	 * @return integer  Incremented integer
	 *
	 * @since 3.x
	 * @access public
	 */
	function onIncrement($input)
	{
		return $input++;
	}
}
?>

As you can see, it is simple to create a JPlugin. It is truly as simple as creating a class that extends JPlugin and writing a method for each event you want the plugin to handle.

Example[edit]

The Administrator module mod_quickicon is a good example of implementing the Observer Pattern. The module Quickicons shows several buttons in Administrator:

J3x-admin-cpanel-mod quickicons.png

Thanks to the plugin system any programmer can add buttons to this module without modifying/hacking the module. How?

The mod_quickicons does this:

// Include buttons defined by published quickicon plugins
JPluginHelper::importPlugin('quickicon');
$app = JFactory::getApplication();
$arrays = (array) $app->triggerEvent('onGetIcons', array($context));

see: https://github.com/joomla/joomla-cms/blob/3.10-dev/administrator/modules/mod_quickicon/helper.php#L149-L153

That means that the plugins in this folders are called:

https://github.com/joomla/joomla-cms/tree/staging/plugins/quickicon

And if they have a method onGetIcons they can inject their own icons. See how Quickicon/extensionupdate does it:

https://github.com/joomla/joomla-cms/blob/staging/plugins/quickicon/extensionupdate/extensionupdate.php#L40

This is a lot of code for just adding a button, however think of the flexibility that this solution provides: now Extension developers can attach buttons to that menu without hacking Joomla.