For many WordPress users, even seasoned PHP developers, creating new plugins for WordPress seems like a daunting task. After talking about specific best practices in plugin development last year, this presentation will take a step back to show attendees how simple creating plugins for WordPress from the ground up can be by looking at the architecture of a WordPress plugin, from the basic concepts of registering actions and filters to more advanced concepts such as the creation of admin pages and registering shortcodes.
2. Plugin Development Demystified
Topics
● Introduction ● Meta Boxes
● Plugins Overview ● Shortcodes
● File Structure ● Publishing your plugin
● Actions and Filter Hooks ● Recommended
Readings
● Activation, Deactivation
and Removal ● Questions
● Administration Pages
3. Introduction
● Migrated from Blogger to Wordpress in April 2004
● Released first plugin in March 2005 (Link Library)
● Released 7 Plugins to date
● http://profiles.wordpress.org/users/jackdewey/
4. Plugins Overview
● Allows developers to extend default Wordpress
capabilities
● Open plugin architecture present since very first
versions
● Plugin API constantly refined and expanded
● Plugin code size and complexity vary widely from
one to another
● Functionality stays in place when theme is changed
● Can be installed directly from Wordpress admin or
through a manual upload and activation process
5. Basic Plugin File Structure
● Made from one or more php code file(s)
● Can optionally contain other file types (e.g. images,
text files, translation files, etc...)
● Located directly in the wp-contentplugins directory
or in a sub-directory within the plugins folder
● Entry point is a .php file that contains a specific
plugin header at its top
6. Plugin File Header
<?php
<?php
/*
/*
Plugin Name: My New Google Analytics Plugin
Plugin Name: My New Google Analytics Plugin
Plugin URI: http://yannickcorner.nayanna.biz
Plugin URI: http://yannickcorner.nayanna.biz
Description: New revolutionary GA Plugin
Description: New revolutionary GA Plugin
Version: 1.0
Version: 1.0
Author: Yannick Lefebvre
Author: Yannick Lefebvre
Author URI: http://yannickcorner.nayanna.biz
Author URI: http://yannickcorner.nayanna.biz
License: GPL2
License: GPL2
*/
*/
?>
?>
● This information registers the plugin with Wordpress
● Most of this data is visible to users in the Plugins
admin section
8. Plugin Evaluation Rules
● Function declared in plugin can be called from
theme template file or other plugins
● Function names must be different from Wordpress
core functions and other plugins
● Entire content is evaluated each time site
is rendered
● A single error will usually bring down the
entire site
● Using a local development environment is much
safer than developing on live site
10. Actions and Filter Hooks
● The power of plugins comes from their ability to
register custom functions to be called at specific
points during the execution of Wordpress
● This process is called hooking
● Two types of hooks
● Action hooks allow for code to be executed at a specific point
during the page processing loop
● Filter hooks are called during Wordpress data processing to
allow plugins to modify, increase or reduce data before it is
displayed
11. Assigning an action hook
add_action ( 'hook_name', 'your_function_name', [priority],
add_action ( 'hook_name', 'your_function_name', [priority],
[accepted_args] );
[accepted_args] );
Example
Example
add_action('wp_head', 'newga_header');
add_action('wp_head', 'newga_header');
● Most hook names can be found in Wordpress
Codex. Includes description of arguments and hook
purpose.
● More complete list is available on third-party sites,
but these lack additional information
● A third source is the Wordpress code itself
function wp_head() {
function wp_head() {
do_action('wp_head');
do_action('wp_head');
}
}
12. Full action hook implementation
add_action('wp_head', 'newga_header');
add_action('wp_head', 'newga_header');
function newga_header() { ?>
function newga_header() { ?>
<script type="text/javascript">
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ?
var gaJsHost = (("https:" == document.location.protocol) ?
"https://ssl." : "http://www.");
"https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-
document.write(unescape("%3Cscript src='" + gaJsHost + "google-
analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
</script>
<script type="text/javascript">
<script type="text/javascript">
try{
try{
var pageTracker = _gat._getTracker("UA-xxxxxx-x");
var pageTracker = _gat._getTracker("UA-xxxxxx-x");
pageTracker._trackPageview();
pageTracker._trackPageview();
} catch(err) {}
} catch(err) {}
</script>
</script>
<? }
<? }
● Prints script code in page header when wp_head()
is called in theme template
14. Assigning a filter hook
add_filter($tag, $function_to_add, [$priority], [$accepted_args]);
add_filter($tag, $function_to_add, [$priority], [$accepted_args]);
Example
Example
add_filter( 'the_content', 'newga_content_filter');
add_filter( 'the_content', 'newga_content_filter');
● Filters hooks receive data arguments that they can
modify within their processing function and must
return data, modified or intact
● In WP core, filter function are called with one or
more data parameters. The number of parameters
needs to be used for accepted_args value
function the_content($more_link_text = null, $stripteaser = 0) {
function the_content($more_link_text = null, $stripteaser = 0) {
$content = get_the_content($more_link_text, $stripteaser);
$content = get_the_content($more_link_text, $stripteaser);
$content = apply_filters('the_content', $content);
$content = apply_filters('the_content', $content);
$content = str_replace(']]>', ']]>', $content);
$content = str_replace(']]>', ']]>', $content);
echo $content;
echo $content;
}
}
15. Full filter hook implementation
add_filter( 'the_content', 'newga_content_filter');
add_filter( 'the_content', 'newga_content_filter');
function newga_content_filter($the_content) {
function newga_content_filter($the_content) {
// Search through contents for links and add google analytics
// Search through contents for links and add google analytics
code after hrefs
code after hrefs
// <a href="http://www.example.com"
// <a href="http://www.example.com"
onClick="recordOutboundLink(this, 'Outbound Links',
onClick="recordOutboundLink(this, 'Outbound Links',
'example.com');return false;">
'example.com');return false;">
return $the_content;
return $the_content;
}
}
● The result of this code would be extra tags and
details around links in post and page content
17. Storing plugin data
● Most plugins have options for user configuration
● There are multiple ways to store custom plugin data
● Wordpress Options: get_option / set_option, single options or
arrays
● Custom tables in SQL schema
● Config files in plugin directory
18. Storing plugin data using Wordpress Options
● update_option( $option_name, $newvalue );
● Creates option if it does not exist. Updates if it does.
● Accepts single variable or array as value
● get_option( $show, $default );
● Show is name of option
● Default is optional value to be returned if option does not exist
● Initial option value is usually created in plugin
activation function
19. Activation / Deactivation
● First step in many plugins is to register functions
that will be called on activation and deactivation
register_activation_hook(__FILE__, 'my_new_plugin_activate');
register_activation_hook(__FILE__, 'my_new_plugin_activate');
register_deactivation_hook(__FILE__, 'my_new_plugin_deactivate');
register_deactivation_hook(__FILE__, 'my_new_plugin_deactivate');
● The deactivation function should NOT be used to
delete user data as deactivation might be temporary
20. Activation Example
register_activation_hook(__FILE__, 'my_new_plugin_activate');
register_activation_hook(__FILE__, 'my_new_plugin_activate');
function my_new_plugin_activate() {
function my_new_plugin_activate() {
if (get_option('NewGA_Options') === false)
if (get_option('NewGA_Options') === false)
{
{
$options['gauser'] = '';
$options['gauser'] = '';
update_option('NewGA_Options', $options);
update_option('NewGA_Options', $options);
}
}
}
}
● This code first checks if the options already exists
and creates new default values if they don't.
21. Uninstallation Code
● Uninstallation code should remove all traces of the
plugin
register_uninstall_hook(__FILE__, 'my_new_plugin_uninstall');
register_uninstall_hook(__FILE__, 'my_new_plugin_uninstall');
● Can also be implemented as straight PHP file called
uninstall.php in plugin directory that will execute
when uninstalled
22. Administrative Pages
● Admin page allows users to configure plugin options
● Register function to get called when the admin
menu is built
add_action('admin_menu',
add_action('admin_menu',
'my_new_plugin_admin_menu');
'my_new_plugin_admin_menu');
function my_new_plugin_admin_menu() {
function my_new_plugin_admin_menu() {
global $pagehooktop;
global $pagehooktop;
$pagehooktop = add_menu_page( 'New GA
$pagehooktop = add_menu_page( 'New GA
General Options', 'New GA', 'manage_options',
General Options', 'New GA', 'manage_options',
'new-ga', 'my_new_plugin_show_admin', plugins_url(
'new-ga', 'my_new_plugin_show_admin', plugins_url(
'/icons/NewGA16.png' , __FILE__ ));
'/icons/NewGA16.png' , __FILE__ ));
}
}
23. Administrative Pages
● Implement admin page rendering function
function my_new_plugin_show_admin() {
function my_new_plugin_show_admin() {
$options = get_option('NewGA_Options');
$options = get_option('NewGA_Options');
?>
?>
<h1>New Google Analytics Plugin</h1>
<h1>New Google Analytics Plugin</h1>
<form name="newgaform" method="post" action="">
<form name="newgaform" method="post" action="">
GA User ID: <input type="text" name="gauser" value="<?php
GA User ID: <input type="text" name="gauser" value="<?php
echo $options['gauser']; ?>"/><br />
echo $options['gauser']; ?>"/><br />
<input type="submit" value="Submit" />
<input type="submit" value="Submit" />
</form>
</form>
<?php }
<?php }
24. Administrative Pages
● Process Post Data
function my_new_plugin_show_admin() {
function my_new_plugin_show_admin() {
if (isset($_POST['gauser']))
if (isset($_POST['gauser']))
{
{
$options['gauser'] = $_POST['gauser'];
$options['gauser'] = $_POST['gauser'];
update_option('NewGA_Options', $options);
update_option('NewGA_Options', $options);
}
}
$options = get_option('NewGA_Options');
$options = get_option('NewGA_Options');
?>
?>
<h1>New Google Analytics Plugin</h1>
<h1>New Google Analytics Plugin</h1>
<form name="newgaform" method="post" action="">
<form name="newgaform" method="post" action="">
GA User ID: <input type="text" name="gauser" value="<?php
GA User ID: <input type="text" name="gauser" value="<?php
echo $options['gauser']; ?>"/><br />
echo $options['gauser']; ?>"/><br />
<input type="submit" value="Submit" />
<input type="submit" value="Submit" />
</form>
</form>
<?php }
<?php }
25. Administrative Pages
● Add Security
function my_new_plugin_show_admin() {
function my_new_plugin_show_admin() {
if (isset($_POST['gauser']))
if (isset($_POST['gauser']))
{
{
check_admin_referer('newga');
check_admin_referer('newga');
$options['gauser'] = $_POST['gauser'];
$options['gauser'] = $_POST['gauser'];
update_option('NewGA_Options', $options);
update_option('NewGA_Options', $options);
}
}
$options = get_option('NewGA_Options');
$options = get_option('NewGA_Options');
?>
?>
<h1>New Google Analytics Plugin</h1>
<h1>New Google Analytics Plugin</h1>
<form name="newgaform" method="post" action="">
<form name="newgaform" method="post" action="">
<?php wp_nonce_field('newga'); ?>
<?php wp_nonce_field('newga'); ?>
GA User ID: <input type="text" name="gauser" value="<?php
GA User ID: <input type="text" name="gauser" value="<?php
echo $options['gauser']; ?>"/><br />
echo $options['gauser']; ?>"/><br />
<input type="submit" value="Submit" />
<input type="submit" value="Submit" />
</form>
</form>
<?php }
<?php }
27. Meta Boxes
● Meta Boxes are the 'containers' that group together
data fields in the default Wordpress admin sections
● Plugins can use meta boxes in their own admin
sections or to add custom sections to other parts of
Wordpress (e.g. Posts Editor, Links Editor, etc...)
28. Meta Boxes
function my_new_plugin_admin_menu() {
function my_new_plugin_admin_menu() {
global $pagehooktop;
global $pagehooktop;
$pagehooktop = add_menu_page( 'New GA General Options', "New
$pagehooktop = add_menu_page( 'New GA General Options', "New
GA", 'manage_options', 'new-ga', 'my_new_plugin_show_admin',
GA", 'manage_options', 'new-ga', 'my_new_plugin_show_admin',
plugins_url( '/icons/NewGA16.png' , __FILE__ ));
plugins_url( '/icons/NewGA16.png' , __FILE__ ));
add_meta_box('newga_general_meta_box', 'General Settings',
add_meta_box('newga_general_meta_box', 'General Settings',
'my_new_plugin_meta_box', $pagehooktop, 'normal', 'high');
'my_new_plugin_meta_box', $pagehooktop, 'normal', 'high');
wp_enqueue_script('postbox');
wp_enqueue_script('postbox');
}
}
29. Meta Boxes Implementation
● The original simple form code does get more
complex when using meta boxes to include all of the
right styles
● Original form code:
<form name="newgaform" method="post" action="">
<form name="newgaform" method="post" action="">
<?php wp_nonce_field('newga'); ?>
<?php wp_nonce_field('newga'); ?>
GA User ID: <input type="text" name="gauser" value="<?php echo
GA User ID: <input type="text" name="gauser" value="<?php echo
$options['gauser']; ?>"/><br />
$options['gauser']; ?>"/><br />
<input type="submit" value="Submit" />
<input type="submit" value="Submit" />
</form>
</form>
● New form code on following page...
30. Meta Boxes Implementation
<form name="newgaform" method="post" action="">
<form name="newgaform" method="post" action="">
<?php wp_nonce_field('newga'); ?>
<?php wp_nonce_field('newga'); ?>
<div id="poststuff" class="metabox-holder" style='width: 95%'>
<div id="poststuff" class="metabox-holder" style='width: 95%'>
<div id="post-body">
<div id="post-body">
<div id="post-body-content">
<div id="post-body-content">
<?php
<?php
if ($_GET['page'] == 'new-ga')
if ($_GET['page'] == 'new-ga')
{
{
global $pagehooktop;
global $pagehooktop;
do_meta_boxes($pagehooktop, 'normal', $data);
do_meta_boxes($pagehooktop, 'normal', $data);
}
}
?>
?>
</div>
</div>
</div>
</div>
<br class="clear"/>
<br class="clear"/>
</div>
</div>
</form>
</form>
<script type="text/javascript">
<script type="text/javascript">
//<![CDATA[
//<![CDATA[
jQuery(document).ready( function($) {
jQuery(document).ready( function($) {
// close postboxes that should be closed
// close postboxes that should be closed
$('.if-js-closed').removeClass('if-js-closed').addClass('closed');
$('.if-js-closed').removeClass('if-js-closed').addClass('closed');
// postboxes setup
// postboxes setup
postboxes.add_postbox_toggles('<?php
postboxes.add_postbox_toggles('<?php
if ($_GET['page'] == 'new-ga')
if ($_GET['page'] == 'new-ga')
{
{
global $pagehooktop;
global $pagehooktop;
echo $pagehooktop;
echo $pagehooktop;
}
}
?>');
?>');
});
});
//]]>
//]]>
Okay, maybe this one is still a bit mystifying :)
Okay, maybe this one is still a bit mystifying :)
</script>
</script>
31. Meta Boxes Implementation
● Code to render contents of Meta Box
function my_new_plugin_meta_box() { ?>
function my_new_plugin_meta_box() { ?>
GA User ID: <input type="text" name="gauser" value="<?php
GA User ID: <input type="text" name="gauser" value="<?php
echo $options['gauser']; ?>"/><br />
echo $options['gauser']; ?>"/><br />
<input type="submit" value="Submit" />
<input type="submit" value="Submit" />
<?php }
<?php }
32. Adding meta boxes to existing editors
add_meta_box ('linklibrary_meta_box', 'Link Library - Additional
add_meta_box ('linklibrary_meta_box', 'Link Library - Additional
Link Parameters', 'll_link_edit_extra', 'link', 'normal',
Link Parameters', 'll_link_edit_extra', 'link', 'normal',
'high');
'high');
33. Saving meta box data added to existing editors
● Action hooks are used to register custom functions
to save additional meta box data
add_action('add_link', 'add_link_field');
add_action('add_link', 'add_link_field');
add_action('edit_link', 'add_link_field');
add_action('edit_link', 'add_link_field');
add_action('delete_link', 'delete_link_field');
add_action('delete_link', 'delete_link_field');
function add_link_field($link_id) {
function add_link_field($link_id) {
// Save extra link fields
// Save extra link fields
// Can be saved to Wordpress options or custom
// Can be saved to Wordpress options or custom MySQL tables
MySQL tables
// $link_id parameter is ID of new or existing
// $link_id parameter is ID of new or existing link
link
}
}
Function delete_link_field($link_id) {
Function delete_link_field($link_id) {
// Delete custom link data from custom MySQL tables
// Delete custom link data from custom MySQL tables
// or Wordpress options
// or Wordpress options
}
}
34. Adding a shortcode
● Simple codes used in a post or page to insert
content
● [gallery]
● [gallery id="123" size="medium"]
● Can also be used to output special code before and
after content
● [style1]My text block[/style1]
● These are often introduced by themes
● Dangerous to use since they will become regular text if you
change to a new theme without these codes
● Consider creating a simple shortcode plugin if you
repeatedly insert similar code on site
35. Shortcode Implementation
● Since shortcodes are found anywhere within posts /
pages, they must return their output instead of
displaying it directly
add_shortcode('youtubevid', 'youtubevid_func');
add_shortcode('youtubevid', 'youtubevid_func');
function youtubevid_func($atts) {
function youtubevid_func($atts) {
extract(shortcode_atts(array(
extract(shortcode_atts(array(
'id'
'id'
), $atts));
), $atts));
$output = '<iframe width="560" height="349"
$output = '<iframe width="560" height="349"
src="http://www.youtube.com/embed/' . $id . '" frameborder="0"
src="http://www.youtube.com/embed/' . $id . '" frameborder="0"
allowfullscreen></iframe>';
allowfullscreen></iframe>';
return $output;
return $output;
}
}
[youtubevid id='hDV-lgmNQUE']
[youtubevid id='hDV-lgmNQUE']
36. Publishing your plugin on Wordpress.org
● Any Open Source plugin can be published on
Wordpress.org with a few very easy steps:
1) Register on Wordpress.org
2) Submit a plugin name and description
3) Receive approval within a few days
4) Create a plugin readme following wordpress.org template
5) Publish plugin to Wordpress subversion repository
38. Recommended Readings
● Professional Wordpress Plugin
Development by Brad Williams, Ozh
Richard and Justin Tadlock, published by
WROX Press
● Wordpress Codex
(codex.wordpress.com)
● PHP.net
● StackOverflow.com Programming
Samples
● Today's presentation and code samples
available at:
● http://yannickcorner.nayanna.biz/wcmtl2011
39. Questions?
Thank you for attending this talk on
Plugin Development Demystified
Contact: ylefebvre@gmail.com
Twitter: @ylefebvre
Blog : http://yannickcorner.nayanna.biz
Plugins: http://profiles.wordpress.org/users/jackdewey/