Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added panel sorter #565

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "2.10-dev"
"dev-master": "3.0-dev"
}
}
}
66 changes: 66 additions & 0 deletions examples/xdebug.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

declare(strict_types=1);

require __DIR__ . '/../src/tracy.php';

use Tracy\Debugger;

// For security reasons, Tracy is visible only on localhost.
// You may force Tracy to run in development mode by passing the Debugger::Development instead of Debugger::Detect.
//Debugger::$strictMode = true;
Debugger::enable(Debugger::Detect, __DIR__ . '/log');

?>
<!DOCTYPE html><link rel="stylesheet" href="assets/style.css">

<h1>Tracy: exception demo</h1>

<?php

class DemoClass
{
public function first($arg1, $arg2)
{
$arg1 = 'new';
$arg3 = 'xxx';
$this->second();
}


public function second()
{
self::third([1, 2, 3]);
}


public static function third($arg5)
{
//require __DIR__ . '/assets/E_COMPILE_WARNING-1.php';
//require __DIR__ . '/assets/E_COMPILE_ERROR.php';
// trigger_error('jo', E_USER_ERROR);
// dump(new Exception);
// dumpe(xdebug_get_function_stack( [ 'local_vars' => true, 'params_as_values' => true ] ));
try {
throw new Exception('Original');
} catch (Exception $e) {
throw new Exception('The my exception', 123, $e);
}
$a++;
}
}



function demo($a, $b)
{
$demo = new DemoClass;
$demo->first($a, $b);
}


if (Debugger::$productionMode) {
echo '<p><b>For security reasons, Tracy is visible only on localhost. Look into the source code to see how to enable Tracy.</b></p>';
}

