Advertisement
Creative Coding

Interacting with WordPress' Plug-in & Theme API

by

The WordPress Repository API is the API used to fetch plug-in and theme information for use on your admin pages. For instance it displays the latest plug-ins on the dashboard, allows you to view themes on your theme tab and allows you to search for, and install, plug-ins straight from the repository. In this tutorial we're going to look at how this API works and how it can be used to access information such as your plug-in's rating, how many times it's been downloaded, or even its ReadMe sections. Using this API, for instance, you can host a link on your website that will always point to the latest version of your plug-in or theme.

When WordPress gathers information on plug-ins and themes from the repository it does so by sending a request to one of two URLs.

  • For plug-ins: http://api.wordpress.org/plugins/info/1.0/
  • For themes: http://api.wordpress.org/themes/info/1.0/

The request takes the form of an array with an 'action' and 'request' key.

	$response = wp_remote_post(
		'http://api.wordpress.org/plugins/info/1.0/',
		array(
			'body' => array(
				'action' => $action,
				'request'=>serialize((object)$args)
			)
		)
	);

Getting Details of a Plug-in or Theme

When retrieving data about a plugin or theme, the 'action' should be set to plugin_information or theme_information respectively. The value of the request key should be a serialized object with a slug property (the slug of the theme/plug-in) and a field property, which indicates what data we are after (the available fields are detailed below). In the above snippet, $args should be an associative array with keys given by those properties.

The return value from wp_remote_post, $response, may be a WP_Query error or else a genuine response from the repository containing an error message. But if all went well, then the returned plug-in or theme object can be extracted from it with the following:

	$returned_object = maybe_unserialize(wp_remote_retrieve_body($response));

Querying the Plug-in & Theme Repositories

To retrieve a list of themes/plug-ins that match certain criteria, the action should be set to query_themes or query_plugins. This should be accompanied with an appropriate key (e.g. 'author', to get plugins/themes by a particular author) in the $args array. The possible criteria are given below.

Again (assuming no errors have occurred), the array of matching plug-ins should be given by:

	$returned_object = maybe_unserialize(wp_remote_retrieve_body($response));
	$plugins = $returned_object->plugins;

and similarly, for themes:

	$returned_object = maybe_unserialize(wp_remote_retrieve_body($response));
	$themes = $returned_object->themes;

Each theme/plugin object in the array has the same properties as those determined by the fields key in the $args array. The available fields are listed below, as well as the default fields (for *_information queries). Please note that the default values are different for different actions.


Plug-in Properties

As noted above $args is an associative array which can contain the following fields:

  • slug – (When the action is plugin_information). The slug of the plug-in to return the data for.
  • browse – (When the action is query_plugins). Takes the values featured, popular or new.
  • author – (When the action is query_plugins). The author's WordPress username, to retrieve plugins by a particular author.
  • tag – (When the action is query_plugins). Tag with which to retrieve plugins for.
  • search – (When the action is query_plugins). A search term, with which to search the repository.
  • fields – an array with possible fields (listed below) as keys and true or false value to return data for that field or not. The fields that are included make up the properties of the returned object above. The possible fields are (default set to true, unless otherwise stated):
    • version – latest
    • author – author name and link to profile
    • requires – the minimum WordPress version required
    • tested – the latest WordPress version tested
    • compatibility – an array which contains an array for each version of your plug-in. This array stores the number of votes, the number of 'works' votes and this number as a percentage.
    • downloaded – the number of downloads
    • rating – as a percentage
    • num_ratings – number of ratings
    • sections – this is an array with the HTML for each section on the WordPress plug-in page as values, keys can include 'description', 'installation', 'screenshots', 'changelog' and 'faq'.
    • download_link – points to repository hosted ZIP file of the plugin's latest version
    • description – (default false)
    • short_description – (default false)

Other fields include 'name', 'slug', 'author_profile', 'tags', 'homepage', 'contributors', 'added' and 'last_updated'.

Example

As a brief example, let's display a list of plug-ins by a particular author, along with how many times they've been downloaded:

// Set the arguments. For brevity of code, I will set only a few fields.
$args = array(
	'author' => 'stephenh1988',
	'fields' => array(
		'downloaded' => true,
		'downloadlink' => true
	)
);

// Make request and extract plug-in object. Action is query_plugins
$response = wp_remote_post(
	'http://api.wordpress.org/plugins/info/1.0/',
	array(
		'body' => array(
			'action' => 'query_plugins',
			'request' => serialize((object)$args)
		)
	)
);

if ( !is_wp_error($response) ) {
	$returned_object = unserialize(wp_remote_retrieve_body($response));
	$plugins = $returned_object->plugins;
	if ( !is_array($plugins) ) {
		// Response body does not contain an object/array
		echo "An error has occurred";
	}
	else {
		// Display a list of the plug-ins and the number of downloads
		if ( $plugins ) {
			echo '<ul>';
			foreach ( $plugins as $plugin ) {
				echo "<li>".esc_html($plugin->name)." (downloaded ".esc_html($plugin->downloaded)." times)</li>";
			}
		}
	}
}
else {
	// Error object returned
	echo "An error has occurred";
}

Theme Properties

The themes API request is very similar, although there are slightly different fields available.

  • slug – (When the action is theme_information) The slug of the theme to return the data for.
  • browse – (When the action is query_themes). Takes the values featured, new or updated.
  • author – (When the action is query_themes). The author's username, to retrieve themes by a particular author.
  • tag – (When the action is query_themes). An array of tags with which to retrieve themes for.
  • search – (When the action is query_themes). A search term, with which to search the repository.
  • fields – again an array with a true or false value for each key (field). The fields that are included make up the properties of the returned object above. The possible fields are (default set to true, unless otherwise stated):
    • version – (latest)
    • author
    • preview_url – URL to wp-themes.com hosted preview
    • screenshot_url – URL to screenshot image
    • screenshot_count* – number of screenshots the theme has
    • screenshots* – array of screenshot URLs
    • rating – (as a percentage)
    • num_ratings – number of ratings
    • downloaded – number of downloads
    • sections
    • description
    • download_link

