Wordpress Plugin Development Demystified

39
Yannick Lefebvre (@ylefebvre) Wordpress Plugin Developer Plugin Development Demystified

description

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.

Transcript of Wordpress Plugin Development Demystified

Page 1: Wordpress Plugin Development Demystified

Yannick Lefebvre (@ylefebvre)Wordpress Plugin Developer

Plugin Development Demystified

Page 2: Wordpress Plugin Development Demystified

Plugin Development Demystified

Topics● Introduction● Plugins Overview● File Structure● Actions and Filter Hooks● Activation, Deactivation

and Removal● Administration Pages

● Meta Boxes● Shortcodes● Publishing your plugin● Recommended

Readings● Questions

Page 3: Wordpress Plugin Development Demystified

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/

Page 4: Wordpress Plugin Development Demystified

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

Page 5: Wordpress Plugin Development Demystified

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-content\plugins 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

Page 6: Wordpress Plugin Development Demystified

Plugin File Header

<?php/*Plugin Name: My New Google Analytics PluginPlugin URI: http://yannickcorner.nayanna.bizDescription: New revolutionary GA PluginVersion: 1.0Author: Yannick LefebvreAuthor URI: http://yannickcorner.nayanna.bizLicense: GPL2*/?>

<?php/*Plugin Name: My New Google Analytics PluginPlugin URI: http://yannickcorner.nayanna.bizDescription: New revolutionary GA PluginVersion: 1.0Author: Yannick LefebvreAuthor URI: http://yannickcorner.nayanna.bizLicense: GPL2*/?>

● This information registers the plugin with Wordpress● Most of this data is visible to users in the Plugins

admin section

Page 7: Wordpress Plugin Development Demystified

First plugin sighting

Page 8: Wordpress Plugin Development Demystified

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

Page 9: Wordpress Plugin Development Demystified

Plugin Evaluation Error

Page 10: Wordpress Plugin Development Demystified

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

Page 11: Wordpress Plugin Development Demystified

Assigning an action hook

add_action ( 'hook_name', 'your_function_name', [priority], [accepted_args] );

Exampleadd_action('wp_head', 'newga_header');

add_action ( 'hook_name', 'your_function_name', [priority], [accepted_args] );

Exampleadd_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() {do_action('wp_head');

}

function wp_head() {do_action('wp_head');

}

Page 12: Wordpress Plugin Development Demystified

Full action hook implementation

add_action('wp_head', 'newga_header');

function newga_header() { ?><script type="text/javascript">var gaJsHost = (("https:" == document.location.protocol) ?

"https://ssl." : "http://www.");document.write(unescape("%3Cscript src='" + gaJsHost + "google-

analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));</script><script type="text/javascript">try{var pageTracker = _gat._getTracker("UA-xxxxxx-x");pageTracker._trackPageview();} catch(err) {}</script>

<? }

add_action('wp_head', 'newga_header');

function newga_header() { ?><script type="text/javascript">var gaJsHost = (("https:" == document.location.protocol) ?

"https://ssl." : "http://www.");document.write(unescape("%3Cscript src='" + gaJsHost + "google-

analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));</script><script type="text/javascript">try{var pageTracker = _gat._getTracker("UA-xxxxxx-x");pageTracker._trackPageview();} catch(err) {}</script>

<? }

● Prints script code in page header when wp_head() is called in theme template

Page 13: Wordpress Plugin Development Demystified

Typical Action Hooks for WP Page

Page 14: Wordpress Plugin Development Demystified

Assigning a filter hook

add_filter($tag, $function_to_add, [$priority], [$accepted_args]);

Exampleadd_filter( 'the_content', 'newga_content_filter');

add_filter($tag, $function_to_add, [$priority], [$accepted_args]);

Exampleadd_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) {$content = get_the_content($more_link_text, $stripteaser);$content = apply_filters('the_content', $content);$content = str_replace(']]>', ']]&gt;', $content);echo $content;

}

function the_content($more_link_text = null, $stripteaser = 0) {$content = get_the_content($more_link_text, $stripteaser);$content = apply_filters('the_content', $content);$content = str_replace(']]>', ']]&gt;', $content);echo $content;

}

Page 15: Wordpress Plugin Development Demystified

Full filter hook implementation

add_filter( 'the_content', 'newga_content_filter');

