API Google Analytics - Une classe PHP5 et des résultats d'intégration

7

Posted by Will | Posted on 27-04-2009

 Script  PHP 5  API  Google  Analytics  Curl  Design Pattern  Singleton

Code Labs GooglePour intégrer Google Analytics à mon système de blog je me suis constitué une petite classe PHP5. Cette classe est implémentée selon le design pattern Singleton. Le constructeur dispose des identifiants de connexion en dur mais on peut très bien utiliser des constantes.

 

<?php

class GAnalytics
{
	private $email;
	private $passwd;
	private $ids;
	private $auth;
	private static $instance;
	
	private function __construct()
	{
		$this->login('********@********', '********', '********');
	}

	private function __clone()
	{}
	
	public static function instance()
	{
		if(!isset(self::$instance))
		{
			$c = __CLASS__;
			self::$instance = new $c;
		}
		
		return self::$instance;
	}
	
	private function login($email, $passwd, $ids)
	{
		$this->email = $email;
		$this->passwd = $passwd;
		$this->ids = $ids;
		
		$ch = curl_init();  
		curl_setopt($ch, CURLOPT_URL, "https://www.google.com/accounts/ClientLogin");  
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);  
		  
		$data = array('accountType' => 'GOOGLE',  
				  'Email' => $this->email,  
				  'Passwd' => $this->passwd,  
				  'source'=>'CLI_GAnalytics',  
				  'service'=>'analytics');  

		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);  
		curl_setopt($ch, CURLOPT_POST, true);  
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);  
		curl_setopt($ch, CURLOPT_POSTFIELDS, $data);  

		$hasil = curl_exec($ch);  
		$hasil = @split("Auth=", $hasil);
		curl_close($ch);

		$this->auth = $hasil[1];
	}
	
	public function getDimensionByMetric($metrics, $dimensions, $date_1, $date_2 = null)
	{
		if(!$date_2)
			$date_2 = $date_1;
			
		$ch = curl_init("https://www.google.com/analytics/feeds/data?ids=ga:" . $this->ids . "&metrics=ga:" . $metrics . "&dimensions=ga:" . $dimensions . "&start-date=" . $date_1 . "&end-date=" . $date_2);  
  
		$header[] = 'Authorization: GoogleLogin auth=' . $this->auth;

		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);  
		curl_setopt($ch, CURLOPT_HTTPHEADER, $header);  
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);  
		curl_setopt($ch, CURLOPT_HEADER, false); 
		  
		$response = curl_exec($ch); 
		$infos = curl_getinfo($ch);
		curl_close($ch);

		if($infos['http_code'] != 200)
			throw new Exception("[EXCEPTION] (" . $info['http_code'] . ") " . $response);
		
		$XML_response = @str_replace('dxp:','',$response);
		$XML_object = simplexml_load_string($XML_response);

		$data = '';
		$label = '';
		foreach($XML_object->entry as $m)
		{
			$tmp = @split('ga:' . $dimensions . '=', $m->title);
			if($label == "")
			{
				$label .= $tmp[1] . ' (' . $m->metric['value'] . ')';
				$data .= $m->metric['value'];
			}
			else
			{
				$label .= '|' . $tmp[1] . ' (' . $m->metric['value'] . ')';
				$data .= ',' . $m->metric['value'];
			}
		}
		
		return array('label' => $label, 'data' => $data);
	}
	
	public function getMetric($metric, $date_1, $date_2 = null)
	{
		if(!$date_2)
			$date_2 = $date_1;
			
		$ch = curl_init("https://www.google.com/analytics/feeds/data?ids=ga:" . $this->ids . "&metrics=ga:" . $metric . "&start-date=" . $date_1 . "&end-date=" . $date_2);  
  
		$header[] = 'Authorization: GoogleLogin auth=' . $this->auth;

		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);  
		curl_setopt($ch, CURLOPT_HTTPHEADER, $header);  
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);  
		curl_setopt($ch, CURLOPT_HEADER, false); 
		  
		$response = curl_exec($ch); 
		$infos = curl_getinfo($ch);
		curl_close($ch);

		if($infos['http_code'] != 200)
			throw new Exception("[EXCEPTION] (" . $info['http_code'] . ") " . $response);
		
		$XML_response = @str_replace('dxp:','',$response);
		$XML_object = simplexml_load_string($XML_response);
		
		return $XML_object->entry->metric['value'] ? $XML_object->entry->metric['value'] : 0;
	}
	
	public function getMetricURI($metric, $uri, $date_1, $date_2 = null)
	{
		if(!$date_2)
			$date_2 = $date_1;
			
		$ch = curl_init("https://www.google.com/analytics/feeds/data?ids=ga:" . $this->ids . "&metrics=ga:" . $metric . "&dimensions=ga:pagePath&filters=ga:pagePath%3D%3D" . $uri . "&start-date=" . $date_1 . "&end-date=" . $date_2);  
  
		$header[] = 'Authorization: GoogleLogin auth=' . $this->auth;

		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);  
		curl_setopt($ch, CURLOPT_HTTPHEADER, $header);  
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);  
		curl_setopt($ch, CURLOPT_HEADER, false); 
		  
		$response = curl_exec($ch); 
		$infos = curl_getinfo($ch);
		curl_close($ch);

		if($infos['http_code'] != 200)
			throw new Exception("[EXCEPTION] (" . $info['http_code'] . ") " . $response);
		
		$XML_response = @str_replace('dxp:','',$response);
		$XML_object = simplexml_load_string($XML_response);
		
		return $XML_object->entry->metric['value'] ? $XML_object->entry->metric['value'] : 0;
	}
}

?>

 

