Teknik is a suite of services with attractive and functional interfaces. https://www.teknik.io/
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 
 
 
 

1113 rader
36 KiB

<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik;
use Exception;
use Piwik\Plugins\UserCountry\LocationProvider\DefaultProvider;
use Piwik\Tracker;
use Piwik\Tracker\Cache;
/**
* Contains helper methods used by both Piwik Core and the Piwik Tracking engine.
*
* This is the only non-Tracker class loaded by the **\/piwik.php** file.
*/
class Common
{
// constants used to map the referrer type to an integer in the log_visit table
const REFERRER_TYPE_DIRECT_ENTRY = 1;
const REFERRER_TYPE_SEARCH_ENGINE = 2;
const REFERRER_TYPE_WEBSITE = 3;
const REFERRER_TYPE_CAMPAIGN = 6;
// Flag used with htmlspecialchar. See php.net/htmlspecialchars.
const HTML_ENCODING_QUOTE_STYLE = ENT_QUOTES;
public static $isCliMode = null;
/*
* Database
*/
/**
* Hashes a string into an integer which should be very low collision risks
* @param string $string String to hash
* @return int Resulting int hash
*/
public static function hashStringToInt($string)
{
$stringHash = substr(md5($string), 0, 8);
return base_convert($stringHash, 16, 10);
}
/**
* Returns a prefixed table name.
*
* The table prefix is determined by the `[database] tables_prefix` INI config
* option.
*
* @param string $table The table name to prefix, ie "log_visit"
* @return string The prefixed name, ie "piwik-production_log_visit".
* @api
*/
public static function prefixTable($table)
{
$prefix = Config::getInstance()->database['tables_prefix'];
return $prefix . $table;
}
/**
* Returns an array containing the prefixed table names of every passed argument.
*
* @param string ... The table names to prefix, ie "log_visit"
* @return array The prefixed names in an array.
*/
public static function prefixTables()
{
$result = array();
foreach (func_get_args() as $table) {
$result[] = self::prefixTable($table);
}
return $result;
}
/**
* Removes the prefix from a table name and returns the result.
*
* 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
*/
public static function unprefixTable($table)
{
static $prefixTable = null;
if (is_null($prefixTable)) {
$prefixTable = Config::getInstance()->database['tables_prefix'];
}
if (empty($prefixTable)
|| strpos($table, $prefixTable) !== 0
) {
return $table;
}
$count = 1;
return str_replace($prefixTable, '', $table, $count);
}
/*
* Tracker
*/
public static function isGoalPluginEnabled()
{
return \Piwik\Plugin\Manager::getInstance()->isPluginActivated('Goals');
}
public static function isActionsPluginEnabled()
{
return \Piwik\Plugin\Manager::getInstance()->isPluginActivated('Actions');
}
/**
* Returns true if PHP was invoked from command-line interface (shell)
*
* @since added in 0.4.4
* @return bool true if PHP invoked as a CGI or from CLI
*/
public static function isPhpCliMode()
{
if (is_bool(self::$isCliMode)) {
return self::$isCliMode;
}
$remoteAddr = @$_SERVER['REMOTE_ADDR'];
return PHP_SAPI == 'cli' ||
(self::isPhpCgiType() && empty($remoteAddr));
}
/**
* 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';
$consolePos = strpos($_SERVER['SCRIPT_NAME'], $searched);
$expectedConsolePos = strlen($_SERVER['SCRIPT_NAME']) - strlen($searched);
$isScriptIsConsole = ($consolePos === $expectedConsolePos);
return self::isPhpCliMode() && $isScriptIsConsole;
}
/*
* String operations
*/
/**
* Multi-byte substr() - works with UTF-8.
*
* Calls `mb_substr` if available and falls back to `substr` if it's not.
*
* @param string $string
* @param int $start
* @param int ... optional length
* @return string
* @api
*/
public static function mb_substr($string, $start)
{
$length = func_num_args() > 2
? func_get_arg(2)
: self::mb_strlen($string);
if (function_exists('mb_substr')) {
return mb_substr($string, $start, $length, 'UTF-8');
}
return substr($string, $start, $length);
}
/**
* Multi-byte strlen() - works with UTF-8
*
* Calls `mb_substr` if available and falls back to `substr` if not.
*
* @param string $string
* @return int
* @api
*/
public static function mb_strlen($string)
{
if (function_exists('mb_strlen')) {
return mb_strlen($string, 'UTF-8');
}
return strlen($string);
}
/**
* 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
*/
public static function mb_strtolower($string)
{
if (function_exists('mb_strtolower')) {
return mb_strtolower($string, 'UTF-8');
}
return strtolower($string);
}
/*
* Escaping input
*/
/**
* 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**.
* - Use of the `magic_quotes` setting will not break this method.
* - Boolean, numeric and null values are not modified.
*
* @param mixed $value The variable to be sanitized. If an array is supplied, the contents
* of the array will be sanitized recursively. The keys of the array
* will also be sanitized.
* @param bool $alreadyStripslashed Implementation detail, ignore.
* @throws Exception If `$value` is of an incorrect type.
* @return mixed The sanitized value.
* @api
*/
public static function sanitizeInputValues($value, $alreadyStripslashed = false)
{
if (is_numeric($value)) {
return $value;
} elseif (is_string($value)) {
$value = self::sanitizeInputValue($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)) {
foreach (array_keys($value) as $key) {
$newKey = $key;
$newKey = self::sanitizeInputValues($newKey, $alreadyStripslashed);
if ($key != $newKey) {
$value[$newKey] = $value[$key];
unset($value[$key]);
}
$value[$newKey] = self::sanitizeInputValues($value[$newKey], $alreadyStripslashed);
}
} elseif (!is_null($value)
&& !is_bool($value)
) {
throw new Exception("The value to escape has not a supported type. Value = " . var_export($value, true));
}
return $value;
}
/**
* Sanitize a single input value
*
* @param string $value
* @return string sanitized input
*/
public static function sanitizeInputValue($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);
// escape
$tmp = @htmlspecialchars($value, self::HTML_ENCODING_QUOTE_STYLE, 'UTF-8');
// note: php 5.2.5 and above, htmlspecialchars is destructive if input is not UTF-8
if ($value != '' && $tmp == '') {
// convert and escape
$value = utf8_encode($value);
$tmp = htmlspecialchars($value, self::HTML_ENCODING_QUOTE_STYLE, 'UTF-8');
}
return $tmp;
}
/**
* Unsanitizes a single input value and returns the result.
*
* @param string $value
* @return string unsanitized input
*/
public static function unsanitizeInputValue($value)
{
return htmlspecialchars_decode($value, self::HTML_ENCODING_QUOTE_STYLE);
}
/**
* Unsanitizes one or more values and returns the result.
*
* 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.
* @api
*/
public static function unsanitizeInputValues($value)
{
if (is_array($value)) {
$result = array();
foreach ($value as $key => $arrayValue) {
$result[$key] = self::unsanitizeInputValues($arrayValue);
}
return $result;
} else {
return self::unsanitizeInputValue($value);
}
}
/**
* Undo the damage caused by magic_quotes; deprecated in php 5.3 but not removed until php 5.4
*
* @param string
* @return string modified or not
*/
private static function undoMagicQuotes($value)
{
return version_compare(PHP_VERSION, '5.4', '<')
&& get_magic_quotes_gpc()
? stripslashes($value)
: $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;
}
/**
* 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._
*
* @param string $varName Name of the request parameter to get. By default, we look in `$_GET[$varName]`
* and `$_POST[$varName]` for the value.
* @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
* exists but has an incorrect type.
* @return mixed The sanitized request parameter.
* @api
*/
public static function getRequestVar($varName, $varDefault = null, $varType = null, $requestArrayToUse = null)
{
if (is_null($requestArrayToUse)) {
$requestArrayToUse = $_GET + $_POST;
}
$varDefault = self::sanitizeInputValues($varDefault);
if ($varType === 'int') {
// settype accepts only integer
// 'int' is simply a shortcut for 'integer'
$varType = 'integer';
}
// there is no value $varName in the REQUEST so we try to use the default value
if (empty($varName)
|| !isset($requestArrayToUse[$varName])
|| (!is_array($requestArrayToUse[$varName])
&& strlen($requestArrayToUse[$varName]) === 0
)
) {
if (is_null($varDefault)) {
throw new Exception("The parameter '$varName' isn't set in the Request, and a default value wasn't provided.");
} else {
if (!is_null($varType)
&& in_array($varType, array('string', 'integer', 'array'))
) {
settype($varDefault, $varType);
}
return $varDefault;
}
}
// Normal case, there is a value available in REQUEST for the requested varName:
// we deal w/ json differently
if ($varType == 'json') {
$value = self::undoMagicQuotes($requestArrayToUse[$varName]);
$value = self::json_decode($value, $assoc = true);
return self::sanitizeInputValues($value, $alreadyStripslashed = true);
}
$value = self::sanitizeInputValues($requestArrayToUse[$varName]);
if (!is_null($varType)) {
$ok = false;
if ($varType === 'string') {
if (is_string($value)) $ok = true;
} elseif ($varType === 'integer') {
if ($value == (string)(int)$value) $ok = true;
} elseif ($varType === 'float') {
if ($value == (string)(float)$value) $ok = true;
} elseif ($varType === 'array') {
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");
}
// The type is not correct
if ($ok === false) {
if ($varDefault === null) {
throw new Exception("The parameter '$varName' doesn't have a correct type, and a default value wasn't provided.");
} // we return the default value with the good type set
else {
settype($varDefault, $varType);
return $varDefault;
}
}
settype($value, $varType);
}
return $value;
}
/*
* Generating unique strings
*/
/**
* Returns a 32 characters long uniq ID
*
* @return string 32 chars
*/
public static function generateUniqId()
{
return md5(uniqid(rand(), true));
}
/**
* Configureable hash() algorithm (defaults to md5)
*
* @param string $str String to be hashed
* @param bool $raw_output
* @return string Hash string
*/
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)
return $hash;
}
return md5($str, $raw_output);
}
/**
* Generate random string.
* Do not use for security related purposes (the string is not truly random).
*
* @param int $length string length
* @param string $alphabet characters allowed in random string
* @return string random string with given length
*/
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);
for ($i = 0; $i < $length; $i++) {
$rand_key = mt_rand(0, strlen($chars) - 1);
$str .= substr($chars, $rand_key, 1);
}
return str_shuffle($str);
}
/*
* Conversions
*/
/**
* Convert hexadecimal representation into binary data.
* !! Will emit warning if input string is not hex!!
*
* @see http://php.net/bin2hex
*
* @param string $str Hexadecimal representation
* @return string
*/
public static function hex2bin($str)
{
return pack("H*", $str);
}
/**
* This function will convert the input string to the binary representation of the ID
* but it will throw an Exception if the specified input ID is not correct
*
* This is used when building segments containing visitorId which could be an invalid string
* therefore throwing Unexpected PHP error [pack(): Type H: illegal hex digit i] severity [E_WARNING]
*
* It would be simply to silent fail the pack() call above but in all other cases, we don't expect an error,
* so better be safe and get the php error when something unexpected is happening
* @param string $id
* @throws Exception
* @return string binary string
*/
public static function convertVisitorIdToBin($id)
{
if (strlen($id) !== Tracker::LENGTH_HEX_ID_STRING
|| @bin2hex(self::hex2bin($id)) != $id
) {
throw new Exception("visitorId is expected to be a " . Tracker::LENGTH_HEX_ID_STRING . " hex char string");
}
return self::hex2bin($id);
}
/**
* Converts a User ID string to the Visitor ID Binary representation.
*
* @param $userId
* @return string
*/
public static function convertUserIdToVisitorIdBin($userId)
{
require_once PIWIK_INCLUDE_PATH . '/libs/PiwikTracker/PiwikTracker.php';
$userIdHashed = \PiwikTracker::getUserIdHashed($userId);
return self::convertVisitorIdToBin($userIdHashed);
}
/**
* 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).
*
* @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
* @return string
*/
public static function long2ip($ip)
{
return IP::long2ip($ip);
}
/**
* JSON encode wrapper
* - missing or broken in some php 5.x versions
*
* @param mixed $value
* @return string
* @deprecated
*/
public static function json_encode($value)
{
return @json_encode($value);
}
/**
* JSON decode wrapper
* - missing or broken in some php 5.x versions
*
* @param string $json
* @param bool $assoc
* @return mixed
* @deprecated
*/
public static function json_decode($json, $assoc = false)
{
return json_decode($json, $assoc);
}
/**
* Detects whether an error occurred during the last json encode/decode.
* @return bool
*/
public static function hasJsonErrorOccurred()
{
return json_last_error() != JSON_ERROR_NONE;
}
/**
* Returns a human readable error message in case an error occcurred during the last json encode/decode.
* Returns an empty string in case there was no error.
*
* @return string
*/
public static function getLastJsonError()
{
switch (json_last_error()) {
case JSON_ERROR_NONE:
return '';
case JSON_ERROR_DEPTH:
return 'Maximum stack depth exceeded';
case JSON_ERROR_STATE_MISMATCH:
return 'Underflow or the modes mismatch';
case JSON_ERROR_CTRL_CHAR:
return 'Unexpected control character found';
case JSON_ERROR_SYNTAX:
return 'Syntax error, malformed JSON';
case JSON_ERROR_UTF8:
return 'Malformed UTF-8 characters, possibly incorrectly encoded';
}
return 'Unknown error';
}
public static function stringEndsWith($haystack, $needle)
{
if ('' === $needle) {
return true;
}
$lastCharacters = substr($haystack, -strlen($needle));
return $lastCharacters === $needle;
}
/**
* Returns the list of parent classes for the given class.
*
* @param string $klass A class name.
* @return string[] The list of parent classes in order from highest ancestor to the descended class.
*/
public static function getClassLineage($klass)
{
$klasses = array_merge(array($klass), array_values(class_parents($klass, $autoload = false)));
return array_reverse($klasses);
}
/*
* DataFiles
*/
/**
* Returns list of continent codes
*
* @see core/DataFiles/Countries.php
*
* @return array Array of 3 letter continent codes
*/
public static function getContinentsList()
{
require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/Countries.php';
$continentsList = $GLOBALS['Piwik_ContinentList'];
return $continentsList;
}
/**
* Returns list of valid country codes
*
* @see core/DataFiles/Countries.php
*
* @param bool $includeInternalCodes
* @return array Array of (2 letter ISO codes => 3 letter continent code)
*/
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;
}
/**
* Returns the list of valid language codes.
*
* See [core/DataFiles/Languages.php](https://github.com/piwik/piwik/blob/master/core/DataFiles/Languages.php).
*
* @return array Array of two letter ISO codes mapped with their associated language names (in English). E.g.
* `array('en' => 'English', 'ja' => 'Japanese')`.
* @api
*/
public static function getLanguagesList()
{
require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/Languages.php';
$languagesList = $GLOBALS['Piwik_LanguageList'];
return $languagesList;
}
/**
* Returns a list of language to country mappings.
*
* See [core/DataFiles/LanguageToCountry.php](https://github.com/piwik/piwik/blob/master/core/DataFiles/LanguageToCountry.php).
*
* @return array Array of two letter ISO language codes mapped with two letter ISO country codes:
* `array('fr' => 'fr') // French => France`
* @api
*/
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;
}
/**
* Returns list of provider names
*
* @see core/DataFiles/Providers.php
*
* @return array Array of ( dnsName => providerName )
*/
public static function getProviderNames()
{
require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/Providers.php';
$providers = $GLOBALS['Piwik_ProviderNames'];
return $providers;
}
/*
* Language, country, continent
*/
/**
* Returns the browser language code, eg. "en-gb,en;q=0.5"
*
* @param string|null $browserLang Optional browser language, otherwise taken from the request header
* @return string
*/
public static function getBrowserLanguage($browserLang = null)
{
static $replacementPatterns = array(
// extraneous bits of RFC 3282 that we ignore
'/(\\\\.)/', // quoted-pairs
'/(\s+)/', // CFWcS white space
'/(\([^)]*\))/', // CFWS comments
'/(;q=[0-9.]+)/', // quality
// found in the LANG environment variable
'/\.(.*)/', // charset (e.g., en_CA.UTF-8)
'/^C$/', // POSIX 'C' locale
);
if (is_null($browserLang)) {
$browserLang = self::sanitizeInputValues(@$_SERVER['HTTP_ACCEPT_LANGUAGE']);
if (empty($browserLang) && self::isPhpCliMode()) {
$browserLang = @getenv('LANG');
}
}
if (empty($browserLang)) {
// a fallback might be to infer the language in HTTP_USER_AGENT (i.e., localized build)
$browserLang = "";
} else {
// language tags are case-insensitive per HTTP/1.1 s3.10 but the region may be capitalized per ISO3166-1;
// underscores are not permitted per RFC 4646 or 4647 (which obsolete RFC 1766 and 3066),
// but we guard against a bad user agent which naively uses its locale
$browserLang = strtolower(str_replace('_', '-', $browserLang));
// filters
$browserLang = preg_replace($replacementPatterns, '', $browserLang);
$browserLang = preg_replace('/((^|,)chrome:.*)/', '', $browserLang, 1); // Firefox bug
$browserLang = preg_replace('/(,)(?:en-securid,)|(?:(^|,)en-securid(,|$))/', '$1', $browserLang, 1); // unregistered language tag
$browserLang = str_replace('sr-sp', 'sr-rs', $browserLang); // unofficial (proposed) code in the wild
}
return $browserLang;
}
/**
* Returns the visitor country based on the Browser 'accepted language'
* information, but provides a hook for geolocation via IP address.
*
* @param string $lang browser lang
* @param bool $enableLanguageToCountryGuess If set to true, some assumption will be made and detection guessed more often, but accuracy could be affected
* @param string $ip
* @return string 2 letter ISO code
*/
public static function getCountry($lang, $enableLanguageToCountryGuess, $ip)
{
if (empty($lang) || strlen($lang) < 2 || $lang == 'xx') {
return 'xx';
}
$validCountries = self::getCountriesList();
return self::extractCountryCodeFromBrowserLanguage($lang, $validCountries, $enableLanguageToCountryGuess);
}
/**
* Returns list of valid country codes
*
* @param string $browserLanguage
* @param array $validCountries Array of valid countries
* @param bool $enableLanguageToCountryGuess (if true, will guess country based on language that lacks region information)
* @return array Array of 2 letter ISO codes
*/
public static function extractCountryCodeFromBrowserLanguage($browserLanguage, $validCountries, $enableLanguageToCountryGuess)
{
$langToCountry = self::getLanguageToCountryList();
if ($enableLanguageToCountryGuess) {
if (preg_match('/^([a-z]{2,3})(?:,|;|$)/', $browserLanguage, $matches)) {
// match language (without region) to infer the country of origin
if (array_key_exists($matches[1], $langToCountry)) {
return $langToCountry[$matches[1]];
}
}
}
if (!empty($validCountries) && preg_match_all('/[-]([a-z]{2})/', $browserLanguage, $matches, PREG_SET_ORDER)) {
foreach ($matches as $parts) {
// match location; we don't make any inferences from the language
if (array_key_exists($parts[1], $validCountries)) {
return $parts[1];
}
}
}
return 'xx';
}
/**
* Returns the visitor language based only on the Browser 'accepted language' information
*
* @param string $browserLanguage Browser's accepted langauge header
* @param array $validLanguages array of valid language codes
* @return string 2 letter ISO 639 code
*/
public static function extractLanguageCodeFromBrowserLanguage($browserLanguage, $validLanguages)
{
// 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];
}
}
// match language only (where no region provided)
if (in_array($parts[1], $validLanguages)) {
return $parts[1];
}
}
}
return 'xx';
}
/**
* Returns the continent of a given country
*
* @param string $country 2 letters isocode
*
* @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';
}
/*
* Campaign
*/
/**
* Returns the list of Campaign parameter names that will be read to classify
* a visit as coming from a Campaign
*
* @return array array(
* 0 => array( ... ) // campaign names parameters
* 1 => array( ... ) // campaign keyword parameters
* );
*/
public static function getCampaignParameters()
{
$return = array(
Config::getInstance()->Tracker['campaign_var_name'],
Config::getInstance()->Tracker['campaign_keyword_var_name'],
);
foreach ($return as &$list) {
if (strpos($list, ',') !== false) {
$list = explode(',', $list);
} else {
$list = array($list);
}
$list = array_map('trim', $list);
}
return $return;
}
/*
* Referrer
*/
/**
* 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
*/
public static function getSqlStringFieldsArray($fields)
{
if (is_string($fields)) {
$fields = array($fields);
}
$count = count($fields);
if ($count == 0) {
return "''";
}
return '?' . str_repeat(',?', $count - 1);
}
/**
* Sets outgoing header.
*
* @param string $header The header.
* @param bool $replace Whether to replace existing or not.
*/
public static function sendHeader($header, $replace = true)
{
// don't send header in CLI mode
if(!Common::isPhpCliMode() and !headers_sent()) {
header($header, $replace);
}
}
/**
* Returns the ID of the current LocationProvider (see UserCountry plugin code) from
* the Tracker cache.
*/
public static function getCurrentLocationProviderId()
{
$cache = Cache::getCacheGeneral();
return empty($cache['currentLocationProviderId'])
? DefaultProvider::ID
: $cache['currentLocationProviderId'];
}
/**
* Marks an orphaned object for garbage collection.
*
* For more information: {@link https://github.com/piwik/piwik/issues/374}
* @param $var The object to destroy.
* @api
*/
public static function destroy(&$var)
{
if (is_object($var) && method_exists($var, '__destruct')) {
$var->__destruct();
}
unset($var);
$var = null;
}
public static function printDebug($info = '')
{
if (isset($GLOBALS['PIWIK_TRACKER_DEBUG']) && $GLOBALS['PIWIK_TRACKER_DEBUG']) {
if (is_object($info)) {
$info = var_export($info, true);
}
Log::getInstance()->setLogLevel(Log::DEBUG);
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);
}
} else {
foreach (explode("\n", $info) as $line) {
Log::debug(htmlspecialchars($line, ENT_QUOTES));
}
}
}
}
}