You are here

class gapi in Google Analytics Statistics 7.x

Same name and namespace in other branches
  1. 7.2 includes/gapi.class.php \gapi
  2. 7 includes/gapi.class.php \gapi

GAPI - Google Analytics PHP Interface

http://code.google.com/p/gapi-google-analytics-php-interface/

@copyright Stig Manning 2009

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

@author Stig Manning <stig@sdm.co.nz> @version 1.3

Hierarchy

Expanded class hierarchy of gapi

File

inc/gapi.class.php, line 27

View source
class gapi {
  const http_interface = 'auto';

  //'auto': autodetect, 'curl' or 'fopen'
  const client_login_url = 'https://www.google.com/accounts/ClientLogin';
  const account_data_url = 'https://www.google.com/analytics/feeds/accounts/default';
  const report_data_url = 'https://www.google.com/analytics/feeds/data';
  const interface_name = 'GAPI-1.3';
  const dev_mode = false;
  private $auth_token = null;
  private $account_entries = array();
  private $account_root_parameters = array();
  private $report_aggregate_metrics = array();
  private $report_root_parameters = array();
  private $results = array();

  /**
   * Constructor function for all new gapi instances
   *
   * Set up authenticate with Google and get auth_token
   *
   * @param String $email
   * @param String $password
   * @param String $token
   * @return gapi
   */
  public function __construct($email, $password, $token = null) {
    if ($token !== null) {
      $this->auth_token = $token;
    }
    else {
      $this
        ->authenticateUser($email, $password);
    }
  }

  /**
   * Return the auth token, used for storing the auth token in the user session
   *
   * @return String
   */
  public function getAuthToken() {
    return $this->auth_token;
  }

  /**
   * Request account data from Google Analytics
   *
   * @param Int $start_index OPTIONAL: Start index of results
   * @param Int $max_results OPTIONAL: Max results returned
   */
  public function requestAccountData($start_index = 1, $max_results = 20) {
    $response = $this
      ->httpRequest(gapi::account_data_url, array(
      'start-index' => $start_index,
      'max-results' => $max_results,
    ), null, $this
      ->generateAuthHeader());
    if (substr($response['code'], 0, 1) == '2') {
      return $this
        ->accountObjectMapper($response['body']);
    }
    else {
      throw new Exception('GAPI: Failed to request account data. Error: "' . strip_tags($response['body']) . '"');
    }
  }

