diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/src/Tracy/Debug.php b/src/Tracy/Debug.php new file mode 100644 index 000000000..ed7ab1a26 --- /dev/null +++ b/src/Tracy/Debug.php @@ -0,0 +1,448 @@ +level)) { + error_reporting($config->level); + } + + if (isset($config->display)) { + self::$display = (bool) $config->display; + ini_set('display_errors', self::$display ? '1' : '0'); // for fatal errors + } + + if (isset($config->html)) { + self::$html = (bool) $config->html; + } + + if (isset($config->logDir)) { + self::$logDir = Environment::expand($config->logDir); + ini_set('log_errors', self::$logDir ? '1' : '0'); // for fatal errors + ini_set('error_log', self::$logDir . '/php.error.log'); + } + + if (isset($config->email)) { + self::$email = (string) $config->email; + } + + if (isset($config->emailSubject)) { + self::$emailSubject = (string) $config->emailSubject; + } + + if (self::$email || self::$display || self::$logDir) { + self::enable(); + } + } + + + + /********************* useful tools ****************d*g**/ + + + + /** + * Dumps information about a variable in readable format. + * + * @param mixed variable to dump. + * @param bool return output instead of printing it? + * @return string + */ + public static function dump($var, $return = FALSE) + { + ob_start(); + var_dump($var); + $output = ob_get_clean(); + + if (self::$html) { + $output = htmlspecialchars($output, ENT_NOQUOTES); + $output = preg_replace('#\]=>\n\ +([a-z]+)#i', '] => $1', $output); + $output = preg_replace('#^([a-z]+)#i', '$1', $output); + $output = "
$output
\n"; + } else { + $output = preg_replace('#\]=>\n\ +#i', '] => ', $output) . "\n"; + } + + if (!$return) echo $output; + + return $output; + } + + + + /** + * Starts/stops stopwatch. + * @return elapsed seconds + */ + public static function timer() + { + static $time = 0; + $now = microtime(TRUE); + $delta = $now - $time; + $time = $now; + return $delta; + } + + + + /********************* error and exception reporing ****************d*g**/ + + + + /** + * Register error handler routine. + * @param int error_reporting level + * @return void + */ + public static function enable($level = NULL) + { + /* + if (self::$display === NULL) { + require_once dirname(__FILE__) . '/Environment.php'; + self::$display = Environment::getName() !== Environment::PRODUCTION; + } + */ + + if ($level !== NULL) error_reporting($level); + set_error_handler(array(__CLASS__, 'errorHandler')); + set_exception_handler(array(__CLASS__, 'exceptionHandler')); // buggy in PHP 5.2.1 + self::$enabled = TRUE; + } + + + + /** + * Unregister error handler routine. + * @return void + */ + public static function disable() + { + if (self::$enabled) { + restore_error_handler(); + restore_exception_handler(); + self::$enabled = FALSE; + } + } + + + + /** + * Unregister error handler routine. + * @return void + */ + public static function isEnabled() + { + return self::$enabled; + } + + + + /** + * Debug exception handler. + * + * @param Exception + * @return void + */ + public static function exceptionHandler(Exception $exception) + { + self::disable(); + + if (self::$html) { + self::handleMessage(self::blueScreen($exception)); + } else { + self::handleMessage($exception->__toString() . "\nPHP version " . PHP_VERSION . "\nNette Framework version 0.7\n"); + } + + exit; + } + + + + /** + * Debug error handler. + * + * @param int level of the error raised + * @param string error message + * @param string file that the error was raised in + * @param int line number the error was raised at + * @param array an array of variables that existed in the scope the error was triggered in + * @return void + */ + public static function errorHandler($code, $message, $file, $line, $context) + { + $fatals = array( + E_ERROR => 'Fatal error', // unfortunately not catchable + E_CORE_ERROR => 'Fatal core rrror', // not catchable + E_COMPILE_ERROR => 'Fatal compile error', // unfortunately not catchable + E_USER_ERROR => 'Fatal error', + E_PARSE => 'Parse error', // unfortunately not catchable + E_RECOVERABLE_ERROR => 'Catchable fatal error', // since PHP 5.2 + ); + + if (isset($fatals[$code])) { + self::disable(); + + $trace = debug_backtrace(); + array_shift($trace); + $type = $fatals[$code]; + + if (self::$html) { + self::handleMessage(self::blueScreen(NULL, $type, $code, $message, $file, $line, $trace, $context)); + } else { + self::handleMessage("$type '$message' in $file on line $line\nPHP version " . PHP_VERSION . "\nNette Framework version 0.7\n"); + } + + exit; + } + + if (($code & error_reporting()) === $code) { + $types = array( + E_WARNING => 'Warning', + E_CORE_WARNING => 'Core warning', // not catchable + E_COMPILE_WARNING => 'Compile warning', // not catchable + E_USER_WARNING => 'Warning', + E_NOTICE => 'Notice', + E_USER_NOTICE => 'Notice', + E_STRICT => 'Strict standards', + E_DEPRECATED => 'Deprecated', + ); + $type = isset($types[$code]) ? $types[$code] : 'Unknown error'; + + if (self::$html) { + $message = "$type: $message in $file on line $line\n
"; + } else { + $message = "$type: $message in $file on line $line\n"; + } + + if (self::$display) { + echo $message; + } + + if (self::$logDir) { + error_log($message); + } + } + } + + + + /** + * Handles error message. + * @param string + * @param bool is fatal + * @return void + */ + private static function handleMessage($message) + { + if (!headers_sent()) { + header('HTTP/1.1 500 Internal Server Error'); + } + + if (self::$logDir) { + // TODO: add configurable file mask + $file = self::$logDir . '/report ' . date('Y-m-d H-i-s ') . substr(microtime(FALSE), 2, 6) . (self::$html ? '.html' : '.txt'); + file_put_contents($file, $message); + } + + if (self::$display) { + while (ob_get_level() && ob_end_clean()); + + echo $message; + + // fix for IE 6 + if (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 6.0')) { + $s = " \t\r\n"; + for ($i = 2e3; $i; $i--) echo $s{rand(0, 3)}; + } + } + + if (self::$email) { + // TODO: pridat limit na pocet odeslanych emailu denne + + // pro mailer v unixu je treba zamenit \r\n na \n, protoze on si to pak opet zameni za \r\n + $message = str_replace("\r\n", "\n", $message); + if (PHP_OS != 'Linux') $message = str_replace("\n", "\r\n", $message); + + mail(self::$email, self::$emailSubject, $message, "Content-Type: text/html; charset=ISO-8859-1"); + } + } + + + + /** + * Paint blue screen. + * @return string + */ + public static function blueScreen($exception, $type = NULL, $code = NULL, $message = NULL, $file = NULL, $line = NULL, $trace = NULL, $context = NULL) + { + if ($exception) { + $type = get_class($exception); + $code = $exception->getCode(); + $message = $exception->getMessage(); + $file = $exception->getFile(); + $line = $exception->getLine(); + $trace = $exception->getTrace(); + } + $colophons = self::$colophons; + require_once dirname(__FILE__) . '/Version.php'; + + ob_start(); + require dirname(__FILE__) . '/templates/Debug.phtml'; + return ob_get_clean(); + } + + + + /** + * Add custom descriptions. + * @param callback + * @return void + */ + public static function addColophon($callback) + { + if (!in_array($callback, self::$colophons, TRUE) && is_callable($callback)) { + self::$colophons[] = $callback; + } + } + + + + /** + * Filters output from self::dump() for sensitive informations. + * @param mixed variable to dump. + * @param string additional key + * @return string + */ + private static function safedump($var, $key = NULL) + { + if ($key !== NULL && array_search(strtolower($key), self::$keysToHide, TRUE)) { + return '*** hidden ***'; + } + + return preg_replace( + '#^(\s*\["(' . implode('|', self::$keysToHide) . ')"\] => string).+#mi', + '$1 (?) *** hidden ***', + self::dump($var, TRUE) + ); + } + + + + /** + * Render template. + */ + private static function openPanel($name, $collaped) + { + static $id; + $id++; + require dirname(__FILE__) . '/templates/Debug.openpanel.phtml'; + } + + + + /** + * Render template. + */ + private static function closePanel() + { + require dirname(__FILE__) . '/templates/Debug.closepanel.phtml'; + } + +} + + + +Debug::constructStatic(); diff --git a/src/Tracy/templates/Debug.closepanel.phtml b/src/Tracy/templates/Debug.closepanel.phtml new file mode 100644 index 000000000..7dd4f69ae --- /dev/null +++ b/src/Tracy/templates/Debug.closepanel.phtml @@ -0,0 +1,36 @@ + + + diff --git a/src/Tracy/templates/Debug.openpanel.phtml b/src/Tracy/templates/Debug.openpanel.phtml new file mode 100644 index 000000000..bf51d54af --- /dev/null +++ b/src/Tracy/templates/Debug.openpanel.phtml @@ -0,0 +1,38 @@ + +
+

+ +
diff --git a/src/Tracy/templates/Debug.phtml b/src/Tracy/templates/Debug.phtml new file mode 100644 index 000000000..4ea8b01d4 --- /dev/null +++ b/src/Tracy/templates/Debug.phtml @@ -0,0 +1,398 @@ +'; +} + +?> + + + + + + + <?php echo htmlspecialchars($type) ?> + + + + + + + + + + +
+

+ +

+
+ + + + + +

File:   Line:

+ +
 $s) {
+            $s = rtrim($s);
+            if (strlen($s) > 100) $s = substr($s, 0, 100) . '...';
+            if ($n === $line) {
+                printf("Line %s:    %s\n", $n, htmlSpecialChars($s));
+            } else {
+                printf("Line %s:    %s\n", $n, htmlSpecialChars($s));
+            }
+        }
+        ?>
+ + + + + + + +
    + $row): ?> +
  1. + + + + ', htmlSpecialChars(basename($row['file'])), ' (', $row['line'], ')' ?> + + + <PHP inner-code> + + + source   + + + + + (arguments ) +

    + + + + + + + + + + +
  2. + +
