update Piwik to version 2.16 (fixes #91)

This commit is contained in:
oliver 2016-04-10 18:55:57 +02:00
commit d885a4baa9
5833 changed files with 418860 additions and 226988 deletions

View file

@ -1,6 +1,6 @@
<?php
/**
* Piwik - Open source web analytics
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
@ -9,9 +9,11 @@
namespace Piwik;
use Exception;
use Piwik\Container\StaticContainer;
use Piwik\Intl\Data\Provider\LanguageDataProvider;
use Piwik\Intl\Data\Provider\RegionDataProvider;
use Piwik\Plugins\UserCountry\LocationProvider\DefaultProvider;
use Piwik\Tracker;
use Piwik\Tracker\Cache;
use Piwik\Tracker\Cache as TrackerCache;
/**
* Contains helper methods used by both Piwik Core and the Piwik Tracking engine.
@ -34,6 +36,7 @@ class Common
/*
* Database
*/
const LANGUAGE_CODE_INVALID = 'xx';
/**
* Hashes a string into an integer which should be very low collision risks
@ -48,7 +51,7 @@ class Common
/**
* Returns a prefixed table name.
*
*
* The table prefix is determined by the `[database] tables_prefix` INI config
* option.
*
@ -82,7 +85,7 @@ class Common
*
* The table prefix is determined by the `[database] tables_prefix` INI config
* option.
*
*
* @param string $table The prefixed table name, eg "piwik-production_log_visit".
* @return string The unprefixed table name, eg "log_visit".
* @api
@ -107,12 +110,12 @@ class Common
*/
public static function isGoalPluginEnabled()
{
return \Piwik\Plugin\Manager::getInstance()->isPluginActivated('Goals');
return Plugin\Manager::getInstance()->isPluginActivated('Goals');
}
public static function isActionsPluginEnabled()
{
return \Piwik\Plugin\Manager::getInstance()->isPluginActivated('Actions');
return Plugin\Manager::getInstance()->isPluginActivated('Actions');
}
/**
@ -127,21 +130,44 @@ class Common
return self::$isCliMode;
}
$remoteAddr = @$_SERVER['REMOTE_ADDR'];
return PHP_SAPI == 'cli' ||
(!strncmp(PHP_SAPI, 'cgi', 3) && empty($remoteAddr));
if(PHP_SAPI == 'cli'){
return true;
}
if(self::isPhpCgiType() && (!isset($_SERVER['REMOTE_ADDR']) || empty($_SERVER['REMOTE_ADDR']))){
return true;
}
return false;
}
/**
* Returns true if the current request is a console command, eg. ./console xx:yy
* Returns true if PHP is executed as CGI type.
*
* @since added in 0.4.4
* @return bool true if PHP invoked as a CGI
*/
public static function isPhpCgiType()
{
$sapiType = php_sapi_name();
return substr($sapiType, 0, 3) === 'cgi';
}
/**
* Returns true if the current request is a console command, eg.
* ./console xx:yy
* or
* php console xx:yy
*
* @return bool
*/
public static function isRunningConsoleCommand()
{
$searched = '/console';
$searched = 'console';
$consolePos = strpos($_SERVER['SCRIPT_NAME'], $searched);
$expectedConsolePos = strlen($_SERVER['SCRIPT_NAME']) - strlen($searched);
$isScriptIsConsole = $consolePos == $expectedConsolePos;
$isScriptIsConsole = ($consolePos === $expectedConsolePos);
return self::isPhpCliMode() && $isScriptIsConsole;
}
@ -151,7 +177,7 @@ class Common
/**
* Multi-byte substr() - works with UTF-8.
*
*
* Calls `mb_substr` if available and falls back to `substr` if it's not.
*
* @param string $string
@ -175,7 +201,7 @@ class Common
/**
* Multi-byte strlen() - works with UTF-8
*
*
* Calls `mb_substr` if available and falls back to `substr` if not.
*
* @param string $string
@ -193,9 +219,9 @@ class Common
/**
* Multi-byte strtolower() - works with UTF-8.
*
*
* Calls `mb_strtolower` if available and falls back to `strtolower` if not.
*
*
* @param string $string
* @return string
* @api
@ -215,18 +241,18 @@ class Common
/**
* Sanitizes a string to help avoid XSS vulnerabilities.
*
*
* This function is automatically called when {@link getRequestVar()} is called,
* so you should not normally have to use it.
*
*
* This function should be used when outputting data that isn't escaped and was
* obtained from the user (for example when using the `|raw` twig filter on goal names).
*
*
* _NOTE: Sanitized input should not be used directly in an SQL query; SQL placeholders
* should still be used._
*
*
* **Implementation Details**
*
*
* - [htmlspecialchars](http://php.net/manual/en/function.htmlspecialchars.php) is used to escape text.
* - Single quotes are not escaped so **Piwik's amazing community** will still be
* **Piwik's amazing community**.
@ -246,10 +272,11 @@ class Common
if (is_numeric($value)) {
return $value;
} elseif (is_string($value)) {
$value = self::sanitizeInputValue($value);
$value = self::sanitizeString($value);
if (!$alreadyStripslashed) {
// a JSON array was already stripslashed, don't do it again for each value
if (!$alreadyStripslashed) // a JSON array was already stripslashed, don't do it again for each value
{
$value = self::undoMagicQuotes($value);
}
} elseif (is_array($value)) {
@ -272,20 +299,32 @@ class Common
}
/**
* Sanitize a single input value
* Sanitize a single input value and removes line breaks, tabs and null characters.
*
* @param string $value
* @return string sanitized input
*/
public static function sanitizeInputValue($value)
{
$value = self::sanitizeLineBreaks($value);
$value = self::sanitizeString($value);
return $value;
}
/**
* Sanitize a single input value
*
* @param $value
* @return string
*/
private static function sanitizeString($value)
{
// $_GET and $_REQUEST already urldecode()'d
// decode
// note: before php 5.2.7, htmlspecialchars() double encodes &#x hex items
$value = html_entity_decode($value, self::HTML_ENCODING_QUOTE_STYLE, 'UTF-8');
// filter
$value = self::sanitizeLineBreaks($value);
$value = self::sanitizeNullBytes($value);
// escape
$tmp = @htmlspecialchars($value, self::HTML_ENCODING_QUOTE_STYLE, 'UTF-8');
@ -295,15 +334,17 @@ class Common
// convert and escape
$value = utf8_encode($value);
$tmp = htmlspecialchars($value, self::HTML_ENCODING_QUOTE_STYLE, 'UTF-8');
return $tmp;
}
return $tmp;
}
/**
* Unsanitizes a single input value and returns the result.
*
*
* @param string $value
* @return string unsanitized input
* @api
*/
public static function unsanitizeInputValue($value)
{
@ -315,10 +356,10 @@ class Common
*
* This method should be used when you need to unescape data that was obtained from
* the user.
*
*
* Some data in Piwik is stored sanitized (such as site name). In this case you may
* have to use this method to unsanitize it in order to, for example, output it in JSON.
*
*
* @param string|array $value The data to unsanitize. If an array is passed, the
* array is sanitized recursively. Key values are not unsanitized.
* @return string|array The unsanitized data.
@ -345,28 +386,42 @@ class Common
*/
private static function undoMagicQuotes($value)
{
return version_compare(PHP_VERSION, '5.4', '<')
&& get_magic_quotes_gpc()
? stripslashes($value)
: $value;
}
static $shouldUndo;
if (!isset($shouldUndo)) {
$shouldUndo = version_compare(PHP_VERSION, '5.4', '<') && get_magic_quotes_gpc();
}
if ($shouldUndo) {
$value = stripslashes($value);
}
/**
*
* @param string
* @return string Line breaks and line carriage removed
*/
public static function sanitizeLineBreaks($value)
{
$value = str_replace(array("\n", "\r", "\0"), '', $value);
return $value;
}
/**
* @param string $value
* @return string Line breaks and line carriage removed
*/
public static function sanitizeLineBreaks($value)
{
return str_replace(array("\n", "\r"), '', $value);
}
/**
* @param string $value
* @return string Null bytes removed
*/
public static function sanitizeNullBytes($value)
{
return str_replace(array("\0"), '', $value);
}
/**
* Gets a sanitized request parameter by name from the `$_GET` and `$_POST` superglobals.
*
*
* Use this function to get request parameter values. **_NEVER use `$_GET` and `$_POST` directly._**
*
*
* If the variable cannot be found, and a default value was not provided, an exception is raised.
*
* _See {@link sanitizeInputValues()} to learn more about sanitization._
@ -376,7 +431,7 @@ class Common
* @param string|null $varDefault The value to return if the request parameter cannot be found or has an empty value.
* @param string|null $varType Expected type of the request variable. This parameters value must be one of the following:
* `'array'`, `'int'`, `'integer'`, `'string'`, `'json'`.
*
*
* If `'json'`, the string value will be `json_decode`-d and then sanitized.
* @param array|null $requestArrayToUse The array to use instead of `$_GET` and `$_POST`.
* @throws Exception If the request parameter doesn't exist and there is no default value, or if the request parameter
@ -389,6 +444,7 @@ class Common
if (is_null($requestArrayToUse)) {
$requestArrayToUse = $_GET + $_POST;
}
$varDefault = self::sanitizeInputValues($varDefault);
if ($varType === 'int') {
// settype accepts only integer
@ -420,22 +476,36 @@ class Common
// we deal w/ json differently
if ($varType == 'json') {
$value = self::undoMagicQuotes($requestArrayToUse[$varName]);
$value = self::json_decode($value, $assoc = true);
$value = json_decode($value, $assoc = true);
return self::sanitizeInputValues($value, $alreadyStripslashed = true);
}
$value = self::sanitizeInputValues($requestArrayToUse[$varName]);
if (!is_null($varType)) {
if (isset($varType)) {
$ok = false;
if ($varType === 'string') {
if (is_string($value)) $ok = true;
if (is_string($value) || is_int($value)) {
$ok = true;
} elseif (is_float($value)) {
$value = Common::forceDotAsSeparatorForDecimalPoint($value);
$ok = true;
}
} elseif ($varType === 'integer') {
if ($value == (string)(int)$value) $ok = true;
if ($value == (string)(int)$value) {
$ok = true;
}
} elseif ($varType === 'float') {
if ($value == (string)(float)$value) $ok = true;
$valueToCompare = (string)(float)$value;
$valueToCompare = Common::forceDotAsSeparatorForDecimalPoint($valueToCompare);
if ($value == $valueToCompare) {
$ok = true;
}
} elseif ($varType === 'array') {
if (is_array($value)) $ok = true;
if (is_array($value)) {
$ok = true;
}
} else {
throw new Exception("\$varType specified is not known. It should be one of the following: array, int, integer, float, string");
}
@ -452,6 +522,7 @@ class Common
}
settype($value, $varType);
}
return $value;
}
@ -466,11 +537,17 @@ class Common
*/
public static function generateUniqId()
{
return md5(uniqid(rand(), true));
if (function_exists('mt_rand')) {
$rand = mt_rand();
} else {
$rand = rand();
}
return md5(uniqid($rand, true));
}
/**
* Configureable hash() algorithm (defaults to md5)
* Configurable hash() algorithm (defaults to md5)
*
* @param string $str String to be hashed
* @param bool $raw_output
@ -479,14 +556,16 @@ class Common
public static function hash($str, $raw_output = false)
{
static $hashAlgorithm = null;
if (is_null($hashAlgorithm)) {
$hashAlgorithm = @Config::getInstance()->General['hash_algorithm'];
}
if ($hashAlgorithm) {
$hash = @hash($hashAlgorithm, $str, $raw_output);
if ($hash !== false)
if ($hash !== false) {
return $hash;
}
}
return md5($str, $raw_output);
@ -503,16 +582,13 @@ class Common
public static function getRandomString($length = 16, $alphabet = "abcdefghijklmnoprstuvwxyz0123456789")
{
$chars = $alphabet;
$str = '';
list($usec, $sec) = explode(" ", microtime());
$seed = ((float)$sec + (float)$usec) * 100000;
mt_srand($seed);
$str = '';
for ($i = 0; $i < $length; $i++) {
$rand_key = mt_rand(0, strlen($chars) - 1);
$str .= substr($chars, $rand_key, 1);
}
return str_shuffle($str);
}
@ -554,27 +630,22 @@ class Common
) {
throw new Exception("visitorId is expected to be a " . Tracker::LENGTH_HEX_ID_STRING . " hex char string");
}
return self::hex2bin($id);
}
/**
* Convert IP address (in network address format) to presentation format.
* This is a backward compatibility function for code that only expects
* IPv4 addresses (i.e., doesn't support IPv6).
* Converts a User ID string to the Visitor ID Binary representation.
*
* @see IP::N2P()
*
* This function does not support the long (or its string representation)
* returned by the built-in ip2long() function, from Piwik 1.3 and earlier.
*
* @deprecated 1.4
*
* @param string $ip IP address in network address format
* @param $userId
* @return string
*/
public static function long2ip($ip)
public static function convertUserIdToVisitorIdBin($userId)
{
return IP::long2ip($ip);
require_once PIWIK_INCLUDE_PATH . '/libs/PiwikTracker/PiwikTracker.php';
$userIdHashed = \PiwikTracker::getUserIdHashed($userId);
return self::convertVisitorIdToBin($userIdHashed);
}
/**
@ -653,14 +724,14 @@ class Common
/**
* Returns the list of parent classes for the given class.
*
* @param string $klass A class name.
* @param string $class A class name.
* @return string[] The list of parent classes in order from highest ancestor to the descended class.
*/
public static function getClassLineage($klass)
public static function getClassLineage($class)
{
$klasses = array_merge(array($klass), array_values(class_parents($klass, $autoload = false)));
$classes = array_merge(array($class), array_values(class_parents($class, $autoload = false)));
return array_reverse($klasses);
return array_reverse($classes);
}
/*
@ -672,14 +743,16 @@ class Common
*
* @see core/DataFiles/Countries.php
*
* @return array Array of 3 letter continent codes
* @return array Array of 3 letter continent codes
*
* @deprecated Use Piwik\Intl\Data\Provider\RegionDataProvider instead.
* @see \Piwik\Intl\Data\Provider\RegionDataProvider::getContinentList()
*/
public static function getContinentsList()
{
require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/Countries.php';
$continentsList = $GLOBALS['Piwik_ContinentList'];
return $continentsList;
/** @var RegionDataProvider $dataProvider */
$dataProvider = StaticContainer::get('Piwik\Intl\Data\Provider\RegionDataProvider');
return $dataProvider->getContinentList();
}
/**
@ -688,19 +761,16 @@ class Common
* @see core/DataFiles/Countries.php
*
* @param bool $includeInternalCodes
* @return array Array of (2 letter ISO codes => 3 letter continent code)
* @return array Array of (2 letter ISO codes => 3 letter continent code)
*
* @deprecated Use Piwik\Intl\Data\Provider\RegionDataProvider instead.
* @see \Piwik\Intl\Data\Provider\RegionDataProvider::getCountryList()
*/
public static function getCountriesList($includeInternalCodes = false)
{
require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/Countries.php';
$countriesList = $GLOBALS['Piwik_CountryList'];
$extras = $GLOBALS['Piwik_CountryList_Extras'];
if ($includeInternalCodes) {
return array_merge($countriesList, $extras);
}
return $countriesList;
/** @var RegionDataProvider $dataProvider */
$dataProvider = StaticContainer::get('Piwik\Intl\Data\Provider\RegionDataProvider');
return $dataProvider->getCountryList($includeInternalCodes);
}
/**
@ -711,13 +781,15 @@ class Common
* @return array Array of two letter ISO codes mapped with their associated language names (in English). E.g.
* `array('en' => 'English', 'ja' => 'Japanese')`.
* @api
*
* @deprecated Use Piwik\Intl\Data\Provider\LanguageDataProvider instead.
* @see \Piwik\Intl\Data\Provider\LanguageDataProvider::getLanguageList()
*/
public static function getLanguagesList()
{
require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/Languages.php';
$languagesList = $GLOBALS['Piwik_LanguageList'];
return $languagesList;
/** @var LanguageDataProvider $dataProvider */
$dataProvider = StaticContainer::get('Piwik\Intl\Data\Provider\LanguageDataProvider');
return $dataProvider->getLanguageList();
}
/**
@ -728,70 +800,15 @@ class Common
* @return array Array of two letter ISO language codes mapped with two letter ISO country codes:
* `array('fr' => 'fr') // French => France`
* @api
*
* @deprecated Use Piwik\Intl\Data\Provider\LanguageDataProvider instead.
* @see \Piwik\Intl\Data\Provider\LanguageDataProvider::getLanguageToCountryList()
*/
public static function getLanguageToCountryList()
{
require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/LanguageToCountry.php';
$languagesList = $GLOBALS['Piwik_LanguageToCountry'];
return $languagesList;
}
/**
* Returns list of search engines by URL
*
* @see core/DataFiles/SearchEngines.php
*
* @return array Array of ( URL => array( searchEngineName, keywordParameter, path, charset ) )
*/
public static function getSearchEngineUrls()
{
require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/SearchEngines.php';
$searchEngines = $GLOBALS['Piwik_SearchEngines'];
Piwik::postEvent('Referrer.addSearchEngineUrls', array(&$searchEngines));
return $searchEngines;
}
/**
* Returns list of search engines by name
*
* @see core/DataFiles/SearchEngines.php
*
* @return array Array of ( searchEngineName => URL )
*/
public static function getSearchEngineNames()
{
$searchEngines = self::getSearchEngineUrls();
$nameToUrl = array();
foreach ($searchEngines as $url => $info) {
if (!isset($nameToUrl[$info[0]])) {
$nameToUrl[$info[0]] = $url;
}
}
return $nameToUrl;
}
/**
* Returns list of social networks by URL
*
* @see core/DataFiles/Socials.php
*
* @return array Array of ( URL => Social Network Name )
*/
public static function getSocialUrls()
{
require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/Socials.php';
$socialUrls = $GLOBALS['Piwik_socialUrl'];
Piwik::postEvent('Referrer.addSocialUrls', array(&$socialUrls));
return $socialUrls;
/** @var LanguageDataProvider $dataProvider */
$dataProvider = StaticContainer::get('Piwik\Intl\Data\Provider\LanguageDataProvider');
return $dataProvider->getLanguageToCountryList();
}
/**
@ -840,7 +857,7 @@ class Common
}
}
if (is_null($browserLang)) {
if (empty($browserLang)) {
// a fallback might be to infer the language in HTTP_USER_AGENT (i.e., localized build)
$browserLang = "";
} else {
@ -872,11 +889,15 @@ class Common
*/
public static function getCountry($lang, $enableLanguageToCountryGuess, $ip)
{
if (empty($lang) || strlen($lang) < 2 || $lang == 'xx') {
return 'xx';
if (empty($lang) || strlen($lang) < 2 || $lang == self::LANGUAGE_CODE_INVALID) {
return self::LANGUAGE_CODE_INVALID;
}
$validCountries = self::getCountriesList();
/** @var RegionDataProvider $dataProvider */
$dataProvider = StaticContainer::get('Piwik\Intl\Data\Provider\RegionDataProvider');
$validCountries = $dataProvider->getCountryList();
return self::extractCountryCodeFromBrowserLanguage($lang, $validCountries, $enableLanguageToCountryGuess);
}
@ -890,7 +911,10 @@ class Common
*/
public static function extractCountryCodeFromBrowserLanguage($browserLanguage, $validCountries, $enableLanguageToCountryGuess)
{
$langToCountry = self::getLanguageToCountryList();
/** @var LanguageDataProvider $dataProvider */
$dataProvider = StaticContainer::get('Piwik\Intl\Data\Provider\LanguageDataProvider');
$langToCountry = $dataProvider->getLanguageToCountryList();
if ($enableLanguageToCountryGuess) {
if (preg_match('/^([a-z]{2,3})(?:,|;|$)/', $browserLanguage, $matches)) {
@ -909,51 +933,90 @@ class Common
}
}
}
return 'xx';
return self::LANGUAGE_CODE_INVALID;
}
/**
* Returns the visitor language based only on the Browser 'accepted language' information
* Returns the language and region string, based only on the Browser 'accepted language' information.
* * The language tag is defined by ISO 639-1
*
* @param string $browserLanguage Browser's accepted langauge header
* @param array $validLanguages array of valid language codes
* @return string 2 letter ISO 639 code
* @return string 2 letter ISO 639 code 'es' (Spanish)
*/
public static function extractLanguageCodeFromBrowserLanguage($browserLanguage, $validLanguages)
public static function extractLanguageCodeFromBrowserLanguage($browserLanguage, $validLanguages = array())
{
// assumes language preference is sorted;
// does not handle language-script-region tags or language range (*)
if (!empty($validLanguages) && preg_match_all('/(?:^|,)([a-z]{2,3})([-][a-z]{2})?/', $browserLanguage, $matches, PREG_SET_ORDER)) {
foreach ($matches as $parts) {
if (count($parts) == 3) {
// match locale (language and location)
if (in_array($parts[1] . $parts[2], $validLanguages)) {
return $parts[1] . $parts[2];
}
$validLanguages = self::checkValidLanguagesIsSet($validLanguages);
$languageRegionCode = self::extractLanguageAndRegionCodeFromBrowserLanguage($browserLanguage, $validLanguages);
if (strlen($languageRegionCode) == 2) {
$languageCode = $languageRegionCode;
} else {
$languageCode = substr($languageRegionCode, 0, 2);
}
if (in_array($languageCode, $validLanguages)) {
return $languageCode;
}
return self::LANGUAGE_CODE_INVALID;
}
/**
* Returns the language and region string, based only on the Browser 'accepted language' information.
* * The language tag is defined by ISO 639-1
* * The region tag is defined by ISO 3166-1
*
* @param string $browserLanguage Browser's accepted langauge header
* @param array $validLanguages array of valid language codes. Note that if the array includes "fr" then it will consider all regional variants of this language valid, such as "fr-ca" etc.
* @return string 2 letter ISO 639 code 'es' (Spanish) or if found, includes the region as well: 'es-ar'
*/
public static function extractLanguageAndRegionCodeFromBrowserLanguage($browserLanguage, $validLanguages = array())
{
$validLanguages = self::checkValidLanguagesIsSet($validLanguages);
if (!preg_match_all('/(?:^|,)([a-z]{2,3})([-][a-z]{2})?/', $browserLanguage, $matches, PREG_SET_ORDER)) {
return self::LANGUAGE_CODE_INVALID;
}
foreach ($matches as $parts) {
$langIso639 = $parts[1];
if (empty($langIso639)) {
continue;
}
// If a region tag is found eg. "fr-ca"
if (count($parts) == 3) {
$regionIso3166 = $parts[2]; // eg. "-ca"
if (in_array($langIso639 . $regionIso3166, $validLanguages)) {
return $langIso639 . $regionIso3166;
}
// match language only (where no region provided)
if (in_array($parts[1], $validLanguages)) {
return $parts[1];
if (in_array($langIso639, $validLanguages)) {
return $langIso639 . $regionIso3166;
}
}
// eg. "fr" or "es"
if (in_array($langIso639, $validLanguages)) {
return $langIso639;
}
}
return 'xx';
return self::LANGUAGE_CODE_INVALID;
}
/**
* Returns the continent of a given country
*
* @param string $country 2 letters isocode
* @param string $country 2 letters iso code
*
* @return string Continent (3 letters code : afr, asi, eur, amn, ams, oce)
*/
public static function getContinent($country)
{
$countryList = self::getCountriesList();
if (isset($countryList[$country])) {
return $countryList[$country];
}
return 'unk';
/** @var RegionDataProvider $dataProvider */
$dataProvider = StaticContainer::get('Piwik\Intl\Data\Provider\RegionDataProvider');
$countryList = $dataProvider->getCountryList();
return isset($countryList[$country]) ? $countryList[$country] : 'unk';
}
/*
@ -995,10 +1058,10 @@ class Common
/**
* Returns a string with a comma separated list of placeholders for use in an SQL query. Used mainly
* to fill the `IN (...)` part of a query.
*
*
* @param array|string $fields The names of the mysql table fields to bind, e.g.
* `array(fieldName1, fieldName2, fieldName3)`.
*
*
* _Note: The content of the array isn't important, just its length._
* @return string The placeholder string, e.g. `"?, ?, ?"`.
* @api
@ -1015,6 +1078,22 @@ class Common
return '?' . str_repeat(',?', $count - 1);
}
/**
* Force the separator for decimal point to be a dot. See https://github.com/piwik/piwik/issues/6435
* If for instance a German locale is used it would be a comma otherwise.
*
* @param float|string $value
* @return string
*/
public static function forceDotAsSeparatorForDecimalPoint($value)
{
if (null === $value || false === $value) {
return $value;
}
return str_replace(',', '.', $value);
}
/**
* Sets outgoing header.
*
@ -1024,23 +1103,62 @@ class Common
public static function sendHeader($header, $replace = true)
{
// don't send header in CLI mode
if(Common::isPhpCliMode()) {
return;
}
if (isset($GLOBALS['PIWIK_TRACKER_LOCAL_TRACKING']) && $GLOBALS['PIWIK_TRACKER_LOCAL_TRACKING']) {
@header($header, $replace);
} else {
if (!Common::isPhpCliMode() and !headers_sent()) {
header($header, $replace);
}
}
/**
* Sends the given response code if supported.
*
* @param int $code Eg 204
*
* @throws Exception
*/
public static function sendResponseCode($code)
{
$messages = array(
200 => 'Ok',
204 => 'No Response',
301 => 'Moved Permanently',
302 => 'Found',
304 => 'Not Modified',
400 => 'Bad Request',
401 => 'Unauthorized',
403 => 'Forbidden',
404 => 'Not Found',
500 => 'Internal Server Error',
503 => 'Service Unavailable',
);
if (!array_key_exists($code, $messages)) {
throw new Exception('Response code not supported: ' . $code);
}
if (strpos(PHP_SAPI, '-fcgi') === false) {
$key = 'HTTP/1.1';
if (array_key_exists('SERVER_PROTOCOL', $_SERVER)
&& strlen($_SERVER['SERVER_PROTOCOL']) < 15
&& strlen($_SERVER['SERVER_PROTOCOL']) > 1) {
$key = $_SERVER['SERVER_PROTOCOL'];
}
} else {
// FastCGI
$key = 'Status:';
}
$message = $messages[$code];
Common::sendHeader($key . ' ' . $code . ' ' . $message);
}
/**
* Returns the ID of the current LocationProvider (see UserCountry plugin code) from
* the Tracker cache.
*/
public static function getCurrentLocationProviderId()
{
$cache = Cache::getCacheGeneral();
$cache = TrackerCache::getCacheGeneral();
return empty($cache['currentLocationProviderId'])
? DefaultProvider::ID
: $cache['currentLocationProviderId'];
@ -1049,11 +1167,11 @@ class Common
/**
* Marks an orphaned object for garbage collection.
*
* For more information: {@link http://dev.piwik.org/trac/ticket/374}
* @param $var The object to destroy.
* For more information: {@link https://github.com/piwik/piwik/issues/374}
* @param mixed $var The object to destroy.
* @api
*/
static public function destroy(&$var)
public static function destroy(&$var)
{
if (is_object($var) && method_exists($var, '__destruct')) {
$var->__destruct();
@ -1062,27 +1180,62 @@ class Common
$var = null;
}
static public function printDebug($info = '')
/**
* @todo This method is weird, it's debugging statements but seem to only work for the tracker, maybe it
* should be moved elsewhere
*/
public static function printDebug($info = '')
{
if (isset($GLOBALS['PIWIK_TRACKER_DEBUG']) && $GLOBALS['PIWIK_TRACKER_DEBUG']) {
if (!headers_sent()) {
// prevent XSS in tracker debug output
header('Content-type: text/plain');
}
if (is_object($info)) {
$info = var_export($info, true);
}
Log::getInstance()->setLogLevel(Log::DEBUG);
$logger = StaticContainer::get('Psr\Log\LoggerInterface');
if (is_array($info) || is_object($info)) {
$info = Common::sanitizeInputValues($info);
$out = var_export($info, true);
foreach (explode("\n", $out) as $line) {
Log::debug($line);
$logger->debug($line);
}
} else {
foreach (explode("\n", $info) as $line) {
Log::debug(htmlspecialchars($line, ENT_QUOTES));
$logger->debug($line);
}
}
}
}
/**
* Returns true if the request is an AJAX request.
*
* @return bool
*/
public static function isXmlHttpRequest()
{
return isset($_SERVER['HTTP_X_REQUESTED_WITH'])
&& (strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest');
}
/**
* @param $validLanguages
* @return array
*/
protected static function checkValidLanguagesIsSet($validLanguages)
{
/** @var LanguageDataProvider $dataProvider */
$dataProvider = StaticContainer::get('Piwik\Intl\Data\Provider\LanguageDataProvider');
if (empty($validLanguages)) {
$validLanguages = array_keys($dataProvider->getLanguageList());
return $validLanguages;
}
return $validLanguages;
}
}