function newga_content_filter($the_content) {

// Search through contents for links and add google analytics code after hrefs

// <a href="http://www.example.com" onClick="recordOutboundLink(this, 'Outbound Links', 'example.com');return false;">

return $the_content;

}

add_filter( 'the_content', 'newga_content_filter');

function newga_content_filter($the_content) {

// Search through contents for links and add google analytics code after hrefs

// <a href="http://www.example.com" onClick="recordOutboundLink(this, 'Outbound Links', 'example.com');return false;">

return $the_content;

}

● The result of this code would be extra tags and details around links in post and page content

Page 16: Wordpress Plugin Development Demystified

Database Read Filters

Page 17: Wordpress Plugin Development Demystified

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

Page 18: Wordpress Plugin Development Demystified

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

Page 19: Wordpress Plugin Development Demystified

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_deactivation_hook(__FILE__, 'my_new_plugin_deactivate');

register_activation_hook(__FILE__, 'my_new_plugin_activate');

register_deactivation_hook(__FILE__, 'my_new_plugin_deactivate');

● The deactivation function should NOT be used to delete user data as deactivation might be temporary

Page 20: Wordpress Plugin Development Demystified

Activation Example

register_activation_hook(__FILE__, 'my_new_plugin_activate');

function my_new_plugin_activate() {if (get_option('NewGA_Options') === false){

$options['gauser'] = '';update_option('NewGA_Options', $options);

}}

register_activation_hook(__FILE__, 'my_new_plugin_activate');

function my_new_plugin_activate() {if (get_option('NewGA_Options') === false){

$options['gauser'] = '';update_option('NewGA_Options', $options);

}}

● This code first checks if the options already exists and creates new default values if they don't.

Page 21: Wordpress Plugin Development Demystified

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

Page 22: Wordpress Plugin Development Demystified

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', 'my_new_plugin_admin_menu');