Other fields include 'name', 'slug', 'tags', 'homepage', 'contributors', and 'last_updated'.

*Please note that in the future themes will be [allowed multiple screenshots][1].

Example

// Set the arguments. For brevity of code, I will use most of the defaults
$args = array(
	'slug' => 'Desk Mess Mirrored',
	'fields' => array( 'screenshot_url'=> true )
);

// Make request and extract plug-in object
$response = wp_remote_post(
	'http://api.wordpress.org/themes/info/1.0/',
	array(
		'body' => array(
			'action' => 'theme_information',
			'request' => serialize((object)$args)
		)
	)
);

if ( !is_wp_error($response) ) {
	$theme = unserialize(wp_remote_retrieve_body($response));
	if ( !is_object($theme) && !is_array($theme) ) {
		// Response body does not contain an object/array
		echo "An error has occurred";
	}
	else {
		// Sanitize data:
		$preview_url = esc_url($theme->preview_url);
		$screenshot_url = esc_attr($theme->screenshot_url);
		$rating = esc_attr($theme->rating);

		// Display the rating of the theme, a screenshot and link to the preview of the theme
		echo "This theme has a rating of {$rating}%. <a href='$preview_url'> View a preview</a>";
		echo "<img src='$screenshot_url' />";
	}
}
else {
	// Error object returned
	echo "An error has occurred";
}

In these examples, I have used (for the most part) the default fields – but in a bid to save that little bit of bandwidth, you should explicitly state which fields you do, and don't want.


Caching

This is an excellent example of where caching, specifically transients, can (and should) be used. Caching the data means we won't fetch the information from the repository on every page load – which would slow the site load. As a simple example, when I ran the example above without caching it took 0.522 seconds to retrieve the data (which is respectable). Once I started used transients, it dropped to 0.001 seconds. In any case we don't need to get this information on every page load – in fact there is little reason to update this data more than once a day (or maybe longer).

If you are not sure how to use transients, you can read up on them in this article.

Let's implement transients in a generic function that will retrieve theme information, given a specific theme (and other arguments):

/**
* Returns a theme object given an array $args or WP_Error object if there is an error
* $args should contain a 'slug' key with the theme's name
* and 'fields' key holding an array of fields to retrieve.
*/
function sh_get_theme_information( $args ) {
	// Set the $request array
	$request = array(
		'body' => array(
			'action' => 'theme_information',
			'request' => serialize((object)$args)
		)
	);

	// Generate a cache key that would hold the response for this request:
	$key='sh_theme_'.md5(serialize($request));

	// Check transient. If it's there - use that, if not re fetch the theme
	if ( false === ($theme = get_transient($key)) ) {

		// Theme not found - we need to re-fetch it
		$response = wp_remote_post('http://api.wordpress.org/themes/info/1.0/',$request);

		if ( is_wp_error($response) )
			return $response;

		$theme = unserialize(wp_remote_retrieve_body($response));

		if ( !is_object($theme) && !is_array($theme) )
			return new WP_Error('theme_api_error', 'An unexpected error has occurred');

		// Set transient for next time... keep it for 24 hours should be good
		set_transient($key, $theme, 60*60*24);
	}

	return $theme;
}

To use this function:

// Set the arguments. For brevity of code, I will just use (mostly) the defaults
$args = array(
	'slug' => 'Desk Mess Mirrored',
	'fields' => array( 'screenshot_url' => true )
);

// Get the theme
$theme = sh_get_theme_information( $args );

// Display theme information (or error message).
if ( is_wp_error($theme) ) {
	echo 'An unexpected error has occurred';

}
else {
	// Sanitize data:
	$preview_url = esc_url($theme->preview_url);
	$screenshot_url = esc_attr($theme->screenshot_url);
	$rating = esc_attr($theme->rating);

	// Display theme rating, screenshot and preview link
	echo "This theme has a rating of {$rating}%.<a href='{$preview_url}'> View a preview </a>";
	echo "<img src='{$screenshot_url}'/>";
}

The basic pattern for retrieving data from any API is broadly the same. Turn a request into a unique, with which to cache the results. Then check if data exists for the key, if it does – great, we can just return that data. Otherwise, fetch the data remotely. Once you've received the response, check for errors, and if there aren't any update the transient and return the newly fetched data.

As a second example, we can construct a function that returns an array of plug-ins by a particular author:

function sh_get_plugins_by_author($author='') {

	if ( empty($author) )
		return false;

	$key = sanitize_key('sh_plugins_'.$author);

	if ( false === ( $plugins = get_transient($key) ) ) {
		$args = array('author'=>$author, 'fields'=>array('downloaded'=>true,'downloadlink'=>true));
		$response = wp_remote_post(
			'http://api.wordpress.org/plugins/info/1.0/',
			array(
				'body' => array(
					'action' => 'query_plugins',
					'request' => serialize((object)$args)
				)
			)
		);
		$plugin_response = unserialize(wp_remote_retrieve_body($response));
		$plugins = $plugin_response->plugins;

		// Set transient for next time... keep it for 24 hours should be good
		set_transient($key, $plugins, 60*60*24);
	}

	return $plugins;
}

(Of course, you can always use the WordPress API in conjunction with soft caching, which I talked about in this article).

You can see a live demonstration of using the WordPress Repository API on my site's WordPress plug-ins page.

Related Posts