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

Support editor link on QueryCollector/ExceptionsCollector #1426

Merged
merged 2 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 18 additions & 33 deletions src/DataCollector/QueryCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ protected function parseTrace($index, array $trace)
'index' => $index,
'namespace' => null,
'name' => null,
'file' => null,
'line' => isset($trace['line']) ? $trace['line'] : '?',
];

Expand All @@ -302,33 +303,36 @@ protected function parseTrace($index, array $trace)
isset($trace['file']) &&
!$this->fileIsInExcludedPath($trace['file'])
) {
$file = $trace['file'];
$frame->file = $trace['file'];

if (isset($trace['object']) && is_a($trace['object'], 'Twig_Template')) {
list($file, $frame->line) = $this->getTwigInfo($trace);
} elseif (strpos($file, storage_path()) !== false) {
$hash = pathinfo($file, PATHINFO_FILENAME);
list($frame->file, $frame->line) = $this->getTwigInfo($trace);
} elseif (strpos($frame->file, storage_path()) !== false) {
$hash = pathinfo($frame->file, PATHINFO_FILENAME);

if (! $frame->name = $this->findViewFromHash($hash)) {
if ($frame->name = $this->findViewFromHash($hash)) {
$frame->file = $frame->name[1];
$frame->name = $frame->name[0];
} else {
$frame->name = $hash;
}

$frame->namespace = 'view';

return $frame;
} elseif (strpos($file, 'Middleware') !== false) {
$frame->name = $this->findMiddlewareFromFile($file);
} elseif (strpos($frame->file, 'Middleware') !== false) {
$frame->name = $this->findMiddlewareFromFile($frame->file);

if ($frame->name) {
$frame->namespace = 'middleware';
} else {
$frame->name = $this->normalizeFilename($file);
$frame->name = $this->normalizeFilePath($frame->file);
}

return $frame;
}

$frame->name = $this->normalizeFilename($file);
$frame->name = $this->normalizeFilePath($frame->file);

return $frame;
}
Expand Down Expand Up @@ -377,7 +381,7 @@ protected function findMiddlewareFromFile($file)
* Find the template name from the hash.
*
* @param string $hash
* @return null|string
* @return null|array
*/
protected function findViewFromHash($hash)
{
Expand All @@ -396,7 +400,7 @@ protected function findViewFromHash($hash)

foreach ($property->getValue($finder) as $name => $path) {
if (($xxh128Exists && hash('xxh128', 'v2' . $path) == $hash) || sha1('v2' . $path) == $hash) {
return $name;
return [$name, $path];
}
}
}
Expand All @@ -422,27 +426,6 @@ protected function getTwigInfo($trace)
return [$file, -1];
}

/**
* Shorten the path by removing the relative links and base dir
*
* @param string $path
* @return string
*/
protected function normalizeFilename($path)
{
if (file_exists($path)) {
$path = realpath($path);
}

$basepath = base_path();

if (! str_starts_with($path, $basepath)) {
return $path;
}

return substr($path, strlen($basepath));
}

/**
* Collect a database transaction event.
* @param string $event
Expand Down Expand Up @@ -492,6 +475,7 @@ public function collect()

$statements = [];
foreach ($queries as $query) {
$source = reset($query['source']);
$totalTime += $query['time'];

$statements[] = [
Expand All @@ -504,7 +488,8 @@ public function collect()
'backtrace' => array_values($query['source']),
'duration' => $query['time'],
'duration_str' => ($query['type'] == 'transaction') ? '' : $this->formatDuration($query['time']),
'stmt_id' => $this->getDataFormatter()->formatSource(reset($query['source'])),
'stmt_id' => $this->getDataFormatter()->formatSource($source),
'xdebug_link' => is_object($source) ? $this->getXdebugLink($source->file ?: '', $source->line) : null,
'connection' => $query['connection'],
];

Expand Down
82 changes: 11 additions & 71 deletions src/DataCollector/RouteCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use Illuminate\Routing\Route;
use Illuminate\Routing\Router;
use Illuminate\Support\Facades\Config;
use InvalidArgumentException;

/**
* Based on Illuminate\Foundation\Console\RoutesCommand for Taylor Otwell
Expand All @@ -25,30 +24,6 @@ class RouteCollector extends DataCollector implements Renderable
*/
protected $router;