  /**
   * Request report data from Google Analytics
   *
   * $report_id is the Google report ID for the selected account
   *
   * $parameters should be in key => value format
   *
   * @param String $report_id
   * @param Array $dimensions Google Analytics dimensions e.g. array('browser')
   * @param Array $metrics Google Analytics metrics e.g. array('pageviews')
   * @param Array $sort_metric OPTIONAL: Dimension or dimensions to sort by e.g.('-visits')
   * @param String $filter OPTIONAL: Filter logic for filtering results
   * @param String $start_date OPTIONAL: Start of reporting period
   * @param String $end_date OPTIONAL: End of reporting period
   * @param Int $start_index OPTIONAL: Start index of results
   * @param Int $max_results OPTIONAL: Max results returned
   */
  public function requestReportData($report_id, $dimensions, $metrics, $sort_metric = null, $filter = null, $start_date = null, $end_date = null, $start_index = 1, $max_results = 30) {
    $parameters = array(
      'ids' => 'ga:' . $report_id,
    );
    if (is_array($dimensions)) {
      $dimensions_string = '';
      foreach ($dimensions as $dimesion) {
        $dimensions_string .= ',ga:' . $dimesion;
      }
      $parameters['dimensions'] = substr($dimensions_string, 1);
    }
    else {
      $parameters['dimensions'] = 'ga:' . $dimensions;
    }
    if (is_array($metrics)) {
      $metrics_string = '';
      foreach ($metrics as $metric) {
        $metrics_string .= ',ga:' . $metric;
      }
      $parameters['metrics'] = substr($metrics_string, 1);
    }
    else {
      $parameters['metrics'] = 'ga:' . $metrics;
    }
    if ($sort_metric == null && isset($parameters['metrics'])) {
      $parameters['sort'] = $parameters['metrics'];
    }
    elseif (is_array($sort_metric)) {
      $sort_metric_string = '';
      foreach ($sort_metric as $sort_metric_value) {

        //Reverse sort - Thanks Nick Sullivan
        if (substr($sort_metric_value, 0, 1) == "-") {
          $sort_metric_string .= ',-ga:' . substr($sort_metric_value, 1);

          // Descending
        }
        else {
          $sort_metric_string .= ',ga:' . $sort_metric_value;

          // Ascending
        }
      }
      $parameters['sort'] = substr($sort_metric_string, 1);
    }
    else {
      if (substr($sort_metric, 0, 1) == "-") {
        $parameters['sort'] = '-ga:' . substr($sort_metric, 1);
      }
      else {
        $parameters['sort'] = 'ga:' . $sort_metric;
      }
    }
    if ($filter != null) {
      $filter = $this
        ->processFilter($filter);
      if ($filter !== false) {
        $parameters['filters'] = $filter;
      }
    }
    if ($start_date == null) {
      $start_date = date('Y-m-d', strtotime('1 month ago'));
    }
    $parameters['start-date'] = $start_date;
    if ($end_date == null) {
      $end_date = date('Y-m-d');
    }
    $parameters['end-date'] = $end_date;
    $parameters['start-index'] = $start_index;
    $parameters['max-results'] = $max_results;
    $parameters['prettyprint'] = gapi::dev_mode ? 'true' : 'false';
    $response = $this
      ->httpRequest(gapi::report_data_url, $parameters, null, $this
      ->generateAuthHeader());

    //HTTP 2xx
    if (substr($response['code'], 0, 1) == '2') {
      return $this
        ->reportObjectMapper($response['body']);
    }
    else {
      throw new Exception('GAPI: Failed to request report data. Error: "' . strip_tags($response['body']) . '"');
    }
  }

  /**
   * Process filter string, clean parameters and convert to Google Analytics
   * compatible format
   *
   * @param String $filter
   * @return String Compatible filter string
   */
  protected function processFilter($filter) {
    $valid_operators = '(!~|=~|==|!=|>|<|>=|<=|=@|!@)';
    $filter = preg_replace('/\\s\\s+/', ' ', trim($filter));

    //Clean duplicate whitespace
    $filter = str_replace(array(
      ',',
      ';',
    ), array(
      '\\,',
      '\\;',
    ), $filter);

    //Escape Google Analytics reserved characters
    $filter = preg_replace('/(&&\\s*|\\|\\|\\s*|^)([a-z]+)(\\s*' . $valid_operators . ')/i', '$1ga:$2$3', $filter);

    //Prefix ga: to metrics and dimensions
    $filter = preg_replace('/[\'\\"]/i', '', $filter);

    //Clear invalid quote characters
    $filter = preg_replace(array(
      '/\\s*&&\\s*/',
      '/\\s*\\|\\|\\s*/',
      '/\\s*' . $valid_operators . '\\s*/',
    ), array(
      ';',
      ',',
      '$1',
    ), $filter);

    //Clean up operators
    if (strlen($filter) > 0) {
      return urlencode($filter);
    }
    else {
      return false;
    }
  }