+ + + + + + + getPanels() as $name => $panel): ?> + + + + + + + + + + +

Variables

+ + $v) { + echo ''; + } + ?> + + + + + +

Constants

+ + $v) { + echo ''; + } + ?> +
', htmlspecialchars($k), '', self::safedump($v, $k), '
+ + + +

Included files

+ + '; + } + ?> + + + +

$_SERVER

+ +

empty

+ + + $v) echo ''; + ?> +
', htmlspecialchars($k), '', self::dump($v, TRUE), '
+ + + + + + + + +
__toString()) ?>
+ + + + + + + + getCause()): ?> + +
__toString()) ?>
+ + + + + + + + +

Headers

+ + $v) echo ''; + ?> +
', htmlspecialchars($k), '', htmlspecialchars($v), '
+ + + + +

$

+ +

empty

+ + + $v) echo ''; + ?> +
', htmlspecialchars($k), '', self::dump($v, TRUE), '
+ + + + + + + +

Headers

+ +
';
+        ?>
+ +

no headers

+ + + + + + + + \ No newline at end of file diff --git a/tests/Tracy/_test.bat b/tests/Tracy/_test.bat new file mode 100644 index 000000000..80de0729a --- /dev/null +++ b/tests/Tracy/_test.bat @@ -0,0 +1,5 @@ +call ../config.bat + +for %%f in (test.*.php) do "%php%" -q "%%f" > "output\%%f.html" + +IF NOT "%diff%"=="" ( start "" %diff% output ref ) diff --git a/tests/Tracy/ref/test.dump.php.html b/tests/Tracy/ref/test.dump.php.html new file mode 100644 index 000000000..287c93bfc --- /dev/null +++ b/tests/Tracy/ref/test.dump.php.html @@ -0,0 +1,51 @@ + + +

