Presented at WCPDX 2012
Plugins can do almost anything
There are hundreds of functions in WP core
We’ll touch on a few, but mostly good practices that apply to any plugin
Nothing!
functions.php is not a snippet bucket.
functions.php starts controlling a lot of non-theme functionality, you risk losing that functionality if you switch or update your theme.
Add Google Analytics code to your site:
add_action( 'wp_head', 'google_analytics' );
function google_analytics() {
?>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-XXXXXXX-YY']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<?php
}
Reference:
A few options. To start: Must-Use Plugins
Create a folder called mu-plugins and put it in wp-content
php files in that directory will be executed automatically, no activation or enabling required.
google-analytics.php
<?php
add_action( 'wp_head', 'google_analytics' );
function google_analytics() {
?>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-XXXXXXX-YY']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<?php
}
Reference:

<?php
add_action( 'wp_head', 'kdl_google_analytics' );
function kdl_google_analytics() {
?>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-XXXXXXX-YY']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<?php
}
Reference:
<?php
//Plugin Name: Basic Google Analytics
add_action( 'wp_head', 'kdl_google_analytics' );
function kdl_google_analytics() {
?>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-XXXXXXX-YY']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<?php
}
Reference:

<?php
/*
Plugin Name: Basic Google Analytics
Plugin URI: trepmal.com
Description: Adds very basic Google Analytics code. Need complex tracking? This isn't for you.
Version: 1
Author: Kailey Lampert
Author URI: kaileylampert.com
*/
add_action( 'wp_head', 'bga_google_analytics' );
function bga_google_analytics() {
//...
}
Now that the plugin is named, that makes a better prefix for the function names.

Doesn’t that look better?
Once named, the plugin can be moved to usual plugin directory
/wp-content/plugins
where it can then be activated like any other.

With the Network: true header, the plugin can only be network-activated when on Multisite.
<?php
/*
Plugin Name: Basic Google Analytics
Plugin URI: trepmal.com
Description: Adds very basic Google Analytics code. Need complex tracking? This isn't for you.
Version: 1
Author: Kailey Lampert
Author URI: kaileylampert.com
Network: true
*/
add_action( 'wp_head', 'bga_google_analytics' );
function bga_google_analytics() {
//...
}
Let’s make an admin-side option, so it can work on multisite.
Using the Settings API we can create an option in the admin.
add_action( 'admin_init', 'bga_new_setting' );
function bga_new_setting() {
// option group, option name, callback function to sanitize the input value
register_setting( 'general', 'bga-google_analytics_id', 'strip_tags' );
// id, label, html field, option group
add_settings_field( 'bga-google_analytics_id', 'Google Analytics ID', 'bga_form_field', 'general' );
}
function bga_form_field() {
// the name of our option (defined above)
$ga_id = get_option( 'bga-google_analytics_id', '' );
// the input field
echo "<input type='text' id='bga-google_analytics_id' name='bga-google_analytics_id' value='$ga_id' /><p class='description'>UA-XXXXXXX-YY</p>";
}
Reference:

add_action( 'wp_head', 'bga_google_analytics' );
function bga_google_analytics() {
$ga_id = get_option( 'bga-google_analytics_id', '' );
?>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', '<?php echo $ga_id; ?>']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<?php
}
Reference:
There are no hard and fast rule about what can or can’t be an mu-plugin, but there are some important things to keep in mind
Let’s improve how the ID is saved. Any user-supplied data needs to be sanitized and validated (where applicable). Since the Analytics ID is pretty predictable, we can be strict.
register_setting( 'general', 'bga-google_analytics_id', 'bga_sanitize_validate' );
function bga_sanitize_validate( $given_id ) {
$saved_id = get_option('bga-google_analytics_id', '');
$clean_id = strtoupper( strip_tags( trim( $given_id ) ) );
if ( $saved_id == $clean_id ) {
return $saved_id;
}
if ( empty( $clean_id ) ) {
add_settings_error('', '', 'Your Google Analytics ID number has been removed.', 'updated' );
return '';
}
$almost_ready_id = 'UA-'. trim( $clean_id, 'UA-' );
preg_match( '/(UA-\d+-\d+)$/', $almost_ready_id, $match );
if ( empty( $match ) ) {
add_settings_error('', '', $almost_ready_id . ' is not a valid ID number', 'error' );
return '';
} else {
$ready_id = $almost_ready_id;
}
add_settings_error('', '', 'Your Google Analytics ID number has been saved.', 'updated' );
return $ready_id;
}
Reference:
Sanitization is always a good thing. So is escaping, make sure the data won’t break anything when it is outputted in the HTML.
add_action( 'wp_head', 'bga_google_analytics' );
function bga_google_analytics() {
$ga_id = get_option( 'bga-google_analytics_id', '' );
?>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', '<?php echo esc_js( $ga_id ); ?>']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<?php
}
(Our sanitize function was pretty restrictive, and we know where it will always be outputted so escaping isn’t required, but it’s always good practice.)
Reference:
Ever install a plugin, activate it, then scan through the admin menu to try and find the options page?
Let’s give the user a hint
add_action( 'admin_init', 'bga_new_setting' );
function bga_new_setting() {
add_filter( 'plugin_action_links_'. plugin_basename( __FILE__ ), 'bga_plugin_action_links', 10, 4 );
//...
}
function bga_plugin_action_links( $actions, $plugin_file, $plugin_data, $context ) {
if ( is_plugin_active( $plugin_file ) )
$actions[] = 'Setup';
return $actions;
}
Reference:

