👋 I'm Mike, a software developer at Automattic, building things with PHP & React, for WordPress & WooCommerce. I write about my hobbies and the projects I work on.

Use WordPress’ URL manipulation functions

WordPress comes with many useful functions developers can make use of in their plugins. Today we’ll look at the URL manipulation functions – add_query_arg and remove_query_arg, both part of WordPress core.


Incredibly useful in plugin development, add_query_arg lets you reliably modify an existing URL by adding or changing it’s query arguments. So for example, if you want to append a query var named ‘page’ set to ‘2’ to the current URL, you could use:

add_query_arg( 'page', 2 );

It’s that easy – you don’t need to worry about the existing query string, or ‘?‘ And ‘&‘ characters as it’s all handled for you.

The function can be used in two ways:

For adding a single argument

In its simplest form, add_query_arg will let you pass a name, value, and optionally an $old_query_or_uri you wish to append the arguments to.

add_query_arg( $key, $value, $old_query_or_uri );

For adding multiple arguments

To add multiple arguments at once you only need add an array, and again optionally the $old_query_or_uri.

add_query_arg( array( $key1 => $value, $key2 => $value ), $old_query_or_uri );

In practice, let’s say we want to add some ordering arguments to a page in admin. We could use this:

add_query_arg( array( 'order' => 'asc', 'orderby' => 'title' ), admin_url( 'admin.php?page=myplugin' ) );

This would give us:



This function will remove single or multiple query arguments from a URL you define, or the current URL. To remove a single argument, pass a string:

// current url: http://yoursite.com/?order=asc&orderby=title
echo remove_query_arg( 'order' );
// echos: http://yoursite.com/?orderby=title

To remove multiple arguments, pass an array of string:

// current url: http://yoursite.com/?order=asc&orderby=title
echo remove_query_arg( array( 'order', 'orderby' ) );
// echos: http://yoursite.com/

Important: Don’t forget to escape!

This caught me out a few weeks ago when I found out (the hard way) that WordPress doesn’t automatically sanitize the current URL if you don’t pass in your own. You need to use esc_url during output:

echo esc_url( add_query_arg( $key, $value ) );

If you forget to do this, a malicious URL, for example one containing some JavaScript, could potentially be output and executed on your page. Escape all the things to be safe!

Avoiding feature bloat in plugins

As a plugin developer, if you release a product (no matter how popular), its a given that over time you’ll receive an influx of feature requests; some user somewhere thinks your plugin should do X to better suit their own personal needs.

Feature requests shouldn’t be dismissed as they can give valuable insight into your user base’s needs and some requests could be perfectly valid oversights on your part. But before coding, consider the impact of adding the feature, work out if it’s actually relevant, and ensure its not just bloat.

nom nom nom bloated

Feature bloat is defined as being defined as:

the tendency of a vendor continually to add unnecessary features to a product that are not of any value to most users, use more system resources than necessary, and often unnecessarily add to the cost of the product

Obviously you don’t want to add something for a single user that’s decremental to the majority, so think it through carefully. Striking the right balance is the key to success.

Continue reading “Avoiding feature bloat in plugins”

Sensible script enqueuing for shortcodes

If you are making a WordPress plugin which uses a shortcode, and said shortcode needs some Javascript enqueued in order to function, it may be tempting to just whack the script in the wp_enqueue_scripts hook and be done with it.

add_action( 'wp_enqueue_scripts', 'enqueue_my_script' );