Nette::Debug dump test

+ + +

Check mode

bool(false) + +

HTML mode

string(20) "<a href="#">test</a>"
+
+
array(5) {
+  [0] => int(10)
+  [1] => float(20.2)
+  [2] => bool(true)
+  [3] => NULL
+  [4] => string(5) "hello"
+}
+
+
object(stdClass)#1 (2) {
+  ["item1"] => array(5) {
+    [0] => int(10)
+    [1] => float(20.2)
+    [2] => bool(true)
+    [3] => NULL
+    [4] => string(5) "hello"
+  }
+  ["item2"] => string(5) "hello"
+}
+
+

Text mode

string(20) "test" + +array(5) { + [0] => int(10) + [1] => float(20.2) + [2] => bool(true) + [3] => NULL + [4] => string(5) "hello" +} + +object(stdClass)#1 (2) { + ["item1"] => array(5) { + [0] => int(10) + [1] => float(20.2) + [2] => bool(true) + [3] => NULL + [4] => string(5) "hello" + } + ["item2"] => string(5) "hello" +} + diff --git a/tests/Tracy/ref/test.error.php.html b/tests/Tracy/ref/test.error.php.html new file mode 100644 index 000000000..567d5befa --- /dev/null +++ b/tests/Tracy/ref/test.error.php.html @@ -0,0 +1,3 @@ +Fatal error 'Error generated by trigger_error' in D:\Web\nette\_trunk\tests\Debug\test.error.php on line 26 +PHP version 5.2.5 +Nette Framework version 0.7 diff --git a/tests/Tracy/ref/test.exception.html.php.html b/tests/Tracy/ref/test.exception.html.php.html new file mode 100644 index 000000000..6560e70cc --- /dev/null +++ b/tests/Tracy/ref/test.exception.html.php.html @@ -0,0 +1,431 @@ + + + + + + + + Exception + + + + + + + + + + +
+