  /**
   * Report Account Mapper to convert the XML to array of useful PHP objects
   *
   * @param String $xml_string
   * @return Array of gapiAccountEntry objects
   */
  protected function accountObjectMapper($xml_string) {
    $xml = simplexml_load_string($xml_string);
    $this->results = null;
    $results = array();
    $account_root_parameters = array();

    //Load root parameters
    $account_root_parameters['updated'] = strval($xml->updated);
    $account_root_parameters['generator'] = strval($xml->generator);
    $account_root_parameters['generatorVersion'] = strval($xml->generator
      ->attributes());
    $open_search_results = $xml
      ->children('http://a9.com/-/spec/opensearchrss/1.0/');
    foreach ($open_search_results as $key => $open_search_result) {
      $report_root_parameters[$key] = intval($open_search_result);
    }

    //ADDED: If statement to avoid error messages
    if (isset($google_results)) {
      $account_root_parameters['startDate'] = strval($google_results->startDate);
      $account_root_parameters['endDate'] = strval($google_results->endDate);
    }

    //Load result entries
    foreach ($xml->entry as $entry) {
      $properties = array();
      foreach ($entry
        ->children('http://schemas.google.com/analytics/2009')->property as $property) {
        $properties[str_replace('ga:', '', $property
          ->attributes()->name)] = strval($property
          ->attributes()->value);
      }
      $properties['title'] = strval($entry->title);
      $properties['updated'] = strval($entry->updated);
      $results[] = new gapiAccountEntry($properties);
    }
    $this->account_root_parameters = $account_root_parameters;
    $this->results = $results;
    return $results;
  }

  /**
   * Report Object Mapper to convert the XML to array of useful PHP objects
   *
   * @param String $xml_string
   * @return Array of gapiReportEntry objects
   */
  protected function reportObjectMapper($xml_string) {
    $xml = simplexml_load_string($xml_string);
    $this->results = null;
    $results = array();
    $report_root_parameters = array();
    $report_aggregate_metrics = array();

    //Load root parameters
    $report_root_parameters['updated'] = strval($xml->updated);
    $report_root_parameters['generator'] = strval($xml->generator);
    $report_root_parameters['generatorVersion'] = strval($xml->generator
      ->attributes());
    $open_search_results = $xml
      ->children('http://a9.com/-/spec/opensearchrss/1.0/');
    foreach ($open_search_results as $key => $open_search_result) {
      $report_root_parameters[$key] = intval($open_search_result);
    }
    $google_results = $xml
      ->children('http://schemas.google.com/analytics/2009');
    foreach ($google_results->dataSource->property as $property_attributes) {
      $report_root_parameters[str_replace('ga:', '', $property_attributes
        ->attributes()->name)] = strval($property_attributes
        ->attributes()->value);
    }
    $report_root_parameters['startDate'] = strval($google_results->startDate);
    $report_root_parameters['endDate'] = strval($google_results->endDate);

    //Load result aggregate metrics
    foreach ($google_results->aggregates->metric as $aggregate_metric) {
      $metric_value = strval($aggregate_metric
        ->attributes()->value);

      //Check for float, or value with scientific notation
      if (preg_match('/^(\\d+\\.\\d+)|(\\d+E\\d+)|(\\d+.\\d+E\\d+)$/', $metric_value)) {
        $report_aggregate_metrics[str_replace('ga:', '', $aggregate_metric
          ->attributes()->name)] = floatval($metric_value);
      }
      else {
        $report_aggregate_metrics[str_replace('ga:', '', $aggregate_metric
          ->attributes()->name)] = intval($metric_value);
      }
    }

    //Load result entries
    foreach ($xml->entry as $entry) {
      $metrics = array();
      foreach ($entry
        ->children('http://schemas.google.com/analytics/2009')->metric as $metric) {
        $metric_value = strval($metric
          ->attributes()->value);

        //Check for float, or value with scientific notation
        if (preg_match('/^(\\d+\\.\\d+)|(\\d+E\\d+)|(\\d+.\\d+E\\d+)$/', $metric_value)) {
          $metrics[str_replace('ga:', '', $metric
            ->attributes()->name)] = floatval($metric_value);
        }
        else {
          $metrics[str_replace('ga:', '', $metric
            ->attributes()->name)] = intval($metric_value);
        }
      }
      $dimensions = array();
      foreach ($entry
        ->children('http://schemas.google.com/analytics/2009')->dimension as $dimension) {
        $dimensions[str_replace('ga:', '', $dimension
          ->attributes()->name)] = strval($dimension
          ->attributes()->value);
      }
      $results[] = new gapiReportEntry($metrics, $dimensions);
    }
    $this->report_root_parameters = $report_root_parameters;
    $this->report_aggregate_metrics = $report_aggregate_metrics;
    $this->results = $results;
    return $results;
  }

