Commit version 24.12.13800
This commit is contained in:
626
libs/Nette/Tracy/BlueScreen/BlueScreen.php
Normal file
626
libs/Nette/Tracy/BlueScreen/BlueScreen.php
Normal file
@ -0,0 +1,626 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Tracy (https://tracy.nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tracy;
|
||||
|
||||
|
||||
/**
|
||||
* Red BlueScreen.
|
||||
*/
|
||||
class BlueScreen
|
||||
{
|
||||
private const MaxMessageLength = 2000;
|
||||
|
||||
/** @var string[] */
|
||||
public $info = [];
|
||||
|
||||
/** @var string[] paths to be collapsed in stack trace (e.g. core libraries) */
|
||||
public $collapsePaths = [];
|
||||
|
||||
/** @var int */
|
||||
public $maxDepth = 5;
|
||||
|
||||
/** @var int */
|
||||
public $maxLength = 150;
|
||||
|
||||
/** @var int */
|
||||
public $maxItems = 100;
|
||||
|
||||
/** @var callable|null a callable returning true for sensitive data; fn(string $key, mixed $val): bool */
|
||||
public $scrubber;
|
||||
|
||||
/** @var string[] */
|
||||
public $keysToHide = [
|
||||
'password', 'passwd', 'pass', 'pwd', 'creditcard', 'credit card', 'cc', 'pin', 'authorization',
|
||||
self::class . '::$snapshot',
|
||||
];
|
||||
|
||||
/** @var bool */
|
||||
public $showEnvironment = true;
|
||||
|
||||
/** @var callable[] */
|
||||
private $panels = [];
|
||||
|
||||
/** @var callable[] functions that returns action for exceptions */
|
||||
private $actions = [];
|
||||
|
||||
/** @var callable[] */
|
||||
private $fileGenerators = [];
|
||||
|
||||
/** @var array */
|
||||
private $snapshot;
|
||||
|
||||
/** @var \WeakMap<\Fiber|\Generator> */
|
||||
private $fibers;
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->collapsePaths = preg_match('#(.+/vendor)/tracy/tracy/src/Tracy/BlueScreen$#', strtr(__DIR__, '\\', '/'), $m)
|
||||
? [$m[1] . '/tracy', $m[1] . '/nette', $m[1] . '/latte']
|
||||
: [dirname(__DIR__)];
|
||||
$this->fileGenerators[] = [self::class, 'generateNewPhpFileContents'];
|
||||
$this->fibers = PHP_VERSION_ID < 80000 ? new \SplObjectStorage : new \WeakMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add custom panel as function (?\Throwable $e): ?array
|
||||
* @return static
|
||||
*/
|
||||
public function addPanel(callable $panel): self
|
||||
{
|
||||
if (!in_array($panel, $this->panels, true)) {
|
||||
$this->panels[] = $panel;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add action.
|
||||
* @return static
|
||||
*/
|
||||
public function addAction(callable $action): self
|
||||
{
|
||||
$this->actions[] = $action;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add new file generator.
|
||||
* @param callable(string): ?string $generator
|
||||
* @return static
|
||||
*/
|
||||
public function addFileGenerator(callable $generator): self
|
||||
{
|
||||
$this->fileGenerators[] = $generator;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param \Fiber|\Generator $fiber
|
||||
* @return static
|
||||
*/
|
||||
public function addFiber($fiber): self
|
||||
{
|
||||
$this->fibers[$fiber] = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renders blue screen.
|
||||
*/
|
||||
public function render(\Throwable $exception): void
|
||||
{
|
||||
if (!headers_sent()) {
|
||||
header('Content-Type: text/html; charset=UTF-8');
|
||||
}
|
||||
|
||||
$this->renderTemplate($exception, __DIR__ . '/assets/page.phtml');
|
||||
}
|
||||
|
||||
|
||||
/** @internal */
|
||||
public function renderToAjax(\Throwable $exception, DeferredContent $defer): void
|
||||
{
|
||||
$defer->addSetup('Tracy.BlueScreen.loadAjax', Helpers::capture(function () use ($exception) {
|
||||
$this->renderTemplate($exception, __DIR__ . '/assets/content.phtml');
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renders blue screen to file (if file exists, it will not be overwritten).
|
||||
*/
|
||||
public function renderToFile(\Throwable $exception, string $file): bool
|
||||
{
|
||||
if ($handle = @fopen($file, 'x')) {
|
||||
ob_start(); // double buffer prevents sending HTTP headers in some PHP
|
||||
ob_start(function ($buffer) use ($handle): void { fwrite($handle, $buffer); }, 4096);
|
||||
$this->renderTemplate($exception, __DIR__ . '/assets/page.phtml', false);
|
||||
ob_end_flush();
|
||||
ob_end_clean();
|
||||
fclose($handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private function renderTemplate(\Throwable $exception, string $template, $toScreen = true): void
|
||||
{
|
||||
[$generators, $fibers] = $this->findGeneratorsAndFibers($exception);
|
||||
$headersSent = headers_sent($headersFile, $headersLine);
|
||||
$obStatus = Debugger::$obStatus;
|
||||
$showEnvironment = $this->showEnvironment && (strpos($exception->getMessage(), 'Allowed memory size') === false);
|
||||
$info = array_filter($this->info);
|
||||
$source = Helpers::getSource();
|
||||
$title = $exception instanceof \ErrorException
|
||||
? Helpers::errorTypeToString($exception->getSeverity())
|
||||
: Helpers::getClass($exception);
|
||||
$lastError = $exception instanceof \ErrorException || $exception instanceof \Error
|
||||
? null
|
||||
: error_get_last();
|
||||
|
||||
if (function_exists('apache_request_headers')) {
|
||||
$httpHeaders = apache_request_headers();
|
||||
} else {
|
||||
$httpHeaders = array_filter($_SERVER, function ($k) { return strncmp($k, 'HTTP_', 5) === 0; }, ARRAY_FILTER_USE_KEY);
|
||||
$httpHeaders = array_combine(array_map(function ($k) { return strtolower(strtr(substr($k, 5), '_', '-')); }, array_keys($httpHeaders)), $httpHeaders);
|
||||
}
|
||||
|
||||
$snapshot = &$this->snapshot;
|
||||
$snapshot = [];
|
||||
$dump = $this->getDumper();
|
||||
|
||||
$css = array_map('file_get_contents', array_merge([
|
||||
__DIR__ . '/../assets/reset.css',
|
||||
__DIR__ . '/assets/bluescreen.css',
|
||||
__DIR__ . '/../assets/toggle.css',
|
||||
__DIR__ . '/../assets/table-sort.css',
|
||||
__DIR__ . '/../assets/tabs.css',
|
||||
__DIR__ . '/../Dumper/assets/dumper-light.css',
|
||||
], Debugger::$customCssFiles));
|
||||
$css = Helpers::minifyCss(implode('', $css));
|
||||
|
||||
$nonce = $toScreen ? Helpers::getNonce() : null;
|
||||
$actions = $toScreen ? $this->renderActions($exception) : [];
|
||||
|
||||
require $template;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return \stdClass[]
|
||||
*/
|
||||
private function renderPanels(?\Throwable $ex): array
|
||||
{
|
||||
$obLevel = ob_get_level();
|
||||
$res = [];
|
||||
foreach ($this->panels as $callback) {
|
||||
try {
|
||||
$panel = $callback($ex);
|
||||
if (empty($panel['tab']) || empty($panel['panel'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$res[] = (object) $panel;
|
||||
continue;
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
|
||||
while (ob_get_level() > $obLevel) { // restore ob-level if broken
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
is_callable($callback, true, $name);
|
||||
$res[] = (object) [
|
||||
'tab' => "Error in panel $name",
|
||||
'panel' => nl2br(Helpers::escapeHtml($e)),
|
||||
];
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
*/
|
||||
private function renderActions(\Throwable $ex): array
|
||||
{
|
||||
$actions = [];
|
||||
foreach ($this->actions as $callback) {
|
||||
$action = $callback($ex);
|
||||
if (!empty($action['link']) && !empty($action['label'])) {
|
||||
$actions[] = $action;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
property_exists($ex, 'tracyAction')
|
||||
&& !empty($ex->tracyAction['link'])
|
||||
&& !empty($ex->tracyAction['label'])
|
||||
) {
|
||||
$actions[] = $ex->tracyAction;
|
||||
}
|
||||
|
||||
if (preg_match('# ([\'"])(\w{3,}(?:\\\\\w{3,})+)\1#i', $ex->getMessage(), $m)) {
|
||||
$class = $m[2];
|
||||
if (
|
||||
!class_exists($class, false) && !interface_exists($class, false) && !trait_exists($class, false)
|
||||
&& ($file = Helpers::guessClassFile($class)) && !@is_file($file) // @ - may trigger error
|
||||
) {
|
||||
[$content, $line] = $this->generateNewFileContents($file, $class);
|
||||
$actions[] = [
|
||||
'link' => Helpers::editorUri($file, $line, 'create', '', $content),
|
||||
'label' => 'create class',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match('# ([\'"])((?:/|[a-z]:[/\\\\])\w[^\'"]+\.\w{2,5})\1#i', $ex->getMessage(), $m)) {
|
||||
$file = $m[2];
|
||||
if (@is_file($file)) { // @ - may trigger error
|
||||
$label = 'open';
|
||||
$content = '';
|
||||
$line = 1;
|
||||
} else {
|
||||
$label = 'create';
|
||||
[$content, $line] = $this->generateNewFileContents($file);
|
||||
}
|
||||
|
||||
$actions[] = [
|
||||
'link' => Helpers::editorUri($file, $line, $label, '', $content),
|
||||
'label' => $label . ' file',
|
||||
];
|
||||
}
|
||||
|
||||
$query = ($ex instanceof \ErrorException ? '' : Helpers::getClass($ex) . ' ')
|
||||
. preg_replace('#\'.*\'|".*"#Us', '', $ex->getMessage());
|
||||
$actions[] = [
|
||||
'link' => 'https://www.google.com/search?sourceid=tracy&q=' . urlencode($query),
|
||||
'label' => 'search',
|
||||
'external' => true,
|
||||
];
|
||||
|
||||
if (
|
||||
$ex instanceof \ErrorException
|
||||
&& !empty($ex->skippable)
|
||||
&& preg_match('#^https?://#', $source = Helpers::getSource())
|
||||
) {
|
||||
$actions[] = [
|
||||
'link' => $source . (strpos($source, '?') ? '&' : '?') . '_tracy_skip_error',
|
||||
'label' => 'skip error',
|
||||
];
|
||||
}
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns syntax highlighted source code.
|
||||
*/
|
||||
public static function highlightFile(
|
||||
string $file,
|
||||
int $line,
|
||||
int $lines = 15,
|
||||
bool $php = true,
|
||||
int $column = 0
|
||||
): ?string
|
||||
{
|
||||
$source = @file_get_contents($file); // @ file may not exist
|
||||
if ($source === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$source = $php
|
||||
? static::highlightPhp($source, $line, $lines, $column)
|
||||
: '<pre class=tracy-code><div>' . static::highlightLine(htmlspecialchars($source, ENT_IGNORE, 'UTF-8'), $line, $lines, $column) . '</div></pre>';
|
||||
|
||||
if ($editor = Helpers::editorUri($file, $line)) {
|
||||
$source = substr_replace($source, ' title="Ctrl-Click to open in editor" data-tracy-href="' . Helpers::escapeHtml($editor) . '"', 4, 0);
|
||||
}
|
||||
|
||||
return $source;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns syntax highlighted source code.
|
||||
*/
|
||||
public static function highlightPhp(string $source, int $line, int $lines = 15, int $column = 0): string
|
||||
{
|
||||
if (function_exists('ini_set')) {
|
||||
ini_set('highlight.comment', '#998; font-style: italic');
|
||||
ini_set('highlight.default', '#000');
|
||||
ini_set('highlight.html', '#06B');
|
||||
ini_set('highlight.keyword', '#D24; font-weight: bold');
|
||||
ini_set('highlight.string', '#080');
|
||||
}
|
||||
|
||||
$source = preg_replace('#(__halt_compiler\s*\(\)\s*;).*#is', '$1', $source);
|
||||
$source = str_replace(["\r\n", "\r"], "\n", $source);
|
||||
$source = explode("\n", highlight_string($source, true));
|
||||
$out = $source[0]; // <code><span color=highlight.html>
|
||||
$source = str_replace('<br />', "\n", $source[1]);
|
||||
$out .= static::highlightLine($source, $line, $lines, $column);
|
||||
$out = str_replace(' ', ' ', $out) . '</code>';
|
||||
return "<pre class='tracy-code'><div>$out</div></pre>";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns highlighted line in HTML code.
|
||||
*/
|
||||
public static function highlightLine(string $html, int $line, int $lines = 15, int $column = 0): string
|
||||
{
|
||||
$source = explode("\n", "\n" . str_replace("\r\n", "\n", $html));
|
||||
$out = '';
|
||||
$spans = 1;
|
||||
$start = $i = max(1, min($line, count($source) - 1) - (int) floor($lines * 2 / 3));
|
||||
while (--$i >= 1) { // find last highlighted block
|
||||
if (preg_match('#.*(</?span[^>]*>)#', $source[$i], $m)) {
|
||||
if ($m[1] !== '</span>') {
|
||||
$spans++;
|
||||
$out .= $m[1];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$source = array_slice($source, $start, $lines, true);
|
||||
end($source);
|
||||
$numWidth = strlen((string) key($source));
|
||||
|
||||
foreach ($source as $n => $s) {
|
||||
$spans += substr_count($s, '<span') - substr_count($s, '</span');
|
||||
$s = str_replace(["\r", "\n"], ['', ''], $s);
|
||||
preg_match_all('#<[^>]+>#', $s, $tags);
|
||||
if ($n == $line) {
|
||||
$s = strip_tags($s);
|
||||
if ($column) {
|
||||
$s = preg_replace(
|
||||
'#((?:&.*?;|[^&]){' . ($column - 1) . '})(&.*?;|.)#u',
|
||||
'\1<span class="tracy-column-highlight">\2</span>',
|
||||
$s . ' ',
|
||||
1
|
||||
);
|
||||
}
|
||||
$out .= sprintf(
|
||||
"<span class='tracy-line-highlight'>%{$numWidth}s: %s\n</span>%s",
|
||||
$n,
|
||||
$s,
|
||||
implode('', $tags[0])
|
||||
);
|
||||
} else {
|
||||
$out .= sprintf("<span class='tracy-line'>%{$numWidth}s:</span> %s\n", $n, $s);
|
||||
}
|
||||
}
|
||||
|
||||
$out .= str_repeat('</span>', $spans);
|
||||
return $out;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns syntax highlighted source code to Terminal.
|
||||
*/
|
||||
public static function highlightPhpCli(string $file, int $line, int $lines = 15, int $column = 0): ?string
|
||||
{
|
||||
$source = @file_get_contents($file); // @ file may not exist
|
||||
if ($source === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$s = self::highlightPhp($source, $line, $lines);
|
||||
|
||||
$colors = [
|
||||
'color: ' . ini_get('highlight.comment') => '1;30',
|
||||
'color: ' . ini_get('highlight.default') => '1;36',
|
||||
'color: ' . ini_get('highlight.html') => '1;35',
|
||||
'color: ' . ini_get('highlight.keyword') => '1;37',
|
||||
'color: ' . ini_get('highlight.string') => '1;32',
|
||||
'tracy-line' => '1;30',
|
||||
'tracy-line-highlight' => "1;37m\e[41",
|
||||
];
|
||||
|
||||
$stack = ['0'];
|
||||
$s = preg_replace_callback(
|
||||
'#<\w+(?: (class|style)=["\'](.*?)["\'])?[^>]*>|</\w+>#',
|
||||
function ($m) use ($colors, &$stack): string {
|
||||
if ($m[0][1] === '/') {
|
||||
array_pop($stack);
|
||||
} else {
|
||||
$stack[] = isset($m[2], $colors[$m[2]]) ? $colors[$m[2]] : '0';
|
||||
}
|
||||
|
||||
return "\e[0m\e[" . end($stack) . 'm';
|
||||
},
|
||||
$s
|
||||
);
|
||||
$s = htmlspecialchars_decode(strip_tags($s), ENT_QUOTES | ENT_HTML5);
|
||||
return $s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Should a file be collapsed in stack trace?
|
||||
* @internal
|
||||
*/
|
||||
public function isCollapsed(string $file): bool
|
||||
{
|
||||
$file = strtr($file, '\\', '/') . '/';
|
||||
foreach ($this->collapsePaths as $path) {
|
||||
$path = strtr($path, '\\', '/') . '/';
|
||||
if (strncmp($file, $path, strlen($path)) === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** @internal */
|
||||
public function getDumper(): \Closure
|
||||
{
|
||||
return function ($v, $k = null): string {
|
||||
return Dumper::toHtml($v, [
|
||||
Dumper::DEPTH => $this->maxDepth,
|
||||
Dumper::TRUNCATE => $this->maxLength,
|
||||
Dumper::ITEMS => $this->maxItems,
|
||||
Dumper::SNAPSHOT => &$this->snapshot,
|
||||
Dumper::LOCATION => Dumper::LOCATION_CLASS,
|
||||
Dumper::SCRUBBER => $this->scrubber,
|
||||
Dumper::KEYS_TO_HIDE => $this->keysToHide,
|
||||
], $k);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public function formatMessage(\Throwable $exception): string
|
||||
{
|
||||
$msg = Helpers::encodeString(trim((string) $exception->getMessage()), self::MaxMessageLength, false);
|
||||
|
||||
// highlight 'string'
|
||||
$msg = preg_replace(
|
||||
'#\'\S(?:[^\']|\\\\\')*\S\'|"\S(?:[^"]|\\\\")*\S"#',
|
||||
'<i>$0</i>',
|
||||
$msg
|
||||
);
|
||||
|
||||
// clickable class & methods
|
||||
$msg = preg_replace_callback(
|
||||
'#(\w+\\\\[\w\\\\]+\w)(?:::(\w+))?#',
|
||||
function ($m) {
|
||||
if (isset($m[2]) && method_exists($m[1], $m[2])) {
|
||||
$r = new \ReflectionMethod($m[1], $m[2]);
|
||||
} elseif (class_exists($m[1], false) || interface_exists($m[1], false)) {
|
||||
$r = new \ReflectionClass($m[1]);
|
||||
}
|
||||
|
||||
if (empty($r) || !$r->getFileName()) {
|
||||
return $m[0];
|
||||
}
|
||||
|
||||
return '<a href="' . Helpers::escapeHtml(Helpers::editorUri($r->getFileName(), $r->getStartLine())) . '" class="tracy-editor">' . $m[0] . '</a>';
|
||||
},
|
||||
$msg
|
||||
);
|
||||
|
||||
// clickable file name
|
||||
$msg = preg_replace_callback(
|
||||
'#([\w\\\\/.:-]+\.(?:php|phpt|phtml|latte|neon))(?|:(\d+)| on line (\d+))?#',
|
||||
function ($m) {
|
||||
return @is_file($m[1])
|
||||
? '<a href="' . Helpers::escapeHtml(Helpers::editorUri($m[1], isset($m[2]) ? (int) $m[2] : null)) . '" class="tracy-editor">' . $m[0] . '</a>'
|
||||
: $m[0];
|
||||
},
|
||||
$msg
|
||||
);
|
||||
|
||||
return $msg;
|
||||
}
|
||||
|
||||
|
||||
private function renderPhpInfo(): void
|
||||
{
|
||||
ob_start();
|
||||
@phpinfo(INFO_LICENSE); // @ phpinfo may be disabled
|
||||
$license = ob_get_clean();
|
||||
ob_start();
|
||||
@phpinfo(INFO_CONFIGURATION | INFO_MODULES); // @ phpinfo may be disabled
|
||||
$info = ob_get_clean();
|
||||
|
||||
if (strpos($license, '<body') === false) {
|
||||
echo '<pre class="tracy-dump tracy-light">', Helpers::escapeHtml($info), '</pre>';
|
||||
} else {
|
||||
$info = str_replace('<table', '<table class="tracy-sortable"', $info);
|
||||
echo preg_replace('#^.+<body>|</body>.+\z|<hr />|<h1>Configuration</h1>#s', '', $info);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** @internal */
|
||||
private function generateNewFileContents(string $file, ?string $class = null): array
|
||||
{
|
||||
foreach (array_reverse($this->fileGenerators) as $generator) {
|
||||
$content = $generator($file, $class);
|
||||
if ($content !== null) {
|
||||
$line = 1;
|
||||
$pos = strpos($content, '$END$');
|
||||
if ($pos !== false) {
|
||||
$content = substr_replace($content, '', $pos, 5);
|
||||
$line = substr_count($content, "\n", 0, $pos) + 1;
|
||||
}
|
||||
|
||||
return [$content, $line];
|
||||
}
|
||||
}
|
||||
|
||||
return ['', 1];
|
||||
}
|
||||
|
||||
|
||||
/** @internal */
|
||||
public static function generateNewPhpFileContents(string $file, ?string $class = null): ?string
|
||||
{
|
||||
if (substr($file, -4) !== '.php') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$res = "<?php\n\ndeclare(strict_types=1);\n\n";
|
||||
if (!$class) {
|
||||
return $res . '$END$';
|
||||
}
|
||||
|
||||
if ($pos = strrpos($class, '\\')) {
|
||||
$res .= 'namespace ' . substr($class, 0, $pos) . ";\n\n";
|
||||
$class = substr($class, $pos + 1);
|
||||
}
|
||||
|
||||
return $res . "class $class\n{\n\$END\$\n}\n";
|
||||
}
|
||||
|
||||
|
||||
private function findGeneratorsAndFibers(object $object): array
|
||||
{
|
||||
$generators = $fibers = [];
|
||||
$add = function ($obj) use (&$generators, &$fibers) {
|
||||
if ($obj instanceof \Generator) {
|
||||
try {
|
||||
new \ReflectionGenerator($obj);
|
||||
$generators[spl_object_id($obj)] = $obj;
|
||||
} catch (\ReflectionException $e) {
|
||||
}
|
||||
} elseif ($obj instanceof \Fiber && $obj->isStarted() && !$obj->isTerminated()) {
|
||||
$fibers[spl_object_id($obj)] = $obj;
|
||||
}
|
||||
};
|
||||
|
||||
foreach ($this->fibers as $k => $v) {
|
||||
$add($this->fibers instanceof \WeakMap ? $k : $v);
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID >= 80000) {
|
||||
Helpers::traverseValue($object, $add);
|
||||
}
|
||||
|
||||
return [$generators, $fibers];
|
||||
}
|
||||
}
|
418
libs/Nette/Tracy/BlueScreen/assets/bluescreen.css
Normal file
418
libs/Nette/Tracy/BlueScreen/assets/bluescreen.css
Normal file
@ -0,0 +1,418 @@
|
||||
/**
|
||||
* This file is part of the Tracy (https://tracy.nette.org)
|
||||
*/
|
||||
|
||||
:root {
|
||||
--tracy-space: 16px;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
:root {
|
||||
--tracy-space: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
html.tracy-bs-visible,
|
||||
html.tracy-bs-visible body {
|
||||
display: block;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#tracy-bs {
|
||||
font: 9pt/1.5 Verdana, sans-serif;
|
||||
background: white;
|
||||
color: #333;
|
||||
position: absolute;
|
||||
z-index: 20000;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#tracy-bs a {
|
||||
text-decoration: none;
|
||||
color: #328ADC;
|
||||
padding: 0 4px;
|
||||
margin: 0 -4px;
|
||||
}
|
||||
|
||||
#tracy-bs a + a {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
#tracy-bs a:hover,
|
||||
#tracy-bs a:focus {
|
||||
color: #085AA3;
|
||||
}
|
||||
|
||||
#tracy-bs-toggle {
|
||||
position: absolute;
|
||||
right: .5em;
|
||||
top: .5em;
|
||||
text-decoration: none;
|
||||
background: #CD1818;
|
||||
color: white !important;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
#tracy-bs-toggle.tracy-collapsed {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.tracy-bs-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: 80vh;
|
||||
}
|
||||
|
||||
.tracy-bs-main.tracy-collapsed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#tracy-bs p,
|
||||
#tracy-bs table,
|
||||
#tracy-bs pre,
|
||||
#tracy-bs h1,
|
||||
#tracy-bs h2,
|
||||
#tracy-bs h3 {
|
||||
margin: 0 0 var(--tracy-space);
|
||||
}
|
||||
|
||||
#tracy-bs h1 {
|
||||
font-size: 15pt;
|
||||
font-weight: normal;
|
||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, .3);
|
||||
}
|
||||
|
||||
#tracy-bs h1 span {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
#tracy-bs h2 {
|
||||
font-size: 14pt;
|
||||
font-weight: normal;
|
||||
margin-top: var(--tracy-space);
|
||||
}
|
||||
|
||||
#tracy-bs h3 {
|
||||
font-size: 10pt;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#tracy-bs pre,
|
||||
#tracy-bs code,
|
||||
#tracy-bs table {
|
||||
font: 9pt/1.5 Consolas, monospace !important;
|
||||
}
|
||||
|
||||
#tracy-bs pre,
|
||||
#tracy-bs table {
|
||||
background: #FDF5CE;
|
||||
padding: .4em .7em;
|
||||
border: 2px solid #ffffffa6;
|
||||
box-shadow: 1px 2px 6px #00000005;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#tracy-bs table pre {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#tracy-bs table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#tracy-bs td,
|
||||
#tracy-bs th {
|
||||
vertical-align: top;
|
||||
text-align: left;
|
||||
padding: 2px 6px;
|
||||
border: 1px solid #e6dfbf;
|
||||
}
|
||||
|
||||
#tracy-bs th {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#tracy-bs tr > :first-child {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
#tracy-bs tr:nth-child(2n),
|
||||
#tracy-bs tr:nth-child(2n) pre {
|
||||
background-color: #F7F0CB;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-footer--sticky {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
#tracy-bs footer ul {
|
||||
font-size: 7pt;
|
||||
padding: var(--tracy-space);
|
||||
margin: var(--tracy-space) 0 0;
|
||||
color: #777;
|
||||
background: #F6F5F3;
|
||||
border-top: 1px solid #DDD;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-footer-logo {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-footer-logo a {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 100px;
|
||||
height: 50px;
|
||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAAAUBAMAAAD/1DctAAAAMFBMVEWupZzj39rEvbTy8O3X0sz9/PvGwLu8tavQysHq6OS0rKP5+Pbd2dT29fPMxbzPx8DKErMJAAAACXBIWXMAAAsTAAALEwEAmpwYAAACGUlEQVQoFX3TQWgTQRQA0MWLIJJDYehBTykhG5ERTx56K1u8eEhCYtomE7x5L4iLh0ViF7egewuFFqSIYE6hIHsIYQ6CQSg9CDKn4QsNCRlB59C74J/ZNHW1+An5+bOPyf6/s46oz2P+A0yIeZZ2ieEHi6TOnLKTxvWq+b52mxlVO3xnM1s7xLX1504XQH65OnW2dBqn7cCkYsFsfYsWpyY/2salmFTpEyzeR8zosYqMdiPDXdyU52K1wgEa/SjGpdEwUAxqvRfckQCDOyFearsEHe2grvkh/cFAHKvdtI3lcVceKQIOFpv+FOZaNPQBwJZLPp+hfrvT5JZXaUFsR8zqQc9qSgAharkfS5M/5F6nGJJAtXq/eLr3ucZpHccSxOOIPaQhtHohpCH2Xu6rLmQ0djnr4/+J3C6v+AW8/XWYxwYNdlhWj/P5fPSTQwVr0T9lGxdaBCqErNZaqYnEwbkjEB3NasGF3lPdrHa1nnxNOMgj0+neePUPjd2v/qVvUv29ifvc19huQ48qwXShy/9o8o3OSk0cs37mOFd0Ydgvsf/oZEnPVtggfd66lORn9mDyyzXU13SRtH2L6aR5T/snGAcZPfAXz5J1YlJWBEuxdMYqQecpBrlM49xAbmqyHA+xlA1FxBtqT2xmJoNXZlIt74ZBLeJ9ZGDqByNI7p543idzJ23vXEv7IgnsxiS+eNtwNbFdLq7+Bi4wQ0I4SVb9AAAAAElFTkSuQmCC') no-repeat;
|
||||
opacity: .6;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-footer-logo a:hover,
|
||||
#tracy-bs .tracy-footer-logo a:focus {
|
||||
opacity: 1;
|
||||
transition: opacity 0.1s;
|
||||
}
|
||||
|
||||
|
||||
#tracy-bs .tracy-section {
|
||||
padding-left: calc(1.5 * var(--tracy-space));
|
||||
padding-right: calc(1.5 * var(--tracy-space));
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-section-panel {
|
||||
background: #F4F3F1;
|
||||
padding: var(--tracy-space) var(--tracy-space) 0;
|
||||
margin: 0 0 var(--tracy-space);
|
||||
border-radius: 8px;
|
||||
box-shadow: inset 1px 1px 0px 0 #00000005;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#tracy-bs .outer, /* deprecated */
|
||||
#tracy-bs .tracy-pane {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#tracy-bs.tracy-mac .tracy-pane {
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
|
||||
/* header */
|
||||
#tracy-bs .tracy-section--error {
|
||||
background: #CD1818;
|
||||
color: white;
|
||||
font-size: 13pt;
|
||||
padding-top: var(--tracy-space);
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-section--error h1 {
|
||||
color: white;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-section--error::selection,
|
||||
#tracy-bs .tracy-section--error ::selection {
|
||||
color: black !important;
|
||||
background: #FDF5CE !important;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-section--error a {
|
||||
color: #ffefa1 !important;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-section--error span span {
|
||||
font-size: 80%;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-section--error a.tracy-action {
|
||||
color: white !important;
|
||||
opacity: 0;
|
||||
font-size: .7em;
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-section--error:hover a.tracy-action {
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-section--error a.tracy-action:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-section--error i {
|
||||
color: #ffefa1;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
/* source code */
|
||||
#tracy-bs pre.tracy-code > div {
|
||||
min-width: 100%;
|
||||
float: left;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-line-highlight {
|
||||
background: #CD1818;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
display: block;
|
||||
padding: 0 1ch;
|
||||
margin: 0 -1ch;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-column-highlight {
|
||||
display: inline-block;
|
||||
backdrop-filter: grayscale(1);
|
||||
margin: 0 -1px;
|
||||
padding: 0 1px;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-line {
|
||||
color: #9F9C7F;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
#tracy-bs a.tracy-editor {
|
||||
color: inherit;
|
||||
border-bottom: 1px dotted rgba(0, 0, 0, .3);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
#tracy-bs a.tracy-editor:hover {
|
||||
background: #0001;
|
||||
}
|
||||
|
||||
#tracy-bs span[data-tracy-href] {
|
||||
border-bottom: 1px dotted rgba(0, 0, 0, .3);
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-dump-whitespace {
|
||||
color: #0003;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-caused {
|
||||
float: right;
|
||||
padding: .3em calc(1.5 * var(--tracy-space));
|
||||
background: #df8075;
|
||||
border-radius: 0 0 0 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-caused a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-callstack {
|
||||
display: grid;
|
||||
grid-template-columns: max-content 1fr;
|
||||
margin-bottom: calc(.5 * var(--tracy-space));
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-callstack-file {
|
||||
text-align: right;
|
||||
padding-right: var(--tracy-space);
|
||||
white-space: nowrap;
|
||||
height: calc(1.5 * var(--tracy-space));
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-callstack-callee {
|
||||
white-space: nowrap;
|
||||
height: calc(1.5 * var(--tracy-space));
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-callstack-additional {
|
||||
grid-column-start: 1;
|
||||
grid-column-end: 3;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-callstack-args tr:first-child > * {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-callstack-args tr:first-child td:before {
|
||||
position: absolute;
|
||||
right: .3em;
|
||||
content: 'may not be true';
|
||||
opacity: .4;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-panel-fadein {
|
||||
animation: tracy-panel-fadein .12s ease;
|
||||
}
|
||||
|
||||
@keyframes tracy-panel-fadein {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-section--causedby {
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-section--causedby:not(.tracy-collapsed) {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-section--causedby .tracy-section--error {
|
||||
background: #cd1818a6;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-section--error + .tracy-section--stack {
|
||||
margin-top: calc(1.5 * var(--tracy-space));
|
||||
}
|
||||
|
||||
|
||||
/* tabs */
|
||||
#tracy-bs .tracy-tab-bar {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
font-size: 110%;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-tab-bar > *:not(:first-child) {
|
||||
margin-left: var(--tracy-space);
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-tab-bar a {
|
||||
display: block;
|
||||
padding: calc(.5 * var(--tracy-space)) var(--tracy-space);
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
border-radius: 5px 5px 0 0;
|
||||
text-decoration: none;
|
||||
transition: all 0.1s;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-tab-bar > .tracy-active a {
|
||||
background: white;
|
||||
}
|
||||
|
||||
#tracy-bs .tracy-tab-panel {
|
||||
border-top: 2px solid white;
|
||||
padding-top: var(--tracy-space);
|
||||
overflow: auto;
|
||||
}
|
77
libs/Nette/Tracy/BlueScreen/assets/bluescreen.js
Normal file
77
libs/Nette/Tracy/BlueScreen/assets/bluescreen.js
Normal file
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* This file is part of the Tracy (https://tracy.nette.org)
|
||||
*/
|
||||
|
||||
class BlueScreen
|
||||
{
|
||||
static init(ajax) {
|
||||
BlueScreen.globalInit();
|
||||
|
||||
let blueScreen = document.getElementById('tracy-bs');
|
||||
|
||||
document.documentElement.classList.add('tracy-bs-visible');
|
||||
if (navigator.platform.indexOf('Mac') > -1) {
|
||||
blueScreen.classList.add('tracy-mac');
|
||||
}
|
||||
|
||||
blueScreen.addEventListener('tracy-toggle', (e) => {
|
||||
if (e.target.matches('#tracy-bs-toggle')) { // blue screen toggle
|
||||
document.documentElement.classList.toggle('tracy-bs-visible', !e.detail.collapsed);
|
||||
|
||||
} else if (!e.target.matches('.tracy-dump *') && e.detail.originalEvent) { // panel toggle
|
||||
e.detail.relatedTarget.classList.toggle('tracy-panel-fadein', !e.detail.collapsed);
|
||||
}
|
||||
});
|
||||
|
||||
if (!ajax) {
|
||||
document.body.appendChild(blueScreen);
|
||||
let id = location.href + document.querySelector('.tracy-section--error').textContent;
|
||||
Tracy.Toggle.persist(blueScreen, sessionStorage.getItem('tracy-toggles-bskey') === id);
|
||||
sessionStorage.setItem('tracy-toggles-bskey', id);
|
||||
}
|
||||
|
||||
(new ResizeObserver(stickyFooter)).observe(blueScreen);
|
||||
|
||||
if (document.documentElement.classList.contains('tracy-bs-visible')) {
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static globalInit() {
|
||||
// enables toggling via ESC
|
||||
document.addEventListener('keyup', (e) => {
|
||||
if (e.keyCode === 27 && !e.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey) { // ESC
|
||||
Tracy.Toggle.toggle(document.getElementById('tracy-bs-toggle'));
|
||||
}
|
||||
});
|
||||
|
||||
Tracy.TableSort.init();
|
||||
Tracy.Tabs.init();
|
||||
|
||||
window.addEventListener('scroll', stickyFooter);
|
||||
|
||||
BlueScreen.globalInit = function() {};
|
||||
}
|
||||
|
||||
|
||||
static loadAjax(content) {
|
||||
let ajaxBs = document.getElementById('tracy-bs');
|
||||
if (ajaxBs) {
|
||||
ajaxBs.remove();
|
||||
}
|
||||
document.body.insertAdjacentHTML('beforeend', content);
|
||||
ajaxBs = document.getElementById('tracy-bs');
|
||||
Tracy.Dumper.init(ajaxBs);
|
||||
BlueScreen.init(true);
|
||||
}
|
||||
}
|
||||
|
||||
function stickyFooter() {
|
||||
let footer = document.querySelector('#tracy-bs footer');
|
||||
footer.classList.toggle('tracy-footer--sticky', false); // to measure footer.offsetTop
|
||||
footer.classList.toggle('tracy-footer--sticky', footer.offsetHeight + footer.offsetTop - window.innerHeight - document.documentElement.scrollTop < 0);
|
||||
}
|
||||
|
||||
let Tracy = window.Tracy = window.Tracy || {};
|
||||
Tracy.BlueScreen = Tracy.BlueScreen || BlueScreen;
|
73
libs/Nette/Tracy/BlueScreen/assets/content.phtml
Normal file
73
libs/Nette/Tracy/BlueScreen/assets/content.phtml
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tracy;
|
||||
|
||||
/**
|
||||
* @var \Throwable $exception
|
||||
* @var array[] $actions
|
||||
* @var string[] $info
|
||||
* @var string $source
|
||||
* @var ?array $lastError
|
||||
* @var string[] $httpHeaders
|
||||
* @var callable $dump
|
||||
* @var array $snapshot
|
||||
* @var bool $showEnvironment
|
||||
* @var BlueScreen $this
|
||||
* @var bool $headersSent
|
||||
* @var ?string $headersFile
|
||||
* @var ?int $headersLine
|
||||
* @var ?array $obStatus
|
||||
* @var \Generator[] $generators
|
||||
* @var \Fiber[] $fibers
|
||||
*/
|
||||
?>
|
||||
<tracy-div id="tracy-bs" itemscope>
|
||||
<a id="tracy-bs-toggle" href="#" class="tracy-toggle"></a>
|
||||
<div class="tracy-bs-main">
|
||||
<?php $ex = $exception; $exceptions = []; ?>
|
||||
<?php require __DIR__ . '/section-exception.phtml' ?>
|
||||
|
||||
<?php require __DIR__ . '/section-lastMutedError.phtml' ?>
|
||||
|
||||
<?php $bottomPanels = [] ?>
|
||||
<?php foreach ($this->renderPanels(null) as $panel): ?>
|
||||
<?php if (!empty($panel->bottom)) { $bottomPanels[] = $panel; continue; } ?>
|
||||
<?php $collapsedClass = !isset($panel->collapsed) || $panel->collapsed ? ' tracy-collapsed' : ''; ?>
|
||||
<section class="tracy-section">
|
||||
<h2 class="tracy-section-label"><a href="#" data-tracy-ref="^+" class="tracy-toggle<?= $collapsedClass ?>"><?= Helpers::escapeHtml($panel->tab) ?></a></h2>
|
||||
|
||||
<div class="tracy-section-panel<?= $collapsedClass ?>">
|
||||
<?= $panel->panel ?>
|
||||
</div>
|
||||
</section>
|
||||
<?php endforeach ?>
|
||||
|
||||
<?php require __DIR__ . '/section-environment.phtml' ?>
|
||||
|
||||
<?php require __DIR__ . '/section-cli.phtml' ?>
|
||||
|
||||
<?php require __DIR__ . '/section-http.phtml' ?>
|
||||
|
||||
<?php foreach ($bottomPanels as $panel): ?>
|
||||
<section class="tracy-section">
|
||||
<h2 class="tracy-section-label"><a href="#" data-tracy-ref="^+" class="tracy-toggle"><?= Helpers::escapeHtml($panel->tab) ?></a></h2>
|
||||
|
||||
<div class="tracy-section-panel">
|
||||
<?= $panel->panel ?>
|
||||
</div>
|
||||
</section>
|
||||
<?php endforeach ?>
|
||||
|
||||
<footer>
|
||||
<ul>
|
||||
<li><b><a href="https://github.com/sponsors/dg" target="_blank" rel="noreferrer noopener">Please support Tracy via a donation 💙️</a></b></li>
|
||||
<li>Report generated at <?= date('Y/m/d H:i:s') ?></li>
|
||||
<?php foreach ($info as $item): ?><li><?= Helpers::escapeHtml($item) ?></li><?php endforeach ?>
|
||||
</ul>
|
||||
<div class="tracy-footer-logo"><a href="https://tracy.nette.org" rel="noreferrer"></a></div>
|
||||
</footer>
|
||||
</div>
|
||||
<meta itemprop=tracy-snapshot content=<?= Dumper::formatSnapshotAttribute($snapshot) ?>>
|
||||
</tracy-div>
|
55
libs/Nette/Tracy/BlueScreen/assets/page.phtml
Normal file
55
libs/Nette/Tracy/BlueScreen/assets/page.phtml
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tracy;
|
||||
|
||||
/**
|
||||
* @var \Throwable $exception
|
||||
* @var string $title
|
||||
* @var ?string $nonce
|
||||
* @var string $css
|
||||
*/
|
||||
|
||||
$code = $exception->getCode() ? ' #' . $exception->getCode() : '';
|
||||
$nonceAttr = $nonce ? ' nonce="' . Helpers::escapeHtml($nonce) . '"' : '';
|
||||
$chain = Helpers::getExceptionChain($exception);
|
||||
?><!DOCTYPE html><!-- "' --></textarea></script></style></pre></xmp></a></iframe></noembed></noframes></noscript></option></select></template></title></table></p></code>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="robots" content="noindex">
|
||||
<meta name="generator" content="Tracy by Nette Framework">
|
||||
|
||||
<title><?= Helpers::escapeHtml($title . ': ' . $exception->getMessage() . $code) ?></title>
|
||||
<!-- in <?= str_replace('--', '- ', Helpers::escapeHtml($exception->getFile() . ':' . $exception->getLine())) ?> -->
|
||||
<?php if (count($chain) > 1): ?>
|
||||
<!--<?php foreach (array_slice($chain, 1) as $ex) {
|
||||
echo str_replace('--', '- ', Helpers::escapeHtml("\n\tcaused by " . Helpers::getClass($ex) . ': ' . $ex->getMessage() . ($ex->getCode() ? ' #' . $ex->getCode() : '')));
|
||||
} ?> -->
|
||||
<?php endif ?>
|
||||
|
||||
<style class="tracy-debug">
|
||||
<?= str_replace('</', '<\/', $css) ?>
|
||||
</style>
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
<?php require __DIR__ . '/content.phtml' ?>
|
||||
|
||||
<script<?= $nonceAttr ?>>
|
||||
'use strict';
|
||||
<?php
|
||||
array_map(function ($file) { echo '(function(){', str_replace(['<!--', '</s'], ['<\!--', '<\/s'], Helpers::minifyJs(file_get_contents($file))), '})();'; }, [
|
||||
__DIR__ . '/../../assets/toggle.js',
|
||||
__DIR__ . '/../../assets/table-sort.js',
|
||||
__DIR__ . '/../../assets/tabs.js',
|
||||
__DIR__ . '/../../Dumper/assets/dumper.js',
|
||||
__DIR__ . '/bluescreen.js',
|
||||
]);
|
||||
?>
|
||||
Tracy.BlueScreen.init();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
36
libs/Nette/Tracy/BlueScreen/assets/section-cli.phtml
Normal file
36
libs/Nette/Tracy/BlueScreen/assets/section-cli.phtml
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tracy;
|
||||
|
||||
/**
|
||||
* @var string $source
|
||||
* @var callable $dump
|
||||
*/
|
||||
|
||||
if (!Helpers::isCli()) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<section class="tracy-section">
|
||||
<h2 class="tracy-section-label"><a href="#" data-tracy-ref="^+" class="tracy-toggle tracy-collapsed">CLI request</a></h2>
|
||||
|
||||
<div class="tracy-section-panel tracy-collapsed">
|
||||
<h3>Process ID <?= Helpers::escapeHtml(getmypid()) ?></h3>
|
||||
<?php if (count($tmp = explode('):', $source, 2)) === 2): ?>
|
||||
<pre>php<?= Helpers::escapeHtml($tmp[1]) ?></pre>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_SERVER['argv'])): ?>
|
||||
<h3>Arguments</h3>
|
||||
<div class="tracy-pane">
|
||||
<table>
|
||||
<?php foreach ($_SERVER['argv'] as $k => $v): ?>
|
||||
<tr><th><?= Helpers::escapeHtml($k) ?></th><td><?= $dump($v, $k) ?></td></tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</section>
|
103
libs/Nette/Tracy/BlueScreen/assets/section-environment.phtml
Normal file
103
libs/Nette/Tracy/BlueScreen/assets/section-environment.phtml
Normal file
@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tracy;
|
||||
|
||||
/**
|
||||
* @var callable $dump
|
||||
* @var bool $showEnvironment
|
||||
* @var array $obStatus
|
||||
* @var BlueScreen $this
|
||||
*/
|
||||
|
||||
if (!$showEnvironment) {
|
||||
return;
|
||||
}
|
||||
|
||||
$constants = get_defined_constants(true)['user'] ?? [];
|
||||
?>
|
||||
<section class="tracy-section">
|
||||
<h2 class="tracy-section-label"><a href="#" data-tracy-ref="^+" class="tracy-toggle tracy-collapsed">Environment</a></h2>
|
||||
|
||||
<div class="tracy-section-panel tracy-collapsed">
|
||||
|
||||
<div class="tracy-tabs">
|
||||
<ul class="tracy-tab-bar">
|
||||
<li class="tracy-tab-label tracy-active"><a href="#">$_SERVER</a></li>
|
||||
<?php if ($_SESSION ?? null): ?>
|
||||
<li class="tracy-tab-label"><a href="#">$_SESSION</a></li>
|
||||
<?php endif ?>
|
||||
<?php if ($constants): ?>
|
||||
<li class="tracy-tab-label"><a href="#">Constants</a></li>
|
||||
<?php endif ?>
|
||||
<li class="tracy-tab-label"><a href="#">Configuration</a></li>
|
||||
<?php if ($obStatus): ?>
|
||||
<li class="tracy-tab-label"><a href="#">Output buffers</a></li>
|
||||
<?php endif ?>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
<div>
|
||||
<div class="tracy-tab-panel tracy-pane tracy-active">
|
||||
<table class="tracy-sortable">
|
||||
<?php foreach ($_SERVER as $k => $v): ?>
|
||||
<tr><th><?= Helpers::escapeHtml($k) ?></th><td><?= $dump($v, $k) ?></td></tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<?php if ($_SESSION ?? null): ?>
|
||||
<div class="tracy-tab-panel">
|
||||
<div class="tracy-pane">
|
||||
<table class="tracy-sortable">
|
||||
<?php foreach ($_SESSION as $k => $v): ?>
|
||||
<tr><th><?= Helpers::escapeHtml($k) ?></th><td><?= $k === '__NF' ? '<i>Nette Session</i>' : $dump($v, $k) ?></td></tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<?php if (!empty($_SESSION['__NF']['DATA'])):?>
|
||||
<h3>Nette Session</h3>
|
||||
<div class="tracy-pane">
|
||||
<table class="tracy-sortable">
|
||||
<?php foreach ($_SESSION['__NF']['DATA'] as $k => $v): ?>
|
||||
<tr><th><?= Helpers::escapeHtml($k) ?></th><td><?= $dump($v, $k) ?></td></tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
|
||||
|
||||
<?php if ($constants): ?>
|
||||
<div class="tracy-tab-panel tracy-pane">
|
||||
<table class="tracy-sortable">
|
||||
<?php foreach ($constants as $k => $v): ?>
|
||||
<tr><th><?= Helpers::escapeHtml($k) ?></th><td><?= $dump($v, $k) ?></td></tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
|
||||
<div class="tracy-tab-panel">
|
||||
<?php $this->renderPhpInfo() ?>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<?php if ($obStatus): ?>
|
||||
<div class="tracy-tab-panel tracy-pane">
|
||||
<?= Dumper::toHtml($obStatus, [Dumper::COLLAPSE_COUNT => 10]) ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tracy;
|
||||
|
||||
/**
|
||||
* @var \Throwable $ex
|
||||
* @var \Throwable[] $exceptions
|
||||
* @var BlueScreen $this
|
||||
* @var array[] $actions
|
||||
* @var callable $dump
|
||||
*/
|
||||
|
||||
$ex = $ex->getPrevious();
|
||||
if (!$ex || in_array($ex, $exceptions, true)) {
|
||||
return;
|
||||
}
|
||||
$exceptions[] = $ex;
|
||||
?>
|
||||
|
||||
<section class="tracy-section" id="tracyCaused<?= count($exceptions) ?>">
|
||||
<h2 class="tracy-section-label"><a href="#" data-tracy-ref="^+" class="tracy-toggle<?= ($collapsed = count($exceptions) > 1) ? ' tracy-collapsed' : '' ?>">Caused by</a></h2>
|
||||
|
||||
<div class="tracy-section-panel tracy-section--causedby<?= $collapsed ? ' tracy-collapsed' : '' ?>">
|
||||
<?php require __DIR__ . '/section-exception.phtml' ?>
|
||||
|
||||
</div>
|
||||
</section>
|
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tracy;
|
||||
|
||||
/**
|
||||
* @var \Throwable $ex
|
||||
* @var callable $dump
|
||||
*/
|
||||
|
||||
if (count((array) $ex) <= count((array) new \Exception)) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<section class="tracy-section">
|
||||
<h2 class="tracy-section-label"><a href="#" data-tracy-ref="^+" class="tracy-toggle tracy-collapsed">Exception</a></h2>
|
||||
<div class="tracy-section-panel tracy-collapsed">
|
||||
<?= $dump($ex) ?>
|
||||
</div>
|
||||
</section>
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tracy;
|
||||
|
||||
/**
|
||||
* @var \Throwable $ex
|
||||
* @var callable $dump
|
||||
*/
|
||||
|
||||
if (!$ex instanceof \ErrorException || empty($ex->context) || !is_array($ex->context)) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<section class="tracy-section">
|
||||
<h2 class="tracy-section-label"><a href="#" data-tracy-ref="^+" class="tracy-toggle tracy-collapsed">Variables</a></h2>
|
||||
|
||||
<div class="tracy-section-panel tracy-collapsed">
|
||||
<div class="tracy-pane">
|
||||
<table class="tracy-sortable">
|
||||
<?php foreach ($ex->context as $k => $v): ?>
|
||||
<tr><th><?= Helpers::escapeHtml($k) ?></th><td><?= $dump($v, $k) ?></td></tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
74
libs/Nette/Tracy/BlueScreen/assets/section-exception.phtml
Normal file
74
libs/Nette/Tracy/BlueScreen/assets/section-exception.phtml
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tracy;
|
||||
|
||||
/**
|
||||
* @var \Throwable $ex
|
||||
* @var \Throwable[] $exceptions
|
||||
* @var array[] $actions
|
||||
* @var callable $dump
|
||||
* @var BlueScreen $this
|
||||
* @var \Generator[] $generators
|
||||
* @var \Fiber[] $fibers
|
||||
*/
|
||||
|
||||
?>
|
||||
<?php require __DIR__ . '/section-header.phtml' ?>
|
||||
|
||||
<?php foreach ($this->renderPanels($ex) as $panel): ?>
|
||||
<section class="tracy-section">
|
||||
<h2 class="tracy-section-label"><a href="#" data-tracy-ref="^+" class="tracy-toggle"><?= Helpers::escapeHtml($panel->tab) ?></a></h2>
|
||||
|
||||
<div class="tracy-section-panel">
|
||||
<?= $panel->panel ?>
|
||||
</div>
|
||||
</section>
|
||||
<?php endforeach ?>
|
||||
|
||||
<?php if (!$exceptions && ($generators || $fibers)): ?>
|
||||
<section class="tracy-section tracy-section--stack">
|
||||
<div class="tracy-section-panel">
|
||||
<div class="tracy-tabs">
|
||||
<ul class="tracy-tab-bar">
|
||||
<li class="tracy-tab-label tracy-active"><a href="#">Main thread</a></li>
|
||||
|
||||
<?php foreach ($generators as $id => $generator): ?>
|
||||
<li class="tracy-tab-label"><a href="#">Generator #<?= $id ?></a></li>
|
||||
<?php endforeach ?>
|
||||
|
||||
<?php foreach ($fibers as $id => $fiber): ?>
|
||||
<li class="tracy-tab-label"><a href="#">Fiber #<?= $id ?></a></li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
|
||||
<div>
|
||||
<div class="tracy-tab-panel tracy-active">
|
||||
<?php require __DIR__ . '/section-stack-exception.phtml' ?>
|
||||
</div>
|
||||
|
||||
<?php foreach ($generators as $generator): ?>
|
||||
<div class="tracy-tab-panel">
|
||||
<?php require __DIR__ . '/section-stack-generator.phtml' ?>
|
||||
</div>
|
||||
<?php endforeach ?>
|
||||
|
||||
<?php foreach ($fibers as $fiber): ?>
|
||||
<div class="tracy-tab-panel">
|
||||
<?php require __DIR__ . '/section-stack-fiber.phtml' ?>
|
||||
</div>
|
||||
<?php endforeach ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<?php else: ?>
|
||||
<?php require __DIR__ . '/section-stack-exception.phtml' ?>
|
||||
<?php endif ?>
|
||||
|
||||
<?php require __DIR__ . '/section-exception-variables.phtml' ?>
|
||||
|
||||
<?php require __DIR__ . '/section-exception-exception.phtml' ?>
|
||||
|
||||
<?php require __DIR__ . '/section-exception-causedBy.phtml' ?>
|
35
libs/Nette/Tracy/BlueScreen/assets/section-header.phtml
Normal file
35
libs/Nette/Tracy/BlueScreen/assets/section-header.phtml
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tracy;
|
||||
|
||||
/**
|
||||
* @var \Throwable $ex
|
||||
* @var \Throwable[] $exceptions
|
||||
* @var array[] $actions
|
||||
* @var BlueScreen $this
|
||||
*/
|
||||
|
||||
$title = $ex instanceof \ErrorException
|
||||
? Helpers::errorTypeToString($ex->getSeverity())
|
||||
: Helpers::getClass($ex);
|
||||
$code = $ex->getCode() ? ' #' . $ex->getCode() : '';
|
||||
|
||||
?>
|
||||
<section class="tracy-section tracy-section--error">
|
||||
<?php if ($ex->getMessage()): ?><p><?= Helpers::escapeHtml($title . $code) ?></p><?php endif ?>
|
||||
|
||||
|
||||
<h1><span><?= $this->formatMessage($ex) ?: Helpers::escapeHtml($title . $code) ?></span>
|
||||
<?php foreach ($actions as $item): ?>
|
||||
<a href="<?= Helpers::escapeHtml($item['link']) ?>" class="tracy-action"<?= empty($item['external']) ? '' : ' target="_blank" rel="noreferrer noopener"'?>><?= Helpers::escapeHtml($item['label']) ?>►</a>
|
||||
<?php endforeach ?>
|
||||
</h1>
|
||||
</section>
|
||||
|
||||
<?php if ($ex->getPrevious()): ?>
|
||||
<div class="tracy-caused">
|
||||
<a href="#tracyCaused<?= count($exceptions) + 1 ?>">Caused by <?= Helpers::escapeHtml(Helpers::getClass($ex->getPrevious())) ?></a>
|
||||
</div>
|
||||
<?php endif ?>
|
91
libs/Nette/Tracy/BlueScreen/assets/section-http.phtml
Normal file
91
libs/Nette/Tracy/BlueScreen/assets/section-http.phtml
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tracy;
|
||||
|
||||
/**
|
||||
* @var string $source
|
||||
* @var string[] $httpHeaders
|
||||
* @var callable $dump
|
||||
* @var bool $headersSent
|
||||
* @var ?string $headersFile
|
||||
* @var ?int $headersLine
|
||||
*/
|
||||
|
||||
if (Helpers::isCli()) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<section class="tracy-section">
|
||||
<h2 class="tracy-section-label"><a href="#" data-tracy-ref="^+" class="tracy-toggle tracy-collapsed">HTTP</a></h2>
|
||||
|
||||
<div class="tracy-section-panel tracy-collapsed">
|
||||
|
||||
<div class="tracy-tabs">
|
||||
<ul class="tracy-tab-bar">
|
||||
<li class="tracy-tab-label tracy-active"><a href="#">Request</a></li>
|
||||
<li class="tracy-tab-label"><a href="#">Response</a></li>
|
||||
</ul>
|
||||
|
||||
<div>
|
||||
|
||||
<div class="tracy-tab-panel tracy-active">
|
||||
<h3><?= Helpers::escapeHtml($_SERVER['REQUEST_METHOD'] ?? 'URL') ?> <a href="<?= Helpers::escapeHtml($source) ?>" target="_blank" rel="noreferrer noopener" style="font-weight: normal"><?= Helpers::escapeHtml($source) ?></a></h3>
|
||||
|
||||
<?php if ($httpHeaders): ?>
|
||||
<div class="tracy-pane">
|
||||
<table class="tracy-sortable">
|
||||
<?php foreach ($httpHeaders as $k => $v): ?>
|
||||
<tr><th><?= Helpers::escapeHtml($k) ?></th><td><?= $dump($v, $k) ?></td></tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
|
||||
<?php foreach (['_GET', '_POST', '_COOKIE'] as $name): ?>
|
||||
<h3>$<?= Helpers::escapeHtml($name) ?></h3>
|
||||
<?php if (empty($GLOBALS[$name])):?>
|
||||
<p><i>empty</i></p>
|
||||
<?php else: ?>
|
||||
<div class="tracy-pane">
|
||||
<table class="tracy-sortable">
|
||||
<?php foreach ($GLOBALS[$name] as $k => $v): ?>
|
||||
<tr><th><?= Helpers::escapeHtml($k) ?></th><td><?= $dump($v, $k) ?></td></tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="tracy-tab-panel">
|
||||
<h3>Code: <?= Helpers::escapeHtml(http_response_code()) ?></h3>
|
||||
<?php if (headers_list()): ?>
|
||||
<div class="tracy-pane">
|
||||
<table class="tracy-sortable">
|
||||
<?php foreach (headers_list() as $s): $s = explode(':', $s, 2); ?>
|
||||
<tr><th><?= Helpers::escapeHtml($s[0]) ?></th><td><?= $dump(trim($s[1]), $s[0]) ?></td></tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<p><i>no headers</i></p>
|
||||
<?php endif ?>
|
||||
|
||||
|
||||
<?php if ($headersSent && $headersFile && @is_file($headersFile)): ?>
|
||||
<p>Headers have been sent, output started at <?= Helpers::editorLink($headersFile, $headersLine) ?> <a href="#" data-tracy-ref="^p + div" class="tracy-toggle tracy-collapsed">source</a></p>
|
||||
<div class="tracy-collapsed"><?= BlueScreen::highlightFile($headersFile, $headersLine) ?></div>
|
||||
<?php elseif ($headersSent): ?>
|
||||
<p>Headers have been sent</p>
|
||||
<?php else: ?>
|
||||
<p>Headers were not sent at the time the exception was thrown</p>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tracy;
|
||||
|
||||
/**
|
||||
* @var ?array $lastError
|
||||
*/
|
||||
|
||||
if (!$lastError) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<section class="tracy-section">
|
||||
<h2 class="tracy-section-label"><a href="#" data-tracy-ref="^+" class="tracy-toggle tracy-collapsed">Last muted error</a></h2>
|
||||
<div class="tracy-section-panel tracy-collapsed">
|
||||
|
||||
<h3><?= Helpers::errorTypeToString($lastError['type']) ?>: <?= Helpers::escapeHtml($lastError['message']) ?></h3>
|
||||
<p><i>Note: the last muted error may have nothing to do with the thrown exception.</i></p>
|
||||
|
||||
<?php if (isset($lastError['file']) && @is_file($lastError['file'])): // @ - may trigger error ?>
|
||||
<p><?= Helpers::editorLink($lastError['file'], $lastError['line']) ?></p>
|
||||
<div><?= BlueScreen::highlightFile($lastError['file'], $lastError['line']) ?></div>
|
||||
<?php else: ?>
|
||||
<p><i>inner-code</i><?php if (isset($lastError['line'])) echo ':', $lastError['line'] ?></p>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</section>
|
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tracy;
|
||||
|
||||
/**
|
||||
* @var callable $dump
|
||||
* @var int $expanded
|
||||
* @var array $stack
|
||||
*/
|
||||
|
||||
if (!$stack) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
|
||||
<section class="tracy-section">
|
||||
<h2 class="tracy-section-label"><a href="#" data-tracy-ref="^+" class="tracy-toggle">Call stack</a></h2>
|
||||
|
||||
<div class="tracy-section-panel">
|
||||
<div class="tracy-callstack">
|
||||
<?php foreach ($stack as $key => $row): ?>
|
||||
<?php $clickable = !empty($row['args']) || (isset($row['file']) && @is_file($row['file'])) // @ - may trigger error ?>
|
||||
|
||||
<div class="tracy-callstack-file">
|
||||
<?php if (isset($row['file']) && @is_file($row['file'])): // @ - may trigger error ?>
|
||||
<?= Helpers::editorLink($row['file'], $row['line']) ?>
|
||||
<?php else: ?>
|
||||
<i>inner-code</i><?php if (isset($row['line'])) echo ':', $row['line'] ?>
|
||||
<?php endif ?>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tracy-callstack-callee">
|
||||
<?php if ($clickable): ?>
|
||||
<a href="#" data-tracy-ref="^div + div" class="tracy-toggle<?php if ($expanded !== $key) echo ' tracy-collapsed' ?>"><?php endif ?>
|
||||
<?php if (isset($row['class'])) echo Helpers::escapeHtml($row['class']), '::' ?><b><?= Helpers::escapeHtml($row['function']) ?></b> <?= empty($row['args']) ? '()' : '(...)' ?>
|
||||
<?php if ($clickable): ?></a><?php endif ?>
|
||||
|
||||
</div>
|
||||
|
||||
<?php if ($clickable): ?>
|
||||
<div class="tracy-callstack-additional<?php if ($expanded !== $key) echo ' tracy-collapsed' ?>">
|
||||
<?php $sourceOriginal = isset($row['file']) && @is_file($row['file']) ? [$row['file'], $row['line']] : null // @ - may trigger error ?>
|
||||
<?php $sourceMapped = $sourceOriginal ? Debugger::mapSource(...$sourceOriginal) : null ?>
|
||||
<?php if ($sourceOriginal && $sourceMapped): ?>
|
||||
<div class="tracy-tabs">
|
||||
<ul class="tracy-tab-bar">
|
||||
<li class="tracy-tab-label<?= $sourceMapped['active'] ? '' : ' tracy-active' ?>"><a href="#">PHP</a></li>
|
||||
<li class="tracy-tab-label<?= $sourceMapped['active'] ? ' tracy-active' : '' ?>"><a href="#"><?= Helpers::escapeHtml($sourceMapped['label']) ?></a></li>
|
||||
</ul>
|
||||
|
||||
<div>
|
||||
<div class="tracy-tab-panel<?= $sourceMapped['active'] ? '' : ' tracy-active' ?>">
|
||||
<?= BlueScreen::highlightFile(...$sourceOriginal) ?>
|
||||
</div>
|
||||
|
||||
<div class="tracy-tab-panel<?= $sourceMapped['active'] ? ' tracy-active' : '' ?>">
|
||||
<?= BlueScreen::highlightFile($sourceMapped['file'], $sourceMapped['line'], 15, false) ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php elseif ($sourceOriginal): ?>
|
||||
<?= BlueScreen::highlightFile(...$sourceOriginal) ?>
|
||||
<?php endif ?>
|
||||
|
||||
|
||||
<?php if (!empty($row['args'])): ?>
|
||||
<table class="tracy-callstack-args">
|
||||
<?php
|
||||
try {
|
||||
$r = isset($row['class']) ? new \ReflectionMethod($row['class'], $row['function']) : new \ReflectionFunction($row['function']);
|
||||
$params = $r->getParameters();
|
||||
} catch (\Exception $e) {
|
||||
$params = [];
|
||||
}
|
||||
foreach ($row['args'] as $k => $v) {
|
||||
$argName = isset($params[$k]) && !$params[$k]->isVariadic() ? $params[$k]->name : $k;
|
||||
echo '<tr><th>', Helpers::escapeHtml((is_string($argName) ? '$' : '#') . $argName), '</th><td>';
|
||||
echo $dump($v, (string) $argName);
|
||||
echo "</td></tr>\n";
|
||||
}
|
||||
?>
|
||||
</table>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tracy;
|
||||
|
||||
/**
|
||||
* @var \Throwable $ex
|
||||
* @var callable $dump
|
||||
* @var BlueScreen $this
|
||||
*/
|
||||
|
||||
$stack = $ex->getTrace();
|
||||
$expanded = null;
|
||||
if (
|
||||
(!$ex instanceof \ErrorException
|
||||
|| in_array($ex->getSeverity(), [E_USER_NOTICE, E_USER_WARNING, E_USER_DEPRECATED], true))
|
||||
&& $this->isCollapsed($ex->getFile())
|
||||
) {
|
||||
foreach ($stack as $key => $row) {
|
||||
if (isset($row['file']) && !$this->isCollapsed($row['file'])) {
|
||||
$expanded = $key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($stack[0]['class'] ?? null, [DevelopmentStrategy::class, ProductionStrategy::class], true)) {
|
||||
array_shift($stack);
|
||||
}
|
||||
if (($stack[0]['class'] ?? null) === Debugger::class && in_array($stack[0]['function'], ['shutdownHandler', 'errorHandler'], true)) {
|
||||
array_shift($stack);
|
||||
}
|
||||
$file = $ex->getFile();
|
||||
$line = $ex->getLine();
|
||||
|
||||
require __DIR__ . '/section-stack-sourceFile.phtml';
|
||||
require __DIR__ . '/section-stack-callStack.phtml';
|
16
libs/Nette/Tracy/BlueScreen/assets/section-stack-fiber.phtml
Normal file
16
libs/Nette/Tracy/BlueScreen/assets/section-stack-fiber.phtml
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tracy;
|
||||
|
||||
/**
|
||||
* @var \Fiber $fiber
|
||||
* @var callable $dump
|
||||
*/
|
||||
|
||||
$ref = new \ReflectionFiber($fiber);
|
||||
$stack = $ref->getTrace();
|
||||
$expanded = 0;
|
||||
|
||||
require __DIR__ . '/section-stack-callStack.phtml';
|
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tracy;
|
||||
|
||||
/**
|
||||
* @var \Generator $generator
|
||||
* @var callable $dump
|
||||
*/
|
||||
|
||||
$ref = new \ReflectionGenerator($generator);
|
||||
$stack = $ref->getTrace();
|
||||
$expanded = null;
|
||||
$execGenerator = $ref->getExecutingGenerator();
|
||||
$refExec = new \ReflectionGenerator($execGenerator);
|
||||
$file = $refExec->getExecutingFile();
|
||||
$line = $refExec->getExecutingLine();
|
||||
|
||||
require __DIR__ . '/section-stack-sourceFile.phtml';
|
||||
require __DIR__ . '/section-stack-callStack.phtml';
|
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tracy;
|
||||
|
||||
/**
|
||||
* @var string $file
|
||||
* @var int $line
|
||||
* @var int $expanded
|
||||
*/
|
||||
|
||||
$sourceOriginal = $file && @is_file($file) ? [$file, $line] : null; // @ - may trigger error
|
||||
$sourceMapped = $sourceOriginal ? Debugger::mapSource($file, $line) : null;
|
||||
?>
|
||||
|
||||
<section class="tracy-section">
|
||||
<h2 class="tracy-section-label"><a href="#" data-tracy-ref="^+" class="tracy-toggle<?= ($collapsed = $expanded !== null) ? ' tracy-collapsed' : '' ?>">Source file</a></h2>
|
||||
|
||||
<div class="tracy-section-panel<?= $collapsed ? ' tracy-collapsed' : '' ?>">
|
||||
<?php if ($sourceOriginal && $sourceMapped): ?>
|
||||
<div class="tracy-tabs">
|
||||
<ul class="tracy-tab-bar">
|
||||
<li class="tracy-tab-label<?= $sourceMapped['active'] ? '' : ' tracy-active' ?>"><a href="#">PHP</a></li>
|
||||
<li class="tracy-tab-label<?= $sourceMapped['active'] ? ' tracy-active' : '' ?>"><a href="#"><?= Helpers::escapeHtml($sourceMapped['label']) ?></a></li>
|
||||
</ul>
|
||||
|
||||
<div>
|
||||
<div class="tracy-tab-panel<?= $sourceMapped['active'] ? '' : ' tracy-active' ?>">
|
||||
<p><b>File:</b> <?= Helpers::editorLink(...$sourceOriginal) ?></p>
|
||||
<?= BlueScreen::highlightFile(...$sourceOriginal) ?>
|
||||
</div>
|
||||
|
||||
<div class="tracy-tab-panel<?= $sourceMapped['active'] ? ' tracy-active' : '' ?>">
|
||||
<p><b>File:</b> <?= Helpers::editorLink($sourceMapped['file'], $sourceMapped['line']) ?></p>
|
||||
<?= BlueScreen::highlightFile($sourceMapped['file'], $sourceMapped['line'], 15, false) ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<p><b>File:</b> <?= Helpers::editorLink($file, $line) ?></p>
|
||||
<?php if ($sourceOriginal) echo BlueScreen::highlightFile(...$sourceOriginal) ?>
|
||||
<?php endif ?>
|
||||
|
||||
</div>
|
||||
</section>
|
Reference in New Issue
Block a user