Exception

+ +

The my exception

+
+ + + +
+

Source file

+ +
+

File: D:\Web\nette\_trunk\tests\Debug\test.exception.html.php   Line: 29

+ +
Line 24:    }
+Line 25:    
+Line 26:    
+Line 27:    function third($arg1)
+Line 28:    {
+Line 29:        throw new Exception('The my exception', 123);
+Line 30:    }
+Line 31:    
+Line 32:    
+Line 33:    first(10, 'any string');
+
+
+
+ + + + +
+

Call stack

+ +
+
    +
  1. + + Debug/test.exception.html.php (23) + source   + third + (arguments ) +

    + + + + + + +
  2. +
  3. + + Debug/test.exception.html.php (16) + source   + second + (arguments ) +

    + + + + + + +
  4. +
  5. + + Debug/test.exception.html.php (33) + source   + first + (arguments ) +

    + + + + + + +
  6. +
+
+
+ + + + + + + +
+

Environment

+ + +
+ + + + +
+

Exception

+ + +
+ + + + + + + + +
+

HTTP request

+ + +
+ + + +
+

HTTP response

+ + +
+ + + + + + \ No newline at end of file diff --git a/tests/Tracy/ref/test.exception.php.html b/tests/Tracy/ref/test.exception.php.html new file mode 100644 index 000000000..e625bd7ba --- /dev/null +++ b/tests/Tracy/ref/test.exception.php.html @@ -0,0 +1,8 @@ +exception 'Exception' with message 'The my exception' in D:\Web\nette\_trunk\tests\Debug\test.exception.php:26 +Stack trace: +#0 D:\Web\nette\_trunk\tests\Debug\test.exception.php(20): third(Array) +#1 D:\Web\nette\_trunk\tests\Debug\test.exception.php(13): second(true, false) +#2 D:\Web\nette\_trunk\tests\Debug\test.exception.php(30): first(10, 'any string') +#3 {main} +PHP version 5.2.5 +Nette Framework version 0.7 diff --git a/tests/Tracy/ref/test.filter.php.html b/tests/Tracy/ref/test.filter.php.html new file mode 100644 index 000000000..85a7b0078 --- /dev/null +++ b/tests/Tracy/ref/test.filter.php.html @@ -0,0 +1,393 @@ + + + + + + + + Fatal error + + + + + + + + + + +
+

