diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php
index 2ca8cfcc2..bc5c6441e 100644
--- a/src/DataCollector/QueryCollector.php
+++ b/src/DataCollector/QueryCollector.php
@@ -288,6 +288,7 @@ protected function parseTrace($index, array $trace)
'index' => $index,
'namespace' => null,
'name' => null,
+ 'file' => null,
'line' => isset($trace['line']) ? $trace['line'] : '?',
];
@@ -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;
}
@@ -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)
{
@@ -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];
}
}
}
@@ -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
@@ -492,6 +475,7 @@ public function collect()
$statements = [];
foreach ($queries as $query) {
+ $source = reset($query['source']);
$totalTime += $query['time'];
$statements[] = [
@@ -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'],
];
diff --git a/src/DataCollector/RouteCollector.php b/src/DataCollector/RouteCollector.php
index a255302f3..11b0b5deb 100644
--- a/src/DataCollector/RouteCollector.php
+++ b/src/DataCollector/RouteCollector.php
@@ -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
@@ -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;
@@ -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('%s:%s-%s', $href, $filename, $reflector->getStartLine(), $reflector->getEndLine());
+ $filename = $this->normalizeFilePath($reflector->getFileName());
+
+ if ($link = $this->getXdebugLink($reflector->getFileName(), $reflector->getStartLine())) {
+ $result['file'] = sprintf(
+ '%s:%s-%s',
+ $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());
}
@@ -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);
- }
}
diff --git a/src/DataCollector/ViewCollector.php b/src/DataCollector/ViewCollector.php
index 959545adf..5b2e662d9 100644
--- a/src/DataCollector/ViewCollector.php
+++ b/src/DataCollector/ViewCollector.php
@@ -5,7 +5,6 @@
use Barryvdh\Debugbar\DataFormatter\SimpleFormatter;
use DebugBar\Bridge\Twig\TwigCollector;
use Illuminate\View\View;
-use InvalidArgumentException;
class ViewCollector extends TwigCollector
{
@@ -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
*
@@ -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
*
@@ -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);
@@ -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;
}
}
@@ -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;
@@ -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);
- }
}
diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php
index 6041afcc9..f6485108d 100644
--- a/src/LaravelDebugbar.php
+++ b/src/LaravelDebugbar.php
@@ -96,6 +96,8 @@ class LaravelDebugbar extends DebugBar
*/
protected $is_lumen = false;
+ protected array $editorTemplateArgs = [];
+
/**
* @param Application $app
*/
@@ -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']);
@@ -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;
}
@@ -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
diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css
index c2163c7ea..9825a45cf 100644
--- a/src/Resources/laravel-debugbar.css
+++ b/src/Resources/laravel-debugbar.css
@@ -578,6 +578,14 @@ ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widget
margin-right: 5px;
}
+ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-stmt-id a {
+ color: #888;
+}
+
+ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-stmt-id a:hover {
+ color: #aaa;
+}
+
ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item table.phpdebugbar-widgets-params {
background-color: #fdfdfd;
margin: 10px 0px;
diff --git a/src/Resources/sqlqueries/widget.js b/src/Resources/sqlqueries/widget.js
index ebed4f08d..27a833542 100644
--- a/src/Resources/sqlqueries/widget.js
+++ b/src/Resources/sqlqueries/widget.js
@@ -83,7 +83,17 @@
$('').addClass(csscls('row-count')).text(stmt.row_count).appendTo(li);
}
if (typeof(stmt.stmt_id) != 'undefined' && stmt.stmt_id) {
- $('').addClass(csscls('stmt-id')).text(stmt.stmt_id).appendTo(li);
+ $('').addClass(csscls('stmt-id'))
+ .append(! stmt.xdebug_link ? $('').text(stmt.stmt_id).html() : $('')
+ .attr('href', stmt.xdebug_link.url).text(stmt.stmt_id)
+ .on('click', function (event) {
+ event.stopPropagation();
+ if (stmt.xdebug_link.ajax) {
+ event.preventDefault();
+ $.ajax(stmt.xdebug_link.url);
+ }
+ })
+ ).appendTo(li);
}
if (stmt.connection) {
$('').addClass(csscls('database')).text(stmt.connection).appendTo(li);