Pour récupérer une instance de cette classe on appelle la méthode statique instance() :

$ga = GAnalytics::instance();

 

Ensuite on peut utiliser les méthodes comme dans l'exemple ci-dessous :

$navigateurs = $ga->getDimensionByMetric('visits', 'browser', date('Y-m-d', time()));
$countries = $ga->getDimensionByMetric('visits', 'country', date('Y-m-d', time()));
$visits = $ga->getMetric('visits', date('Y-m-d', time()));
$unique_visits = $ga->getMetric('visitors', date('Y-m-d', time()));
$page_views = $ga->getMetric('pageviews', date('Y-m-d', time()));

 

  • getDimensionByMetric() permet d'effectuer une requête avec un metric et une dimension.
  • getMetric() permet d'effectuer une requête avec un metric uniquement.
  • getMetricURI() permet d'effectuer une requête avec un metric par rapport à une URI précise.

 

On passe soit un intervalle (une période), soit une seule date.

Cette classe et ces méthodes sont très perfectibles, mais elles me suffisent amplement pour une intégration basique de Google Analytics. Ceci m'a permis d'avoir le résultat suivant :

 

Google Analytics dans mon système de blog - Page statistiques

L'image ci-dessus présente ma page de statistiques dans ma partie administrateur du blog. On peut voir le nombre de visites au total, le nombre de pages vues et le nombres de visiteurs uniques, le tout sur la journée. J'ai aussi deux graphiques représentant les visites par navigateur et par pays.

 

Google Analytics dans mon système de blog - Détail par article

L'image ci-dessus représente l'intégration de Google Analytics pour chacun de mes articles. On peut y voir le nombre de fois que cet article a été affiché, le nombre de consultations uniques ainsi que le temps moyen passé sur cet article, le tout sur la journée.

 

 

Liens annexes :

http://www.willdurand.fr/posts/20/api-google-analytics-decouverte-par-l-exemple.html

http://www.willdurand.fr/posts/21/api-google-analytics-round-2-graphiques-et-statistiques-par-page.html

Commentaires

Ajouter un commentaire

avatar

Julien Coquet  (14 May 2009 - 10:24:24)

Bien vu William!

Pour rendre ton code plus flexible dans le cas d'utilisation de plusieurs profils, je te suggère de rendre publique ta méthode login(), ce qui te permet de l'appeler plusieurs fois avec plusieurs profils ;-)

avatar

Will  (14 May 2009 - 10:24:24)

Bonjour Julien, effectivement je pourrais faire cela et j'avoue ne pas m'être posé la question.

Je suis parti sur un pattern Singleton et une connexion unique pour alléger le système (même si la durée max d'une connexion est de 15 minutes). Effectivement, pour différents profils ce serait très bien.

Merci Wink

avatar

Moins-depenser  (02 July 2009 - 15:12:44)

Bonjour,

je me permet de te poser une petite question concernant l'utilisation de "instance()". Quel est ici l'intérêt ? Idem pour __clone() ? 

C'est juste pour savoir si je loupe quelque chose en faites (puisque je ne m'en sers jamais).

J'aurai simplement mis en public __construct et donc appelé classiquement $machin = new GAnalytics();

Et merci pour la classe (même si finalement, elle ne convenait pas à mes besoins ;))

Merci d'avance !

avatar

Will  (02 July 2009 - 16:22:17)

Bonjour,

Pour répondre à vos questions, cette classe reprend le design pattern Singleton. C'est un choix d'architecture.

Selon moi, une seul objet est en charge des stats parce qu'on a un seul site. Exemple, mon compte en banque est géré par un seul banquier, je vais pas aller voir un nouveau banquier chaque fois que je veux retirer de l'argent. Après, c'est chacun sa vision.

De plus, la récupération du token d'authentification est longue, cela permet de ne faire ce traitement qu'une fois par exécution.

avatar

Moins-depenser  (02 July 2009 - 16:59:32)

 Ok je comprends mieux merci :)

 

Par contre sur l'exemple ci-dessus, c'est peut-etre pas indispensable, ou alors comme l'a souligné le premier commentaire, avoir mis login() en public ou même un "getIds()" en public (pour changer de site sans se reconnecter), ça aurait pu être utile. (après si tu considères effectivement que tu n'as qu'un site ;))

 

Sinon j'utilise une solution maison qui consiste à enregistrer l'objet et le rappeler ensuite.

 

exemple : Record::set('bidule', new MaClass);

 

et ensuite Record::get('bidule')->methode();

 

Mais c'est moins stricte puisqu'on peut passé outre et c'est plus long à écrire. (après, c'est un code "privée" que je suis seul à utiliser, donc je me laisse plus d'ouverture que si tu comptes rendre public ton MVC).

 

Néanmoins, je retiens la méthode, je vais essayé de m'en servir pour quelques classes qui en auraient besoin.

avatar

Ben  (13 November 2009 - 22:37:17)

Pouvez vous me dire comment faire pour ajouter un "segments personnalisé" a la requete.

D'apres le blog de l'api google la variable est 'userDefinedValue'

J'ai essayé avec filters=ga: mais je n'y arrive pas et :

$navigateurs = $ga->getDimensionByMetric('visits', 'userDefinedValue', date('Y-m-d', time()));

 

M'affiche le total de l'ensemble de mes visites sans prendre en contre mon filtre.

array(2) { ["label"]=> string(16) "(not set) (7228)" ["data"]=> string(4) "7228" }
 

 

merci d'avance de votre aide.

avatar

Will  (29 November 2009 - 12:56:16)

@Ben : le filtre "userDefined" ne doit-il pas être enregistré dans votre compte Google Analytics avant de l'utiliser ?