Fatal error

+ +

The my error

+
+ + + +
+

Source file

+ +
+

File: D:\Web\nette\_trunk\tests\Debug\test.filter.php   Line: 19

+ +
Line 14:        $struct = (object) array(
+Line 15:            'arr' => array(
+Line 16:                'password' => 'druhe heslo',
+Line 17:            ),
+Line 18:        );
+Line 19:        trigger_error('The my error', E_USER_ERROR);
+Line 20:    }
+Line 21:    
+Line 22:    
+Line 23:    
+
+
+
+ + + + +
+

Call stack

+ +
+
    +
  1. + + Debug/test.filter.php (19) + source   + trigger_error + (arguments ) +

    + + + + + + +
  2. +
  3. + + Debug/test.filter.php (24) + source   + first + (arguments ) +

    + + + + + + +
  4. +
+
+
+ + + + + + + +
+

Environment

+ + +
+ + + + + + + + + + + + +
+

HTTP request

+ + +
+ + + +
+

HTTP response

+ + +
+ + + + + + \ No newline at end of file diff --git a/tests/Tracy/ref/test.notice.php.html b/tests/Tracy/ref/test.notice.php.html new file mode 100644 index 000000000..cbd3de502 --- /dev/null +++ b/tests/Tracy/ref/test.notice.php.html @@ -0,0 +1,4 @@ +

Nette::Debug notice test

+ + +Notice: Undefined variable: x in D:\Web\nette\_trunk\tests\Debug\test.notice.php on line 28 diff --git a/tests/Tracy/ref/test.timer.php.html b/tests/Tracy/ref/test.timer.php.html new file mode 100644 index 000000000..91d4c6631 --- /dev/null +++ b/tests/Tracy/ref/test.timer.php.html @@ -0,0 +1,4 @@ +

Nette::Debug timer test

+ + +2 \ No newline at end of file diff --git a/tests/Tracy/ref/test.warning.php.html b/tests/Tracy/ref/test.warning.php.html new file mode 100644 index 000000000..80fb60afc --- /dev/null +++ b/tests/Tracy/ref/test.warning.php.html @@ -0,0 +1,4 @@ +

Nette::Debug warning test

+ + +Warning: rename(..,..): Permission denied in D:\Web\nette\_trunk\tests\Debug\test.warning.php on line 28 diff --git a/tests/Tracy/test.dump.php b/tests/Tracy/test.dump.php new file mode 100644 index 000000000..7936e520a --- /dev/null +++ b/tests/Tracy/test.dump.php @@ -0,0 +1,44 @@ + + +

Nette::Debug dump test

+ + + $arr, 'item2' => 'hello'); + + +echo '

Check mode

'; + +Debug::dump(Debug::$html); + + +echo '

HTML mode

'; + +Debug::$html = TRUE; + +Debug::dump('test'); + +Debug::dump($arr); + +Debug::dump($obj); + + + +echo '

Text mode

'; + +Debug::$html = FALSE; + +Debug::dump('test'); + +Debug::dump($arr); + +Debug::dump($obj); diff --git a/tests/Tracy/test.error.php b/tests/Tracy/test.error.php new file mode 100644 index 000000000..fed0d2943 --- /dev/null +++ b/tests/Tracy/test.error.php @@ -0,0 +1,30 @@ + array( + 'password' => 'druhe heslo', + ), + ); + trigger_error('The my error', E_USER_ERROR); +} + + + +first('root', 'prvni heslo'); \ No newline at end of file diff --git a/tests/Tracy/test.notice.php b/tests/Tracy/test.notice.php new file mode 100644 index 000000000..31bc1c36e --- /dev/null +++ b/tests/Tracy/test.notice.php @@ -0,0 +1,32 @@ +

Nette::Debug notice test

+ + +Nette::Debug timer test + + +Nette::Debug warning test + + +