  /**
   * Authenticate Google Account with Google
   *
   * @param String $email
   * @param String $password
   */
  protected function authenticateUser($email, $password) {
    $post_variables = array(
      'accountType' => 'GOOGLE',
      'Email' => $email,
      'Passwd' => $password,
      'source' => gapi::interface_name,
      'service' => 'analytics',
    );
    $response = $this
      ->httpRequest(gapi::client_login_url, null, $post_variables);

    //Convert newline delimited variables into url format then import to array
    parse_str(str_replace(array(
      "\n",
      "\r\n",
    ), '&', $response['body']), $auth_token);
    if (substr($response['code'], 0, 1) != '2' || !is_array($auth_token) || empty($auth_token['Auth'])) {
      throw new Exception('GAPI: Failed to authenticate user. Error: "' . strip_tags($response['body']) . '"');
    }
    $this->auth_token = $auth_token['Auth'];
  }

  /**
   * Generate authentication token header for all requests
   *
   * @return Array
   */
  protected function generateAuthHeader() {
    return array(
      'Authorization: GoogleLogin auth=' . $this->auth_token,
    );
  }

  /**
   * Perform http request
   *
   *
   * @param Array $get_variables
   * @param Array $post_variables
   * @param Array $headers
   */
  protected function httpRequest($url, $get_variables = null, $post_variables = null, $headers = null) {
    $interface = gapi::http_interface;
    if (gapi::http_interface == 'auto') {
      if (function_exists('curl_exec')) {
        $interface = 'curl';
      }
      else {
        $interface = 'fopen';
      }
    }
    if ($interface == 'curl') {
      return $this
        ->curlRequest($url, $get_variables, $post_variables, $headers);
    }
    elseif ($interface == 'fopen') {
      return $this
        ->fopenRequest($url, $get_variables, $post_variables, $headers);
    }
    else {
      throw new Exception('Invalid http interface defined. No such interface "' . gapi::http_interface . '"');
    }
  }

  /**
   * HTTP request using PHP CURL functions
   * Requires curl library installed and configured for PHP
   *
   * @param Array $get_variables
   * @param Array $post_variables
   * @param Array $headers
   */
  private function curlRequest($url, $get_variables = null, $post_variables = null, $headers = null) {
    $ch = curl_init();
    if (is_array($get_variables)) {
      $get_variables = '?' . str_replace('&amp;', '&', urldecode(http_build_query($get_variables)));
    }
    else {
      $get_variables = null;
    }
    curl_setopt($ch, CURLOPT_URL, $url . $get_variables);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

    //CURL doesn't like google's cert
    if (is_array($post_variables)) {
      curl_setopt($ch, CURLOPT_POST, true);
      curl_setopt($ch, CURLOPT_POSTFIELDS, $post_variables);
    }
    if (is_array($headers)) {
      curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    }
    $response = curl_exec($ch);
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    return array(
      'body' => $response,
      'code' => $code,
    );
  }

  /**
   * HTTP request using native PHP fopen function
   * Requires PHP openSSL
   *
   * @param Array $get_variables
   * @param Array $post_variables
   * @param Array $headers
   */
  private function fopenRequest($url, $get_variables = null, $post_variables = null, $headers = null) {
    $http_options = array(
      'method' => 'GET',
      'timeout' => 3,
    );
    if (is_array($headers)) {
      $headers = implode("\r\n", $headers) . "\r\n";
    }
    else {
      $headers = '';
    }
    if (is_array($get_variables)) {
      $get_variables = '?' . str_replace('&amp;', '&', urldecode(http_build_query($get_variables)));
    }
    else {
      $get_variables = null;
    }
    if (is_array($post_variables)) {
      $post_variables = str_replace('&amp;', '&', urldecode(http_build_query($post_variables)));
      $http_options['method'] = 'POST';
      $headers = "Content-type: application/x-www-form-urlencoded\r\n" . "Content-Length: " . strlen($post_variables) . "\r\n" . $headers;
      $http_options['header'] = $headers;
      $http_options['content'] = $post_variables;
    }
    else {
      $post_variables = '';
      $http_options['header'] = $headers;
    }
    $context = stream_context_create(array(
      'http' => $http_options,
    ));
    $response = @file_get_contents($url . $get_variables, null, $context);
    return array(
      'body' => $response !== false ? $response : 'Request failed, fopen provides no further information',
      'code' => $response !== false ? '200' : '400',
    );
  }

