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
@ -10,26 +10,28 @@
namespace Piwik;
use Exception;
use Piwik\Container\StaticContainer;
use Piwik\Intl\Data\Provider\DateTimeFormatProvider;
/**
* Utility class that wraps date/time related PHP functions. Using this class can
* be easier than using `date`, `time`, `date_default_timezone_set`, etc.
*
*
* ### Performance concerns
*
*
* The helper methods in this class are instance methods and thus `Date` instances
* need to be constructed before they can be used. The memory allocation can result
* in noticeable performance degradation if you construct thousands of Date instances,
* say, in a loop.
*
*
* ### Examples
*
*
* **Basic usage**
*
*
* $date = Date::factory('2007-07-24 14:04:24', 'EST');
* $date->addHour(5);
* echo $date->getLocalized("%longDay% the %day% of %longMonth% at %time%");
*
* echo $date->getLocalized("EEE, d. MMM y 'at' HH:mm:ss");
*
* @api
*/
class Date
@ -40,6 +42,36 @@ class Date
/** The default date time string format. */
const DATE_TIME_FORMAT = 'Y-m-d H:i:s';
const DATETIME_FORMAT_LONG = DateTimeFormatProvider::DATE_FORMAT_LONG;
const DATETIME_FORMAT_SHORT = DateTimeFormatProvider::DATETIME_FORMAT_SHORT;
const DATE_FORMAT_LONG = DateTimeFormatProvider::DATE_FORMAT_LONG;
const DATE_FORMAT_DAY_MONTH = DateTimeFormatProvider::DATE_FORMAT_DAY_MONTH;
const DATE_FORMAT_SHORT = DateTimeFormatProvider::DATE_FORMAT_SHORT;
const DATE_FORMAT_MONTH_SHORT = DateTimeFormatProvider::DATE_FORMAT_MONTH_SHORT;
const DATE_FORMAT_MONTH_LONG = DateTimeFormatProvider::DATE_FORMAT_MONTH_LONG;
const DATE_FORMAT_YEAR = DateTimeFormatProvider::DATE_FORMAT_YEAR;
const TIME_FORMAT = DateTimeFormatProvider::TIME_FORMAT;
/**
* Max days for months (non-leap-year). See {@link addPeriod()} implementation.
*
* @var int[]
*/
private static $maxDaysInMonth = array(
'1' => 31,
'2' => 28,
'3' => 31,
'4' => 30,
'5' => 31,
'6' => 30,
'7' => 31,
'8' => 31,
'9' => 30,
'10' => 31,
'11' => 30,
'12' => 31
);
/**
* The stored timestamp is always UTC based.
* The returned timestamp via getTimestamp() will have the conversion applied
@ -84,7 +116,6 @@ class Date
*/
public static function factory($dateString, $timezone = null)
{
$invalidDateException = new Exception(Piwik::translate('General_ExceptionInvalidDateFormat', array("YYYY-MM-DD, or 'today' or 'yesterday'", "strtotime", "http://php.net/strtotime")) . ": $dateString");
if ($dateString instanceof self) {
$dateString = $dateString->toString();
}
@ -105,7 +136,7 @@ class Date
($dateString = strtotime($dateString)) === false
)
) {
throw $invalidDateException;
throw self::getInvalidDateFormatException($dateString);
} else {
$date = new Date($dateString);
}
@ -113,7 +144,7 @@ class Date
// can't be doing web analytics before the 1st website
// Tue, 06 Aug 1991 00:00:00 GMT
if ($timestamp < 681436800) {
throw $invalidDateException;
throw self::getInvalidDateFormatException($dateString);
}
if (empty($timezone)) {
return $date;
@ -133,6 +164,19 @@ class Date
return $this->toString(self::DATE_TIME_FORMAT);
}
/**
* Returns the current hour in UTC timezone.
* @return string
* @throws Exception
*/
public function getHourUTC()
{
$dateTime = $this->getDatetime();
$hourInTz = Date::factory($dateTime, 'UTC')->toString('G');
return $hourInTz;
}
/**
* Returns the start of the day of the current timestamp in UTC. For example,
* if the current timestamp is `'2007-07-24 14:04:24'` in UTC, the result will
@ -164,7 +208,7 @@ class Date
/**
* Returns a new date object with the same timestamp as `$this` but with a new
* timezone.
*
*
* See {@link getTimestamp()} to see how the timezone is used.
*
* @param string $timezone eg, `'UTC'`, `'Europe/London'`, etc.
@ -222,6 +266,17 @@ class Date
return strtotime($datetime);
}
/**
* Returns the date in the "Y-m-d H:i:s" PHP format
*
* @param int $timestamp
* @return string
*/
public static function getDatetimeFromTimestamp($timestamp)
{
return date("Y-m-d H:i:s", $timestamp);
}
/**
* Returns the Unix timestamp of the date in UTC.
*
@ -253,15 +308,16 @@ class Date
// Unit tests pass (@see Date.test.php) but I'm pretty sure this is not the right way to do it
date_default_timezone_set($this->timezone);
$dtzone = timezone_open('UTC');
$time = date('r', $this->timestamp);
$dtime = date_create($time);
$time = date('r', $this->timestamp);
$dtime = date_create($time);
date_timezone_set($dtime, $dtzone);
$dateWithTimezone = date_format($dtime, 'r');
$dateWithTimezone = date_format($dtime, 'r');
$dateWithoutTimezone = substr($dateWithTimezone, 0, -6);
$timestamp = strtotime($dateWithoutTimezone);
$timestamp = strtotime($dateWithoutTimezone);
date_default_timezone_set('UTC');
return (int)$timestamp;
return (int) $timestamp;
}
/**
@ -375,7 +431,6 @@ class Date
return 0;
}
if ($currentYear < $toCompareYear) {
return -1;
}
return 1;
@ -383,7 +438,7 @@ class Date
/**
* Returns `true` if current date is today.
*
*
* @return bool
*/
public function isToday()
@ -560,9 +615,39 @@ class Date
/**
* Returns a localized date string using the given template.
* The template should contain tags that will be replaced with localized date strings.
*
* Allowed tags include:
*
*
* @param string $template eg. `"MMM y"`
* @return string eg. `"Aug 2009"`
*/
public function getLocalized($template)
{
$template = $this->replaceLegacyPlaceholders($template);
$dateTimeFormatProvider = StaticContainer::get('Piwik\Intl\Data\Provider\DateTimeFormatProvider');
$template = $dateTimeFormatProvider->getFormatPattern($template);
$tokens = self::parseFormat($template);
$out = '';
foreach ($tokens AS $token) {
if (is_array($token)) {
$out .= $this->formatToken(array_shift($token));
} else {
$out .= $token;
}
}
return $out;
}
/**
* Replaces legacy placeholders
*
* @deprecated should be removed in Piwik 3.0.0 or later
*
* - **%day%**: replaced with the day of the month without leading zeros, eg, **1** or **20**.
* - **%shortMonth%**: the short month in the current language, eg, **Jan**, **Feb**.
* - **%longMonth%**: the whole month name in the current language, eg, **January**, **February**.
@ -571,27 +656,183 @@ class Date
* - **%longYear%**: the four digit year, eg, **2007**, **2013**.
* - **%shortYear%**: the two digit year, eg, **07**, **13**.
* - **%time%**: the time of day, eg, **07:35:00**, or **15:45:00**.
*
* @param string $template eg. `"%shortMonth% %longYear%"`
* @return string eg. `"Aug 2009"`
*/
public function getLocalized($template)
protected function replaceLegacyPlaceholders($template)
{
if (strpos($template, '%') === false) {
return $template;
}
$mapping = array(
'%day%' => 'd',
'%shortMonth%' => 'MMM',
'%longMonth%' => 'MMMM',
'%shortDay%' => 'EEE',
'%longDay%' => 'EEEE',
'%longYear%' => 'y',
'%shortYear%' => 'yy',
'%time%' => 'HH:mm:ss'
);
return str_replace(array_keys($mapping), array_values($mapping), $template);
}
protected function formatToken($token)
{
$day = $this->toString('j');
$dayOfWeek = $this->toString('N');
$monthOfYear = $this->toString('n');
$patternToValue = array(
"%day%" => $day,
"%shortMonth%" => Piwik::translate('General_ShortMonth_' . $monthOfYear),
"%longMonth%" => Piwik::translate('General_LongMonth_' . $monthOfYear),
"%shortDay%" => Piwik::translate('General_ShortDay_' . $dayOfWeek),
"%longDay%" => Piwik::translate('General_LongDay_' . $dayOfWeek),
"%longYear%" => $this->toString('Y'),
"%shortYear%" => $this->toString('y'),
"%time%" => $this->toString('H:i:s')
);
$out = str_replace(array_keys($patternToValue), array_values($patternToValue), $template);
return $out;
$translator = StaticContainer::get('Piwik\Translation\Translator');
switch ($token) {
// year
case "yyyy":
case "y":
return $this->toString('Y');
case "yy":
return $this->toString('y');
// month
case "MMMM":
return $translator->translate('Intl_Month_Long_' . $monthOfYear);
case "MMM":
return $translator->translate('Intl_Month_Short_' . $monthOfYear);
case "MM":
return $this->toString('n');
case "M":
return $this->toString('m');
case "LLLL":
return $translator->translate('Intl_Month_Long_StandAlone_' . $monthOfYear);
case "LLL":
return $translator->translate('Intl_Month_Short_StandAlone_' . $monthOfYear);
case "LL":
return $this->toString('n');
case "L":
return $this->toString('m');
// day
case "dd":
return $this->toString('d');
case "d":
return $this->toString('j');
case "EEEE":
return $translator->translate('Intl_Day_Long_' . $dayOfWeek);
case "EEE":
case "EE":
case "E":
return $translator->translate('Intl_Day_Short_' . $dayOfWeek);
case "CCCC":
return $translator->translate('Intl_Day_Long_StandAlone_' . $dayOfWeek);
case "CCC":
case "CC":
case "C":
return $translator->translate('Intl_Day_Short_StandAlone_' . $dayOfWeek);
case "D":
return 1 + (int)$this->toString('z'); // 1 - 366
case "F":
return (int)(((int)$this->toString('j') + 6) / 7);
// week in month
case "w":
$weekDay = date('N', mktime(0, 0, 0, $this->toString('m'), 1, $this->toString('y')));
return floor(($weekDay + (int)$this->toString('m') - 2) / 7) + 1;
// week in year
case "W":
return $this->toString('N');
// hour
case "HH":
return $this->toString('H');
case "H":
return $this->toString('G');
case "hh":
return $this->toString('h');
case "h":
return $this->toString('g');
// minute
case "mm":
case "m":
return $this->toString('i');
// second
case "ss":
case "s":
return $this->toString('s');
// am / pm
case "a":
return $this->toString('a') == 'am' ? $translator->translate('Intl_Time_AM') : $translator->translate('Intl_Time_PM');
// currently not implemented:
case "G":
case "GG":
case "GGG":
case "GGGG":
case "GGGGG":
return ''; // era
case "z":
case "Z":
case "v":
return ''; // time zone
}
return '';
}
protected static $tokens = array(
'G', 'y', 'M', 'L', 'd', 'h', 'H', 'm', 's', 'E', 'c', 'e', 'D', 'F', 'w', 'W', 'a', 'z', 'Z', 'v',
);
/**
* Parses the datetime format pattern and returns a tokenized result array
*
* Examples:
* Input Output
* 'dd.mm.yyyy' array(array('dd'), '.', array('mm'), '.', array('yyyy'))
* 'y?M?d?EEEE ah:mm:ss' array(array('y'), '?', array('M'), '?', array('d'), '?', array('EEEE'), ' ', array('a'), array('h'), ':', array('mm'), ':', array('ss'))
*
* @param string $pattern the pattern to be parsed
* @return array tokenized parsing result
*/
protected static function parseFormat($pattern)
{
static $formats = array(); // cache
if (isset($formats[$pattern])) {
return $formats[$pattern];
}
$tokens = array();
$n = strlen($pattern);
$isLiteral = false;
$literal = '';
for ($i = 0; $i < $n; ++$i) {
$c = $pattern[$i];
if ($c === "'") {
if ($i < $n - 1 && $pattern[$i + 1] === "'") {
$tokens[] = "'";
$i++;
} elseif ($isLiteral) {
$tokens[] = $literal;
$literal = '';
$isLiteral = false;
} else {
$isLiteral = true;
$literal = '';
}
} elseif ($isLiteral) {
$literal .= $c;
} else {
for ($j = $i + 1; $j < $n; ++$j) {
if ($pattern[$j] !== $c) {
break;
}
}
$p = str_repeat($c, $j - $i);
if (in_array($c, self::$tokens)) {
$tokens[] = array($p);
} else {
$tokens[] = $p;
}
$i = $j - 1;
}
}
if ($literal !== '') {
$tokens[] = $literal;
}
return $formats[$pattern] = $tokens;
}
/**
@ -664,6 +905,17 @@ class Date
return $this->addHour(-$n);
}
/**
* Subtracts `$n` seconds from `$this` date and returns the result in a new Date.
*
* @param int $n Number of seconds to subtract. Can be less than 0.
* @return \Piwik\Date
*/
public function subSeconds($n)
{
return new Date($this->timestamp - $n, $this->timezone);
}
/**
* Adds a period to `$this` date and returns the result in a new Date instance.
*
@ -673,14 +925,41 @@ class Date
*/
public function addPeriod($n, $period)
{
if ($n < 0) {
$ts = strtotime("$n $period", $this->timestamp);
if (strtolower($period) == 'month') { // TODO: comments
$dateInfo = getdate($this->timestamp);
$ts = mktime(
$dateInfo['hours'],
$dateInfo['minutes'],
$dateInfo['seconds'],
$dateInfo['mon'] + (int)$n,
1,
$dateInfo['year']
);
$daysToAdd = min($dateInfo['mday'], self::getMaxDaysInMonth($ts)) - 1;
$ts += self::NUM_SECONDS_IN_DAY * $daysToAdd;
} else {
$ts = strtotime("+$n $period", $this->timestamp);
$time = $n < 0 ? "$n $period" : "+$n $period";
$ts = strtotime($time, $this->timestamp);
}
return new Date($ts, $this->timezone);
}
private static function getMaxDaysInMonth($timestamp)
{
$month = (int)date('m', $timestamp);
if (date('L', $timestamp) == 1
&& $month == 2
) {
return 29;
} else {
return self::$maxDaysInMonth[$month];
}
}
/**
* Subtracts a period from `$this` date and returns the result in a new Date instance.
*
@ -703,4 +982,10 @@ class Date
{
return $secs / self::NUM_SECONDS_IN_DAY;
}
private static function getInvalidDateFormatException($dateString)
{
$message = Piwik::translate('General_ExceptionInvalidDateFormat', array("YYYY-MM-DD, or 'today' or 'yesterday'", "strtotime", "http://php.net/strtotime"));
return new Exception($message . ": $dateString");
}
}