function enqueue_my_script() {
wp_enqueue_script( 'script-name', plugins_url( '/js/script.js' , __FILE__ ), array(), '1.0.0', false );

Yes this works fine, and is perfectly fine during development, but as the script gets loaded site wide this can be terribly inefficient if the script is specific to your plugin.

To improve matters, you can employ some conditional tags to control where your script gets loaded. For example:

add_action( 'wp_enqueue_scripts', 'enqueue_my_script' );

function enqueue_my_script() {
if ( is_archive() || is_single() ) {
wp_enqueue_script( 'script-name', plugins_url( '/js/script.js' , __FILE__ ), array(), '1.0.0', false );

The above code would enqueue on single posts and within post archives.

For a shortcode however, this can be tricky; you know the shortcode can only be used within posts and pages, but a) thats pretty much site wide anyway and b) you don’t know if the shortcode is being used on the page you are enqueuing the script.

One way you can further optimise your enqueue would be to check the content of a post and see if it contains your code – but again, the drawback is that will most likely only work in certain situations, for example if your shortcode is intended to be used when viewing single posts only.

The sensible method

Forget conditional tags. Forget enqueuing site wide. Instead the solution is quite simple…

First, register your script early, don’t enqueue it.

add_action( 'wp_enqueue_scripts', 'register_my_script' );

function register_my_script() {
wp_register_script( 'script-name', plugins_url( '/js/script.js' , __FILE__ ), array(), '1.0.0', true );

At this point, WordPress is aware your script exists, but it won’t be loaded onto pages. Notice the ‘true’ in your wp_register_script call – this is important and tells WP that you want the script to be loaded in the footer of your page.

Next, and this is the best part, you can enqueue on demand. Now in your plugin where you render your shortcode simply add:

wp_enqueue_script( 'script-name' );

This will enqueue your previously registered script ready for output in the footer of the page.

Now you have a lazy-loaded script, and no need for fancy conditional rules – much better.

Developing for WordPress? Keep your shit secure

If you are developing WordPress plugins (or themes) for distribution via WordPress.org, or for client projects, it should be a no-brainer that writing insecure code can lead to severe consequences.

Having your plugin pulled from the repository, seeing a loss in respect and end-user confidence, or even worse, seeing users fall victim to easily preventable attacks are all possibilities if plugin security is not taken seriously.

Continue reading “Developing for WordPress? Keep your shit secure”

Download Monitor, Legacy Importer & Page Addon officially released

Today I finally finished up and deployed “version 1” of the Download Monitor plugin. This is more of a re-release than an update, hence the version reset (which should also prevent automatic updates!).

If you want to update from a legacy version of Download Monitor, after installing the new version you’ll need to also install and run the Download Monitor Legacy Importer. This will handle migrating all of your data to the new format.

Showcase your download catalog
Showcase your download catalog
I’ve also re-released the page-addon as a separate plugin.

The page addon basically lets you add a [download_page] shortcode which lists all downloads on your site with categories, tags, pagination and searching. It also adds ‘single’ views for your downloads.

Because this used to be part of the main Download Monitor plugin, albeit not as good as it is now, I’ve made this a “Pay what you want” add-on so pay what you feel is fair 🙂

I hope you all find these updates useful!

A New Job Board Plugin for WordPress


I’ve just made a new plugin available (on github) that I’ve been working on to allow you to add and manage job listings on your WP site. It’s name; WP Job Manager.


One of the shortcode options for job display
One of the shortcode options for job display
  • Allows admin to create job listings
  • Frontend submission form supporting guests and registered users
  • Jobs can be set to expire after X days
  • Ajaxified job listing shortcode with search, filters and pagination
  • “Overview” shortcode
  • Widgets for showing jobs in your sidebar
  • Jobs can be ‘applied’ for via a munged email address or a given URL (revealed on click)
  • (should) work with any theme given some style love
  • Manage jobs from admin, including approving jobs if required
  • Employer dashboard for marking jobs filled or ending listings early
  • RSS feeds for the currently viewed search

Read on for a more in-depth look at what this baby can do.

Continue reading “A New Job Board Plugin for WordPress”

Download Monitor Legacy Importer (beta)

As mentioned in my previous post, the new Download Monitor plugin will be making full use of custom post types making legacy data unusable. To help with this I’ve created a new Legacy Importer plugin which will:

  • Find old downloads/meta/tags/categories
  • Convert them to the new format
  • If left enabled, map shortcodes referencing the legacy IDs to the new download IDs.

It will not:

  • Prevent conflicts between old and new IDs. It is recommended that you update the old shortcodes to the new IDs as soon as you can.
  • Import logs and custom formats (custom formats of course are gone in this version in favour of template files).
  • Delete the old tables. You can do this manually,

You can find the beta for this new legacy importer on github for now. It will be placed on WordPress.org once complete.

The New WordPress Download Monitor Plugin

A few months back I announced that the Download Monitor plugin was no longer being maintained. Why? Several reasons really:

  1. Dealing with daily support emails caused a massive headache
  2. Some of the code was embarrassing, and the plugin badly needed a rewrite..
  3. ..but due to legacy this would have been messy and difficult
  4. The donation model didn’t really work, and .org would’t allow ads inside the plugin to fund development

However, despite all of this, given the popularity of the plugin I decided to secretly start building a new version without the restraints of legacy code dictating the way forward…

Continue reading “The New WordPress Download Monitor Plugin”