From 1032d825d32dd043280cfbf6fdb3224eec2323ea Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 9 Oct 2017 18:33:45 +0200 Subject: [PATCH 01/13] opened 3.0-dev --- composer.json | 2 +- src/Tracy/Debugger.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index f170f0ae3..8a5be3e04 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "3.0-dev" } } } diff --git a/src/Tracy/Debugger.php b/src/Tracy/Debugger.php index 5a6423400..16deced2a 100644 --- a/src/Tracy/Debugger.php +++ b/src/Tracy/Debugger.php @@ -15,7 +15,7 @@ */ class Debugger { - const VERSION = '2.5.2'; + const VERSION = '3.0-dev'; /** server modes for Debugger::enable() */ const From 98aa373a5200d078d17d05e4d3a35957ab69f5af Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 9 Oct 2017 18:35:20 +0200 Subject: [PATCH 02/13] requires PHP 7.1 --- .travis.yml | 4 ---- appveyor.yml | 2 +- composer.json | 6 +++--- readme.md | 2 ++ 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3b1d5df58..332fc3606 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,5 @@ language: php php: - - 5.4 - - 5.5 - - 5.6 - - 7.0 - 7.1 - 7.2 diff --git a/appveyor.yml b/appveyor.yml index f198beafb..f682febe0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,7 +14,7 @@ install: # Install PHP - IF EXIST c:\php (SET PHP=0) ELSE (mkdir c:\php) - IF %PHP%==1 cd c:\php - - IF %PHP%==1 curl https://windows.php.net/downloads/releases/archives/php-5.6.14-Win32-VC11-x86.zip --output php.zip + - IF %PHP%==1 curl https://windows.php.net/downloads/releases/archives/php-7.1.17-Win32-VC14-x64.zip --output php.zip - IF %PHP%==1 7z x php.zip >nul - IF %PHP%==1 echo extension_dir=ext >> php.ini - IF %PHP%==1 echo extension=php_openssl.dll >> php.ini diff --git a/composer.json b/composer.json index 8a5be3e04..d1cdc8cda 100644 --- a/composer.json +++ b/composer.json @@ -15,13 +15,13 @@ } ], "require": { - "php": ">=5.4.4", + "php": ">=7.1", "ext-session": "*", "ext-json": "*" }, "require-dev": { - "nette/di": "~2.3", - "nette/tester": "~1.7" + "nette/di": "^2.3", + "nette/tester": "^2.0" }, "suggest": { "https://nette.org/donate": "Please support Tracy via a donation" diff --git a/readme.md b/readme.md index a93826b0c..445786df9 100644 --- a/readme.md +++ b/readme.md @@ -41,6 +41,8 @@ composer require tracy/tracy Alternatively, you can download the whole package or [tracy.phar](https://github.com/nette/tester/releases) file. +Tracy 3.0-dev requires PHP version 7.1 or newer (supports PHP up to 7.2) and is compatible with Chrome 49+, Firefox 45+, MS Edge 12+, Safari 10+ and iOS Safari 10.2+. + Tracy 2.5 requires PHP version 5.4.4 or newer (supports PHP up to 7.2) and is compatible with Chrome 49+, Firefox 45+, MS Edge 12+, Safari 10+ and iOS Safari 10.2+. Tracy 2.4 requires PHP version 5.4.4 or newer (supports PHP up to 7.2) and is compatible with Chrome 29+, Firefox 28+, IE 11+, MS Edge 12+, Safari 9+ and iOS Safari 9.2+. From 93ce168061bee01016e6ea816d6b7efb99b0b6a5 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 2 Dec 2015 16:32:29 +0100 Subject: [PATCH 03/13] Throwable replaces Exception --- src/Tracy/Bar.php | 3 --- src/Tracy/BlueScreen.php | 5 ++--- src/Tracy/Debugger.php | 13 ++----------- src/Tracy/FireLogger.php | 2 +- src/Tracy/Logger.php | 8 ++++---- 5 files changed, 9 insertions(+), 22 deletions(-) diff --git a/src/Tracy/Bar.php b/src/Tracy/Bar.php index 746cb5bd3..34c85689b 100644 --- a/src/Tracy/Bar.php +++ b/src/Tracy/Bar.php @@ -163,10 +163,7 @@ private function renderPanels($suffix = null) $e = new \Exception('Support for Nette\Diagnostics\IBarPanel is deprecated'); } - } catch (\Exception $e) { } catch (\Throwable $e) { - } - if (isset($e)) { while (ob_get_level() > $obLevel) { // restore ob-level if broken ob_end_clean(); } diff --git a/src/Tracy/BlueScreen.php b/src/Tracy/BlueScreen.php index 51f92ce53..35177e279 100644 --- a/src/Tracy/BlueScreen.php +++ b/src/Tracy/BlueScreen.php @@ -68,7 +68,7 @@ public function addAction($action) /** * Renders blue screen. - * @param \Exception|\Throwable $exception + * @param \Throwable $exception * @return void */ public function render($exception) @@ -87,7 +87,7 @@ public function render($exception) /** * Renders blue screen to file (if file exists, it will not be overwritten). - * @param \Exception|\Throwable $exception + * @param \Throwable $exception * @param string $file file path * @return void */ @@ -152,7 +152,6 @@ private function renderPanels($ex) } $res[] = (object) $panel; continue; - } catch (\Exception $e) { } catch (\Throwable $e) { } while (ob_get_level() > $obLevel) { // restore ob-level if broken diff --git a/src/Tracy/Debugger.php b/src/Tracy/Debugger.php index 16deced2a..40fd83c85 100644 --- a/src/Tracy/Debugger.php +++ b/src/Tracy/Debugger.php @@ -282,7 +282,7 @@ public static function shutdownHandler() /** * Handler to catch uncaught exception. - * @param \Exception|\Throwable $exception + * @param \Throwable $exception * @return void * @internal */ @@ -306,7 +306,6 @@ public static function exceptionHandler($exception, $exit = true) if (self::$productionMode) { try { self::log($exception, self::EXCEPTION); - } catch (\Exception $e) { } catch (\Throwable $e) { } @@ -338,8 +337,6 @@ public static function exceptionHandler($exception, $exit = true) if ($file && self::$browser) { exec(self::$browser . ' ' . escapeshellarg($file)); } - } catch (\Exception $e) { - echo "$s\nUnable to log error: {$e->getMessage()}\n"; } catch (\Throwable $e) { echo "$s\nUnable to log error: {$e->getMessage()}\n"; } @@ -350,13 +347,9 @@ public static function exceptionHandler($exception, $exit = true) foreach (self::$onFatalError as $handler) { call_user_func($handler, $exception); } - } catch (\Exception $e) { } catch (\Throwable $e) { - } - if ($e) { try { self::log($e, self::EXCEPTION); - } catch (\Exception $e) { } catch (\Throwable $e) { } } @@ -381,7 +374,7 @@ public static function errorHandler($severity, $message, $file, $line, $context if ($severity === E_RECOVERABLE_ERROR || $severity === E_USER_ERROR) { if (Helpers::findTrace(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), '*::__toString')) { - $previous = isset($context['e']) && ($context['e'] instanceof \Exception || $context['e'] instanceof \Throwable) ? $context['e'] : null; + $previous = isset($context['e']) && $context['e'] instanceof \Throwable ? $context['e'] : null; $e = new ErrorException($message, 0, $severity, $file, $line, $previous); $e->context = $context; self::exceptionHandler($e); @@ -400,7 +393,6 @@ public static function errorHandler($severity, $message, $file, $line, $context Helpers::improveException($e); try { self::log($e, self::ERROR); - } catch (\Exception $foo) { } catch (\Throwable $foo) { } return null; @@ -425,7 +417,6 @@ public static function errorHandler($severity, $message, $file, $line, $context } elseif (self::$productionMode) { try { self::log("$message in $file:$line", self::ERROR); - } catch (\Exception $foo) { } catch (\Throwable $foo) { } return null; diff --git a/src/Tracy/FireLogger.php b/src/Tracy/FireLogger.php index 0167bf67c..b6c46a664 100644 --- a/src/Tracy/FireLogger.php +++ b/src/Tracy/FireLogger.php @@ -52,7 +52,7 @@ public function log($message, $priority = self::DEBUG) $item['template'] = array_shift($args); } - if (isset($args[0]) && ($args[0] instanceof \Exception || $args[0] instanceof \Throwable)) { + if (isset($args[0]) && $args[0] instanceof \Throwable) { $e = array_shift($args); $trace = $e->getTrace(); if ( diff --git a/src/Tracy/Logger.php b/src/Tracy/Logger.php index 0f85a052e..b61c6ce33 100644 --- a/src/Tracy/Logger.php +++ b/src/Tracy/Logger.php @@ -59,7 +59,7 @@ public function log($message, $priority = self::INFO) throw new \RuntimeException("Logging directory '$this->directory' is not found or is not directory."); } - $exceptionFile = $message instanceof \Exception || $message instanceof \Throwable + $exceptionFile = $message instanceof \Throwable ? $this->getExceptionFile($message) : null; $line = static::formatLogLine($message, $exceptionFile); @@ -87,7 +87,7 @@ public function log($message, $priority = self::INFO) */ public static function formatMessage($message) { - if ($message instanceof \Exception || $message instanceof \Throwable) { + if ($message instanceof \Throwable) { while ($message) { $tmp[] = ($message instanceof \ErrorException ? Helpers::errorTypeToString($message->getSeverity()) . ': ' . $message->getMessage() @@ -121,7 +121,7 @@ public static function formatLogLine($message, $exceptionFile = null) /** - * @param \Exception|\Throwable $exception + * @param \Throwable $exception * @return string */ public function getExceptionFile($exception) @@ -146,7 +146,7 @@ public function getExceptionFile($exception) /** * Logs exception to the file if file doesn't exist. - * @param \Exception|\Throwable $exception + * @param \Throwable $exception * @return string logged error filename */ protected function logException($exception, $file = null) From 17d2eae9e6552ea335157bd519fda1387fbc898e Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 11 Mar 2018 14:58:21 +0100 Subject: [PATCH 04/13] used PHP 7.1 features --- src/Bridges/Nette/MailSender.php | 2 +- src/Bridges/Nette/TracyExtension.php | 6 +++--- src/Tracy/Bar.php | 4 ++-- src/Tracy/BlueScreen.php | 2 +- src/Tracy/Debugger.php | 18 ++++++++---------- src/Tracy/Dumper.php | 12 ++++++------ src/Tracy/Helpers.php | 6 +++--- src/Tracy/Logger.php | 4 ++-- src/Tracy/OutputDebugger.php | 2 +- src/Tracy/assets/Bar/info.panel.phtml | 12 ++++++------ src/shortcuts.php | 2 +- 11 files changed, 34 insertions(+), 36 deletions(-) diff --git a/src/Bridges/Nette/MailSender.php b/src/Bridges/Nette/MailSender.php index 4658d584e..c10114903 100644 --- a/src/Bridges/Nette/MailSender.php +++ b/src/Bridges/Nette/MailSender.php @@ -39,7 +39,7 @@ public function __construct(Nette\Mail\IMailer $mailer, $fromEmail = null) */ public function send($message, $email) { - $host = preg_replace('#[^\w.-]+#', '', isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : php_uname('n')); + $host = preg_replace('#[^\w.-]+#', '', $_SERVER['HTTP_HOST'] ?? php_uname('n')); $mail = new Nette\Mail\Message; $mail->setHeader('X-Mailer', 'Tracy'); diff --git a/src/Bridges/Nette/TracyExtension.php b/src/Bridges/Nette/TracyExtension.php index 2398c3927..5dff52622 100644 --- a/src/Bridges/Nette/TracyExtension.php +++ b/src/Bridges/Nette/TracyExtension.php @@ -93,13 +93,13 @@ public function afterCompile(Nette\PhpGenerator\ClassType $class) } $logger = $builder->getDefinition($this->prefix('logger')); - if ($logger->getFactory()->getEntity() !== ['Tracy\Debugger', 'getLogger']) { + if ($logger->getFactory()->getEntity() !== [Tracy\Debugger::class, 'getLogger']) { $initialize->addBody($builder->formatPhp('Tracy\Debugger::setLogger(?);', [$logger])); } - if ($this->config['netteMailer'] && $builder->getByType('Nette\Mail\IMailer')) { + if ($this->config['netteMailer'] && $builder->getByType(Nette\Mail\IMailer::class)) { $initialize->addBody($builder->formatPhp('Tracy\Debugger::getLogger(?)->mailer = ?;', [ $logger, - [new Nette\DI\Statement('Tracy\Bridges\Nette\MailSender', ['fromEmail' => $this->config['fromEmail']]), 'send'], + [new Nette\DI\Statement(Tracy\Bridges\Nette\MailSender::class, ['fromEmail' => $this->config['fromEmail']]), 'send'], ])); } diff --git a/src/Tracy/Bar.php b/src/Tracy/Bar.php index 34c85689b..032d5791e 100644 --- a/src/Tracy/Bar.php +++ b/src/Tracy/Bar.php @@ -49,7 +49,7 @@ public function addPanel(IBarPanel $panel, $id = null) */ public function getPanel($id) { - return isset($this->panels[$id]) ? $this->panels[$id] : null; + return $this->panels[$id] ?? null; } @@ -186,7 +186,7 @@ private function renderPanels($suffix = null) */ public function dispatchAssets() { - $asset = isset($_GET['_tracy_bar']) ? $_GET['_tracy_bar'] : null; + $asset = $_GET['_tracy_bar'] ?? null; if ($asset === 'js') { header('Content-Type: text/javascript'); header('Cache-Control: max-age=864000'); diff --git a/src/Tracy/BlueScreen.php b/src/Tracy/BlueScreen.php index 35177e279..b64b9c22f 100644 --- a/src/Tracy/BlueScreen.php +++ b/src/Tracy/BlueScreen.php @@ -146,7 +146,7 @@ private function renderPanels($ex) $res = []; foreach ($this->panels as $callback) { try { - $panel = call_user_func($callback, $ex); + $panel = $callback($ex); if (empty($panel['tab']) || empty($panel['panel'])) { continue; } diff --git a/src/Tracy/Debugger.php b/src/Tracy/Debugger.php index 40fd83c85..e585980e1 100644 --- a/src/Tracy/Debugger.php +++ b/src/Tracy/Debugger.php @@ -15,15 +15,15 @@ */ class Debugger { - const VERSION = '3.0-dev'; + public const VERSION = '3.0-dev'; /** server modes for Debugger::enable() */ - const + public const DEVELOPMENT = false, PRODUCTION = true, DETECT = null; - const COOKIE_SECRET = 'tracy-debug'; + public const COOKIE_SECRET = 'tracy-debug'; /** @var bool in production mode is suppressed any debugging output */ public static $productionMode = self::DETECT; @@ -80,7 +80,7 @@ class Debugger public static $email; /** for Debugger::log() and Debugger::fireLog() */ - const + public const DEBUG = ILogger::DEBUG, INFO = ILogger::INFO, WARNING = ILogger::WARNING, @@ -153,7 +153,7 @@ public static function enable($mode = null, $logDirectory = null, $email = null) self::$maxLen = &self::$maxLength; self::$reserved = str_repeat('t', 30000); - self::$time = isset($_SERVER['REQUEST_TIME_FLOAT']) ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime(true); + self::$time = $_SERVER['REQUEST_TIME_FLOAT'] ?? microtime(true); self::$obLevel = ob_get_level(); self::$cpuUsage = !self::$productionMode && function_exists('getrusage') ? getrusage() : null; @@ -345,7 +345,7 @@ public static function exceptionHandler($exception, $exit = true) try { $e = null; foreach (self::$onFatalError as $handler) { - call_user_func($handler, $exception); + $handler($exception); } } catch (\Throwable $e) { try { @@ -455,7 +455,7 @@ public static function getBlueScreen() self::$blueScreen = new BlueScreen; self::$blueScreen->info = [ 'PHP ' . PHP_VERSION, - isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : null, + $_SERVER['SERVER_SOFTWARE'] ?? null, 'Tracy ' . self::VERSION, ]; } @@ -616,9 +616,7 @@ public static function fireLog($message) */ public static function detectDebugMode($list = null) { - $addr = isset($_SERVER['REMOTE_ADDR']) - ? $_SERVER['REMOTE_ADDR'] - : php_uname('n'); + $addr = $_SERVER['REMOTE_ADDR'] ?? php_uname('n'); $secret = isset($_COOKIE[self::COOKIE_SECRET]) && is_string($_COOKIE[self::COOKIE_SECRET]) ? $_COOKIE[self::COOKIE_SECRET] : null; diff --git a/src/Tracy/Dumper.php b/src/Tracy/Dumper.php index 650473160..8e9177d23 100644 --- a/src/Tracy/Dumper.php +++ b/src/Tracy/Dumper.php @@ -13,7 +13,7 @@ */ class Dumper { - const + public const DEPTH = 'depth', // how many nested levels of array/object properties display (defaults to 4) TRUNCATE = 'truncate', // how truncate long strings? (defaults to 150) COLLAPSE = 'collapse', // collapse top array/object or how big are collapsed? (defaults to 14) @@ -23,7 +23,7 @@ class Dumper LIVE = 'live', // will be rendered using JavaScript DEBUGINFO = 'debuginfo'; // use magic method __debugInfo if exists (defaults to false) - const + public const LOCATION_SOURCE = 0b0001, // shows where dump was called LOCATION_LINK = 0b0010, // appends clickable anchor LOCATION_CLASS = 0b0100; // shows where class is defined @@ -104,7 +104,7 @@ public static function toHtml($var, array $options = null) }); $live = !empty($options[self::LIVE]) && $var && (is_array($var) || is_object($var) || is_resource($var)); - list($file, $line, $code) = $loc ? self::findLocation() : null; + [$file, $line, $code] = $loc ? self::findLocation() : null; $locAttrs = $file && $loc & self::LOCATION_SOURCE ? Helpers::formatHtml( ' title="%in file % on line %" data-tracy-href="%"', "$code\n", $file, $line, Helpers::editorUri($file, $line) ) : null; @@ -292,7 +292,7 @@ private static function dumpResource(&$var, $options, $level) . '#' . (int) $var . ''; if (isset(self::$resources[$type])) { $out = "$out\n
"; - foreach (call_user_func(self::$resources[$type], $var) as $k => $v) { + foreach ((self::$resources[$type])($var) as $k => $v) { $out .= ' ' . str_repeat('| ', $level) . '' . '' . Helpers::escapeHtml($k) . ' => ' . self::dumpVar($v, $options, $level + 1); } @@ -380,7 +380,7 @@ private static function toJson(&$var, $options, $level = 0) $type = get_resource_type($var); $obj = ['id' => self::$livePrefix . (int) $var, 'name' => $type . ' resource']; if (isset(self::$resources[$type])) { - foreach (call_user_func(self::$resources[$type], $var) as $k => $v) { + foreach ((self::$resources[$type])($var) as $k => $v) { $obj['items'][] = [$k, self::toJson($v, $options, $level + 1)]; } } @@ -460,7 +460,7 @@ private static function exportObject($obj, array $exporters, $useDebugInfo) { foreach ($exporters as $type => $dumper) { if (!$type || $obj instanceof $type) { - return call_user_func($dumper, $obj); + return $dumper($obj); } } diff --git a/src/Tracy/Helpers.php b/src/Tracy/Helpers.php index 6c7ced40e..694877896 100644 --- a/src/Tracy/Helpers.php +++ b/src/Tracy/Helpers.php @@ -110,7 +110,7 @@ public static function fixStack($exception) $frame = [ 'file' => $row['file'], 'line' => $row['line'], - 'function' => isset($row['function']) ? $row['function'] : '*unknown*', + 'function' => $row['function'] ?? '*unknown*', 'args' => [], ]; if (!empty($row['class'])) { @@ -154,7 +154,7 @@ public static function errorTypeToString($type) E_DEPRECATED => 'Deprecated', E_USER_DEPRECATED => 'User Deprecated', ]; - return isset($types[$type]) ? $types[$type] : 'Unknown error'; + return $types[$type] ?? 'Unknown error'; } @@ -163,7 +163,7 @@ public static function getSource() { if (isset($_SERVER['REQUEST_URI'])) { return (!empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https://' : 'http://') - . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '') + . ($_SERVER['HTTP_HOST'] ?? '') . $_SERVER['REQUEST_URI']; } else { return 'CLI (PID: ' . getmypid() . ')' diff --git a/src/Tracy/Logger.php b/src/Tracy/Logger.php index b61c6ce33..e4d840f3d 100644 --- a/src/Tracy/Logger.php +++ b/src/Tracy/Logger.php @@ -174,7 +174,7 @@ protected function sendEmail($message) && @filemtime($this->directory . '/email-sent') + $snooze < time() // @ file may not exist && @file_put_contents($this->directory . '/email-sent', 'sent') // @ file may not be writable ) { - call_user_func($this->mailer, $message, implode(', ', (array) $this->email)); + ($this->mailer)($message, implode(', ', (array) $this->email)); } } @@ -188,7 +188,7 @@ protected function sendEmail($message) */ public function defaultMailer($message, $email) { - $host = preg_replace('#[^\w.-]+#', '', isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : php_uname('n')); + $host = preg_replace('#[^\w.-]+#', '', $_SERVER['HTTP_HOST'] ?? php_uname('n')); $parts = str_replace( ["\r\n", "\n"], ["\n", PHP_EOL], diff --git a/src/Tracy/OutputDebugger.php b/src/Tracy/OutputDebugger.php index 2e9c599cc..b4ea065ce 100644 --- a/src/Tracy/OutputDebugger.php +++ b/src/Tracy/OutputDebugger.php @@ -13,7 +13,7 @@ */ class OutputDebugger { - const BOM = "\xEF\xBB\xBF"; + private const BOM = "\xEF\xBB\xBF"; /** @var array of [file, line, output, stack] */ private $list = []; diff --git a/src/Tracy/assets/Bar/info.panel.phtml b/src/Tracy/assets/Bar/info.panel.phtml index fa6e48663..9e6b8f58b 100644 --- a/src/Tracy/assets/Bar/info.panel.phtml +++ b/src/Tracy/assets/Bar/info.panel.phtml @@ -34,13 +34,13 @@ $info = [ 'OPcache' => $opcache ? round(count($cachedFiles) * 100 / count(get_included_files())) . '% cached' : null, 'Classes + interfaces + traits' => $countClasses(get_declared_classes()) . ' + ' . $countClasses(get_declared_interfaces()) . ' + ' . $countClasses(get_declared_traits()), - 'Your IP' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null, - 'Server IP' => isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : null, + 'Your IP' => $_SERVER['REMOTE_ADDR'] ?? null, + 'Server IP' => $_SERVER['SERVER_ADDR'] ?? null, 'HTTP method / response code' => isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] . ' / ' . http_response_code() : null, 'PHP' => PHP_VERSION, 'Xdebug' => extension_loaded('xdebug') ? phpversion('xdebug') : null, 'Tracy' => Debugger::VERSION, - 'Server' => isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : null, + 'Server' => $_SERVER['SERVER_SOFTWARE'] ?? null, ]; $info = array_map('strval', array_filter($info + (array) $this->data)); @@ -51,10 +51,10 @@ if (class_exists('Composer\Autoload\ClassLoader', false)) { $composer = @json_decode(file_get_contents($lockFile)); // @ may not exist or be valid list($packages, $devPackages) = [(array) @$composer->packages, (array) @$composer->{'packages-dev'}]; // @ keys may not exist foreach ([&$packages, &$devPackages] as &$items) { - @array_walk($items, function($package) { // @ keys may not exist - $package->hash = $package->source->reference ?: $package->dist->reference; + array_walk($items, function($package) { + $package->hash = $package->source->reference ?? $package->dist->reference ?? null; }, $items); - usort($items, function ($a, $b) { return strcmp($a->name, $b->name); }); + usort($items, function ($a, $b) { return $a->name <=> $b->name; }); } } diff --git a/src/shortcuts.php b/src/shortcuts.php index 02c70b20c..1215c04e5 100644 --- a/src/shortcuts.php +++ b/src/shortcuts.php @@ -36,7 +36,7 @@ function dumpe($var) */ function bdump($var) { - call_user_func_array('Tracy\Debugger::barDump', func_get_args()); + Tracy\Debugger::barDump(...func_get_args()); return $var; } } From 6b54db482fc3f169e35ea581319332ce49051b12 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 21 Mar 2018 03:16:19 +0100 Subject: [PATCH 05/13] added PHP 7.1 typehints --- src/Bridges/Nette/Bridge.php | 14 ++-- src/Bridges/Nette/MailSender.php | 6 +- src/Bridges/Nette/TracyExtension.php | 2 +- src/Tracy/Bar.php | 31 +++----- src/Tracy/BlueScreen.php | 44 ++++------- src/Tracy/Debugger.php | 75 ++++++------------- src/Tracy/DefaultBarPanel.php | 8 +- src/Tracy/Dumper.php | 74 ++++++------------ src/Tracy/FireLogger.php | 6 +- src/Tracy/Helpers.php | 40 +++++----- src/Tracy/IBarPanel.php | 6 +- src/Tracy/ILogger.php | 2 +- src/Tracy/Logger.php | 27 ++----- src/Tracy/OutputDebugger.php | 14 ++-- src/Tracy/assets/Bar/bar.phtml | 2 +- src/Tracy/assets/Bar/info.panel.phtml | 6 +- src/shortcuts.php | 2 +- .../TracyExtension.services.phpt | 2 +- tests/Tracy/Logger.extensible.phpt | 2 +- tools/create-phar/create-phar.php | 6 +- 20 files changed, 131 insertions(+), 238 deletions(-) diff --git a/src/Bridges/Nette/Bridge.php b/src/Bridges/Nette/Bridge.php index fea7dd355..911448c88 100644 --- a/src/Bridges/Nette/Bridge.php +++ b/src/Bridges/Nette/Bridge.php @@ -19,11 +19,11 @@ */ class Bridge { - public static function initialize() + public static function initialize(): void { $blueScreen = Tracy\Debugger::getBlueScreen(); - $blueScreen->addPanel(function ($e) { + $blueScreen->addPanel(function ($e): ?array { if ($e instanceof Latte\CompileException) { return [ 'tab' => 'Template', @@ -39,9 +39,10 @@ public static function initialize() . '
', ]; } + return null; }); - $blueScreen->addAction(function ($e) { + $blueScreen->addAction(function ($e): ?array { if ( $e instanceof Latte\CompileException && @is_file($e->sourceName) // @ - may trigger error @@ -53,9 +54,10 @@ public static function initialize() 'label' => 'fix it', ]; } + return null; }); - $blueScreen->addAction(function ($e) { + $blueScreen->addAction(function ($e): ?array { if ($e instanceof Nette\MemberAccessException || $e instanceof \LogicException) { $loc = $e instanceof Nette\MemberAccessException ? $e->getTrace()[1] : $e->getTrace()[0]; if (preg_match('#Cannot (?:read|write to) an undeclared property .+::\$(\w+), did you mean \$(\w+)\?#A', $e->getMessage(), $m)) { // @ - may trigger error @@ -71,9 +73,10 @@ public static function initialize() ]; } } + return null; }); - $blueScreen->addPanel(function ($e) { + $blueScreen->addPanel(function ($e): ?array { if ( $e instanceof Nette\Neon\Exception && preg_match('#line (\d+)#', $e->getMessage(), $m) @@ -87,6 +90,7 @@ public static function initialize() : BlueScreen::highlightPhp($trace['args'][0], $m[1]), ]; } + return null; }); } } diff --git a/src/Bridges/Nette/MailSender.php b/src/Bridges/Nette/MailSender.php index c10114903..648f40bf6 100644 --- a/src/Bridges/Nette/MailSender.php +++ b/src/Bridges/Nette/MailSender.php @@ -25,7 +25,7 @@ class MailSender private $fromEmail; - public function __construct(Nette\Mail\IMailer $mailer, $fromEmail = null) + public function __construct(Nette\Mail\IMailer $mailer, string $fromEmail = null) { $this->mailer = $mailer; $this->fromEmail = $fromEmail; @@ -34,10 +34,8 @@ public function __construct(Nette\Mail\IMailer $mailer, $fromEmail = null) /** * @param mixed $message - * @param string $email - * @return void */ - public function send($message, $email) + public function send($message, string $email): void { $host = preg_replace('#[^\w.-]+#', '', $_SERVER['HTTP_HOST'] ?? php_uname('n')); diff --git a/src/Bridges/Nette/TracyExtension.php b/src/Bridges/Nette/TracyExtension.php index 5dff52622..0acaee3ec 100644 --- a/src/Bridges/Nette/TracyExtension.php +++ b/src/Bridges/Nette/TracyExtension.php @@ -43,7 +43,7 @@ class TracyExtension extends Nette\DI\CompilerExtension private $cliMode; - public function __construct($debugMode = false, $cliMode = false) + public function __construct(bool $debugMode = false, bool $cliMode = false) { $this->debugMode = $debugMode; $this->cliMode = $cliMode; diff --git a/src/Tracy/Bar.php b/src/Tracy/Bar.php index 032d5791e..81f72cea8 100644 --- a/src/Tracy/Bar.php +++ b/src/Tracy/Bar.php @@ -25,11 +25,9 @@ class Bar /** * Add custom panel. - * @param IBarPanel $panel - * @param string $id * @return static */ - public function addPanel(IBarPanel $panel, $id = null) + public function addPanel(IBarPanel $panel, string $id = null): self { if ($id === null) { $c = 0; @@ -44,10 +42,8 @@ public function addPanel(IBarPanel $panel, $id = null) /** * Returns panel with given id - * @param string $id - * @return IBarPanel|null */ - public function getPanel($id) + public function getPanel(string $id): ?IBarPanel { return $this->panels[$id] ?? null; } @@ -55,9 +51,8 @@ public function getPanel($id) /** * Renders loading - - - - - - - Fatal Error: Call to undefined function missing_function() - - %A% - - - - -
- -
-
-

Fatal Error

- -

Call to undefined function missing_function() - search► -

-
- - - - - - - - - -
-

Source file

- -
-

File: %a%

-
%d%: -%d%: -%d%: function second($arg1, $arg2) -%d%: { -%d%: third([1, 2, 3]); -%d%: } -%d%: -%d%: -%d%: function third($arg1) -%d%: { -%d%: missing_function(); -%d%: } -%d%: -%d%: -%d%: first(10, 'any string'); -
- - - - - - - -
-

Exception

-
-

-		
- - - - - - -
-

Environment

- -
-

$_SERVER

-
- %A%
-
- - -

$_SESSION

-
%A%
- - - - -

Constants

-
- %A%
-
- - -

Configuration options

-
- %A%
-
- - -
-

HTTP request

- -
-%A% -

$_GET

-

empty

-

$_POST

-

empty

-

$_COOKIE

-

empty

-
- - -
-

HTTP response

- -
-

Headers

-
%A%
-
- - - - -
    -
  • Report generated at %a%
  • -
  • CLI%a?%
  • -
  • PHP %a%
- - -
-
- - - - - -%A%%A% diff --git a/tests/Tracy/expected/Debugger.E_ERROR.html.xdebug.expect b/tests/Tracy/expected/Debugger.E_ERROR.html.xdebug.expect deleted file mode 100644 index 226208cd1..000000000 --- a/tests/Tracy/expected/Debugger.E_ERROR.html.xdebug.expect +++ /dev/null @@ -1,208 +0,0 @@ - -Fatal error: Call to undefined function missing_function() in %a% on line %d% - - - - - - - - Fatal Error: Call to undefined function missing_function() - - %A% - - - - -
- -
-
-

Fatal Error

- -

Call to undefined function missing_function() - search► -

-
- - - - - - - - - -
-

Source file

- -
-

File: %a%

-
%d%: -%d%: -%d%: function second($arg1, $arg2) -%d%: { -%d%: third([1, 2, 3]); -%d%: } -%d%: -%d%: -%d%: function third($arg1) -%d%: { -%d%: missing_function(); -%d%: } -%d%: -%d%: -%d%: first(10, 'any string'); -
- - -
-

Call stack

- -
-
    -
  1. - - %a% - source  - third() -

    - -
    %d%: -%d%: -%d%: function first($arg1, $arg2) -%d%: { -%d%: second(true, false); -%d%: } -%d%: -%d%: -%d%: function second($arg1, $arg2) -%d%: { -%d%: third([1, 2, 3]); -%d%: } -%d%: -%d%: -%d%: function third($arg1) -
    - - -
  2. -
  3. - - %a% - source  - second() -

    - -
    %d%:%A%
    - - -
  4. -
  5. - - %a% - source  - first() -

    - -
    %d%: third([1, 2, 3]); -%d%: } -%d%: -%d%: -%d%: function third($arg1) -%d%: { -%d%: missing_function(); -%d%: } -%d%: -%d%: -%d%: first(10, 'any string'); -%d%: -
    - - -
  6. -