function my_new_plugin_admin_menu() {

global $pagehooktop;$pagehooktop = add_menu_page( 'New GA

General Options', 'New GA', 'manage_options', 'new-ga', 'my_new_plugin_show_admin', plugins_url( '/icons/NewGA16.png' , __FILE__ ));

}

add_action('admin_menu', 'my_new_plugin_admin_menu');

function my_new_plugin_admin_menu() {

global $pagehooktop;$pagehooktop = add_menu_page( 'New GA

General Options', 'New GA', 'manage_options', 'new-ga', 'my_new_plugin_show_admin', plugins_url( '/icons/NewGA16.png' , __FILE__ ));

}

Page 23: Wordpress Plugin Development Demystified

Administrative Pages

● Implement admin page rendering function

function my_new_plugin_show_admin() { $options = get_option('NewGA_Options');?><h1>New Google Analytics Plugin</h1>

<form name="newgaform" method="post" action="">

GA User ID: <input type="text" name="gauser" value="<?php echo $options['gauser']; ?>"/><br />

<input type="submit" value="Submit" /></form>

<?php }

function my_new_plugin_show_admin() { $options = get_option('NewGA_Options');?><h1>New Google Analytics Plugin</h1>

<form name="newgaform" method="post" action="">

GA User ID: <input type="text" name="gauser" value="<?php echo $options['gauser']; ?>"/><br />

<input type="submit" value="Submit" /></form>

<?php }

Page 24: Wordpress Plugin Development Demystified

Administrative Pages

● Process Post Data

function my_new_plugin_show_admin() {

if (isset($_POST['gauser'])){

$options['gauser'] = $_POST['gauser'];update_option('NewGA_Options', $options);

}$options = get_option('NewGA_Options');?><h1>New Google Analytics Plugin</h1>

<form name="newgaform" method="post" action="">

GA User ID: <input type="text" name="gauser" value="<?php echo $options['gauser']; ?>"/><br />

<input type="submit" value="Submit" /></form>

<?php }

function my_new_plugin_show_admin() {

if (isset($_POST['gauser'])){

$options['gauser'] = $_POST['gauser'];update_option('NewGA_Options', $options);

}$options = get_option('NewGA_Options');?><h1>New Google Analytics Plugin</h1>

<form name="newgaform" method="post" action="">

GA User ID: <input type="text" name="gauser" value="<?php echo $options['gauser']; ?>"/><br />

<input type="submit" value="Submit" /></form>

<?php }

Page 25: Wordpress Plugin Development Demystified

Administrative Pages

● Add Security

function my_new_plugin_show_admin() {

if (isset($_POST['gauser'])){

check_admin_referer('newga');$options['gauser'] = $_POST['gauser'];update_option('NewGA_Options', $options);

}

$options = get_option('NewGA_Options');?><h1>New Google Analytics Plugin</h1>

<form name="newgaform" method="post" action=""><?php wp_nonce_field('newga'); ?>GA User ID: <input type="text" name="gauser" value="<?php

echo $options['gauser']; ?>"/><br /><input type="submit" value="Submit" /></form>

<?php }

function my_new_plugin_show_admin() {

if (isset($_POST['gauser'])){

check_admin_referer('newga');$options['gauser'] = $_POST['gauser'];update_option('NewGA_Options', $options);

}

$options = get_option('NewGA_Options');?><h1>New Google Analytics Plugin</h1>

<form name="newgaform" method="post" action=""><?php wp_nonce_field('newga'); ?>GA User ID: <input type="text" name="gauser" value="<?php

echo $options['gauser']; ?>"/><br /><input type="submit" value="Submit" /></form>

<?php }

Page 26: Wordpress Plugin Development Demystified

Updated Header Code

function newga_header() {$options = get_option('NewGA_Options');?>

<script type="text/javascript">var gaJsHost = (("https:" == document.location.protocol) ?

"https://ssl." : "http://www.");document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));

</script><script type="text/javascript">try{var pageTracker = _gat._getTracker("<?php echo $options['gauser']; ?

>");pageTracker._trackPageview();} catch(err) {}</script>

<?php}

function newga_header() {$options = get_option('NewGA_Options');?>

<script type="text/javascript">var gaJsHost = (("https:" == document.location.protocol) ?

"https://ssl." : "http://www.");document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));

</script><script type="text/javascript">try{var pageTracker = _gat._getTracker("<?php echo $options['gauser']; ?

>");pageTracker._trackPageview();} catch(err) {}</script>

<?php}

● Use option data in header code

Page 27: Wordpress Plugin Development Demystified

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...)

Page 28: Wordpress Plugin Development Demystified

Meta Boxes

function my_new_plugin_admin_menu() {

global $pagehooktop;$pagehooktop = add_menu_page( 'New GA General Options', "New

GA", 'manage_options', 'new-ga', 'my_new_plugin_show_admin', plugins_url( '/icons/NewGA16.png' , __FILE__ ));

add_meta_box('newga_general_meta_box', 'General Settings', 'my_new_plugin_meta_box', $pagehooktop, 'normal', 'high');

wp_enqueue_script('postbox');}

function my_new_plugin_admin_menu() {

global $pagehooktop;$pagehooktop = add_menu_page( 'New GA General Options', "New

GA", 'manage_options', 'new-ga', 'my_new_plugin_show_admin', plugins_url( '/icons/NewGA16.png' , __FILE__ ));

add_meta_box('newga_general_meta_box', 'General Settings', 'my_new_plugin_meta_box', $pagehooktop, 'normal', 'high');

wp_enqueue_script('postbox');}

Page 29: Wordpress Plugin Development Demystified

Meta Boxes Implementation

<form name="newgaform" method="post" action=""><?php wp_nonce_field('newga'); ?>GA User ID: <input type="text" name="gauser" value="<?php echo

$options['gauser']; ?>"/><br /><input type="submit" value="Submit" /></form>

<form name="newgaform" method="post" action=""><?php wp_nonce_field('newga'); ?>GA User ID: <input type="text" name="gauser" value="<?php echo

$options['gauser']; ?>"/><br /><input type="submit" value="Submit" /></form>

● The original simple form code does get more complex when using meta boxes to include all of the right styles

● Original form code:

● New form code on following page...

Page 30: Wordpress Plugin Development Demystified

Meta Boxes Implementation

<form name="newgaform" method="post" action=""><?php wp_nonce_field('newga'); ?><div id="poststuff" class="metabox-holder" style='width: 95%'>

<div id="post-body"><div id="post-body-content">

<?php if ($_GET['page'] == 'new-ga'){

global $pagehooktop;do_meta_boxes($pagehooktop, 'normal', $data);

}?>

</div></div><br class="clear"/>

</div></form><script type="text/javascript">

//<![CDATA[jQuery(document).ready( function($) {

// close postboxes that should be closed$('.if-js-closed').removeClass('if-js-closed').addClass('closed');// postboxes setuppostboxes.add_postbox_toggles('<?php

if ($_GET['page'] == 'new-ga'){

global $pagehooktop;echo $pagehooktop;

}?>');

});//]]>

</script>

<form name="newgaform" method="post" action=""><?php wp_nonce_field('newga'); ?><div id="poststuff" class="metabox-holder" style='width: 95%'>

<div id="post-body"><div id="post-body-content">

<?php if ($_GET['page'] == 'new-ga'){

global $pagehooktop;do_meta_boxes($pagehooktop, 'normal', $data);

}?>

</div></div><br class="clear"/>

</div></form><script type="text/javascript">

//<![CDATA[jQuery(document).ready( function($) {

// close postboxes that should be closed$('.if-js-closed').removeClass('if-js-closed').addClass('closed');// postboxes setuppostboxes.add_postbox_toggles('<?php

if ($_GET['page'] == 'new-ga'){

global $pagehooktop;echo $pagehooktop;

}?>');

});//]]>

