How do I properly implement Google's Measurement Protocol in Wordpress using PHP?

admin

Administrator
Staff member
I have been struggling to implement Google's Measurement Protocol on a Wordpress site.
Apache Server, using PHP 5.4.35.

What I want to do is get server-side visitor data loaded into Google Analytics. The site has huge discrepancies between server logs and GA data, and I'm hoping to get the two to (somewhat) match up.

I have taken <a href="http://www.stumiller.me/implementing-google-analytics-measurement-protocol-in-php-and-wordpress/" rel="nofollow">the code published by Stu Miller on his website</a>, and made some slight edits hoping to reflect my use case. I've added the following to my wp-includes/functions.php file:

Code:
require( ABSPATH . WPINC . '/http.php' );
require( ABSPATH . WPINC . '/class-http.php' );
require( ABSPATH . WPINC . '/general-template.php' );

 // Handle the parsing of the _ga cookie or setting it to a unique identifier

// 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 )
  );
}

function gaParseCookie() {
  if (isset($_COOKIE['_ga'])) {
    list($version,$domainDepth, $cid1, $cid2) = split('[\.]', $_COOKIE["_ga"],4);
    $contents = array('version' =&gt; $version, 'domainDepth' =&gt; $domainDepth, 'cid' =&gt; $cid1.'.'.$cid2);
    $cid = $contents['cid'];
  }
  else $cid = gaGenUUID();
  return $cid;
}

function gaBuildHit( $method = null, $info = null ) {
  if ( $method &amp;&amp; $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' =&gt; $v,
      'tid' =&gt; $tid,
      'cid' =&gt; $cid,
      't' =&gt; 'pageview',
      'dt' =&gt; $info['title'],
      'dp' =&gt; $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' =&gt; $v,
      'tid' =&gt; $tid,
      'cid' =&gt; $cid,
      't' =&gt; 'transaction',
      'ti' =&gt; $ti,
      'ta' =&gt; $ta,
      'tr' =&gt; $tr,
      'cu' =&gt; $cu
    );
    gaFireHit($data);

    // Set up Item params
    $in = urlencode($info['info']-&gt;product_name); // item name;
    $ip = $tr;
    $iq = 1;
    $ic = urlencode($info['info']-&gt;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' =&gt; $v,
      'tid' =&gt; $tid,
      'cid' =&gt; $cid,
      't' =&gt; 'item',
      'ti' =&gt; $ti,
      'in' =&gt; $in,
      'ip' =&gt; $ip,
      'iq' =&gt; $iq,
      'ic' =&gt; $ic,
      'iv' =&gt; $iv,
      'cu' =&gt; $cu
    );
    gaFireHit($data);
  } // end ecommerce method
 }
}
global $post;
$data = array(
// Get the queried object and sanitize it
  'title' =&gt; $title = $post-&gt;post_title,
  'slug' =&gt; $slug = $post-&gt;post_name
);
gaBuildHit( 'pageview', $data);

// 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&amp;';
    $getString .= http_build_query($data);
    $result = wp_remote_get( $getString );

    #$sendlog = error_log($getString, 1, "[email protected]"); // comment this in and change your email to get an log sent to your email

    return $result;
  }
  return false;
}

I have received several errors along the way, which started with the wp_remote_get() function being not defined, which resulted in my including the:

Code:
require( ABSPATH . WPINC . '/http.php' );

This inclusion moved up the error list to WP_Http() class not found, which resulted in my inclusion of:

Code:
require( ABSPATH . WPINC . '/class-http.php' );

... and so on. I don't quite remember the error which prompted my inclusion of:

Code:
require( ABSPATH . WPINC . '/general-template.php' );

I believe it was a PHP Fatal error: Call to undefined function get_bloginfo().

Anyway, I think I'm doing includes wrong. The latest fatal error is: <strong>Fatal error: Call to undefined function home_url() in /home/<em>user</em>/public_html/<em>domain</em>/wp-includes/general-template.php on line 658</strong>

I looked up the Wordpress Codex a bit and it also seems like the wp_remote_safe_post() would have been the better option, with <a href="https://developers.google.com/analytics/devguides/collection/protocol/v1/devguide" rel="nofollow">Google recommending POST instead of GET due to the restricted payload on GET</a>.

Unfortunately I lack the experience to make sense of how the code is supposed to be used, I had been hoping the initial code would be good enough to make happen, with maybe some tweaks.

Am I doing something wrong with the includes? Or has wordpress 4.6.1 moved so far ahead that all of this is deprecated? Or maybe I'm using the wrong functions.php file?