Implementing Google Universal Analytics Measurement Protocol in PHP and WordPress

Google Universal AnalyticsI’ve had some historic difficulty in being able to track ecommerce transactions in Google Analytics that happen ‘behind the scenes’ of the Smart Insights site – typically when PayPal sends an IPN message, server to server, for a subscription renewal in our case. Since those messages don’t touch the browser, you can’t use the Google Analytics javascript to track them. So I started looking to see how/if I could send data directly to Google Analytics from a backend script running on our WordPress install.

I found several custom PHP classes that seemed, initially, to do the trick;

Problem is, they’re both pretty old and seem to be no longer developed, so I figured there must be something better. My colleague Dave Chaffey then pointed me in the direction of the Google Measurement Protocol, which I hadn’t heard of before.

What is the Google Measurement Protocol and why should I care?

The Measurement Protocol is an addition to the latest version of Google Analytics, which is called ‘Universal Analytics’. From the explainer documentation;

The Google Analytics Measurement Protocol allows developers to make HTTP requests to send raw user interaction data directly to Google Analytics servers. This allows developers to measure how users interact with their business from almost any environment.

More at ‘Measurement Protocol Overview

Google allows you to form and send any of the following types of Analytics request via this API;

  • Page Tracking
  • Event Tracking
  • Ecommerce Tracking
  • Social Interactions
  • Exception Tracking
  • User Timing Tracking
  • App tracking

Using the Google Measurement Protocol in PHP and WordPress

NOTE:  You can only use the Measurement Protocol if you have the Universal Analytics tracking code set up on your site. It will not work with the standard tracking code. If you need help with this, there’s a fantastic introduction at the KissMetrics site: Universal Analytics: Switching to the Next Version of Google Analytics

As is typical with Google and ‘beta’ products (as the Universal Analytics code is still in beta) they give pretty sparse examples and no specific code, so it’s up to you to work things out yourself.

In my case I wanted to be able to fire two types of requests to Google – an ecommerce transaction when a membership renewal occurs, and a custom pageview which is used as a ‘Goal‘ on Analytics.

As I wanted the code to be reusable throughout the WordPress install, I came up with a set of functions (mine lives in a function file of its own, yours could live in your standard theme functions.php file if you wish).

There are two steps needed to make a request to the Measurement Protocol.

1. Obtain the current user’s unique identifier (CID) from the Google Analytics cookie (if it exists!)

Very vital, this, and something I ended up banging my head on the desk about. When a user lands on your site, the Google Analytics code assigns them a unique identifer. This lives in a cookie, and follows them across all requests they make on your site. If you fire a server side request and make up your own unique identifier, the Analytics service has no way of matching the two up.

Obviously this isn’t a problem in my original use case – a behind the scenes ecommerce renewal won’t have a browser cookie to get anything from, and it doesn’t matter that you assign it a unique identifier all of its own. But – if you decide to use the Measurement Protocol for other things, front of site (and I did within 10 minutes of finishing my first code), then you do need to check for the existence of that cookie first. If you don’t, a user that has logged page history will suddenly become anonymous when you fire your Measurement Protocol request. This means that things like conversion funnels won’t work, which is not good.

So, here’s how we parse the GA cookie;

// Handle the parsing of the _ga cookie or setting it to a unique identifier
function gaParseCookie() {
  if (isset($_COOKIE['_ga'])) {
    list($version,$domainDepth, $cid1, $cid2) = split('[\.]', $_COOKIE["_ga"],4);
    $contents = array('version' => $version, 'domainDepth' => $domainDepth, 'cid' => $cid1.'.'.$cid2);
    $cid = $contents['cid'];
  }
  else $cid = gaGenUUID();
  return $cid;
}

This code will pluck the CID value from the cookie if it exists. If not, it runs another function to generate one – gaGenUUID(). The Measurement Protocol documents say that the CID must “be a random UUID (version 4) as described in http://www.ietf.org/rfc/rfc4122.txt“. Ah, okay. So let’s create one of those, then;

// Generate UUID v4 function - needed to generate a CID when one isn't available
function gaGenUUID() {
  return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
    // 32 bits for "time_low"
    mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),

    // 16 bits for "time_mid"
    mt_rand( 0, 0xffff ),

    // 16 bits for "time_hi_and_version",
    // four most significant bits holds version number 4
    mt_rand( 0, 0x0fff ) | 0x4000,

    // 16 bits, 8 bits for "clk_seq_hi_res",
    // 8 bits for "clk_seq_low",
    // two most significant bits holds zero and one for variant DCE1.1
    mt_rand( 0, 0x3fff ) | 0x8000,

    // 48 bits for "node"
    mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
  );
}

So, we now have a CID that we can use.

2. Build and send our Measurement Protocol request

