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