-
- - - - - -
-

Exception

-
-

-		
- - - - - - -
-

Environment

- -
-

$_SERVER

-
- %A%
-
- - -

$_SESSION

-
%A%
- - - - -

Constants

-
- %A%
-
- - -

Configuration options

-
- %A%
-
- - -
-

HTTP request

- -
-%A% -

$_GET

-

empty

-

$_POST

-

empty

-

$_COOKIE

-

empty

-
- - -
-

HTTP response

- -
-

Headers

-
%A%
-
- - - - -
    -
  • Report generated at %a%
  • -
  • CLI%a?%
  • -
  • PHP %a%
- - -
-
- - - - - -%A%%A% diff --git a/tests/Tracy/expected/Debugger.scream.expect b/tests/Tracy/expected/Debugger.scream.expect index 8adc0fd09..db3de6c92 100644 --- a/tests/Tracy/expected/Debugger.scream.expect +++ b/tests/Tracy/expected/Debugger.scream.expect @@ -1,7 +1,5 @@ -%a%: mktime(): You should be using the time() function instead in %a% on line %d% - -Deprecated: mktime(): %a% +Deprecated: mktime(): You should be using the time() function instead in %a% on line %d% Notice: Undefined variable: x in %a% on line %d% From 0d0dbd81b1a51c672f8586ab48068fe588e62330 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 10 Nov 2015 18:35:12 +0100 Subject: [PATCH 09/13] Exception::__toString in PHP7 generates output compatible with Tracy --- src/Tracy/Debugger.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Tracy/Debugger.php b/src/Tracy/Debugger.php index fd7f730c3..ad0ad28ef 100644 --- a/src/Tracy/Debugger.php +++ b/src/Tracy/Debugger.php @@ -316,20 +316,17 @@ public static function exceptionHandler(\Throwable $exception, bool $exit = true } else { self::fireLog($exception); - $s = get_class($exception) . ($exception->getMessage() === '' ? '' : ': ' . $exception->getMessage()) - . ' in ' . $exception->getFile() . ':' . $exception->getLine() - . "\nStack trace:\n" . $exception->getTraceAsString(); try { $file = self::log($exception, self::EXCEPTION); if ($file && !headers_sent()) { header("X-Tracy-Error-Log: $file"); } - echo "$s\n" . ($file ? "(stored in $file)\n" : ''); + echo "$exception\n" . ($file ? "(stored in $file)\n" : ''); if ($file && self::$browser) { exec(self::$browser . ' ' . escapeshellarg($file)); } } catch (\Throwable $e) { - echo "$s\nUnable to log error: {$e->getMessage()}\n"; + echo "$exception\nUnable to log error: {$e->getMessage()}\n"; } } From 6656db199faec6eddba738999c3c86e5e73ebe9d Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 13 Aug 2018 19:17:54 +0200 Subject: [PATCH 10/13] removed deprecated stuff --- src/Tracy/Bar.php | 3 --- src/Tracy/Debugger.php | 4 ---- 2 files changed, 7 deletions(-) diff --git a/src/Tracy/Bar.php b/src/Tracy/Bar.php index 3709a86a1..247163613 100644 --- a/src/Tracy/Bar.php +++ b/src/Tracy/Bar.php @@ -149,9 +149,6 @@ private function renderPanels(string $suffix = null): array try { $tab = $panel->getTab(); $panelHtml = $tab ? $panel->getPanel() : null; - if ($tab && $panel instanceof \Nette\Diagnostics\IBarPanel) { - $e = new \Exception('Support for Nette\Diagnostics\IBarPanel is deprecated'); - } } catch (\Throwable $e) { while (ob_get_level() > $obLevel) { // restore ob-level if broken diff --git a/src/Tracy/Debugger.php b/src/Tracy/Debugger.php index ad0ad28ef..3d6a79ecd 100644 --- a/src/Tracy/Debugger.php +++ b/src/Tracy/Debugger.php @@ -67,9 +67,6 @@ class Debugger /** @var bool display location by dump()? */ public static $showLocation = false; - /** @deprecated */ - public static $maxLen = 150; - /********************* logging ****************d*g**/ /** @var string|null name of the directory where errors should be logged */ @@ -152,7 +149,6 @@ public static function enable($mode = null, string $logDirectory = null, string self::$productionMode = is_bool($mode) ? $mode : !self::detectDebugMode($mode); } - self::$maxLen = &self::$maxLength; self::$reserved = str_repeat('t', 30000); self::$time = $_SERVER['REQUEST_TIME_FLOAT'] ?? microtime(true); self::$obLevel = ob_get_level(); From a53ae21c5b0e65919d580bf2db31c97e77efb4de Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 13 Aug 2018 19:21:33 +0200 Subject: [PATCH 11/13] bridge: compatible with nette/di 3.0, dropped 2.3 --- composer.json | 2 +- tests/Tracy.Bridges/TracyExtension.services.phpt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index d1cdc8cda..844a6c182 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "ext-json": "*" }, "require-dev": { - "nette/di": "^2.3", + "nette/di": "^2.4 || ^3.0", "nette/tester": "^2.0" }, "suggest": { diff --git a/tests/Tracy.Bridges/TracyExtension.services.phpt b/tests/Tracy.Bridges/TracyExtension.services.phpt index 1be815a4f..a3806a10c 100644 --- a/tests/Tracy.Bridges/TracyExtension.services.phpt +++ b/tests/Tracy.Bridges/TracyExtension.services.phpt @@ -23,6 +23,7 @@ class CustomLogger implements ILogger $compiler = new DI\Compiler; +$compiler->setClassName('Container'); $compiler->addExtension('tracy', new TracyExtension); $compiler->addConfig([ 'tracy' => [ @@ -33,7 +34,7 @@ $compiler->addConfig([ ], ]); -eval(@$compiler->compile([], 'Container')); // @ compatiblity with DI 2.3 & 2.4 +eval($compiler->compile()); $container = new Container; $container->initialize(); From 6b9675e60909e2742f7586edd175b594cdfbf87d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Tvrd=C3=ADk?= <_github.com@jantvrdik.com> Date: Mon, 27 Aug 2018 20:37:07 +0200 Subject: [PATCH 12/13] add PSR-3 adapters (#314) --- composer.json | 3 +- src/Bridges/Psr/PsrToTracyLoggerAdapter.php | 62 +++++++++++++++++++ src/Bridges/Psr/TracyToPsrLoggerAdapter.php | 61 ++++++++++++++++++ .../PsrToTracyLoggerAdapter.phpt | 45 ++++++++++++++ .../TracyToPsrLoggerAdapter.phpt | 47 ++++++++++++++ 5 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 src/Bridges/Psr/PsrToTracyLoggerAdapter.php create mode 100644 src/Bridges/Psr/TracyToPsrLoggerAdapter.php create mode 100644 tests/Tracy.Bridges/PsrToTracyLoggerAdapter.phpt create mode 100644 tests/Tracy.Bridges/TracyToPsrLoggerAdapter.phpt diff --git a/composer.json b/composer.json index 844a6c182..2830ef33b 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,8 @@ }, "require-dev": { "nette/di": "^2.4 || ^3.0", - "nette/tester": "^2.0" + "nette/tester": "^2.0", + "psr/log": "^1.0" }, "suggest": { "https://nette.org/donate": "Please support Tracy via a donation" diff --git a/src/Bridges/Psr/PsrToTracyLoggerAdapter.php b/src/Bridges/Psr/PsrToTracyLoggerAdapter.php new file mode 100644 index 000000000..9f20389b1 --- /dev/null +++ b/src/Bridges/Psr/PsrToTracyLoggerAdapter.php @@ -0,0 +1,62 @@ + Psr\Log\LogLevel::DEBUG, + Tracy\ILogger::INFO => Psr\Log\LogLevel::INFO, + Tracy\ILogger::WARNING => Psr\Log\LogLevel::WARNING, + Tracy\ILogger::ERROR => Psr\Log\LogLevel::ERROR, + Tracy\ILogger::EXCEPTION => Psr\Log\LogLevel::ERROR, + Tracy\ILogger::CRITICAL => Psr\Log\LogLevel::CRITICAL, + ]; + + /** @var Psr\Log\LoggerInterface */ + private $psrLogger; + + + public function __construct(Psr\Log\LoggerInterface $psrLogger) + { + $this->psrLogger = $psrLogger; + } + + + public function log($value, string $priority = self::INFO) + { + if ($value instanceof \Throwable) { + $message = $value->getMessage(); + $context = ['exception' => $value]; + + } elseif (!is_string($value)) { + $message = trim(Tracy\Dumper::toText($value)); + $context = []; + + } else { + $message = $value; + $context = []; + } + + $this->psrLogger->log( + self::PRIORITY_MAP[$priority] ?? Psr\Log\LogLevel::ERROR, + $message, + $context + ); + } +} diff --git a/src/Bridges/Psr/TracyToPsrLoggerAdapter.php b/src/Bridges/Psr/TracyToPsrLoggerAdapter.php new file mode 100644 index 000000000..155290bf8 --- /dev/null +++ b/src/Bridges/Psr/TracyToPsrLoggerAdapter.php @@ -0,0 +1,61 @@ + Tracy\ILogger::CRITICAL, + Psr\Log\LogLevel::ALERT => Tracy\ILogger::CRITICAL, + Psr\Log\LogLevel::CRITICAL => Tracy\ILogger::CRITICAL, + Psr\Log\LogLevel::ERROR => Tracy\ILogger::ERROR, + Psr\Log\LogLevel::WARNING => Tracy\ILogger::WARNING, + Psr\Log\LogLevel::NOTICE => Tracy\ILogger::WARNING, + Psr\Log\LogLevel::INFO => Tracy\ILogger::INFO, + Psr\Log\LogLevel::DEBUG => Tracy\ILogger::DEBUG, + ]; + + /** @var Tracy\ILogger */ + private $tracyLogger; + + + public function __construct(Tracy\ILogger $tracyLogger) + { + $this->tracyLogger = $tracyLogger; + } + + + public function log($level, $message, array $context = []) + { + $priority = self::PRIORITY_MAP[$level] ?? Tracy\ILogger::ERROR; + + if (isset($context['exception']) && $context['exception'] instanceof \Throwable) { + $this->tracyLogger->log($context['exception'], $priority); + unset($context['exception']); + } + + if ($context) { + $message = [ + 'message' => $message, + 'context' => $context, + ]; + } + + $this->tracyLogger->log($message, $priority); + } +} diff --git a/tests/Tracy.Bridges/PsrToTracyLoggerAdapter.phpt b/tests/Tracy.Bridges/PsrToTracyLoggerAdapter.phpt new file mode 100644 index 000000000..c419c1601 --- /dev/null +++ b/tests/Tracy.Bridges/PsrToTracyLoggerAdapter.phpt @@ -0,0 +1,45 @@ +entries[] = [$level, $message, $context]; + } +} + + +$psrLogger = new DummyPsrLogger; +$tracyLogger = new PsrToTracyLoggerAdapter($psrLogger); +$exception = new \Exception('Something went wrong'); + +$tracyLogger->log('info'); +$tracyLogger->log('warning', ILogger::WARNING); +$tracyLogger->log(123); +$tracyLogger->log(['x' => 'y']); +$tracyLogger->log($exception); + +Assert::same([ + [Psr\Log\LogLevel::INFO, 'info', []], + [Psr\Log\LogLevel::WARNING, 'warning', []], + [Psr\Log\LogLevel::INFO, '123', []], + [Psr\Log\LogLevel::INFO, "array (1)\n x => \"y\"", []], + [Psr\Log\LogLevel::INFO, 'Something went wrong', ['exception' => $exception]], +], $psrLogger->entries); diff --git a/tests/Tracy.Bridges/TracyToPsrLoggerAdapter.phpt b/tests/Tracy.Bridges/TracyToPsrLoggerAdapter.phpt new file mode 100644 index 000000000..115de84ff --- /dev/null +++ b/tests/Tracy.Bridges/TracyToPsrLoggerAdapter.phpt @@ -0,0 +1,47 @@ +entries[] = [$priority, $value]; + } +} + + +$tracyLogger = new DummyTracyLogger; +$psrLogger = new TracyToPsrLoggerAdapter($tracyLogger); +$exception = new \Exception('Something went wrong'); + +$psrLogger->info('info'); +$psrLogger->warning('warning'); +$psrLogger->error('order failed with exception', ['exception' => $exception]); +$psrLogger->error('order failed with context', ['orderId' => 123]); +$psrLogger->error('order failed with context and exception', ['orderId' => 123, 'exception' => $exception]); + +Assert::same([ + [ILogger::INFO, 'info'], + [ILogger::WARNING, 'warning'], + [ILogger::ERROR, $exception], + [ILogger::ERROR, 'order failed with exception'], + [ILogger::ERROR, ['message' => 'order failed with context', 'context' => ['orderId' => 123]]], + [ILogger::ERROR, $exception], + [ILogger::ERROR, ['message' => 'order failed with context and exception', 'context' => ['orderId' => 123]]], +], $tracyLogger->entries); From 62058e2b7fb89da25affd940b66ca5d6ab801855 Mon Sep 17 00:00:00 2001 From: Jan Tvrdik Date: Mon, 27 Aug 2018 20:45:30 +0200 Subject: [PATCH 13/13] logger refactoring --- src/Tracy/BlueScreenLogger.php | 78 ++++++++++++++++++++ src/Tracy/Debugger.php | 19 ++--- src/Tracy/Helpers.php | 25 +++++++ src/Tracy/Logger.php | 128 +++++++++++++++------------------ src/Tracy/MailLogger.php | 102 ++++++++++++++++++++++++++ src/Tracy/StreamLogger.php | 43 +++++++++++ 6 files changed, 318 insertions(+), 77 deletions(-) create mode 100644 src/Tracy/BlueScreenLogger.php create mode 100644 src/Tracy/MailLogger.php create mode 100644 src/Tracy/StreamLogger.php diff --git a/src/Tracy/BlueScreenLogger.php b/src/Tracy/BlueScreenLogger.php new file mode 100644 index 000000000..c71e80463 --- /dev/null +++ b/src/Tracy/BlueScreenLogger.php @@ -0,0 +1,78 @@ +directory = $directory; + $this->blueScreen = $blueScreen; + } + + + public function log($message, string $priority = self::INFO): ?string + { + if (!$this->directory) { + throw new \LogicException('Logging directory is not specified.'); + + } elseif (!is_dir($this->directory)) { + throw new \RuntimeException("Logging directory '$this->directory' is not found or is not directory."); + } + + if ($message instanceof \Throwable) { + return $this->logException($message); + } + + return null; + } + + + public function getExceptionFile(\Throwable $exception): string + { + while ($exception) { + $data[] = [ + get_class($exception), $exception->getMessage(), $exception->getCode(), $exception->getFile(), $exception->getLine(), + array_map(function ($item) { unset($item['args']); return $item; }, $exception->getTrace()), + ]; + $exception = $exception->getPrevious(); + } + $hash = substr(md5(serialize($data)), 0, 10); + $dir = strtr($this->directory . '/', '\\/', DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR); + foreach (new \DirectoryIterator($this->directory) as $file) { + if (strpos($file->getBasename(), $hash)) { + return $dir . $file; + } + } + return $dir . 'exception--' . @date('Y-m-d--H-i') . "--$hash.html"; // @ timezone may not be set + } + + + /** + * Logs exception to the file if file doesn't exist. + * @return string logged error filename + */ + protected function logException(\Throwable $exception, ?string $file = null): string + { + $file = $file ?? $this->getExceptionFile($exception); + $bs = $this->blueScreen ?? new BlueScreen; + $bs->renderToFile($exception, $file); + return $file; + } +} diff --git a/src/Tracy/Debugger.php b/src/Tracy/Debugger.php index 3d6a79ecd..d9eb53560 100644 --- a/src/Tracy/Debugger.php +++ b/src/Tracy/Debugger.php @@ -10,6 +10,7 @@ namespace Tracy; use ErrorException; +use Tracy; /** @@ -139,11 +140,11 @@ final public function __construct() /** * Enables displaying or logging errors and exceptions. - * @param mixed $mode production, development mode, autodetection or IP address(es) whitelist. - * @param string $logDirectory error log directory - * @param string $email administrator email; enables email sending in production mode + * @param mixed $mode production, development mode, autodetection or IP address(es) whitelist. + * @param string|ILogger $logDirectoryOrLogger error log directory or logger instance + * @param string $email administrator email; enables email sending in production mode */ - public static function enable($mode = null, string $logDirectory = null, string $email = null): void + public static function enable($mode = null, $logDirectoryOrLogger = null, string $email = null) { if ($mode !== null || self::$productionMode === null) { self::$productionMode = is_bool($mode) ? $mode : !self::detectDebugMode($mode); @@ -158,8 +159,10 @@ public static function enable($mode = null, string $logDirectory = null, string if ($email !== null) { self::$email = $email; } - if ($logDirectory !== null) { - self::$logDirectory = $logDirectory; + if (is_string($logDirectoryOrLogger)) { + self::$logDirectory = $logDirectoryOrLogger; + } elseif ($logDirectoryOrLogger instanceof ILogger) { + self::$logger = $logDirectoryOrLogger; } if (self::$logDirectory) { if (!preg_match('#([a-z]+:)?[/\\\\]#Ai', self::$logDirectory)) { @@ -466,8 +469,8 @@ public static function getLogger(): ILogger { if (!self::$logger) { self::$logger = new Logger(self::$logDirectory, self::$email, self::getBlueScreen()); - self::$logger->directory = &self::$logDirectory; // back compatiblity - self::$logger->email = &self::$email; + self::$logDirectory = &self::$logger->directory; // back compatibility + self::$email = &self::$logger->email; } return self::$logger; } diff --git a/src/Tracy/Helpers.php b/src/Tracy/Helpers.php index 5c2a138d6..0723f0ca7 100644 --- a/src/Tracy/Helpers.php +++ b/src/Tracy/Helpers.php @@ -262,4 +262,29 @@ public static function getNonce(): ?string ? $m[1] : null; } + + + /** + * @param string|\Exception|\Throwable + * @return string + * @internal + */ + public static function formatMessage($message) + { + if ($message instanceof \Exception || $message instanceof \Throwable) { + while ($message) { + $tmp[] = ($message instanceof \ErrorException + ? Helpers::errorTypeToString($message->getSeverity()) . ': ' . $message->getMessage() + : Helpers::getClass($message) . ': ' . $message->getMessage() . ($message->getCode() ? ' #' . $message->getCode() : '') + ) . ' in ' . $message->getFile() . ':' . $message->getLine(); + $message = $message->getPrevious(); + } + $message = implode("\ncaused by ", $tmp); + + } elseif (!is_string($message)) { + $message = Dumper::toText($message); + } + + return trim($message); + } } diff --git a/src/Tracy/Logger.php b/src/Tracy/Logger.php index 6a6637a8a..332a8e875 100644 --- a/src/Tracy/Logger.php +++ b/src/Tracy/Logger.php @@ -33,6 +33,15 @@ class Logger implements ILogger /** @var BlueScreen|null */ private $blueScreen; + /** @var StreamLogger[] */ + private $streamLoggers; + + /** @var BlueScreenLogger */ + private $blueScreenLogger; + + /** @var MailLogger */ + private $mailLogger; + /** * @param string|array|null $email @@ -43,6 +52,9 @@ public function __construct(?string $directory, $email = null, BlueScreen $blueS $this->email = $email; $this->blueScreen = $blueScreen; $this->mailer = [$this, 'defaultMailer']; + + $this->blueScreenLogger = $this->createBlueScreenLogger(); + $this->mailLogger = $this->createMailLogger(); } @@ -53,6 +65,14 @@ public function __construct(?string $directory, $email = null, BlueScreen $blueS * @return string|null logged error filename */ public function log($message, string $priority = self::INFO): ?string + { + $exceptionFile = $this->getStreamLogger($priority)->log($message, $priority); + $this->mailLogger->log($message, $priority); + return $exceptionFile; + } + + + protected function getStreamLogger($priority) { if (!$this->directory) { throw new \LogicException('Logging directory is not specified.'); @@ -60,25 +80,34 @@ public function log($message, string $priority = self::INFO): ?string throw new \RuntimeException("Logging directory '$this->directory' is not found or is not directory."); } - $exceptionFile = $message instanceof \Throwable - ? $this->getExceptionFile($message) - : null; - $line = static::formatLogLine($message, $exceptionFile); - $file = $this->directory . '/' . strtolower($priority ?: self::INFO) . '.log'; - - if (!@file_put_contents($file, $line . PHP_EOL, FILE_APPEND | LOCK_EX)) { // @ is escalated to exception - throw new \RuntimeException("Unable to write to log file '$file'. Is directory writable?"); + $path = $this->directory . '/' . strtolower($priority ?: self::INFO) . '.log'; + if (!isset($this->streamLoggers[$path])) { + $this->streamLoggers[$path] = new StreamLogger($path, $this->blueScreenLogger); } - if ($exceptionFile) { - $this->logException($message, $exceptionFile); - } + return $this->streamLoggers[$path]; + } - if (in_array($priority, [self::ERROR, self::EXCEPTION, self::CRITICAL], true)) { - $this->sendEmail($message); - } - return $exceptionFile; + protected function createBlueScreenLogger() + { + $blueScreenLogger = new BlueScreenLogger($this->directory, $this->blueScreen); + $blueScreenLogger->directory = &$this->directory; + + return $blueScreenLogger; + } + + + protected function createMailLogger() + { + $mailLogger = new MailLogger($this->directory, $this->email); + $mailLogger->directory = &$this->directory; + $mailLogger->email = &$this->email; + $mailLogger->fromEmail = &$this->fromEmail; + $mailLogger->emailSnooze = &$this->emailSnooze; + $mailLogger->mailer = &$this->mailer; + + return $mailLogger; } @@ -119,82 +148,43 @@ public static function formatLogLine($message, string $exceptionFile = null): st } + /** + * @deprecated + */ public function getExceptionFile(\Throwable $exception): string { - while ($exception) { - $data[] = [ - get_class($exception), $exception->getMessage(), $exception->getCode(), $exception->getFile(), $exception->getLine(), - array_map(function ($item) { unset($item['args']); return $item; }, $exception->getTrace()), - ]; - $exception = $exception->getPrevious(); - } - $hash = substr(md5(serialize($data)), 0, 10); - $dir = strtr($this->directory . '/', '\\/', DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR); - foreach (new \DirectoryIterator($this->directory) as $file) { - if (strpos($file->getBasename(), $hash)) { - return $dir . $file; - } - } - return $dir . 'exception--' . @date('Y-m-d--H-i') . "--$hash.html"; // @ timezone may not be set + return $this->blueScreenLogger->getExceptionFile($exception); } /** - * Logs exception to the file if file doesn't exist. - * @return string logged error filename + * @deprecated */ - protected function logException(\Throwable $exception, string $file = null): string + protected function logException(\Throwable $exception, ?string $file = null): string { - $file = $file ?: $this->getExceptionFile($exception); - $bs = $this->blueScreen ?: new BlueScreen; - $bs->renderToFile($exception, $file); - return $file; + $reflection = new \ReflectionMethod($this->blueScreenLogger, 'logException'); + $reflection->setAccessible(true); + return $reflection->invoke($this->blueScreenLogger, $exception, $file); } /** - * @param mixed $message + * @deprecated */ protected function sendEmail($message): void { - $snooze = is_numeric($this->emailSnooze) - ? $this->emailSnooze - : @strtotime($this->emailSnooze) - time(); // @ timezone may not be set - - if ( - $this->email - && $this->mailer - && @filemtime($this->directory . '/email-sent') + $snooze < time() // @ file may not exist - && @file_put_contents($this->directory . '/email-sent', 'sent') // @ file may not be writable - ) { - ($this->mailer)($message, implode(', ', (array) $this->email)); - } + $reflection = new \ReflectionMethod($this->mailLogger, 'sendEmail'); + $reflection->setAccessible(true); + $reflection->invoke($this->mailLogger, $message); } /** - * Default mailer. - * @param mixed $message + * @deprecated * @internal */ public function defaultMailer($message, string $email): void { - $host = preg_replace('#[^\w.-]+#', '', $_SERVER['HTTP_HOST'] ?? php_uname('n')); - $parts = str_replace( - ["\r\n", "\n"], - ["\n", PHP_EOL], - [ - 'headers' => implode("\n", [ - 'From: ' . ($this->fromEmail ?: "noreply@$host"), - 'X-Mailer: Tracy', - 'Content-Type: text/plain; charset=UTF-8', - 'Content-Transfer-Encoding: 8bit', - ]) . "\n", - 'subject' => "PHP: An error occurred on the server $host", - 'body' => static::formatMessage($message) . "\n\nsource: " . Helpers::getSource(), - ] - ); - - mail($email, $parts['subject'], $parts['body'], $parts['headers']); + $this->mailLogger->defaultMailer($message, $email); } } diff --git a/src/Tracy/MailLogger.php b/src/Tracy/MailLogger.php new file mode 100644 index 000000000..be37fe51a --- /dev/null +++ b/src/Tracy/MailLogger.php @@ -0,0 +1,102 @@ +directory = $directory; + $this->email = $email; + $this->mailer = [$this, 'defaultMailer']; + } + + + public function log($message, string $priority = self::INFO) + { + if (!$this->directory) { + throw new \LogicException('Logging directory is not specified.'); + } elseif (!is_dir($this->directory)) { + throw new \RuntimeException("Logging directory '$this->directory' is not found or is not directory."); + } + + if (in_array($priority, [self::ERROR, self::EXCEPTION, self::CRITICAL], true)) { + $this->sendEmail($message); + return true; + } + + return false; + } + + + /** + * @param mixed $message + */ + protected function sendEmail($message): void + { + $snooze = is_numeric($this->emailSnooze) + ? $this->emailSnooze + : @strtotime($this->emailSnooze) - time(); // @ timezone may not be set + + if ( + $this->email + && $this->mailer + && @filemtime($this->directory . '/email-sent') + $snooze < time() // @ file may not exist + && @file_put_contents($this->directory . '/email-sent', 'sent') // @ file may not be writable + ) { + ($this->mailer)($message, implode(', ', (array) $this->email)); + } + } + + + /** + * Default mailer. + * @param mixed $message + * @internal + */ + public function defaultMailer($message, string $email): void + { + $host = preg_replace('#[^\w.-]+#', '', $_SERVER['HTTP_HOST'] ?? php_uname('n')); + $parts = str_replace( + ["\r\n", "\n"], + ["\n", PHP_EOL], + [ + 'headers' => implode("\n", [ + 'From: ' . ($this->fromEmail ?: "noreply@$host"), + 'X-Mailer: Tracy', + 'Content-Type: text/plain; charset=UTF-8', + 'Content-Transfer-Encoding: 8bit', + ]) . "\n", + 'subject' => "PHP: An error occurred on the server $host", + 'body' => static::formatMessage($message) . "\n\nsource: " . Helpers::getSource(), + ] + ); + + mail($email, $parts['subject'], $parts['body'], $parts['headers']); + } +} diff --git a/src/Tracy/StreamLogger.php b/src/Tracy/StreamLogger.php new file mode 100644 index 000000000..7703c8cc8 --- /dev/null +++ b/src/Tracy/StreamLogger.php @@ -0,0 +1,43 @@ +path = $path; + $this->blueScreenLogger = $blueScreenLogger; + } + + + public function log($message, string $priority = self::INFO): ?string + { + $exceptionFile = $this->blueScreenLogger ? $this->blueScreenLogger->log($message, $priority) : null; + $line = Logger::formatLogLine($message, $exceptionFile); + if (!@file_put_contents($this->path, $line . PHP_EOL, FILE_APPEND | LOCK_EX)) { // @ is escalated to exception + throw new \RuntimeException("Unable to write to log file '{$this->path}'. Is directory writable?"); + } + + return $exceptionFile; + } +}