demo(10, 'any string');
5 changes: 3 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ Alternatively, you can download the whole package or [tracy.phar](https://github

| Tracy | compatible with PHP | compatible with browsers
|-----------|---------------|----------
| Tracy 2.10| PHP 8.0 – 8.3 | Chrome 64+, Firefox 69+, Safari 15.4+ and iOS Safari 15.4+
| Tracy 2.9 | PHP 7.2 – 8.2 | Chrome 64+, Firefox 69+, Safari 13.1+ and iOS Safari 13.4+
| Tracy 3.0 | PHP 8.0 – 8.3 | Chrome 112+, Firefox 117+, Safari 16.5+
| Tracy 2.10| PHP 8.0 – 8.3 | Chrome 64+, Firefox 69+, Safari 15.4+
| Tracy 2.9 | PHP 7.2 – 8.2 | Chrome 64+, Firefox 69+, Safari 13.1+


Usage
Expand Down
72 changes: 1 addition & 71 deletions src/Bridges/Nette/Bridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,95 +9,25 @@

namespace Tracy\Bridges\Nette;

use Latte;
use Nette;
use Tracy;
use Tracy\BlueScreen;
use Tracy\Helpers;


/**
* Bridge for NEON & Latte.
* Bridge for NEON.
*/
class Bridge
{
public static function initialize(): void
{
$blueScreen = Tracy\Debugger::getBlueScreen();
if (!class_exists(Latte\Bridges\Tracy\BlueScreenPanel::class)) {
$blueScreen->addPanel([self::class, 'renderLatteError']);
$blueScreen->addAction([self::class, 'renderLatteUnknownMacro']);
$blueScreen->addFileGenerator(fn(string $file) => substr($file, -6) === '.latte'
? "{block content}\n\$END\$"
: null);
Tracy\Debugger::addSourceMapper([self::class, 'mapLatteSourceCode']);
}

$blueScreen->addAction([self::class, 'renderMemberAccessException']);
$blueScreen->addPanel([self::class, 'renderNeonError']);
}


public static function renderLatteError(?\Throwable $e): ?array
{
if ($e instanceof Latte\CompileException && $e->sourceName) {
return [
'tab' => 'Template',
'panel' => (preg_match('#\n|\?#', $e->sourceName)
? ''
: '<p>'
. (@is_file($e->sourceName) // @ - may trigger error
? '<b>File:</b> ' . Helpers::editorLink($e->sourceName, $e->sourceLine)
: '<b>' . htmlspecialchars($e->sourceName . ($e->sourceLine ? ':' . $e->sourceLine : '')) . '</b>')
. '</p>')
. BlueScreen::highlightFile($e->sourceCode, $e->sourceLine, php: false),
];
}

return null;
}


public static function renderLatteUnknownMacro(?\Throwable $e): ?array
{
if (
$e instanceof Latte\CompileException
&& $e->sourceName
&& @is_file($e->sourceName) // @ - may trigger error
&& (preg_match('#Unknown macro (\{\w+)\}, did you mean (\{\w+)\}\?#A', $e->getMessage(), $m)
|| preg_match('#Unknown attribute (n:\w+), did you mean (n:\w+)\?#A', $e->getMessage(), $m))
) {
return [
'link' => Helpers::editorUri($e->sourceName, $e->sourceLine, 'fix', $m[1], $m[2]),
'label' => 'fix it',
];
}

return null;
}


/** @return array{file: string, line: int, label: string, active: bool} */
public static function mapLatteSourceCode(string $file, int $line): ?array
{
if (!strpos($file, '.latte--')) {
return null;
}

$lines = file($file);
if (
!preg_match('#^/(?:\*\*|/) source: (\S+\.latte)#m', implode('', array_slice($lines, 0, 10)), $m)
|| !@is_file($m[1]) // @ - may trigger error
) {
return null;
}

$file = $m[1];
$line = $line && preg_match('#/\* line (\d+) \*/#', $lines[$line - 1], $m) ? (int) $m[1] : 0;
return ['file' => $file, 'line' => $line, 'label' => 'Latte', 'active' => true];
}


public static function renderMemberAccessException(?\Throwable $e): ?array
{
if (!$e instanceof Nette\MemberAccessException && !$e instanceof \LogicException) {
Expand Down
2 changes: 1 addition & 1 deletion src/Bridges/Nette/TracyExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public function afterCompile(Nette\PhpGenerator\ClassType $class): void
if ($this->debugMode) {
foreach ($this->config->bar as $item) {
if (is_string($item) && substr($item, 0, 1) === '@') {
$item = new Statement(['@' . $builder::THIS_CONTAINER, 'getService'], [substr($item, 1)]);
$item = new Statement(['@' . $builder::ThisContainer, 'getService'], [substr($item, 1)]);
} elseif (is_string($item)) {
$item = new Statement($item);
}
Expand Down
16 changes: 15 additions & 1 deletion src/Tracy/Bar/Bar.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@
*/
class Bar
{
/** @var IBarPanel[] */
/** @var array<string, IBarPanel> */
private array $panels = [];
private bool $loaderRendered = false;

/** @var ?callable(string, string): int */
private $panelSorter;


/**
* Add custom panel.
Expand All @@ -37,6 +40,13 @@ public function addPanel(IBarPanel $panel, ?string $id = null): self
return $this;
}

/**
* @param callable(string, string): int $sorter
*/
public function replacePanelSorter(callable $sorter): void
{
$this->panelSorter = $sorter;
}

/**
* Returns panel with given id
Expand Down Expand Up @@ -136,6 +146,10 @@ private function renderPanels(string $suffix = ''): array
$obLevel = ob_get_level();
$panels = [];

if($this->panelSorter !== null) {
uksort($this->panels, $this->panelSorter);
}

foreach ($this->panels as $id => $panel) {
$idHtml = preg_replace('#[^a-z0-9]+#i', '-', $id) . $suffix;
try {
Expand Down
14 changes: 14 additions & 0 deletions src/Tracy/BlueScreen/BlueScreen.php
Original file line number Diff line number Diff line change
Expand Up @@ -499,4 +499,18 @@ private function findGeneratorsAndFibers(object $object): array
Helpers::traverseValue($object, $add);
return [$generators, $fibers];
}


public function getRealArgsAndVariables(\Throwable $exception): array
{
$args = $variables = [];
if (function_exists('xdebug_get_function_stack') && version_compare(phpversion('xdebug'), '3.3.0', '>=')) {
$stack = xdebug_get_function_stack(['from_exception' => $exception]);
foreach (array_reverse($stack) as $k => $row) {
$args[$k] = $row['params'] ?? [];
$variables[$k - 1] = $row['variables'] ?? [];
}
}
return [$args, $variables];
}
}
8 changes: 6 additions & 2 deletions src/Tracy/BlueScreen/assets/bluescreen.css
Original file line number Diff line number Diff line change
Expand Up @@ -350,11 +350,15 @@ html.tracy-bs-visible body {
grid-column-end: 3;
}

#tracy-bs .tracy-callstack-args tr:first-child > * {
#tracy-bs .tracy-callstack-args tr > :first-child {
width: 10em;
}

#tracy-bs .tracy-callstack-args-warning tr:first-child > * {
position: relative;
}

#tracy-bs .tracy-callstack-args tr:first-child td:before {
#tracy-bs .tracy-callstack-args-warning tr:first-child td:before {
position: absolute;
right: .3em;
content: 'may not be true';
Expand Down
34 changes: 31 additions & 3 deletions src/Tracy/BlueScreen/assets/section-stack-callStack.phtml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ namespace Tracy;
* @var callable $dump
* @var int $expanded
* @var array $stack
* @var ?array $realArgs
* @var ?array $variables
*/

if (!$stack) {
Expand Down Expand Up @@ -66,15 +68,27 @@ if (!$stack) {
<?php endif ?>


<?php if (!empty($row['args'])): ?>
<?php if (!empty($realArgs[$key])): ?>
<table class="tracy-callstack-args">
<?php
try {
foreach ($realArgs[$key] as $argName => $v) {
echo '<tr><th>', Helpers::escapeHtml((is_string($argName) ? '$' : '#') . $argName), '</th><td>';
echo $dump($v, $argName);
echo "</td></tr>\n";
}
?>
</table>

<?php elseif (!empty($row['args'])): ?>
<table class="tracy-callstack-args tracy-callstack-args-warning">
<?php
if (isset($row['class']) ? method_exists($row['class'], $row['function']) : function_exists($row['function'])) {
$r = isset($row['class']) ? new \ReflectionMethod($row['class'], $row['function']) : new \ReflectionFunction($row['function']);
$params = $r->getParameters();
} catch (\Exception) {
} else {
$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>';
Expand All @@ -84,6 +98,20 @@ if (!$stack) {
?>
</table>
<?php endif ?>

<?php if (!empty($variables[$key])): ?>
<h3>Local Variables</h3>

<table class="tracy-callstack-args">
<?php
foreach ($variables[$key] as $k => $v) {
echo '<tr><th>$', Helpers::escapeHtml($k), '</th><td>';
echo $dump($v, $k);
echo "</td></tr>\n";
}
?>
</table>
<?php endif ?>
</div>
<?php endif ?>
<?php endforeach ?>
Expand Down
2 changes: 2 additions & 0 deletions src/Tracy/BlueScreen/assets/section-stack-exception.phtml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ if (($stack[0]['class'] ?? null) === Debugger::class && in_array($stack[0]['func
}
$file = $ex->getFile();
$line = $ex->getLine();
[$realArgs, $variables] = $this->getRealArgsAndVariables($ex);

require __DIR__ . '/section-stack-sourceFile.phtml';
require __DIR__ . '/section-stack-variables.phtml';
require __DIR__ . '/section-stack-callStack.phtml';
30 changes: 30 additions & 0 deletions src/Tracy/BlueScreen/assets/section-stack-variables.phtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace Tracy;

/**
* @var ?array $variables
*/

if (empty($variables[-1])) {
return;
}
?>

<section class="tracy-section">
<h2 class="tracy-section-label"><a href="#" data-tracy-ref="^+" class="tracy-toggle tracy-collapsed">Local variables</a></h2>

<div class="tracy-section-panel tracy-collapsed">
<table class="tracy-callstack-args">
<?php
foreach ($variables[-1] as $k => $v) {
echo '<tr><th>$', Helpers::escapeHtml($k), '</th><td>';
echo $dump($v, $k);
echo "</td></tr>\n";
}
?>
</table>
</div>
</section>
4 changes: 2 additions & 2 deletions src/Tracy/Debugger/Debugger.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*/
class Debugger
{
public const Version = '2.10.5';
public const Version = '3.0-dev';

/** server modes for Debugger::enable() */
public const
Expand Down Expand Up @@ -289,7 +289,7 @@ public static function shutdownHandler(): void
{
$error = error_get_last();
if (in_array($error['type'] ?? null, [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE, E_RECOVERABLE_ERROR, E_USER_ERROR], true)) {
self::exceptionHandler(Helpers::fixStack(new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line'])));
self::exceptionHandler(new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']));
} elseif (($error['type'] ?? null) === E_COMPILE_WARNING) {
error_clear_last();
self::errorHandler($error['type'], $error['message'], $error['file'], $error['line']);
Expand Down