Cache remote (HTTP) request with Transients API

Catching the weather API remote data

The msg, you’re showing in your question is basically the result from the weather API. And it says, that there’s no data available for your location.

The first thing you want to do is some research in the Codex and the “WP HTTP API”.

The right/WP way to grab remote data

After you’ve learned about the WP HTTP API, you’ll see that the common way to do it is (simplified like this):

$response = wp_remote_request( 'http://example.com?some=parameter', array(
    'ssl_verify' => true
) );

If there’s an error (as shown in your example), then you’ll be able to catch it using the WP_Error class:

is_wp_error( $response ) AND printf(
    'There was an ERROR in your request.<br />Code: %s<br />Message: %s',
    $response->get_error_code(),
    $response->get_error_message()
);

Then it’s time to get the appropriate data. This will show 200 and OK, if everything on the remote side worked out. IMPORTANT: The remote data will likely follow no standard than their internal one. So there can be errors, but you will still get the positive 200/OK message back from them.

$response_code   = wp_remote_retrieve_response_code( $response );
$response_status = wp_remote_retrieve_response_message( $response );

Get the result

Finally it’s time to inspect the result. First, we get rid of leading/trailing white space(s). In the following sample, you see how to use the WP HTTP API to check the header. If we caught JSON, then we go with json_decode() and if we got XML, then we go with PHPs native SimpleXML class.

// Prepare the data:
$content = trim( wp_remote_retrieve_body( $response ) );
// Convert output to JSON
if ( strstr( wp_remote_retrieve_header( $response, 'content-type' ), 'json' ) )
{
    $content = json_decode( $content );
}
// … else, after a double check, we simply go with XML string
elseif ( strstr(
        wp_remote_retrieve_header( $response, 'content-type' ),
        'application/xhtml+xml'
    ) )
{
    // Lets make sure it is really an XML file
    // We also get cases where it's "<?XML" and "<?xml"
    if ( '<?xml' !== strtolower( substr( $content, 0, 5 ) ) )
        return false;

    // Also return stuff wrapped up in <![CDATA[Foo]]>
    $content = simplexml_load_string( $content, null, LIBXML_NOCDATA );
}
// If both didn't work out, then we maybe got a CSV, or something else...

In case of a CSV file, you’ll have to find a custom solution or search for a PHP class on the interwebs. But honestly: If they’re using CSV, it’s easier to search for another service.

Cache the data with a Transient

The Transient API offers a pretty nice way to do this:

// Set Transient
$transient = set_transient(
    'Your cache key',
    $content,
    60*60*6
);

You should then be able to catch the transient with get_transient().

Common errors

An often encountered error is that the SSL verification doesn’t work. Gladly you can turn it on/off pretty easy:

// ON:
add_filter( 'https_ssl_verify', '__return_true' );
// OFF:
add_filter( 'https_ssl_verify', '__return_false' );

There’s one pretty funny thing, as you’ll find out when inspecting the appropriate core file: Core also got a filter for local requests. But don’t get fooled by this one. This filter is only meant to get used in case you’re A) providing a remote service from within your WP install and B) also consuming it yourself! I know, this can be quite a #WTF?! moment that this isn’t a switch for you to use different SSL verification settings between your local install and your production environment/server, but it also has an idea behind it: It’s to test services that you provide yourself as I also explained to the WP G+ community here.

// Debug your own service without SSL verification.
add_filter( 'https_local_ssl_verify', '__return_false' );

Debugging the request & its results

Without diggin’ too deep into the update process, but the WP HTTP API uses the WP_HTTP class. It also offers a nice thing: A debug hook.

do_action( 'http_api_debug', $response, 'response', $class, $args, $url );

Where $response can also be a WP_Error object that maybe tells you more.

Note: From a brief test, this filter seems to only (for some reason) work if you place it as close to where you’re actually doing the request. So maybe you need to call it from within a callback on one of the below filters.

Y NO CURL?

Easy. All the funkiness of the “WP HTTP API”, that I’ve shown above, is basically a function based wrapper for the WP_HTTP class internals, which acts as base class (and will be extended for different scenarios). The extending WP_HTTP_* classes are Fsockopen, Streams, Curl, Proxy, Cookie, Encoding. If you hook a callback to the 'http_api_debug'-action, then the third argument will tell you which class was used for your request. You don’t have to call the classes directly. Just use the functions.

For most remote/HTTP API requests, it’s the WP_HTTP_curl class, which is a wrapper for PHPs native curl library.

Inside the WP_HTTP_curl Class, you’ll find the request() method. This method offers two filters to intercept the SSL behavior: One for local requests 'https_local_ssl_verify' and one for remote requests 'https_ssl_verify'. WP will likely define local as localhost and what you get in return from get_option( 'siteurl' );.

Leave a Comment