PageRenderTime 42ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Racecore/GATracking/GATracking.php

https://github.com/michanismus/google-measurement-php-client
PHP | 409 lines | 162 code | 59 blank | 188 comment | 13 complexity | 8a894588277f8d0f606c5f12bb4bff49 MD5 | raw file
  1. <?php
  2. namespace Racecore\GATracking;
  3. use Racecore\GATracking\Exception\EndpointServerException;
  4. use Racecore\GATracking\Exception\MissingConfigurationException;
  5. use Racecore\GATracking\Tracking\AbstractTracking;
  6. /**
  7. * Google Analytics Measurement PHP Class
  8. * Licensed under the 3-clause BSD License.
  9. * This source file is subject to the 3-clause BSD License that is
  10. * bundled with this package in the LICENSE file. It is also available at
  11. * the following URL: http://www.opensource.org/licenses/BSD-3-Clause
  12. *
  13. * Google Documentation
  14. * https://developers.google.com/analytics/devguides/collection/protocol/v1/
  15. *
  16. * @author Marco Rieger
  17. * @email Rieger(at)racecore.de
  18. * @git https://github.com/ins0
  19. * @url http://www.racecore.de
  20. * @package Racecore\GATracking\Tracking
  21. */
  22. class GATracking
  23. {
  24. /**
  25. * Google Analytics Account ID UA-...
  26. *
  27. * @var
  28. */
  29. private $accountID;
  30. /**
  31. * Current User Client ID
  32. *
  33. * @var string
  34. */
  35. private $clientID;
  36. /**
  37. * Protocol Version
  38. *
  39. * @var string
  40. */
  41. private $protocol = '1';
  42. /**
  43. * Analytics Endpoint URL
  44. *
  45. * @var string
  46. */
  47. private $analytics_endpoint = 'http://www.google-analytics.com/collect';
  48. /**
  49. * Tacking Holder
  50. *
  51. * @var array
  52. */
  53. private $tracking_holder = array();
  54. /**
  55. * Holds the last Response from Google Analytics Server
  56. *
  57. * @var string
  58. */
  59. private $last_response = null;
  60. /**
  61. * Holds all Responses from GA Server
  62. *
  63. * @var array
  64. */
  65. private $last_response_stack = array();
  66. /**
  67. * Sets the Analytics Account ID
  68. *
  69. * @param $account
  70. */
  71. public function setAccountID($account)
  72. {
  73. $this->accountID = $account;
  74. }
  75. /**
  76. * Set the current Client ID
  77. *
  78. * @param $clientID
  79. * @return $this
  80. */
  81. public function setClientID($clientID)
  82. {
  83. $this->clientID = $clientID;
  84. return $this;
  85. }
  86. /**
  87. * Returns the current Client ID
  88. *
  89. * @return string
  90. */
  91. public function getClientID()
  92. {
  93. if (!$this->clientID) {
  94. $this->clientID = $this->createClientID();
  95. }
  96. return $this->clientID;
  97. }
  98. /**
  99. * Return all registered Events
  100. *
  101. * @return array
  102. */
  103. public function getEvents()
  104. {
  105. return $this->tracking_holder;
  106. }
  107. /**
  108. * Returns current Google Account ID
  109. *
  110. * @return mixed
  111. */
  112. public function getAccountID()
  113. {
  114. return $this->accountID;
  115. }
  116. /**
  117. * Constructor
  118. *
  119. * @param string $accountID
  120. */
  121. public function __construct( $accountID = null )
  122. {
  123. $this->setAccountID( $accountID );
  124. return $this;
  125. }
  126. /**
  127. * Create a GUID on Client specific values
  128. *
  129. * @return string
  130. */
  131. private function createClientID()
  132. {
  133. // collect user specific data
  134. if (isset($_COOKIE['_ga'])) {
  135. $gaCookie = explode('.', $_COOKIE['_ga']);
  136. if( isset($gaCookie[2] ) )
  137. {
  138. // check if uuid
  139. if( $this->checkUUID( $gaCookie[2] ) )
  140. {
  141. // uuid set in cookie
  142. return $gaCookie[2];
  143. }
  144. elseif( isset($gaCookie[2]) && isset($gaCookie[3]) )
  145. {
  146. // google default client id
  147. return $gaCookie[2] . '.' . $gaCookie[3];
  148. }
  149. }
  150. }
  151. // nothing found - return random uuid client id
  152. return $this->generateUUID();
  153. }
  154. /**
  155. * Check if is a valid UUID v4
  156. *
  157. * @param $uuid
  158. * @return int
  159. */
  160. private function checkUUID( $uuid )
  161. {
  162. return preg_match('#^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$#i', $uuid );
  163. }
  164. /**
  165. * Generate UUID v4 function - needed to generate a CID when one isn't available
  166. *
  167. * @author Andrew Moore http://www.php.net/manual/en/function.uniqid.php#94959
  168. * @return string
  169. */
  170. private function generateUUID() {
  171. return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
  172. // 32 bits for "time_low"
  173. mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
  174. // 16 bits for "time_mid"
  175. mt_rand( 0, 0xffff ),
  176. // 16 bits for "time_hi_and_version",
  177. // four most significant bits holds version number 4
  178. mt_rand( 0, 0x0fff ) | 0x4000,
  179. // 16 bits, 8 bits for "clk_seq_hi_res",
  180. // 8 bits for "clk_seq_low",
  181. // two most significant bits holds zero and one for variant DCE1.1
  182. mt_rand( 0, 0x3fff ) | 0x8000,
  183. // 48 bits for "node"
  184. mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
  185. );
  186. }
  187. /**
  188. * Send all captured Trackings to Analytics Server
  189. * Flush all prev. captured tracking responses
  190. *
  191. * @return bool
  192. */
  193. public function send()
  194. {
  195. // clear response logs
  196. $this->last_response_stack = array();
  197. $this->last_response = null;
  198. /** @var AbstractTracking $event */
  199. foreach ($this->tracking_holder as $tracking) {
  200. $this->sendTracking($tracking);
  201. }
  202. return true;
  203. }
  204. /**
  205. * Returns the Client IP
  206. * The last octect of the IP address is removed to anonymize the user
  207. *
  208. * @param string $address
  209. * @return string
  210. */
  211. function getClientIP($address = '')
  212. {
  213. if (!$address) {
  214. $address = $_SERVER['REMOTE_ADDR'];
  215. }
  216. if (!$address) {
  217. return '';
  218. }
  219. // Capture the first three octects of the IP address and replace the forth
  220. // with 0, e.g. 124.455.3.123 becomes 124.455.3.0
  221. $regex = "/^([^.]+\.[^.]+\.[^.]+\.).*/";
  222. if (preg_match($regex, $address, $matches)) {
  223. return $matches[1] . '0';
  224. }
  225. return '';
  226. }
  227. /**
  228. * Build the POST Packet
  229. *
  230. * @param AbstractTracking $event
  231. * @return string
  232. * @throws Exception\MissingConfigurationException
  233. */
  234. private function buildPacket( AbstractTracking $event )
  235. {
  236. // get packet
  237. $eventPacket = $event->getPaket();
  238. if( ! $this->getAccountID() )
  239. {
  240. throw new MissingConfigurationException('Google Account ID is missing');
  241. }
  242. // Add Protocol
  243. $eventPacket['v'] = $this->protocol; // protocol version
  244. $eventPacket['tid'] = $this->getAccountID(); // account id
  245. $eventPacket['cid'] = $this->getClientID(); // client id
  246. $eventPacket = array_reverse($eventPacket);
  247. // build query
  248. return http_build_query($eventPacket);
  249. }
  250. /**
  251. * Send an Event to Google Analytics
  252. * Will be removed
  253. *
  254. * @param AbstractTracking $tracking
  255. * @return bool
  256. * @throws Exception\EndpointServerException
  257. * @deprecated Use sendTracking
  258. */
  259. public function sendEvent(AbstractTracking $tracking)
  260. {
  261. return $this->sendTracking($tracking);
  262. }
  263. /**
  264. * Send an Event to Google Analytics
  265. *
  266. * @param AbstractTracking $event
  267. * @return bool
  268. * @throws Exception\EndpointServerException
  269. */
  270. public function sendTracking(AbstractTracking $event)
  271. {
  272. // get packet
  273. $eventPacket = $this->buildPacket( $event );
  274. // get endpoint
  275. $endpoint = parse_url($this->analytics_endpoint);
  276. // port
  277. $port = ($endpoint['scheme'] == 'https' ? 443 : 80);
  278. // connect
  279. $connection = @fsockopen($endpoint['scheme'] == 'https' ? 'ssl://' : $endpoint['host'], $port, $error, $errorstr, 10);
  280. if (!$connection) {
  281. throw new EndpointServerException('Analytics Host not reachable!');
  282. }
  283. $header = 'POST ' . $endpoint['path'] . ' HTTP/1.1' . "\r\n" .
  284. 'Host: ' . $endpoint['host'] . "\r\n" .
  285. 'User-Agent: Google-Measurement-PHP-Client' . "\r\n" .
  286. 'Content-Type: application/x-www-form-urlencoded' . "\r\n" .
  287. 'Content-Length: ' . strlen($eventPacket) . "\r\n" .
  288. 'Connection: Close' . "\r\n\r\n";
  289. $this->last_response = '';
  290. // frwite data
  291. fwrite($connection, $header);
  292. fwrite($connection, $eventPacket);
  293. // response
  294. $response = '';
  295. // receive response
  296. while (!feof($connection)) {
  297. $response .= fgets($connection, 1024);
  298. }
  299. // response
  300. $responseContainer = explode("\r\n\r\n", $response, 2);
  301. $responseContainer[0] = explode("\r\n", $responseContainer[0]);
  302. // save last response
  303. $this->addResponse( $responseContainer );
  304. // connection close
  305. fclose($connection);
  306. return true;
  307. }
  308. /**
  309. * Add a Response to the Stack
  310. *
  311. * @param $response
  312. * @return bool
  313. */
  314. public function addResponse( $response )
  315. {
  316. $this->last_response_stack[] = $response;
  317. $this->last_response = $response;
  318. return true;
  319. }
  320. /**
  321. * Returns the last Response from Google Analytics Server
  322. *
  323. * @author Marco Rieger
  324. * @return string
  325. */
  326. public function getLastResponse()
  327. {
  328. return $this->last_response;
  329. }
  330. /**
  331. * Returns all Responses since the last Send Method Call
  332. *
  333. * @return array
  334. */
  335. public function getLastResponseStack()
  336. {
  337. return $this->last_response_stack;
  338. }
  339. /**
  340. * Add Tracking Event
  341. *
  342. * @param AbstractTracking $tracking
  343. * @return $this
  344. */
  345. public function addTracking(AbstractTracking $tracking)
  346. {
  347. $this->tracking_holder[] = $tracking;
  348. return $this;
  349. }
  350. }