</script>Okay, maybe this one is still a bit mystifying :)Okay, maybe this one is still a bit mystifying :)

Page 31: Wordpress Plugin Development Demystified

Meta Boxes Implementation

function my_new_plugin_meta_box() { ?>GA User ID: <input type="text" name="gauser" value="<?php

echo $options['gauser']; ?>"/><br /><input type="submit" value="Submit" />

<?php }

function my_new_plugin_meta_box() { ?>GA User ID: <input type="text" name="gauser" value="<?php

echo $options['gauser']; ?>"/><br /><input type="submit" value="Submit" />

<?php }

● Code to render contents of Meta Box

Page 32: Wordpress Plugin Development Demystified

Adding meta boxes to existing editors

add_meta_box ('linklibrary_meta_box', 'Link Library - Additional Link Parameters', 'll_link_edit_extra', 'link', 'normal', 'high');

add_meta_box ('linklibrary_meta_box', 'Link Library - Additional Link Parameters', 'll_link_edit_extra', 'link', 'normal', 'high');

Page 33: Wordpress Plugin Development Demystified

Saving meta box data added to existing editors

add_action('add_link', 'add_link_field');add_action('edit_link', 'add_link_field');add_action('delete_link', 'delete_link_field');

function add_link_field($link_id) {// Save extra link fields// Can be saved to Wordpress options or custom MySQL tables// $link_id parameter is ID of new or existing link

}

Function delete_link_field($link_id) {// Delete custom link data from custom MySQL tables// or Wordpress options

}

add_action('add_link', 'add_link_field');add_action('edit_link', 'add_link_field');add_action('delete_link', 'delete_link_field');

function add_link_field($link_id) {// Save extra link fields// Can be saved to Wordpress options or custom MySQL tables// $link_id parameter is ID of new or existing link

}

Function delete_link_field($link_id) {// Delete custom link data from custom MySQL tables// or Wordpress options

}

● Action hooks are used to register custom functions to save additional meta box data

Page 34: Wordpress Plugin Development Demystified

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

Page 35: Wordpress Plugin Development Demystified

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');

function youtubevid_func($atts) { extract(shortcode_atts(array( 'id' ), $atts));

$output = '<iframe width="560" height="349" src="http://www.youtube.com/embed/' . $id . '" frameborder="0" allowfullscreen></iframe>';

return $output;}

add_shortcode('youtubevid', 'youtubevid_func');

function youtubevid_func($atts) { extract(shortcode_atts(array( 'id' ), $atts));

$output = '<iframe width="560" height="349" src="http://www.youtube.com/embed/' . $id . '" frameborder="0" allowfullscreen></iframe>';

return $output;}

[youtubevid id='hDV-lgmNQUE'][youtubevid id='hDV-lgmNQUE']

Page 36: Wordpress Plugin Development Demystified

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

Page 37: Wordpress Plugin Development Demystified

TortoiseSVN User Interface

Page 38: Wordpress Plugin Development Demystified

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

Page 39: Wordpress Plugin Development Demystified

Questions?

Thank you for attending this talk on Plugin Development Demystified

Contact: [email protected]: @ylefebvre

Blog : http://yannickcorner.nayanna.bizPlugins: http://profiles.wordpress.org/users/jackdewey/