/**
* A list of known editor strings.
*
* @var array
*/
protected $editors = [
'sublime' => 'subl://open?url=file://%file&line=%line',
'textmate' => 'txmt://open?url=file://%file&line=%line',
'emacs' => 'emacs://open?url=file://%file&line=%line',
'macvim' => 'mvim://open/?url=file://%file&line=%line',
'phpstorm' => 'phpstorm://open?file=%file&line=%line',
'idea' => 'idea://open?file=%file&line=%line',
'vscode' => 'vscode://file/%file:%line',
'vscode-insiders' => 'vscode-insiders://file/%file:%line',
'vscode-remote' => 'vscode://vscode-remote/%file:%line',
'vscode-insiders-remote' => 'vscode-insiders://vscode-remote/%file:%line',
'vscodium' => 'vscodium://file/%file:%line',
'nova' => 'nova://core/open/file?filename=%file&line=%line',
'xdebug' => 'xdebug://%file@%line',
'atom' => 'atom://core/open/file?filename=%file&line=%line',
'espresso' => 'x-espresso://open?filepath=%file&lines=%line',
'netbeans' => 'netbeans://open/?f=%file:%line',
];

public function __construct(Router $router)
{
$this->router = $router;
Expand Down Expand Up @@ -100,10 +75,17 @@ protected function getRouteInformation($route)
}

if (isset($reflector)) {
$filename = ltrim(str_replace(base_path(), '', $reflector->getFileName()), '/');

if ($href = $this->getEditorHref($reflector->getFileName(), $reflector->getStartLine())) {
$result['file'] = sprintf('<a href="%s">%s:%s-%s</a>', $href, $filename, $reflector->getStartLine(), $reflector->getEndLine());
$filename = $this->normalizeFilePath($reflector->getFileName());

if ($link = $this->getXdebugLink($reflector->getFileName(), $reflector->getStartLine())) {
$result['file'] = sprintf(
'<a href="%s" onclick="%s">%s:%s-%s</a>',
$link['url'],
$link['ajax'] ? 'event.preventDefault();$.ajax(this.href);' : '',
$filename,
$reflector->getStartLine(),
$reflector->getEndLine()
);
} else {
$result['file'] = sprintf('%s:%s-%s', $filename, $reflector->getStartLine(), $reflector->getEndLine());
}
Expand Down Expand Up @@ -175,46 +157,4 @@ protected function displayRoutes(array $routes)

$this->table->render($this->getOutput());
}

/**
* Get the editor href for a given file and line, if available.
*
* @param string $filePath
* @param int $line
*
* @throws InvalidArgumentException If editor resolver does not return a string
*
* @return null|string
*/
protected function getEditorHref($filePath, $line)
{
if (empty(config('debugbar.editor'))) {
return null;
}

if (empty($this->editors[config('debugbar.editor')])) {
throw new InvalidArgumentException(
'Unknown editor identifier: ' . config('debugbar.editor') . '. Known editors:' .
implode(', ', array_keys($this->editors))
);
}

$filePath = $this->replaceSitesPath($filePath);

$url = str_replace(['%file', '%line'], [$filePath, $line], $this->editors[config('debugbar.editor')]);

return $url;
}

/**
* Replace remote path
*
* @param string $filePath
*
* @return string
*/
protected function replaceSitesPath($filePath)
{
return str_replace(config('debugbar.remote_sites_path'), config('debugbar.local_sites_path'), $filePath);
}
}
73 changes: 3 additions & 70 deletions src/DataCollector/ViewCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use Barryvdh\Debugbar\DataFormatter\SimpleFormatter;
use DebugBar\Bridge\Twig\TwigCollector;
use Illuminate\View\View;
use InvalidArgumentException;