<?php
/*
Plugin Name: Basic Google Analytics
Plugin URI: trepmal.com
Description: Adds very basic Google Analytics code. Need complex tracking? This isn't for you.
Version: 1
Author: Kailey Lampert
Author URI: kaileylampert.com
*/
global $basic_google_analytics;
$basic_google_analytics = new Basic_Google_Analytics();
class Basic_Google_Analytics {
function __construct( ) {
add_filter( 'plugin_action_links_'. plugin_basename( __FILE__ ), array( &$this, 'plugin_action_links' ), 10, 4 );
add_action( 'admin_init' , array( &$this, 'admin_init' ) );
add_action( 'wp_head', array( &$this, 'wp_head' ) );
}
function plugin_action_links( $actions, $plugin_file, $plugin_data, $context ) {
if ( is_plugin_active( $plugin_file ) )
$actions[] = '<a href="' . admin_url('options-general.php#bga-google_analytics_id') . '">' . __( 'Setup', 'basic-google-analytics' ) . '</a>';
return $actions;
}
function admin_init() {
register_setting( 'general', 'bga-google_analytics_id', array( &$this, 'sanitize_validate' ) );
add_settings_field( 'bga-google_analytics_id', '<label for="bga-google_analytics_id">' . __( 'Google Analytics ID', 'basic-google-analytics' ) . '</label>' , array( &$this, 'fields_html') , 'general' );
}
function fields_html() {
$ga_id = get_option( 'bga-google_analytics_id', '' );
echo "<input type='text' id='bga-google_analytics_id' name='bga-google_analytics_id' value='$ga_id' /><p class='description'>UA-XXXXXXX-YY</p>";
}
function sanitize_validate( $given_id ) {
$saved_id = get_option('bga-google_analytics_id', '');
$clean_id = strtoupper( strip_tags( trim( $given_id ) ) ); //original, cleaned input
// if no change, carry on
if ( $saved_id == $clean_id ) {
return $saved_id;
}
// has the id been removed entirely?
if ( empty( $clean_id ) ) {
add_settings_error('', '', __( 'Your Google Analytics ID number has been removed.', 'basic-google-analytics' ), 'updated' );
return '';
}
$almost_ready_id = 'UA-'. trim( $clean_id, 'UA-' ); // account for variance, make sure number is prefixed with "UA-"
preg_match( '/(UA-\d+-\d+)$/', $almost_ready_id, $match ); // expects: UA-XXXXXXX-YY
// if empty, then the regex failed to find the correct pattern
if ( empty( $match ) ) {
add_settings_error('', '', sprintf( __( '%s is not a valid ID number', 'basic-google-analytics' ), $almost_ready_id ), 'error' );
return '';
} else {
$ready_id = $almost_ready_id;
}
// if we've made it this far, we have a good id
add_settings_error('', '', __( 'Your Google Analytics ID number has been saved.', 'basic-google-analytics' ), 'updated' );
return $ready_id;
}
function wp_head() {
if ( is_super_admin() ) return; //if not MS, will return true if user can delete other users
// if ( is_user_logged_in() ) return; //or ignore any logged in user
$ga_id = get_option( 'bga-google_analytics_id', '' );
if ( empty( $ga_id ) ) return;
?>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', '<?php echo esc_js( $ga_id ); ?>']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<?php
}
}
I use regularly:
I use less-regularly
Not a plugin, but read this first: Debugging in WordPress