There are two functions used here – one to build the request, the other to send it. In both cases you call gaBuildHit() with two parameters – the method (which in my example is either ‘pageview’ or ‘ecommerce’) and an array of data that is needed by the Measurement Protocol. This differs depending on the method you’re using, so let’s jump into the code and I’ll explain more afterward.

function gaBuildHit( $method = null, $info = null ) {
  if ( $method && $info) {

  // Standard params
  $v = 1;
  $tid = "UA-XXXXXXX"; // Put your own Analytics ID in here
  $cid = gaParseCookie();

  // Register a PAGEVIEW
  if ($method === 'pageview') {

    // Send PageView hit
    $data = array(
      'v' => $v,
      'tid' => $tid,
      'cid' => $cid,
      't' => 'pageview',
      'dt' => $info['title'],
      'dp' => $info['slug']
    );

    gaFireHit($data);

  } // end pageview method

  // Register an ECOMMERCE TRANSACTION (and an associated ITEM)
  else if ($method === 'ecommerce') {

    // Set up Transaction params
    $ti = uniqid(); // Transaction ID
    $ta = 'SI';
    $tr = $info['price']; // transaction value (native currency)
    $cu = $info['cc']; // currency code

    // Send Transaction hit
    $data = array(
      'v' => $v,
      'tid' => $tid,
      'cid' => $cid,
      't' => 'transaction',
      'ti' => $ti,
      'ta' => $ta,
      'tr' => $tr,
      'cu' => $cu
    );
    gaFireHit($data);

    // Set up Item params
    $in = urlencode($info['info']->product_name); // item name;
    $ip = $tr;
    $iq = 1;
    $ic = urlencode($info['info']->product_id); // item SKU
    $iv = urlencode('SI'); // Product Category - we use 'SI' in all cases, you may not want to

    // Send Item hit
    $data = array(
      'v' => $v,
      'tid' => $tid,
      'cid' => $cid,
      't' => 'item',
      'ti' => $ti,
      'in' => $in,
      'ip' => $ip,
      'iq' => $iq,
      'ic' => $ic,
      'iv' => $iv,
      'cu' => $cu
    );
    gaFireHit($data);

  } // end ecommerce method
 }
}

// See https://developers.google.com/analytics/devguides/collection/protocol/v1/devguide
function gaFireHit( $data = null ) {
  if ( $data ) {
    $getString = 'https://ssl.google-analytics.com/collect';
    $getString .= '?payload_data&';
    $getString .= http_build_query($data);
    $result = wp_remote_get( $getString );

    #$sendlog = error_log($getString, 1, "ME@EMAIL.COM"); // comment this in and change your email to get an log sent to your email

    return $result;
  }
  return false;
}

Registering a pageview

Details on the parameters needed here.

You need to send the code in the following way:

$data = array(
  'title' => 'Any page title you want to send',
  'slug' => '/please/change/me/to-whatever-you-want/'
);
gaBuildHit( 'pageview', $data);

Registering an ecommerce transaction

You have to send a minimum of two requests here – the first is the initial transaction, and the second is for the specific item, They’re bound together on the same ‘transaction ID’ so Analytics can match them up. Details on the parameters needed here.

Please note – if you have multiple line items, my code doesn’t accommodate that as we only ever have one. You’ll need to amend the code to loop over the items and send them all.

You need to send the code in the following way:

$data = array(
  'info' => array ('product_id' => 102, 'product_name' => 'Smart Insights membership' ), // we already have these details in an array so send that, you may want to amend
  'cc' => 'GBP', // the currency code
  'price' => 209.00, // the price
  'country' => GB // the country ISO code
);
gaBuildHit( 'ecommerce', $data);

Some things to be aware of

  1. If you choose to take a peek at what is returned from the API after a request, you’ll be surprised to learn that you only ever get a 200 response, regardless whether your request has succeeded or failed. So it’s a bit pointless, really, you can’t debug it that way
  2. If you use the Real Time view on Analytics, you will be able to see your ‘pageview’ requests appear within seconds, so you know it works!
  3. If you use the ‘ecommerce’ method, you’ll probably have to wait for 2-3 hours (like I did) before data appears, or not. Not helpful, but the code should work – at least you don’t have to start from scratch like I did! 🙂
  4. The code above is written for our site – you will more than likely have to tweak and change it to make it work for yours.

And that’s that – though it could do with some tidying (which I’ll do when I extend it further), it should be easy to add any of the other methods available on the Measurement Protocol API to these functions. Running within your WordPress install it should give a good starting point to using the server side Google Analytics Measurement Protocol in PHP and WordPress.

I’d love to hear how other WordPress developers are using the new Google Universal Analytics Measurement Protocol within their WordPress sites – let me know in the comments!