/** * Renders blue screen. * @param \Exception|\Throwable * @return void */ public function render($exception) { $panels = $this->panels; $info = array_filter($this->info); $source = Helpers::getSource(); $sourceIsUrl = preg_match('#^https?://#', $source); $title = $exception instanceof \ErrorException ? Helpers::errorTypeToString($exception->getSeverity()) : get_class($exception); $skipError = $sourceIsUrl && $exception instanceof \ErrorException && !empty($exception->skippable) ? $source . (strpos($source, '?') ? '&' : '?') . '_tracy_skip_error' : NULL; require __DIR__ . '/assets/BlueScreen/bluescreen.phtml'; }
/** * @param string|\Exception|\Throwable * @return string */ protected 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()) . ' in ' . $message->getFile() . ':' . $message->getLine(); $message = $message->getPrevious(); } $message = implode($tmp, "\ncaused by "); } elseif (!is_string($message)) { $message = Dumper::toText($message); } return trim($message); }
private function renderTemplate($exception, $template) { $info = array_filter($this->info); $source = Helpers::getSource(); $sourceIsUrl = preg_match('#^https?://#', $source); $title = $exception instanceof \ErrorException ? Helpers::errorTypeToString($exception->getSeverity()) : Helpers::getClass($exception); $skipError = $sourceIsUrl && $exception instanceof \ErrorException && !empty($exception->skippable) ? $source . (strpos($source, '?') ? '&' : '?') . '_tracy_skip_error' : NULL; $lastError = $exception instanceof \ErrorException || $exception instanceof \Error ? NULL : error_get_last(); $dump = function ($v) { return Dumper::toHtml($v, [Dumper::DEPTH => $this->maxDepth, Dumper::TRUNCATE => $this->maxLength, Dumper::LIVE => TRUE, Dumper::LOCATION => Dumper::LOCATION_CLASS]); }; require $template; }
/** * Checks if the function generates PHP error or throws exception. * @param callable * @param int|string|array * @param string message * @return null|Exception */ public static function error($function, $expectedType, $expectedMessage = NULL) { if (is_string($expectedType) && !preg_match('#^E_[A-Z_]+\\z#', $expectedType)) { return static::exception($function, $expectedType, $expectedMessage); } $expected = is_array($expectedType) ? $expectedType : array(array($expectedType, $expectedMessage)); foreach ($expected as &$item) { list($expectedType, $expectedMessage) = $item; if (is_int($expectedType)) { $item[2] = Helpers::errorTypeToString($expectedType); } elseif (is_string($expectedType)) { $item[0] = constant($item[2] = $expectedType); } else { throw new \Exception('Error type must be E_* constant.'); } } set_error_handler(function ($severity, $message, $file, $line) use(&$expected) { if (($severity & error_reporting()) !== $severity) { return; } $errorStr = Helpers::errorTypeToString($severity) . ($message ? " ({$message})" : ''); list($expectedType, $expectedMessage, $expectedTypeStr) = array_shift($expected); if ($expectedType === NULL) { restore_error_handler(); Assert::fail("Generated more errors than expected: {$errorStr} was generated in file {$file} on line {$line}"); } elseif ($severity !== $expectedType) { restore_error_handler(); Assert::fail("{$expectedTypeStr} was expected, but {$errorStr} was generated in file {$file} on line {$line}"); } elseif ($expectedMessage && !Assert::isMatching($expectedMessage, $message)) { restore_error_handler(); Assert::fail("{$expectedTypeStr} with a message matching %2 was expected but got %1", $message, $expectedMessage); } }); reset($expected); call_user_func($function); restore_error_handler(); if ($expected) { self::fail('Error was expected, but was not generated'); } }
/** * @param \Exception|\Throwable * @internal */ public static function dumpException($e) { $trace = $e->getTrace(); array_splice($trace, 0, $e instanceof \ErrorException ? 1 : 0, array(array('file' => $e->getFile(), 'line' => $e->getLine()))); $testFile = NULL; foreach (array_reverse($trace) as $item) { if (isset($item['file'])) { // in case of shutdown handler, we want to skip inner-code blocks and debugging calls $testFile = $item['file']; break; } } if ($e instanceof AssertException) { $expected = $e->expected; $actual = $e->actual; if (is_object($expected) || is_array($expected) || is_string($expected) && strlen($expected) > self::$maxLength || is_object($actual) || is_array($actual) || is_string($actual) && strlen($actual) > self::$maxLength) { $args = isset($_SERVER['argv'][1]) ? '.[' . implode(' ', preg_replace(array('#^-*(.{1,20}).*#i', '#[^=a-z0-9. -]+#i'), array('$1', '-'), array_slice($_SERVER['argv'], 1))) . ']' : ''; $stored[] = self::saveOutput($testFile, $expected, $args . '.expected'); $stored[] = self::saveOutput($testFile, $actual, $args . '.actual'); } if (is_string($actual) && is_string($expected)) { for ($i = 0; $i < strlen($actual) && isset($expected[$i]) && $actual[$i] === $expected[$i]; $i++) { } $i = max(0, min($i, max(strlen($actual), strlen($expected)) - self::$maxLength + 3)); for (; $i && $i < count($actual) && $actual[$i - 1] >= "€" && $actual[$i] >= "€" && $actual[$i] < "À"; $i--) { } if ($i) { $expected = substr_replace($expected, '...', 0, $i); $actual = substr_replace($actual, '...', 0, $i); } } $message = 'Failed: ' . $e->origMessage; if ((is_string($actual) && is_string($expected) || is_array($actual) && is_array($expected)) && preg_match('#^(.*)(%\\d)(.*)(%\\d.*)\\z#s', $message, $m)) { if (($delta = strlen($m[1]) - strlen($m[3])) >= 3) { $message = "{$m['1']}{$m['2']}\n" . str_repeat(' ', $delta - 3) . "...{$m['3']}{$m['4']}"; } else { $message = "{$m['1']}{$m['2']}{$m['3']}\n" . str_repeat(' ', strlen($m[1]) - 4) . "... {$m['4']}"; } } $message = strtr($message, array('%1' => self::color('yellow') . self::toLine($actual) . self::color('white'), '%2' => self::color('yellow') . self::toLine($expected) . self::color('white'))); } else { $message = ($e instanceof \ErrorException ? Helpers::errorTypeToString($e->getSeverity()) : get_class($e)) . ': ' . preg_replace('#[\\x00-\\x09\\x0B-\\x1F]+#', ' ', $e->getMessage()); } $s = self::color('white', $message) . "\n\n" . (isset($stored) ? 'diff ' . Helpers::escapeArg($stored[0]) . ' ' . Helpers::escapeArg($stored[1]) . "\n\n" : ''); foreach ($trace as $item) { $item += array('file' => NULL, 'class' => NULL, 'type' => NULL, 'function' => NULL); if ($e instanceof AssertException && $item['file'] === __DIR__ . DIRECTORY_SEPARATOR . 'Assert.php') { continue; } $s .= 'in ' . ($item['file'] ? ($item['file'] === $testFile ? self::color('white') : '') . implode(DIRECTORY_SEPARATOR, array_slice(explode(DIRECTORY_SEPARATOR, $item['file']), -self::$maxPathSegments)) . "({$item['line']})" . self::color('gray') . ' ' : '[internal function]') . $item['class'] . $item['type'] . (isset($item['function']) ? $item['function'] . '()' : '') . self::color() . "\n"; } if ($e->getPrevious()) { $s .= "\n(previous) " . static::dumpException($e->getPrevious()); } return $s; }
/** * Checks if the function does neither generate PHP error nor throw Exception * @param callable * @param string * @return null|\Exception */ public static function noError($function, $message = NULL) { self::$counter++; set_error_handler(function ($severity, $msg, $file, $line) use(&$expected, $message) { if (($severity & error_reporting()) !== $severity) { return; } $errorStr = Helpers::errorTypeToString($severity) . ($msg ? " ({$msg})" : ''); restore_error_handler(); Assert::fail(($message ? "{$message}: " : '') . "occurred error {$errorStr}"); }); try { call_user_func($function); } catch (\Exception $e) { if ($e instanceof AssertException) { // assertion exception already thrown by error handler, re-throw throw $e; } restore_error_handler(); Assert::fail(($message ? "{$message}: " : '') . sprintf('%s was thrown. Code: %d Message: %s', get_class($e), $e->getCode(), $e->getMessage())); } restore_error_handler(); }
/** @internal */ public static function dumpException(\Exception $e) { $trace = $e->getTrace(); array_splice($trace, 0, $e instanceof \ErrorException ? 1 : 0, array(array('file' => $e->getFile(), 'line' => $e->getLine()))); $testFile = NULL; foreach (array_reverse($trace) as $item) { if (isset($item['file'])) { // in case of shutdown handler, we want to skip inner-code blocks and debugging calls $testFile = $item['file']; break; } } if ($e instanceof AssertException) { if (is_object($e->expected) || is_array($e->expected) || is_string($e->expected) && strlen($e->expected) > self::$maxLength || is_object($e->actual) || is_array($e->actual) || is_string($e->actual) && strlen($e->actual) > self::$maxLength) { $args = isset($_SERVER['argv'][1]) ? '.[' . preg_replace('#[^a-z0-9-. ]+#i', '_', $_SERVER['argv'][1]) . ']' : ''; $stored[] = self::saveOutput($testFile, $e->expected, $args . '.expected'); $stored[] = self::saveOutput($testFile, $e->actual, $args . '.actual'); } if (is_string($e->actual) && is_string($e->expected)) { for ($i = 0; $i < strlen($e->actual) && isset($e->expected[$i]) && $e->actual[$i] === $e->expected[$i]; $i++) { } $i = max(0, min($i, max(strlen($e->actual), strlen($e->expected)) - self::$maxLength + 3)); for (; $i && $i < count($e->actual) && $e->actual[$i - 1] >= "€" && $e->actual[$i] >= "€" && $e->actual[$i] < "À"; $i--) { } if ($i) { $e->expected = substr_replace($e->expected, '...', 0, $i); $e->actual = substr_replace($e->actual, '...', 0, $i); } } $message = 'Failed: ' . $e->getMessage(); if (is_string($e->actual) && is_string($e->expected) || is_array($e->actual) && is_array($e->expected)) { preg_match('#^(.*)(%\\d)(.*)(%\\d.*)\\z#', $message, $m); if (($delta = strlen($m[1]) - strlen($m[3])) >= 3) { $message = "{$m['1']}{$m['2']}\n" . str_repeat(' ', $delta - 3) . "...{$m['3']}{$m['4']}"; } else { $message = "{$m['1']}{$m['2']}{$m['3']}\n" . str_repeat(' ', strlen($m[1]) - 4) . "... {$m['4']}"; } } $message = strtr($message, array('%1' => "[1;33m" . Dumper::toLine($e->actual) . "[1;37m", '%2' => "[1;33m" . Dumper::toLine($e->expected) . "[1;37m")); } else { $message = ($e instanceof \ErrorException ? Helpers::errorTypeToString($e->getSeverity()) : get_class($e)) . ": {$e->getMessage()}"; } $s = "[1;37m{$message}[0m\n\n" . (isset($stored) ? "diff " . escapeshellarg($stored[0]) . " " . escapeshellarg($stored[1]) . "\n\n" : ''); foreach ($trace as $item) { $item += array('file' => NULL); $s .= 'in ' . ($item['file'] === $testFile ? "[1;37m" : '') . ($item['file'] ? implode(DIRECTORY_SEPARATOR, array_slice(explode(DIRECTORY_SEPARATOR, $item['file']), -3)) . "({$item['line']})" : '[internal function]') . "[1;30m " . (isset($item['class']) ? $item['class'] . $item['type'] : '') . (isset($item['function']) ? $item['function'] . '()' : '') . "[0m\n"; } if ($e->getPrevious()) { $s .= "\n(previous) " . static::dumpException($e->getPrevious()); } return $s; }