  /**
   * Case insensitive array_key_exists function, also returns
   * matching key.
   *
   * @param String $key
   * @param Array $search
   * @return String Matching array key
   */
  public static function array_key_exists_nc($key, $search) {
    if (array_key_exists($key, $search)) {
      return $key;
    }
    if (!(is_string($key) && is_array($search))) {
      return false;
    }
    $key = strtolower($key);
    foreach ($search as $k => $v) {
      if (strtolower($k) == $key) {
        return $k;
      }
    }
    return false;
  }

  /**
   * Get Results
   *
   * @return Array
   */
  public function getResults() {
    if (is_array($this->results)) {
      return $this->results;
    }
    else {
      return;
    }
  }

  /**
   * Get an array of the metrics and the matchning
   * aggregate values for the current result
   *
   * @return Array
   */
  public function getMetrics() {
    return $this->report_aggregate_metrics;
  }

  /**
   * Call method to find a matching root parameter or
   * aggregate metric to return
   *
   * @param $name String name of function called
   * @return String
   * @throws Exception if not a valid parameter or aggregate
   * metric, or not a 'get' function
   */
  public function __call($name, $parameters) {
    if (!preg_match('/^get/', $name)) {
      throw new Exception('No such function "' . $name . '"');
    }
    $name = preg_replace('/^get/', '', $name);
    $parameter_key = gapi::array_key_exists_nc($name, $this->report_root_parameters);
    if ($parameter_key) {
      return $this->report_root_parameters[$parameter_key];
    }
    $aggregate_metric_key = gapi::array_key_exists_nc($name, $this->report_aggregate_metrics);
    if ($aggregate_metric_key) {
      return $this->report_aggregate_metrics[$aggregate_metric_key];
    }
    throw new Exception('No valid root parameter or aggregate metric called "' . $name . '"');
  }

}

Members

Namesort descending Modifiers Type Description Overrides
gapi::$account_entries private property
gapi::$account_root_parameters private property
gapi::$auth_token private property
gapi::$report_aggregate_metrics private property
gapi::$report_root_parameters private property
gapi::$results private property
gapi::accountObjectMapper protected function Report Account Mapper to convert the XML to array of useful PHP objects
gapi::account_data_url constant
gapi::array_key_exists_nc public static function Case insensitive array_key_exists function, also returns matching key.
gapi::authenticateUser protected function Authenticate Google Account with Google
gapi::client_login_url constant
gapi::curlRequest private function HTTP request using PHP CURL functions Requires curl library installed and configured for PHP
gapi::dev_mode constant
gapi::fopenRequest private function HTTP request using native PHP fopen function Requires PHP openSSL
gapi::generateAuthHeader protected function Generate authentication token header for all requests
gapi::getAuthToken public function Return the auth token, used for storing the auth token in the user session
gapi::getMetrics public function Get an array of the metrics and the matchning aggregate values for the current result
gapi::getResults public function Get Results
gapi::httpRequest protected function Perform http request
gapi::http_interface constant
gapi::interface_name constant
gapi::processFilter protected function Process filter string, clean parameters and convert to Google Analytics compatible format
gapi::reportObjectMapper protected function Report Object Mapper to convert the XML to array of useful PHP objects
gapi::report_data_url constant
gapi::requestAccountData public function Request account data from Google Analytics
gapi::requestReportData public function Request report data from Google Analytics
gapi::__call public function Call method to find a matching root parameter or aggregate metric to return
gapi::__construct public function Constructor function for all new gapi instances