summaryrefslogtreecommitdiff
path: root/framework/Util
diff options
context:
space:
mode:
Diffstat (limited to 'framework/Util')
-rw-r--r--framework/Util/TDataFieldAccessor.php164
-rw-r--r--framework/Util/TDateTimeStamp.php1406
-rw-r--r--framework/Util/TLogRouter.php2414
-rw-r--r--framework/Util/TLogger.php472
-rw-r--r--framework/Util/TParameterModule.php346
-rw-r--r--framework/Util/TSimpleDateFormatter.php752
-rw-r--r--framework/Util/TVarDumper.php254
7 files changed, 2904 insertions, 2904 deletions
diff --git a/framework/Util/TDataFieldAccessor.php b/framework/Util/TDataFieldAccessor.php
index e0181033..b96cf0c9 100644
--- a/framework/Util/TDataFieldAccessor.php
+++ b/framework/Util/TDataFieldAccessor.php
@@ -1,82 +1,82 @@
-<?php
-/**
- * TDataFieldAccessor class file
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright &copy; 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Util
- */
-
-/**
- * TDataFieldAccessor class
- *
- * TDataFieldAccessor is a utility class that provides access to a field of some data.
- * The accessor attempts to obtain the field value in the following order:
- * - If the data is an array, then the field is treated as an array index
- * and the corresponding element value is returned;
- * - If the data is a TMap or TList object, then the field is treated as a key
- * into the map or list, and the corresponding value is returned.
- * - If the data is an object, the field is treated as a property or sub-property
- * defined with getter methods. For example, if the object has a method called
- * getMyValue(), then field 'MyValue' will retrieve the result of this method call.
- * If getMyValue() returns an object which contains a method getMySubValue(),
- * then field 'MyValue.MySubValue' will return that method call result.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @version $Id$
- * @package System.Util
- * @since 3.0
- */
-class TDataFieldAccessor
-{
- /**
- * Evaluates the data value at the specified field.
- * - If the data is an array, then the field is treated as an array index
- * and the corresponding element value is returned;
- * - If the data is a TMap or TList object, then the field is treated as a key
- * into the map or list, and the corresponding value is returned.
- * - If the data is an object, the field is treated as a property or sub-property
- * defined with getter methods. For example, if the object has a method called
- * getMyValue(), then field 'MyValue' will retrieve the result of this method call.
- * If getMyValue() returns an object which contains a method getMySubValue(),
- * then field 'MyValue.MySubValue' will return that method call result.
- * @param mixed data containing the field value, can be an array, TMap, TList or object.
- * @param mixed field value
- * @return mixed value at the specified field
- * @throws TInvalidDataValueException if field or data is invalid
- */
- public static function getDataFieldValue($data,$field)
- {
- try
- {
- if(is_array($data) || ($data instanceof ArrayAccess))
- return $data[$field];
- else if(is_object($data))
- {
- if(strpos($field,'.')===false) // simple field
- {
- if(method_exists($data, 'get'.$field))
- return call_user_func(array($data,'get'.$field));
- else
- return $data->{$field};
- }
- else // field in the format of xxx.yyy.zzz
- {
- $object=$data;
- foreach(explode('.',$field) as $f)
- $object = TDataFieldAccessor::getDataFieldValue($object, $f);
- return $object;
- }
- }
- }
- catch(Exception $e)
- {
- throw new TInvalidDataValueException('datafieldaccessor_datafield_invalid',$field,$e->getMessage());
- }
- throw new TInvalidDataValueException('datafieldaccessor_data_invalid',$field);
- }
-}
-
+<?php
+/**
+ * TDataFieldAccessor class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Util
+ */
+
+/**
+ * TDataFieldAccessor class
+ *
+ * TDataFieldAccessor is a utility class that provides access to a field of some data.
+ * The accessor attempts to obtain the field value in the following order:
+ * - If the data is an array, then the field is treated as an array index
+ * and the corresponding element value is returned;
+ * - If the data is a TMap or TList object, then the field is treated as a key
+ * into the map or list, and the corresponding value is returned.
+ * - If the data is an object, the field is treated as a property or sub-property
+ * defined with getter methods. For example, if the object has a method called
+ * getMyValue(), then field 'MyValue' will retrieve the result of this method call.
+ * If getMyValue() returns an object which contains a method getMySubValue(),
+ * then field 'MyValue.MySubValue' will return that method call result.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id$
+ * @package System.Util
+ * @since 3.0
+ */
+class TDataFieldAccessor
+{
+ /**
+ * Evaluates the data value at the specified field.
+ * - If the data is an array, then the field is treated as an array index
+ * and the corresponding element value is returned;
+ * - If the data is a TMap or TList object, then the field is treated as a key
+ * into the map or list, and the corresponding value is returned.
+ * - If the data is an object, the field is treated as a property or sub-property
+ * defined with getter methods. For example, if the object has a method called
+ * getMyValue(), then field 'MyValue' will retrieve the result of this method call.
+ * If getMyValue() returns an object which contains a method getMySubValue(),
+ * then field 'MyValue.MySubValue' will return that method call result.
+ * @param mixed data containing the field value, can be an array, TMap, TList or object.
+ * @param mixed field value
+ * @return mixed value at the specified field
+ * @throws TInvalidDataValueException if field or data is invalid
+ */
+ public static function getDataFieldValue($data,$field)
+ {
+ try
+ {
+ if(is_array($data) || ($data instanceof ArrayAccess))
+ return $data[$field];
+ else if(is_object($data))
+ {
+ if(strpos($field,'.')===false) // simple field
+ {
+ if(method_exists($data, 'get'.$field))
+ return call_user_func(array($data,'get'.$field));
+ else
+ return $data->{$field};
+ }
+ else // field in the format of xxx.yyy.zzz
+ {
+ $object=$data;
+ foreach(explode('.',$field) as $f)
+ $object = TDataFieldAccessor::getDataFieldValue($object, $f);
+ return $object;
+ }
+ }
+ }
+ catch(Exception $e)
+ {
+ throw new TInvalidDataValueException('datafieldaccessor_datafield_invalid',$field,$e->getMessage());
+ }
+ throw new TInvalidDataValueException('datafieldaccessor_data_invalid',$field);
+ }
+}
+
diff --git a/framework/Util/TDateTimeStamp.php b/framework/Util/TDateTimeStamp.php
index 50c669d7..8232d924 100644
--- a/framework/Util/TDateTimeStamp.php
+++ b/framework/Util/TDateTimeStamp.php
@@ -1,703 +1,703 @@
-<?php
-/**
- * TDateTimeStamp class file.
- *
- * COPYRIGHT
- * (c) 2003-2005 John Lim and released under BSD-style license except for code by
- * jackbbs, which includes getTimeStamp, getGMTDiff, isLeapYear
- * and originally found at http://www.php.net/manual/en/function.mktime.php
- *
- * @author Wei Zhuo <weizhuo[at]gamil[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright &copy; 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Util
- */
-
-/*
- This code was originally for windows. But apparently this problem happens
- also with Linux, RH 7.3 and later!
-
- glibc-2.2.5-34 and greater has been changed to return -1 for dates <
- 1970. This used to work. The problem exists with RedHat 7.3 and 8.0
- echo (mktime(0, 0, 0, 1, 1, 1960)); // prints -1
-
- References:
- http://bugs.php.net/bug.php?id=20048&edit=2
- http://lists.debian.org/debian-glibc/2002/debian-glibc-200205/msg00010.html
-*/
-
-if (!defined('ADODB_ALLOW_NEGATIVE_TS')
- && !defined('ADODB_NO_NEGATIVE_TS')) define('ADODB_NO_NEGATIVE_TS',1);
-
-/**
- * TDateTimeStamp Class
- *
- * TDateTimeStamp Class is adpated from the ADOdb Date Library,
- * part of the ADOdb abstraction library
- * Download: http://phplens.com/phpeverywhere/
- *
- * http://phplens.com/phpeverywhere/adodb_date_library
- *
- * PHP native date functions use integer timestamps for computations.
- * Because of this, dates are restricted to the years 1901-2038 on Unix
- * and 1970-2038 on Windows due to integer overflow for dates beyond
- * those years. This library overcomes these limitations by replacing the
- * native function's signed integers (normally 32-bits) with PHP floating
- * point numbers (normally 64-bits).
- *
- * Dates from 100 A.D. to 3000 A.D. and later have been tested. The minimum
- * is 100 A.D. as <100 will invoke the 2 => 4 digit year conversion.
- * The maximum is billions of years in the future, but this is a theoretical
- * limit as the computation of that year would take too long with the
- * current implementation of {@link getTimeStamp}.
- *
- * PERFORMANCE
- * For high speed, this library uses the native date functions where
- * possible, and only switches to PHP code when the dates fall outside
- * the 32-bit signed integer range.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @version $Id$
- * @package System.Util
- * @since 3.0.4
- */
-class TDateTimeStamp
-{
- protected static $_month_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31);
- protected static $_month_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31);
-
- /**
- * Gets day of week, 0 = Sunday,... 6=Saturday.
- * Algorithm from PEAR::Date_Calc
- */
- public function getDayofWeek($year, $month, $day)
- {
- /*
- Pope Gregory removed 10 days - October 5 to October 14 - from the year 1582 and
- proclaimed that from that time onwards 3 days would be dropped from the calendar
- every 400 years.
-
- Thursday, October 4, 1582 (Julian) was followed immediately by Friday, October 15, 1582 (Gregorian).
- */
- if ($year <= 1582)
- {
- if ($year < 1582 ||
- ($year == 1582 && ($month < 10 || ($month == 10 && $day < 15))))
- {
- $greg_correction = 3;
- }
- else
- {
- $greg_correction = 0;
- }
- }
- else
- {
- $greg_correction = 0;
- }
-
- if($month > 2)
- $month -= 2;
- else
- {
- $month += 10;
- $year--;
- }
-
- $day = floor((13 * $month - 1) / 5) +
- $day + ($year % 100) +
- floor(($year % 100) / 4) +
- floor(($year / 100) / 4) - 2 *
- floor($year / 100) + 77 + $greg_correction;
-
- return $day - 7 * floor($day / 7);
- }
-
- /**
- * Checks for leap year, returns true if it is. No 2-digit year check. Also
- * handles julian calendar correctly.
- * @param float year to check
- * @return boolean true if is leap year
- */
- public function isLeapYear($year)
- {
- $year = $this->digitCheck($year);
- if ($year % 4 != 0)
- return false;
-
- if ($year % 400 == 0)
- return true;
- // if gregorian calendar (>1582), century not-divisible by 400 is not leap
- else if ($year > 1582 && $year % 100 == 0 )
- return false;
- return true;
- }
-
- /**
- * Fix 2-digit years. Works for any century.
- * Assumes that if 2-digit is more than 30 years in future, then previous century.
- * @return integer change two digit year into multiple digits
- */
- protected function digitCheck($y)
- {
- if ($y < 100){
- $yr = (integer) date("Y");
- $century = (integer) ($yr /100);
-
- if ($yr%100 > 50) {
- $c1 = $century + 1;
- $c0 = $century;
- } else {
- $c1 = $century;
- $c0 = $century - 1;
- }
- $c1 *= 100;
- // if 2-digit year is less than 30 years in future, set it to this century
- // otherwise if more than 30 years in future, then we set 2-digit year to the prev century.
- if (($y + $c1) < $yr+30) $y = $y + $c1;
- else $y = $y + $c0*100;
- }
- return $y;
- }
-
- public function get4DigitYear($y)
- {
- return $this->digitCheck($y);
- }
-
- /**
- * @return integer get local time zone offset from GMT
- */
- public function getGMTDiff()
- {
- static $TZ;
- if (isset($TZ)) return $TZ;
-
- $TZ = mktime(0,0,0,1,2,1970) - gmmktime(0,0,0,1,2,1970);
- return $TZ;
- }
-
- /**
- * @return array an array with date info.
- */
- function getDate($d=false,$fast=false)
- {
- if ($d === false) return getdate();
- // check if number in 32-bit signed range
- if ((abs($d) <= 0x7FFFFFFF))
- {
- if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) // if windows, must be +ve integer
- return @getdate($d);
- }
- return $this->_getDateInternal($d);
- }
-
-
-
- /**
- * Low-level function that returns the getdate() array. We have a special
- * $fast flag, which if set to true, will return fewer array values,
- * and is much faster as it does not calculate dow, etc.
- * @param float original date
- * @param boolean false to compute the day of the week, default is true
- * @param boolean true to calculate the GMT dates
- * @return array an array with date info.
- */
- protected function _getDateInternal($origd=false,$fast=true,$is_gmt=false)
- {
- static $YRS;
-
- $d = $origd - ($is_gmt ? 0 : $this->getGMTDiff());
-
- $_day_power = 86400;
- $_hour_power = 3600;
- $_min_power = 60;
-
- if ($d < -12219321600)
- $d -= 86400*10; // if 15 Oct 1582 or earlier, gregorian correction
-
- $_month_table_normal =& self::$_month_normal;
- $_month_table_leaf = & self::$_month_leaf;
-
- $d366 = $_day_power * 366;
- $d365 = $_day_power * 365;
-
- if ($d < 0)
- {
- if (empty($YRS))
- $YRS = array(
- 1970 => 0,
- 1960 => -315619200,
- 1950 => -631152000,
- 1940 => -946771200,
- 1930 => -1262304000,
- 1920 => -1577923200,
- 1910 => -1893456000,
- 1900 => -2208988800,
- 1890 => -2524521600,
- 1880 => -2840140800,
- 1870 => -3155673600,
- 1860 => -3471292800,
- 1850 => -3786825600,
- 1840 => -4102444800,
- 1830 => -4417977600,
- 1820 => -4733596800,
- 1810 => -5049129600,
- 1800 => -5364662400,
- 1790 => -5680195200,
- 1780 => -5995814400,
- 1770 => -6311347200,
- 1760 => -6626966400,
- 1750 => -6942499200,
- 1740 => -7258118400,
- 1730 => -7573651200,
- 1720 => -7889270400,
- 1710 => -8204803200,
- 1700 => -8520336000,
- 1690 => -8835868800,
- 1680 => -9151488000,
- 1670 => -9467020800,
- 1660 => -9782640000,
- 1650 => -10098172800,
- 1640 => -10413792000,
- 1630 => -10729324800,
- 1620 => -11044944000,
- 1610 => -11360476800,
- 1600 => -11676096000);
-
- if ($is_gmt)
- $origd = $d;
- // The valid range of a 32bit signed timestamp is typically from
- // Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT
- //
-
- # old algorithm iterates through all years. new algorithm does it in
- # 10 year blocks
-
- /*
- # old algo
- for ($a = 1970 ; --$a >= 0;) {
- $lastd = $d;
-
- if ($leaf = _adodb_is_leap_year($a)) $d += $d366;
- else $d += $d365;
-
- if ($d >= 0) {
- $year = $a;
- break;
- }
- }
- */
-
- $lastsecs = 0;
- $lastyear = 1970;
- foreach($YRS as $year => $secs)
- {
- if ($d >= $secs)
- {
- $a = $lastyear;
- break;
- }
- $lastsecs = $secs;
- $lastyear = $year;
- }
-
- $d -= $lastsecs;
- if (!isset($a)) $a = $lastyear;
-
- //echo ' yr=',$a,' ', $d,'.';
-
- for (; --$a >= 0;)
- {
- $lastd = $d;
-
- if ($leaf = $this->isLeapYear($a))
- $d += $d366;
- else
- $d += $d365;
-
- if ($d >= 0)
- {
- $year = $a;
- break;
- }
- }
- /**/
-
- $secsInYear = 86400 * ($leaf ? 366 : 365) + $lastd;
-
- $d = $lastd;
- $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal;
- for ($a = 13 ; --$a > 0;)
- {
- $lastd = $d;
- $d += $mtab[$a] * $_day_power;
- if ($d >= 0)
- {
- $month = $a;
- $ndays = $mtab[$a];
- break;
- }
- }
-
- $d = $lastd;
- $day = $ndays + ceil(($d+1) / ($_day_power));
-
- $d += ($ndays - $day+1)* $_day_power;
- $hour = floor($d/$_hour_power);
-
- } else {
- for ($a = 1970 ;; $a++)
- {
- $lastd = $d;
-
- if ($leaf = $this->isLeapYear($a)) $d -= $d366;
- else $d -= $d365;
- if ($d < 0)
- {
- $year = $a;
- break;
- }
- }
- $secsInYear = $lastd;
- $d = $lastd;
- $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal;
- for ($a = 1 ; $a <= 12; $a++)
- {
- $lastd = $d;
- $d -= $mtab[$a] * $_day_power;
- if ($d < 0)
- {
- $month = $a;
- $ndays = $mtab[$a];
- break;
- }
- }
- $d = $lastd;
- $day = ceil(($d+1) / $_day_power);
- $d = $d - ($day-1) * $_day_power;
- $hour = floor($d /$_hour_power);
- }
-
- $d -= $hour * $_hour_power;
- $min = floor($d/$_min_power);
- $secs = $d - $min * $_min_power;
- if ($fast)
- {
- return array(
- 'seconds' => $secs,
- 'minutes' => $min,
- 'hours' => $hour,
- 'mday' => $day,
- 'mon' => $month,
- 'year' => $year,
- 'yday' => floor($secsInYear/$_day_power),
- 'leap' => $leaf,
- 'ndays' => $ndays
- );
- }
-
-
- $dow = $this->getDayofWeek($year,$month,$day);
-
- return array(
- 'seconds' => $secs,
- 'minutes' => $min,
- 'hours' => $hour,
- 'mday' => $day,
- 'wday' => $dow,
- 'mon' => $month,
- 'year' => $year,
- 'yday' => floor($secsInYear/$_day_power),
- 'weekday' => gmdate('l',$_day_power*(3+$dow)),
- 'month' => gmdate('F',mktime(0,0,0,$month,2,1971)),
- 0 => $origd
- );
- }
-
- /**
- * @return boolean true if valid date, semantic check only.
- */
- public function isValidDate($y,$m,$d)
- {
- if ($this->isLeapYear($y))
- $marr =& self::$_month_leaf;
- else
- $marr =& self::$_month_normal;
-
- if ($m > 12 || $m < 1) return false;
-
- if ($d > 31 || $d < 1) return false;
-
- if ($marr[$m] < $d) return false;
-
- if ($y < 1000 && $y > 3000) return false;
-
- return true;
- }
-
- /**
- * @return string formatted date based on timestamp $d
- */
- function formatDate($fmt,$d=false,$is_gmt=false)
- {
- if ($d === false)
- return ($is_gmt)? @gmdate($fmt): @date($fmt);
-
- // check if number in 32-bit signed range
- if ((abs($d) <= 0x7FFFFFFF))
- {
- // if windows, must be +ve integer
- if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0)
- return ($is_gmt)? @gmdate($fmt,$d): @date($fmt,$d);
- }
-
- $_day_power = 86400;
-
- $arr = $this->getDate($d,true,$is_gmt);
-
- $year = $arr['year'];
- $month = $arr['mon'];
- $day = $arr['mday'];
- $hour = $arr['hours'];
- $min = $arr['minutes'];
- $secs = $arr['seconds'];
-
- $max = strlen($fmt);
- $dates = '';
-
- $isphp5 = PHP_VERSION >= 5;
-
- /*
- at this point, we have the following integer vars to manipulate:
- $year, $month, $day, $hour, $min, $secs
- */
- for ($i=0; $i < $max; $i++)
- {
- switch($fmt[$i])
- {
- case 'T': $dates .= date('T');break;
- // YEAR
- case 'L': $dates .= $arr['leap'] ? '1' : '0'; break;
- case 'r': // Thu, 21 Dec 2000 16:01:07 +0200
-
- // 4.3.11 uses '04 Jun 2004'
- // 4.3.8 uses ' 4 Jun 2004'
- $dates .= gmdate('D',$_day_power*(3+$this->getDayOfWeek($year,$month,$day))).', '
- . ($day<10?'0'.$day:$day) . ' '.date('M',mktime(0,0,0,$month,2,1971)).' '.$year.' ';
-
- if ($hour < 10) $dates .= '0'.$hour; else $dates .= $hour;
-
- if ($min < 10) $dates .= ':0'.$min; else $dates .= ':'.$min;
-
- if ($secs < 10) $dates .= ':0'.$secs; else $dates .= ':'.$secs;
-
- $gmt = $this->getGMTDiff();
- if ($isphp5)
- $dates .= sprintf(' %s%04d',($gmt<=0)?'+':'-',abs($gmt)/36);
- else
- $dates .= sprintf(' %s%04d',($gmt<0)?'+':'-',abs($gmt)/36);
- break;
-
- case 'Y': $dates .= $year; break;
- case 'y': $dates .= substr($year,strlen($year)-2,2); break;
- // MONTH
- case 'm': if ($month<10) $dates .= '0'.$month; else $dates .= $month; break;
- case 'Q': $dates .= ($month+3)>>2; break;
- case 'n': $dates .= $month; break;
- case 'M': $dates .= date('M',mktime(0,0,0,$month,2,1971)); break;
- case 'F': $dates .= date('F',mktime(0,0,0,$month,2,1971)); break;
- // DAY
- case 't': $dates .= $arr['ndays']; break;
- case 'z': $dates .= $arr['yday']; break;
- case 'w': $dates .= $this->getDayOfWeek($year,$month,$day); break;
- case 'l': $dates .= gmdate('l',$_day_power*(3+$this->getDayOfWeek($year,$month,$day))); break;
- case 'D': $dates .= gmdate('D',$_day_power*(3+$this->getDayOfWeek($year,$month,$day))); break;
- case 'j': $dates .= $day; break;
- case 'd': if ($day<10) $dates .= '0'.$day; else $dates .= $day; break;
- case 'S':
- $d10 = $day % 10;
- if ($d10 == 1) $dates .= 'st';
- else if ($d10 == 2 && $day != 12) $dates .= 'nd';
- else if ($d10 == 3) $dates .= 'rd';
- else $dates .= 'th';
- break;
-
- // HOUR
- case 'Z':
- $dates .= ($is_gmt) ? 0 : -$this->getGMTDiff(); break;
- case 'O':
- $gmt = ($is_gmt) ? 0 : $this->getGMTDiff();
-
- if ($isphp5)
- $dates .= sprintf('%s%04d',($gmt<=0)?'+':'-',abs($gmt)/36);
- else
- $dates .= sprintf('%s%04d',($gmt<0)?'+':'-',abs($gmt)/36);
- break;
-
- case 'H':
- if ($hour < 10) $dates .= '0'.$hour;
- else $dates .= $hour;
- break;
- case 'h':
- if ($hour > 12) $hh = $hour - 12;
- else {
- if ($hour == 0) $hh = '12';
- else $hh = $hour;
- }
-
- if ($hh < 10) $dates .= '0'.$hh;
- else $dates .= $hh;
- break;
-
- case 'G':
- $dates .= $hour;
- break;
-
- case 'g':
- if ($hour > 12) $hh = $hour - 12;
- else {
- if ($hour == 0) $hh = '12';
- else $hh = $hour;
- }
- $dates .= $hh;
- break;
- // MINUTES
- case 'i': if ($min < 10) $dates .= '0'.$min; else $dates .= $min; break;
- // SECONDS
- case 'U': $dates .= $d; break;
- case 's': if ($secs < 10) $dates .= '0'.$secs; else $dates .= $secs; break;
- // AM/PM
- // Note 00:00 to 11:59 is AM, while 12:00 to 23:59 is PM
- case 'a':
- if ($hour>=12) $dates .= 'pm';
- else $dates .= 'am';
- break;
- case 'A':
- if ($hour>=12) $dates .= 'PM';
- else $dates .= 'AM';
- break;
- default:
- $dates .= $fmt[$i]; break;
- // ESCAPE
- case "\\":
- $i++;
- if ($i < $max) $dates .= $fmt[$i];
- break;
- }
- }
- return $dates;
- }
-
- /**
- * Not a very fast algorithm - O(n) operation. Could be optimized to O(1).
- * @return integer|float a timestamp given a local time. Originally by jackbbs.
- */
- function getTimeStamp($hr,$min,$sec,$mon=false,$day=false,$year=false,$is_gmt=false)
- {
-
- if ($mon === false)
- return $is_gmt? @gmmktime($hr,$min,$sec): @mktime($hr,$min,$sec);
-
- // for windows, we don't check 1970 because with timezone differences,
- // 1 Jan 1970 could generate negative timestamp, which is illegal
- if (1971 <= $year && $year < 2038 || !defined('ADODB_NO_NEGATIVE_TS')
- && (1901 < $year && $year < 2038))
- {
- return $is_gmt ? @gmmktime($hr,$min,$sec,$mon,$day,$year)
- : @mktime($hr,$min,$sec,$mon,$day,$year);
- }
-
- $gmt_different = ($is_gmt) ? 0 : $this->getGMTDiff();
-
- /*
- # disabled because some people place large values in $sec.
- # however we need it for $mon because we use an array...
- $hr = intval($hr);
- $min = intval($min);
- $sec = intval($sec);
- */
- $mon = (int)$mon;
- $day = (int)$day;
- $year = (int)$year;
-
-
- $year = $this->digitCheck($year);
-
- if ($mon > 12)
- {
- $y = floor($mon / 12);
- $year += $y;
- $mon -= $y*12;
- }
- else if ($mon < 1)
- {
- $y = ceil((1-$mon) / 12);
- $year -= $y;
- $mon += $y*12;
- }
-
- $_day_power = 86400;
- $_hour_power = 3600;
- $_min_power = 60;
-
- $_month_table_normal = & self::$_month_normal;
- $_month_table_leaf = & self::$_month_leaf;
-
- $_total_date = 0;
- if ($year >= 1970)
- {
- for ($a = 1970 ; $a <= $year; $a++)
- {
- $leaf = $this->isLeapYear($a);
- if ($leaf == true) {
- $loop_table = $_month_table_leaf;
- $_add_date = 366;
- } else {
- $loop_table = $_month_table_normal;
- $_add_date = 365;
- }
- if ($a < $year) {
- $_total_date += $_add_date;
- } else {
- for($b=1;$b<$mon;$b++) {
- $_total_date += $loop_table[$b];
- }
- }
- }
- $_total_date +=$day-1;
- $ret = $_total_date * $_day_power + $hr * $_hour_power + $min * $_min_power + $sec + $gmt_different;
-
- } else {
- for ($a = 1969 ; $a >= $year; $a--) {
- $leaf = $this->isLeapYear($a);
- if ($leaf == true) {
- $loop_table = $_month_table_leaf;
- $_add_date = 366;
- } else {
- $loop_table = $_month_table_normal;
- $_add_date = 365;
- }
- if ($a > $year) { $_total_date += $_add_date;
- } else {
- for($b=12;$b>$mon;$b--) {
- $_total_date += $loop_table[$b];
- }
- }
- }
- $_total_date += $loop_table[$mon] - $day;
-
- $_day_time = $hr * $_hour_power + $min * $_min_power + $sec;
- $_day_time = $_day_power - $_day_time;
- $ret = -( $_total_date * $_day_power + $_day_time - $gmt_different);
- if ($ret < -12220185600) $ret += 10*86400; // if earlier than 5 Oct 1582 - gregorian correction
- else if ($ret < -12219321600) $ret = -12219321600; // if in limbo, reset to 15 Oct 1582.
- }
- //print " dmy=$day/$mon/$year $hr:$min:$sec => " .$ret;
- return $ret;
- }
-}
-
-?>
+<?php
+/**
+ * TDateTimeStamp class file.
+ *
+ * COPYRIGHT
+ * (c) 2003-2005 John Lim and released under BSD-style license except for code by
+ * jackbbs, which includes getTimeStamp, getGMTDiff, isLeapYear
+ * and originally found at http://www.php.net/manual/en/function.mktime.php
+ *
+ * @author Wei Zhuo <weizhuo[at]gamil[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Util
+ */
+
+/*
+ This code was originally for windows. But apparently this problem happens
+ also with Linux, RH 7.3 and later!
+
+ glibc-2.2.5-34 and greater has been changed to return -1 for dates <
+ 1970. This used to work. The problem exists with RedHat 7.3 and 8.0
+ echo (mktime(0, 0, 0, 1, 1, 1960)); // prints -1
+
+ References:
+ http://bugs.php.net/bug.php?id=20048&edit=2
+ http://lists.debian.org/debian-glibc/2002/debian-glibc-200205/msg00010.html
+*/
+
+if (!defined('ADODB_ALLOW_NEGATIVE_TS')
+ && !defined('ADODB_NO_NEGATIVE_TS')) define('ADODB_NO_NEGATIVE_TS',1);
+
+/**
+ * TDateTimeStamp Class
+ *
+ * TDateTimeStamp Class is adpated from the ADOdb Date Library,
+ * part of the ADOdb abstraction library
+ * Download: http://phplens.com/phpeverywhere/
+ *
+ * http://phplens.com/phpeverywhere/adodb_date_library
+ *
+ * PHP native date functions use integer timestamps for computations.
+ * Because of this, dates are restricted to the years 1901-2038 on Unix
+ * and 1970-2038 on Windows due to integer overflow for dates beyond
+ * those years. This library overcomes these limitations by replacing the
+ * native function's signed integers (normally 32-bits) with PHP floating
+ * point numbers (normally 64-bits).
+ *
+ * Dates from 100 A.D. to 3000 A.D. and later have been tested. The minimum
+ * is 100 A.D. as <100 will invoke the 2 => 4 digit year conversion.
+ * The maximum is billions of years in the future, but this is a theoretical
+ * limit as the computation of that year would take too long with the
+ * current implementation of {@link getTimeStamp}.
+ *
+ * PERFORMANCE
+ * For high speed, this library uses the native date functions where
+ * possible, and only switches to PHP code when the dates fall outside
+ * the 32-bit signed integer range.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Util
+ * @since 3.0.4
+ */
+class TDateTimeStamp
+{
+ protected static $_month_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31);
+ protected static $_month_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31);
+
+ /**
+ * Gets day of week, 0 = Sunday,... 6=Saturday.
+ * Algorithm from PEAR::Date_Calc
+ */
+ public function getDayofWeek($year, $month, $day)
+ {
+ /*
+ Pope Gregory removed 10 days - October 5 to October 14 - from the year 1582 and
+ proclaimed that from that time onwards 3 days would be dropped from the calendar
+ every 400 years.
+
+ Thursday, October 4, 1582 (Julian) was followed immediately by Friday, October 15, 1582 (Gregorian).
+ */
+ if ($year <= 1582)
+ {
+ if ($year < 1582 ||
+ ($year == 1582 && ($month < 10 || ($month == 10 && $day < 15))))
+ {
+ $greg_correction = 3;
+ }
+ else
+ {
+ $greg_correction = 0;
+ }
+ }
+ else
+ {
+ $greg_correction = 0;
+ }
+
+ if($month > 2)
+ $month -= 2;
+ else
+ {
+ $month += 10;
+ $year--;
+ }
+
+ $day = floor((13 * $month - 1) / 5) +
+ $day + ($year % 100) +
+ floor(($year % 100) / 4) +
+ floor(($year / 100) / 4) - 2 *
+ floor($year / 100) + 77 + $greg_correction;
+
+ return $day - 7 * floor($day / 7);
+ }
+
+ /**
+ * Checks for leap year, returns true if it is. No 2-digit year check. Also
+ * handles julian calendar correctly.
+ * @param float year to check
+ * @return boolean true if is leap year
+ */
+ public function isLeapYear($year)
+ {
+ $year = $this->digitCheck($year);
+ if ($year % 4 != 0)
+ return false;
+
+ if ($year % 400 == 0)
+ return true;
+ // if gregorian calendar (>1582), century not-divisible by 400 is not leap
+ else if ($year > 1582 && $year % 100 == 0 )
+ return false;
+ return true;
+ }
+
+ /**
+ * Fix 2-digit years. Works for any century.
+ * Assumes that if 2-digit is more than 30 years in future, then previous century.
+ * @return integer change two digit year into multiple digits
+ */
+ protected function digitCheck($y)
+ {
+ if ($y < 100){
+ $yr = (integer) date("Y");
+ $century = (integer) ($yr /100);
+
+ if ($yr%100 > 50) {
+ $c1 = $century + 1;
+ $c0 = $century;
+ } else {
+ $c1 = $century;
+ $c0 = $century - 1;
+ }
+ $c1 *= 100;
+ // if 2-digit year is less than 30 years in future, set it to this century
+ // otherwise if more than 30 years in future, then we set 2-digit year to the prev century.
+ if (($y + $c1) < $yr+30) $y = $y + $c1;
+ else $y = $y + $c0*100;
+ }
+ return $y;
+ }
+
+ public function get4DigitYear($y)
+ {
+ return $this->digitCheck($y);
+ }
+
+ /**
+ * @return integer get local time zone offset from GMT
+ */
+ public function getGMTDiff()
+ {
+ static $TZ;
+ if (isset($TZ)) return $TZ;
+
+ $TZ = mktime(0,0,0,1,2,1970) - gmmktime(0,0,0,1,2,1970);
+ return $TZ;
+ }
+
+ /**
+ * @return array an array with date info.
+ */
+ function getDate($d=false,$fast=false)
+ {
+ if ($d === false) return getdate();
+ // check if number in 32-bit signed range
+ if ((abs($d) <= 0x7FFFFFFF))
+ {
+ if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) // if windows, must be +ve integer
+ return @getdate($d);
+ }
+ return $this->_getDateInternal($d);
+ }
+
+
+
+ /**
+ * Low-level function that returns the getdate() array. We have a special
+ * $fast flag, which if set to true, will return fewer array values,
+ * and is much faster as it does not calculate dow, etc.
+ * @param float original date
+ * @param boolean false to compute the day of the week, default is true
+ * @param boolean true to calculate the GMT dates
+ * @return array an array with date info.
+ */
+ protected function _getDateInternal($origd=false,$fast=true,$is_gmt=false)
+ {
+ static $YRS;
+
+ $d = $origd - ($is_gmt ? 0 : $this->getGMTDiff());
+
+ $_day_power = 86400;
+ $_hour_power = 3600;
+ $_min_power = 60;
+
+ if ($d < -12219321600)
+ $d -= 86400*10; // if 15 Oct 1582 or earlier, gregorian correction
+
+ $_month_table_normal =& self::$_month_normal;
+ $_month_table_leaf = & self::$_month_leaf;
+
+ $d366 = $_day_power * 366;
+ $d365 = $_day_power * 365;
+
+ if ($d < 0)
+ {
+ if (empty($YRS))
+ $YRS = array(
+ 1970 => 0,
+ 1960 => -315619200,
+ 1950 => -631152000,
+ 1940 => -946771200,
+ 1930 => -1262304000,
+ 1920 => -1577923200,
+ 1910 => -1893456000,
+ 1900 => -2208988800,
+ 1890 => -2524521600,
+ 1880 => -2840140800,
+ 1870 => -3155673600,
+ 1860 => -3471292800,
+ 1850 => -3786825600,
+ 1840 => -4102444800,
+ 1830 => -4417977600,
+ 1820 => -4733596800,
+ 1810 => -5049129600,
+ 1800 => -5364662400,
+ 1790 => -5680195200,
+ 1780 => -5995814400,
+ 1770 => -6311347200,
+ 1760 => -6626966400,
+ 1750 => -6942499200,
+ 1740 => -7258118400,
+ 1730 => -7573651200,
+ 1720 => -7889270400,
+ 1710 => -8204803200,
+ 1700 => -8520336000,
+ 1690 => -8835868800,
+ 1680 => -9151488000,
+ 1670 => -9467020800,
+ 1660 => -9782640000,
+ 1650 => -10098172800,
+ 1640 => -10413792000,
+ 1630 => -10729324800,
+ 1620 => -11044944000,
+ 1610 => -11360476800,
+ 1600 => -11676096000);
+
+ if ($is_gmt)
+ $origd = $d;
+ // The valid range of a 32bit signed timestamp is typically from
+ // Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT
+ //
+
+ # old algorithm iterates through all years. new algorithm does it in
+ # 10 year blocks
+
+ /*
+ # old algo
+ for ($a = 1970 ; --$a >= 0;) {
+ $lastd = $d;
+
+ if ($leaf = _adodb_is_leap_year($a)) $d += $d366;
+ else $d += $d365;
+
+ if ($d >= 0) {
+ $year = $a;
+ break;
+ }
+ }
+ */
+
+ $lastsecs = 0;
+ $lastyear = 1970;
+ foreach($YRS as $year => $secs)
+ {
+ if ($d >= $secs)
+ {
+ $a = $lastyear;
+ break;
+ }
+ $lastsecs = $secs;
+ $lastyear = $year;
+ }
+
+ $d -= $lastsecs;
+ if (!isset($a)) $a = $lastyear;
+
+ //echo ' yr=',$a,' ', $d,'.';
+
+ for (; --$a >= 0;)
+ {
+ $lastd = $d;
+
+ if ($leaf = $this->isLeapYear($a))
+ $d += $d366;
+ else
+ $d += $d365;
+
+ if ($d >= 0)
+ {
+ $year = $a;
+ break;
+ }
+ }
+ /**/
+
+ $secsInYear = 86400 * ($leaf ? 366 : 365) + $lastd;
+
+ $d = $lastd;
+ $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal;
+ for ($a = 13 ; --$a > 0;)
+ {
+ $lastd = $d;
+ $d += $mtab[$a] * $_day_power;
+ if ($d >= 0)
+ {
+ $month = $a;
+ $ndays = $mtab[$a];
+ break;
+ }
+ }
+
+ $d = $lastd;
+ $day = $ndays + ceil(($d+1) / ($_day_power));
+
+ $d += ($ndays - $day+1)* $_day_power;
+ $hour = floor($d/$_hour_power);
+
+ } else {
+ for ($a = 1970 ;; $a++)
+ {
+ $lastd = $d;
+
+ if ($leaf = $this->isLeapYear($a)) $d -= $d366;
+ else $d -= $d365;
+ if ($d < 0)
+ {
+ $year = $a;
+ break;
+ }
+ }
+ $secsInYear = $lastd;
+ $d = $lastd;
+ $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal;
+ for ($a = 1 ; $a <= 12; $a++)
+ {
+ $lastd = $d;
+ $d -= $mtab[$a] * $_day_power;
+ if ($d < 0)
+ {
+ $month = $a;
+ $ndays = $mtab[$a];
+ break;
+ }
+ }
+ $d = $lastd;
+ $day = ceil(($d+1) / $_day_power);
+ $d = $d - ($day-1) * $_day_power;
+ $hour = floor($d /$_hour_power);
+ }
+
+ $d -= $hour * $_hour_power;
+ $min = floor($d/$_min_power);
+ $secs = $d - $min * $_min_power;
+ if ($fast)
+ {
+ return array(
+ 'seconds' => $secs,
+ 'minutes' => $min,
+ 'hours' => $hour,
+ 'mday' => $day,
+ 'mon' => $month,
+ 'year' => $year,
+ 'yday' => floor($secsInYear/$_day_power),
+ 'leap' => $leaf,
+ 'ndays' => $ndays
+ );
+ }
+
+
+ $dow = $this->getDayofWeek($year,$month,$day);
+
+ return array(
+ 'seconds' => $secs,
+ 'minutes' => $min,
+ 'hours' => $hour,
+ 'mday' => $day,
+ 'wday' => $dow,
+ 'mon' => $month,
+ 'year' => $year,
+ 'yday' => floor($secsInYear/$_day_power),
+ 'weekday' => gmdate('l',$_day_power*(3+$dow)),
+ 'month' => gmdate('F',mktime(0,0,0,$month,2,1971)),
+ 0 => $origd
+ );
+ }
+
+ /**
+ * @return boolean true if valid date, semantic check only.
+ */
+ public function isValidDate($y,$m,$d)
+ {
+ if ($this->isLeapYear($y))
+ $marr =& self::$_month_leaf;
+ else
+ $marr =& self::$_month_normal;
+
+ if ($m > 12 || $m < 1) return false;
+
+ if ($d > 31 || $d < 1) return false;
+
+ if ($marr[$m] < $d) return false;
+
+ if ($y < 1000 && $y > 3000) return false;
+
+ return true;
+ }
+
+ /**
+ * @return string formatted date based on timestamp $d
+ */
+ function formatDate($fmt,$d=false,$is_gmt=false)
+ {
+ if ($d === false)
+ return ($is_gmt)? @gmdate($fmt): @date($fmt);
+
+ // check if number in 32-bit signed range
+ if ((abs($d) <= 0x7FFFFFFF))
+ {
+ // if windows, must be +ve integer
+ if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0)
+ return ($is_gmt)? @gmdate($fmt,$d): @date($fmt,$d);
+ }
+
+ $_day_power = 86400;
+
+ $arr = $this->getDate($d,true,$is_gmt);
+
+ $year = $arr['year'];
+ $month = $arr['mon'];
+ $day = $arr['mday'];
+ $hour = $arr['hours'];
+ $min = $arr['minutes'];
+ $secs = $arr['seconds'];
+
+ $max = strlen($fmt);
+ $dates = '';
+
+ $isphp5 = PHP_VERSION >= 5;
+
+ /*
+ at this point, we have the following integer vars to manipulate:
+ $year, $month, $day, $hour, $min, $secs
+ */
+ for ($i=0; $i < $max; $i++)
+ {
+ switch($fmt[$i])
+ {
+ case 'T': $dates .= date('T');break;
+ // YEAR
+ case 'L': $dates .= $arr['leap'] ? '1' : '0'; break;
+ case 'r': // Thu, 21 Dec 2000 16:01:07 +0200
+
+ // 4.3.11 uses '04 Jun 2004'
+ // 4.3.8 uses ' 4 Jun 2004'
+ $dates .= gmdate('D',$_day_power*(3+$this->getDayOfWeek($year,$month,$day))).', '
+ . ($day<10?'0'.$day:$day) . ' '.date('M',mktime(0,0,0,$month,2,1971)).' '.$year.' ';
+
+ if ($hour < 10) $dates .= '0'.$hour; else $dates .= $hour;
+
+ if ($min < 10) $dates .= ':0'.$min; else $dates .= ':'.$min;
+
+ if ($secs < 10) $dates .= ':0'.$secs; else $dates .= ':'.$secs;
+
+ $gmt = $this->getGMTDiff();
+ if ($isphp5)
+ $dates .= sprintf(' %s%04d',($gmt<=0)?'+':'-',abs($gmt)/36);
+ else
+ $dates .= sprintf(' %s%04d',($gmt<0)?'+':'-',abs($gmt)/36);
+ break;
+
+ case 'Y': $dates .= $year; break;
+ case 'y': $dates .= substr($year,strlen($year)-2,2); break;
+ // MONTH
+ case 'm': if ($month<10) $dates .= '0'.$month; else $dates .= $month; break;
+ case 'Q': $dates .= ($month+3)>>2; break;
+ case 'n': $dates .= $month; break;
+ case 'M': $dates .= date('M',mktime(0,0,0,$month,2,1971)); break;
+ case 'F': $dates .= date('F',mktime(0,0,0,$month,2,1971)); break;
+ // DAY
+ case 't': $dates .= $arr['ndays']; break;
+ case 'z': $dates .= $arr['yday']; break;
+ case 'w': $dates .= $this->getDayOfWeek($year,$month,$day); break;
+ case 'l': $dates .= gmdate('l',$_day_power*(3+$this->getDayOfWeek($year,$month,$day))); break;
+ case 'D': $dates .= gmdate('D',$_day_power*(3+$this->getDayOfWeek($year,$month,$day))); break;
+ case 'j': $dates .= $day; break;
+ case 'd': if ($day<10) $dates .= '0'.$day; else $dates .= $day; break;
+ case 'S':
+ $d10 = $day % 10;
+ if ($d10 == 1) $dates .= 'st';
+ else if ($d10 == 2 && $day != 12) $dates .= 'nd';
+ else if ($d10 == 3) $dates .= 'rd';
+ else $dates .= 'th';
+ break;
+
+ // HOUR
+ case 'Z':
+ $dates .= ($is_gmt) ? 0 : -$this->getGMTDiff(); break;
+ case 'O':
+ $gmt = ($is_gmt) ? 0 : $this->getGMTDiff();
+
+ if ($isphp5)
+ $dates .= sprintf('%s%04d',($gmt<=0)?'+':'-',abs($gmt)/36);
+ else
+ $dates .= sprintf('%s%04d',($gmt<0)?'+':'-',abs($gmt)/36);
+ break;
+
+ case 'H':
+ if ($hour < 10) $dates .= '0'.$hour;
+ else $dates .= $hour;
+ break;
+ case 'h':
+ if ($hour > 12) $hh = $hour - 12;
+ else {
+ if ($hour == 0) $hh = '12';
+ else $hh = $hour;
+ }
+
+ if ($hh < 10) $dates .= '0'.$hh;
+ else $dates .= $hh;
+ break;
+
+ case 'G':
+ $dates .= $hour;
+ break;
+
+ case 'g':
+ if ($hour > 12) $hh = $hour - 12;
+ else {
+ if ($hour == 0) $hh = '12';
+ else $hh = $hour;
+ }
+ $dates .= $hh;
+ break;
+ // MINUTES
+ case 'i': if ($min < 10) $dates .= '0'.$min; else $dates .= $min; break;
+ // SECONDS
+ case 'U': $dates .= $d; break;
+ case 's': if ($secs < 10) $dates .= '0'.$secs; else $dates .= $secs; break;
+ // AM/PM
+ // Note 00:00 to 11:59 is AM, while 12:00 to 23:59 is PM
+ case 'a':
+ if ($hour>=12) $dates .= 'pm';
+ else $dates .= 'am';
+ break;
+ case 'A':
+ if ($hour>=12) $dates .= 'PM';
+ else $dates .= 'AM';
+ break;
+ default:
+ $dates .= $fmt[$i]; break;
+ // ESCAPE
+ case "\\":
+ $i++;
+ if ($i < $max) $dates .= $fmt[$i];
+ break;
+ }
+ }
+ return $dates;
+ }
+
+ /**
+ * Not a very fast algorithm - O(n) operation. Could be optimized to O(1).
+ * @return integer|float a timestamp given a local time. Originally by jackbbs.
+ */
+ function getTimeStamp($hr,$min,$sec,$mon=false,$day=false,$year=false,$is_gmt=false)
+ {
+
+ if ($mon === false)
+ return $is_gmt? @gmmktime($hr,$min,$sec): @mktime($hr,$min,$sec);
+
+ // for windows, we don't check 1970 because with timezone differences,
+ // 1 Jan 1970 could generate negative timestamp, which is illegal
+ if (1971 <= $year && $year < 2038 || !defined('ADODB_NO_NEGATIVE_TS')
+ && (1901 < $year && $year < 2038))
+ {
+ return $is_gmt ? @gmmktime($hr,$min,$sec,$mon,$day,$year)
+ : @mktime($hr,$min,$sec,$mon,$day,$year);
+ }
+
+ $gmt_different = ($is_gmt) ? 0 : $this->getGMTDiff();
+
+ /*
+ # disabled because some people place large values in $sec.
+ # however we need it for $mon because we use an array...
+ $hr = intval($hr);
+ $min = intval($min);
+ $sec = intval($sec);
+ */
+ $mon = (int)$mon;
+ $day = (int)$day;
+ $year = (int)$year;
+
+
+ $year = $this->digitCheck($year);
+
+ if ($mon > 12)
+ {
+ $y = floor($mon / 12);
+ $year += $y;
+ $mon -= $y*12;
+ }
+ else if ($mon < 1)
+ {
+ $y = ceil((1-$mon) / 12);
+ $year -= $y;
+ $mon += $y*12;
+ }
+
+ $_day_power = 86400;
+ $_hour_power = 3600;
+ $_min_power = 60;
+
+ $_month_table_normal = & self::$_month_normal;
+ $_month_table_leaf = & self::$_month_leaf;
+
+ $_total_date = 0;
+ if ($year >= 1970)
+ {
+ for ($a = 1970 ; $a <= $year; $a++)
+ {
+ $leaf = $this->isLeapYear($a);
+ if ($leaf == true) {
+ $loop_table = $_month_table_leaf;
+ $_add_date = 366;
+ } else {
+ $loop_table = $_month_table_normal;
+ $_add_date = 365;
+ }
+ if ($a < $year) {
+ $_total_date += $_add_date;
+ } else {
+ for($b=1;$b<$mon;$b++) {
+ $_total_date += $loop_table[$b];
+ }
+ }
+ }
+ $_total_date +=$day-1;
+ $ret = $_total_date * $_day_power + $hr * $_hour_power + $min * $_min_power + $sec + $gmt_different;
+
+ } else {
+ for ($a = 1969 ; $a >= $year; $a--) {
+ $leaf = $this->isLeapYear($a);
+ if ($leaf == true) {
+ $loop_table = $_month_table_leaf;
+ $_add_date = 366;
+ } else {
+ $loop_table = $_month_table_normal;
+ $_add_date = 365;
+ }
+ if ($a > $year) { $_total_date += $_add_date;
+ } else {
+ for($b=12;$b>$mon;$b--) {
+ $_total_date += $loop_table[$b];
+ }
+ }
+ }
+ $_total_date += $loop_table[$mon] - $day;
+
+ $_day_time = $hr * $_hour_power + $min * $_min_power + $sec;
+ $_day_time = $_day_power - $_day_time;
+ $ret = -( $_total_date * $_day_power + $_day_time - $gmt_different);
+ if ($ret < -12220185600) $ret += 10*86400; // if earlier than 5 Oct 1582 - gregorian correction
+ else if ($ret < -12219321600) $ret = -12219321600; // if in limbo, reset to 15 Oct 1582.
+ }
+ //print " dmy=$day/$mon/$year $hr:$min:$sec => " .$ret;
+ return $ret;
+ }
+}
+
+?>
diff --git a/framework/Util/TLogRouter.php b/framework/Util/TLogRouter.php
index 20b0c663..14895899 100644
--- a/framework/Util/TLogRouter.php
+++ b/framework/Util/TLogRouter.php
@@ -1,1207 +1,1207 @@
-<?php
-/**
- * TLogRouter, TLogRoute, TFileLogRoute, TEmailLogRoute class file
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright &copy; 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Util
- */
-
-Prado::using('System.Data.TDbConnection');
-
-/**
- * TLogRouter class.
- *
- * TLogRouter manages routes that record log messages in different media different ways.
- * For example, a file log route {@link TFileLogRoute} records log messages
- * in log files. An email log route {@link TEmailLogRoute} sends log messages
- * to email addresses.
- *
- * Log routes may be configured in application or page folder configuration files
- * or an external configuration file specified by {@link setConfigFile ConfigFile}.
- * The format is as follows,
- * <code>
- * <route class="TFileLogRoute" Categories="System.Web.UI" Levels="Warning" />
- * <route class="TEmailLogRoute" Categories="Application" Levels="Fatal" Emails="admin@pradosoft.com" />
- * </code>
- * PHP configuration style:
- * <code>
- *
- * </code>
- * You can specify multiple routes with different filtering conditions and different
- * targets, even if the routes are of the same type.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @author Carl G. Mathisen <carlgmathisen@gmail.com>
- * @version $Id$
- * @package System.Util
- * @since 3.0
- */
-class TLogRouter extends TModule
-{
- /**
- * @var array list of routes available
- */
- private $_routes=array();
- /**
- * @var string external configuration file
- */
- private $_configFile=null;
-
- /**
- * Initializes this module.
- * This method is required by the IModule interface.
- * @param mixed configuration for this module, can be null
- * @throws TConfigurationException if {@link getConfigFile ConfigFile} is invalid.
- */
- public function init($config)
- {
- if($this->_configFile!==null)
- {
- if(is_file($this->_configFile))
- {
- if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP)
- {
- $phpConfig = include $this->_configFile;
- $this->loadConfig($phpConfig);
- }
- else
- {
- $dom=new TXmlDocument;
- $dom->loadFromFile($this->_configFile);
- $this->loadConfig($dom);
- }
- }
- else
- throw new TConfigurationException('logrouter_configfile_invalid',$this->_configFile);
- }
- $this->loadConfig($config);
- $this->getApplication()->attachEventHandler('OnEndRequest',array($this,'collectLogs'));
- }
-
- /**
- * Loads configuration from an XML element or PHP array
- * @param mixed configuration node
- * @throws TConfigurationException if log route class or type is not specified
- */
- private function loadConfig($config)
- {
- if(is_array($config))
- {
- if(isset($config['routes']) && is_array($config['routes']))
- {
- foreach($config['routes'] as $route)
- {
- $properties = isset($route['properties'])?$route['properties']:array();
- if(!isset($route['class']))
- throw new TConfigurationException('logrouter_routeclass_required');
- $route=Prado::createComponent($route['class']);
- if(!($route instanceof TLogRoute))
- throw new TConfigurationException('logrouter_routetype_invalid');
- foreach($properties as $name=>$value)
- $route->setSubproperty($name,$value);
- $this->_routes[]=$route;
- $route->init($route);
- }
- }
- }
- else
- {
- foreach($config->getElementsByTagName('route') as $routeConfig)
- {
- $properties=$routeConfig->getAttributes();
- if(($class=$properties->remove('class'))===null)
- throw new TConfigurationException('logrouter_routeclass_required');
- $route=Prado::createComponent($class);
- if(!($route instanceof TLogRoute))
- throw new TConfigurationException('logrouter_routetype_invalid');
- foreach($properties as $name=>$value)
- $route->setSubproperty($name,$value);
- $this->_routes[]=$route;
- $route->init($routeConfig);
- }
- }
- }
-
- /**
- * Adds a TLogRoute instance to the log router.
- *
- * @param TLogRoute $route
- * @throws TInvalidDataTypeException if the route object is invalid
- */
- public function addRoute($route)
- {
- if(!($route instanceof TLogRoute))
- throw new TInvalidDataTypeException('logrouter_routetype_invalid');
- $this->_routes[]=$route;
- $route->init(null);
- }
-
- /**
- * @return string external configuration file. Defaults to null.
- */
- public function getConfigFile()
- {
- return $this->_configFile;
- }
-
- /**
- * @param string external configuration file in namespace format. The file
- * must be suffixed with '.xml'.
- * @throws TConfigurationException if the file is invalid.
- */
- public function setConfigFile($value)
- {
- if(($this->_configFile=Prado::getPathOfNamespace($value,$this->getApplication()->getConfigurationFileExt()))===null)
- throw new TConfigurationException('logrouter_configfile_invalid',$value);
- }
-
- /**
- * Collects log messages from a logger.
- * This method is an event handler to application's EndRequest event.
- * @param mixed event parameter
- */
- public function collectLogs($param)
- {
- $logger=Prado::getLogger();
- foreach($this->_routes as $route)
- $route->collectLogs($logger);
- }
-}
-
-/**
- * TLogRoute class.
- *
- * TLogRoute is the base class for all log route classes.
- * A log route object retrieves log messages from a logger and sends it
- * somewhere, such as files, emails.
- * The messages being retrieved may be filtered first before being sent
- * to the destination. The filters include log level filter and log category filter.
- *
- * To specify level filter, set {@link setLevels Levels} property,
- * which takes a string of comma-separated desired level names (e.g. 'Error, Debug').
- * To specify category filter, set {@link setCategories Categories} property,
- * which takes a string of comma-separated desired category names (e.g. 'System.Web, System.IO').
- *
- * Level filter and category filter are combinational, i.e., only messages
- * satisfying both filter conditions will they be returned.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @version $Id$
- * @package System.Util
- * @since 3.0
- */
-abstract class TLogRoute extends TApplicationComponent
-{
- /**
- * @var array lookup table for level names
- */
- protected static $_levelNames=array(
- TLogger::DEBUG=>'Debug',
- TLogger::INFO=>'Info',
- TLogger::NOTICE=>'Notice',
- TLogger::WARNING=>'Warning',
- TLogger::ERROR=>'Error',
- TLogger::ALERT=>'Alert',
- TLogger::FATAL=>'Fatal'
- );
- /**
- * @var array lookup table for level values
- */
- protected static $_levelValues=array(
- 'debug'=>TLogger::DEBUG,
- 'info'=>TLogger::INFO,
- 'notice'=>TLogger::NOTICE,
- 'warning'=>TLogger::WARNING,
- 'error'=>TLogger::ERROR,
- 'alert'=>TLogger::ALERT,
- 'fatal'=>TLogger::FATAL
- );
- /**
- * @var integer log level filter (bits)
- */
- private $_levels=null;
- /**
- * @var array log category filter
- */
- private $_categories=null;
-
- /**
- * Initializes the route.
- * @param TXmlElement configurations specified in {@link TLogRouter}.
- */
- public function init($config)
- {
- }
-
- /**
- * @return integer log level filter
- */
- public function getLevels()
- {
- return $this->_levels;
- }
-
- /**
- * @param integer|string integer log level filter (in bits). If the value is
- * a string, it is assumed to be comma-separated level names. Valid level names
- * include 'Debug', 'Info', 'Notice', 'Warning', 'Error', 'Alert' and 'Fatal'.
- */
- public function setLevels($levels)
- {
- if(is_integer($levels))
- $this->_levels=$levels;
- else
- {
- $this->_levels=null;
- $levels=strtolower($levels);
- foreach(explode(',',$levels) as $level)
- {
- $level=trim($level);
- if(isset(self::$_levelValues[$level]))
- $this->_levels|=self::$_levelValues[$level];
- }
- }
- }
-
- /**
- * @return array list of categories to be looked for
- */
- public function getCategories()
- {
- return $this->_categories;
- }
-
- /**
- * @param array|string list of categories to be looked for. If the value is a string,
- * it is assumed to be comma-separated category names.
- */
- public function setCategories($categories)
- {
- if(is_array($categories))
- $this->_categories=$categories;
- else
- {
- $this->_categories=null;
- foreach(explode(',',$categories) as $category)
- {
- if(($category=trim($category))!=='')
- $this->_categories[]=$category;
- }
- }
- }
-
- /**
- * @param integer level value
- * @return string level name
- */
- protected function getLevelName($level)
- {
- return isset(self::$_levelNames[$level])?self::$_levelNames[$level]:'Unknown';
- }
-
- /**
- * @param string level name
- * @return integer level value
- */
- protected function getLevelValue($level)
- {
- return isset(self::$_levelValues[$level])?self::$_levelValues[$level]:0;
- }
-
- /**
- * Formats a log message given different fields.
- * @param string message content
- * @param integer message level
- * @param string message category
- * @param integer timestamp
- * @return string formatted message
- */
- protected function formatLogMessage($message,$level,$category,$time)
- {
- return @gmdate('M d H:i:s',$time).' ['.$this->getLevelName($level).'] ['.$category.'] '.$message."\n";
- }
-
- /**
- * Retrieves log messages from logger to log route specific destination.
- * @param TLogger logger instance
- */
- public function collectLogs(TLogger $logger)
- {
- $logs=$logger->getLogs($this->getLevels(),$this->getCategories());
- if(!empty($logs))
- $this->processLogs($logs);
- }
-
- /**
- * Processes log messages and sends them to specific destination.
- * Derived child classes must implement this method.
- * @param array list of messages. Each array elements represents one message
- * with the following structure:
- * array(
- * [0] => message
- * [1] => level
- * [2] => category
- * [3] => timestamp);
- */
- abstract protected function processLogs($logs);
-}
-
-/**
- * TFileLogRoute class.
- *
- * TFileLogRoute records log messages in files.
- * The log files are stored under {@link setLogPath LogPath} and the file name
- * is specified by {@link setLogFile LogFile}. If the size of the log file is
- * greater than {@link setMaxFileSize MaxFileSize} (in kilo-bytes), a rotation
- * is performed, which renames the current log file by suffixing the file name
- * with '.1'. All existing log files are moved backwards one place, i.e., '.2'
- * to '.3', '.1' to '.2'. The property {@link setMaxLogFiles MaxLogFiles}
- * specifies how many files to be kept.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @version $Id$
- * @package System.Util
- * @since 3.0
- */
-class TFileLogRoute extends TLogRoute
-{
- /**
- * @var integer maximum log file size
- */
- private $_maxFileSize=512; // in KB
- /**
- * @var integer number of log files used for rotation
- */
- private $_maxLogFiles=2;
- /**
- * @var string directory storing log files
- */
- private $_logPath=null;
- /**
- * @var string log file name
- */
- private $_logFile='prado.log';
-
- /**
- * @return string directory storing log files. Defaults to application runtime path.
- */
- public function getLogPath()
- {
- if($this->_logPath===null)
- $this->_logPath=$this->getApplication()->getRuntimePath();
- return $this->_logPath;
- }
-
- /**
- * @param string directory (in namespace format) storing log files.
- * @throws TConfigurationException if log path is invalid
- */
- public function setLogPath($value)
- {
- if(($this->_logPath=Prado::getPathOfNamespace($value))===null || !is_dir($this->_logPath) || !is_writable($this->_logPath))
- throw new TConfigurationException('filelogroute_logpath_invalid',$value);
- }
-
- /**
- * @return string log file name. Defaults to 'prado.log'.
- */
- public function getLogFile()
- {
- return $this->_logFile;
- }
-
- /**
- * @param string log file name
- */
- public function setLogFile($value)
- {
- $this->_logFile=$value;
- }
-
- /**
- * @return integer maximum log file size in kilo-bytes (KB). Defaults to 1024 (1MB).
- */
- public function getMaxFileSize()
- {
- return $this->_maxFileSize;
- }
-
- /**
- * @param integer maximum log file size in kilo-bytes (KB).
- * @throws TInvalidDataValueException if the value is smaller than 1.
- */
- public function setMaxFileSize($value)
- {
- $this->_maxFileSize=TPropertyValue::ensureInteger($value);
- if($this->_maxFileSize<=0)
- throw new TInvalidDataValueException('filelogroute_maxfilesize_invalid');
- }
-
- /**
- * @return integer number of files used for rotation. Defaults to 2.
- */
- public function getMaxLogFiles()
- {
- return $this->_maxLogFiles;
- }
-
- /**
- * @param integer number of files used for rotation.
- */
- public function setMaxLogFiles($value)
- {
- $this->_maxLogFiles=TPropertyValue::ensureInteger($value);
- if($this->_maxLogFiles<1)
- throw new TInvalidDataValueException('filelogroute_maxlogfiles_invalid');
- }
-
- /**
- * Saves log messages in files.
- * @param array list of log messages
- */
- protected function processLogs($logs)
- {
- $logFile=$this->getLogPath().DIRECTORY_SEPARATOR.$this->getLogFile();
- if(@filesize($logFile)>$this->_maxFileSize*1024)
- $this->rotateFiles();
- foreach($logs as $log)
- error_log($this->formatLogMessage($log[0],$log[1],$log[2],$log[3]),3,$logFile);
- }
-
- /**
- * Rotates log files.
- */
- protected function rotateFiles()
- {
- $file=$this->getLogPath().DIRECTORY_SEPARATOR.$this->getLogFile();
- for($i=$this->_maxLogFiles;$i>0;--$i)
- {
- $rotateFile=$file.'.'.$i;
- if(is_file($rotateFile))
- {
- if($i===$this->_maxLogFiles)
- unlink($rotateFile);
- else
- rename($rotateFile,$file.'.'.($i+1));
- }
- }
- if(is_file($file))
- rename($file,$file.'.1');
- }
-}
-
-/**
- * TEmailLogRoute class.
- *
- * TEmailLogRoute sends selected log messages to email addresses.
- * The target email addresses may be specified via {@link setEmails Emails} property.
- * Optionally, you may set the email {@link setSubject Subject} and the
- * {@link setSentFrom SentFrom} address.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @version $Id$
- * @package System.Util
- * @since 3.0
- */
-class TEmailLogRoute extends TLogRoute
-{
- /**
- * Regex pattern for email address.
- */
- const EMAIL_PATTERN='/^([0-9a-zA-Z]+[-._+&])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}$/';
- /**
- * Default email subject.
- */
- const DEFAULT_SUBJECT='Prado Application Log';
- /**
- * @var array list of destination email addresses.
- */
- private $_emails=array();
- /**
- * @var string email subject
- */
- private $_subject='';
- /**
- * @var string email sent from address
- */
- private $_from='';
-
- /**
- * Initializes the route.
- * @param TXmlElement configurations specified in {@link TLogRouter}.
- * @throws TConfigurationException if {@link getSentFrom SentFrom} is empty and
- * 'sendmail_from' in php.ini is also empty.
- */
- public function init($config)
- {
- if($this->_from==='')
- $this->_from=ini_get('sendmail_from');
- if($this->_from==='')
- throw new TConfigurationException('emaillogroute_sentfrom_required');
- }
-
- /**
- * Sends log messages to specified email addresses.
- * @param array list of log messages
- */
- protected function processLogs($logs)
- {
- $message='';
- foreach($logs as $log)
- $message.=$this->formatLogMessage($log[0],$log[1],$log[2],$log[3]);
- $message=wordwrap($message,70);
- $returnPath = ini_get('sendmail_path') ? "Return-Path:{$this->_from}\r\n" : '';
- foreach($this->_emails as $email)
- mail($email,$this->getSubject(),$message,"From:{$this->_from}\r\n{$returnPath}");
-
- }
-
- /**
- * @return array list of destination email addresses
- */
- public function getEmails()
- {
- return $this->_emails;
- }
-
- /**
- * @return array|string list of destination email addresses. If the value is
- * a string, it is assumed to be comma-separated email addresses.
- */
- public function setEmails($emails)
- {
- if(is_array($emails))
- $this->_emails=$emails;
- else
- {
- $this->_emails=array();
- foreach(explode(',',$emails) as $email)
- {
- $email=trim($email);
- if(preg_match(self::EMAIL_PATTERN,$email))
- $this->_emails[]=$email;
- }
- }
- }
-
- /**
- * @return string email subject. Defaults to TEmailLogRoute::DEFAULT_SUBJECT
- */
- public function getSubject()
- {
- if($this->_subject===null)
- $this->_subject=self::DEFAULT_SUBJECT;
- return $this->_subject;
- }
-
- /**
- * @param string email subject.
- */
- public function setSubject($value)
- {
- $this->_subject=$value;
- }
-
- /**
- * @return string send from address of the email
- */
- public function getSentFrom()
- {
- return $this->_from;
- }
-
- /**
- * @param string send from address of the email
- */
- public function setSentFrom($value)
- {
- $this->_from=$value;
- }
-}
-
-/**
- * TBrowserLogRoute class.
- *
- * TBrowserLogRoute prints selected log messages in the response.
- *
- * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @version $Id$
- * @package System.Util
- * @since 3.0
- */
-class TBrowserLogRoute extends TLogRoute
-{
- /**
- * @var string css class for indentifying the table structure in the dom tree
- */
- private $_cssClass=null;
-
- public function processLogs($logs)
- {
- if(empty($logs) || $this->getApplication()->getMode()==='Performance') return;
- $first = $logs[0][3];
- $even = true;
- $response = $this->getApplication()->getResponse();
- $response->write($this->renderHeader());
- for($i=0,$n=count($logs);$i<$n;++$i)
- {
- if ($i<$n-1)
- {
- $timing['delta'] = $logs[$i+1][3] - $logs[$i][3];
- $timing['total'] = $logs[$i+1][3] - $first;
- }
- else
- {
- $timing['delta'] = '?';
- $timing['total'] = $logs[$i][3] - $first;
- }
- $timing['even'] = !($even = !$even);
- $response->write($this->renderMessage($logs[$i],$timing));
- }
- $response->write($this->renderFooter());
- }
-
- /**
- * @param string the css class of the control
- */
- public function setCssClass($value)
- {
- $this->_cssClass = TPropertyValue::ensureString($value);
- }
-
- /**
- * @return string the css class of the control
- */
- public function getCssClass()
- {
- return TPropertyValue::ensureString($this->_cssClass);
- }
-
- protected function renderHeader()
- {
- $string = '';
- if($className=$this->getCssClass())
- {
- $string = <<<EOD
-<table class="$className">
- <tr class="header">
- <th colspan="5">
- Application Log
- </th>
- </tr><tr class="description">
- <th>&nbsp;</th>
- <th>Category</th><th>Message</th><th>Time Spent (s)</th><th>Cumulated Time Spent (s)</th>
- </tr>
-EOD;
- }
- else
- {
- $string = <<<EOD
-<table cellspacing="0" cellpadding="2" border="0" width="100%" style="table-layout:auto">
- <tr>
- <th style="background-color: black; color:white;" colspan="5">
- Application Log
- </th>
- </tr><tr style="background-color: #ccc; color:black">
- <th style="width: 15px">&nbsp;</th>
- <th style="width: auto">Category</th><th style="width: auto">Message</th><th style="width: 120px">Time Spent (s)</th><th style="width: 150px">Cumulated Time Spent (s)</th>
- </tr>
-EOD;
- }
- return $string;
- }
-
- protected function renderMessage($log, $info)
- {
- $string = '';
- $total = sprintf('%0.6f', $info['total']);
- $delta = sprintf('%0.6f', $info['delta']);
- $msg = preg_replace('/\(line[^\)]+\)$/','',$log[0]); //remove line number info
- $msg = THttpUtility::htmlEncode($msg);
- if($this->getCssClass())
- {
- $colorCssClass = $log[1];
- $messageCssClass = $info['even'] ? 'even' : 'odd';
- $string = <<<EOD
- <tr class="message level$colorCssClass $messageCssClass">
- <td class="code">&nbsp;</td>
- <td class="category">{$log[2]}</td>
- <td class="message">{$msg}</td>
- <td class="time">{$delta}</td>
- <td class="cumulatedtime">{$total}</td>
- </tr>
-EOD;
- }
- else
- {
- $bgcolor = $info['even'] ? "#fff" : "#eee";
- $color = $this->getColorLevel($log[1]);
- $string = <<<EOD
- <tr style="background-color: {$bgcolor}; color:#000">
- <td style="border:1px solid silver;background-color: $color;">&nbsp;</td>
- <td>{$log[2]}</td>
- <td>{$msg}</td>
- <td style="text-align:center">{$delta}</td>
- <td style="text-align:center">{$total}</td>
- </tr>
-EOD;
- }
- return $string;
- }
-
- protected function getColorLevel($level)
- {
- switch($level)
- {
- case TLogger::DEBUG: return 'green';
- case TLogger::INFO: return 'black';
- case TLogger::NOTICE: return '#3333FF';
- case TLogger::WARNING: return '#33FFFF';
- case TLogger::ERROR: return '#ff9933';
- case TLogger::ALERT: return '#ff00ff';
- case TLogger::FATAL: return 'red';
- }
- return '';
- }
-
- protected function renderFooter()
- {
- $string = '';
- if($this->getCssClass())
- {
- $string .= '<tr class="footer"><td colspan="5">';
- foreach(self::$_levelValues as $name => $level)
- {
- $string .= '<span class="level'.$level.'">'.strtoupper($name)."</span>";
- }
- }
- else
- {
- $string .= "<tr><td colspan=\"5\" style=\"text-align:center; background-color:black; border-top: 1px solid #ccc; padding:0.2em;\">";
- foreach(self::$_levelValues as $name => $level)
- {
- $string .= "<span style=\"color:white; border:1px solid white; background-color:".$this->getColorLevel($level);
- $string .= ";margin: 0.5em; padding:0.01em;\">".strtoupper($name)."</span>";
- }
- }
- $string .= '</td></tr></table>';
- return $string;
- }
-}
-
-
-/**
- * TDbLogRoute class
- *
- * TDbLogRoute stores log messages in a database table.
- * To specify the database table, set {@link setConnectionID ConnectionID} to be
- * the ID of a {@link TDataSourceConfig} module and {@link setLogTableName LogTableName}.
- * If they are not setting, an SQLite3 database named 'sqlite3.log' will be created and used
- * under the runtime directory.
- *
- * By default, the database table name is 'pradolog'. It has the following structure:
- * <code>
- * CREATE TABLE pradolog
- * (
- * log_id INTEGER NOT NULL PRIMARY KEY,
- * level INTEGER,
- * category VARCHAR(128),
- * logtime VARCHAR(20),
- * message VARCHAR(255)
- * );
- * </code>
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @version $Id$
- * @package System.Util
- * @since 3.1.2
- */
-class TDbLogRoute extends TLogRoute
-{
- /**
- * @var string the ID of TDataSourceConfig module
- */
- private $_connID='';
- /**
- * @var TDbConnection the DB connection instance
- */
- private $_db;
- /**
- * @var string name of the DB log table
- */
- private $_logTable='pradolog';
- /**
- * @var boolean whether the log DB table should be created automatically
- */
- private $_autoCreate=true;
-
- /**
- * Destructor.
- * Disconnect the db connection.
- */
- public function __destruct()
- {
- if($this->_db!==null)
- $this->_db->setActive(false);
- }
-
- /**
- * Initializes this module.
- * This method is required by the IModule interface.
- * It initializes the database for logging purpose.
- * @param TXmlElement configuration for this module, can be null
- * @throws TConfigurationException if the DB table does not exist.
- */
- public function init($config)
- {
- $db=$this->getDbConnection();
- $db->setActive(true);
-
- $sql='SELECT * FROM '.$this->_logTable.' WHERE 0=1';
- try
- {
- $db->createCommand($sql)->query()->close();
- }
- catch(Exception $e)
- {
- // DB table not exists
- if($this->_autoCreate)
- $this->createDbTable();
- else
- throw new TConfigurationException('db_logtable_inexistent',$this->_logTable);
- }
-
- parent::init($config);
- }
-
- /**
- * Stores log messages into database.
- * @param array list of log messages
- */
- protected function processLogs($logs)
- {
- $sql='INSERT INTO '.$this->_logTable.'(level, category, logtime, message) VALUES (:level, :category, :logtime, :message)';
- $command=$this->getDbConnection()->createCommand($sql);
- foreach($logs as $log)
- {
- $command->bindValue(':message',$log[0]);
- $command->bindValue(':level',$log[1]);
- $command->bindValue(':category',$log[2]);
- $command->bindValue(':logtime',$log[3]);
- $command->execute();
- }
- }
-
- /**
- * Creates the DB table for storing log messages.
- * @todo create sequence for PostgreSQL
- */
- protected function createDbTable()
- {
- $db = $this->getDbConnection();
- $driver=$db->getDriverName();
- $autoidAttributes = '';
- if($driver==='mysql')
- $autoidAttributes = 'AUTO_INCREMENT';
-
- $sql='CREATE TABLE '.$this->_logTable.' (
- log_id INTEGER NOT NULL PRIMARY KEY ' . $autoidAttributes . ',
- level INTEGER,
- category VARCHAR(128),
- logtime VARCHAR(20),
- message VARCHAR(255))';
- $db->createCommand($sql)->execute();
- }
-
- /**
- * Creates the DB connection.
- * @param string the module ID for TDataSourceConfig
- * @return TDbConnection the created DB connection
- * @throws TConfigurationException if module ID is invalid or empty
- */
- protected function createDbConnection()
- {
- if($this->_connID!=='')
- {
- $config=$this->getApplication()->getModule($this->_connID);
- if($config instanceof TDataSourceConfig)
- return $config->getDbConnection();
- else
- throw new TConfigurationException('dblogroute_connectionid_invalid',$this->_connID);
- }
- else
- {
- $db=new TDbConnection;
- // default to SQLite3 database
- $dbFile=$this->getApplication()->getRuntimePath().'/sqlite3.log';
- $db->setConnectionString('sqlite:'.$dbFile);
- return $db;
- }
- }
-
- /**
- * @return TDbConnection the DB connection instance
- */
- public function getDbConnection()
- {
- if($this->_db===null)
- $this->_db=$this->createDbConnection();
- return $this->_db;
- }
-
- /**
- * @return string the ID of a {@link TDataSourceConfig} module. Defaults to empty string, meaning not set.
- */
- public function getConnectionID()
- {
- return $this->_connID;
- }
-
- /**
- * Sets the ID of a TDataSourceConfig module.
- * The datasource module will be used to establish the DB connection for this log route.
- * @param string ID of the {@link TDataSourceConfig} module
- */
- public function setConnectionID($value)
- {
- $this->_connID=$value;
- }
-
- /**
- * @return string the name of the DB table to store log content. Defaults to 'pradolog'.
- * @see setAutoCreateLogTable
- */
- public function getLogTableName()
- {
- return $this->_logTable;
- }
-
- /**
- * Sets the name of the DB table to store log content.
- * Note, if {@link setAutoCreateLogTable AutoCreateLogTable} is false
- * and you want to create the DB table manually by yourself,
- * you need to make sure the DB table is of the following structure:
- * (key CHAR(128) PRIMARY KEY, value BLOB, expire INT)
- * @param string the name of the DB table to store log content
- * @see setAutoCreateLogTable
- */
- public function setLogTableName($value)
- {
- $this->_logTable=$value;
- }
-
- /**
- * @return boolean whether the log DB table should be automatically created if not exists. Defaults to true.
- * @see setAutoCreateLogTable
- */
- public function getAutoCreateLogTable()
- {
- return $this->_autoCreate;
- }
-
- /**
- * @param boolean whether the log DB table should be automatically created if not exists.
- * @see setLogTableName
- */
- public function setAutoCreateLogTable($value)
- {
- $this->_autoCreate=TPropertyValue::ensureBoolean($value);
- }
-
-}
-
-/**
- * TFirebugLogRoute class.
- *
- * TFirebugLogRoute prints selected log messages in the firebug log console.
- *
- * {@link http://www.getfirebug.com/ FireBug Website}
- *
- * @author Enrico Stahn <mail@enricostahn.com>, Christophe Boulain <Christophe.Boulain@gmail.com>
- * @version $Id$
- * @package System.Util
- * @since 3.1.2
- */
-class TFirebugLogRoute extends TBrowserLogRoute
-{
- protected function renderHeader ()
- {
- $string = <<<EOD
-
-<script type="text/javascript">
-/*<![CDATA[*/
-if (typeof(console) == 'object')
-{
- console.log ("[Cumulated Time] [Time] [Level] [Category] [Message]");
-
-EOD;
-
- return $string;
- }
-
- protected function renderMessage ($log, $info)
- {
- $logfunc = $this->getFirebugLoggingFunction($log[1]);
- $total = sprintf('%0.6f', $info['total']);
- $delta = sprintf('%0.6f', $info['delta']);
- $msg = trim($this->formatLogMessage($log[0], $log[1], $log[2], ''));
- $msg = preg_replace('/\(line[^\)]+\)$/', '', $msg); //remove line number info
- $msg = "[{$total}] [{$delta}] ".$msg; // Add time spent and cumulated time spent
- $string = $logfunc . '(\'' . addslashes($msg) . '\');' . "\n";
-
- return $string;
- }
-
-
- protected function renderFooter ()
- {
- $string = <<<EOD
-
-}
-</script>
-
-EOD;
-
- return $string;
- }
-
- protected function getFirebugLoggingFunction($level)
- {
- switch ($level)
- {
- case TLogger::DEBUG:
- case TLogger::INFO:
- case TLogger::NOTICE:
- return 'console.log';
- case TLogger::WARNING:
- return 'console.warn';
- case TLogger::ERROR:
- case TLogger::ALERT:
- case TLogger::FATAL:
- return 'console.error';
- default:
- return 'console.log';
- }
- }
-
-}
-
-/**
- * TFirePhpLogRoute class.
- *
- * TFirePhpLogRoute prints log messages in the firebug log console via firephp.
- *
- * {@link http://www.getfirebug.com/ FireBug Website}
- * {@link http://www.firephp.org/ FirePHP Website}
- *
- * @author Yves Berkholz <godzilla80[at]gmx[dot]net>
- * @version $Id$
- * @package System.Util
- * @since 3.1.5
- */
-class TFirePhpLogRoute extends TLogRoute
-{
- /**
- * Default group label
- */
- const DEFAULT_LABEL = 'System.Util.TLogRouter(TFirePhpLogRoute)';
-
- private $_groupLabel = null;
-
- public function processLogs($logs)
- {
- if(empty($logs) || $this->getApplication()->getMode()==='Performance')
- return;
-
- if(headers_sent()) {
- echo '
- <div style="width:100%; background-color:darkred; color:#FFF; padding:2px">
- TFirePhpLogRoute.GroupLabel "<i>' . $this -> getGroupLabel() . '</i>" -
- Routing to FirePHP impossible, because headers already sent!
- </div>
- ';
- $fallback = new TBrowserLogRoute();
- $fallback->processLogs($logs);
- return;
- }
-
- require_once Prado::getPathOfNamespace('System.3rdParty.FirePHPCore') . '/FirePHP.class.php';
- $firephp = FirePHP::getInstance(true);
- $firephp->setOptions(array('useNativeJsonEncode' => false));
- $firephp->group($this->getGroupLabel(), array('Collapsed' => true));
- $firephp->log('Time, Message');
-
- $first = $logs[0][3];
- $c = count($logs);
- for($i=0,$n=$c;$i<$n;++$i)
- {
- $message = $logs[$i][0];
- $level = $logs[$i][1];
- $category = $logs[$i][2];
-
- if ($i<$n-1)
- {
- $delta = $logs[$i+1][3] - $logs[$i][3];
- $total = $logs[$i+1][3] - $first;
- }
- else
- {
- $delta = '?';
- $total = $logs[$i][3] - $first;
- }
-
- $message = sPrintF('+%0.6f: %s', $delta, preg_replace('/\(line[^\)]+\)$/', '', $message));
- $firephp->fb($message, $category, self::translateLogLevel($level));
- }
- $firephp->log(sPrintF('%0.6f', $total), 'Cumulated Time');
- $firephp->groupEnd();
- }
-
- /**
- * Translates a PRADO log level attribute into one understood by FirePHP for correct visualization
- * @param string prado log level
- * @return string FirePHP log level
- */
- protected static function translateLogLevel($level)
- {
- switch($level)
- {
- case TLogger::INFO:
- return FirePHP::INFO;
- case TLogger::DEBUG:
- case TLogger::NOTICE:
- return FirePHP::LOG;
- case TLogger::WARNING:
- return FirePHP::WARN;
- case TLogger::ERROR:
- case TLogger::ALERT:
- case TLogger::FATAL:
- return FirePHP::ERROR;
- default:
- return FirePHP::LOG;
- }
- }
-
- /**
- * @return string group label. Defaults to TFirePhpLogRoute::DEFAULT_LABEL
- */
- public function getGroupLabel()
- {
- if($this->_groupLabel===null)
- $this->_groupLabel=self::DEFAULT_LABEL;
-
- return $this->_groupLabel;
- }
-
- /**
- * @param string group label.
- */
- public function setGroupLabel($value)
- {
- $this->_groupLabel=$value;
- }
-}
+<?php
+/**
+ * TLogRouter, TLogRoute, TFileLogRoute, TEmailLogRoute class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Util
+ */
+
+Prado::using('System.Data.TDbConnection');
+
+/**
+ * TLogRouter class.
+ *
+ * TLogRouter manages routes that record log messages in different media different ways.
+ * For example, a file log route {@link TFileLogRoute} records log messages
+ * in log files. An email log route {@link TEmailLogRoute} sends log messages
+ * to email addresses.
+ *
+ * Log routes may be configured in application or page folder configuration files
+ * or an external configuration file specified by {@link setConfigFile ConfigFile}.
+ * The format is as follows,
+ * <code>
+ * <route class="TFileLogRoute" Categories="System.Web.UI" Levels="Warning" />
+ * <route class="TEmailLogRoute" Categories="Application" Levels="Fatal" Emails="admin@pradosoft.com" />
+ * </code>
+ * PHP configuration style:
+ * <code>
+ *
+ * </code>
+ * You can specify multiple routes with different filtering conditions and different
+ * targets, even if the routes are of the same type.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @author Carl G. Mathisen <carlgmathisen@gmail.com>
+ * @version $Id$
+ * @package System.Util
+ * @since 3.0
+ */
+class TLogRouter extends TModule
+{
+ /**
+ * @var array list of routes available
+ */
+ private $_routes=array();
+ /**
+ * @var string external configuration file
+ */
+ private $_configFile=null;
+
+ /**
+ * Initializes this module.
+ * This method is required by the IModule interface.
+ * @param mixed configuration for this module, can be null
+ * @throws TConfigurationException if {@link getConfigFile ConfigFile} is invalid.
+ */
+ public function init($config)
+ {
+ if($this->_configFile!==null)
+ {
+ if(is_file($this->_configFile))
+ {
+ if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP)
+ {
+ $phpConfig = include $this->_configFile;
+ $this->loadConfig($phpConfig);
+ }
+ else
+ {
+ $dom=new TXmlDocument;
+ $dom->loadFromFile($this->_configFile);
+ $this->loadConfig($dom);
+ }
+ }
+ else
+ throw new TConfigurationException('logrouter_configfile_invalid',$this->_configFile);
+ }
+ $this->loadConfig($config);
+ $this->getApplication()->attachEventHandler('OnEndRequest',array($this,'collectLogs'));
+ }
+
+ /**
+ * Loads configuration from an XML element or PHP array
+ * @param mixed configuration node
+ * @throws TConfigurationException if log route class or type is not specified
+ */
+ private function loadConfig($config)
+ {
+ if(is_array($config))
+ {
+ if(isset($config['routes']) && is_array($config['routes']))
+ {
+ foreach($config['routes'] as $route)
+ {
+ $properties = isset($route['properties'])?$route['properties']:array();
+ if(!isset($route['class']))
+ throw new TConfigurationException('logrouter_routeclass_required');
+ $route=Prado::createComponent($route['class']);
+ if(!($route instanceof TLogRoute))
+ throw new TConfigurationException('logrouter_routetype_invalid');
+ foreach($properties as $name=>$value)
+ $route->setSubproperty($name,$value);
+ $this->_routes[]=$route;
+ $route->init($route);
+ }
+ }
+ }
+ else
+ {
+ foreach($config->getElementsByTagName('route') as $routeConfig)
+ {
+ $properties=$routeConfig->getAttributes();
+ if(($class=$properties->remove('class'))===null)
+ throw new TConfigurationException('logrouter_routeclass_required');
+ $route=Prado::createComponent($class);
+ if(!($route instanceof TLogRoute))
+ throw new TConfigurationException('logrouter_routetype_invalid');
+ foreach($properties as $name=>$value)
+ $route->setSubproperty($name,$value);
+ $this->_routes[]=$route;
+ $route->init($routeConfig);
+ }
+ }
+ }
+
+ /**
+ * Adds a TLogRoute instance to the log router.
+ *
+ * @param TLogRoute $route
+ * @throws TInvalidDataTypeException if the route object is invalid
+ */
+ public function addRoute($route)
+ {
+ if(!($route instanceof TLogRoute))
+ throw new TInvalidDataTypeException('logrouter_routetype_invalid');
+ $this->_routes[]=$route;
+ $route->init(null);
+ }
+
+ /**
+ * @return string external configuration file. Defaults to null.
+ */
+ public function getConfigFile()
+ {
+ return $this->_configFile;
+ }
+
+ /**
+ * @param string external configuration file in namespace format. The file
+ * must be suffixed with '.xml'.
+ * @throws TConfigurationException if the file is invalid.
+ */
+ public function setConfigFile($value)
+ {
+ if(($this->_configFile=Prado::getPathOfNamespace($value,$this->getApplication()->getConfigurationFileExt()))===null)
+ throw new TConfigurationException('logrouter_configfile_invalid',$value);
+ }
+
+ /**
+ * Collects log messages from a logger.
+ * This method is an event handler to application's EndRequest event.
+ * @param mixed event parameter
+ */
+ public function collectLogs($param)
+ {
+ $logger=Prado::getLogger();
+ foreach($this->_routes as $route)
+ $route->collectLogs($logger);
+ }
+}
+
+/**
+ * TLogRoute class.
+ *
+ * TLogRoute is the base class for all log route classes.
+ * A log route object retrieves log messages from a logger and sends it
+ * somewhere, such as files, emails.
+ * The messages being retrieved may be filtered first before being sent
+ * to the destination. The filters include log level filter and log category filter.
+ *
+ * To specify level filter, set {@link setLevels Levels} property,
+ * which takes a string of comma-separated desired level names (e.g. 'Error, Debug').
+ * To specify category filter, set {@link setCategories Categories} property,
+ * which takes a string of comma-separated desired category names (e.g. 'System.Web, System.IO').
+ *
+ * Level filter and category filter are combinational, i.e., only messages
+ * satisfying both filter conditions will they be returned.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id$
+ * @package System.Util
+ * @since 3.0
+ */
+abstract class TLogRoute extends TApplicationComponent
+{
+ /**
+ * @var array lookup table for level names
+ */
+ protected static $_levelNames=array(
+ TLogger::DEBUG=>'Debug',
+ TLogger::INFO=>'Info',
+ TLogger::NOTICE=>'Notice',
+ TLogger::WARNING=>'Warning',
+ TLogger::ERROR=>'Error',
+ TLogger::ALERT=>'Alert',
+ TLogger::FATAL=>'Fatal'
+ );
+ /**
+ * @var array lookup table for level values
+ */
+ protected static $_levelValues=array(
+ 'debug'=>TLogger::DEBUG,
+ 'info'=>TLogger::INFO,
+ 'notice'=>TLogger::NOTICE,
+ 'warning'=>TLogger::WARNING,
+ 'error'=>TLogger::ERROR,
+ 'alert'=>TLogger::ALERT,
+ 'fatal'=>TLogger::FATAL
+ );
+ /**
+ * @var integer log level filter (bits)
+ */
+ private $_levels=null;
+ /**
+ * @var array log category filter
+ */
+ private $_categories=null;
+
+ /**
+ * Initializes the route.
+ * @param TXmlElement configurations specified in {@link TLogRouter}.
+ */
+ public function init($config)
+ {
+ }
+
+ /**
+ * @return integer log level filter
+ */
+ public function getLevels()
+ {
+ return $this->_levels;
+ }
+
+ /**
+ * @param integer|string integer log level filter (in bits). If the value is
+ * a string, it is assumed to be comma-separated level names. Valid level names
+ * include 'Debug', 'Info', 'Notice', 'Warning', 'Error', 'Alert' and 'Fatal'.
+ */
+ public function setLevels($levels)
+ {
+ if(is_integer($levels))
+ $this->_levels=$levels;
+ else
+ {
+ $this->_levels=null;
+ $levels=strtolower($levels);
+ foreach(explode(',',$levels) as $level)
+ {
+ $level=trim($level);
+ if(isset(self::$_levelValues[$level]))
+ $this->_levels|=self::$_levelValues[$level];
+ }
+ }
+ }
+
+ /**
+ * @return array list of categories to be looked for
+ */
+ public function getCategories()
+ {
+ return $this->_categories;
+ }
+
+ /**
+ * @param array|string list of categories to be looked for. If the value is a string,
+ * it is assumed to be comma-separated category names.
+ */
+ public function setCategories($categories)
+ {
+ if(is_array($categories))
+ $this->_categories=$categories;
+ else
+ {
+ $this->_categories=null;
+ foreach(explode(',',$categories) as $category)
+ {
+ if(($category=trim($category))!=='')
+ $this->_categories[]=$category;
+ }
+ }
+ }
+
+ /**
+ * @param integer level value
+ * @return string level name
+ */
+ protected function getLevelName($level)
+ {
+ return isset(self::$_levelNames[$level])?self::$_levelNames[$level]:'Unknown';
+ }
+
+ /**
+ * @param string level name
+ * @return integer level value
+ */
+ protected function getLevelValue($level)
+ {
+ return isset(self::$_levelValues[$level])?self::$_levelValues[$level]:0;
+ }
+
+ /**
+ * Formats a log message given different fields.
+ * @param string message content
+ * @param integer message level
+ * @param string message category
+ * @param integer timestamp
+ * @return string formatted message
+ */
+ protected function formatLogMessage($message,$level,$category,$time)
+ {
+ return @gmdate('M d H:i:s',$time).' ['.$this->getLevelName($level).'] ['.$category.'] '.$message."\n";
+ }
+
+ /**
+ * Retrieves log messages from logger to log route specific destination.
+ * @param TLogger logger instance
+ */
+ public function collectLogs(TLogger $logger)
+ {
+ $logs=$logger->getLogs($this->getLevels(),$this->getCategories());
+ if(!empty($logs))
+ $this->processLogs($logs);
+ }
+
+ /**
+ * Processes log messages and sends them to specific destination.
+ * Derived child classes must implement this method.
+ * @param array list of messages. Each array elements represents one message
+ * with the following structure:
+ * array(
+ * [0] => message
+ * [1] => level
+ * [2] => category
+ * [3] => timestamp);
+ */
+ abstract protected function processLogs($logs);
+}
+
+/**
+ * TFileLogRoute class.
+ *
+ * TFileLogRoute records log messages in files.
+ * The log files are stored under {@link setLogPath LogPath} and the file name
+ * is specified by {@link setLogFile LogFile}. If the size of the log file is
+ * greater than {@link setMaxFileSize MaxFileSize} (in kilo-bytes), a rotation
+ * is performed, which renames the current log file by suffixing the file name
+ * with '.1'. All existing log files are moved backwards one place, i.e., '.2'
+ * to '.3', '.1' to '.2'. The property {@link setMaxLogFiles MaxLogFiles}
+ * specifies how many files to be kept.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id$
+ * @package System.Util
+ * @since 3.0
+ */
+class TFileLogRoute extends TLogRoute
+{
+ /**
+ * @var integer maximum log file size
+ */
+ private $_maxFileSize=512; // in KB
+ /**
+ * @var integer number of log files used for rotation
+ */
+ private $_maxLogFiles=2;
+ /**
+ * @var string directory storing log files
+ */
+ private $_logPath=null;
+ /**
+ * @var string log file name
+ */
+ private $_logFile='prado.log';
+
+ /**
+ * @return string directory storing log files. Defaults to application runtime path.
+ */
+ public function getLogPath()
+ {
+ if($this->_logPath===null)
+ $this->_logPath=$this->getApplication()->getRuntimePath();
+ return $this->_logPath;
+ }
+
+ /**
+ * @param string directory (in namespace format) storing log files.
+ * @throws TConfigurationException if log path is invalid
+ */
+ public function setLogPath($value)
+ {
+ if(($this->_logPath=Prado::getPathOfNamespace($value))===null || !is_dir($this->_logPath) || !is_writable($this->_logPath))
+ throw new TConfigurationException('filelogroute_logpath_invalid',$value);
+ }
+
+ /**
+ * @return string log file name. Defaults to 'prado.log'.
+ */
+ public function getLogFile()
+ {
+ return $this->_logFile;
+ }
+
+ /**
+ * @param string log file name
+ */
+ public function setLogFile($value)
+ {
+ $this->_logFile=$value;
+ }
+
+ /**
+ * @return integer maximum log file size in kilo-bytes (KB). Defaults to 1024 (1MB).
+ */
+ public function getMaxFileSize()
+ {
+ return $this->_maxFileSize;
+ }
+
+ /**
+ * @param integer maximum log file size in kilo-bytes (KB).
+ * @throws TInvalidDataValueException if the value is smaller than 1.
+ */
+ public function setMaxFileSize($value)
+ {
+ $this->_maxFileSize=TPropertyValue::ensureInteger($value);
+ if($this->_maxFileSize<=0)
+ throw new TInvalidDataValueException('filelogroute_maxfilesize_invalid');
+ }
+
+ /**
+ * @return integer number of files used for rotation. Defaults to 2.
+ */
+ public function getMaxLogFiles()
+ {
+ return $this->_maxLogFiles;
+ }
+
+ /**
+ * @param integer number of files used for rotation.
+ */
+ public function setMaxLogFiles($value)
+ {
+ $this->_maxLogFiles=TPropertyValue::ensureInteger($value);
+ if($this->_maxLogFiles<1)
+ throw new TInvalidDataValueException('filelogroute_maxlogfiles_invalid');
+ }
+
+ /**
+ * Saves log messages in files.
+ * @param array list of log messages
+ */
+ protected function processLogs($logs)
+ {
+ $logFile=$this->getLogPath().DIRECTORY_SEPARATOR.$this->getLogFile();
+ if(@filesize($logFile)>$this->_maxFileSize*1024)
+ $this->rotateFiles();
+ foreach($logs as $log)
+ error_log($this->formatLogMessage($log[0],$log[1],$log[2],$log[3]),3,$logFile);
+ }
+
+ /**
+ * Rotates log files.
+ */
+ protected function rotateFiles()
+ {
+ $file=$this->getLogPath().DIRECTORY_SEPARATOR.$this->getLogFile();
+ for($i=$this->_maxLogFiles;$i>0;--$i)
+ {
+ $rotateFile=$file.'.'.$i;
+ if(is_file($rotateFile))
+ {
+ if($i===$this->_maxLogFiles)
+ unlink($rotateFile);
+ else
+ rename($rotateFile,$file.'.'.($i+1));
+ }
+ }
+ if(is_file($file))
+ rename($file,$file.'.1');
+ }
+}
+
+/**
+ * TEmailLogRoute class.
+ *
+ * TEmailLogRoute sends selected log messages to email addresses.
+ * The target email addresses may be specified via {@link setEmails Emails} property.
+ * Optionally, you may set the email {@link setSubject Subject} and the
+ * {@link setSentFrom SentFrom} address.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id$
+ * @package System.Util
+ * @since 3.0
+ */
+class TEmailLogRoute extends TLogRoute
+{
+ /**
+ * Regex pattern for email address.
+ */
+ const EMAIL_PATTERN='/^([0-9a-zA-Z]+[-._+&])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}$/';
+ /**
+ * Default email subject.
+ */
+ const DEFAULT_SUBJECT='Prado Application Log';
+ /**
+ * @var array list of destination email addresses.
+ */
+ private $_emails=array();
+ /**
+ * @var string email subject
+ */
+ private $_subject='';
+ /**
+ * @var string email sent from address
+ */
+ private $_from='';
+
+ /**
+ * Initializes the route.
+ * @param TXmlElement configurations specified in {@link TLogRouter}.
+ * @throws TConfigurationException if {@link getSentFrom SentFrom} is empty and
+ * 'sendmail_from' in php.ini is also empty.
+ */
+ public function init($config)
+ {
+ if($this->_from==='')
+ $this->_from=ini_get('sendmail_from');
+ if($this->_from==='')
+ throw new TConfigurationException('emaillogroute_sentfrom_required');
+ }
+
+ /**
+ * Sends log messages to specified email addresses.
+ * @param array list of log messages
+ */
+ protected function processLogs($logs)
+ {
+ $message='';
+ foreach($logs as $log)
+ $message.=$this->formatLogMessage($log[0],$log[1],$log[2],$log[3]);
+ $message=wordwrap($message,70);
+ $returnPath = ini_get('sendmail_path') ? "Return-Path:{$this->_from}\r\n" : '';
+ foreach($this->_emails as $email)
+ mail($email,$this->getSubject(),$message,"From:{$this->_from}\r\n{$returnPath}");
+
+ }
+
+ /**
+ * @return array list of destination email addresses
+ */
+ public function getEmails()
+ {
+ return $this->_emails;
+ }
+
+ /**
+ * @return array|string list of destination email addresses. If the value is
+ * a string, it is assumed to be comma-separated email addresses.
+ */
+ public function setEmails($emails)
+ {
+ if(is_array($emails))
+ $this->_emails=$emails;
+ else
+ {
+ $this->_emails=array();
+ foreach(explode(',',$emails) as $email)
+ {
+ $email=trim($email);
+ if(preg_match(self::EMAIL_PATTERN,$email))
+ $this->_emails[]=$email;
+ }
+ }
+ }
+
+ /**
+ * @return string email subject. Defaults to TEmailLogRoute::DEFAULT_SUBJECT
+ */
+ public function getSubject()
+ {
+ if($this->_subject===null)
+ $this->_subject=self::DEFAULT_SUBJECT;
+ return $this->_subject;
+ }
+
+ /**
+ * @param string email subject.
+ */
+ public function setSubject($value)
+ {
+ $this->_subject=$value;
+ }
+
+ /**
+ * @return string send from address of the email
+ */
+ public function getSentFrom()
+ {
+ return $this->_from;
+ }
+
+ /**
+ * @param string send from address of the email
+ */
+ public function setSentFrom($value)
+ {
+ $this->_from=$value;
+ }
+}
+
+/**
+ * TBrowserLogRoute class.
+ *
+ * TBrowserLogRoute prints selected log messages in the response.
+ *
+ * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Util
+ * @since 3.0
+ */
+class TBrowserLogRoute extends TLogRoute
+{
+ /**
+ * @var string css class for indentifying the table structure in the dom tree
+ */
+ private $_cssClass=null;
+
+ public function processLogs($logs)
+ {
+ if(empty($logs) || $this->getApplication()->getMode()==='Performance') return;
+ $first = $logs[0][3];
+ $even = true;
+ $response = $this->getApplication()->getResponse();
+ $response->write($this->renderHeader());
+ for($i=0,$n=count($logs);$i<$n;++$i)
+ {
+ if ($i<$n-1)
+ {
+ $timing['delta'] = $logs[$i+1][3] - $logs[$i][3];
+ $timing['total'] = $logs[$i+1][3] - $first;
+ }
+ else
+ {
+ $timing['delta'] = '?';
+ $timing['total'] = $logs[$i][3] - $first;
+ }
+ $timing['even'] = !($even = !$even);
+ $response->write($this->renderMessage($logs[$i],$timing));
+ }
+ $response->write($this->renderFooter());
+ }
+
+ /**
+ * @param string the css class of the control
+ */
+ public function setCssClass($value)
+ {
+ $this->_cssClass = TPropertyValue::ensureString($value);
+ }
+
+ /**
+ * @return string the css class of the control
+ */
+ public function getCssClass()
+ {
+ return TPropertyValue::ensureString($this->_cssClass);
+ }
+
+ protected function renderHeader()
+ {
+ $string = '';
+ if($className=$this->getCssClass())
+ {
+ $string = <<<EOD
+<table class="$className">
+ <tr class="header">
+ <th colspan="5">
+ Application Log
+ </th>
+ </tr><tr class="description">
+ <th>&nbsp;</th>
+ <th>Category</th><th>Message</th><th>Time Spent (s)</th><th>Cumulated Time Spent (s)</th>
+ </tr>
+EOD;
+ }
+ else
+ {
+ $string = <<<EOD
+<table cellspacing="0" cellpadding="2" border="0" width="100%" style="table-layout:auto">
+ <tr>
+ <th style="background-color: black; color:white;" colspan="5">
+ Application Log
+ </th>
+ </tr><tr style="background-color: #ccc; color:black">
+ <th style="width: 15px">&nbsp;</th>
+ <th style="width: auto">Category</th><th style="width: auto">Message</th><th style="width: 120px">Time Spent (s)</th><th style="width: 150px">Cumulated Time Spent (s)</th>
+ </tr>
+EOD;
+ }
+ return $string;
+ }
+
+ protected function renderMessage($log, $info)
+ {
+ $string = '';
+ $total = sprintf('%0.6f', $info['total']);
+ $delta = sprintf('%0.6f', $info['delta']);
+ $msg = preg_replace('/\(line[^\)]+\)$/','',$log[0]); //remove line number info
+ $msg = THttpUtility::htmlEncode($msg);
+ if($this->getCssClass())
+ {
+ $colorCssClass = $log[1];
+ $messageCssClass = $info['even'] ? 'even' : 'odd';
+ $string = <<<EOD
+ <tr class="message level$colorCssClass $messageCssClass">
+ <td class="code">&nbsp;</td>
+ <td class="category">{$log[2]}</td>
+ <td class="message">{$msg}</td>
+ <td class="time">{$delta}</td>
+ <td class="cumulatedtime">{$total}</td>
+ </tr>
+EOD;
+ }
+ else
+ {
+ $bgcolor = $info['even'] ? "#fff" : "#eee";
+ $color = $this->getColorLevel($log[1]);
+ $string = <<<EOD
+ <tr style="background-color: {$bgcolor}; color:#000">
+ <td style="border:1px solid silver;background-color: $color;">&nbsp;</td>
+ <td>{$log[2]}</td>
+ <td>{$msg}</td>
+ <td style="text-align:center">{$delta}</td>
+ <td style="text-align:center">{$total}</td>
+ </tr>
+EOD;
+ }
+ return $string;
+ }
+
+ protected function getColorLevel($level)
+ {
+ switch($level)
+ {
+ case TLogger::DEBUG: return 'green';
+ case TLogger::INFO: return 'black';
+ case TLogger::NOTICE: return '#3333FF';
+ case TLogger::WARNING: return '#33FFFF';
+ case TLogger::ERROR: return '#ff9933';
+ case TLogger::ALERT: return '#ff00ff';
+ case TLogger::FATAL: return 'red';
+ }
+ return '';
+ }
+
+ protected function renderFooter()
+ {
+ $string = '';
+ if($this->getCssClass())
+ {
+ $string .= '<tr class="footer"><td colspan="5">';
+ foreach(self::$_levelValues as $name => $level)
+ {
+ $string .= '<span class="level'.$level.'">'.strtoupper($name)."</span>";
+ }
+ }
+ else
+ {
+ $string .= "<tr><td colspan=\"5\" style=\"text-align:center; background-color:black; border-top: 1px solid #ccc; padding:0.2em;\">";
+ foreach(self::$_levelValues as $name => $level)
+ {
+ $string .= "<span style=\"color:white; border:1px solid white; background-color:".$this->getColorLevel($level);
+ $string .= ";margin: 0.5em; padding:0.01em;\">".strtoupper($name)."</span>";
+ }
+ }
+ $string .= '</td></tr></table>';
+ return $string;
+ }
+}
+
+
+/**
+ * TDbLogRoute class
+ *
+ * TDbLogRoute stores log messages in a database table.
+ * To specify the database table, set {@link setConnectionID ConnectionID} to be
+ * the ID of a {@link TDataSourceConfig} module and {@link setLogTableName LogTableName}.
+ * If they are not setting, an SQLite3 database named 'sqlite3.log' will be created and used
+ * under the runtime directory.
+ *
+ * By default, the database table name is 'pradolog'. It has the following structure:
+ * <code>
+ * CREATE TABLE pradolog
+ * (
+ * log_id INTEGER NOT NULL PRIMARY KEY,
+ * level INTEGER,
+ * category VARCHAR(128),
+ * logtime VARCHAR(20),
+ * message VARCHAR(255)
+ * );
+ * </code>
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id$
+ * @package System.Util
+ * @since 3.1.2
+ */
+class TDbLogRoute extends TLogRoute
+{
+ /**
+ * @var string the ID of TDataSourceConfig module
+ */
+ private $_connID='';
+ /**
+ * @var TDbConnection the DB connection instance
+ */
+ private $_db;
+ /**
+ * @var string name of the DB log table
+ */
+ private $_logTable='pradolog';
+ /**
+ * @var boolean whether the log DB table should be created automatically
+ */
+ private $_autoCreate=true;
+
+ /**
+ * Destructor.
+ * Disconnect the db connection.
+ */
+ public function __destruct()
+ {
+ if($this->_db!==null)
+ $this->_db->setActive(false);
+ }
+
+ /**
+ * Initializes this module.
+ * This method is required by the IModule interface.
+ * It initializes the database for logging purpose.
+ * @param TXmlElement configuration for this module, can be null
+ * @throws TConfigurationException if the DB table does not exist.
+ */
+ public function init($config)
+ {
+ $db=$this->getDbConnection();
+ $db->setActive(true);
+
+ $sql='SELECT * FROM '.$this->_logTable.' WHERE 0=1';
+ try
+ {
+ $db->createCommand($sql)->query()->close();
+ }
+ catch(Exception $e)
+ {
+ // DB table not exists
+ if($this->_autoCreate)
+ $this->createDbTable();
+ else
+ throw new TConfigurationException('db_logtable_inexistent',$this->_logTable);
+ }
+
+ parent::init($config);
+ }
+
+ /**
+ * Stores log messages into database.
+ * @param array list of log messages
+ */
+ protected function processLogs($logs)
+ {
+ $sql='INSERT INTO '.$this->_logTable.'(level, category, logtime, message) VALUES (:level, :category, :logtime, :message)';
+ $command=$this->getDbConnection()->createCommand($sql);
+ foreach($logs as $log)
+ {
+ $command->bindValue(':message',$log[0]);
+ $command->bindValue(':level',$log[1]);
+ $command->bindValue(':category',$log[2]);
+ $command->bindValue(':logtime',$log[3]);
+ $command->execute();
+ }
+ }
+
+ /**
+ * Creates the DB table for storing log messages.
+ * @todo create sequence for PostgreSQL
+ */
+ protected function createDbTable()
+ {
+ $db = $this->getDbConnection();
+ $driver=$db->getDriverName();
+ $autoidAttributes = '';
+ if($driver==='mysql')
+ $autoidAttributes = 'AUTO_INCREMENT';
+
+ $sql='CREATE TABLE '.$this->_logTable.' (
+ log_id INTEGER NOT NULL PRIMARY KEY ' . $autoidAttributes . ',
+ level INTEGER,
+ category VARCHAR(128),
+ logtime VARCHAR(20),
+ message VARCHAR(255))';
+ $db->createCommand($sql)->execute();
+ }
+
+ /**
+ * Creates the DB connection.
+ * @param string the module ID for TDataSourceConfig
+ * @return TDbConnection the created DB connection
+ * @throws TConfigurationException if module ID is invalid or empty
+ */
+ protected function createDbConnection()
+ {
+ if($this->_connID!=='')
+ {
+ $config=$this->getApplication()->getModule($this->_connID);
+ if($config instanceof TDataSourceConfig)
+ return $config->getDbConnection();
+ else
+ throw new TConfigurationException('dblogroute_connectionid_invalid',$this->_connID);
+ }
+ else
+ {
+ $db=new TDbConnection;
+ // default to SQLite3 database
+ $dbFile=$this->getApplication()->getRuntimePath().'/sqlite3.log';
+ $db->setConnectionString('sqlite:'.$dbFile);
+ return $db;
+ }
+ }
+
+ /**
+ * @return TDbConnection the DB connection instance
+ */
+ public function getDbConnection()
+ {
+ if($this->_db===null)
+ $this->_db=$this->createDbConnection();
+ return $this->_db;
+ }
+
+ /**
+ * @return string the ID of a {@link TDataSourceConfig} module. Defaults to empty string, meaning not set.
+ */
+ public function getConnectionID()
+ {
+ return $this->_connID;
+ }
+
+ /**
+ * Sets the ID of a TDataSourceConfig module.
+ * The datasource module will be used to establish the DB connection for this log route.
+ * @param string ID of the {@link TDataSourceConfig} module
+ */
+ public function setConnectionID($value)
+ {
+ $this->_connID=$value;
+ }
+
+ /**
+ * @return string the name of the DB table to store log content. Defaults to 'pradolog'.
+ * @see setAutoCreateLogTable
+ */
+ public function getLogTableName()
+ {
+ return $this->_logTable;
+ }
+
+ /**
+ * Sets the name of the DB table to store log content.
+ * Note, if {@link setAutoCreateLogTable AutoCreateLogTable} is false
+ * and you want to create the DB table manually by yourself,
+ * you need to make sure the DB table is of the following structure:
+ * (key CHAR(128) PRIMARY KEY, value BLOB, expire INT)
+ * @param string the name of the DB table to store log content
+ * @see setAutoCreateLogTable
+ */
+ public function setLogTableName($value)
+ {
+ $this->_logTable=$value;
+ }
+
+ /**
+ * @return boolean whether the log DB table should be automatically created if not exists. Defaults to true.
+ * @see setAutoCreateLogTable
+ */
+ public function getAutoCreateLogTable()
+ {
+ return $this->_autoCreate;
+ }
+
+ /**
+ * @param boolean whether the log DB table should be automatically created if not exists.
+ * @see setLogTableName
+ */
+ public function setAutoCreateLogTable($value)
+ {
+ $this->_autoCreate=TPropertyValue::ensureBoolean($value);
+ }
+
+}
+
+/**
+ * TFirebugLogRoute class.
+ *
+ * TFirebugLogRoute prints selected log messages in the firebug log console.
+ *
+ * {@link http://www.getfirebug.com/ FireBug Website}
+ *
+ * @author Enrico Stahn <mail@enricostahn.com>, Christophe Boulain <Christophe.Boulain@gmail.com>
+ * @version $Id$
+ * @package System.Util
+ * @since 3.1.2
+ */
+class TFirebugLogRoute extends TBrowserLogRoute
+{
+ protected function renderHeader ()
+ {
+ $string = <<<EOD
+
+<script type="text/javascript">
+/*<![CDATA[*/
+if (typeof(console) == 'object')
+{
+ console.log ("[Cumulated Time] [Time] [Level] [Category] [Message]");
+
+EOD;
+
+ return $string;
+ }
+
+ protected function renderMessage ($log, $info)
+ {
+ $logfunc = $this->getFirebugLoggingFunction($log[1]);
+ $total = sprintf('%0.6f', $info['total']);
+ $delta = sprintf('%0.6f', $info['delta']);
+ $msg = trim($this->formatLogMessage($log[0], $log[1], $log[2], ''));
+ $msg = preg_replace('/\(line[^\)]+\)$/', '', $msg); //remove line number info
+ $msg = "[{$total}] [{$delta}] ".$msg; // Add time spent and cumulated time spent
+ $string = $logfunc . '(\'' . addslashes($msg) . '\');' . "\n";
+
+ return $string;
+ }
+
+
+ protected function renderFooter ()
+ {
+ $string = <<<EOD
+
+}
+</script>
+
+EOD;
+
+ return $string;
+ }
+
+ protected function getFirebugLoggingFunction($level)
+ {
+ switch ($level)
+ {
+ case TLogger::DEBUG:
+ case TLogger::INFO:
+ case TLogger::NOTICE:
+ return 'console.log';
+ case TLogger::WARNING:
+ return 'console.warn';
+ case TLogger::ERROR:
+ case TLogger::ALERT:
+ case TLogger::FATAL:
+ return 'console.error';
+ default:
+ return 'console.log';
+ }
+ }
+
+}
+
+/**
+ * TFirePhpLogRoute class.
+ *
+ * TFirePhpLogRoute prints log messages in the firebug log console via firephp.
+ *
+ * {@link http://www.getfirebug.com/ FireBug Website}
+ * {@link http://www.firephp.org/ FirePHP Website}
+ *
+ * @author Yves Berkholz <godzilla80[at]gmx[dot]net>
+ * @version $Id$
+ * @package System.Util
+ * @since 3.1.5
+ */
+class TFirePhpLogRoute extends TLogRoute
+{
+ /**
+ * Default group label
+ */
+ const DEFAULT_LABEL = 'System.Util.TLogRouter(TFirePhpLogRoute)';
+
+ private $_groupLabel = null;
+
+ public function processLogs($logs)
+ {
+ if(empty($logs) || $this->getApplication()->getMode()==='Performance')
+ return;
+
+ if(headers_sent()) {
+ echo '
+ <div style="width:100%; background-color:darkred; color:#FFF; padding:2px">
+ TFirePhpLogRoute.GroupLabel "<i>' . $this -> getGroupLabel() . '</i>" -
+ Routing to FirePHP impossible, because headers already sent!
+ </div>
+ ';
+ $fallback = new TBrowserLogRoute();
+ $fallback->processLogs($logs);
+ return;
+ }
+
+ require_once Prado::getPathOfNamespace('System.3rdParty.FirePHPCore') . '/FirePHP.class.php';
+ $firephp = FirePHP::getInstance(true);
+ $firephp->setOptions(array('useNativeJsonEncode' => false));
+ $firephp->group($this->getGroupLabel(), array('Collapsed' => true));
+ $firephp->log('Time, Message');
+
+ $first = $logs[0][3];
+ $c = count($logs);
+ for($i=0,$n=$c;$i<$n;++$i)
+ {
+ $message = $logs[$i][0];
+ $level = $logs[$i][1];
+ $category = $logs[$i][2];
+
+ if ($i<$n-1)
+ {
+ $delta = $logs[$i+1][3] - $logs[$i][3];
+ $total = $logs[$i+1][3] - $first;
+ }
+ else
+ {
+ $delta = '?';
+ $total = $logs[$i][3] - $first;
+ }
+
+ $message = sPrintF('+%0.6f: %s', $delta, preg_replace('/\(line[^\)]+\)$/', '', $message));
+ $firephp->fb($message, $category, self::translateLogLevel($level));
+ }
+ $firephp->log(sPrintF('%0.6f', $total), 'Cumulated Time');
+ $firephp->groupEnd();
+ }
+
+ /**
+ * Translates a PRADO log level attribute into one understood by FirePHP for correct visualization
+ * @param string prado log level
+ * @return string FirePHP log level
+ */
+ protected static function translateLogLevel($level)
+ {
+ switch($level)
+ {
+ case TLogger::INFO:
+ return FirePHP::INFO;
+ case TLogger::DEBUG:
+ case TLogger::NOTICE:
+ return FirePHP::LOG;
+ case TLogger::WARNING:
+ return FirePHP::WARN;
+ case TLogger::ERROR:
+ case TLogger::ALERT:
+ case TLogger::FATAL:
+ return FirePHP::ERROR;
+ default:
+ return FirePHP::LOG;
+ }
+ }
+
+ /**
+ * @return string group label. Defaults to TFirePhpLogRoute::DEFAULT_LABEL
+ */
+ public function getGroupLabel()
+ {
+ if($this->_groupLabel===null)
+ $this->_groupLabel=self::DEFAULT_LABEL;
+
+ return $this->_groupLabel;
+ }
+
+ /**
+ * @param string group label.
+ */
+ public function setGroupLabel($value)
+ {
+ $this->_groupLabel=$value;
+ }
+}
diff --git a/framework/Util/TLogger.php b/framework/Util/TLogger.php
index 97334629..6ad5df91 100644
--- a/framework/Util/TLogger.php
+++ b/framework/Util/TLogger.php
@@ -1,236 +1,236 @@
-<?php
-/**
- * TLogger class file
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright &copy; 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Util
- */
-
-/**
- * TLogger class.
- *
- * TLogger records log messages in memory and implements the methods to
- * retrieve the messages with filter conditions, including log levels,
- * log categories, and by control.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @version $Id$
- * @package System.Util
- * @since 3.0
- */
-class TLogger extends TComponent
-{
- /**
- * Log levels.
- */
- const DEBUG=0x01;
- const INFO=0x02;
- const NOTICE=0x04;
- const WARNING=0x08;
- const ERROR=0x10;
- const ALERT=0x20;
- const FATAL=0x40;
- /**
- * @var array log messages
- */
- private $_logs=array();
- /**
- * @var integer log levels (bits) to be filtered
- */
- private $_levels;
- /**
- * @var array list of categories to be filtered
- */
- private $_categories;
- /**
- * @var array list of control client ids to be filtered
- */
- private $_controls;
- /**
- * @var float timestamp used to filter
- */
- private $_timestamp;
-
- /**
- * Logs a message.
- * Messages logged by this method may be retrieved via {@link getLogs}.
- * @param string message to be logged
- * @param integer level of the message. Valid values include
- * TLogger::DEBUG, TLogger::INFO, TLogger::NOTICE, TLogger::WARNING,
- * TLogger::ERROR, TLogger::ALERT, TLogger::FATAL.
- * @param string category of the message
- * @param string|TControl control of the message
- */
- public function log($message,$level,$category='Uncategorized', $ctl=null)
- {
- if($ctl) {
- if($ctl instanceof TControl)
- $ctl = $ctl->ClientId;
- else if(!is_string($ctl))
- $ctl = null;
- } else
- $ctl = null;
- $this->_logs[]=array($message,$level,$category,microtime(true),memory_get_usage(),$ctl);
- }
-
- /**
- * Retrieves log messages.
- * Messages may be filtered by log levels and/or categories and/or control client ids and/or timestamp.
- * A level filter is specified by an integer, whose bits indicate the levels interested.
- * For example, (TLogger::INFO | TLogger::WARNING) specifies INFO and WARNING levels.
- * A category filter is specified by an array of categories to filter.
- * A message whose category name starts with any filtering category
- * will be returned. For example, a category filter array('System.Web','System.IO')
- * will return messages under categories such as 'System.Web', 'System.IO',
- * 'System.Web.UI', 'System.Web.UI.WebControls', etc.
- * A control client id filter is specified by an array of control client id
- * A message whose control client id starts with any filtering naming panels
- * will be returned. For example, a category filter array('ctl0_body_header',
- * 'ctl0_body_content_sidebar')
- * will return messages under categories such as 'ctl0_body_header', 'ctl0_body_content_sidebar',
- * 'ctl0_body_header_title', 'ctl0_body_content_sidebar_savebutton', etc.
- * A timestamp filter is specified as an interger or float number.
- * A message whose registered timestamp is less or equal the filter value will be returned.
- * Level filter, category filter, control filter and timestamp filter are combinational, i.e., only messages
- * satisfying all filter conditions will they be returned.
- * @param integer level filter
- * @param array category filter
- * @param array control filter
- * @return array list of messages. Each array elements represents one message
- * with the following structure:
- * array(
- * [0] => message
- * [1] => level
- * [2] => category
- * [3] => timestamp (by microtime(), float number));
- * [4] => memory in bytes
- * [5] => control client id
- */
- public function getLogs($levels=null,$categories=null,$controls=null,$timestamp=null)
- {
- $this->_levels=$levels;
- $this->_categories=$categories;
- $this->_controls=$controls;
- $this->_timestamp=$timestamp;
- if(empty($levels) && empty($categories) && empty($controls) && is_null($timestamp))
- return $this->_logs;
- $logs = $this->_logs;
- if(!empty($levels))
- $logs = array_values(array_filter( array_filter($logs,array($this,'filterByLevels')) ));
- if(!empty($categories))
- $logs = array_values(array_filter( array_filter($logs,array($this,'filterByCategories')) ));
- if(!empty($controls))
- $logs = array_values(array_filter( array_filter($logs,array($this,'filterByControl')) ));
- if(!is_null($timestamp))
- $logs = array_values(array_filter( array_filter($logs,array($this,'filterByTimeStamp')) ));
- return $logs;
- }
-
- /**
- * Deletes log messages from the queue.
- * Messages may be filtered by log levels and/or categories and/or control client ids and/or timestamp.
- * A level filter is specified by an integer, whose bits indicate the levels interested.
- * For example, (TLogger::INFO | TLogger::WARNING) specifies INFO and WARNING levels.
- * A category filter is specified by an array of categories to filter.
- * A message whose category name starts with any filtering category
- * will be deleted. For example, a category filter array('System.Web','System.IO')
- * will delete messages under categories such as 'System.Web', 'System.IO',
- * 'System.Web.UI', 'System.Web.UI.WebControls', etc.
- * A control client id filter is specified by an array of control client id
- * A message whose control client id starts with any filtering naming panels
- * will be deleted. For example, a category filter array('ctl0_body_header',
- * 'ctl0_body_content_sidebar')
- * will delete messages under categories such as 'ctl0_body_header', 'ctl0_body_content_sidebar',
- * 'ctl0_body_header_title', 'ctl0_body_content_sidebar_savebutton', etc.
- * A timestamp filter is specified as an interger or float number.
- * A message whose registered timestamp is less or equal the filter value will be returned.
- * Level filter, category filter, control filter and timestamp filter are combinational, i.e., only messages
- * satisfying all filter conditions will they be returned.
- * @param integer level filter
- * @param array category filter
- * @param array control filter
- */
- public function deleteLogs($levels=null,$categories=null,$controls=null,$timestamp=null)
- {
- $this->_levels=$levels;
- $this->_categories=$categories;
- $this->_controls=$controls;
- $this->_timestamp=$timestamp;
- if(empty($levels) && empty($categories) && empty($controls) && is_null($timestamp))
- {
- $this->_logs=array();
- return;
- }
- $logs = $this->_logs;
- if(!empty($levels))
- $logs = array_filter( array_filter($logs,array($this,'filterByLevels')) );
- if(!empty($categories))
- $logs = array_filter( array_filter($logs,array($this,'filterByCategories')) );
- if(!empty($controls))
- $logs = array_filter( array_filter($logs,array($this,'filterByControl')) );
- if(!is_null($timestamp))
- $logs = array_filter( array_filter($logs,array($this,'filterByTimeStamp')) );
- $this->_logs = array_values( array_diff_key($this->_logs, $logs) );
- }
-
- /**
- * Filter function used by {@link getLogs}.
- * @param array element to be filtered
- */
- private function filterByCategories($value)
- {
- foreach($this->_categories as $category)
- {
- // element 2 is the category
- if($value[2]===$category || strpos($value[2],$category.'.')===0)
- return $value;
- }
- return false;
- }
-
- /**
- * Filter function used by {@link getLogs}
- * @param array element to be filtered
- */
- private function filterByLevels($value)
- {
- // element 1 are the levels
- if($value[1] & $this->_levels)
- return $value;
- else
- return false;
- }
-
- /**
- * Filter function used by {@link getLogs}
- * @param array element to be filtered
- */
- private function filterByControl($value)
- {
- // element 5 are the control client ids
- foreach($this->_controls as $control)
- {
- if($value[5]===$control || strpos($value[5],$control)===0)
- return $value;
- }
- return false;
- }
-
- /**
- * Filter function used by {@link getLogs}
- * @param array element to be filtered
- */
- private function filterByTimeStamp($value)
- {
- // element 3 is the timestamp
- if($value[3] <= $this->_timestamp)
- return $value;
- else
- return false;
- }
-}
-
+<?php
+/**
+ * TLogger class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Util
+ */
+
+/**
+ * TLogger class.
+ *
+ * TLogger records log messages in memory and implements the methods to
+ * retrieve the messages with filter conditions, including log levels,
+ * log categories, and by control.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id$
+ * @package System.Util
+ * @since 3.0
+ */
+class TLogger extends TComponent
+{
+ /**
+ * Log levels.
+ */
+ const DEBUG=0x01;
+ const INFO=0x02;
+ const NOTICE=0x04;
+ const WARNING=0x08;
+ const ERROR=0x10;
+ const ALERT=0x20;
+ const FATAL=0x40;
+ /**
+ * @var array log messages
+ */
+ private $_logs=array();
+ /**
+ * @var integer log levels (bits) to be filtered
+ */
+ private $_levels;
+ /**
+ * @var array list of categories to be filtered
+ */
+ private $_categories;
+ /**
+ * @var array list of control client ids to be filtered
+ */
+ private $_controls;
+ /**
+ * @var float timestamp used to filter
+ */
+ private $_timestamp;
+
+ /**
+ * Logs a message.
+ * Messages logged by this method may be retrieved via {@link getLogs}.
+ * @param string message to be logged
+ * @param integer level of the message. Valid values include
+ * TLogger::DEBUG, TLogger::INFO, TLogger::NOTICE, TLogger::WARNING,
+ * TLogger::ERROR, TLogger::ALERT, TLogger::FATAL.
+ * @param string category of the message
+ * @param string|TControl control of the message
+ */
+ public function log($message,$level,$category='Uncategorized', $ctl=null)
+ {
+ if($ctl) {
+ if($ctl instanceof TControl)
+ $ctl = $ctl->ClientId;
+ else if(!is_string($ctl))
+ $ctl = null;
+ } else
+ $ctl = null;
+ $this->_logs[]=array($message,$level,$category,microtime(true),memory_get_usage(),$ctl);
+ }
+
+ /**
+ * Retrieves log messages.
+ * Messages may be filtered by log levels and/or categories and/or control client ids and/or timestamp.
+ * A level filter is specified by an integer, whose bits indicate the levels interested.
+ * For example, (TLogger::INFO | TLogger::WARNING) specifies INFO and WARNING levels.
+ * A category filter is specified by an array of categories to filter.
+ * A message whose category name starts with any filtering category
+ * will be returned. For example, a category filter array('System.Web','System.IO')
+ * will return messages under categories such as 'System.Web', 'System.IO',
+ * 'System.Web.UI', 'System.Web.UI.WebControls', etc.
+ * A control client id filter is specified by an array of control client id
+ * A message whose control client id starts with any filtering naming panels
+ * will be returned. For example, a category filter array('ctl0_body_header',
+ * 'ctl0_body_content_sidebar')
+ * will return messages under categories such as 'ctl0_body_header', 'ctl0_body_content_sidebar',
+ * 'ctl0_body_header_title', 'ctl0_body_content_sidebar_savebutton', etc.
+ * A timestamp filter is specified as an interger or float number.
+ * A message whose registered timestamp is less or equal the filter value will be returned.
+ * Level filter, category filter, control filter and timestamp filter are combinational, i.e., only messages
+ * satisfying all filter conditions will they be returned.
+ * @param integer level filter
+ * @param array category filter
+ * @param array control filter
+ * @return array list of messages. Each array elements represents one message
+ * with the following structure:
+ * array(
+ * [0] => message
+ * [1] => level
+ * [2] => category
+ * [3] => timestamp (by microtime(), float number));
+ * [4] => memory in bytes
+ * [5] => control client id
+ */
+ public function getLogs($levels=null,$categories=null,$controls=null,$timestamp=null)
+ {
+ $this->_levels=$levels;
+ $this->_categories=$categories;
+ $this->_controls=$controls;
+ $this->_timestamp=$timestamp;
+ if(empty($levels) && empty($categories) && empty($controls) && is_null($timestamp))
+ return $this->_logs;
+ $logs = $this->_logs;
+ if(!empty($levels))
+ $logs = array_values(array_filter( array_filter($logs,array($this,'filterByLevels')) ));
+ if(!empty($categories))
+ $logs = array_values(array_filter( array_filter($logs,array($this,'filterByCategories')) ));
+ if(!empty($controls))
+ $logs = array_values(array_filter( array_filter($logs,array($this,'filterByControl')) ));
+ if(!is_null($timestamp))
+ $logs = array_values(array_filter( array_filter($logs,array($this,'filterByTimeStamp')) ));
+ return $logs;
+ }
+
+ /**
+ * Deletes log messages from the queue.
+ * Messages may be filtered by log levels and/or categories and/or control client ids and/or timestamp.
+ * A level filter is specified by an integer, whose bits indicate the levels interested.
+ * For example, (TLogger::INFO | TLogger::WARNING) specifies INFO and WARNING levels.
+ * A category filter is specified by an array of categories to filter.
+ * A message whose category name starts with any filtering category
+ * will be deleted. For example, a category filter array('System.Web','System.IO')
+ * will delete messages under categories such as 'System.Web', 'System.IO',
+ * 'System.Web.UI', 'System.Web.UI.WebControls', etc.
+ * A control client id filter is specified by an array of control client id
+ * A message whose control client id starts with any filtering naming panels
+ * will be deleted. For example, a category filter array('ctl0_body_header',
+ * 'ctl0_body_content_sidebar')
+ * will delete messages under categories such as 'ctl0_body_header', 'ctl0_body_content_sidebar',
+ * 'ctl0_body_header_title', 'ctl0_body_content_sidebar_savebutton', etc.
+ * A timestamp filter is specified as an interger or float number.
+ * A message whose registered timestamp is less or equal the filter value will be returned.
+ * Level filter, category filter, control filter and timestamp filter are combinational, i.e., only messages
+ * satisfying all filter conditions will they be returned.
+ * @param integer level filter
+ * @param array category filter
+ * @param array control filter
+ */
+ public function deleteLogs($levels=null,$categories=null,$controls=null,$timestamp=null)
+ {
+ $this->_levels=$levels;
+ $this->_categories=$categories;
+ $this->_controls=$controls;
+ $this->_timestamp=$timestamp;
+ if(empty($levels) && empty($categories) && empty($controls) && is_null($timestamp))
+ {
+ $this->_logs=array();
+ return;
+ }
+ $logs = $this->_logs;
+ if(!empty($levels))
+ $logs = array_filter( array_filter($logs,array($this,'filterByLevels')) );
+ if(!empty($categories))
+ $logs = array_filter( array_filter($logs,array($this,'filterByCategories')) );
+ if(!empty($controls))
+ $logs = array_filter( array_filter($logs,array($this,'filterByControl')) );
+ if(!is_null($timestamp))
+ $logs = array_filter( array_filter($logs,array($this,'filterByTimeStamp')) );
+ $this->_logs = array_values( array_diff_key($this->_logs, $logs) );
+ }
+
+ /**
+ * Filter function used by {@link getLogs}.
+ * @param array element to be filtered
+ */
+ private function filterByCategories($value)
+ {
+ foreach($this->_categories as $category)
+ {
+ // element 2 is the category
+ if($value[2]===$category || strpos($value[2],$category.'.')===0)
+ return $value;
+ }
+ return false;
+ }
+
+ /**
+ * Filter function used by {@link getLogs}
+ * @param array element to be filtered
+ */
+ private function filterByLevels($value)
+ {
+ // element 1 are the levels
+ if($value[1] & $this->_levels)
+ return $value;
+ else
+ return false;
+ }
+
+ /**
+ * Filter function used by {@link getLogs}
+ * @param array element to be filtered
+ */
+ private function filterByControl($value)
+ {
+ // element 5 are the control client ids
+ foreach($this->_controls as $control)
+ {
+ if($value[5]===$control || strpos($value[5],$control)===0)
+ return $value;
+ }
+ return false;
+ }
+
+ /**
+ * Filter function used by {@link getLogs}
+ * @param array element to be filtered
+ */
+ private function filterByTimeStamp($value)
+ {
+ // element 3 is the timestamp
+ if($value[3] <= $this->_timestamp)
+ return $value;
+ else
+ return false;
+ }
+}
+
diff --git a/framework/Util/TParameterModule.php b/framework/Util/TParameterModule.php
index 31f109c1..a142e4ae 100644
--- a/framework/Util/TParameterModule.php
+++ b/framework/Util/TParameterModule.php
@@ -1,173 +1,173 @@
-<?php
-/**
- * TParameterModule class
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright &copy; 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Util
- */
-
-/**
- * TParameterModule class
- *
- * TParameterModule enables loading application parameters from external
- * storage other than the application configuration.
- * To load parameters from an XML file, configure the module by setting
- * its {@link setParameterFile ParameterFile} property.
- * Note, the property only accepts a file path in namespace format with
- * file extension being '.xml'. The file format is as follows, which is
- * similar to the parameter portion in an application configuration,
- * <code>
- * <parameters>
- * <parameter id="param1" value="paramValue1" />
- * <parameter id="param2" Property1="Value1" Property2="Value2" ... />
- * </parameters>
- * </code>
- *
- * In addition, any content enclosed within the module tag is also treated
- * as parameters, e.g.,
- * <code>
- * <module class="System.Util.TParameterModule">
- * <parameter id="param1" value="paramValue1" />
- * <parameter id="param2" Property1="Value1" Property2="Value2" ... />
- * </module>
- * </code>
- *
- * If a parameter is defined both in the external file and within the module
- * tag, the former takes precedence.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @author Carl G. Mathisen <carlgmathisen@gmail.com>
- * @version $Id$
- * @package System.Util
- * @since 3.0
- */
-class TParameterModule extends TModule
-{
- /**
- * @deprecated since 3.2
- */
- const PARAM_FILE_EXT='.xml';
- private $_initialized=false;
- private $_paramFile=null;
-
- /**
- * Initializes the module by loading parameters.
- * @param mixed content enclosed within the module tag
- */
- public function init($config)
- {
- $this->loadParameters($config);
- if($this->_paramFile!==null)
- {
- $configFile = null;
- if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_XML && ($cache=$this->getApplication()->getCache())!==null)
- {
- $cacheKey='TParameterModule:'.$this->_paramFile;
- if(($configFile=$cache->get($cacheKey))===false)
- {
- $cacheFile=new TXmlDocument;
- $cacheFile->loadFromFile($this->_paramFile);
- $cache->set($cacheKey,$cacheFile,0,new TFileCacheDependency($this->_paramFile));
- }
- }
- else
- {
- if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP)
- {
- $configFile = include $this->_paramFile;
- }
- else
- {
- $configFile=new TXmlDocument;
- $configFile->loadFromFile($this->_paramFile);
- }
- }
- $this->loadParameters($configFile);
- }
- $this->_initialized=true;
- }
-
- /**
- * Loads parameters into application.
- * @param mixed XML of PHP representation of the parameters
- * @throws TConfigurationException if the parameter file format is invalid
- */
- protected function loadParameters($config)
- {
- $parameters=array();
- if(is_array($config))
- {
- foreach($config as $id => $parameter)
- {
- if(is_array($parameter) && isset($parameter['class']))
- {
- $properties = isset($parameter['properties'])?$parameter['properties']:array();
- $parameters[$id]=array($parameter['class'],$properties);
- }
- else
- {
- $parameters[$id] = $parameter;
- }
- }
- }
- else if($config instanceof TXmlElement)
- {
- foreach($config->getElementsByTagName('parameter') as $node)
- {
- $properties=$node->getAttributes();
- if(($id=$properties->remove('id'))===null)
- throw new TConfigurationException('parametermodule_parameterid_required');
- if(($type=$properties->remove('class'))===null)
- {
- if(($value=$properties->remove('value'))===null)
- $parameters[$id]=$node;
- else
- $parameters[$id]=$value;
- }
- else
- $parameters[$id]=array($type,$properties->toArray());
- }
- }
-
- $appParams=$this->getApplication()->getParameters();
- foreach($parameters as $id=>$parameter)
- {
- if(is_array($parameter))
- {
- $component=Prado::createComponent($parameter[0]);
- foreach($parameter[1] as $name=>$value)
- $component->setSubProperty($name,$value);
- $appParams->add($id,$component);
- }
- else
- $appParams->add($id,$parameter);
- }
- }
-
- /**
- * @return string the parameter file path
- */
- public function getParameterFile()
- {
- return $this->_paramFile;
- }
-
- /**
- * @param string the parameter file path. It must be in namespace format
- * and the file extension is '.xml'.
- * @throws TInvalidOperationException if the module is initialized
- * @throws TConfigurationException if the file is invalid
- */
- public function setParameterFile($value)
- {
- if($this->_initialized)
- throw new TInvalidOperationException('parametermodule_parameterfile_unchangeable');
- else if(($this->_paramFile=Prado::getPathOfNamespace($value,$this->getApplication()->getConfigurationFileExt()))===null || !is_file($this->_paramFile))
- throw new TConfigurationException('parametermodule_parameterfile_invalid',$value);
- }
-}
-
+<?php
+/**
+ * TParameterModule class
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Util
+ */
+
+/**
+ * TParameterModule class
+ *
+ * TParameterModule enables loading application parameters from external
+ * storage other than the application configuration.
+ * To load parameters from an XML file, configure the module by setting
+ * its {@link setParameterFile ParameterFile} property.
+ * Note, the property only accepts a file path in namespace format with
+ * file extension being '.xml'. The file format is as follows, which is
+ * similar to the parameter portion in an application configuration,
+ * <code>
+ * <parameters>
+ * <parameter id="param1" value="paramValue1" />
+ * <parameter id="param2" Property1="Value1" Property2="Value2" ... />
+ * </parameters>
+ * </code>
+ *
+ * In addition, any content enclosed within the module tag is also treated
+ * as parameters, e.g.,
+ * <code>
+ * <module class="System.Util.TParameterModule">
+ * <parameter id="param1" value="paramValue1" />
+ * <parameter id="param2" Property1="Value1" Property2="Value2" ... />
+ * </module>
+ * </code>
+ *
+ * If a parameter is defined both in the external file and within the module
+ * tag, the former takes precedence.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @author Carl G. Mathisen <carlgmathisen@gmail.com>
+ * @version $Id$
+ * @package System.Util
+ * @since 3.0
+ */
+class TParameterModule extends TModule
+{
+ /**
+ * @deprecated since 3.2
+ */
+ const PARAM_FILE_EXT='.xml';
+ private $_initialized=false;
+ private $_paramFile=null;
+
+ /**
+ * Initializes the module by loading parameters.
+ * @param mixed content enclosed within the module tag
+ */
+ public function init($config)
+ {
+ $this->loadParameters($config);
+ if($this->_paramFile!==null)
+ {
+ $configFile = null;
+ if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_XML && ($cache=$this->getApplication()->getCache())!==null)
+ {
+ $cacheKey='TParameterModule:'.$this->_paramFile;
+ if(($configFile=$cache->get($cacheKey))===false)
+ {
+ $cacheFile=new TXmlDocument;
+ $cacheFile->loadFromFile($this->_paramFile);
+ $cache->set($cacheKey,$cacheFile,0,new TFileCacheDependency($this->_paramFile));
+ }
+ }
+ else
+ {
+ if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP)
+ {
+ $configFile = include $this->_paramFile;
+ }
+ else
+ {
+ $configFile=new TXmlDocument;
+ $configFile->loadFromFile($this->_paramFile);
+ }
+ }
+ $this->loadParameters($configFile);
+ }
+ $this->_initialized=true;
+ }
+
+ /**
+ * Loads parameters into application.
+ * @param mixed XML of PHP representation of the parameters
+ * @throws TConfigurationException if the parameter file format is invalid
+ */
+ protected function loadParameters($config)
+ {
+ $parameters=array();
+ if(is_array($config))
+ {
+ foreach($config as $id => $parameter)
+ {
+ if(is_array($parameter) && isset($parameter['class']))
+ {
+ $properties = isset($parameter['properties'])?$parameter['properties']:array();
+ $parameters[$id]=array($parameter['class'],$properties);
+ }
+ else
+ {
+ $parameters[$id] = $parameter;
+ }
+ }
+ }
+ else if($config instanceof TXmlElement)
+ {
+ foreach($config->getElementsByTagName('parameter') as $node)
+ {
+ $properties=$node->getAttributes();
+ if(($id=$properties->remove('id'))===null)
+ throw new TConfigurationException('parametermodule_parameterid_required');
+ if(($type=$properties->remove('class'))===null)
+ {
+ if(($value=$properties->remove('value'))===null)
+ $parameters[$id]=$node;
+ else
+ $parameters[$id]=$value;
+ }
+ else
+ $parameters[$id]=array($type,$properties->toArray());
+ }
+ }
+
+ $appParams=$this->getApplication()->getParameters();
+ foreach($parameters as $id=>$parameter)
+ {
+ if(is_array($parameter))
+ {
+ $component=Prado::createComponent($parameter[0]);
+ foreach($parameter[1] as $name=>$value)
+ $component->setSubProperty($name,$value);
+ $appParams->add($id,$component);
+ }
+ else
+ $appParams->add($id,$parameter);
+ }
+ }
+
+ /**
+ * @return string the parameter file path
+ */
+ public function getParameterFile()
+ {
+ return $this->_paramFile;
+ }
+
+ /**
+ * @param string the parameter file path. It must be in namespace format
+ * and the file extension is '.xml'.
+ * @throws TInvalidOperationException if the module is initialized
+ * @throws TConfigurationException if the file is invalid
+ */
+ public function setParameterFile($value)
+ {
+ if($this->_initialized)
+ throw new TInvalidOperationException('parametermodule_parameterfile_unchangeable');
+ else if(($this->_paramFile=Prado::getPathOfNamespace($value,$this->getApplication()->getConfigurationFileExt()))===null || !is_file($this->_paramFile))
+ throw new TConfigurationException('parametermodule_parameterfile_invalid',$value);
+ }
+}
+
diff --git a/framework/Util/TSimpleDateFormatter.php b/framework/Util/TSimpleDateFormatter.php
index 78b2666f..6b1ec51f 100644
--- a/framework/Util/TSimpleDateFormatter.php
+++ b/framework/Util/TSimpleDateFormatter.php
@@ -1,376 +1,376 @@
-<?php
-/**
- * TSimpleDateFormatter class file
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright &copy; 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Util
- */
-
-/**
- * TSimpleDateFormatter class.
- *
- * Formats and parses dates using the SimpleDateFormat pattern.
- * This pattern is compatible with the I18N and java's SimpleDateFormatter.
- * <code>
- * Pattern | Description
- * ----------------------------------------------------
- * d | Day of month 1 to 31, no padding
- * dd | Day of monath 01 to 31, zero leading
- * M | Month digit 1 to 12, no padding
- * MM | Month digit 01 to 12, zero leading
- * yy | 2 year digit, e.g., 96, 05
- * yyyy | 4 year digit, e.g., 2005
- * ----------------------------------------------------
- * </code>
- *
- * Usage example, to format a date
- * <code>
- * $formatter = new TSimpleDateFormatter("dd/MM/yyy");
- * echo $formatter->format(time());
- * </code>
- *
- * To parse the date string into a date timestamp.
- * <code>
- * $formatter = new TSimpleDateFormatter("d-M-yyy");
- * echo $formatter->parse("24-6-2005");
- * </code>
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @version $Id$
- * @package System.Util
- * @since 3.0
- */
-class TSimpleDateFormatter
-{
- /**
- * Formatting pattern.
- * @var string
- */
- private $pattern;
-
- /**
- * Charset, default is 'UTF-8'
- * @var string
- */
- private $charset = 'UTF-8';
-
- /**
- * Constructor, create a new date time formatter.
- * @param string formatting pattern.
- * @param string pattern and value charset
- */
- public function __construct($pattern, $charset='UTF-8')
- {
- $this->setPattern($pattern);
- $this->setCharset($charset);
- }
-
- /**
- * @return string formatting pattern.
- */
- public function getPattern()
- {
- return $this->pattern;
- }
-
- /**
- * @param string formatting pattern.
- */
- public function setPattern($pattern)
- {
- $this->pattern = $pattern;
- }
-
- /**
- * @return string formatting charset.
- */
- public function getCharset()
- {
- return $this->charset;
- }
-
- /**
- * @param string formatting charset.
- */
- public function setCharset($charset)
- {
- $this->charset = $charset;
- }
-
- /**
- * Format the date according to the pattern.
- * @param string|int the date to format, either integer or a string readable by strtotime.
- * @return string formatted date.
- */
- public function format($value)
- {
- $date = $this->getDate($value);
- $bits['yyyy'] = $date['year'];
- $bits['yy'] = substr("{$date['year']}", -2);
-
- $bits['MM'] = str_pad("{$date['mon']}", 2, '0', STR_PAD_LEFT);
- $bits['M'] = $date['mon'];
-
- $bits['dd'] = str_pad("{$date['mday']}", 2, '0', STR_PAD_LEFT);
- $bits['d'] = $date['mday'];
-
- $pattern = preg_replace('/M{3,4}/', 'MM', $this->pattern);
- return str_replace(array_keys($bits), $bits, $pattern);
- }
-
- public function getMonthPattern()
- {
- if(is_int(strpos($this->pattern, 'MMMM')))
- return 'MMMM';
- if(is_int(strpos($this->pattern, 'MMM')))
- return 'MMM';
- if(is_int(strpos($this->pattern, 'MM')))
- return 'MM';
- if(is_int(strpos($this->pattern, 'M')))
- return 'M';
- return false;
- }
-
- public function getDayPattern()
- {
- if(is_int(strpos($this->pattern, 'dd')))
- return 'dd';
- if(is_int(strpos($this->pattern, 'd')))
- return 'd';
- return false;
- }
-
- public function getYearPattern()
- {
- if(is_int(strpos($this->pattern, 'yyyy')))
- return 'yyyy';
- if(is_int(strpos($this->pattern, 'yy')))
- return 'yy';
- return false;
- }
-
- public function getDayMonthYearOrdering()
- {
- $ordering = array();
- if(is_int($day= strpos($this->pattern, 'd')))
- $ordering['day'] = $day;
- if(is_int($month= strpos($this->pattern, 'M')))
- $ordering['month'] = $month;
- if(is_int($year= strpos($this->pattern, 'yy')))
- $ordering['year'] = $year;
- asort($ordering);
- return array_keys($ordering);
- }
-
- /**
- * Gets the time stamp from string or integer.
- * @param string|int date to parse
- * @return array date info array
- */
- private function getDate($value)
- {
- if(!is_string($value))
- {
- $s = Prado::createComponent('System.Util.TDateTimeStamp');
- return $s->getDate($value);
- }
- $date = @strtotime($value);
- if($date < 0)
- throw new TInvalidDataValueException('invalid_date', $value);
- return @getdate($date);
- }
-
- /**
- * @return boolean true if the given value matches with the date pattern.
- */
- public function isValidDate($value)
- {
- if($value === null) {
- return false;
- } else {
- return $this->parse($value, false) !== null;
- }
- }
-
- /**
- * Parse the string according to the pattern.
- * @param string|int date string or integer to parse
- * @return int date time stamp
- * @throws TInvalidDataValueException if date string is malformed.
- */
- public function parse($value,$defaultToCurrentTime=true)
- {
- if(is_int($value) || is_float($value))
- return $value;
- else if(!is_string($value))
- throw new TInvalidDataValueException('date_to_parse_must_be_string', $value);
-
- if(empty($this->pattern)) return time();
-
- $date = time();
-
- if($this->length(trim($value)) < 1)
- return $defaultToCurrentTime ? $date : null;
-
- $pattern = $this->pattern;
-
- $i_val = 0;
- $i_format = 0;
- $pattern_length = $this->length($pattern);
- $c = '';
- $token='';
- $x=null; $y=null;
-
-
- if($defaultToCurrentTime)
- {
- $year = "{$date['year']}";
- $month = $date['mon'];
- $day = $date['mday'];
- }
- else
- {
- $year = null;
- $month = null;
- $day = null;
- }
-
- while ($i_format < $pattern_length)
- {
- $c = $this->charAt($pattern,$i_format);
- $token='';
- while ($this->charEqual($pattern, $i_format, $c)
- && ($i_format < $pattern_length))
- {
- $token .= $this->charAt($pattern, $i_format++);
- }
-
- if ($token=='yyyy' || $token=='yy' || $token=='y')
- {
- if ($token=='yyyy') { $x=4;$y=4; }
- if ($token=='yy') { $x=2;$y=2; }
- if ($token=='y') { $x=2;$y=4; }
- $year = $this->getInteger($value,$i_val,$x,$y);
- if($year === null)
- return null;
- //throw new TInvalidDataValueException('Invalid year', $value);
- $i_val += strlen($year);
- if(strlen($year) == 2)
- {
- $iYear = (int)$year;
- if($iYear > 70)
- $year = $iYear + 1900;
- else
- $year = $iYear + 2000;
- }
- $year = (int)$year;
- }
- elseif($token=='MM' || $token=='M')
- {
- $month=$this->getInteger($value,$i_val,
- $this->length($token),2);
- $iMonth = (int)$month;
- if($month === null || $iMonth < 1 || $iMonth > 12 )
- return null;
- //throw new TInvalidDataValueException('Invalid month', $value);
- $i_val += strlen($month);
- $month = $iMonth;
- }
- elseif ($token=='dd' || $token=='d')
- {
- $day = $this->getInteger($value,$i_val,
- $this->length($token), 2);
- $iDay = (int)$day;
- if($day === null || $iDay < 1 || $iDay >31)
- return null;
- //throw new TInvalidDataValueException('Invalid day', $value);
- $i_val += strlen($day);
- $day = $iDay;
- }
- else
- {
- if($this->substring($value, $i_val, $this->length($token)) != $token)
- return null;
- //throw new TInvalidDataValueException("Subpattern '{$this->pattern}' mismatch", $value);
- else
- $i_val += $this->length($token);
- }
- }
- if ($i_val != $this->length($value))
- return null;
- //throw new TInvalidDataValueException("Pattern '{$this->pattern}' mismatch", $value);
- if(!$defaultToCurrentTime && ($month === null || $day === null || $year === null))
- return null;
- else
- {
- if(empty($year)) {
- $year = date('Y');
- }
- $day = (int)$day <= 0 ? 1 : (int)$day;
- $month = (int)$month <= 0 ? 1 : (int)$month;
- $s = Prado::createComponent('System.Util.TDateTimeStamp');
- return $s->getTimeStamp(0, 0, 0, $month, $day, $year);
- }
- }
-
- /**
- * Calculate the length of a string, may be consider iconv_strlen?
- */
- private function length($string)
- {
- //use iconv_strlen or just strlen?
- return strlen($string);
- }
-
- /**
- * Get the char at a position.
- */
- private function charAt($string, $pos)
- {
- return $this->substring($string, $pos, 1);
- }
-
- /**
- * Gets a portion of a string, uses iconv_substr.
- */
- private function substring($string, $start, $length)
- {
- return iconv_substr($string, $start, $length);
- }
-
- /**
- * Returns true if char at position equals a particular char.
- */
- private function charEqual($string, $pos, $char)
- {
- return $this->charAt($string, $pos) == $char;
- }
-
- /**
- * Gets integer from part of a string, allows integers of any length.
- * @param string string to retrieve the integer from.
- * @param int starting position
- * @param int minimum integer length
- * @param int maximum integer length
- * @return string integer portion of the string, null otherwise
- */
- private function getInteger($str,$i,$minlength,$maxlength)
- {
- //match for digits backwards
- for ($x = $maxlength; $x >= $minlength; $x--)
- {
- $token= $this->substring($str, $i,$x);
- if ($this->length($token) < $minlength)
- return null;
- if (preg_match('/^\d+$/', $token))
- return $token;
- }
- return null;
- }
-}
-
-?>
+<?php
+/**
+ * TSimpleDateFormatter class file
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Util
+ */
+
+/**
+ * TSimpleDateFormatter class.
+ *
+ * Formats and parses dates using the SimpleDateFormat pattern.
+ * This pattern is compatible with the I18N and java's SimpleDateFormatter.
+ * <code>
+ * Pattern | Description
+ * ----------------------------------------------------
+ * d | Day of month 1 to 31, no padding
+ * dd | Day of monath 01 to 31, zero leading
+ * M | Month digit 1 to 12, no padding
+ * MM | Month digit 01 to 12, zero leading
+ * yy | 2 year digit, e.g., 96, 05
+ * yyyy | 4 year digit, e.g., 2005
+ * ----------------------------------------------------
+ * </code>
+ *
+ * Usage example, to format a date
+ * <code>
+ * $formatter = new TSimpleDateFormatter("dd/MM/yyy");
+ * echo $formatter->format(time());
+ * </code>
+ *
+ * To parse the date string into a date timestamp.
+ * <code>
+ * $formatter = new TSimpleDateFormatter("d-M-yyy");
+ * echo $formatter->parse("24-6-2005");
+ * </code>
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Util
+ * @since 3.0
+ */
+class TSimpleDateFormatter
+{
+ /**
+ * Formatting pattern.
+ * @var string
+ */
+ private $pattern;
+
+ /**
+ * Charset, default is 'UTF-8'
+ * @var string
+ */
+ private $charset = 'UTF-8';
+
+ /**
+ * Constructor, create a new date time formatter.
+ * @param string formatting pattern.
+ * @param string pattern and value charset
+ */
+ public function __construct($pattern, $charset='UTF-8')
+ {
+ $this->setPattern($pattern);
+ $this->setCharset($charset);
+ }
+
+ /**
+ * @return string formatting pattern.
+ */
+ public function getPattern()
+ {
+ return $this->pattern;
+ }
+
+ /**
+ * @param string formatting pattern.
+ */
+ public function setPattern($pattern)
+ {
+ $this->pattern = $pattern;
+ }
+
+ /**
+ * @return string formatting charset.
+ */
+ public function getCharset()
+ {
+ return $this->charset;
+ }
+
+ /**
+ * @param string formatting charset.
+ */
+ public function setCharset($charset)
+ {
+ $this->charset = $charset;
+ }
+
+ /**
+ * Format the date according to the pattern.
+ * @param string|int the date to format, either integer or a string readable by strtotime.
+ * @return string formatted date.
+ */
+ public function format($value)
+ {
+ $date = $this->getDate($value);
+ $bits['yyyy'] = $date['year'];
+ $bits['yy'] = substr("{$date['year']}", -2);
+
+ $bits['MM'] = str_pad("{$date['mon']}", 2, '0', STR_PAD_LEFT);
+ $bits['M'] = $date['mon'];
+
+ $bits['dd'] = str_pad("{$date['mday']}", 2, '0', STR_PAD_LEFT);
+ $bits['d'] = $date['mday'];
+
+ $pattern = preg_replace('/M{3,4}/', 'MM', $this->pattern);
+ return str_replace(array_keys($bits), $bits, $pattern);
+ }
+
+ public function getMonthPattern()
+ {
+ if(is_int(strpos($this->pattern, 'MMMM')))
+ return 'MMMM';
+ if(is_int(strpos($this->pattern, 'MMM')))
+ return 'MMM';
+ if(is_int(strpos($this->pattern, 'MM')))
+ return 'MM';
+ if(is_int(strpos($this->pattern, 'M')))
+ return 'M';
+ return false;
+ }
+
+ public function getDayPattern()
+ {
+ if(is_int(strpos($this->pattern, 'dd')))
+ return 'dd';
+ if(is_int(strpos($this->pattern, 'd')))
+ return 'd';
+ return false;
+ }
+
+ public function getYearPattern()
+ {
+ if(is_int(strpos($this->pattern, 'yyyy')))
+ return 'yyyy';
+ if(is_int(strpos($this->pattern, 'yy')))
+ return 'yy';
+ return false;
+ }
+
+ public function getDayMonthYearOrdering()
+ {
+ $ordering = array();
+ if(is_int($day= strpos($this->pattern, 'd')))
+ $ordering['day'] = $day;
+ if(is_int($month= strpos($this->pattern, 'M')))
+ $ordering['month'] = $month;
+ if(is_int($year= strpos($this->pattern, 'yy')))
+ $ordering['year'] = $year;
+ asort($ordering);
+ return array_keys($ordering);
+ }
+
+ /**
+ * Gets the time stamp from string or integer.
+ * @param string|int date to parse
+ * @return array date info array
+ */
+ private function getDate($value)
+ {
+ if(!is_string($value))
+ {
+ $s = Prado::createComponent('System.Util.TDateTimeStamp');
+ return $s->getDate($value);
+ }
+ $date = @strtotime($value);
+ if($date < 0)
+ throw new TInvalidDataValueException('invalid_date', $value);
+ return @getdate($date);
+ }
+
+ /**
+ * @return boolean true if the given value matches with the date pattern.
+ */
+ public function isValidDate($value)
+ {
+ if($value === null) {
+ return false;
+ } else {
+ return $this->parse($value, false) !== null;
+ }
+ }
+
+ /**
+ * Parse the string according to the pattern.
+ * @param string|int date string or integer to parse
+ * @return int date time stamp
+ * @throws TInvalidDataValueException if date string is malformed.
+ */
+ public function parse($value,$defaultToCurrentTime=true)
+ {
+ if(is_int($value) || is_float($value))
+ return $value;
+ else if(!is_string($value))
+ throw new TInvalidDataValueException('date_to_parse_must_be_string', $value);
+
+ if(empty($this->pattern)) return time();
+
+ $date = time();
+
+ if($this->length(trim($value)) < 1)
+ return $defaultToCurrentTime ? $date : null;
+
+ $pattern = $this->pattern;
+
+ $i_val = 0;
+ $i_format = 0;
+ $pattern_length = $this->length($pattern);
+ $c = '';
+ $token='';
+ $x=null; $y=null;
+
+
+ if($defaultToCurrentTime)
+ {
+ $year = "{$date['year']}";
+ $month = $date['mon'];
+ $day = $date['mday'];
+ }
+ else
+ {
+ $year = null;
+ $month = null;
+ $day = null;
+ }
+
+ while ($i_format < $pattern_length)
+ {
+ $c = $this->charAt($pattern,$i_format);
+ $token='';
+ while ($this->charEqual($pattern, $i_format, $c)
+ && ($i_format < $pattern_length))
+ {
+ $token .= $this->charAt($pattern, $i_format++);
+ }
+
+ if ($token=='yyyy' || $token=='yy' || $token=='y')
+ {
+ if ($token=='yyyy') { $x=4;$y=4; }
+ if ($token=='yy') { $x=2;$y=2; }
+ if ($token=='y') { $x=2;$y=4; }
+ $year = $this->getInteger($value,$i_val,$x,$y);
+ if($year === null)
+ return null;
+ //throw new TInvalidDataValueException('Invalid year', $value);
+ $i_val += strlen($year);
+ if(strlen($year) == 2)
+ {
+ $iYear = (int)$year;
+ if($iYear > 70)
+ $year = $iYear + 1900;
+ else
+ $year = $iYear + 2000;
+ }
+ $year = (int)$year;
+ }
+ elseif($token=='MM' || $token=='M')
+ {
+ $month=$this->getInteger($value,$i_val,
+ $this->length($token),2);
+ $iMonth = (int)$month;
+ if($month === null || $iMonth < 1 || $iMonth > 12 )
+ return null;
+ //throw new TInvalidDataValueException('Invalid month', $value);
+ $i_val += strlen($month);
+ $month = $iMonth;
+ }
+ elseif ($token=='dd' || $token=='d')
+ {
+ $day = $this->getInteger($value,$i_val,
+ $this->length($token), 2);
+ $iDay = (int)$day;
+ if($day === null || $iDay < 1 || $iDay >31)
+ return null;
+ //throw new TInvalidDataValueException('Invalid day', $value);
+ $i_val += strlen($day);
+ $day = $iDay;
+ }
+ else
+ {
+ if($this->substring($value, $i_val, $this->length($token)) != $token)
+ return null;
+ //throw new TInvalidDataValueException("Subpattern '{$this->pattern}' mismatch", $value);
+ else
+ $i_val += $this->length($token);
+ }
+ }
+ if ($i_val != $this->length($value))
+ return null;
+ //throw new TInvalidDataValueException("Pattern '{$this->pattern}' mismatch", $value);
+ if(!$defaultToCurrentTime && ($month === null || $day === null || $year === null))
+ return null;
+ else
+ {
+ if(empty($year)) {
+ $year = date('Y');
+ }
+ $day = (int)$day <= 0 ? 1 : (int)$day;
+ $month = (int)$month <= 0 ? 1 : (int)$month;
+ $s = Prado::createComponent('System.Util.TDateTimeStamp');
+ return $s->getTimeStamp(0, 0, 0, $month, $day, $year);
+ }
+ }
+
+ /**
+ * Calculate the length of a string, may be consider iconv_strlen?
+ */
+ private function length($string)
+ {
+ //use iconv_strlen or just strlen?
+ return strlen($string);
+ }
+
+ /**
+ * Get the char at a position.
+ */
+ private function charAt($string, $pos)
+ {
+ return $this->substring($string, $pos, 1);
+ }
+
+ /**
+ * Gets a portion of a string, uses iconv_substr.
+ */
+ private function substring($string, $start, $length)
+ {
+ return iconv_substr($string, $start, $length);
+ }
+
+ /**
+ * Returns true if char at position equals a particular char.
+ */
+ private function charEqual($string, $pos, $char)
+ {
+ return $this->charAt($string, $pos) == $char;
+ }
+
+ /**
+ * Gets integer from part of a string, allows integers of any length.
+ * @param string string to retrieve the integer from.
+ * @param int starting position
+ * @param int minimum integer length
+ * @param int maximum integer length
+ * @return string integer portion of the string, null otherwise
+ */
+ private function getInteger($str,$i,$minlength,$maxlength)
+ {
+ //match for digits backwards
+ for ($x = $maxlength; $x >= $minlength; $x--)
+ {
+ $token= $this->substring($str, $i,$x);
+ if ($this->length($token) < $minlength)
+ return null;
+ if (preg_match('/^\d+$/', $token))
+ return $token;
+ }
+ return null;
+ }
+}
+
+?>
diff --git a/framework/Util/TVarDumper.php b/framework/Util/TVarDumper.php
index 0ea10e71..c2f712ac 100644
--- a/framework/Util/TVarDumper.php
+++ b/framework/Util/TVarDumper.php
@@ -1,128 +1,128 @@
-<?php
-/**
- * TVarDumper class file
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @link http://www.pradosoft.com/
+<?php
+/**
+ * TVarDumper class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.pradosoft.com/
* @copyright Copyright &copy; 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Util
- */
-
-/**
- * TVarDumper class.
- *
- * TVarDumper is intended to replace the buggy PHP function var_dump and print_r.
- * It can correctly identify the recursively referenced objects in a complex
- * object structure. It also has a recursive depth control to avoid indefinite
- * recursive display of some peculiar variables.
- *
- * TVarDumper can be used as follows,
- * <code>
- * echo TVarDumper::dump($var);
- * </code>
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @version $Id$
- * @package System.Util
- * @since 3.0
- */
-class TVarDumper
-{
- private static $_objects;
- private static $_output;
- private static $_depth;
-
- /**
- * Converts a variable into a string representation.
- * This method achieves the similar functionality as var_dump and print_r
- * but is more robust when handling complex objects such as PRADO controls.
- * @param mixed variable to be dumped
- * @param integer maximum depth that the dumper should go into the variable. Defaults to 10.
- * @return string the string representation of the variable
- */
- public static function dump($var,$depth=10,$highlight=false)
- {
- self::$_output='';
- self::$_objects=array();
- self::$_depth=$depth;
- self::dumpInternal($var,0);
- if($highlight)
- {
- $result=highlight_string("<?php\n".self::$_output,true);
- return preg_replace('/&lt;\\?php<br \\/>/','',$result,1);
- }
- else
- return self::$_output;
- }
-
- private static function dumpInternal($var,$level)
- {
- switch(gettype($var))
- {
- case 'boolean':
- self::$_output.=$var?'true':'false';
- break;
- case 'integer':
- self::$_output.="$var";
- break;
- case 'double':
- self::$_output.="$var";
- break;
- case 'string':
- self::$_output.="'$var'";
- break;
- case 'resource':
- self::$_output.='{resource}';
- break;
- case 'NULL':
- self::$_output.="null";
- break;
- case 'unknown type':
- self::$_output.='{unknown}';
- break;
- case 'array':
- if(self::$_depth<=$level)
- self::$_output.='array(...)';
- else if(empty($var))
- self::$_output.='array()';
- else
- {
- $keys=array_keys($var);
- $spaces=str_repeat(' ',$level*4);
- self::$_output.="array\n".$spaces.'(';
- foreach($keys as $key)
- {
- self::$_output.="\n".$spaces." [$key] => ";
- self::$_output.=self::dumpInternal($var[$key],$level+1);
- }
- self::$_output.="\n".$spaces.')';
- }
- break;
- case 'object':
- if(($id=array_search($var,self::$_objects,true))!==false)
- self::$_output.=get_class($var).'#'.($id+1).'(...)';
- else if(self::$_depth<=$level)
- self::$_output.=get_class($var).'(...)';
- else
- {
- $id=array_push(self::$_objects,$var);
- $className=get_class($var);
- $members=(array)$var;
- $keys=array_keys($members);
- $spaces=str_repeat(' ',$level*4);
- self::$_output.="$className#$id\n".$spaces.'(';
- foreach($keys as $key)
- {
- $keyDisplay=strtr(trim($key),array("\0"=>':'));
- self::$_output.="\n".$spaces." [$keyDisplay] => ";
- self::$_output.=self::dumpInternal($members[$key],$level+1);
- }
- self::$_output.="\n".$spaces.')';
- }
- break;
- }
- }
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Util
+ */
+
+/**
+ * TVarDumper class.
+ *
+ * TVarDumper is intended to replace the buggy PHP function var_dump and print_r.
+ * It can correctly identify the recursively referenced objects in a complex
+ * object structure. It also has a recursive depth control to avoid indefinite
+ * recursive display of some peculiar variables.
+ *
+ * TVarDumper can be used as follows,
+ * <code>
+ * echo TVarDumper::dump($var);
+ * </code>
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id$
+ * @package System.Util
+ * @since 3.0
+ */
+class TVarDumper
+{
+ private static $_objects;
+ private static $_output;
+ private static $_depth;
+
+ /**
+ * Converts a variable into a string representation.
+ * This method achieves the similar functionality as var_dump and print_r
+ * but is more robust when handling complex objects such as PRADO controls.
+ * @param mixed variable to be dumped
+ * @param integer maximum depth that the dumper should go into the variable. Defaults to 10.
+ * @return string the string representation of the variable
+ */
+ public static function dump($var,$depth=10,$highlight=false)
+ {
+ self::$_output='';
+ self::$_objects=array();
+ self::$_depth=$depth;
+ self::dumpInternal($var,0);
+ if($highlight)
+ {
+ $result=highlight_string("<?php\n".self::$_output,true);
+ return preg_replace('/&lt;\\?php<br \\/>/','',$result,1);
+ }
+ else
+ return self::$_output;
+ }
+
+ private static function dumpInternal($var,$level)
+ {
+ switch(gettype($var))
+ {
+ case 'boolean':
+ self::$_output.=$var?'true':'false';
+ break;
+ case 'integer':
+ self::$_output.="$var";
+ break;
+ case 'double':
+ self::$_output.="$var";
+ break;
+ case 'string':
+ self::$_output.="'$var'";
+ break;
+ case 'resource':
+ self::$_output.='{resource}';
+ break;
+ case 'NULL':
+ self::$_output.="null";
+ break;
+ case 'unknown type':
+ self::$_output.='{unknown}';
+ break;
+ case 'array':
+ if(self::$_depth<=$level)
+ self::$_output.='array(...)';
+ else if(empty($var))
+ self::$_output.='array()';
+ else
+ {
+ $keys=array_keys($var);
+ $spaces=str_repeat(' ',$level*4);
+ self::$_output.="array\n".$spaces.'(';
+ foreach($keys as $key)
+ {
+ self::$_output.="\n".$spaces." [$key] => ";
+ self::$_output.=self::dumpInternal($var[$key],$level+1);
+ }
+ self::$_output.="\n".$spaces.')';
+ }
+ break;
+ case 'object':
+ if(($id=array_search($var,self::$_objects,true))!==false)
+ self::$_output.=get_class($var).'#'.($id+1).'(...)';
+ else if(self::$_depth<=$level)
+ self::$_output.=get_class($var).'(...)';
+ else
+ {
+ $id=array_push(self::$_objects,$var);
+ $className=get_class($var);
+ $members=(array)$var;
+ $keys=array_keys($members);
+ $spaces=str_repeat(' ',$level*4);
+ self::$_output.="$className#$id\n".$spaces.'(';
+ foreach($keys as $key)
+ {
+ $keyDisplay=strtr(trim($key),array("\0"=>':'));
+ self::$_output.="\n".$spaces." [$keyDisplay] => ";
+ self::$_output.=self::dumpInternal($members[$key],$level+1);
+ }
+ self::$_output.="\n".$spaces.')';
+ }
+ break;
+ }
+ }
+}
+