class ViewCollector extends TwigCollector
{
Expand All @@ -14,30 +13,6 @@ class ViewCollector extends TwigCollector
protected $collect_data;
protected $exclude_paths;

/**
* A list of known editor strings.
*
* @var array
*/
protected $editors = [
'sublime' => 'subl://open?url=file://%file&line=%line',
'textmate' => 'txmt://open?url=file://%file&line=%line',
'emacs' => 'emacs://open?url=file://%file&line=%line',
'macvim' => 'mvim://open/?url=file://%file&line=%line',
'phpstorm' => 'phpstorm://open?file=%file&line=%line',
'idea' => 'idea://open?file=%file&line=%line',
'vscode' => 'vscode://file/%file:%line',
'vscode-insiders' => 'vscode-insiders://file/%file:%line',
'vscode-remote' => 'vscode://vscode-remote/%file:%line',
'vscode-insiders-remote' => 'vscode-insiders://vscode-remote/%file:%line',
'vscodium' => 'vscodium://file/%file:%line',
'nova' => 'nova://core/open/file?filename=%file&line=%line',
'xdebug' => 'xdebug://%file@%line',
'atom' => 'atom://core/open/file?filename=%file&line=%line',
'espresso' => 'x-espresso://open?filepath=%file&lines=%line',
'netbeans' => 'netbeans://open/?f=%file:%line',
];

/**
* Create a ViewCollector
*
Expand Down Expand Up @@ -73,36 +48,6 @@ public function getWidgets()
];
}

/**
* Get the editor href for a given file and line, if available.
*
* @param string $filePath
* @param int $line
*
* @throws InvalidArgumentException If editor resolver does not return a string
*
* @return null|string
*/
protected function getEditorHref($filePath, $line)
{
if (empty(config('debugbar.editor'))) {
return null;
}

if (empty($this->editors[config('debugbar.editor')])) {
throw new InvalidArgumentException(
'Unknown editor identifier: ' . config('debugbar.editor') . '. Known editors:' .
implode(', ', array_keys($this->editors))
);
}

$filePath = $this->replaceSitesPath($filePath);

$url = str_replace(['%file', '%line'], [$filePath, $line], $this->editors[config('debugbar.editor')]);

return $url;
}

/**
* Add a View instance to the Collector
*
Expand Down Expand Up @@ -132,7 +77,7 @@ public function addView(View $view)
}

if ($path && is_string($path)) {
$path = realpath($path);
$path = $this->normalizeFilePath($path);
$shortPath = ltrim(str_replace(base_path(), '', $path), '/');
} elseif (is_object($path)) {
$type = get_class($view);
Expand All @@ -148,7 +93,7 @@ public function addView(View $view)
}

foreach ($this->exclude_paths as $excludePath) {
if (strpos($shortPath, $excludePath) !== false) {
if (str_starts_with($path, $excludePath)) {
return;
}
}
Expand All @@ -167,7 +112,7 @@ public function addView(View $view)
];

if ($this->getXdebugLinkTemplate()) {
$template['xdebug_link'] = $this->getXdebugLink($path);
$template['xdebug_link'] = $this->getXdebugLink(realpath($view->getPath()));
}

$this->templates[] = $template;
Expand All @@ -182,16 +127,4 @@ public function collect()
'templates' => $templates,
];
}

/**
* Replace remote path
*
* @param string $filePath
*
* @return string
*/
protected function replaceSitesPath($filePath)
{
return str_replace(config('debugbar.remote_sites_path'), config('debugbar.local_sites_path'), $filePath);
}
}
19 changes: 19 additions & 0 deletions src/LaravelDebugbar.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ class LaravelDebugbar extends DebugBar
*/
protected $is_lumen = false;

protected array $editorTemplateArgs = [];

/**
* @param Application $app
*/
Expand Down Expand Up @@ -139,6 +141,8 @@ public function boot()
/** @var Application $app */
$app = $this->app;

$this->editorTemplateArgs = [$this->app['config']->get('debugbar.editor'), $this->getRemoteServerReplacements()];

// Set custom error handler
if ($app['config']->get('debugbar.error_handler', false)) {
set_error_handler([$this, 'handleError']);
Expand Down Expand Up @@ -605,6 +609,10 @@ public function addCollector(DataCollectorInterface $collector)
if (method_exists($collector, 'useHtmlVarDumper')) {
$collector->useHtmlVarDumper();
}
if (method_exists($collector, 'setEditorLinkTemplate')) {
$collector->setEditorLinkTemplate($this->editorTemplateArgs[0]);
$collector->addXdebugReplacements($this->editorTemplateArgs[1]);
}

return $this;
}
Expand Down Expand Up @@ -1192,6 +1200,17 @@ protected function addServerTimingHeaders(Response $response)
}
}

/**
* @return array
*/
private function getRemoteServerReplacements()
{
$localPath = $this->app['config']->get('debugbar.local_sites_path', '');
$remotePaths = array_filter(explode(',', $this->app['config']->get('debugbar.remote_sites_path', ''))) ?: [base_path()];

return array_fill_keys($remotePaths, $localPath);
}

/**
* @return \Monolog\Logger
* @throws Exception
Expand Down
Loading
Loading