OsmAnd/config/site/GoogleAnalytics/Internals/Request/HttpRequest.php

234 lines
6.8 KiB
PHP
Raw Normal View History

2012-01-14 00:17:28 +01:00
<?php
/**
* Generic Server-Side Google Analytics PHP Client
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License (LGPL) as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*
* Google Analytics is a registered trademark of Google Inc.
*
* @link http://code.google.com/p/php-ga
*
* @license http://www.gnu.org/licenses/lgpl.html
* @author Thomas Bachem <tb@unitedprototype.com>
* @copyright Copyright (c) 2010 United Prototype GmbH (http://unitedprototype.com)
*/
namespace UnitedPrototype\GoogleAnalytics\Internals\Request;
use UnitedPrototype\GoogleAnalytics\Config;
use UnitedPrototype\GoogleAnalytics\Internals\Util;
/**
* @link http://code.google.com/p/gaforflash/source/browse/trunk/src/com/google/analytics/core/GIFRequest.as
*/
abstract class HttpRequest {
/**
* Indicates the type of request, will be mapped to "utmt" parameter
*
* @see ParameterHolder::$utmt
* @var string
*/
protected $type;
/**
* @var \UnitedPrototype\GoogleAnalytics\Config
*/
protected $config;
/**
* @var string
*/
protected $xForwardedFor;
/**
* @var string
*/
protected $userAgent;
/**
* @param \UnitedPrototype\GoogleAnalytics\Config $config
*/
public function __construct(Config $config = null) {
$this->setConfig($config ? $config : new Config());
}
/**
* @return \UnitedPrototype\GoogleAnalytics\Config
*/
public function getConfig() {
return $this->config;
}
/**
* @param \UnitedPrototype\GoogleAnalytics\Config $config
*/
public function setConfig(Config $config) {
$this->config = $config;
}
/**
* @param string $value
*/
protected function setXForwardedFor($value) {
$this->xForwardedFor = $value;
}
/**
* @param string $value
*/
protected function setUserAgent($value) {
$this->userAgent = $value;
}
/**
* @return string
*/
protected function buildHttpRequest() {
$parameters = $this->buildParameters();
// This constant is supported as the 4th argument of http_build_query()
// from PHP 5.3.6 on and will tell it to use rawurlencode() instead of urlencode()
// internally, see http://code.google.com/p/php-ga/issues/detail?id=3
if(defined('PHP_QUERY_RFC3986')) {
// http_build_query() does automatically skip all array entries
// with null values, exactly what we want here
$queryString = http_build_query($parameters->toArray(), '', '&', PHP_QUERY_RFC3986);
} else {
// Manually replace "+"s with "%20" for backwards-compatibility
$queryString = str_replace('+', '%20', http_build_query($parameters->toArray(), '', '&'));
}
// Mimic Javascript's encodeURIComponent() encoding for the query
// string just to be sure we are 100% consistent with GA's Javascript client
$queryString = Util::convertToUriComponentEncoding($queryString);
// Recent versions of ga.js use HTTP POST requests if the query string is too long
$usePost = strlen($queryString) > 2036;
if(!$usePost) {
$r = 'GET ' . $this->config->getEndpointPath() . '?' . $queryString . ' HTTP/1.0' . "\r\n";
} else {
// FIXME: The "/p" shouldn't be hardcoded here, instead we need a GET and a POST endpoint...
$r = 'POST /p' . $this->config->getEndpointPath() . ' HTTP/1.0' . "\r\n";
}
$r .= 'Host: ' . $this->config->getEndpointHost() . "\r\n";
if($this->userAgent) {
$r .= 'User-Agent: ' . str_replace(array("\n", "\r"), '', $this->userAgent) . "\r\n";
}
if($this->xForwardedFor) {
// Sadly "X-Fowarded-For" is not supported by GA so far,
// see e.g. http://www.google.com/support/forum/p/Google+Analytics/thread?tid=017691c9e71d4b24,
// but we include it nonetheless for the pure sake of correctness (and hope)
$r .= 'X-Forwarded-For: ' . str_replace(array("\n", "\r"), '', $this->xForwardedFor) . "\r\n";
}
if($usePost) {
// Don't ask me why "text/plain", but ga.js says so :)
$r .= 'Content-Type: text/plain' . "\r\n";
$r .= 'Content-Length: ' . strlen($queryString) . "\r\n";
}
$r .= 'Connection: close' . "\r\n";
$r .= "\r\n\r\n";
if($usePost) {
$r .= $queryString;
}
return $r;
}
/**
* @return \UnitedPrototype\GoogleAnalytics\Internals\ParameterHolder
*/
protected abstract function buildParameters();
/**
* This method should only be called directly or indirectly by fire(), but must
* remain public as it can be called by a closure function.
*
* Sends either a normal HTTP request with response or an asynchronous request
* to Google Analytics without waiting for the response. Will always return
* null in the latter case, or false if any connection problems arise.
*
* @see HttpRequest::fire()
* @param string $request
* @return null|string|bool
*/
public function _send() {
$request = $this->buildHttpRequest();
$response = null;
// Do not actually send the request if endpoint host is set to null
if($this->config->getEndpointHost() !== null) {
$timeout = $this->config->getRequestTimeout();
$socket = fsockopen($this->config->getEndpointHost(), 80, $errno, $errstr, $timeout);
if(!$socket) return false;
if($this->config->getFireAndForget()) {
stream_set_blocking($socket, false);
}
$timeoutS = intval($timeout);
$timeoutUs = ($timeout - $timeoutS) * 100000;
stream_set_timeout($socket, $timeoutS, $timeoutUs);
fwrite($socket, $request);
if(!$this->config->getFireAndForget()) {
while(!feof($socket)) {
$response .= fgets($socket, 512);
}
}
fclose($socket);
}
if($loggingCallback = $this->config->getLoggingCallback()) {
$loggingCallback($request, $response);
}
return $response;
}
/**
* Simply delegates to send() if config option "sendOnShutdown" is disabled
* or enqueues the request by registering a PHP shutdown function.
*/
public function fire() {
if($this->config->getSendOnShutdown()) {
// This dumb variable assignment is needed as PHP prohibits using
// $this in closure use statements
$instance = $this;
// We use a closure here to retain the current values/states of
// this instance and $request (as the use statement will copy them
// into its own scope)
register_shutdown_function(function() use($instance) {
$instance->_send();
});
} else {
$this->_send();
}
}
}
?>