From 6019d618772546eaefa59bf37ccd0fcf4dabfb22 Mon Sep 17 00:00:00 2001 From: boynet Date: Mon, 5 Sep 2016 14:16:39 +0300 Subject: [PATCH 001/585] fixing display in RTL languages --- src/Resources/laravel-debugbar.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index f13586f22..5bbb1eac9 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -1,6 +1,8 @@ div.phpdebugbar { font-size: 13px; font-family: "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif; + direction: ltr; + text-align: left; } div.phpdebugbar-resize-handle { From 9d5c0f1bac79bbf05259e2dbe3d8522b705966ed Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Wed, 15 Feb 2017 10:01:31 +0100 Subject: [PATCH 002/585] Add ServerTiming headers for TimeData Collector See https://ma.ttias.be/server-timings-chrome-devtools/ --- src/LaravelDebugbar.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 0ae234745..03934d90d 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -611,6 +611,8 @@ public function modifyResponse(Request $request, Response $response) } } + $this->addServerTimingHeaders($response); + return $response; } @@ -906,4 +908,23 @@ protected function addClockworkHeaders(Response $response) $response->headers->set('X-Clockwork-Version', 1, true); $response->headers->set('X-Clockwork-Path', $prefix .'/clockwork/', true); } + + /** + * Add Server-Timing headers for the TimeData collector + * + * @see https://www.w3.org/TR/server-timing/ + * @param Response $response + */ + protected function addServerTimingHeaders(Response $response) + { + if ($this->hasCollector('time')) { + $collector = $this->getCollector('time'); + + foreach ($collector->collect()['measures'] as $k => $m) { + $headers[] = sprintf('%d=%F; "%s"', $k, $m['duration'], str_replace('"', "'", $m['label'])); + } + + $response->headers->set('Server-Timing', $collector->getServerTimingHeaders(), false); + } + } } From 9b70a71055ea99d8d16fca23f3639aa808fe678f Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Wed, 15 Feb 2017 14:17:07 +0100 Subject: [PATCH 003/585] Update LaravelDebugbar.php --- src/LaravelDebugbar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 03934d90d..80477f079 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -924,7 +924,7 @@ protected function addServerTimingHeaders(Response $response) $headers[] = sprintf('%d=%F; "%s"', $k, $m['duration'], str_replace('"', "'", $m['label'])); } - $response->headers->set('Server-Timing', $collector->getServerTimingHeaders(), false); + $response->headers->set('Server-Timing', $headers, false); } } } From 84ebc068969b5918afc7a4fb010d38f9999b0eaf Mon Sep 17 00:00:00 2001 From: Burak Erdem Date: Wed, 15 Feb 2017 15:31:49 +0200 Subject: [PATCH 004/585] Update query log icon. --- src/DataCollector/QueryCollector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index aa55ffcd4..c578fdcd7 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -379,7 +379,7 @@ public function getWidgets() { return [ "queries" => [ - "icon" => "inbox", + "icon" => "database", "widget" => "PhpDebugBar.Widgets.SQLQueriesWidget", "map" => "queries", "default" => "[]" From ac6adfef8794d01f6d49a9a603e474701365f5e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Nikolaou?= Date: Fri, 17 Feb 2017 11:18:52 +0200 Subject: [PATCH 005/585] Improve the queries tab - Collect database transactions events (begin, commit, rollback). - Display the stack of the backtrace when a query item is expanded. - Model binding is detected explicitly in the `Router#substituteBindings` method. - The backtrace now detects middleware. - Redesigned the params table to display bindings, hints and backtrace nicely. - The data formatting methods have been extracted to a `QueryFormatter`. See https://github.com/barryvdh/laravel-debugbar/issues/483 P.S.: In the screenshot I have replace the fonts with a system font task. I plan to submit a PR to the [php-debugbar](https://github.com/maximebf/php-debugbar) repo about that. --- src/DataCollector/QueryCollector.php | 204 +++++++++++++++++---------- src/DataFormatter/QueryFormatter.php | 201 ++++++++++++++++++++++++++ src/LaravelDebugbar.php | 6 +- src/Resources/laravel-debugbar.css | 54 ++++++- 4 files changed, 382 insertions(+), 83 deletions(-) create mode 100644 src/DataFormatter/QueryFormatter.php diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index c578fdcd7..a1da1c84d 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -14,6 +14,7 @@ class QueryCollector extends PDOCollector protected $queries = []; protected $renderSqlWithParams = false; protected $findSource = false; + protected $middleware = []; protected $explainQuery = false; protected $explainTypes = ['SELECT']; // ['SELECT', 'INSERT', 'UPDATE', 'DELETE']; for MySQL 5.6.3+ protected $showHints = false; @@ -52,10 +53,12 @@ public function setShowHints($enabled = true) * Enable/disable finding the source * * @param bool $value + * @param array $middleware */ - public function setFindSource($value = true) + public function setFindSource($value, array $middleware) { $this->findSource = (bool) $value; + $this->middleware = $middleware; } /** @@ -97,7 +100,7 @@ public function addQuery($query, $bindings, $time, $connection) $explainResults = $statement->fetchAll(\PDO::FETCH_CLASS); } - $bindings = $this->checkBindings($bindings); + $bindings = $this->getDataFormatter()->checkBindings($bindings); if (!empty($bindings) && $this->renderSqlWithParams) { foreach ($bindings as $key => $binding) { // This regex matches placeholders only, not the question marks, @@ -110,7 +113,8 @@ public function addQuery($query, $bindings, $time, $connection) } } - $source = null; + $source = []; + if ($this->findSource) { try { $source = $this->findSource(); @@ -120,7 +124,7 @@ public function addQuery($query, $bindings, $time, $connection) $this->queries[] = [ 'query' => $query, - 'bindings' => $this->escapeBindings($bindings), + 'bindings' => $this->getDataFormatter()->escapeBindings($bindings), 'time' => $time, 'source' => $source, 'explain' => $explainResults, @@ -133,36 +137,6 @@ public function addQuery($query, $bindings, $time, $connection) } } - /** - * Check bindings for illegal (non UTF-8) strings, like Binary data. - * - * @param $bindings - * @return mixed - */ - protected function checkBindings($bindings) - { - foreach ($bindings as &$binding) { - if (is_string($binding) && !mb_check_encoding($binding, 'UTF-8')) { - $binding = '[BINARY DATA]'; - } - } - return $bindings; - } - - /** - * Make the bindings safe for outputting. - * - * @param array $bindings - * @return array - */ - protected function escapeBindings($bindings) - { - foreach ($bindings as &$binding) { - $binding = htmlentities($binding, ENT_QUOTES, 'UTF-8', false); - } - return $bindings; - } - /** * Explainer::performQueryAnalysis() * @@ -200,39 +174,103 @@ protected function performQueryAnalysis($query) $hints[] = 'An argument has a leading wildcard character: ' . $matches[1]. '. The predicate with this argument is not sargable and cannot use an index if one exists.'; } - return implode("
", $hints); + return $hints; } /** - * Use a backtrace to search for the origin of the query. + * Use a backtrace to search for the origins of the query. + * + * @return array */ protected function findSource() { - $traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT); - foreach ($traces as $trace) { - if (isset($trace['class']) && isset($trace['file']) && strpos( - $trace['file'], - DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR - ) === false - ) { - if (isset($trace['object']) && is_a($trace['object'], 'Twig_Template')) { - list($file, $line) = $this->getTwigInfo($trace); - } elseif (strpos($trace['file'], storage_path()) !== false) { - $hash = pathinfo($trace['file'], PATHINFO_FILENAME); - $line = isset($trace['line']) ? $trace['line'] : '?'; - - if ($name = $this->findViewFromHash($hash)) { - return 'view::' . $name . ':' . $line; - } - return 'view::' . $hash . ':' . $line; + $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT); + + $sources = []; + + foreach ($stack as $index => $trace) { + $sources[] = $this->parseTrace($index, $trace); + } + + return array_filter($sources); + } + + /** + * Parse a trace element from the backtrace stack. + * + * @param int $index + * @param array $trace + * @return array|bool + */ + protected function parseTrace($index, array $trace) + { + $frame = (object) [ + 'index' => $index, + 'namespace' => null, + 'name' => null, + 'file' => null, + 'line' => isset($trace['line']) ? $trace['line'] : '?', + ]; + + if (isset($trace['function']) && $trace['function'] == 'substituteBindings') { + $frame->name = 'Route binding'; + + return $frame; + } + + if (isset($trace['class']) && isset($trace['file']) && strpos( + $trace['file'], + DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR + ) === false + ) { + $frame->file = $trace['file']; + + if (isset($trace['object']) && is_a($trace['object'], 'Twig_Template')) { + 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)) { + $frame->name = $hash; + } + + $frame->namespace = 'view'; + + return $frame; + } elseif (strpos($frame->file, 'Middleware') !== false) { + $frame->name = $this->findMiddlewareFromFile($frame->file); + + if ($frame->name) { + $frame->namespace = 'middleware'; } else { - $file = $trace['file']; - $line = isset($trace['line']) ? $trace['line'] : '?'; + $frame->name = $this->normalizeFilename($frame->file); } - return $this->normalizeFilename($file) . ':' . $line; - } elseif (isset($trace['function']) && $trace['function'] == 'Illuminate\Routing\{closure}') { - return 'Route binding'; + return $frame; + } + + $frame->name = $this->normalizeFilename($frame->file); + + return $frame; + } + + + return false; + } + + /** + * Find the middleware alias from the file. + * + * @param string $file + * @return string|null + */ + protected function findMiddlewareFromFile($file) + { + $filename = pathinfo($file, PATHINFO_FILENAME); + + foreach ($this->middleware as $alias => $class) { + if (strpos($class, $filename) !== false) { + return $alias; } } } @@ -298,6 +336,34 @@ protected function normalizeFilename($path) return str_replace(base_path(), '', $path); } + /** + * Collect a database transaction event. + * @param string $event + * @param \Illuminate\Database\Connection $connection + * @return array + */ + public function collectTransactionEvent($event, $connection) + { + $source = []; + + if ($this->findSource) { + try { + $source = $this->findSource(); + } catch (\Exception $e) { + } + } + + $this->queries[] = [ + 'query' => $event, + 'bindings' => [], + 'time' => 0, + 'source' => $source, + 'explain' => [], + 'connection' => $connection->getDatabaseName(), + 'hints' => null, + ]; + } + /** * Reset the queries. */ @@ -318,17 +384,14 @@ public function collect() foreach ($queries as $query) { $totalTime += $query['time']; - $bindings = $query['bindings']; - if($query['hints']){ - $bindings['hints'] = $query['hints']; - } + $metadata = $this->getDataFormatter()->formatMetadata($query); $statements[] = [ - 'sql' => $this->formatSql($query['query']), - 'params' => (object) $bindings, + 'sql' => $this->getDataFormatter()->formatSql($query['query']), + 'params' => $metadata, 'duration' => $query['time'], 'duration_str' => $this->formatDuration($query['time']), - 'stmt_id' => $query['source'], + 'stmt_id' => $this->getDataFormatter()->formatSource(reset($query['source'])), 'connection' => $query['connection'], ]; @@ -353,17 +416,6 @@ public function collect() return $data; } - /** - * Removes extra spaces at the beginning and end of the SQL query and its lines. - * - * @param string $sql - * @return string - */ - protected function formatSql($sql) - { - return trim(preg_replace("/\s*\n\s*/", "\n", $sql)); - } - /** * {@inheritDoc} */ diff --git a/src/DataFormatter/QueryFormatter.php b/src/DataFormatter/QueryFormatter.php new file mode 100644 index 000000000..d8b93a9f2 --- /dev/null +++ b/src/DataFormatter/QueryFormatter.php @@ -0,0 +1,201 @@ +' . $index . '. ' . $binding; + }, $bindings, array_keys($bindings)); + + return $this->formatList($bindings); + } + + /** + * Format the hints into an list. + * + * @param array $hints + * @return string + */ + public function formatHints(array $hints) + { + return $this->formatList($hints); + } + + /** + * Format the backtrace sources into an ordered list. + * + * @param array $sources + * @return string + */ + public function formatSources(array $sources) + { + $items = []; + + foreach ($sources as $source) { + $parts = [ + 'index' => '' . $source->index . '. ', + ]; + + if ($source->namespace) { + $parts['namespace'] = $source->namespace . '::'; + } + + $parts['name'] = $source->name; + $parts['line'] = ':' . $source->line . ''; + + $items[] = implode($parts); + } + + return $this->formatList($items); + } + + /** + * Format a source object. + * + * @param object|null $source If the backtrace is disabled, the $source will be null. + * @return string + */ + public function formatSource($source) + { + if (! is_object($source)) { + return ''; + } + + $parts = []; + + if ($source->namespace) { + $parts['namespace'] = $source->namespace . '::'; + } + + $parts['name'] = $source->name; + $parts['line'] = ':' . $source->line; + + return implode($parts); + } + + /** + * Generate an array with metadata about the query. + * + * @param array $query + * @return object + */ + public function formatMetadata(array $query) + { + $metadata = (object) []; + + if ($query['bindings']) { + $metadata = $this->addMetadata( + $metadata, 'bindings', $this->formatBindings($query['bindings']) + ); + } + + if ($query['hints']) { + $metadata = $this->addMetadata( + $metadata, 'hints', $this->formatHints($query['hints']) + ); + } + + if ($query['source']) { + $metadata = $this->addMetadata( + $metadata, 'backtrace', $this->formatSources($query['source']) + ); + } + + return $metadata; + } + + /** + * Append an item to the metadata. + * + * @param object $metadata + * @param string $name + * @param string $parameter + * @return object + */ + public function addMetadata($metadata, $name, $parameter) + { + $icons = [ + 'bindings' => 'thumb-tack', + 'hints' => 'question-circle', + 'backtrace' => 'list-ul', + ]; + + $icon = isset($icons[$name]) ? $icons[$name] : 'circle'; + + $key = ucfirst($name) . ' '; + + $metadata->$key = $parameter; + + return $metadata; + } + + /** + * Format an array into a list. + * + * @param array $items + * @return string + */ + protected function formatList(array $items) + { + $list = []; + + foreach ($items as $item) { + $list[] = '
  • ' . $item . '
  • '; + } + + return '
      ' . implode($list) . '
    '; + } +} diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 80477f079..9e6a9c69d 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -22,6 +22,7 @@ use DebugBar\DataCollector\PhpInfoCollector; use DebugBar\DataCollector\RequestDataCollector; use DebugBar\DataCollector\TimeDataCollector; +use Barryvdh\Debugbar\DataFormatter\QueryFormatter; use Barryvdh\Debugbar\Support\Clockwork\ClockworkCollector; use DebugBar\DebugBar; use DebugBar\Storage\PdoStorage; @@ -286,12 +287,15 @@ function ($level, $message = null, $context = null) use ($logger) { } $queryCollector = new QueryCollector($timeCollector); + $queryCollector->setDataFormatter(new QueryFormatter()); + if ($this->app['config']->get('debugbar.options.db.with_params')) { $queryCollector->setRenderSqlWithParams(true); } if ($this->app['config']->get('debugbar.options.db.backtrace')) { - $queryCollector->setFindSource(true); + $middleware = $this->app['router']->getMiddleware(); + $queryCollector->setFindSource(true, $middleware); } if ($this->app['config']->get('debugbar.options.db.explain.enabled')) { diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index 5bbb1eac9..d5c06f831 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -200,25 +200,36 @@ ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item { font-family: inherit; overflow: visible; display: flex; + flex-wrap: wrap; } ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-sql { - flex: 1 1 auto; + flex: 1; + margin-right: 5px; } ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-duration { - flex: 0 0 auto; + /*flex: 0 0 auto;*/ + margin-left: auto; + margin-right: 5px; } ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-database { - flex: 0 0 auto; + /*flex: 0 0 auto;*/ + margin-left: auto; +} + +ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-stmt-id { + /*flex: 0 0 auto;*/ + margin-left: auto; + margin-right: 5px; } ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-params { + background-color: rgba(255, 255, 255, .5); flex: 1 1 auto; - order: -1; - width: auto; - max-width: 200px; + margin: 10px 100% 10px 0; + max-width: 100%; } ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item:nth-child(even) { @@ -243,8 +254,39 @@ div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugb color: #f1c40f; } +div.phpdebugbar-widgets-sqlqueries { + line-height: 20px; +} + div.phpdebugbar-widgets-sqlqueries .phpdebugbar-widgets-status { background: none !important; font-family: inherit !important; font-weight: 400 !important; } + +div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params th, +div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params td { + padding: 5px 10px; +} + +div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params td.phpdebugbar-widgets-name { + text-align: right; + vertical-align: top; + white-space: nowrap; +} + +div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params td.phpdebugbar-widgets-value { + text-align: left; +} + +ul.phpdebugbar-widgets-list ul.phpdebugbar-widgets-table-list { + text-align: left; +} + +ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-table-list-item { + /*padding: 5px 10px;*/ +} + +.phpdebugbar-text-muted { + color: #888; +} From 506c1ebb9e344ee3f79c04ec30d42b5323671558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Nikolaou?= Date: Sat, 18 Feb 2017 17:06:08 +0200 Subject: [PATCH 006/585] Add missing event listeners for database transactions --- src/LaravelDebugbar.php | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 9e6a9c69d..b44c52617 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -337,6 +337,37 @@ function ($query, $bindings = null, $time = null, $connectionName = null) use ($ ) ); } + + try { + $db->getEventDispatcher()->listen([ + \Illuminate\Database\Events\TransactionBeginning::class, + 'connection.*.beganTransaction', + ], function ($transaction) use ($queryCollector) { + $queryCollector->collectTransactionEvent('Begin Transaction', $transaction->connection); + }); + + $db->getEventDispatcher()->listen([ + \Illuminate\Database\Events\TransactionCommitted::class, + 'connection.*.committed', + ], function ($transaction) use ($queryCollector) { + $queryCollector->collectTransactionEvent('Commit Transaction', $transaction->connection); + }); + + $db->getEventDispatcher()->listen([ + \Illuminate\Database\Events\TransactionRolledBack::class, + 'connection.*.rollingBack', + ], function ($transaction) use ($queryCollector) { + $queryCollector->collectTransactionEvent('Rollback Transaction', $transaction->connection); + }); + } catch (\Exception $e) { + $this->addThrowable( + new Exception( + 'Cannot add listen transactions to Queries for Laravel Debugbar: ' . $e->getMessage(), + $e->getCode(), + $e + ) + ); + } } if ($this->shouldCollect('mail', true) && class_exists('Illuminate\Mail\MailServiceProvider')) { From 0d552cfdd703fc72c7b1a70c6f4d172794e5c48d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Nikolaou?= Date: Sat, 18 Feb 2017 21:16:11 +0200 Subject: [PATCH 007/585] Delegate HTML rendering of query metadata to LaravelSQLQueriesWidget --- src/DataCollector/QueryCollector.php | 38 +++-- src/DataFormatter/QueryFormatter.php | 131 +--------------- src/Resources/laravel-debugbar.css | 4 + src/Resources/sqlqueries/widget.js | 225 +++++++++++++++++++++++++++ 4 files changed, 255 insertions(+), 143 deletions(-) create mode 100644 src/Resources/sqlqueries/widget.js diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index a1da1c84d..ec1baff9b 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -124,6 +124,7 @@ public function addQuery($query, $bindings, $time, $connection) $this->queries[] = [ 'query' => $query, + 'type' => 'query', 'bindings' => $this->getDataFormatter()->escapeBindings($bindings), 'time' => $time, 'source' => $source, @@ -208,7 +209,6 @@ protected function parseTrace($index, array $trace) 'index' => $index, 'namespace' => null, 'name' => null, - 'file' => null, 'line' => isset($trace['line']) ? $trace['line'] : '?', ]; @@ -223,12 +223,12 @@ protected function parseTrace($index, array $trace) DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR ) === false ) { - $frame->file = $trace['file']; + $file = $trace['file']; if (isset($trace['object']) && is_a($trace['object'], 'Twig_Template')) { - list($frame->file, $frame->line) = $this->getTwigInfo($trace); - } elseif (strpos($frame->file, storage_path()) !== false) { - $hash = pathinfo($frame->file, PATHINFO_FILENAME); + list($file, $frame->line) = $this->getTwigInfo($trace); + } elseif (strpos($file, storage_path()) !== false) { + $hash = pathinfo($file, PATHINFO_FILENAME); if (! $frame->name = $this->findViewFromHash($hash)) { $frame->name = $hash; @@ -237,19 +237,19 @@ protected function parseTrace($index, array $trace) $frame->namespace = 'view'; return $frame; - } elseif (strpos($frame->file, 'Middleware') !== false) { - $frame->name = $this->findMiddlewareFromFile($frame->file); + } elseif (strpos($file, 'Middleware') !== false) { + $frame->name = $this->findMiddlewareFromFile($file); if ($frame->name) { $frame->namespace = 'middleware'; } else { - $frame->name = $this->normalizeFilename($frame->file); + $frame->name = $this->normalizeFilename($file); } return $frame; } - $frame->name = $this->normalizeFilename($frame->file); + $frame->name = $this->normalizeFilename($file); return $frame; } @@ -355,6 +355,7 @@ public function collectTransactionEvent($event, $connection) $this->queries[] = [ 'query' => $event, + 'type' => 'transaction', 'bindings' => [], 'time' => 0, 'source' => $source, @@ -384,13 +385,15 @@ public function collect() foreach ($queries as $query) { $totalTime += $query['time']; - $metadata = $this->getDataFormatter()->formatMetadata($query); - $statements[] = [ 'sql' => $this->getDataFormatter()->formatSql($query['query']), - 'params' => $metadata, + 'type' => $query['type'], + 'params' => [], + 'bindings' => $query['bindings'], + 'hints' => $query['hints'], + 'backtrace' => array_values($query['source']), 'duration' => $query['time'], - 'duration_str' => $this->formatDuration($query['time']), + 'duration_str' => ($query['type'] == 'transaction') ? '' : $this->formatDuration($query['time']), 'stmt_id' => $this->getDataFormatter()->formatSource(reset($query['source'])), 'connection' => $query['connection'], ]; @@ -399,6 +402,7 @@ public function collect() foreach($query['explain'] as $explain){ $statements[] = [ 'sql' => ' - EXPLAIN #' . $explain->id . ': `' . $explain->table . '` (' . $explain->select_type . ')', + 'type' => 'explain', 'params' => $explain, 'row_count' => $explain->rows, 'stmt_id' => $explain->id, @@ -406,8 +410,12 @@ public function collect() } } + $nb_statements = array_filter($queries, function ($query) { + return $query['type'] == 'query'; + }); + $data = [ - 'nb_statements' => count($queries), + 'nb_statements' => count($nb_statements), 'nb_failed_statements' => 0, 'accumulated_duration' => $totalTime, 'accumulated_duration_str' => $this->formatDuration($totalTime), @@ -432,7 +440,7 @@ public function getWidgets() return [ "queries" => [ "icon" => "database", - "widget" => "PhpDebugBar.Widgets.SQLQueriesWidget", + "widget" => "PhpDebugBar.Widgets.LaravelSQLQueriesWidget", "map" => "queries", "default" => "[]" ], diff --git a/src/DataFormatter/QueryFormatter.php b/src/DataFormatter/QueryFormatter.php index d8b93a9f2..aa0db8c7b 100644 --- a/src/DataFormatter/QueryFormatter.php +++ b/src/DataFormatter/QueryFormatter.php @@ -10,7 +10,7 @@ class QueryFormatter extends DataFormatter /** * Removes extra spaces at the beginning and end of the SQL query and its lines. * - * @param string $sql + * @param string $sql * @return string */ public function formatSql($sql) @@ -31,6 +31,7 @@ public function checkBindings($bindings) $binding = '[BINARY DATA]'; } } + return $bindings; } @@ -45,61 +46,8 @@ public function escapeBindings($bindings) foreach ($bindings as &$binding) { $binding = htmlentities($binding, ENT_QUOTES, 'UTF-8', false); } - return $bindings; - } - - /** - * Format query bindings into an ordered list. - * - * @param array $bindings - * @return string - */ - public function formatBindings(array $bindings) - { - $bindings = array_map(function ($binding, $index) { - return '' . $index . '. ' . $binding; - }, $bindings, array_keys($bindings)); - - return $this->formatList($bindings); - } - - /** - * Format the hints into an list. - * - * @param array $hints - * @return string - */ - public function formatHints(array $hints) - { - return $this->formatList($hints); - } - - /** - * Format the backtrace sources into an ordered list. - * - * @param array $sources - * @return string - */ - public function formatSources(array $sources) - { - $items = []; - - foreach ($sources as $source) { - $parts = [ - 'index' => '' . $source->index . '. ', - ]; - - if ($source->namespace) { - $parts['namespace'] = $source->namespace . '::'; - } - - $parts['name'] = $source->name; - $parts['line'] = ':' . $source->line . ''; - - $items[] = implode($parts); - } - return $this->formatList($items); + return $bindings; } /** @@ -125,77 +73,4 @@ public function formatSource($source) return implode($parts); } - - /** - * Generate an array with metadata about the query. - * - * @param array $query - * @return object - */ - public function formatMetadata(array $query) - { - $metadata = (object) []; - - if ($query['bindings']) { - $metadata = $this->addMetadata( - $metadata, 'bindings', $this->formatBindings($query['bindings']) - ); - } - - if ($query['hints']) { - $metadata = $this->addMetadata( - $metadata, 'hints', $this->formatHints($query['hints']) - ); - } - - if ($query['source']) { - $metadata = $this->addMetadata( - $metadata, 'backtrace', $this->formatSources($query['source']) - ); - } - - return $metadata; - } - - /** - * Append an item to the metadata. - * - * @param object $metadata - * @param string $name - * @param string $parameter - * @return object - */ - public function addMetadata($metadata, $name, $parameter) - { - $icons = [ - 'bindings' => 'thumb-tack', - 'hints' => 'question-circle', - 'backtrace' => 'list-ul', - ]; - - $icon = isset($icons[$name]) ? $icons[$name] : 'circle'; - - $key = ucfirst($name) . ' '; - - $metadata->$key = $parameter; - - return $metadata; - } - - /** - * Format an array into a list. - * - * @param array $items - * @return string - */ - protected function formatList(array $items) - { - $list = []; - - foreach ($items as $item) { - $list[] = '
  • ' . $item . '
  • '; - } - - return '
      ' . implode($list) . '
    '; - } } diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index d5c06f831..7e93000f6 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -203,6 +203,10 @@ ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item { flex-wrap: wrap; } +.phpdebugbar-widgets-sql.phpdebugbar-widgets-name { + font-weight: bold; +} + ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-sql { flex: 1; margin-right: 5px; diff --git a/src/Resources/sqlqueries/widget.js b/src/Resources/sqlqueries/widget.js new file mode 100644 index 000000000..17e273c52 --- /dev/null +++ b/src/Resources/sqlqueries/widget.js @@ -0,0 +1,225 @@ +(function($) { + + var csscls = PhpDebugBar.utils.makecsscls('phpdebugbar-widgets-'); + + /** + * Widget for the displaying sql queries + * + * Options: + * - data + */ + var LaravelSQLQueriesWidget = PhpDebugBar.Widgets.LaravelSQLQueriesWidget = PhpDebugBar.Widget.extend({ + + className: csscls('sqlqueries'), + + onFilterClick: function(el) { + $(el).toggleClass(csscls('excluded')); + + var excludedLabels = []; + this.$toolbar.find(csscls('.filter') + csscls('.excluded')).each(function() { + excludedLabels.push(this.rel); + }); + + this.$list.$el.find("li[connection=" + $(el).attr("rel") + "]").toggle(); + + this.set('exclude', excludedLabels); + }, + + render: function() { + this.$status = $('
    ').addClass(csscls('status')).appendTo(this.$el); + + this.$toolbar = $('
    ').addClass(csscls('toolbar')).appendTo(this.$el); + + var filters = [], self = this; + + this.$list = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function(li, stmt) { + if (stmt.type === 'transaction') { + $('').addClass(csscls('sql')).addClass(csscls('name')).text(stmt.sql).appendTo(li); + } else { + $('').addClass(csscls('sql')).html(PhpDebugBar.Widgets.highlight(stmt.sql, 'sql')).appendTo(li); + } + if (stmt.duration_str) { + $('').addClass(csscls('duration')).text(stmt.duration_str).appendTo(li); + } + if (stmt.memory_str) { + $('').addClass(csscls('memory')).text(stmt.memory_str).appendTo(li); + } + if (typeof(stmt.row_count) != 'undefined') { + $('').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); + } + if (stmt.connection) { + $('').addClass(csscls('database')).text(stmt.connection).appendTo(li); + li.attr("connection",stmt.connection); + if ( $.inArray(stmt.connection, filters) == -1 ) { + filters.push(stmt.connection); + $('') + .addClass(csscls('filter')) + .text(stmt.connection) + .attr('rel', stmt.connection) + .on('click', function() { self.onFilterClick(this); }) + .appendTo(self.$toolbar); + if (filters.length>1) { + self.$toolbar.show(); + self.$list.$el.css("margin-bottom","20px"); + } + } + } + if (typeof(stmt.is_success) != 'undefined' && !stmt.is_success) { + li.addClass(csscls('error')); + li.append($('').addClass(csscls('error')).text("[" + stmt.error_code + "] " + stmt.error_message)); + } + + var table = $('
    Metadata
    ').addClass(csscls('params')).appendTo(li); + + if (stmt.bindings && stmt.bindings.length) { + table.append(function () { + var icon = 'thumb-tack'; + var $icon = ''; + var $name = $('').addClass(csscls('name')).html('Bindings ' + $icon); + var $value = $('').addClass(csscls('value')); + var $span = $('').addClass('phpdebugbar-text-muted'); + + var index = 0; + var $bindings = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function(li, binding) { + var $index = $span.clone().text(index++ + '.'); + li.append($index, ' ', binding).removeClass(csscls('list-item')).addClass(csscls('table-list-item')); + }}); + + $bindings.set('data', stmt.bindings); + + $bindings.$el + .removeClass(csscls('list')) + .addClass(csscls('table-list')) + .appendTo($value); + + return $('').append($name, $value); + }); + } + + if (stmt.hints && stmt.hints.length) { + table.append(function () { + var icon = 'question-circle'; + var $icon = ''; + var $name = $('').addClass(csscls('name')).html('Hints ' + $icon); + var $value = $('').addClass(csscls('value')); + + var $hints = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function(li, hint) { + li.append(hint).removeClass(csscls('list-item')).addClass(csscls('table-list-item')); + }}); + + $hints.set('data', stmt.hints); + $hints.$el + .removeClass(csscls('list')) + .addClass(csscls('table-list')) + .appendTo($value); + + return $('').append($name, $value); + }); + } + + if (stmt.backtrace && stmt.backtrace.length) { + table.append(function () { + var icon = 'list-ul'; + var $icon = ''; + var $name = $('').addClass(csscls('name')).html('Backtrace ' + $icon); + var $value = $('').addClass(csscls('value')); + var $span = $('').addClass('phpdebugbar-text-muted'); + + var $backtrace = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function(li, source) { + var $parts = [ + $span.clone().text(source.index + '.'), + ' ', + ]; + + if (source.namespace) { + $parts.push(source.namespace + '::'); + } + + $parts.push(source.name); + $parts.push($span.clone().text(':' + source.line)); + + li.append($parts).removeClass(csscls('list-item')).addClass(csscls('table-list-item')); + }}); + + $backtrace.set('data', stmt.backtrace); + + $backtrace.$el + .removeClass(csscls('list')) + .addClass(csscls('table-list')) + .appendTo($value); + + return $('').append($name, $value); + }); + } + + if (stmt.params && !$.isEmptyObject(stmt.params)) { + for (var key in stmt.params) { + if (typeof stmt.params[key] !== 'function') { + table.append('' + key + '' + stmt.params[key] + ''); + } + } + } + + li.css('cursor', 'pointer').click(function() { + if (table.is(':visible')) { + table.hide(); + } else { + table.show(); + } + }); + }}); + this.$list.$el.appendTo(this.$el); + + this.bindAttr('data', function(data) { + this.$list.set('data', data.statements); + this.$status.empty(); + var stmt; + + // Search for duplicate statements. + for (var sql = {}, duplicate = 0, i = 0; i < data.statements.length; i++) { + if(data.statements[i].type === 'query') { + stmt = data.statements[i].sql; + if (data.statements[i].bindings && data.statements[i].bindings.length) { + stmt += JSON.stringify(data.statements[i].bindings); + } + sql[stmt] = sql[stmt] || { keys: [] }; + sql[stmt].keys.push(i); + } + } + // Add classes to all duplicate SQL statements. + for (stmt in sql) { + if (sql[stmt].keys.length > 1) { + duplicate += sql[stmt].keys.length; + + for (i = 0; i < sql[stmt].keys.length; i++) { + this.$list.$el.find('.' + csscls('list-item')).eq(sql[stmt].keys[i]) + .addClass(csscls('sql-duplicate')) + .addClass(csscls('sql-duplicate-'+duplicate)); + } + } + } + + var t = $('').text(data.nb_statements + " statements were executed").appendTo(this.$status); + if (data.nb_failed_statements) { + t.append(", " + data.nb_failed_statements + " of which failed"); + } + if (duplicate) { + t.append(", " + duplicate + " of which were duplicated"); + t.append(", " + (data.nb_statements - duplicate) + " unique"); + } + if (data.accumulated_duration_str) { + this.$status.append($('').addClass(csscls('duration')).text(data.accumulated_duration_str)); + } + if (data.memory_usage_str) { + this.$status.append($('').addClass(csscls('memory')).text(data.memory_usage_str)); + } + }); + } + + }); + +})(PhpDebugBar.$); From c89a3ad9a7b48e8f83b20cc67a781f7a70e1a237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Nikolaou?= Date: Sat, 18 Feb 2017 21:19:28 +0200 Subject: [PATCH 008/585] Register LaravelSQLQueriesWidget to the JavascriptRenderer --- src/JavascriptRenderer.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/JavascriptRenderer.php b/src/JavascriptRenderer.php index cd14ec7f2..fa19402f0 100644 --- a/src/JavascriptRenderer.php +++ b/src/JavascriptRenderer.php @@ -19,6 +19,7 @@ public function __construct(DebugBar $debugBar, $baseUrl = null, $basePath = nul $this->cssFiles['laravel'] = __DIR__ . '/Resources/laravel-debugbar.css'; $this->cssVendors['fontawesome'] = __DIR__ . '/Resources/vendor/font-awesome/style.css'; + $this->jsFiles['laravel-sql'] = __DIR__ . '/Resources/sqlqueries/widget.js'; } /** From dec5b871316b8e5183a08239d6251b163c4f5456 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Fri, 3 Mar 2017 09:27:22 +0100 Subject: [PATCH 009/585] Enable more collectors + backtrace --- config/debugbar.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/config/debugbar.php b/config/debugbar.php index 5ed7ad9c0..d90e13e9f 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -92,17 +92,17 @@ 'db' => true, // Show database (PDO) queries and bindings 'views' => true, // Views with their data 'route' => true, // Current route information + 'auth' => true, // Display Laravel authentication status + 'gate' => true, // Display Laravel Gate checks + 'session' => true, // Display session data + 'symfony_request' => true, // Only one can be enabled.. + 'mail' => true, // Catch mail messages 'laravel' => false, // Laravel version and environment 'events' => false, // All events fired 'default_request' => false, // Regular or special Symfony request logger - 'symfony_request' => true, // Only one can be enabled.. - 'mail' => true, // Catch mail messages 'logs' => false, // Add the latest log messages 'files' => false, // Show the included files 'config' => false, // Display config settings - 'auth' => false, // Display Laravel authentication status - 'gate' => false, // Display Laravel Gate checks - 'session' => true, // Display session data ], /* @@ -116,13 +116,13 @@ 'options' => [ 'auth' => [ - 'show_name' => false, // Also show the users name/email in the debugbar + 'show_name' => true, // Also show the users name/email in the debugbar ], 'db' => [ 'with_params' => true, // Render SQL with the parameters substituted + 'backtrace' => true, // Use a backtrace to find the origin of the query in your files. 'timeline' => false, // Add the queries to the timeline - 'backtrace' => false, // EXPERIMENTAL: Use a backtrace to find the origin of the query in your files. - 'explain' => [ // EXPERIMENTAL: Show EXPLAIN output on queries + 'explain' => [ // Show EXPLAIN output on queries 'enabled' => false, 'types' => ['SELECT'], // ['SELECT', 'INSERT', 'UPDATE', 'DELETE']; for MySQL 5.6.3+ ], From e1ee861ebaab6537dbe295a13deba55f6bb677ec Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Fri, 3 Mar 2017 09:36:40 +0100 Subject: [PATCH 010/585] Explicityly check for laravel/framework and debugbar To avoid missing 3rd party package traces --- src/DataCollector/QueryCollector.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index ec1baff9b..6f6d7df67 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -201,7 +201,7 @@ protected function findSource() * * @param int $index * @param array $trace - * @return array|bool + * @return object|bool */ protected function parseTrace($index, array $trace) { @@ -220,7 +220,10 @@ protected function parseTrace($index, array $trace) if (isset($trace['class']) && isset($trace['file']) && strpos( $trace['file'], - DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR + DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'laravel' . DIRECTORY_SEPARATOR . 'framework' + ) === false && strpos( + $trace['file'], + DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'barryvdh' . DIRECTORY_SEPARATOR . 'laravel-debugbar' ) === false ) { $file = $trace['file']; From 99351a09bfcdcd491ee5c79e7b3e0350dd94d8a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E9=94=AE=E6=A0=8B?= Date: Wed, 8 Mar 2017 16:57:38 +0800 Subject: [PATCH 011/585] Fix get the route-middleware in lumen --- src/LaravelDebugbar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index b44c52617..5cf9e3dc2 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -294,7 +294,7 @@ function ($level, $message = null, $context = null) use ($logger) { } if ($this->app['config']->get('debugbar.options.db.backtrace')) { - $middleware = $this->app['router']->getMiddleware(); + $middleware = ! $this->is_lumen ? $this->app['router']->getMiddleware() : []; $queryCollector->setFindSource(true, $middleware); } From 75f749ba12d3120d21ad05ae9367a4b0234c08fa Mon Sep 17 00:00:00 2001 From: Adrian Z Date: Wed, 15 Mar 2017 12:07:11 +0100 Subject: [PATCH 012/585] Use addThrowable instead of deprecated addException --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 1c2338273..6fcd4b168 100644 --- a/readme.md +++ b/readme.md @@ -116,7 +116,7 @@ Or log exceptions: try { throw new Exception('foobar'); } catch (Exception $e) { - Debugbar::addException($e); + Debugbar::addThrowable($e); } ``` From c2bb740be0f366fd8524dd09593e570ecc66c463 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Fri, 31 Mar 2017 11:10:11 +0200 Subject: [PATCH 013/585] Log deprecated (symfony) errors optionally --- src/LaravelDebugbar.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 5cf9e3dc2..04b54404a 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -124,6 +124,11 @@ public function boot() /** @var Application $app */ $app = $this->app; + + // Set custom error handler + if ($app['config']->get('debugbar.error_handler' , false)) { + set_error_handler([$this, 'handleError']); + } $this->selectStorage($debugbar); @@ -449,6 +454,25 @@ public function shouldCollect($name, $default = false) return $this->app['config']->get('debugbar.collectors.' . $name, $default); } + /** + * Handle silenced errors + * + * @param $level + * @param $message + * @param string $file + * @param int $line + * @param array $context + * @throws \ErrorException + */ + public function handleError($level, $message, $file = '', $line = 0, $context = []) + { + if (error_reporting() & $level) { + throw new \ErrorException($message, 0, $level, $file, $line); + } else { + $this->addMessage($message, 'deprecation'); + } + } + /** * Starts a measure * From 6b2f13febca739d070ebcedd003c9d5ed46340ef Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Fri, 31 Mar 2017 11:11:51 +0200 Subject: [PATCH 014/585] Add option for error handler --- config/debugbar.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/config/debugbar.php b/config/debugbar.php index d90e13e9f..74a5e802a 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -62,6 +62,17 @@ 'capture_ajax' => true, + /* + |-------------------------------------------------------------------------- + | Custom Error Handler for Deprecated warnings + |-------------------------------------------------------------------------- + | + | When enabled, the Debugbar shows deprecated warnings for Symfony components + | in the Messages tab. + | + */ + 'error_handler' => false, + /* |-------------------------------------------------------------------------- | Clockwork integration From 41df91e4a21af95b519399c9aeef775b1f9f5c37 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Fri, 31 Mar 2017 11:20:51 +0200 Subject: [PATCH 015/585] Only add server timing on ajax, when enabled --- src/LaravelDebugbar.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 04b54404a..97d10439a 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -648,6 +648,11 @@ public function modifyResponse(Request $request, Response $response) ) { try { $this->sendDataInHeaders(true); + + if ($app['config']->get('debugbar.add_ajax_timing', false)) { + $this->addServerTimingHeaders($response); + } + } catch (\Exception $e) { $app['log']->error('Debugbar exception: ' . $e->getMessage()); } @@ -670,7 +675,7 @@ public function modifyResponse(Request $request, Response $response) } } - $this->addServerTimingHeaders($response); + return $response; } @@ -979,6 +984,7 @@ protected function addServerTimingHeaders(Response $response) if ($this->hasCollector('time')) { $collector = $this->getCollector('time'); + $headers = []; foreach ($collector->collect()['measures'] as $k => $m) { $headers[] = sprintf('%d=%F; "%s"', $k, $m['duration'], str_replace('"', "'", $m['label'])); } From 3d5653df7102d7b0ea78ab836ea4108f5220f900 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Fri, 31 Mar 2017 11:22:31 +0200 Subject: [PATCH 016/585] Update debugbar.php --- config/debugbar.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/debugbar.php b/config/debugbar.php index 74a5e802a..9c69f75c1 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -58,9 +58,11 @@ | The Debugbar can capture Ajax requests and display them. If you don't want this (ie. because of errors), | you can use this option to disable sending the data through the headers. | + | Optionally, you can also send ServerTiming headers on ajax requests for the Chrome DevTools. */ 'capture_ajax' => true, + 'add_ajax_timing' => false, /* |-------------------------------------------------------------------------- From dcc635088ae7e9dd7f4351bedc1de10a9dc2a093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Aur=C3=A9lio=20Deleu?= Date: Sun, 23 Apr 2017 15:21:01 +0200 Subject: [PATCH 017/585] Support Laravel 5.5 (#631) --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 412c61c45..f8207f3c0 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ ], "require": { "php": ">=5.5.9", - "illuminate/support": "5.1.*|5.2.*|5.3.*|5.4.*", + "illuminate/support": "5.1.*|5.2.*|5.3.*|5.4.*|5.5.*", "symfony/finder": "~2.7|~3.0", "maximebf/debugbar": "~1.13.0" }, From ef93304a31da33e0dd9906ef814d50972dab9ca3 Mon Sep 17 00:00:00 2001 From: Seita Moriyama Date: Mon, 15 May 2017 18:45:19 +0900 Subject: [PATCH 018/585] Validate $recaller in MultiAuthCollector.php (#633) --- src/DataCollector/MultiAuthCollector.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/DataCollector/MultiAuthCollector.php b/src/DataCollector/MultiAuthCollector.php index 8c1be24d6..15a9da821 100644 --- a/src/DataCollector/MultiAuthCollector.php +++ b/src/DataCollector/MultiAuthCollector.php @@ -1,8 +1,9 @@ getRequest()->cookies->get($guard->getRecallerName()) : null; + $recaller = $guard instanceof SessionGuard + ? new Recaller($guard->getRequest()->cookies->get($guard->getRecallerName())) + : null; - if($usingSession && !is_null($recaller)) { - list($id, $token) = explode('|', $recaller); - return $guard->getProvider()->retrieveByToken($id, $token); + if (!is_null($recaller) && $recaller->valid()) { + return $guard->getProvider()->retrieveByToken($recaller->id(), $recaller->token()); } else { return $guard->user(); } } - + /** * @{inheritDoc} */ From d120febf55726dc1542b260dff20824ed8ffbe5c Mon Sep 17 00:00:00 2001 From: Brian Faust Date: Mon, 15 May 2017 12:46:09 +0300 Subject: [PATCH 019/585] Enable/Disable via environment (#629) --- config/debugbar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/debugbar.php b/config/debugbar.php index 9c69f75c1..9ec387275 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -12,7 +12,7 @@ | */ - 'enabled' => null, + 'enabled' => env('DEBUGBAR_ENABLED', false), /* |-------------------------------------------------------------------------- From 82be3f021b7feaac709c0b912dd62b44667ceaaa Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Mon, 15 May 2017 11:46:30 +0200 Subject: [PATCH 020/585] Update debugbar.php --- config/debugbar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/debugbar.php b/config/debugbar.php index 9ec387275..fbe0c4add 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -12,7 +12,7 @@ | */ - 'enabled' => env('DEBUGBAR_ENABLED', false), + 'enabled' => env('DEBUGBAR_ENABLED', null), /* |-------------------------------------------------------------------------- From 7a47a10120939d58aa7a16d218e87999066661de Mon Sep 17 00:00:00 2001 From: Seita Moriyama Date: Wed, 24 May 2017 17:25:34 +0900 Subject: [PATCH 021/585] Validate $recaller in MultiAuthCollector.php (#638) Fixed #633 . Validate $recaller in MultiAuthCollector.php without the Recaller class. I modified it to work the same as when using the Recaller class. --- src/DataCollector/MultiAuthCollector.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/DataCollector/MultiAuthCollector.php b/src/DataCollector/MultiAuthCollector.php index 15a9da821..f8ed20012 100644 --- a/src/DataCollector/MultiAuthCollector.php +++ b/src/DataCollector/MultiAuthCollector.php @@ -1,9 +1,10 @@ getRequest()->cookies->get($guard->getRecallerName())) + ? $guard->getRequest()->cookies->get($guard->getRecallerName()) : null; - if (!is_null($recaller) && $recaller->valid()) { - return $guard->getProvider()->retrieveByToken($recaller->id(), $recaller->token()); - } else { - return $guard->user(); + if (is_string($recaller) && Str::contains($recaller, '|')) { + $segments = explode('|', $recaller); + if (count($segments) == 2 && trim($segments[0]) !== '' && trim($segments[1]) !== '') { + return $guard->getProvider()->retrieveByToken($segments[0], $segments[1]); + } } + return $guard->user(); } /** From 209974a1cfe6fdf2e6b6abf6e1a1dc4fad2ea90e Mon Sep 17 00:00:00 2001 From: Bogdan Padalko Date: Mon, 29 May 2017 09:56:55 +0300 Subject: [PATCH 022/585] Add route domain config option (#623) --- config/debugbar.php | 9 +++++++++ src/ServiceProvider.php | 1 + 2 files changed, 10 insertions(+) diff --git a/config/debugbar.php b/config/debugbar.php index fbe0c4add..0fb450fd8 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -180,4 +180,13 @@ */ 'route_prefix' => '_debugbar', + /* + |-------------------------------------------------------------------------- + | DebugBar route domain + |-------------------------------------------------------------------------- + | + | By default DebugBar route served from the same domain that request served. + | To override default domain, specify it as a non-empty value. + */ + 'route_domain' => null, ]; diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index d7b0cfa32..0fabc7e3e 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -77,6 +77,7 @@ public function boot() $routeConfig = [ 'namespace' => 'Barryvdh\Debugbar\Controllers', 'prefix' => $this->app['config']->get('debugbar.route_prefix'), + 'domain' => $this->app['config']->get('debugbar.route_domain'), ]; $this->getRouter()->group($routeConfig, function($router) { From ebccdacdf26e3a8f43ff9122fe616eaca73f3be2 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Wed, 31 May 2017 22:14:07 +0200 Subject: [PATCH 023/585] Export gate arguments to save time --- src/DataCollector/GateCollector.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/DataCollector/GateCollector.php b/src/DataCollector/GateCollector.php index 1c343cf72..8ed4bb591 100644 --- a/src/DataCollector/GateCollector.php +++ b/src/DataCollector/GateCollector.php @@ -5,18 +5,22 @@ use DebugBar\DataCollector\MessagesCollector; use Illuminate\Contracts\Auth\Access\Gate; use Illuminate\Contracts\Auth\Authenticatable; +use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter; /** * Collector for Laravel's Auth provider */ class GateCollector extends MessagesCollector { + /** @var ValueExporter */ + protected $exporter; /** * @param Gate $gate */ public function __construct(Gate $gate) { parent::__construct('gate'); + $this->exporter = new ValueExporter(); if (method_exists($gate, 'after')) { $gate->after([$this, 'addCheck']); @@ -31,7 +35,7 @@ public function addCheck(Authenticatable $user, $ability, $result, $arguments = 'ability' => $ability, 'result' => $result, 'user' => $user->getAuthIdentifier(), - 'arguments' => $arguments, + 'arguments' => $this->exporter->exportValue($arguments), ], $label, false); } } From 0b24dbc16752bc904b9ba7ec895a2d12b534c5ac Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 1 Jun 2017 11:54:55 -0500 Subject: [PATCH 024/585] Support Auto-Discovery (#643) This will make the package work with auto-discovery in Laravel 5.5. Enjoy :) --- composer.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/composer.json b/composer.json index f8207f3c0..0b968898a 100644 --- a/composer.json +++ b/composer.json @@ -26,6 +26,14 @@ "extra": { "branch-alias": { "dev-master": "2.3-dev" + }, + "laravel": { + "providers": [ + "Barryvdh\\Debugbar\\ServiceProvider" + ], + "aliases": { + "Debugbar": "Barryvdh\\Debugbar\\Facade" + } } } } From de15d00a74696db62e1b4782474c27ed0c4fc763 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 1 Jun 2017 19:46:08 +0200 Subject: [PATCH 025/585] Bump to 2.4 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 0b968898a..f1873b49a 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ }, "extra": { "branch-alias": { - "dev-master": "2.3-dev" + "dev-master": "2.4-dev" }, "laravel": { "providers": [ From 1878ff7d12a54464b62a182c545ddeb985ab26ae Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 1 Jun 2017 19:48:56 +0200 Subject: [PATCH 026/585] Update readme.md --- readme.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/readme.md b/readme.md index 6fcd4b168..9a5995409 100644 --- a/readme.md +++ b/readme.md @@ -48,6 +48,9 @@ composer require barryvdh/laravel-debugbar ``` After updating composer, add the ServiceProvider to the providers array in config/app.php + +> Laravel 5.5 uses Package Auto-Discovery, so doesn't require you to manually add the ServiceProvider + > If you use a catch-all/fallback route, make sure you load the Debugbar ServiceProvider before your own App ServiceProviders. ### Laravel 5.x: From 17afbae62e47696443904413644d2f5ad2eafcd5 Mon Sep 17 00:00:00 2001 From: Tyler Date: Thu, 8 Jun 2017 04:00:45 -0500 Subject: [PATCH 027/585] Fixed 506c1eb for Laravel 5.1 Support (#648) --- src/LaravelDebugbar.php | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 97d10439a..cfe849ab6 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -348,21 +348,45 @@ function ($query, $bindings = null, $time = null, $connectionName = null) use ($ \Illuminate\Database\Events\TransactionBeginning::class, 'connection.*.beganTransaction', ], function ($transaction) use ($queryCollector) { - $queryCollector->collectTransactionEvent('Begin Transaction', $transaction->connection); + + // Laravel 5.2 changed the way some core events worked. We must account for + // the first argument being an "event object", where arguments are passed + // via object properties, instead of individual arguments. + if($transaction instanceof \Illuminate\Database\Events\TransactionBeginning) { + $connection = $transaction->connection; + } else { + $connection = $transaction; + } + + $queryCollector->collectTransactionEvent('Begin Transaction', $connection); }); $db->getEventDispatcher()->listen([ \Illuminate\Database\Events\TransactionCommitted::class, 'connection.*.committed', ], function ($transaction) use ($queryCollector) { - $queryCollector->collectTransactionEvent('Commit Transaction', $transaction->connection); + + if($transaction instanceof \Illuminate\Database\Events\TransactionCommitted) { + $connection = $transaction->connection; + } else { + $connection = $transaction; + } + + $queryCollector->collectTransactionEvent('Commit Transaction', $connection); }); $db->getEventDispatcher()->listen([ \Illuminate\Database\Events\TransactionRolledBack::class, 'connection.*.rollingBack', ], function ($transaction) use ($queryCollector) { - $queryCollector->collectTransactionEvent('Rollback Transaction', $transaction->connection); + + if($transaction instanceof \Illuminate\Database\Events\TransactionRolledBack) { + $connection = $transaction->connection; + } else { + $connection = $transaction; + } + + $queryCollector->collectTransactionEvent('Rollback Transaction', $connection); }); } catch (\Exception $e) { $this->addThrowable( From af98b3a4ccac9364f2145fae974ff3392ec402b1 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Wed, 14 Jun 2017 09:44:44 +0200 Subject: [PATCH 028/585] Don't inject in 'false' content (eg. streamed etc) --- src/LaravelDebugbar.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index cfe849ab6..1295abdc9 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -684,6 +684,7 @@ public function modifyResponse(Request $request, Response $response) ($response->headers->has('Content-Type') && strpos($response->headers->get('Content-Type'), 'html') === false) || $request->getRequestFormat() !== 'html' + || $response->getContent() === false ) { try { // Just collect + store data, don't inject it. From 3723bd12326f25c21e059706de85ffbc220d93a7 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Tue, 11 Jul 2017 14:52:57 +0200 Subject: [PATCH 029/585] Catch resolving guards --- src/DataCollector/MultiAuthCollector.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/DataCollector/MultiAuthCollector.php b/src/DataCollector/MultiAuthCollector.php index f8ed20012..c25d7ba10 100644 --- a/src/DataCollector/MultiAuthCollector.php +++ b/src/DataCollector/MultiAuthCollector.php @@ -34,7 +34,11 @@ public function collect() $names = ''; foreach($this->guards as $guardName) { - $user = $this->resolveUser($this->auth->guard($guardName)); + try { + $user = $this->resolveUser($this->auth->guard($guardName)); + } catch (\Exception $e) { + continue; + } $data['guards'][$guardName] = $this->getUserInformation($user); From 8d49f9582e29ed0b85997dbbfe8f561f2ea831cf Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Fri, 21 Jul 2017 14:13:24 +0200 Subject: [PATCH 030/585] Update for v3, l5.5 --- composer.json | 13 ++++++++----- readme.md | 13 ++++++++----- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/composer.json b/composer.json index f1873b49a..a3afeedcb 100644 --- a/composer.json +++ b/composer.json @@ -10,10 +10,11 @@ } ], "require": { - "php": ">=5.5.9", - "illuminate/support": "5.1.*|5.2.*|5.3.*|5.4.*|5.5.*", - "symfony/finder": "~2.7|~3.0", - "maximebf/debugbar": "~1.13.0" + "php": ">=7.0", + "maximebf/debugbar": "~1.14.0", + "illuminate/support": "5.5.*", + "symfony/debug": "~3.3", + "symfony/finder": "~3.3" }, "autoload": { "psr-4": { @@ -23,9 +24,11 @@ "src/helpers.php" ] }, + "minimum-stability": "dev", + "prefer-stable": true, "extra": { "branch-alias": { - "dev-master": "2.4-dev" + "dev-master": "3.0-dev" }, "laravel": { "providers": [ diff --git a/readme.md b/readme.md index 9a5995409..33c4f1bc4 100644 --- a/readme.md +++ b/readme.md @@ -3,7 +3,7 @@ [![Latest Stable Version](https://poser.pugx.org/barryvdh/laravel-debugbar/version.png)](https://packagist.org/packages/barryvdh/laravel-debugbar) [![Total Downloads](https://poser.pugx.org/barryvdh/laravel-debugbar/d/total.png)](https://packagist.org/packages/barryvdh/laravel-debugbar) -### For Laravel 4, please use the [1.8 branch](https://github.com/barryvdh/laravel-debugbar/tree/1.8)! +### For Laravel < 5.5, please use the [2.4 branch](https://github.com/barryvdh/laravel-debugbar/tree/2.4)! This is a package to integrate [PHP Debug Bar](http://phpdebugbar.com/) with Laravel 5. It includes a ServiceProvider to register the debugbar and attach it to the output. You can publish assets and configure it through Laravel. @@ -47,13 +47,16 @@ Require this package with composer: composer require barryvdh/laravel-debugbar ``` -After updating composer, add the ServiceProvider to the providers array in config/app.php +Laravel 5.5 uses Package Auto-Discovery, so doesn't require you to manually add the ServiceProvider. + +The Debugbar will be enabled when `APP_DEBUG` is `true`. -> Laravel 5.5 uses Package Auto-Discovery, so doesn't require you to manually add the ServiceProvider > If you use a catch-all/fallback route, make sure you load the Debugbar ServiceProvider before your own App ServiceProviders. -### Laravel 5.x: +### Laravel 5: + +If you don't use auto-discovery, add the ServiceProvider to the providers array in config/app.php ```php Barryvdh\Debugbar\ServiceProvider::class, @@ -65,7 +68,7 @@ If you want to use the facade to log messages, add this to your facades in app.p 'Debugbar' => Barryvdh\Debugbar\Facade::class, ``` -The profiler is enabled by default, if you have app.debug=true. You can override that in the config (`debugbar.enabled`). See more options in `config/debugbar.php` +The profiler is enabled by default, if you have APP_DEBUG=true. You can override that in the config (`debugbar.enabled`) or by setting `DEBUGBAR_ENABLED` in your `.env`. See more options in `config/debugbar.php` You can also set in your config if you want to include/exclude the vendor files also (FontAwesome, Highlight.js and jQuery). If you already use them in your site, set it to false. You can also only display the js or css vendors, by setting it to 'js' or 'css'. (Highlight.js requires both css + js, so set to `true` for syntax highlighting) From 70831dee21009e969ce651adbfd818ba21438093 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Fri, 21 Jul 2017 14:43:26 +0200 Subject: [PATCH 031/585] Update for 5.5, remove old checks --- composer.json | 8 +- config/debugbar.php | 2 +- src/Console/PublishCommand.php | 39 ------ src/DataCollector/AuthCollector.php | 115 ------------------ src/DataCollector/EventCollector.php | 8 -- src/DataCollector/GateCollector.php | 4 +- src/DataCollector/MultiAuthCollector.php | 65 +++++++++- src/Facade.php | 2 +- src/LaravelDebugbar.php | 9 +- src/LumenServiceProvider.php | 8 -- .../{Debugbar.php => InjectDebugbar.php} | 2 +- src/ServiceProvider.php | 39 +++--- src/SymfonyHttpDriver.php | 13 +- src/helpers.php | 12 +- 14 files changed, 99 insertions(+), 227 deletions(-) delete mode 100644 src/Console/PublishCommand.php delete mode 100644 src/DataCollector/AuthCollector.php rename src/Middleware/{Debugbar.php => InjectDebugbar.php} (99%) diff --git a/composer.json b/composer.json index a3afeedcb..da045a8ad 100644 --- a/composer.json +++ b/composer.json @@ -12,9 +12,11 @@ "require": { "php": ">=7.0", "maximebf/debugbar": "~1.14.0", - "illuminate/support": "5.5.*", - "symfony/debug": "~3.3", - "symfony/finder": "~3.3" + "illuminate/support": "5.5.x", + "illuminate/session": "5.5.x", + "symfony/debug": "^3.3", + "symfony/finder": "^3.3", + "illuminate/routing": "5.5.x" }, "autoload": { "psr-4": { diff --git a/config/debugbar.php b/config/debugbar.php index 0fb450fd8..caaa646b9 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -12,7 +12,7 @@ | */ - 'enabled' => env('DEBUGBAR_ENABLED', null), + 'enabled' => env('DEBUGBAR_ENABLED', env('APP_DEBUG', false)), /* |-------------------------------------------------------------------------- diff --git a/src/Console/PublishCommand.php b/src/Console/PublishCommand.php deleted file mode 100644 index 84479d156..000000000 --- a/src/Console/PublishCommand.php +++ /dev/null @@ -1,39 +0,0 @@ - - * @deprecated No longer needed because of the AssetController - */ -class PublishCommand extends Command -{ - /** - * The console command name. - * - * @var string - */ - protected $name = 'debugbar:publish'; - - /** - * The console command description. - * - * @var string - */ - protected $description = 'Publish the Debugbar assets'; - - /** - * Execute the console command. - * - * @return void - */ - public function fire() - { - $this->info( - 'NOTICE: Since laravel-debugbar 1.7.x, publishing assets is no longer necessary. The assets in public/packages/barryvdh/laravel-debugbar and maximebf/php-debugbar can be safely removed.' - ); - } -} diff --git a/src/DataCollector/AuthCollector.php b/src/DataCollector/AuthCollector.php deleted file mode 100644 index 9f98160eb..000000000 --- a/src/DataCollector/AuthCollector.php +++ /dev/null @@ -1,115 +0,0 @@ -auth = $auth; - } - - /** - * Set to show the users name/email - * @param bool $showName - */ - public function setShowName($showName) - { - $this->showName = (bool) $showName; - } - - /** - * @{inheritDoc} - */ - public function collect() - { - try { - $user = $this->auth->user(); - } catch (\Exception $e) { - $user = null; - } - return $this->getUserInformation($user); - } - - /** - * Get displayed user information - * @param \Illuminate\Auth\UserInterface $user - * @return array - */ - protected function getUserInformation($user = null) - { - // Defaults - if (is_null($user)) { - return [ - 'name' => 'Guest', - 'user' => ['guest' => true], - ]; - } - - // The default auth identifer is the ID number, which isn't all that - // useful. Try username and email. - $identifier = $user->getAuthIdentifier(); - if (is_numeric($identifier)) { - try { - if ($user->username) { - $identifier = $user->username; - } elseif ($user->email) { - $identifier = $user->email; - } - } catch (\Exception $e) { - } - } - - return [ - 'name' => $identifier, - 'user' => $user instanceof Arrayable ? $user->toArray() : $user, - ]; - } - - /** - * @{inheritDoc} - */ - public function getName() - { - return 'auth'; - } - - /** - * @{inheritDoc} - */ - public function getWidgets() - { - $widgets = [ - 'auth' => [ - 'icon' => 'lock', - 'widget' => 'PhpDebugBar.Widgets.VariableListWidget', - 'map' => 'auth.user', - 'default' => '{}' - ] - ]; - if ($this->showName) { - $widgets['auth.name'] = [ - 'icon' => 'user', - 'tooltip' => 'Auth status', - 'map' => 'auth.name', - 'default' => '', - ]; - } - return $widgets; - } -} diff --git a/src/DataCollector/EventCollector.php b/src/DataCollector/EventCollector.php index 75326b6d0..757cd7ffb 100644 --- a/src/DataCollector/EventCollector.php +++ b/src/DataCollector/EventCollector.php @@ -23,14 +23,6 @@ public function __construct($requestStartTime = null) public function onWildcardEvent($name = null, $data = []) { - // Pre-Laravel 5.4, using 'firing' to get the current event name. - if (method_exists($this->events, 'firing')) { - $name = $this->events->firing(); - - // Get the arguments passed to the event - $data = func_get_args(); - } - $params = $this->prepareParams($data); $time = microtime(true); diff --git a/src/DataCollector/GateCollector.php b/src/DataCollector/GateCollector.php index 8ed4bb591..529b8be98 100644 --- a/src/DataCollector/GateCollector.php +++ b/src/DataCollector/GateCollector.php @@ -22,9 +22,7 @@ public function __construct(Gate $gate) parent::__construct('gate'); $this->exporter = new ValueExporter(); - if (method_exists($gate, 'after')) { - $gate->after([$this, 'addCheck']); - } + $gate->after([$this, 'addCheck']); } public function addCheck(Authenticatable $user, $ability, $result, $arguments = []) diff --git a/src/DataCollector/MultiAuthCollector.php b/src/DataCollector/MultiAuthCollector.php index c25d7ba10..59722c763 100644 --- a/src/DataCollector/MultiAuthCollector.php +++ b/src/DataCollector/MultiAuthCollector.php @@ -2,28 +2,45 @@ namespace Barryvdh\Debugbar\DataCollector; +use DebugBar\DataCollector\DataCollector; +use DebugBar\DataCollector\Renderable; use Illuminate\Auth\SessionGuard; use Illuminate\Contracts\Auth\Guard; use Illuminate\Support\Str; +use Illuminate\Contracts\Support\Arrayable; + /** * Collector for Laravel's Auth provider */ -class MultiAuthCollector extends AuthCollector +class MultiAuthCollector extends DataCollector implements Renderable { /** @var array $guards */ protected $guards; + /** @var \Illuminate\Auth\AuthManager */ + protected $auth; + /** @var bool */ + protected $showName = false; + /** * @param \Illuminate\Auth\AuthManager $auth * @param array $guards */ public function __construct($auth, $guards) { - parent::__construct($auth); + $this->auth = $auth; $this->guards = $guards; } + /** + * Set to show the users name/email + * @param bool $showName + */ + public function setShowName($showName) + { + $this->showName = (bool) $showName; + } /** * @{inheritDoc} @@ -77,6 +94,49 @@ private function resolveUser(Guard $guard) return $guard->user(); } + /** + * Get displayed user information + * @param \Illuminate\Auth\UserInterface $user + * @return array + */ + protected function getUserInformation($user = null) + { + // Defaults + if (is_null($user)) { + return [ + 'name' => 'Guest', + 'user' => ['guest' => true], + ]; + } + + // The default auth identifer is the ID number, which isn't all that + // useful. Try username and email. + $identifier = $user->getAuthIdentifier(); + if (is_numeric($identifier)) { + try { + if ($user->username) { + $identifier = $user->username; + } elseif ($user->email) { + $identifier = $user->email; + } + } catch (\Exception $e) { + } + } + + return [ + 'name' => $identifier, + 'user' => $user instanceof Arrayable ? $user->toArray() : $user, + ]; + } + + /** + * @{inheritDoc} + */ + public function getName() + { + return 'auth'; + } + /** * @{inheritDoc} */ @@ -102,4 +162,5 @@ public function getWidgets() return $widgets; } + } diff --git a/src/Facade.php b/src/Facade.php index 678a2b3f4..d2bbcf5eb 100644 --- a/src/Facade.php +++ b/src/Facade.php @@ -7,6 +7,6 @@ class Facade extends \Illuminate\Support\Facades\Facade */ protected static function getFacadeAccessor() { - return 'debugbar'; + return LaravelDebugbar::class; } } diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 1295abdc9..1cdabc78c 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -436,13 +436,8 @@ function ($query, $bindings = null, $time = null, $connectionName = null) use ($ if ($this->shouldCollect('auth', false)) { try { - if($this->checkVersion('5.2')) { - // fix for compatibility with Laravel 5.2.* - $guards = array_keys($this->app['config']->get('auth.guards')); - $authCollector = new MultiAuthCollector($app['auth'], $guards); - } else { - $authCollector = new AuthCollector($app['auth']); - } + $guards = array_keys($this->app['config']->get('auth.guards')); + $authCollector = new MultiAuthCollector($app['auth'], $guards); $authCollector->setShowName( $this->app['config']->get('debugbar.options.auth.show_name') diff --git a/src/LumenServiceProvider.php b/src/LumenServiceProvider.php index c0e0c0596..d28a4d2b7 100644 --- a/src/LumenServiceProvider.php +++ b/src/LumenServiceProvider.php @@ -37,14 +37,6 @@ protected function registerMiddleware($middleware) $this->app->middleware([$middleware]); } - /** - * Check the App Debug status - */ - protected function checkAppDebug() - { - return env('APP_DEBUG'); - } - /** * Get the services provided by the provider. * diff --git a/src/Middleware/Debugbar.php b/src/Middleware/InjectDebugbar.php similarity index 99% rename from src/Middleware/Debugbar.php rename to src/Middleware/InjectDebugbar.php index edbc12779..5c3a8efff 100644 --- a/src/Middleware/Debugbar.php +++ b/src/Middleware/InjectDebugbar.php @@ -9,7 +9,7 @@ use Illuminate\Contracts\Debug\ExceptionHandler; use Symfony\Component\Debug\Exception\FatalThrowableError; -class Debugbar +class InjectDebugbar { /** * The App container diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 0fabc7e3e..185461c58 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -1,5 +1,9 @@ mergeConfigFrom($configPath, 'debugbar'); $this->app->alias( - 'DebugBar\DataFormatter\DataFormatter', - 'DebugBar\DataFormatter\DataFormatterInterface' + DataFormatter::class, + DataFormatterInterface::class ); - $this->app->singleton('debugbar', function ($app) { - $debugbar = new LaravelDebugbar($app); + $this->app->singleton(LaravelDebugbar::class, function () { + $debugbar = new LaravelDebugbar($this->app); - if ($app->bound(SessionManager::class)) { - $sessionManager = $app->make(SessionManager::class); + if ($this->app->bound(SessionManager::class)) { + $sessionManager = $this->app->make(SessionManager::class); $httpDriver = new SymfonyHttpDriver($sessionManager); $debugbar->setHttpDriver($httpDriver); } @@ -40,7 +44,7 @@ public function register() } ); - $this->app->alias('debugbar', 'Barryvdh\Debugbar\LaravelDebugbar'); + $this->app->alias(LaravelDebugbar::class, 'debugbar'); $this->app->singleton('command.debugbar.clear', function ($app) { @@ -63,12 +67,7 @@ public function boot() $configPath = __DIR__ . '/../config/debugbar.php'; $this->publishes([$configPath => $this->getConfigPath()], 'config'); - // If enabled is null, set from the app.debug value - $enabled = $this->app['config']->get('debugbar.enabled'); - - if (is_null($enabled)) { - $enabled = $this->checkAppDebug(); - } + $enabled = (bool) $this->app['config']->get('debugbar.enabled'); if (! $enabled) { return; @@ -111,7 +110,7 @@ public function boot() $debugbar->enable(); $debugbar->boot(); - $this->registerMiddleware('Barryvdh\Debugbar\Middleware\Debugbar'); + $this->registerMiddleware(InjectDebugbar::class); } /** @@ -151,18 +150,10 @@ protected function publishConfig($configPath) */ protected function registerMiddleware($middleware) { - $kernel = $this->app['Illuminate\Contracts\Http\Kernel']; + $kernel = $this->app[Kernel::class]; $kernel->pushMiddleware($middleware); } - /** - * Check the App Debug status - */ - protected function checkAppDebug() - { - return $this->app['config']->get('app.debug'); - } - /** * Get the services provided by the provider. * @@ -170,6 +161,6 @@ protected function checkAppDebug() */ public function provides() { - return ['debugbar', 'command.debugbar.clear']; + return ['debugbar', 'command.debugbar.clear', DataFormatterInterface::class, LaravelDebugbar::class]; } } diff --git a/src/SymfonyHttpDriver.php b/src/SymfonyHttpDriver.php index f17619969..088a5ab9f 100644 --- a/src/SymfonyHttpDriver.php +++ b/src/SymfonyHttpDriver.php @@ -11,8 +11,9 @@ */ class SymfonyHttpDriver implements HttpDriverInterface { - /** @var \Symfony\Component\HttpFoundation\Session\Session|\Illuminate\Contracts\Session\Session|\Illuminate\Session\SessionManager */ + /** @var \Illuminate\Contracts\Session\Session|\Illuminate\Session\SessionManager */ protected $session; + /** @var \Symfony\Component\HttpFoundation\Response */ protected $response; @@ -40,6 +41,7 @@ public function isSessionStarted() if (!$this->session->isStarted()) { $this->session->start(); } + return $this->session->isStarted(); } @@ -48,14 +50,7 @@ public function isSessionStarted() */ public function setSessionValue($name, $value) { - // In Laravel 5.4 the session changed to use their own custom implementation - // instead of the one from Symfony. One of the changes was the set method - // that was changed to put. Here we check if we are using the new one. - if (method_exists($this->session, 'driver') && $this->session->driver() instanceof \Illuminate\Contracts\Session\Session) { - $this->session->put($name, $value); - return; - } - $this->session->set($name, $value); + $this->session->put($name, $value); } /** diff --git a/src/helpers.php b/src/helpers.php index 396360165..0cd00444f 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -8,7 +8,7 @@ */ function debugbar() { - return app('debugbar'); + return app(\Barryvdh\Debugbar\LaravelDebugbar::class); } } @@ -21,7 +21,7 @@ function debugbar() */ function debug($value) { - $debugbar = app('debugbar'); + $debugbar = debugbar(); foreach (func_get_args() as $value) { $debugbar->addMessage($value, 'debug'); } @@ -37,7 +37,7 @@ function debug($value) */ function start_measure($name, $label = null) { - app('debugbar')->startMeasure($name, $label); + debugbar()->startMeasure($name, $label); } } @@ -49,7 +49,7 @@ function start_measure($name, $label = null) */ function stop_measure($name) { - app('debugbar')->stopMeasure($name); + debugbar()->stopMeasure($name); } } @@ -63,7 +63,7 @@ function stop_measure($name) */ function add_measure($label, $start, $end) { - app('debugbar')->addMeasure($label, $start, $end); + debugbar()->addMeasure($label, $start, $end); } } @@ -76,6 +76,6 @@ function add_measure($label, $start, $end) */ function measure($label, \Closure $closure) { - app('debugbar')->measure($label, $closure); + debugbar()->measure($label, $closure); } } From e5257dc121cbd504928b4b5f0262b0b3e691bf42 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Fri, 21 Jul 2017 15:50:18 +0200 Subject: [PATCH 032/585] Update font awesome, use vardumper layout --- ...uestCollector.php => RequestCollector.php} | 2 +- ...eRouteCollector.php => RouteCollector.php} | 2 +- src/JavascriptRenderer.php | 15 +++++++++++ src/LaravelDebugbar.php | 26 ++++++++++++++++--- src/Resources/laravel-debugbar.css | 13 ++++++++-- src/Resources/vendor/font-awesome/style.css | 2 +- 6 files changed, 52 insertions(+), 8 deletions(-) rename src/DataCollector/{SymfonyRequestCollector.php => RequestCollector.php} (98%) rename src/DataCollector/{IlluminateRouteCollector.php => RouteCollector.php} (97%) diff --git a/src/DataCollector/SymfonyRequestCollector.php b/src/DataCollector/RequestCollector.php similarity index 98% rename from src/DataCollector/SymfonyRequestCollector.php rename to src/DataCollector/RequestCollector.php index 37fbcc4b9..25bada3c2 100644 --- a/src/DataCollector/SymfonyRequestCollector.php +++ b/src/DataCollector/RequestCollector.php @@ -12,7 +12,7 @@ * Based on \Symfony\Component\HttpKernel\DataCollector\RequestDataCollector by Fabien Potencier * */ -class SymfonyRequestCollector extends DataCollector implements DataCollectorInterface, Renderable +class RequestCollector extends DataCollector implements DataCollectorInterface, Renderable { /** @var \Symfony\Component\HttpFoundation\Request $request */ protected $request; diff --git a/src/DataCollector/IlluminateRouteCollector.php b/src/DataCollector/RouteCollector.php similarity index 97% rename from src/DataCollector/IlluminateRouteCollector.php rename to src/DataCollector/RouteCollector.php index c9db589da..6128e331d 100644 --- a/src/DataCollector/IlluminateRouteCollector.php +++ b/src/DataCollector/RouteCollector.php @@ -14,7 +14,7 @@ * https://github.com/laravel/framework/blob/master/src/Illuminate/Foundation/Console/RoutesCommand.php * */ -class IlluminateRouteCollector extends DataCollector implements Renderable +class RouteCollector extends DataCollector implements Renderable { /** * The router instance. diff --git a/src/JavascriptRenderer.php b/src/JavascriptRenderer.php index fa19402f0..9cdaf22f9 100644 --- a/src/JavascriptRenderer.php +++ b/src/JavascriptRenderer.php @@ -56,9 +56,24 @@ public function renderHead() $html .= '' . "\n"; } + $html .= $this->getInlineHtml(); + + return $html; } + protected function getInlineHtml() + { + $html = ''; + + foreach (['head', 'css', 'js'] as $asset) { + foreach ($this->getAssets('inline_' . $asset) as $item) { + $html .= $item . "\n"; + } + } + + return $html; + } /** * Get the last modified time of any assets. * diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 1cdabc78c..9987dcb49 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -9,13 +9,14 @@ use Barryvdh\Debugbar\DataCollector\MultiAuthCollector; use Barryvdh\Debugbar\DataCollector\QueryCollector; use Barryvdh\Debugbar\DataCollector\SessionCollector; -use Barryvdh\Debugbar\DataCollector\SymfonyRequestCollector; +use Barryvdh\Debugbar\DataCollector\RequestCollector; use Barryvdh\Debugbar\DataCollector\ViewCollector; use Barryvdh\Debugbar\Storage\FilesystemStorage; use DebugBar\Bridge\MonologCollector; use DebugBar\Bridge\SwiftMailer\SwiftLogCollector; use DebugBar\Bridge\SwiftMailer\SwiftMailCollector; use DebugBar\DataCollector\ConfigCollector; +use DebugBar\DataCollector\DataCollectorInterface; use DebugBar\DataCollector\ExceptionsCollector; use DebugBar\DataCollector\MemoryCollector; use DebugBar\DataCollector\MessagesCollector; @@ -222,7 +223,7 @@ function ($view, $data = []) use ($debugbar) { if (!$this->isLumen() && $this->shouldCollect('route')) { try { - $this->addCollector($this->app->make('Barryvdh\Debugbar\DataCollector\IlluminateRouteCollector')); + $this->addCollector($this->app->make('Barryvdh\Debugbar\DataCollector\RouteCollector')); } catch (\Exception $e) { $this->addThrowable( new Exception( @@ -473,6 +474,25 @@ public function shouldCollect($name, $default = false) return $this->app['config']->get('debugbar.collectors.' . $name, $default); } + /** + * Adds a data collector + * + * @param DataCollectorInterface $collector + * + * @throws DebugBarException + * @return $this + */ + public function addCollector(DataCollectorInterface $collector) + { + parent::addCollector($collector); + + if (method_exists($collector, 'useHtmlVarDumper')) { + $collector->useHtmlVarDumper(); + } + + return $this; + } + /** * Handle silenced errors * @@ -626,7 +646,7 @@ public function modifyResponse(Request $request, Response $response) if ($this->shouldCollect('symfony_request', true) && !$this->hasCollector('request')) { try { - $this->addCollector(new SymfonyRequestCollector($request, $response, $sessionManager)); + $this->addCollector(new RequestCollector($request, $response, $sessionManager)); } catch (\Exception $e) { $this->addThrowable( new Exception( diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index 7e93000f6..334ffaee5 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -18,14 +18,23 @@ a.phpdebugbar-restore-btn { border-right-color: #ddd !important; } -div.phpdebugbar code, div.phpdebugbar pre { +div.phpdebugbar code, div.phpdebugbar pre, div.phpdebugbar samp { background: none; - font-family: monospace; + font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; border: 0; padding: 0; } +div.phpdebugbar code, div.phpdebugbar pre { + color: #000; +} + +div.phpdebugbar pre.sf-dump { + color: #a0a000; + outline: 0; +} + div.phpdebugbar-body { border-top: none; } diff --git a/src/Resources/vendor/font-awesome/style.css b/src/Resources/vendor/font-awesome/style.css index 2078bb381..6fc6c340a 100644 --- a/src/Resources/vendor/font-awesome/style.css +++ b/src/Resources/vendor/font-awesome/style.css @@ -6,7 +6,7 @@ * -------------------------- */ @font-face { font-family: 'PhpDebugbarFontAwesome'; - src: url(data:application/font-woff;charset=utf-8;base64,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) format('woff'); + src: url(data:application/font-woff;charset=utf-8;base64,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) format('woff'); font-weight: normal; font-style: normal; } From 41b818f3fbf106e69e636ce4a4956a89e9391835 Mon Sep 17 00:00:00 2001 From: Adel Date: Mon, 24 Jul 2017 22:20:35 +0300 Subject: [PATCH 033/585] add cache tab (#678) * add cache tab. #634 * More universal cache events checking * cache tab: data formatter instead of serialize --- config/debugbar.php | 4 + src/DataCollector/CacheCollector.php | 114 +++++++++++++++++++++++++++ src/LaravelDebugbar.php | 20 +++++ 3 files changed, 138 insertions(+) create mode 100644 src/DataCollector/CacheCollector.php diff --git a/config/debugbar.php b/config/debugbar.php index caaa646b9..425a409dd 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -116,6 +116,7 @@ 'logs' => false, // Add the latest log messages 'files' => false, // Show the included files 'config' => false, // Display config settings + 'cache' => false, // Display cache events ], /* @@ -153,6 +154,9 @@ 'logs' => [ 'file' => null ], + 'cache' => [ + 'values' => true // collect cache values + ], ], /* diff --git a/src/DataCollector/CacheCollector.php b/src/DataCollector/CacheCollector.php new file mode 100644 index 000000000..86843710a --- /dev/null +++ b/src/DataCollector/CacheCollector.php @@ -0,0 +1,114 @@ + 'hit', + 'Illuminate\Cache\Events\CacheMissed' => 'missed', + 'Illuminate\Cache\Events\KeyWritten' => 'write', + 'Illuminate\Cache\Events\KeyForgotten' => 'delete', + ]; + + public function __construct($requestStartTime = null, $collectValues) + { + parent::__construct(); + + $this->collectValues = $collectValues; + } + + public function onClassEvent($event) + { + $class = get_class($event); + if (isset($this->classMap[$class])) { + $params = []; + + if(isset($event->minutes)) { + $params['minutes'] = $event->minutes; + } + + if(isset($event->value)) { + if ($this->collectValues) { + $params['value'] = $this->getDataFormatter()->formatVar($event->value); + } else { + $params['value'] = '(values collecting turned off)'; + } + } + + if(!empty($event->tags)) { + $params['tags'] = $event->tags; + } + + $time = microtime(true); + $this->addMeasure($this->classMap[$class] . ' ' . $event->key, $time, $time, $params); + } + } + + public function onStringEvent($event, $payload) + { + $params = []; + + if(is_array($payload)) { + if (isset($payload[2])) { + $params['minutes'] = $payload[2]; + } + + if (isset($payload[1])) { + if ($this->collectValues) { + $params['value'] = $this->getDataFormatter()->formatVar($payload[1]); + } else { + $params['value'] = '(values collecting turned off)'; + } + } + } + + $time = microtime(true); + $this->addMeasure( str_replace('cache.', '', $event) . ' ' . (is_array($payload) ? $payload[0] : $payload), + $time, $time, $params); + } + + public function subscribe(Dispatcher $events) + { + if (class_exists('Illuminate\Cache\Events\CacheHit')) { + $events->listen('Illuminate\Cache\Events\*', [$this, 'onClassEvent']); + } else { + $events->listen('cache.*', [$this, 'onStringEvent']); + } + } + + public function collect() + { + $data = parent::collect(); + $data['nb_measures'] = count($data['measures']); + + return $data; + } + + public function getName() + { + return 'cache'; + } + + public function getWidgets() + { + return [ + 'cache' => [ + 'icon' => 'lock', + 'widget' => 'PhpDebugBar.Widgets.TimelineWidget', + 'map' => 'cache', + 'default' => '{}', + ], + 'cache:badge' => [ + 'map' => 'cache.nb_measures', + 'default' => 0, + ], + ]; + } +} diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 9987dcb49..d820a8cf3 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -1,6 +1,7 @@ shouldCollect('cache', false) && isset($this->app['events'])) { + try { + $collectValues = $this->app['config']->get('debugbar.options.cache.values', true); + $startTime = $this->app['request']->server('REQUEST_TIME_FLOAT'); + $cacheCollector = new CacheCollector($startTime, $collectValues); + $this->addCollector($cacheCollector); + $this->app['events']->subscribe($cacheCollector); + + } catch (\Exception $e) { + $this->addThrowable( + new Exception( + 'Cannot add CacheCollector to Laravel Debugbar: ' . $e->getMessage(), + $e->getCode(), + $e + ) + ); + } + } + $renderer = $this->getJavascriptRenderer(); $renderer->setIncludeVendors($this->app['config']->get('debugbar.include_vendors', true)); $renderer->setBindAjaxHandlerToXHR($app['config']->get('debugbar.capture_ajax', true)); From edf7236031ed83464ad6fbbad10a14fe74db9d49 Mon Sep 17 00:00:00 2001 From: Victor Isadov Date: Tue, 25 Jul 2017 11:23:19 +0300 Subject: [PATCH 034/585] Update readme.md (#679) --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index 33c4f1bc4..b41dcbe3b 100644 --- a/readme.md +++ b/readme.md @@ -25,6 +25,7 @@ This package includes some custom collectors: - LogsCollector: Show the latest log entries from the storage logs. (disabled by default) - FilesCollector: Show the files that are included/required by PHP. (disabled by default) - ConfigCollector: Display the values from the config files. (disabled by default) + - CacheCollector: Display all cache events. (disabled by default) Bootstraps the following collectors for Laravel: - LogCollector: Show all Log messages From bc8e142fe28d9dfcbb47884dd75e7e9bf0ac1dc8 Mon Sep 17 00:00:00 2001 From: Adel Date: Tue, 25 Jul 2017 14:34:05 +0300 Subject: [PATCH 035/585] fix cache tab, support laravel>=5.4 event system (#680) --- src/DataCollector/CacheCollector.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/DataCollector/CacheCollector.php b/src/DataCollector/CacheCollector.php index 86843710a..87fddd6c5 100644 --- a/src/DataCollector/CacheCollector.php +++ b/src/DataCollector/CacheCollector.php @@ -24,8 +24,16 @@ public function __construct($requestStartTime = null, $collectValues) $this->collectValues = $collectValues; } - public function onClassEvent($event) + public function onClassEvent($name, $event = null) { + if(is_object($name)) { + $event = $name; + } + + if(is_array($event)) { + $event = $event[0]; + } + $class = get_class($event); if (isset($this->classMap[$class])) { $params = []; From 2c78cc44c4017deba7d798e0a3f10c2ca617cfad Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Tue, 25 Jul 2017 14:08:40 +0200 Subject: [PATCH 036/585] Tweak cache collector --- src/DataCollector/CacheCollector.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DataCollector/CacheCollector.php b/src/DataCollector/CacheCollector.php index 87fddd6c5..13bfc1aed 100644 --- a/src/DataCollector/CacheCollector.php +++ b/src/DataCollector/CacheCollector.php @@ -108,14 +108,14 @@ public function getWidgets() { return [ 'cache' => [ - 'icon' => 'lock', + 'icon' => 'clipboard', 'widget' => 'PhpDebugBar.Widgets.TimelineWidget', 'map' => 'cache', 'default' => '{}', ], 'cache:badge' => [ 'map' => 'cache.nb_measures', - 'default' => 0, + 'default' => 'null', ], ]; } From 6fa682397ac8809c757d1fdee4917e5d819b0af0 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Tue, 25 Jul 2017 15:01:25 +0200 Subject: [PATCH 037/585] Update dev deps --- composer.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/composer.json b/composer.json index da045a8ad..38b40be8f 100644 --- a/composer.json +++ b/composer.json @@ -40,5 +40,8 @@ "Debugbar": "Barryvdh\\Debugbar\\Facade" } } + }, + "require-dev": { + "illuminate/framework": "5.5.x" } } From 58bdd2919ac2da5997362181e9546c9cd82ffc5e Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Tue, 25 Jul 2017 15:01:32 +0200 Subject: [PATCH 038/585] Update cache collector --- src/DataCollector/CacheCollector.php | 79 ++++++++-------------------- 1 file changed, 21 insertions(+), 58 deletions(-) diff --git a/src/DataCollector/CacheCollector.php b/src/DataCollector/CacheCollector.php index 13bfc1aed..04aaeb9b7 100644 --- a/src/DataCollector/CacheCollector.php +++ b/src/DataCollector/CacheCollector.php @@ -2,6 +2,11 @@ namespace Barryvdh\Debugbar\DataCollector; use DebugBar\DataCollector\TimeDataCollector; +use Illuminate\Cache\Events\CacheEvent; +use Illuminate\Cache\Events\CacheHit; +use Illuminate\Cache\Events\CacheMissed; +use Illuminate\Cache\Events\KeyForgotten; +use Illuminate\Cache\Events\KeyWritten; use Illuminate\Events\Dispatcher; class CacheCollector extends TimeDataCollector @@ -11,10 +16,10 @@ class CacheCollector extends TimeDataCollector /** @var array */ protected $classMap = [ - 'Illuminate\Cache\Events\CacheHit' => 'hit', - 'Illuminate\Cache\Events\CacheMissed' => 'missed', - 'Illuminate\Cache\Events\KeyWritten' => 'write', - 'Illuminate\Cache\Events\KeyForgotten' => 'delete', + CacheHit::class => 'hit', + CacheMissed::class => 'missed', + KeyWritten::class => 'written', + KeyForgotten::class => 'forgotten', ]; public function __construct($requestStartTime = null, $collectValues) @@ -24,70 +29,28 @@ public function __construct($requestStartTime = null, $collectValues) $this->collectValues = $collectValues; } - public function onClassEvent($name, $event = null) + public function onCacheEvent(CacheEvent $event) { - if(is_object($name)) { - $event = $name; - } - - if(is_array($event)) { - $event = $event[0]; - } - $class = get_class($event); - if (isset($this->classMap[$class])) { - $params = []; - - if(isset($event->minutes)) { - $params['minutes'] = $event->minutes; - } - - if(isset($event->value)) { - if ($this->collectValues) { - $params['value'] = $this->getDataFormatter()->formatVar($event->value); - } else { - $params['value'] = '(values collecting turned off)'; - } - } - - if(!empty($event->tags)) { - $params['tags'] = $event->tags; - } + $params = get_object_vars($event); - $time = microtime(true); - $this->addMeasure($this->classMap[$class] . ' ' . $event->key, $time, $time, $params); - } - } - - public function onStringEvent($event, $payload) - { - $params = []; - - if(is_array($payload)) { - if (isset($payload[2])) { - $params['minutes'] = $payload[2]; - } - - if (isset($payload[1])) { - if ($this->collectValues) { - $params['value'] = $this->getDataFormatter()->formatVar($payload[1]); - } else { - $params['value'] = '(values collecting turned off)'; - } + if(isset($params['value'])) { + if ($this->collectValues) { + $params['value'] = $this->getDataFormatter()->formatVar($event->value); + } else { + unset($params['value']); } } $time = microtime(true); - $this->addMeasure( str_replace('cache.', '', $event) . ' ' . (is_array($payload) ? $payload[0] : $payload), - $time, $time, $params); + $this->addMeasure($this->classMap[$class] . "\t" . $event->key, $time, $time, $params); } - public function subscribe(Dispatcher $events) + + public function subscribe(Dispatcher $dispatcher) { - if (class_exists('Illuminate\Cache\Events\CacheHit')) { - $events->listen('Illuminate\Cache\Events\*', [$this, 'onClassEvent']); - } else { - $events->listen('cache.*', [$this, 'onStringEvent']); + foreach ($this->classMap as $eventClass => $type) { + $dispatcher->listen($eventClass, [$this, 'onCacheEvent']); } } From 3030d1ae6b63a45714b0783cbaf29057375ce49a Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 17 Aug 2017 09:20:49 +0200 Subject: [PATCH 039/585] Allow 5.4 --- composer.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 38b40be8f..280a953e0 100644 --- a/composer.json +++ b/composer.json @@ -12,11 +12,11 @@ "require": { "php": ">=7.0", "maximebf/debugbar": "~1.14.0", - "illuminate/support": "5.5.x", - "illuminate/session": "5.5.x", - "symfony/debug": "^3.3", - "symfony/finder": "^3.3", - "illuminate/routing": "5.5.x" + "illuminate/routing": "5.4.x|5.5.x", + "illuminate/session": "5.4.x|5.5.x", + "illuminate/support": "5.4.x|5.5.x", + "symfony/debug": "^3.2", + "symfony/finder": "^3.2" }, "autoload": { "psr-4": { From eabef3695317d2d69ea1e8f719c73157e2baa206 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 17 Aug 2017 09:33:08 +0200 Subject: [PATCH 040/585] Update ServiceProvider.php --- src/ServiceProvider.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 185461c58..2f9b4aaff 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -67,7 +67,10 @@ public function boot() $configPath = __DIR__ . '/../config/debugbar.php'; $this->publishes([$configPath => $this->getConfigPath()], 'config'); - $enabled = (bool) $this->app['config']->get('debugbar.enabled'); + $enabled = $this->app['config']->get('debugbar.enabled'); + if ($enabled === null) { + $enabled = $this->app['config']->get('app.debug'); + } if (! $enabled) { return; From b60825e9c285e95468164aa5d9fd73869aeb0c5e Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Mon, 28 Aug 2017 22:18:09 +0200 Subject: [PATCH 041/585] Update composer.json --- composer.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 280a953e0..f3735c192 100644 --- a/composer.json +++ b/composer.json @@ -12,11 +12,11 @@ "require": { "php": ">=7.0", "maximebf/debugbar": "~1.14.0", - "illuminate/routing": "5.4.x|5.5.x", - "illuminate/session": "5.4.x|5.5.x", - "illuminate/support": "5.4.x|5.5.x", - "symfony/debug": "^3.2", - "symfony/finder": "^3.2" + "illuminate/routing": "5.5.x", + "illuminate/session": "5.5.x", + "illuminate/support": "5.5.x", + "symfony/debug": "^3", + "symfony/finder": "^3" }, "autoload": { "psr-4": { From a8f798029783e021f25affab6c34f786983639df Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Mon, 28 Aug 2017 22:21:05 +0200 Subject: [PATCH 042/585] Add note about package discovery --- readme.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index b41dcbe3b..e3c3d813f 100644 --- a/readme.md +++ b/readme.md @@ -3,6 +3,8 @@ [![Latest Stable Version](https://poser.pugx.org/barryvdh/laravel-debugbar/version.png)](https://packagist.org/packages/barryvdh/laravel-debugbar) [![Total Downloads](https://poser.pugx.org/barryvdh/laravel-debugbar/d/total.png)](https://packagist.org/packages/barryvdh/laravel-debugbar) +### Note for v3: Debugbar is now enabled by requiring the package, but still needs APP_DEBUG=true by default! + ### For Laravel < 5.5, please use the [2.4 branch](https://github.com/barryvdh/laravel-debugbar/tree/2.4)! This is a package to integrate [PHP Debug Bar](http://phpdebugbar.com/) with Laravel 5. @@ -42,20 +44,19 @@ It also provides a Facade interface for easy logging Messages, Exceptions and Ti ## Installation -Require this package with composer: +Require this package with composer. It is recommended to only require the package for development. ```shell -composer require barryvdh/laravel-debugbar +composer require barryvdh/laravel-debugbar --dev ``` Laravel 5.5 uses Package Auto-Discovery, so doesn't require you to manually add the ServiceProvider. The Debugbar will be enabled when `APP_DEBUG` is `true`. - > If you use a catch-all/fallback route, make sure you load the Debugbar ServiceProvider before your own App ServiceProviders. -### Laravel 5: +### Laravel 5.5+: If you don't use auto-discovery, add the ServiceProvider to the providers array in config/app.php From 70c29502e4cf6305e3b2e8a20021f2d86d9abd53 Mon Sep 17 00:00:00 2001 From: Alexander Obuhovich Date: Thu, 17 Aug 2017 10:35:45 +0300 Subject: [PATCH 043/585] Show paths from "vendor/laravel/framework" folder in query traces (#695) --- src/DataCollector/QueryCollector.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 6f6d7df67..786a59db9 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -220,8 +220,11 @@ protected function parseTrace($index, array $trace) if (isset($trace['class']) && isset($trace['file']) && strpos( $trace['file'], - DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'laravel' . DIRECTORY_SEPARATOR . 'framework' + DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'laravel' . DIRECTORY_SEPARATOR . 'framework/src/Illuminate/Database' ) === false && strpos( + $trace['file'], + DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'laravel' . DIRECTORY_SEPARATOR . 'framework/src/Illuminate/Events' + ) === false && strpos( $trace['file'], DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'barryvdh' . DIRECTORY_SEPARATOR . 'laravel-debugbar' ) === false From 5b321c05cdd0b480315b173af8150473ab96e806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristian=20Pallar=C3=A9s?= Date: Thu, 17 Aug 2017 09:36:45 +0200 Subject: [PATCH 044/585] Fix Server-Timing headers (#691) --- src/LaravelDebugbar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index d820a8cf3..f9c3a25e2 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -1046,7 +1046,7 @@ protected function addServerTimingHeaders(Response $response) $headers = []; foreach ($collector->collect()['measures'] as $k => $m) { - $headers[] = sprintf('%d=%F; "%s"', $k, $m['duration'], str_replace('"', "'", $m['label'])); + $headers[] = sprintf('%d=%F; "%s"', $k, $m['duration'] * 1000, str_replace('"', "'", $m['label'])); } $response->headers->set('Server-Timing', $headers, false); From bce341e3194bbeffa60ac782d645067557f5075e Mon Sep 17 00:00:00 2001 From: C-Bass Date: Tue, 29 Aug 2017 04:54:25 -0400 Subject: [PATCH 045/585] as of Laravel 5.5 fire() must be renamed to handle() (#705) https://laravel.com/docs/5.5/upgrade Artisan The fire Method Any fire methods present on your Artisan commands should be renamed to handle --- src/Console/ClearCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/ClearCommand.php b/src/Console/ClearCommand.php index 8ca66032b..64c7dd28d 100644 --- a/src/Console/ClearCommand.php +++ b/src/Console/ClearCommand.php @@ -16,7 +16,7 @@ public function __construct(DebugBar $debugbar) parent::__construct(); } - public function fire() + public function handle() { $this->debugbar->boot(); From a59bd27e78adf3746bfd6bb57938cc9382ec751b Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Wed, 6 Sep 2017 10:02:00 +0200 Subject: [PATCH 046/585] Limit backtrace to 50 --- src/DataCollector/QueryCollector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 786a59db9..326258f40 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -185,7 +185,7 @@ protected function performQueryAnalysis($query) */ protected function findSource() { - $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT); + $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT, 50); $sources = []; From 322bafa9c06018da6f8a6057ac054b9581fae50d Mon Sep 17 00:00:00 2001 From: Upperfoot Date: Thu, 7 Sep 2017 12:35:21 +0100 Subject: [PATCH 047/585] Resolve Problem with relocation of Router in Lumen (#718) Due to this commit https://github.com/laravel/lumen-framework/commit/7018d57d684e7e63bcef9576bc718e9ad75feb0c on the Lumen 5.5 branch, the core methods required for Routing have been moved to Laravel\Lumen\Routing\Router rather than it residing within the Lumen Application. --- src/LumenServiceProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LumenServiceProvider.php b/src/LumenServiceProvider.php index d28a4d2b7..a3542753d 100644 --- a/src/LumenServiceProvider.php +++ b/src/LumenServiceProvider.php @@ -14,7 +14,7 @@ class LumenServiceProvider extends ServiceProvider */ protected function getRouter() { - return $this->app; + return $this->app->router; } /** From 1189ebccda6172d9010188e3dbcd24d9558231c2 Mon Sep 17 00:00:00 2001 From: Alexander Obuhovich Date: Thu, 7 Sep 2017 14:36:18 +0300 Subject: [PATCH 048/585] Allow enabling/disabling DebugBar from middlewares (#713) --- src/Controllers/BaseController.php | 2 +- src/Controllers/OpenHandlerController.php | 20 ++--------- src/LaravelDebugbar.php | 13 +++++-- src/Middleware/DebugbarEnabled.php | 42 +++++++++++++++++++++++ src/Middleware/InjectDebugbar.php | 6 ++++ src/ServiceProvider.php | 22 ++---------- 6 files changed, 64 insertions(+), 41 deletions(-) create mode 100644 src/Middleware/DebugbarEnabled.php diff --git a/src/Controllers/BaseController.php b/src/Controllers/BaseController.php index 904747d48..3c0d08b41 100644 --- a/src/Controllers/BaseController.php +++ b/src/Controllers/BaseController.php @@ -35,4 +35,4 @@ public function __construct(Request $request, LaravelDebugbar $debugbar) } } } -} \ No newline at end of file +} diff --git a/src/Controllers/OpenHandlerController.php b/src/Controllers/OpenHandlerController.php index 3d2c73ee4..77056f403 100644 --- a/src/Controllers/OpenHandlerController.php +++ b/src/Controllers/OpenHandlerController.php @@ -6,17 +6,10 @@ class OpenHandlerController extends BaseController { - + public function handle() { - $debugbar = $this->debugbar; - - if (!$debugbar->isEnabled()) { - $this->app->abort('500', 'Debugbar is not enabled'); - } - - $openHandler = new OpenHandler($debugbar); - + $openHandler = new OpenHandler($this->debugbar); $data = $openHandler->handle(null, false, false); return new Response( @@ -40,14 +33,7 @@ public function clockwork($id) 'id' => $id, ]; - $debugbar = $this->debugbar; - - if (!$debugbar->isEnabled()) { - $this->app->abort('500', 'Debugbar is not enabled'); - } - - $openHandler = new OpenHandler($debugbar); - + $openHandler = new OpenHandler($this->debugbar); $data = $openHandler->handle($request, false, false); // Convert to Clockwork diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index f9c3a25e2..9c0f29ca4 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -126,7 +126,7 @@ public function boot() /** @var Application $app */ $app = $this->app; - + // Set custom error handler if ($app['config']->get('debugbar.error_handler' , false)) { set_error_handler([$this, 'handleError']); @@ -615,7 +615,7 @@ public function getJavascriptRenderer($baseUrl = null, $basePath = null) public function modifyResponse(Request $request, Response $response) { $app = $this->app; - if ($app->runningInConsole() || !$this->isEnabled() || $this->isDebugbarRequest()) { + if (!$this->isEnabled() || $this->isDebugbarRequest()) { return $response; } @@ -747,7 +747,14 @@ public function modifyResponse(Request $request, Response $response) public function isEnabled() { if ($this->enabled === null) { - $this->enabled = value($this->app['config']->get('debugbar.enabled')); + $config = $this->app['config']; + $configEnabled = value($config->get('debugbar.enabled')); + + if ($configEnabled === null) { + $configEnabled = $config->get('app.debug'); + } + + $this->enabled = $configEnabled && !$this->app->runningInConsole() && !$this->app->environment('testing'); } return $this->enabled; diff --git a/src/Middleware/DebugbarEnabled.php b/src/Middleware/DebugbarEnabled.php new file mode 100644 index 000000000..9e10a7664 --- /dev/null +++ b/src/Middleware/DebugbarEnabled.php @@ -0,0 +1,42 @@ +debugbar = $debugbar; + } + + /** + * Handle an incoming request. + * + * @param Request $request + * @param Closure $next + * @return mixed + */ + public function handle($request, Closure $next) + { + if (!$this->debugbar->isEnabled()) { + abort(404); + } + + return $next($request); + + } +} diff --git a/src/Middleware/InjectDebugbar.php b/src/Middleware/InjectDebugbar.php index 5c3a8efff..f4db7d969 100644 --- a/src/Middleware/InjectDebugbar.php +++ b/src/Middleware/InjectDebugbar.php @@ -46,6 +46,12 @@ public function __construct(Container $container, LaravelDebugbar $debugbar) */ public function handle($request, Closure $next) { + if (!$this->debugbar->isEnabled()) { + return $next($request); + } + + $this->debugbar->boot(); + try { /** @var \Illuminate\Http\Response $response */ $response = $next($request); diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 2f9b4aaff..6079911e9 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -1,5 +1,6 @@ app; - $configPath = __DIR__ . '/../config/debugbar.php'; $this->publishes([$configPath => $this->getConfigPath()], 'config'); - $enabled = $this->app['config']->get('debugbar.enabled'); - if ($enabled === null) { - $enabled = $this->app['config']->get('app.debug'); - } - - if (! $enabled) { - return; - } - $routeConfig = [ 'namespace' => 'Barryvdh\Debugbar\Controllers', 'prefix' => $this->app['config']->get('debugbar.route_prefix'), 'domain' => $this->app['config']->get('debugbar.route_domain'), + 'middleware' => [DebugbarEnabled::class], ]; $this->getRouter()->group($routeConfig, function($router) { @@ -104,15 +95,6 @@ public function boot() ]); }); - if ($app->runningInConsole() || $app->environment('testing')) { - return; - } - - /** @var LaravelDebugbar $debugbar */ - $debugbar = $this->app['debugbar']; - $debugbar->enable(); - $debugbar->boot(); - $this->registerMiddleware(InjectDebugbar::class); } From fff9d0723f705a12f11602e0c4f2ed2105d49979 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Mon, 18 Sep 2017 15:31:20 +0200 Subject: [PATCH 049/585] Check for ignored urls --- src/Middleware/InjectDebugbar.php | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/Middleware/InjectDebugbar.php b/src/Middleware/InjectDebugbar.php index f4db7d969..42934fa02 100644 --- a/src/Middleware/InjectDebugbar.php +++ b/src/Middleware/InjectDebugbar.php @@ -25,6 +25,13 @@ class InjectDebugbar */ protected $debugbar; + /** + * The URIs that should be excluded. + * + * @var array + */ + protected $except = []; + /** * Create a new middleware instance. * @@ -35,6 +42,7 @@ public function __construct(Container $container, LaravelDebugbar $debugbar) { $this->container = $container; $this->debugbar = $debugbar; + $this->except = config('debugbar.except') ?: []; } /** @@ -46,7 +54,7 @@ public function __construct(Container $container, LaravelDebugbar $debugbar) */ public function handle($request, Closure $next) { - if (!$this->debugbar->isEnabled()) { + if (!$this->debugbar->isEnabled() || $this->inExceptArray($request)) { return $next($request); } @@ -91,4 +99,25 @@ protected function handleException($passable, Exception $e) return $handler->render($passable, $e); } + + /** + * Determine if the request has a URI that should be ignored. + * + * @param \Illuminate\Http\Request $request + * @return bool + */ + protected function inExceptArray($request) + { + foreach ($this->except as $except) { + if ($except !== '/') { + $except = trim($except, '/'); + } + + if ($request->is($except)) { + return true; + } + } + + return false; + } } From 01a859752094e00aa8548832312366753272f8af Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Mon, 18 Sep 2017 15:32:46 +0200 Subject: [PATCH 050/585] Update debugbar.php --- config/debugbar.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/config/debugbar.php b/config/debugbar.php index 425a409dd..8350e1c8d 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -9,10 +9,15 @@ | | Debugbar is enabled by default, when debug is set to true in app.php. | You can override the value by setting enable to true or false instead of null. + | + | You can provide an array of URI's that must be ignored (eg. 'api/*') | */ - 'enabled' => env('DEBUGBAR_ENABLED', env('APP_DEBUG', false)), + 'enabled' => env('DEBUGBAR_ENABLED', null), + 'except' => [ + // + ], /* |-------------------------------------------------------------------------- From 99abc7fc49e7440346f7b6eee54e397f62a4c981 Mon Sep 17 00:00:00 2001 From: Dave James Miller Date: Wed, 4 Oct 2017 08:19:56 +0100 Subject: [PATCH 051/585] Queries on different databases aren't duplicates (#730) --- src/Resources/sqlqueries/widget.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Resources/sqlqueries/widget.js b/src/Resources/sqlqueries/widget.js index 17e273c52..4959e0837 100644 --- a/src/Resources/sqlqueries/widget.js +++ b/src/Resources/sqlqueries/widget.js @@ -186,6 +186,9 @@ if (data.statements[i].bindings && data.statements[i].bindings.length) { stmt += JSON.stringify(data.statements[i].bindings); } + if (data.statements[i].connection) { + stmt += '@' + data.statements[i].connection; + } sql[stmt] = sql[stmt] || { keys: [] }; sql[stmt].keys.push(i); } From 43ba768e201d0c0c12d12c513b2ff7ae7ec256a3 Mon Sep 17 00:00:00 2001 From: Broutard Date: Wed, 4 Oct 2017 09:22:27 +0200 Subject: [PATCH 052/585] feat: add a forget button in the cache panel (#728) * feat: add a forget button in the cache panel * LaravelCacheWidget now extends TimelineWidget * fix: escape value --- src/Controllers/CacheController.php | 27 +++++++++++++ src/DataCollector/CacheCollector.php | 18 +++++++-- src/JavascriptRenderer.php | 1 + src/Resources/cache/widget.js | 59 ++++++++++++++++++++++++++++ src/Resources/laravel-debugbar.css | 12 ++++++ src/ServiceProvider.php | 5 +++ 6 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 src/Controllers/CacheController.php create mode 100644 src/Resources/cache/widget.js diff --git a/src/Controllers/CacheController.php b/src/Controllers/CacheController.php new file mode 100644 index 000000000..3d04305ab --- /dev/null +++ b/src/Controllers/CacheController.php @@ -0,0 +1,27 @@ +tags($tags); + } else { + unset($tags); + } + + $success = $cache->forget($key); + + return response()->json(compact('success')); + } + +} diff --git a/src/DataCollector/CacheCollector.php b/src/DataCollector/CacheCollector.php index 04aaeb9b7..61b7ad4fa 100644 --- a/src/DataCollector/CacheCollector.php +++ b/src/DataCollector/CacheCollector.php @@ -34,16 +34,26 @@ public function onCacheEvent(CacheEvent $event) $class = get_class($event); $params = get_object_vars($event); - if(isset($params['value'])) { + $label = $this->classMap[$class]; + + if (isset($params['value'])) { if ($this->collectValues) { - $params['value'] = $this->getDataFormatter()->formatVar($event->value); + $params['value'] = htmlspecialchars($this->getDataFormatter()->formatVar($event->value)); } else { unset($params['value']); } } + + if (!empty($params['key']) && in_array($label, ['hit', 'written'])) { + $params['delete'] = route('debugbar.cache.delete', [ + 'key' => urlencode($params['key']), + 'tags' => !empty($params['tags']) ? json_encode($params['tags']) : '', + ]); + } + $time = microtime(true); - $this->addMeasure($this->classMap[$class] . "\t" . $event->key, $time, $time, $params); + $this->addMeasure($label . "\t" . $event->key, $time, $time, $params); } @@ -72,7 +82,7 @@ public function getWidgets() return [ 'cache' => [ 'icon' => 'clipboard', - 'widget' => 'PhpDebugBar.Widgets.TimelineWidget', + 'widget' => 'PhpDebugBar.Widgets.LaravelCacheWidget', 'map' => 'cache', 'default' => '{}', ], diff --git a/src/JavascriptRenderer.php b/src/JavascriptRenderer.php index 9cdaf22f9..df4423258 100644 --- a/src/JavascriptRenderer.php +++ b/src/JavascriptRenderer.php @@ -20,6 +20,7 @@ public function __construct(DebugBar $debugBar, $baseUrl = null, $basePath = nul $this->cssFiles['laravel'] = __DIR__ . '/Resources/laravel-debugbar.css'; $this->cssVendors['fontawesome'] = __DIR__ . '/Resources/vendor/font-awesome/style.css'; $this->jsFiles['laravel-sql'] = __DIR__ . '/Resources/sqlqueries/widget.js'; + $this->jsFiles['laravel-cache'] = __DIR__ . '/Resources/cache/widget.js'; } /** diff --git a/src/Resources/cache/widget.js b/src/Resources/cache/widget.js new file mode 100644 index 000000000..ad7d967fe --- /dev/null +++ b/src/Resources/cache/widget.js @@ -0,0 +1,59 @@ +(function($) { + + var csscls = PhpDebugBar.utils.makecsscls('phpdebugbar-widgets-'); + + /** + * Widget for the displaying cache events + * + * Options: + * - data + */ + var LaravelCacheWidget = PhpDebugBar.Widgets.LaravelCacheWidget = PhpDebugBar.Widgets.TimelineWidget.extend({ + + tagName: 'ul', + + className: csscls('timeline cache'), + + onForgetClick: function(e, el) { + e.stopPropagation(); + + $.ajax({ + url: $(el).attr("data-url"), + type: 'DELETE', + success: function(result) { + $(el).fadeOut(200); + } + }); + }, + + render: function() { + LaravelCacheWidget.__super__.render.apply(this); + + this.bindAttr('data', function(data) { + + if (data.measures) { + var self = this; + var lines = this.$el.find('.'+csscls('measure')); + + for (var i = 0; i < data.measures.length; i++) { + var measure = data.measures[i]; + var m = lines[i]; + + if (measure.params && !$.isEmptyObject(measure.params)) { + + if (measure.params.delete && measure.params.key) { + $('
    ') + .addClass(csscls('forget')) + .text('forget') + .attr('data-url', measure.params.delete) + .one('click', function(e) { self.onForgetClick(e, this); }) + .appendTo(m); + } + } + } + } + }); + } + }); + +})(PhpDebugBar.$); diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index 334ffaee5..3681b5a9f 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -303,3 +303,15 @@ ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-table-list-item { .phpdebugbar-text-muted { color: #888; } + +ul.phpdebugbar-widgets-cache a.phpdebugbar-widgets-forget { + float: right; + font-size: 12px; + padding: 0 4px; + background: #f4645f; + margin: 0 2px; + border-radius: 4px; + color: #fff; + text-decoration: none; + line-height: 1.5rem; +} \ No newline at end of file diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 6079911e9..f86b593ac 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -93,6 +93,11 @@ public function boot() 'uses' => 'AssetController@js', 'as' => 'debugbar.assets.js', ]); + + $router->delete('cache/{key}/{tags?}', [ + 'uses' => 'CacheController@delete', + 'as' => 'debugbar.cache.delete', + ]); }); $this->registerMiddleware(InjectDebugbar::class); From ab1ef1c3aae256d08cfbf54ff0824a2812f91cbc Mon Sep 17 00:00:00 2001 From: Devin Date: Sun, 24 Dec 2017 04:51:20 -0800 Subject: [PATCH 053/585] Update GateCollector (#735) addCheck assumed that the authorized model was the authenticated App\User. In my case I have a App\User, App\Account and an App\AccountUser. The App\User is authenticated but authorizations are on the App\AccountUser which does not implement Authenticatable. This change type hints $user to Authorizable because thats what is really important in this collector. It also uses the Authorizable's base class name in the message instead of assuming user. --- src/DataCollector/GateCollector.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/DataCollector/GateCollector.php b/src/DataCollector/GateCollector.php index 529b8be98..73ef225e3 100644 --- a/src/DataCollector/GateCollector.php +++ b/src/DataCollector/GateCollector.php @@ -4,7 +4,7 @@ use DebugBar\DataCollector\MessagesCollector; use Illuminate\Contracts\Auth\Access\Gate; -use Illuminate\Contracts\Auth\Authenticatable; +use Illuminate\Contracts\Auth\Access\Authorizable; use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter; /** @@ -25,14 +25,14 @@ public function __construct(Gate $gate) $gate->after([$this, 'addCheck']); } - public function addCheck(Authenticatable $user, $ability, $result, $arguments = []) + public function addCheck(Authorizable $user, $ability, $result, $arguments = []) { $label = $result ? 'success' : 'error'; $this->addMessage([ 'ability' => $ability, 'result' => $result, - 'user' => $user->getAuthIdentifier(), + snake_case(class_basename($user)) => $user->id, 'arguments' => $this->exporter->exportValue($arguments), ], $label, false); } From 0b04bcd2264b19317d05b9bb3a059412d6466839 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Sun, 24 Dec 2017 13:52:57 +0100 Subject: [PATCH 054/585] Bump debugbar version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f3735c192..f428ce5f7 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ ], "require": { "php": ">=7.0", - "maximebf/debugbar": "~1.14.0", + "maximebf/debugbar": "~1.15.0", "illuminate/routing": "5.5.x", "illuminate/session": "5.5.x", "illuminate/support": "5.5.x", From 8395020ee6b27a6f2fae04a80cf8f12355042ebb Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Sun, 24 Dec 2017 13:54:33 +0100 Subject: [PATCH 055/585] Prepare for L5.6 --- composer.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index f428ce5f7..c96ac7dff 100644 --- a/composer.json +++ b/composer.json @@ -12,11 +12,11 @@ "require": { "php": ">=7.0", "maximebf/debugbar": "~1.15.0", - "illuminate/routing": "5.5.x", - "illuminate/session": "5.5.x", - "illuminate/support": "5.5.x", - "symfony/debug": "^3", - "symfony/finder": "^3" + "illuminate/routing": "5.5.x|5.6.x", + "illuminate/session": "5.5.x|5.6.x", + "illuminate/support": "5.5.x|5.6.x", + "symfony/debug": "^3|^4", + "symfony/finder": "^3|^4" }, "autoload": { "psr-4": { @@ -30,7 +30,7 @@ "prefer-stable": true, "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.2-dev" }, "laravel": { "providers": [ From 0c878d52e4c9fa4b9cb7c76643ef8e4d3eee1f4e Mon Sep 17 00:00:00 2001 From: Scott Pinkelman Date: Sun, 24 Dec 2017 05:56:43 -0700 Subject: [PATCH 056/585] Minor CSS Tweaks (#676) * Adds z-index declaration to phpdebugbar div so that it isn't overlapped by pre.sf-dump elements * Sets .phpdebugbar-widgets-sql elements to 'cursor: text;' to make it easier to copy the queries into an SQL editor --- src/Resources/laravel-debugbar.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index 3681b5a9f..cdfb0bb86 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -3,6 +3,7 @@ div.phpdebugbar { font-family: "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif; direction: ltr; text-align: left; + z-index: 100000; } div.phpdebugbar-resize-handle { @@ -219,6 +220,7 @@ ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item { ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-sql { flex: 1; margin-right: 5px; + cursor: text; } ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-duration { From 72fce8d5050a07421713313bc672fbd76b3c1dfc Mon Sep 17 00:00:00 2001 From: Except10n Date: Sun, 24 Dec 2017 21:00:55 +0800 Subject: [PATCH 057/585] Support instance of application in Lumen (#611) --- src/DataCollector/FilesCollector.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/DataCollector/FilesCollector.php b/src/DataCollector/FilesCollector.php index aad57ecd1..4c56837c4 100644 --- a/src/DataCollector/FilesCollector.php +++ b/src/DataCollector/FilesCollector.php @@ -4,18 +4,18 @@ use DebugBar\DataCollector\DataCollector; use DebugBar\DataCollector\Renderable; -use Illuminate\Contracts\Foundation\Application; +use Illuminate\Container\Container; class FilesCollector extends DataCollector implements Renderable { - /** @var \Illuminate\Contracts\Foundation\Application */ + /** @var \Illuminate\Container\Container */ protected $app; protected $basePath; /** - * @param \Illuminate\Contracts\Foundation\Application $app + * @param \Illuminate\Container\Container $app */ - public function __construct(Application $app = null) + public function __construct(Container $app = null) { $this->app = $app; $this->basePath = base_path(); From d42cb99f8394895516d96575f29ee20be5e1f678 Mon Sep 17 00:00:00 2001 From: Except10n Date: Sun, 24 Dec 2017 21:02:02 +0800 Subject: [PATCH 058/585] Support phpredis php extension (#606) --- src/LaravelDebugbar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 9c0f29ca4..6f291d706 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -1012,7 +1012,7 @@ protected function selectStorage(DebugBar $debugbar) case 'redis': $connection = $config->get('debugbar.storage.connection'); $client = $this->app['redis']->connection($connection); - if (is_a($client, 'Illuminate\Redis\Connections\PredisConnection', false)) { + if (is_a($client, 'Illuminate\Redis\Connections\Connection', false)) { $client = $client->client(); } $storage = new RedisStorage($client); From e2cbcbfcc5b5207167e48a575180a6cb9a28c9e5 Mon Sep 17 00:00:00 2001 From: Ilya Gusev Date: Sun, 24 Dec 2017 16:02:35 +0300 Subject: [PATCH 059/585] Update Installation (#577) Added `--dev` param From 36254876921a29c5360b2da49a9c62aa686b180b Mon Sep 17 00:00:00 2001 From: Alexander Menk Date: Sun, 24 Dec 2017 14:06:54 +0100 Subject: [PATCH 060/585] Add Xdebug Link functionality (#567) https://github.com/barryvdh/laravel-debugbar/issues/566 --- src/DataCollector/ViewCollector.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/DataCollector/ViewCollector.php b/src/DataCollector/ViewCollector.php index 92e4d4133..444662599 100644 --- a/src/DataCollector/ViewCollector.php +++ b/src/DataCollector/ViewCollector.php @@ -80,12 +80,18 @@ public function addView(View $view) $params = $data; } - $this->templates[] = [ + $template = [ 'name' => $path ? sprintf('%s (%s)', $name, $path) : $name, 'param_count' => count($params), 'params' => $params, 'type' => $type, ]; + + if ( $this->getXdebugLink($path)) { + $template['xdebug_link'] = $this->getXdebugLink($path); + } + + $this->templates[] = $template; } public function collect() From cb93a99a35de7376a52bc2750b84e6be3def3e55 Mon Sep 17 00:00:00 2001 From: Mark Beech Date: Sun, 24 Dec 2017 13:08:47 +0000 Subject: [PATCH 061/585] Give rows a little more breathing space, especially for the query tab (#535) --- src/Resources/laravel-debugbar.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index cdfb0bb86..d708aab14 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -205,7 +205,7 @@ div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-w } ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item { - padding: 5px 10px; + padding: 15px 10px; border: none; font-family: inherit; overflow: visible; From a3858e5334b9a8735cbc42ee58fbc3b4fc43bae3 Mon Sep 17 00:00:00 2001 From: dha0stw Date: Sun, 24 Dec 2017 21:09:57 +0800 Subject: [PATCH 062/585] Disable specific vendors (#339) * to read configure file and affect renderer * add disableVendors setting * add braces to foreach syntax * fulfillment comment rule * fix typo --- config/debugbar.php | 13 +++++++++++++ src/LaravelDebugbar.php | 6 ++++++ 2 files changed, 19 insertions(+) diff --git a/config/debugbar.php b/config/debugbar.php index 8350e1c8d..b5f850103 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -55,6 +55,19 @@ 'include_vendors' => true, + /* + |-------------------------------------------------------------------------- + | Disable Vendors + |-------------------------------------------------------------------------- + | + | Could be easily disable specific vendor by setting following array. + | You should inject jquery library manually. + | Options: 'jquery', 'highlightjs', 'fontawesome'. + | + */ + + 'disableVendors' => [], + /* |-------------------------------------------------------------------------- | Capture Ajax Requests diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 6f291d706..0b99dcda5 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -483,6 +483,12 @@ function ($query, $bindings = null, $time = null, $connectionName = null) use ($ } $renderer = $this->getJavascriptRenderer(); + $disableVendors = $this->app['config']->get('debugbar.disableVendors', []); + if (!empty($disableVendors)) { + foreach($disableVendors as $vendor) { + $renderer->disableVendor($vendor); + { + } $renderer->setIncludeVendors($this->app['config']->get('debugbar.include_vendors', true)); $renderer->setBindAjaxHandlerToXHR($app['config']->get('debugbar.capture_ajax', true)); From 9ea44af4523988996800e8d6b4b195095e907cbe Mon Sep 17 00:00:00 2001 From: Kai Rienow Date: Sun, 24 Dec 2017 14:16:47 +0100 Subject: [PATCH 063/585] Add newline character. (#477) --- src/Storage/FilesystemStorage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Storage/FilesystemStorage.php b/src/Storage/FilesystemStorage.php index d336921c7..1ea11aee7 100644 --- a/src/Storage/FilesystemStorage.php +++ b/src/Storage/FilesystemStorage.php @@ -33,7 +33,7 @@ public function save($id, $data) { if (!$this->files->isDirectory($this->dirname)) { if ($this->files->makeDirectory($this->dirname, 0777, true)) { - $this->files->put($this->dirname . '.gitignore', "*\n!.gitignore"); + $this->files->put($this->dirname . '.gitignore', "*\n!.gitignore\n"); } else { throw new \Exception("Cannot create directory '$this->dirname'.."); } From d5c46b6a51b2773c6c8fbd149517a7719f4ff1f7 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Sun, 24 Dec 2017 14:25:43 +0100 Subject: [PATCH 064/585] Make sure guards is array --- src/LaravelDebugbar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 0b99dcda5..b88c174d7 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -438,7 +438,7 @@ function ($query, $bindings = null, $time = null, $connectionName = null) use ($ if ($this->shouldCollect('auth', false)) { try { - $guards = array_keys($this->app['config']->get('auth.guards')); + $guards = array_keys($this->app['config']->get('auth.guards', [])); $authCollector = new MultiAuthCollector($app['auth'], $guards); $authCollector->setShowName( From 7dd4edb8e6026793b60f1d0231037dfe57bf9df1 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Wed, 27 Dec 2017 19:41:56 +0100 Subject: [PATCH 065/585] Revert "Disable specific vendors" (#764) * Revert "Make sure guards is array" This reverts commit d5c46b6a51b2773c6c8fbd149517a7719f4ff1f7. * Revert "Add newline character. (#477)" This reverts commit 9ea44af4523988996800e8d6b4b195095e907cbe. * Revert "Disable specific vendors (#339)" This reverts commit a3858e5334b9a8735cbc42ee58fbc3b4fc43bae3. --- config/debugbar.php | 13 ------------- src/LaravelDebugbar.php | 6 ------ 2 files changed, 19 deletions(-) diff --git a/config/debugbar.php b/config/debugbar.php index b5f850103..8350e1c8d 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -55,19 +55,6 @@ 'include_vendors' => true, - /* - |-------------------------------------------------------------------------- - | Disable Vendors - |-------------------------------------------------------------------------- - | - | Could be easily disable specific vendor by setting following array. - | You should inject jquery library manually. - | Options: 'jquery', 'highlightjs', 'fontawesome'. - | - */ - - 'disableVendors' => [], - /* |-------------------------------------------------------------------------- | Capture Ajax Requests diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index b88c174d7..8bcca42c9 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -483,12 +483,6 @@ function ($query, $bindings = null, $time = null, $connectionName = null) use ($ } $renderer = $this->getJavascriptRenderer(); - $disableVendors = $this->app['config']->get('debugbar.disableVendors', []); - if (!empty($disableVendors)) { - foreach($disableVendors as $vendor) { - $renderer->disableVendor($vendor); - { - } $renderer->setIncludeVendors($this->app['config']->get('debugbar.include_vendors', true)); $renderer->setBindAjaxHandlerToXHR($app['config']->get('debugbar.capture_ajax', true)); From b5d1df3238c8422e0dd2de07727a8c42c7aabe08 Mon Sep 17 00:00:00 2001 From: Jurager Date: Sun, 4 Feb 2018 20:31:08 +0600 Subject: [PATCH 066/585] Laravel 5.6 support added (#784) * Laravel 5.6 support added Laravel 5.6 uses Symfony's http-kernel 4.0, exportValue has been deprecated since 3.2 and fully removed in 4.0 * Laravel 5.6 support added Laravel 5.6 uses Symfony's http-kernel 4.0, exportValue has been deprecated since 3.2 and fully removed in 4.0 --- src/DataCollector/GateCollector.php | 6 +++--- src/DataCollector/ViewCollector.php | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/DataCollector/GateCollector.php b/src/DataCollector/GateCollector.php index 73ef225e3..31de257dc 100644 --- a/src/DataCollector/GateCollector.php +++ b/src/DataCollector/GateCollector.php @@ -5,7 +5,7 @@ use DebugBar\DataCollector\MessagesCollector; use Illuminate\Contracts\Auth\Access\Gate; use Illuminate\Contracts\Auth\Access\Authorizable; -use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter; +use Symfony\Component\VarDumper\Cloner\VarCloner; /** * Collector for Laravel's Auth provider @@ -20,7 +20,7 @@ class GateCollector extends MessagesCollector public function __construct(Gate $gate) { parent::__construct('gate'); - $this->exporter = new ValueExporter(); + $this->exporter = new VarCloner(); $gate->after([$this, 'addCheck']); } @@ -33,7 +33,7 @@ public function addCheck(Authorizable $user, $ability, $result, $arguments = []) 'ability' => $ability, 'result' => $result, snake_case(class_basename($user)) => $user->id, - 'arguments' => $this->exporter->exportValue($arguments), + 'arguments' => $this->exporter->cloneVar($arguments), ], $label, false); } } diff --git a/src/DataCollector/ViewCollector.php b/src/DataCollector/ViewCollector.php index 444662599..4805aa90f 100644 --- a/src/DataCollector/ViewCollector.php +++ b/src/DataCollector/ViewCollector.php @@ -4,7 +4,7 @@ use DebugBar\Bridge\Twig\TwigCollector; use Illuminate\View\View; -use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter; +use Symfony\Component\VarDumper\Cloner\VarCloner; class ViewCollector extends TwigCollector { @@ -21,7 +21,7 @@ public function __construct($collectData = true) $this->collect_data = $collectData; $this->name = 'views'; $this->templates = []; - $this->exporter = new ValueExporter(); + $this->exporter = new VarCloner(); } public function getName() @@ -75,7 +75,7 @@ public function addView(View $view) } else { $data = []; foreach ($view->getData() as $key => $value) { - $data[$key] = $this->exporter->exportValue($value); + $data[$key] = $this->exporter->cloneVar($value); } $params = $data; } From f0018d359a2ad6968ad11b283283a925e017f3c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Nabia=C5=82ek?= Date: Wed, 7 Feb 2018 09:29:09 +0100 Subject: [PATCH 067/585] Allow to use nullable user (#786) --- src/DataCollector/GateCollector.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DataCollector/GateCollector.php b/src/DataCollector/GateCollector.php index 31de257dc..93dcf1def 100644 --- a/src/DataCollector/GateCollector.php +++ b/src/DataCollector/GateCollector.php @@ -25,14 +25,14 @@ public function __construct(Gate $gate) $gate->after([$this, 'addCheck']); } - public function addCheck(Authorizable $user, $ability, $result, $arguments = []) + public function addCheck(Authorizable $user = null, $ability, $result, $arguments = []) { $label = $result ? 'success' : 'error'; $this->addMessage([ 'ability' => $ability, 'result' => $result, - snake_case(class_basename($user)) => $user->id, + ($user ? snake_case(class_basename($user)) : 'user') => ($user ? $user->id : null), 'arguments' => $this->exporter->cloneVar($arguments), ], $label, false); } From 59a7a08d84111a5b2407fb58b9d74af3ebe430e7 Mon Sep 17 00:00:00 2001 From: Anthony Lawrence Date: Wed, 7 Feb 2018 19:51:45 +0000 Subject: [PATCH 068/585] Replaced use of removed ValueCollector with VarCloner (#787) * Replaced use of removed ValueCollector with VarCloner As per L5.6, Symfony 4 is now used. As such, VarCloner must be used in place of ValueCollector. * Changed PHPDoc for exporter. --- src/DataCollector/EventCollector.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/DataCollector/EventCollector.php b/src/DataCollector/EventCollector.php index 757cd7ffb..f3cfdde90 100644 --- a/src/DataCollector/EventCollector.php +++ b/src/DataCollector/EventCollector.php @@ -4,21 +4,21 @@ use DebugBar\DataCollector\TimeDataCollector; use Illuminate\Events\Dispatcher; use Illuminate\Support\Str; -use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter; +use Symfony\Component\VarDumper\Cloner\VarCloner; class EventCollector extends TimeDataCollector { /** @var Dispatcher */ protected $events; - /** @var ValueExporter */ + /** @var VarCloner */ protected $exporter; public function __construct($requestStartTime = null) { parent::__construct($requestStartTime); - $this->exporter = new ValueExporter(); + $this->exporter = new VarCloner(); } public function onWildcardEvent($name = null, $data = []) @@ -76,7 +76,7 @@ protected function prepareParams($params) if (is_object($value) && Str::is('Illuminate\*\Events\*', get_class($value))) { $value = $this->prepareParams(get_object_vars($value)); } - $data[$key] = htmlentities($this->exporter->exportValue($value), ENT_QUOTES, 'UTF-8', false); + $data[$key] = htmlentities($this->exporter->cloneVar($value), ENT_QUOTES, 'UTF-8', false); } return $data; From 664fa434e26febf04d60df0368303cf0fe14f63a Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Sun, 25 Feb 2018 14:31:37 +0100 Subject: [PATCH 069/585] Add SimpleFormatter for faster exporting --- src/DataCollector/EventCollector.php | 11 +-- src/DataCollector/GateCollector.php | 8 +- src/DataCollector/ViewCollector.php | 5 +- src/DataFormatter/SimpleFormatter.php | 105 ++++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 14 deletions(-) create mode 100644 src/DataFormatter/SimpleFormatter.php diff --git a/src/DataCollector/EventCollector.php b/src/DataCollector/EventCollector.php index f3cfdde90..572cab538 100644 --- a/src/DataCollector/EventCollector.php +++ b/src/DataCollector/EventCollector.php @@ -1,6 +1,7 @@ exporter = new VarCloner(); + $this->setDataFormatter(new SimpleFormatter()); } public function onWildcardEvent($name = null, $data = []) @@ -55,7 +52,7 @@ public function onWildcardEvent($name = null, $data = []) $listener = $reflector->getName() . ' (' . $filename . ':' . $reflector->getStartLine() . '-' . $reflector->getEndLine() . ')'; } else { // Not sure if this is possible, but to prevent edge cases - $listener = $this->formatVar($listener); + $listener = $this->getDataFormatter()->formatVar($listener); } $params['listeners.' . $i] = $listener; @@ -76,7 +73,7 @@ protected function prepareParams($params) if (is_object($value) && Str::is('Illuminate\*\Events\*', get_class($value))) { $value = $this->prepareParams(get_object_vars($value)); } - $data[$key] = htmlentities($this->exporter->cloneVar($value), ENT_QUOTES, 'UTF-8', false); + $data[$key] = htmlentities($this->getDataFormatter()->formatVar($value), ENT_QUOTES, 'UTF-8', false); } return $data; diff --git a/src/DataCollector/GateCollector.php b/src/DataCollector/GateCollector.php index 93dcf1def..a9a3e39eb 100644 --- a/src/DataCollector/GateCollector.php +++ b/src/DataCollector/GateCollector.php @@ -2,6 +2,7 @@ namespace Barryvdh\Debugbar\DataCollector; +use Barryvdh\Debugbar\DataFormatter\SimpleFormatter; use DebugBar\DataCollector\MessagesCollector; use Illuminate\Contracts\Auth\Access\Gate; use Illuminate\Contracts\Auth\Access\Authorizable; @@ -12,16 +13,13 @@ */ class GateCollector extends MessagesCollector { - /** @var ValueExporter */ - protected $exporter; /** * @param Gate $gate */ public function __construct(Gate $gate) { parent::__construct('gate'); - $this->exporter = new VarCloner(); - + $this->setDataFormatter(new SimpleFormatter()); $gate->after([$this, 'addCheck']); } @@ -33,7 +31,7 @@ public function addCheck(Authorizable $user = null, $ability, $result, $argument 'ability' => $ability, 'result' => $result, ($user ? snake_case(class_basename($user)) : 'user') => ($user ? $user->id : null), - 'arguments' => $this->exporter->cloneVar($arguments), + 'arguments' => $this->getDataFormatter()->formatVar($arguments), ], $label, false); } } diff --git a/src/DataCollector/ViewCollector.php b/src/DataCollector/ViewCollector.php index 4805aa90f..b7c657e16 100644 --- a/src/DataCollector/ViewCollector.php +++ b/src/DataCollector/ViewCollector.php @@ -2,6 +2,7 @@ namespace Barryvdh\Debugbar\DataCollector; +use Barryvdh\Debugbar\DataFormatter\SimpleFormatter; use DebugBar\Bridge\Twig\TwigCollector; use Illuminate\View\View; use Symfony\Component\VarDumper\Cloner\VarCloner; @@ -18,10 +19,10 @@ class ViewCollector extends TwigCollector */ public function __construct($collectData = true) { + $this->setDataFormatter(new SimpleFormatter()); $this->collect_data = $collectData; $this->name = 'views'; $this->templates = []; - $this->exporter = new VarCloner(); } public function getName() @@ -75,7 +76,7 @@ public function addView(View $view) } else { $data = []; foreach ($view->getData() as $key => $value) { - $data[$key] = $this->exporter->cloneVar($value); + $data[$key] = $this->getDataFormatter()->formatVar($value); } $params = $data; } diff --git a/src/DataFormatter/SimpleFormatter.php b/src/DataFormatter/SimpleFormatter.php new file mode 100644 index 000000000..0a1fc9380 --- /dev/null +++ b/src/DataFormatter/SimpleFormatter.php @@ -0,0 +1,105 @@ +exportValue($data); + } + + /** + * Converts a PHP value to a string. + * + * @param mixed $value The PHP value + * @param int $depth Only for internal usage + * @param bool $deep Only for internal usage + * + * @return string The string representation of the given value + * @author Bernhard Schussek + */ + private function exportValue($value, $depth = 1, $deep = false) + { + if ($value instanceof \__PHP_Incomplete_Class) { + return sprintf('__PHP_Incomplete_Class(%s)', $this->getClassNameFromIncomplete($value)); + } + + if (is_object($value)) { + if ($value instanceof \DateTimeInterface) { + return sprintf('Object(%s) - %s', get_class($value), $value->format(\DateTime::ATOM)); + } + + return sprintf('Object(%s)', get_class($value)); + } + + if (is_array($value)) { + if (empty($value)) { + return '[]'; + } + + $indent = str_repeat(' ', $depth); + + $a = array(); + foreach ($value as $k => $v) { + if (is_array($v)) { + $deep = true; + } + $a[] = sprintf('%s => %s', $k, $this->exportValue($v, $depth + 1, $deep)); + } + + if ($deep) { + return sprintf("[\n%s%s\n%s]", $indent, implode(sprintf(", \n%s", $indent), $a), str_repeat(' ', $depth - 1)); + } + + $s = sprintf('[%s]', implode(', ', $a)); + + if (80 > strlen($s)) { + return $s; + } + + return sprintf("[\n%s%s\n]", $indent, implode(sprintf(",\n%s", $indent), $a)); + } + + if (is_resource($value)) { + return sprintf('Resource(%s#%d)', get_resource_type($value), $value); + } + + if (null === $value) { + return 'null'; + } + + if (false === $value) { + return 'false'; + } + + if (true === $value) { + return 'true'; + } + + return (string) $value; + } + + /** + * @param \__PHP_Incomplete_Class $value + * @return mixed + * @author Bernhard Schussek + */ + private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value) + { + $array = new \ArrayObject($value); + + return $array['__PHP_Incomplete_Class_Name']; + } +} From 7a91480cc6e597caed5117a3c5d685f06d35c5a1 Mon Sep 17 00:00:00 2001 From: Rob Date: Tue, 6 Mar 2018 08:35:31 +0000 Subject: [PATCH 070/585] If user is instance of Authenticatable call getAuthIdentifier() defined by the interface rather than assuming it has a public id field (#799) --- src/DataCollector/GateCollector.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/DataCollector/GateCollector.php b/src/DataCollector/GateCollector.php index a9a3e39eb..5e6ad850b 100644 --- a/src/DataCollector/GateCollector.php +++ b/src/DataCollector/GateCollector.php @@ -6,6 +6,7 @@ use DebugBar\DataCollector\MessagesCollector; use Illuminate\Contracts\Auth\Access\Gate; use Illuminate\Contracts\Auth\Access\Authorizable; +use Illuminate\Contracts\Auth\Authenticatable; use Symfony\Component\VarDumper\Cloner\VarCloner; /** @@ -25,12 +26,20 @@ public function __construct(Gate $gate) public function addCheck(Authorizable $user = null, $ability, $result, $arguments = []) { + $userKey = 'user'; + $userId = null; + + if ($user) { + $userKey = snake_case(class_basename($user)); + $userId = $user instanceof Authenticatable ? $user->getAuthIdentifier() : $user->id; + } + $label = $result ? 'success' : 'error'; $this->addMessage([ 'ability' => $ability, 'result' => $result, - ($user ? snake_case(class_basename($user)) : 'user') => ($user ? $user->id : null), + $userKey => $userId, 'arguments' => $this->getDataFormatter()->formatVar($arguments), ], $label, false); } From d723a163f86cd8fdf6fa348f7d72d2c126164290 Mon Sep 17 00:00:00 2001 From: Yiannis Siantos Date: Sun, 22 Apr 2018 21:17:52 +0100 Subject: [PATCH 071/585] Issue 776 - Check if we're still collecting in the listen handler (#823) --- src/LaravelDebugbar.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 8bcca42c9..6d52fd394 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -319,6 +319,9 @@ function ($level, $message = null, $context = null) use ($logger) { try { $db->listen( function ($query, $bindings = null, $time = null, $connectionName = null) use ($db, $queryCollector) { + if ($this->shouldCollect('db', true)) { + return; // Issue 776 : We've turned off collecting after the listener was attached + } // Laravel 5.2 changed the way some core events worked. We must account for // the first argument being an "event object", where arguments are passed // via object properties, instead of individual arguments. From b2d09d5ca5118a620bc4c8c626b9f9c3295ac455 Mon Sep 17 00:00:00 2001 From: Miguel Piedrafita Date: Sun, 22 Apr 2018 22:18:51 +0200 Subject: [PATCH 072/585] Update license year (#766) And prevent further updates --- LICENSE | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 1b751326e..f724f7c29 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (C) 2013-2014 Barry vd. Heuvel +Copyright (C) 2013-present Barry vd. Heuvel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in @@ -16,4 +16,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. From 0858cd56f6587e26519e37bdc94d6bd96de3c878 Mon Sep 17 00:00:00 2001 From: tomzx Date: Fri, 27 Apr 2018 05:20:51 -0400 Subject: [PATCH 073/585] Extract exclusion logic to a separate function. (#826) Normalize the path to use the / separator to simplify logic and avoid usage of DIRECTORY_SEPARATOR. --- src/DataCollector/QueryCollector.php | 38 ++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 326258f40..211c5f1ea 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -218,16 +218,9 @@ protected function parseTrace($index, array $trace) return $frame; } - if (isset($trace['class']) && isset($trace['file']) && strpos( - $trace['file'], - DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'laravel' . DIRECTORY_SEPARATOR . 'framework/src/Illuminate/Database' - ) === false && strpos( - $trace['file'], - DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'laravel' . DIRECTORY_SEPARATOR . 'framework/src/Illuminate/Events' - ) === false && strpos( - $trace['file'], - DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'barryvdh' . DIRECTORY_SEPARATOR . 'laravel-debugbar' - ) === false + if (isset($trace['class']) && + isset($trace['file']) && + !$this->fileIsInExcludedPath($trace['file']) ) { $file = $trace['file']; @@ -264,6 +257,31 @@ protected function parseTrace($index, array $trace) return false; } + /** + * Check if the given file is to be excluded from analysis + * + * @param string $file + * @return bool + */ + protected function fileIsInExcludedPath($file) + { + $excludedPaths = [ + '/vendor/laravel/framework/src/Illuminate/Database', + '/vendor/laravel/framework/src/Illuminate/Events', + '/vendor/barryvdh/laravel-debugbar', + ]; + + $normalizedPath = str_replace('\\', '/', $file); + + foreach ($excludedPaths as $excludedPath) { + if (strpos($normalizedPath, $excludedPath) !== false) { + return true; + } + } + + return false; + } + /** * Find the middleware alias from the file. * From d3cdca2ad6cc6e67735b4a63e7551c690a497f5f Mon Sep 17 00:00:00 2001 From: Sije Harkema Date: Thu, 3 May 2018 20:27:04 +0200 Subject: [PATCH 074/585] Fix no queries being collected anymore (#827) Missing negation --- src/LaravelDebugbar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 6d52fd394..5ad17d307 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -319,7 +319,7 @@ function ($level, $message = null, $context = null) use ($logger) { try { $db->listen( function ($query, $bindings = null, $time = null, $connectionName = null) use ($db, $queryCollector) { - if ($this->shouldCollect('db', true)) { + if (!$this->shouldCollect('db', true)) { return; // Issue 776 : We've turned off collecting after the listener was attached } // Laravel 5.2 changed the way some core events worked. We must account for From e123836dbb9a6ff6ab4413cd47414d268098a877 Mon Sep 17 00:00:00 2001 From: Toshiyuki Goto Date: Wed, 25 Jul 2018 17:44:26 +0900 Subject: [PATCH 075/585] ignore explain query (#711) (#852) --- src/Support/Clockwork/Converter.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Support/Clockwork/Converter.php b/src/Support/Clockwork/Converter.php index e6473d972..409cdf846 100644 --- a/src/Support/Clockwork/Converter.php +++ b/src/Support/Clockwork/Converter.php @@ -91,6 +91,9 @@ public function convert($data) if (isset($data['queries'])) { $queries = $data['queries']; foreach($queries['statements'] as $statement){ + if ($statement['type'] === 'explain') { + continue; + } $output['databaseQueries'][] = [ 'query' => $statement['sql'], 'bindings' => $statement['params'], From e359a7627f90a8da05ac0ace00d6277cadf12924 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 2 Aug 2018 12:54:48 +0200 Subject: [PATCH 076/585] Update composer.json --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index c96ac7dff..49b2f8c61 100644 --- a/composer.json +++ b/composer.json @@ -12,9 +12,9 @@ "require": { "php": ">=7.0", "maximebf/debugbar": "~1.15.0", - "illuminate/routing": "5.5.x|5.6.x", - "illuminate/session": "5.5.x|5.6.x", - "illuminate/support": "5.5.x|5.6.x", + "illuminate/routing": "5.5.x|5.6.x|5.7.x", + "illuminate/session": "5.5.x|5.6.x|5.7.x", + "illuminate/support": "5.5.x|5.6.x|5.7.x", "symfony/debug": "^3|^4", "symfony/finder": "^3|^4" }, From 59d310c332571cbafd9206261140a2daeb9980f3 Mon Sep 17 00:00:00 2001 From: Freek Van der Herten Date: Thu, 16 Aug 2018 12:07:26 +0200 Subject: [PATCH 077/585] Fix dev dependency (#859) --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 49b2f8c61..d3db379b3 100644 --- a/composer.json +++ b/composer.json @@ -42,6 +42,6 @@ } }, "require-dev": { - "illuminate/framework": "5.5.x" + "laravel/framework": "5.5.x" } } From 5b68f3972083a7eeec0d6f161962fcda71a127c0 Mon Sep 17 00:00:00 2001 From: IT Man Date: Wed, 22 Aug 2018 13:06:19 +0200 Subject: [PATCH 078/585] Fix for policies (#860) Policies cannot be used now due to this reason. And additionally, the method `Auth::user()` returns the class `Authenticatable` (which is used in gates), not `Authorizable`. Also, I believe the class `Authenticatable` should be used since it is checking its instance on the line 33. --- src/DataCollector/GateCollector.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/DataCollector/GateCollector.php b/src/DataCollector/GateCollector.php index 5e6ad850b..ecc029d85 100644 --- a/src/DataCollector/GateCollector.php +++ b/src/DataCollector/GateCollector.php @@ -5,7 +5,6 @@ use Barryvdh\Debugbar\DataFormatter\SimpleFormatter; use DebugBar\DataCollector\MessagesCollector; use Illuminate\Contracts\Auth\Access\Gate; -use Illuminate\Contracts\Auth\Access\Authorizable; use Illuminate\Contracts\Auth\Authenticatable; use Symfony\Component\VarDumper\Cloner\VarCloner; @@ -24,7 +23,7 @@ public function __construct(Gate $gate) $gate->after([$this, 'addCheck']); } - public function addCheck(Authorizable $user = null, $ability, $result, $arguments = []) + public function addCheck(Authenticatable $user = null, $ability, $result, $arguments = []) { $userKey = 'user'; $userId = null; From 706192dceff809e292664cd59c7dbb96fada2842 Mon Sep 17 00:00:00 2001 From: Ken Andries Date: Fri, 28 Sep 2018 14:20:10 +0200 Subject: [PATCH 079/585] Fix call GateCollector using callback instead of array (#867) This fixes #862 & #863 Pre Laravel 5.7 the the `$gate->after` would not explicity check for optional / null. Laravel 5.7 the Gate class in Laravel will explicity check the callback signature this does not work with array arguments. Gate Class (Laravel 5.7) ```php protected function callbackAllowsGuests($callback) { $parameters = (new ReflectionFunction($callback))->getParameters(); return isset($parameters[0]) && $this->parameterAllowsGuests($parameters[0]); } ``` The `$callable` will be array as argument, The `ReflectionFunction` can only take callable argument. The reason we get this far into the code and is not cuaght by the Gate contract is the fact that the contract expects a `callable` type since an array is considered callable if you use a object reference as the first argument and string argument as second argument as method name. This is a bit of breaking change between Laravel 5.6 - Laravel 5.6. My fix is backwards compatible with Laravel 5.6. --- src/DataCollector/GateCollector.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/DataCollector/GateCollector.php b/src/DataCollector/GateCollector.php index ecc029d85..3e7c0a0d1 100644 --- a/src/DataCollector/GateCollector.php +++ b/src/DataCollector/GateCollector.php @@ -20,7 +20,9 @@ public function __construct(Gate $gate) { parent::__construct('gate'); $this->setDataFormatter(new SimpleFormatter()); - $gate->after([$this, 'addCheck']); + $gate->after(function (Authenticatable $user = null, $ability, $result, $arguments = []) { + $this->addCheck($user, $ability, $result, $arguments); + }); } public function addCheck(Authenticatable $user = null, $ability, $result, $arguments = []) From e32e130750a369a69320710b2b3d9d55e667276c Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Tue, 23 Oct 2018 22:01:06 +0200 Subject: [PATCH 080/585] Telescope compatibility (#883) * Update BaseController.php * Set telescope to default except --- config/debugbar.php | 2 +- src/Controllers/BaseController.php | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/config/debugbar.php b/config/debugbar.php index 8350e1c8d..c7c9ceee3 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -16,7 +16,7 @@ 'enabled' => env('DEBUGBAR_ENABLED', null), 'except' => [ - // + 'telescope*' ], /* diff --git a/src/Controllers/BaseController.php b/src/Controllers/BaseController.php index 3c0d08b41..36dfe5b4d 100644 --- a/src/Controllers/BaseController.php +++ b/src/Controllers/BaseController.php @@ -3,6 +3,7 @@ use Barryvdh\Debugbar\LaravelDebugbar; use Illuminate\Routing\Controller; use Illuminate\Http\Request; +use Laravel\Telescope\Telescope; if (class_exists('Illuminate\Routing\Controller')) { @@ -17,6 +18,13 @@ public function __construct(Request $request, LaravelDebugbar $debugbar) if ($request->hasSession()){ $request->session()->reflash(); } + + $this->middleware(function ($request, $next) { + if (class_exists(Telescope::class)) { + Telescope::stopRecording(); + } + return $next($request); + }); } } From d7815315b2f784ffefb5147d6bfc1980b1b9477e Mon Sep 17 00:00:00 2001 From: Adam Vercimak Date: Tue, 23 Oct 2018 15:04:17 -0500 Subject: [PATCH 081/585] Mimic bindValue and only quote non-integer and non-float data type (#868) --- src/DataCollector/QueryCollector.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 211c5f1ea..14f3cc0e0 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -109,7 +109,13 @@ public function addQuery($query, $bindings, $time, $connection) $regex = is_numeric($key) ? "/\?(?=(?:[^'\\\']*'[^'\\\']*')*[^'\\\']*$)/" : "/:{$key}(?=(?:[^'\\\']*'[^'\\\']*')*[^'\\\']*$)/"; - $query = preg_replace($regex, $pdo->quote($binding), $query, 1); + + // Mimic bindValue and only quote non-integer and non-float data types + if (!is_int($binding) && !is_float($binding)) { + $binding = $pdo->quote($binding); + } + + $query = preg_replace($regex, $binding, $query, 1); } } From 61f3be9042f195327649f95e2b96850d20d529e4 Mon Sep 17 00:00:00 2001 From: Tom Lankhorst Date: Tue, 23 Oct 2018 22:04:53 +0200 Subject: [PATCH 082/585] Remove .pull-left, .pull-right from FA css (#878) Avoid polluting CSS's 'global namespace' --- src/Resources/vendor/font-awesome/style.css | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Resources/vendor/font-awesome/style.css b/src/Resources/vendor/font-awesome/style.css index 6fc6c340a..1a3fc43ec 100644 --- a/src/Resources/vendor/font-awesome/style.css +++ b/src/Resources/vendor/font-awesome/style.css @@ -63,12 +63,6 @@ border: solid 0.08em #eeeeee; border-radius: .1em; } -.pull-right { - float: right; -} -.pull-left { - float: left; -} .phpdebugbar-fa.pull-left { margin-right: .3em; } From 0e4c5fada69ccbfca06ea6b9e9e797ac2dec745a Mon Sep 17 00:00:00 2001 From: Hiroshi Onozaki Date: Wed, 24 Oct 2018 05:05:23 +0900 Subject: [PATCH 083/585] Fix explain mode regex #363 (#876) --- src/DataCollector/QueryCollector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 14f3cc0e0..5aa239db0 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -94,7 +94,7 @@ public function addQuery($query, $bindings, $time, $connection) $bindings = $connection->prepareBindings($bindings); // Run EXPLAIN on this query (if needed) - if ($this->explainQuery && preg_match('/^('.implode($this->explainTypes).') /i', $query)) { + if ($this->explainQuery && preg_match('/^\s*('.implode('|', $this->explainTypes).') /i', $query)) { $statement = $pdo->prepare('EXPLAIN ' . $query); $statement->execute($bindings); $explainResults = $statement->fetchAll(\PDO::FETCH_CLASS); From 4c2e35ece1e456835721489321f99d8e21dba599 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Tue, 23 Oct 2018 22:22:52 +0200 Subject: [PATCH 084/585] Tweak RequestCollector, use HTML + VarDumper --- src/DataCollector/RequestCollector.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/DataCollector/RequestCollector.php b/src/DataCollector/RequestCollector.php index 25bada3c2..30c5e75e9 100644 --- a/src/DataCollector/RequestCollector.php +++ b/src/DataCollector/RequestCollector.php @@ -51,7 +51,7 @@ public function getWidgets() return [ "request" => [ "icon" => "tags", - "widget" => "PhpDebugBar.Widgets.VariableListWidget", + "widget" => "PhpDebugBar.Widgets.HtmlVariableListWidget", "map" => "request", "default" => "{}" ] @@ -86,19 +86,20 @@ public function collect() $statusCode = $response->getStatusCode(); $data = [ + 'path_info' => $request->getPathInfo(), + 'status_code' => $statusCode, + 'status_text' => isset(Response::$statusTexts[$statusCode]) ? Response::$statusTexts[$statusCode] : '', 'format' => $request->getRequestFormat(), 'content_type' => $response->headers->get('Content-Type') ? $response->headers->get( 'Content-Type' ) : 'text/html', - 'status_text' => isset(Response::$statusTexts[$statusCode]) ? Response::$statusTexts[$statusCode] : '', - 'status_code' => $statusCode, 'request_query' => $request->query->all(), 'request_request' => $request->request->all(), 'request_headers' => $request->headers->all(), 'request_server' => $request->server->all(), 'request_cookies' => $request->cookies->all(), 'response_headers' => $responseHeaders, - 'path_info' => $request->getPathInfo(), + ]; if ($this->session) { @@ -123,11 +124,15 @@ public function collect() if (isset($data['request_server']['PHP_AUTH_PW'])) { $data['request_server']['PHP_AUTH_PW'] = '******'; } + ; foreach ($data as $key => $var) { if (!is_string($data[$key])) { - $data[$key] = $this->formatVar($var); + $data[$key] = DataCollector::getDefaultVarDumper()->renderVar($var); + } else { + $data[$key] = e($data[$key]); } + } return $data; From 9d5caf43c5f3a3aea2178942f281054805872e7c Mon Sep 17 00:00:00 2001 From: Roman Senchuk Date: Fri, 9 Nov 2018 10:37:55 +0200 Subject: [PATCH 085/585] \Exception isn't well with newer PHP (#886) --- src/DataCollector/MultiAuthCollector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DataCollector/MultiAuthCollector.php b/src/DataCollector/MultiAuthCollector.php index 59722c763..d2c31ee85 100644 --- a/src/DataCollector/MultiAuthCollector.php +++ b/src/DataCollector/MultiAuthCollector.php @@ -119,7 +119,7 @@ protected function getUserInformation($user = null) } elseif ($user->email) { $identifier = $user->email; } - } catch (\Exception $e) { + } catch (\Throwable $e) { } } From 6a42db246fe34f8fb5fdc179fc8e70d5b4fbe72e Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 15 Nov 2018 16:45:44 +0100 Subject: [PATCH 086/585] Telescope link (#884) * Create TelescopeController.php * Add telescope route * Add telescope link * Add requestId * Update LaravelDebugbar.php --- src/Controllers/TelescopeController.php | 22 ++++++++++++++++++++++ src/DataCollector/RequestCollector.php | 20 +++++++++++++++++--- src/LaravelDebugbar.php | 2 +- src/ServiceProvider.php | 5 +++++ 4 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 src/Controllers/TelescopeController.php diff --git a/src/Controllers/TelescopeController.php b/src/Controllers/TelescopeController.php new file mode 100644 index 000000000..e98118eda --- /dev/null +++ b/src/Controllers/TelescopeController.php @@ -0,0 +1,22 @@ +find($uuid); + $result = $storage->get('request', (new EntryQueryOptions())->batchId($entry->batchId))->first(); + + return redirect(config('telescope.path') . '/requests/' . $result->id); + } +} diff --git a/src/DataCollector/RequestCollector.php b/src/DataCollector/RequestCollector.php index 30c5e75e9..a8abc8677 100644 --- a/src/DataCollector/RequestCollector.php +++ b/src/DataCollector/RequestCollector.php @@ -5,6 +5,8 @@ use DebugBar\DataCollector\DataCollector; use DebugBar\DataCollector\DataCollectorInterface; use DebugBar\DataCollector\Renderable; +use Laravel\Telescope\IncomingEntry; +use Laravel\Telescope\Telescope; use Symfony\Component\HttpFoundation\Response; /** @@ -20,6 +22,8 @@ class RequestCollector extends DataCollector implements DataCollectorInterface, protected $response; /** @var \Symfony\Component\HttpFoundation\Session\SessionInterface $session */ protected $session; + /** @var string|null */ + protected $currentRequestId; /** * Create a new SymfonyRequestCollector @@ -28,11 +32,12 @@ class RequestCollector extends DataCollector implements DataCollectorInterface, * @param \Symfony\Component\HttpFoundation\Request $response * @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session */ - public function __construct($request, $response, $session = null) + public function __construct($request, $response, $session = null, $currentRequestId = null) { $this->request = $request; $this->response = $response; $this->session = $session; + $this->currentRequestId = $currentRequestId; } /** @@ -99,7 +104,6 @@ public function collect() 'request_server' => $request->server->all(), 'request_cookies' => $request->cookies->all(), 'response_headers' => $responseHeaders, - ]; if ($this->session) { @@ -135,7 +139,17 @@ public function collect() } - return $data; + $htmlData = []; + if (class_exists(Telescope::class)) { + $entry = IncomingEntry::make([ + 'requestId' => $this->currentRequestId, + ])->type('debugbar'); + Telescope::$entriesQueue[] = $entry; + $url = route('debugbar.telescope', [$entry->uuid]); + $htmlData['telescope'] = 'View in Telescope'; + } + + return $htmlData + $data; } private function getCookieHeader($name, $value, $expires, $path, $domain, $secure, $httponly) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 5ad17d307..2878767ed 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -669,7 +669,7 @@ public function modifyResponse(Request $request, Response $response) if ($this->shouldCollect('symfony_request', true) && !$this->hasCollector('request')) { try { - $this->addCollector(new RequestCollector($request, $response, $sessionManager)); + $this->addCollector(new RequestCollector($request, $response, $sessionManager, $this->getCurrentRequestId())); } catch (\Exception $e) { $this->addThrowable( new Exception( diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index f86b593ac..9078fe8e5 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -84,6 +84,11 @@ public function boot() 'as' => 'debugbar.clockwork', ]); + $router->get('telescope/{id}', [ + 'uses' => 'TelescopeController@show', + 'as' => 'debugbar.telescope', + ]); + $router->get('assets/stylesheets', [ 'uses' => 'AssetController@css', 'as' => 'debugbar.assets.css', From 94a12a5c27119e46e15cb96a31eb0f7670828d44 Mon Sep 17 00:00:00 2001 From: Pavlo Zhukov Date: Wed, 28 Nov 2018 16:27:04 +0200 Subject: [PATCH 087/585] Fix exception on fetching Auth api user info (#889) * Fix exception on fetching Auth api user info I noticed such exception in my log during debugging API queries ``` [2018-11-23 01:25:43] local.ERROR: Debugbar exception: Method Illuminate\Database\Query\Builder::getAuthIdentifier does not exist. ``` So I've found a problem and fixed it * Add missed use --- src/DataCollector/MultiAuthCollector.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/DataCollector/MultiAuthCollector.php b/src/DataCollector/MultiAuthCollector.php index d2c31ee85..e5e1259a2 100644 --- a/src/DataCollector/MultiAuthCollector.php +++ b/src/DataCollector/MultiAuthCollector.php @@ -5,6 +5,7 @@ use DebugBar\DataCollector\DataCollector; use DebugBar\DataCollector\Renderable; use Illuminate\Auth\SessionGuard; +use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Auth\Guard; use Illuminate\Support\Str; use Illuminate\Contracts\Support\Arrayable; @@ -111,7 +112,7 @@ protected function getUserInformation($user = null) // The default auth identifer is the ID number, which isn't all that // useful. Try username and email. - $identifier = $user->getAuthIdentifier(); + $identifier = $user instanceof Authenticatable ? $user->getAuthIdentifier() : $user->id; if (is_numeric($identifier)) { try { if ($user->username) { From d438953c7f329996860695eba422d6e3ce95656e Mon Sep 17 00:00:00 2001 From: Orkhan Alikhanov Date: Tue, 29 Jan 2019 01:04:51 +0400 Subject: [PATCH 088/585] Allow non-Authenticatable gate users (#895) --- src/DataCollector/GateCollector.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DataCollector/GateCollector.php b/src/DataCollector/GateCollector.php index 3e7c0a0d1..aaac67937 100644 --- a/src/DataCollector/GateCollector.php +++ b/src/DataCollector/GateCollector.php @@ -20,12 +20,12 @@ public function __construct(Gate $gate) { parent::__construct('gate'); $this->setDataFormatter(new SimpleFormatter()); - $gate->after(function (Authenticatable $user = null, $ability, $result, $arguments = []) { + $gate->after(function ($user = null, $ability, $result, $arguments = []) { $this->addCheck($user, $ability, $result, $arguments); }); } - public function addCheck(Authenticatable $user = null, $ability, $result, $arguments = []) + public function addCheck($user = null, $ability, $result, $arguments = []) { $userKey = 'user'; $userId = null; From f0184766a907cdf1f491a3dc4ce94f1fa6deaf4d Mon Sep 17 00:00:00 2001 From: Jaspar Gupta Date: Mon, 28 Jan 2019 21:05:13 +0000 Subject: [PATCH 089/585] Add method annotations to facade for IDE suggestions (#892) * docs: add IDE method suggestions to facade * docs: add addMessage method annotation --- src/Facade.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/Facade.php b/src/Facade.php index d2bbcf5eb..9c1f75926 100644 --- a/src/Facade.php +++ b/src/Facade.php @@ -1,5 +1,22 @@ Date: Mon, 4 Feb 2019 19:23:43 +0900 Subject: [PATCH 090/585] In case of insert/update/delete query, do not explain #888 (#903) *workaround* --- config/debugbar.php | 2 +- src/DataCollector/QueryCollector.php | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/config/debugbar.php b/config/debugbar.php index c7c9ceee3..72ccd7bd3 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -143,7 +143,7 @@ 'timeline' => false, // Add the queries to the timeline 'explain' => [ // Show EXPLAIN output on queries 'enabled' => false, - 'types' => ['SELECT'], // ['SELECT', 'INSERT', 'UPDATE', 'DELETE']; for MySQL 5.6.3+ + 'types' => ['SELECT'], // // workaround ['SELECT'] only. https://github.com/barryvdh/laravel-debugbar/issues/888 ['SELECT', 'INSERT', 'UPDATE', 'DELETE']; for MySQL 5.6.3+ ], 'hints' => true, // Show hints for common mistakes ], diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 5aa239db0..1686744f6 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -70,9 +70,10 @@ public function setFindSource($value, array $middleware) public function setExplainSource($enabled, $types) { $this->explainQuery = $enabled; - if($types){ - $this->explainTypes = $types; - } + // workaround ['SELECT'] only. https://github.com/barryvdh/laravel-debugbar/issues/888 +// if($types){ +// $this->explainTypes = $types; +// } } /** From f78618a0ea1110aedfcbb98db7fecf657945baa2 Mon Sep 17 00:00:00 2001 From: Vaios Karampinis Date: Mon, 18 Feb 2019 21:51:47 +0200 Subject: [PATCH 091/585] Dont register routes and middleware if we are not in debug mode (#906) --- src/ServiceProvider.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 9078fe8e5..966a54746 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -63,6 +63,10 @@ function ($app) { */ public function boot() { + if (!$this->app['config']->get('app.debug')) { + return; + } + $configPath = __DIR__ . '/../config/debugbar.php'; $this->publishes([$configPath => $this->getConfigPath()], 'config'); From 5fcba4cc8e92a230b13b99c1083fc22ba8a5c479 Mon Sep 17 00:00:00 2001 From: George Hanson Date: Tue, 26 Feb 2019 18:01:54 +0000 Subject: [PATCH 092/585] Add Laravel 5.8 support (#911) --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index d3db379b3..f5bdc6fbe 100644 --- a/composer.json +++ b/composer.json @@ -12,9 +12,9 @@ "require": { "php": ">=7.0", "maximebf/debugbar": "~1.15.0", - "illuminate/routing": "5.5.x|5.6.x|5.7.x", - "illuminate/session": "5.5.x|5.6.x|5.7.x", - "illuminate/support": "5.5.x|5.6.x|5.7.x", + "illuminate/routing": "5.5.x|5.6.x|5.7.x|5.8.x", + "illuminate/session": "5.5.x|5.6.x|5.7.x|5.8.x", + "illuminate/support": "5.5.x|5.6.x|5.7.x|5.8.x", "symfony/debug": "^3|^4", "symfony/finder": "^3|^4" }, From b1fe6d2342a735ad5915db1912fe8458692ead39 Mon Sep 17 00:00:00 2001 From: Vaios Karampinis Date: Tue, 26 Feb 2019 22:02:18 +0200 Subject: [PATCH 093/585] Avoid to hook debugbar in response if its an ajax request (#910) * Avoid to hook debugbar in response if its an ajax request * move check if its an ajax call inside modifyResponse * use isJsonRequest($request) to check for ajax call --- src/LaravelDebugbar.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 2878767ed..26f14c329 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -723,6 +723,7 @@ public function modifyResponse(Request $request, Response $response) strpos($response->headers->get('Content-Type'), 'html') === false) || $request->getRequestFormat() !== 'html' || $response->getContent() === false + || $this->isJsonRequest($request) ) { try { // Just collect + store data, don't inject it. From 2d195779ea4f809f69764a795e2ec371dbb76a96 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Mon, 25 Mar 2019 10:39:08 +0100 Subject: [PATCH 094/585] Revert "Dont register routes and middleware if we are not in debug mode (#906)" (#918) This reverts commit f78618a0ea1110aedfcbb98db7fecf657945baa2. --- src/ServiceProvider.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 966a54746..9078fe8e5 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -63,10 +63,6 @@ function ($app) { */ public function boot() { - if (!$this->app['config']->get('app.debug')) { - return; - } - $configPath = __DIR__ . '/../config/debugbar.php'; $this->publishes([$configPath => $this->getConfigPath()], 'config'); From 81f988f983f4e970b5a02d634a93c20773e0659b Mon Sep 17 00:00:00 2001 From: zhuston Date: Tue, 23 Jul 2019 11:59:21 -0400 Subject: [PATCH 095/585] db.backtrace_exclude_paths config added to allow additional exclude paths --- config/debugbar.php | 1 + src/DataCollector/QueryCollector.php | 23 ++++++++++++++++------- src/LaravelDebugbar.php | 5 +++++ 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/config/debugbar.php b/config/debugbar.php index 72ccd7bd3..5db74198d 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -140,6 +140,7 @@ 'db' => [ 'with_params' => true, // Render SQL with the parameters substituted 'backtrace' => true, // Use a backtrace to find the origin of the query in your files. + 'backtrace_exclude_paths' => [], // Paths to exclude from backtrace. (in addition to defaults) 'timeline' => false, // Add the queries to the timeline 'explain' => [ // Show EXPLAIN output on queries 'enabled' => false, diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 1686744f6..5a96fe6a5 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -19,6 +19,11 @@ class QueryCollector extends PDOCollector protected $explainTypes = ['SELECT']; // ['SELECT', 'INSERT', 'UPDATE', 'DELETE']; for MySQL 5.6.3+ protected $showHints = false; protected $reflection = []; + protected $backtraceExcludePaths = [ + '/vendor/laravel/framework/src/Illuminate/Database', + '/vendor/laravel/framework/src/Illuminate/Events', + '/vendor/barryvdh/laravel-debugbar', + ]; /** * @param TimeDataCollector $timeCollector @@ -61,6 +66,16 @@ public function setFindSource($value, array $middleware) $this->middleware = $middleware; } + /** + * Set additional paths to exclude from the backtrace + * + * @param array $excludePaths Array of file paths to exclude from backtrace + */ + public function mergeBacktraceExcludePaths(array $excludePaths) + { + $this->backtraceExcludePaths = array_merge($this->backtraceExcludePaths, $excludePaths); + } + /** * Enable/disable the EXPLAIN queries * @@ -272,15 +287,9 @@ protected function parseTrace($index, array $trace) */ protected function fileIsInExcludedPath($file) { - $excludedPaths = [ - '/vendor/laravel/framework/src/Illuminate/Database', - '/vendor/laravel/framework/src/Illuminate/Events', - '/vendor/barryvdh/laravel-debugbar', - ]; - $normalizedPath = str_replace('\\', '/', $file); - foreach ($excludedPaths as $excludedPath) { + foreach ($this->backtraceExcludePaths as $excludedPath) { if (strpos($normalizedPath, $excludedPath) !== false) { return true; } diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 26f14c329..36f2b5711 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -305,6 +305,11 @@ function ($level, $message = null, $context = null) use ($logger) { $queryCollector->setFindSource(true, $middleware); } + if ($this->app['config']->get('debugbar.options.db.backtrace_exclude_paths')) { + $excludePaths = $this->app['config']->get('debugbar.options.db.backtrace_exclude_paths'); + $queryCollector->mergeBacktraceExcludePaths($excludePaths); + } + if ($this->app['config']->get('debugbar.options.db.explain.enabled')) { $types = $this->app['config']->get('debugbar.options.db.explain.types'); $queryCollector->setExplainSource(true, $types); From bda348f9105a3cdd6dd9bd3966298b69fdd1dfcc Mon Sep 17 00:00:00 2001 From: Jonathan Reinink Date: Mon, 29 Jul 2019 16:00:51 -0400 Subject: [PATCH 096/585] Add new models collector (#947) --- config/debugbar.php | 5 ++- src/DataCollector/ModelsCollector.php | 54 +++++++++++++++++++++++++++ src/LaravelDebugbar.php | 10 +++++ 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 src/DataCollector/ModelsCollector.php diff --git a/config/debugbar.php b/config/debugbar.php index 72ccd7bd3..46efd3f3f 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -9,7 +9,7 @@ | | Debugbar is enabled by default, when debug is set to true in app.php. | You can override the value by setting enable to true or false instead of null. - | + | | You can provide an array of URI's that must be ignored (eg. 'api/*') | */ @@ -79,7 +79,7 @@ | */ 'error_handler' => false, - + /* |-------------------------------------------------------------------------- | Clockwork integration @@ -122,6 +122,7 @@ 'files' => false, // Show the included files 'config' => false, // Display config settings 'cache' => false, // Display cache events + 'models' => false, // Display models ], /* diff --git a/src/DataCollector/ModelsCollector.php b/src/DataCollector/ModelsCollector.php new file mode 100644 index 000000000..8ac07851a --- /dev/null +++ b/src/DataCollector/ModelsCollector.php @@ -0,0 +1,54 @@ +setDataFormatter(new SimpleFormatter()); + + $events->listen('eloquent.*', function ($event, $models) { + if (Str::contains($event, 'eloquent.retrieved')) { + foreach ($models as $model) { + $class = get_class($model); + $this->models[$class] = ($this->models[$class] ?? 0) + 1; + } + } + }); + } + + public function collect() + { + foreach ($this->models as $type => $count) { + $this->addMessage($count, $type); + } + + return [ + 'count' => array_sum($this->models), + 'messages' => $this->getMessages(), + ]; + } + + public function getWidgets() + { + $widgets = parent::getWidgets(); + $widgets['models']['icon'] = 'cubes'; + + return $widgets; + } +} diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 26f14c329..06e7bb9ce 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -7,6 +7,7 @@ use Barryvdh\Debugbar\DataCollector\GateCollector; use Barryvdh\Debugbar\DataCollector\LaravelCollector; use Barryvdh\Debugbar\DataCollector\LogsCollector; +use Barryvdh\Debugbar\DataCollector\ModelsCollector; use Barryvdh\Debugbar\DataCollector\MultiAuthCollector; use Barryvdh\Debugbar\DataCollector\QueryCollector; use Barryvdh\Debugbar\DataCollector\SessionCollector; @@ -404,6 +405,15 @@ function ($query, $bindings = null, $time = null, $connectionName = null) use ($ } } + if ($this->shouldCollect('models', false)) { + try { + $modelsCollector = $this->app->make('Barryvdh\Debugbar\DataCollector\ModelsCollector'); + $this->addCollector($modelsCollector); + } catch (\Exception $e){ + // No Models collector + } + } + if ($this->shouldCollect('mail', true) && class_exists('Illuminate\Mail\MailServiceProvider')) { try { $mailer = $this->app['mailer']->getSwiftMailer(); From 0a6659abc27eb74e377907f80c03a7948a34e4b8 Mon Sep 17 00:00:00 2001 From: Stoyan Kyosev Date: Mon, 5 Aug 2019 18:43:41 +0300 Subject: [PATCH 097/585] Fix session destroy when using multiple guards (#948) Related to #856 It seems the recaller logic is outdated as it leads to session destroy (and thus csrf token regeneration) when using multi-auth. It fails to retrieve the guard from the cookie, which leads to $guard->user() call and session migration. --- src/DataCollector/MultiAuthCollector.php | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/DataCollector/MultiAuthCollector.php b/src/DataCollector/MultiAuthCollector.php index e5e1259a2..db3649c2e 100644 --- a/src/DataCollector/MultiAuthCollector.php +++ b/src/DataCollector/MultiAuthCollector.php @@ -81,18 +81,14 @@ private function resolveUser(Guard $guard) // if we're logging in using remember token // then we must resolve user „manually” // to prevent csrf token regeneration - + $recaller = $guard instanceof SessionGuard - ? $guard->getRequest()->cookies->get($guard->getRecallerName()) + ? new Recaller($guard->getRequest()->cookies->get($guard->getRecallerName())) : null; - if (is_string($recaller) && Str::contains($recaller, '|')) { - $segments = explode('|', $recaller); - if (count($segments) == 2 && trim($segments[0]) !== '' && trim($segments[1]) !== '') { - return $guard->getProvider()->retrieveByToken($segments[0], $segments[1]); - } - } - return $guard->user(); + return !is_null($recaller) && !is_null($user = $this->provider->retrieveByToken( + $recaller->id(), $recaller->token() + )) ? $user : $guard->user(); } /** From 9b08d61d7f60cb218a05194caa3e3440c4f8bb53 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Mon, 5 Aug 2019 18:05:42 +0200 Subject: [PATCH 098/585] Update MultiAuthCollector.php --- src/DataCollector/MultiAuthCollector.php | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/DataCollector/MultiAuthCollector.php b/src/DataCollector/MultiAuthCollector.php index db3649c2e..2a100a3bf 100644 --- a/src/DataCollector/MultiAuthCollector.php +++ b/src/DataCollector/MultiAuthCollector.php @@ -4,6 +4,7 @@ use DebugBar\DataCollector\DataCollector; use DebugBar\DataCollector\Renderable; +use Illuminate\Auth\Recaller; use Illuminate\Auth\SessionGuard; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Auth\Guard; @@ -21,6 +22,7 @@ class MultiAuthCollector extends DataCollector implements Renderable /** @var \Illuminate\Auth\AuthManager */ protected $auth; + /** @var bool */ protected $showName = false; @@ -51,9 +53,9 @@ public function collect() $data = []; $names = ''; - foreach($this->guards as $guardName) { + foreach($this->guards as $guardName => $config) { try { - $user = $this->resolveUser($this->auth->guard($guardName)); + $user = $this->resolveUser($this->auth->guard($guardName), $config); } catch (\Exception $e) { continue; } @@ -76,19 +78,26 @@ public function collect() return $data; } - private function resolveUser(Guard $guard) + private function resolveUser(Guard $guard, array $config) { // if we're logging in using remember token // then we must resolve user „manually” // to prevent csrf token regeneration - + $recaller = $guard instanceof SessionGuard ? new Recaller($guard->getRequest()->cookies->get($guard->getRecallerName())) : null; - return !is_null($recaller) && !is_null($user = $this->provider->retrieveByToken( - $recaller->id(), $recaller->token() - )) ? $user : $guard->user(); + if ($recaller !== null) { + $provider = $this->auth->createUserProvider($config['provider']); + + $user = $provider->retrieveByToken($recaller->id(), $recaller->token()); + if ($user) { + return $user; + } + } + + return $guard->user(); } /** From 113db72fb3ed3b57fbbcdc4fdb0b27e4254f4d7d Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Mon, 5 Aug 2019 18:06:01 +0200 Subject: [PATCH 099/585] Update debugbar.php --- config/debugbar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/debugbar.php b/config/debugbar.php index 46efd3f3f..3275f105a 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -110,7 +110,7 @@ 'db' => true, // Show database (PDO) queries and bindings 'views' => true, // Views with their data 'route' => true, // Current route information - 'auth' => true, // Display Laravel authentication status + 'auth' => false, // Display Laravel authentication status 'gate' => true, // Display Laravel Gate checks 'session' => true, // Display session data 'symfony_request' => true, // Only one can be enabled.. From 6e20d8964c9a3b26090335e64a474f70e87a1116 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Mon, 5 Aug 2019 18:06:31 +0200 Subject: [PATCH 100/585] Update LaravelDebugbar.php --- src/LaravelDebugbar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 06e7bb9ce..5816f4b45 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -451,7 +451,7 @@ function ($query, $bindings = null, $time = null, $connectionName = null) use ($ if ($this->shouldCollect('auth', false)) { try { - $guards = array_keys($this->app['config']->get('auth.guards', [])); + $guards = $this->app['config']->get('auth.guards', []); $authCollector = new MultiAuthCollector($app['auth'], $guards); $authCollector->setShowName( From 66f39beaeab230c34bf504c2b42f51a3eef4c97f Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 5 Aug 2019 09:06:55 -0700 Subject: [PATCH 101/585] Update MultiAuthCollector.php (#939) Check if property exists --- src/DataCollector/MultiAuthCollector.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DataCollector/MultiAuthCollector.php b/src/DataCollector/MultiAuthCollector.php index db3649c2e..b373a4db5 100644 --- a/src/DataCollector/MultiAuthCollector.php +++ b/src/DataCollector/MultiAuthCollector.php @@ -111,9 +111,9 @@ protected function getUserInformation($user = null) $identifier = $user instanceof Authenticatable ? $user->getAuthIdentifier() : $user->id; if (is_numeric($identifier)) { try { - if ($user->username) { + if (isset($user->username)) { $identifier = $user->username; - } elseif ($user->email) { + } elseif (isset($user->email)) { $identifier = $user->email; } } catch (\Throwable $e) { From dbf6053604f3e0018ec15e38bc0f8de10dd702c5 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Mon, 5 Aug 2019 18:08:34 +0200 Subject: [PATCH 102/585] Update MultiAuthCollector.php --- src/DataCollector/MultiAuthCollector.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/DataCollector/MultiAuthCollector.php b/src/DataCollector/MultiAuthCollector.php index 2a100a3bf..7323e2793 100644 --- a/src/DataCollector/MultiAuthCollector.php +++ b/src/DataCollector/MultiAuthCollector.php @@ -83,12 +83,9 @@ private function resolveUser(Guard $guard, array $config) // if we're logging in using remember token // then we must resolve user „manually” // to prevent csrf token regeneration + if ($guard instanceof SessionGuard) { - $recaller = $guard instanceof SessionGuard - ? new Recaller($guard->getRequest()->cookies->get($guard->getRecallerName())) - : null; - - if ($recaller !== null) { + $recaller = new Recaller($guard->getRequest()->cookies->get($guard->getRecallerName())); $provider = $this->auth->createUserProvider($config['provider']); $user = $provider->retrieveByToken($recaller->id(), $recaller->token()); From 39956cd7fe8834d9aeb93d3cf5ea04208f64f388 Mon Sep 17 00:00:00 2001 From: Metallizzer Date: Tue, 20 Aug 2019 09:56:24 +0700 Subject: [PATCH 103/585] Update laravel-debugbar.css --- src/Resources/laravel-debugbar.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index d708aab14..b9e7e57a7 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -3,7 +3,7 @@ div.phpdebugbar { font-family: "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif; direction: ltr; text-align: left; - z-index: 100000; + z-index: 9999999999; } div.phpdebugbar-resize-handle { @@ -316,4 +316,4 @@ ul.phpdebugbar-widgets-cache a.phpdebugbar-widgets-forget { color: #fff; text-decoration: none; line-height: 1.5rem; -} \ No newline at end of file +} From e43aa86bb3a92cea79f84ab268fbd038c0869e63 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Wed, 21 Aug 2019 13:00:02 +0200 Subject: [PATCH 104/585] Update composer.json --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index f5bdc6fbe..a64f48968 100644 --- a/composer.json +++ b/composer.json @@ -12,9 +12,9 @@ "require": { "php": ">=7.0", "maximebf/debugbar": "~1.15.0", - "illuminate/routing": "5.5.x|5.6.x|5.7.x|5.8.x", - "illuminate/session": "5.5.x|5.6.x|5.7.x|5.8.x", - "illuminate/support": "5.5.x|5.6.x|5.7.x|5.8.x", + "illuminate/routing": "^5.5|^6", + "illuminate/session": "^5.5|^6", + "illuminate/support": "^5.5|^6", "symfony/debug": "^3|^4", "symfony/finder": "^3|^4" }, From 42a71087cdf244ca0e50b75728eb60d9739eb9f8 Mon Sep 17 00:00:00 2001 From: Martin Dilling-Hansen Date: Wed, 21 Aug 2019 13:46:41 +0200 Subject: [PATCH 105/585] Update docblocks to allow multiple arguments These methods does accept multiple arguments, but your IDE complains when you do :) --- src/LaravelDebugbar.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 5816f4b45..3aed504b2 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -41,15 +41,15 @@ * Debug bar subclass which adds all without Request and with LaravelCollector. * Rest is added in Service Provider * - * @method void emergency($message) - * @method void alert($message) - * @method void critical($message) - * @method void error($message) - * @method void warning($message) - * @method void notice($message) - * @method void info($message) - * @method void debug($message) - * @method void log($message) + * @method void emergency(...$message) + * @method void alert(...$message) + * @method void critical(...$message) + * @method void error(...$message) + * @method void warning(...$message) + * @method void notice(...$message) + * @method void info(...$message) + * @method void debug(...$message) + * @method void log(...$message) */ class LaravelDebugbar extends DebugBar { From 96e86c0da30c145f2662d888b24070df6abce7e4 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 22 Aug 2019 09:29:33 +0200 Subject: [PATCH 106/585] Update LaravelDebugbar.php --- src/LaravelDebugbar.php | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 3aed504b2..64954aad3 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -449,23 +449,23 @@ function ($query, $bindings = null, $time = null, $connectionName = null) use ($ $this->addCollector(new FilesCollector($app)); } - if ($this->shouldCollect('auth', false)) { - try { - $guards = $this->app['config']->get('auth.guards', []); - $authCollector = new MultiAuthCollector($app['auth'], $guards); - - $authCollector->setShowName( - $this->app['config']->get('debugbar.options.auth.show_name') - ); - $this->addCollector($authCollector); - } catch (\Exception $e) { - $this->addThrowable( - new Exception( - 'Cannot add AuthCollector to Laravel Debugbar: ' . $e->getMessage(), $e->getCode(), $e - ) - ); - } - } +// if ($this->shouldCollect('auth', false)) { +// try { +// $guards = $this->app['config']->get('auth.guards', []); +// $authCollector = new MultiAuthCollector($app['auth'], $guards); + +// $authCollector->setShowName( +// $this->app['config']->get('debugbar.options.auth.show_name') +// ); +// $this->addCollector($authCollector); +// } catch (\Exception $e) { +// $this->addThrowable( +// new Exception( +// 'Cannot add AuthCollector to Laravel Debugbar: ' . $e->getMessage(), $e->getCode(), $e +// ) +// ); +// } +// } if ($this->shouldCollect('gate', false)) { try { From bf19d9362d0efd1e97c67a76f13f8085953bbbbd Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 22 Aug 2019 09:52:30 +0200 Subject: [PATCH 107/585] Simplify Auth, only add user when already resolved --- src/DataCollector/MultiAuthCollector.php | 43 ++++++++++++------------ src/LaravelDebugbar.php | 34 +++++++++---------- 2 files changed, 39 insertions(+), 38 deletions(-) diff --git a/src/DataCollector/MultiAuthCollector.php b/src/DataCollector/MultiAuthCollector.php index 32cd197c8..f040e2b9a 100644 --- a/src/DataCollector/MultiAuthCollector.php +++ b/src/DataCollector/MultiAuthCollector.php @@ -50,21 +50,27 @@ public function setShowName($showName) */ public function collect() { - $data = []; + $data = [ + 'guards' => [], + ]; $names = ''; foreach($this->guards as $guardName => $config) { try { - $user = $this->resolveUser($this->auth->guard($guardName), $config); + $guard = $this->auth->guard($guardName); + if ($this->hasUser($guard)) { + $user = $guard->user(); + + if(!is_null($user)) { + $data['guards'][$guardName] = $this->getUserInformation($user); + $names .= $guardName . ": " . $data['guards'][$guardName]['name'] . ', '; + } + } else { + $data['guards'][$guardName] = null; + } } catch (\Exception $e) { continue; } - - $data['guards'][$guardName] = $this->getUserInformation($user); - - if(!is_null($user)) { - $names .= $guardName . ": " . $data['guards'][$guardName]['name'] . ', '; - } } foreach ($data['guards'] as $key => $var) { @@ -78,23 +84,18 @@ public function collect() return $data; } - private function resolveUser(Guard $guard, array $config) + private function hasUser(Guard $guard) { - // if we're logging in using remember token - // then we must resolve user „manually” - // to prevent csrf token regeneration - if ($guard instanceof SessionGuard) { - - $recaller = new Recaller($guard->getRequest()->cookies->get($guard->getRecallerName())); - $provider = $this->auth->createUserProvider($config['provider']); + if (method_exists($guard, 'hasUser')) { + return $guard->hasUser(); + } - $user = $provider->retrieveByToken($recaller->id(), $recaller->token()); - if ($user) { - return $user; - } + // For Laravel 5.5 + if (method_exists($guard, 'alreadyAuthenticated')) { + return $guard->alreadyAuthenticated(); } - return $guard->user(); + return false; } /** diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 64954aad3..e203b2197 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -449,23 +449,23 @@ function ($query, $bindings = null, $time = null, $connectionName = null) use ($ $this->addCollector(new FilesCollector($app)); } -// if ($this->shouldCollect('auth', false)) { -// try { -// $guards = $this->app['config']->get('auth.guards', []); -// $authCollector = new MultiAuthCollector($app['auth'], $guards); - -// $authCollector->setShowName( -// $this->app['config']->get('debugbar.options.auth.show_name') -// ); -// $this->addCollector($authCollector); -// } catch (\Exception $e) { -// $this->addThrowable( -// new Exception( -// 'Cannot add AuthCollector to Laravel Debugbar: ' . $e->getMessage(), $e->getCode(), $e -// ) -// ); -// } -// } + if ($this->shouldCollect('auth', false)) { + try { + $guards = $this->app['config']->get('auth.guards', []); + $authCollector = new MultiAuthCollector($app['auth'], $guards); + + $authCollector->setShowName( + $this->app['config']->get('debugbar.options.auth.show_name') + ); + $this->addCollector($authCollector); + } catch (\Exception $e) { + $this->addThrowable( + new Exception( + 'Cannot add AuthCollector to Laravel Debugbar: ' . $e->getMessage(), $e->getCode(), $e + ) + ); + } + } if ($this->shouldCollect('gate', false)) { try { From 3b2eb63d04762348324433bd1228a04e8d2776ae Mon Sep 17 00:00:00 2001 From: Brandon Surowiec Date: Thu, 22 Aug 2019 10:23:19 -0400 Subject: [PATCH 108/585] Prefer Str:: class for Laravel 6 compatability. --- src/DataCollector/RequestCollector.php | 5 +++-- src/LaravelDebugbar.php | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/DataCollector/RequestCollector.php b/src/DataCollector/RequestCollector.php index a8abc8677..3a243781a 100644 --- a/src/DataCollector/RequestCollector.php +++ b/src/DataCollector/RequestCollector.php @@ -5,6 +5,7 @@ use DebugBar\DataCollector\DataCollector; use DebugBar\DataCollector\DataCollectorInterface; use DebugBar\DataCollector\Renderable; +use Illuminate\Support\Str; use Laravel\Telescope\IncomingEntry; use Laravel\Telescope\Telescope; use Symfony\Component\HttpFoundation\Response; @@ -115,8 +116,8 @@ public function collect() } foreach ($data['request_server'] as $key => $value) { - if (str_is('*_KEY', $key) || str_is('*_PASSWORD', $key) - || str_is('*_SECRET', $key) || str_is('*_PW', $key)) { + if (Str::is('*_KEY', $key) || Str::is('*_PASSWORD', $key) + || Str::is('*_SECRET', $key) || Str::is('*_PW', $key)) { $data['request_server'][$key] = '******'; } } diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index e203b2197..526261a63 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -34,6 +34,7 @@ use Illuminate\Contracts\Foundation\Application; use Illuminate\Session\SessionManager; +use Illuminate\Support\Str; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -98,7 +99,7 @@ public function __construct($app = null) } $this->app = $app; $this->version = $app->version(); - $this->is_lumen = str_contains($this->version, 'Lumen'); + $this->is_lumen = Str::contains($this->version, 'Lumen'); } /** From 91468c5bae6ef13aa45b8658d1d9722e4c70d238 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20N=C3=BCrnberger?= Date: Mon, 26 Aug 2019 07:23:03 +0200 Subject: [PATCH 109/585] use interface/contract --- src/DataCollector/ModelsCollector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DataCollector/ModelsCollector.php b/src/DataCollector/ModelsCollector.php index 8ac07851a..b58cb9059 100644 --- a/src/DataCollector/ModelsCollector.php +++ b/src/DataCollector/ModelsCollector.php @@ -4,7 +4,7 @@ use Barryvdh\Debugbar\DataFormatter\SimpleFormatter; use DebugBar\DataCollector\MessagesCollector; -use Illuminate\Events\Dispatcher; +use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Support\Str; /** From c4a8f120c43b00db0df874251233c3d9c2b75328 Mon Sep 17 00:00:00 2001 From: Mike Bronner Date: Tue, 27 Aug 2019 08:08:04 -0700 Subject: [PATCH 110/585] Protect against possible null values in `$models` array. --- src/DataCollector/ModelsCollector.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/DataCollector/ModelsCollector.php b/src/DataCollector/ModelsCollector.php index b58cb9059..1f4423d48 100644 --- a/src/DataCollector/ModelsCollector.php +++ b/src/DataCollector/ModelsCollector.php @@ -24,6 +24,8 @@ public function __construct(Dispatcher $events) $events->listen('eloquent.*', function ($event, $models) { if (Str::contains($event, 'eloquent.retrieved')) { + $models = array_filter($models); + foreach ($models as $model) { $class = get_class($model); $this->models[$class] = ($this->models[$class] ?? 0) + 1; From fe7fb05b7ff0d975bbbb5ecc1210cda4c01846c6 Mon Sep 17 00:00:00 2001 From: Mike Bronner Date: Tue, 27 Aug 2019 08:12:58 -0700 Subject: [PATCH 111/585] Update ModelsCollector.php --- src/DataCollector/ModelsCollector.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/DataCollector/ModelsCollector.php b/src/DataCollector/ModelsCollector.php index 1f4423d48..3ce356c38 100644 --- a/src/DataCollector/ModelsCollector.php +++ b/src/DataCollector/ModelsCollector.php @@ -24,9 +24,7 @@ public function __construct(Dispatcher $events) $events->listen('eloquent.*', function ($event, $models) { if (Str::contains($event, 'eloquent.retrieved')) { - $models = array_filter($models); - - foreach ($models as $model) { + foreach (array_filter($models) as $model) { $class = get_class($model); $this->models[$class] = ($this->models[$class] ?? 0) + 1; } From deb06769728e0e0794155d22ee788d412999dc8a Mon Sep 17 00:00:00 2001 From: Adam Rodriguez Date: Wed, 28 Aug 2019 10:39:20 -0700 Subject: [PATCH 112/585] Prefer class over removed helper --- src/DataCollector/GateCollector.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/DataCollector/GateCollector.php b/src/DataCollector/GateCollector.php index aaac67937..921df7439 100644 --- a/src/DataCollector/GateCollector.php +++ b/src/DataCollector/GateCollector.php @@ -7,6 +7,7 @@ use Illuminate\Contracts\Auth\Access\Gate; use Illuminate\Contracts\Auth\Authenticatable; use Symfony\Component\VarDumper\Cloner\VarCloner; +use Illuminate\Support\Str; /** * Collector for Laravel's Auth provider @@ -31,7 +32,7 @@ public function addCheck($user = null, $ability, $result, $arguments = []) $userId = null; if ($user) { - $userKey = snake_case(class_basename($user)); + $userKey = Str::snake(class_basename($user)); $userId = $user instanceof Authenticatable ? $user->getAuthIdentifier() : $user->id; } From 6678e1f3965b4d35b97f7ced4640d91662ad723c Mon Sep 17 00:00:00 2001 From: Pierre Arnissolle Date: Sat, 7 Sep 2019 13:11:13 +0200 Subject: [PATCH 113/585] Highlight the vendor:publish section Because I already missed this step in past... --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index e3c3d813f..f3a3d25a6 100644 --- a/readme.md +++ b/readme.md @@ -74,7 +74,7 @@ The profiler is enabled by default, if you have APP_DEBUG=true. You can override You can also set in your config if you want to include/exclude the vendor files also (FontAwesome, Highlight.js and jQuery). If you already use them in your site, set it to false. You can also only display the js or css vendors, by setting it to 'js' or 'css'. (Highlight.js requires both css + js, so set to `true` for syntax highlighting) -Copy the package config to your local config with the publish command: +#### Copy the package config to your local config with the publish command: ```shell php artisan vendor:publish --provider="Barryvdh\Debugbar\ServiceProvider" From 4adad64cff01c909fff4b1153f643bc00e557848 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Fourie Date: Wed, 18 Sep 2019 11:13:10 +0200 Subject: [PATCH 114/585] Laravel 6 logo --- src/Resources/laravel-debugbar.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index b9e7e57a7..3c073a701 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -49,7 +49,7 @@ div.phpdebugbar-header { div.phpdebugbar-header, a.phpdebugbar-restore-btn, div.phpdebugbar-openhandler .phpdebugbar-openhandler-header { - background: #f5f5f5 url(data:image/png;base64,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) no-repeat 5px 3px; + background: #f5f5f5 url(data:image/png;base64,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) no-repeat 5px 3px; } a.phpdebugbar-close-btn { From 9b42a02e9abed988d36e6de95f195e758964b885 Mon Sep 17 00:00:00 2001 From: Bilal Iqbal Date: Tue, 24 Sep 2019 17:30:27 +0500 Subject: [PATCH 115/585] Replaced hard-coded _debugbar with config variable --- src/LaravelDebugbar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 526261a63..c1397eafd 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -782,7 +782,7 @@ public function isEnabled() */ protected function isDebugbarRequest() { - return $this->app['request']->segment(1) == '_debugbar'; + return $this->app['request']->segment(1) == $this->app['config']->get('debugbar.route_prefix'); } /** From 8da801b4783419ddac6fbee50f138a0aad8a77fc Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Wed, 30 Oct 2019 21:52:57 +0100 Subject: [PATCH 116/585] Create FUNDING.yml --- .github/FUNDING.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..798753326 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: barryvdh +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] From 5162cbfffa4cc8f3b07d55b899198552cc7f02b9 Mon Sep 17 00:00:00 2001 From: Carlos Egusquiza Date: Sat, 23 Nov 2019 11:08:27 +0100 Subject: [PATCH 117/585] Allow symfony 5 --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index a64f48968..f5eada032 100644 --- a/composer.json +++ b/composer.json @@ -15,8 +15,8 @@ "illuminate/routing": "^5.5|^6", "illuminate/session": "^5.5|^6", "illuminate/support": "^5.5|^6", - "symfony/debug": "^3|^4", - "symfony/finder": "^3|^4" + "symfony/debug": "^3|^4|^5", + "symfony/finder": "^3|^4|^5" }, "autoload": { "psr-4": { From a5d04afa7f8aaaabd6bc38cdcd3c0fd0c5170e48 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Mon, 2 Dec 2019 11:52:22 -0600 Subject: [PATCH 118/585] allow semantic versioning for maximebf/debugbar --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f5eada032..a143684b6 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ ], "require": { "php": ">=7.0", - "maximebf/debugbar": "~1.15.0", + "maximebf/debugbar": "^1.15", "illuminate/routing": "^5.5|^6", "illuminate/session": "^5.5|^6", "illuminate/support": "^5.5|^6", From 3128abb739bc5ff4636c3db6f2d0c19f38305f21 Mon Sep 17 00:00:00 2001 From: chrypro <7782312+chrypro@users.noreply.github.com> Date: Fri, 6 Dec 2019 18:37:31 +0200 Subject: [PATCH 119/585] Update RouteCollector.php --- src/DataCollector/RouteCollector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DataCollector/RouteCollector.php b/src/DataCollector/RouteCollector.php index 6128e331d..2df08fbb3 100644 --- a/src/DataCollector/RouteCollector.php +++ b/src/DataCollector/RouteCollector.php @@ -58,7 +58,7 @@ protected function getRouteInformation($route) $result = array_merge($result, $action); - if (isset($action['controller']) && strpos($action['controller'], '@') !== false) { + if (isset($action['controller']) && is_string($action['controller']) && strpos($action['controller'], '@') !== false) { list($controller, $method) = explode('@', $action['controller']); if(class_exists($controller) && method_exists($controller, $method)) { $reflector = new \ReflectionMethod($controller, $method); From 7afc4a493d39a63be912d9274601806b9f06fb27 Mon Sep 17 00:00:00 2001 From: Steve Lacey Date: Sun, 9 Feb 2020 21:12:23 +0700 Subject: [PATCH 120/585] Fetch support (#975) --- composer.json | 2 +- src/LaravelDebugbar.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index a143684b6..b0df73526 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ ], "require": { "php": ">=7.0", - "maximebf/debugbar": "^1.15", + "maximebf/debugbar": "^1.15.1", "illuminate/routing": "^5.5|^6", "illuminate/session": "^5.5|^6", "illuminate/support": "^5.5|^6", diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index c1397eafd..73cc4cf48 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -498,6 +498,7 @@ function ($query, $bindings = null, $time = null, $connectionName = null) use ($ $renderer = $this->getJavascriptRenderer(); $renderer->setIncludeVendors($this->app['config']->get('debugbar.include_vendors', true)); + $renderer->setBindAjaxHandlerToFetch($app['config']->get('debugbar.capture_ajax', true)); $renderer->setBindAjaxHandlerToXHR($app['config']->get('debugbar.capture_ajax', true)); $this->booted = true; From 42d5da5379a7860093f8e4032167e4cb5ebec180 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Tue, 25 Feb 2020 21:42:23 +0100 Subject: [PATCH 121/585] Update composer.json --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index b0df73526..eddc364bc 100644 --- a/composer.json +++ b/composer.json @@ -12,9 +12,9 @@ "require": { "php": ">=7.0", "maximebf/debugbar": "^1.15.1", - "illuminate/routing": "^5.5|^6", - "illuminate/session": "^5.5|^6", - "illuminate/support": "^5.5|^6", + "illuminate/routing": "^5.5|^6|^7", + "illuminate/session": "^5.5|^6|^7", + "illuminate/support": "^5.5|^6|^7", "symfony/debug": "^3|^4|^5", "symfony/finder": "^3|^4|^5" }, From 32bbba88ce69a91a5a836ac115d5076915e6d6c1 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Tue, 3 Mar 2020 11:10:40 -0600 Subject: [PATCH 122/585] Add timing information to events This fixes #390 and expands on #392. Additionally, the change to referencing the Dispatcher contract rather than the implementation allows for greater flexibility (specifically I'm using it with October CMS, which has its own event dispatcher that implements the Dispatcher contract). While the timing information isn't 100% accurate, as the first event will be tracked if it took all of the time from when the collector was setup to when the event fired, and any future events are really just tracking the time between itself and the previous event, this is still valuable information that is close enough to reality to act upon. Additionally, this collector was setup from the start to track event timings, but was never actually implemented properly as it would record the start and end of the event as being the same time, always resulting in a timing of 0. --- src/DataCollector/EventCollector.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/DataCollector/EventCollector.php b/src/DataCollector/EventCollector.php index 572cab538..d3974a411 100644 --- a/src/DataCollector/EventCollector.php +++ b/src/DataCollector/EventCollector.php @@ -3,7 +3,7 @@ use Barryvdh\Debugbar\DataFormatter\SimpleFormatter; use DebugBar\DataCollector\TimeDataCollector; -use Illuminate\Events\Dispatcher; +use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Support\Str; use Symfony\Component\VarDumper\Cloner\VarCloner; @@ -12,16 +12,20 @@ class EventCollector extends TimeDataCollector /** @var Dispatcher */ protected $events; + /** @var integer */ + protected $previousTime; + public function __construct($requestStartTime = null) { parent::__construct($requestStartTime); + $this->previousTime = microtime(true); $this->setDataFormatter(new SimpleFormatter()); } public function onWildcardEvent($name = null, $data = []) { $params = $this->prepareParams($data); - $time = microtime(true); + $currentTime = microtime(true); // Find all listeners for the current event foreach ($this->events->getListeners($name) as $i => $listener) { @@ -57,7 +61,8 @@ public function onWildcardEvent($name = null, $data = []) $params['listeners.' . $i] = $listener; } - $this->addMeasure($name, $time, $time, $params); + $this->addMeasure($name, $this->previousTime, $currentTime, $params); + $this->previousTime = $currentTime; } public function subscribe(Dispatcher $events) From 31bb601e1157b9a7333cfa88ea19daddbabd9942 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Mon, 23 Mar 2020 16:00:46 -0600 Subject: [PATCH 123/585] Fix support for connection transaction logging The change to how wildcard event listeners work made in Laravel 5.4 (https://github.com/laravel/framework/commit/dbbfc62beff1625b0d45bbf39650d047555cf4fa) broke the existing code added in https://github.com/barryvdh/laravel-debugbar/commit/506c1ebb9e344ee3f79c04ec30d42b5323671558 when attempting to use the debugbar in a Laravel 5.5 application (or October CMS, given that it's connection interface fires the string events and not the event objects. This was not detected as an issue in October CMS until its event handling was brought into line with Laravel 5.5: https://github.com/octobercms/library/commit/8b61480f1f79069195896b9aed934b953977b2f0 --- src/LaravelDebugbar.php | 65 ++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 73cc4cf48..1bdc28519 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -351,50 +351,47 @@ function ($query, $bindings = null, $time = null, $connectionName = null) use ($ } try { - $db->getEventDispatcher()->listen([ + $db->getEventDispatcher()->listen( \Illuminate\Database\Events\TransactionBeginning::class, - 'connection.*.beganTransaction', - ], function ($transaction) use ($queryCollector) { - - // Laravel 5.2 changed the way some core events worked. We must account for - // the first argument being an "event object", where arguments are passed - // via object properties, instead of individual arguments. - if($transaction instanceof \Illuminate\Database\Events\TransactionBeginning) { - $connection = $transaction->connection; - } else { - $connection = $transaction; + function ($transaction) use ($queryCollector) { + $queryCollector->collectTransactionEvent('Begin Transaction', $transaction->connection); } + ); - $queryCollector->collectTransactionEvent('Begin Transaction', $connection); - }); - - $db->getEventDispatcher()->listen([ + $db->getEventDispatcher()->listen( \Illuminate\Database\Events\TransactionCommitted::class, - 'connection.*.committed', - ], function ($transaction) use ($queryCollector) { - - if($transaction instanceof \Illuminate\Database\Events\TransactionCommitted) { - $connection = $transaction->connection; - } else { - $connection = $transaction; + function ($transaction) use ($queryCollector) { + $queryCollector->collectTransactionEvent('Commit Transaction', $transaction->connection); } + ); - $queryCollector->collectTransactionEvent('Commit Transaction', $connection); - }); - - $db->getEventDispatcher()->listen([ + $db->getEventDispatcher()->listen( \Illuminate\Database\Events\TransactionRolledBack::class, - 'connection.*.rollingBack', - ], function ($transaction) use ($queryCollector) { + function ($transaction) use ($queryCollector) { + $queryCollector->collectTransactionEvent('Rollback Transaction', $transaction->connection); + } + ); - if($transaction instanceof \Illuminate\Database\Events\TransactionRolledBack) { - $connection = $transaction->connection; - } else { - $connection = $transaction; + $db->getEventDispatcher()->listen( + 'connection.*.beganTransaction', + function ($event, $params) use ($queryCollector) { + $queryCollector->collectTransactionEvent('Begin Transaction', $params[0]); } + ); - $queryCollector->collectTransactionEvent('Rollback Transaction', $connection); - }); + $db->getEventDispatcher()->listen( + 'connection.*.committed', + function ($event, $params) use ($queryCollector) { + $queryCollector->collectTransactionEvent('Commit Transaction', $params[0]); + } + ); + + $db->getEventDispatcher()->listen( + 'connection.*.rollingBack', + function ($event, $params) use ($queryCollector) { + $queryCollector->collectTransactionEvent('Rollback Transaction', $params[0]); + } + ); } catch (\Exception $e) { $this->addThrowable( new Exception( From 75432f948f6d3f05f8c6b4326e68a40f6c5682db Mon Sep 17 00:00:00 2001 From: Tristan Mouchet Date: Mon, 13 Apr 2020 14:01:48 +0100 Subject: [PATCH 124/585] Complete redesign of the DebugBar (CSS only) Hey, I was finding the bar messy and not very intuitive, this CSS update is improving [a lot] the overall look of the bar and its features. Hope you like it! --- src/Resources/laravel-debugbar.css | 602 ++++++++++++++++++++++++----- 1 file changed, 509 insertions(+), 93 deletions(-) diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index 3c073a701..bbec32fcb 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -1,13 +1,166 @@ + +/* Force Laravel Whoops exception handler to be displayed under the debug bar */ +.Whoops.container { + z-index: 5999999; +} + div.phpdebugbar { font-size: 13px; font-family: "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif; direction: ltr; text-align: left; - z-index: 9999999999; + z-index: 6000000; +} + +div.phpdebugbar-openhandler-overlay { + z-index: 6000001; + cursor: pointer; +} + +div.phpdebugbar-openhandler { + border: 1px solid #aaa; + border-top: 3px solid #fa5661; + width: 80%; + height: 70%; + padding: 10px; + border-radius: 5px; + overflow-y: scroll; + z-index: 6000002; + cursor: default; +} + +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > a, +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions button { + display: inline-block; + cursor: pointer; + margin: 5px; + padding: 0px 12px 2px; + border-radius: 3px; + border: 1px solid #ddd; + background-color: #f5f5f5; + color: #000; + text-shadow: 1px 1px #fff; + font-size: 13px; +} + +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form { + margin: 15px 0px 5px; + text-transform: uppercase; + font-size: 13px; + font-weight: bold; +} + +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form br { + display: none; +} + +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form > b { + display: none; +} + +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form input, +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form select { + margin: 0px 10px 10px 2px; + border: 1px solid #aaa; + border-radius: 3px; + padding: 3px 6px 2px; + line-height: 16px; +} + +@media (max-width: 720px) { + div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form select + br, + div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form input[name="uri"] + br { + display: block; + } +} + +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form select { + padding: 2px 5px 1px; +} + +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions input[name="uri"] { + width: 200px; +} + +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions input[name="ip"] { + width: 90px; +} + +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions button { + outline: none; + margin: 0px 0px 10px 2px; + padding: 5px 15px 4px; + line-height: 12px; +} + +div.phpdebugbar-openhandler table { + margin: 15px 0px 10px; + table-layout: auto; + border-collapse: collapse; + width: 100%; +} + +div.phpdebugbar-openhandler table td, +div.phpdebugbar-openhandler table th { + width: auto!important; + text-align: left; + border: 0px solid #bbb; + padding: 2px 8px; + font-size: 14px; +} + +div.phpdebugbar-openhandler table th { + text-shadow: 1px 1px #fff; + font-size: 12px; + text-transform: uppercase; + padding: 5px 8px; +} + +div.phpdebugbar-openhandler table th, +div.phpdebugbar-openhandler table tr:nth-child(2n) { + background-color: #f7f7f7; +} + +div.phpdebugbar-openhandler table th:nth-child(1), div.phpdebugbar-openhandler table td:nth-child(1), /* Date */ +div.phpdebugbar-openhandler table th:nth-child(2), div.phpdebugbar-openhandler table td:nth-child(2), /* Method */ +div.phpdebugbar-openhandler table th:nth-child(4), div.phpdebugbar-openhandler table td:nth-child(4), /* IP */ +div.phpdebugbar-openhandler table th:nth-child(5), div.phpdebugbar-openhandler table td:nth-child(5) { /* Filter */ + width: 5%!important; + white-space: nowrap; +} + +div.phpdebugbar-openhandler table th:nth-child(2), div.phpdebugbar-openhandler table td:nth-child(2), /* Method */ +div.phpdebugbar-openhandler table th:nth-child(4), div.phpdebugbar-openhandler table td:nth-child(4), /* IP */ +div.phpdebugbar-openhandler table th:nth-child(5), div.phpdebugbar-openhandler table td:nth-child(5) { /* Filter */ + text-align: center; +} + +div.phpdebugbar-openhandler table th:nth-child(3) { /* URL */ + width: calc(100vw - 100% - 196px)!important; +} + +div.phpdebugbar-openhandler table td a { + display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + cursor: pointer; } div.phpdebugbar-resize-handle { - border-bottom-color: #ddd; + display: block!important; + height: 3px; + margin-top: -4px; + width: 100%; + background: none; + cursor: ns-resize; + border-top: 1px solid #ccc; + border-bottom: 0px; + background-color: #fa5661; +} + +.phpdebugbar.phpdebugbar-minimized div.phpdebugbar-resize-handle { + cursor: default!important; } div.phpdebugbar-closed, @@ -15,10 +168,6 @@ div.phpdebugbar-minimized { border-top-color: #ddd; } -a.phpdebugbar-restore-btn { - border-right-color: #ddd !important; -} - div.phpdebugbar code, div.phpdebugbar pre, div.phpdebugbar samp { background: none; font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; @@ -27,23 +176,46 @@ div.phpdebugbar code, div.phpdebugbar pre, div.phpdebugbar samp { padding: 0; } +div.phpdebugbar .hljs { + padding: 0; +} + +div.phpdebugbar .phpdebugbar-widgets-messages .hljs > code { + padding-bottom: 3px; +} + div.phpdebugbar code, div.phpdebugbar pre { color: #000; } +div.phpdebugbar-widgets-exceptions .phpdebugbar-widgets-filename { + margin-top: 4px; +} + +div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item pre.phpdebugbar-widgets-file { + border: 1px solid #d2d2d2; + border-left: 2px solid #d2d2d2; +} + +div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item pre.phpdebugbar-widgets-file[style="display: block;"] ~ div { + display: block; +} + div.phpdebugbar pre.sf-dump { - color: #a0a000; - outline: 0; + color: #000; + outline: none; + padding: 0px!important; } div.phpdebugbar-body { - border-top: none; + border-top: 1px solid #ddd; } div.phpdebugbar-header { min-height: 30px; line-height: 20px; padding-left: 39px; + text-shadow: 1px 1px #FFF; } div.phpdebugbar-header, @@ -52,9 +224,21 @@ div.phpdebugbar-openhandler .phpdebugbar-openhandler-header { background: #f5f5f5 url(data:image/png;base64,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) no-repeat 5px 3px; } +div.phpdebugbar-openhandler .phpdebugbar-openhandler-header { + background-size: 20px; + padding: 2px 0px 3px 36px; + background-position: 9px 5px; + border-radius: 3px; +} + +div.phpdebugbar-openhandler .phpdebugbar-openhandler-header a { + cursor: pointer; +} + a.phpdebugbar-close-btn { background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuM4zml1AAAADDSURBVDhPxZCxCsIwFEUzuGdwCvQD7BIIcehUXDqVfGM/wsG/iG4ifkzMlRuSPLo4eeFBue8c6Iv6b4wxW557Hs0KnWa3seqDxTiOyVqbhmF4UND4Rofdruyce3rvE6bIRSo9GOI1McbLPM/vVm4l7MAQr0kpHaQsJTDE+6zrepym6SVFdNgR69M+hBTLzWCI10gJvydvBkO8ZlmWayvhJnkzGOI1+fBTCOHWPkT7YNiBId4HizxnCKy+r81uX/otSn0A7dioI/vYX+8AAAAASUVORK5CYII=) no-repeat 9px 6px; color : #555; + border-right: none; } a.phpdebugbar-open-btn { @@ -69,35 +253,59 @@ div.phpdebugbar-openhandler-header { } a.phpdebugbar-restore-btn { - background-size: 20px; - width: 16px; - border-right-color: #ccc; + border-right-color: #ddd!important; + height: 20px; + width: 23px; + background-position: center; + background-size: 21px; } div.phpdebugbar-header > div > * { font-size: 13px; + padding: 5px 8px; } div.phpdebugbar-header .phpdebugbar-tab { - padding: 5px 6px; + padding: 5px 8px; + border-left: 1px solid #ddd; +} + +div.phpdebugbar-header .phpdebugbar-header-left { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } div.phpdebugbar .phpdebugbar-header select { - padding: 1px 0; + margin: 4px 0px 0px 8px; + padding: 2px 3px 3px 3px; + border-radius: 3px; + width: auto; + cursor: pointer; } -dl.phpdebugbar-widgets-kvlist dt { - width: 200px; +dl.phpdebugbar-widgets-kvlist dt, +dl.phpdebugbar-widgets-kvlist dd { min-height: 20px; - padding: 7px 5px; line-height: 20px; + padding: 4px 5px 5px; + border-top: 0px; +} + +dl.phpdebugbar-widgets-kvlist dd.phpdebugbar-widgets-value.phpdebugbar-widgets-pretty .phpdebugbar-widgets-code-block { + padding: 0px 0px; + background: transparent; +} + +dl.phpdebugbar-widgets-kvlist dt { + width: 200px; } dl.phpdebugbar-widgets-kvlist dd { - min-height: 20px; margin-left: 210px; - padding: 7px 5px; - line-height: 20px; } ul.phpdebugbar-widgets-timeline .phpdebugbar-widgets-measure { @@ -112,89 +320,171 @@ ul.phpdebugbar-widgets-timeline li:nth-child(even) { ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-value { height: 15px; - background-color: #f4645f; + background-color:#3a96bd; } ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label, -ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-collector { +ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-collector { top: 0px; + color: #fff; +} + +ul.phpdebugbar-widgets-timeline > li:nth-child(n+2) span.phpdebugbar-widgets-label { + color: #000; +} + +ul.phpdebugbar-widgets-timeline li .phpdebugbar-widgets-value span.phpdebugbar-widgets-label { + color: #fff; +} + +div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar { + width: calc(100% - 20px); + padding: 4px 0px 4px; + height: 20px; + border: 1px solid #ddd; + border-bottom: 0px; + background-color: #e8e8e8; + border-radius: 5px 5px 0px 0px; +} + +div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar input { + width: calc(100% - 48px); + margin-left: 0px; + border-radius: 3px; + padding: 2px 6px; + height: 15px; +} + +div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-label, +div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-collector { + padding: 1px 0px 0px 10px; + margin: 0px; + text-transform: uppercase; + font-style: normal; + color: #333; +} + +.phpdebugbar-widgets-toolbar i.phpdebugbar-fa.phpdebugbar-fa-search { + position: relative; + top: -1px; + padding: 0px 10px; +} + +div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter, +div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter.phpdebugbar-widgets-excluded { + position: relative; + top: -48px; + display: inline-block; + background-color: #6d6d6d; + margin-left: 3px; + border-radius: 3px; + padding: 5px 8px 4px; + text-transform: uppercase; + font-size: 10px; + text-shadow: 1px 1px #585858; + transition: background-color .25s linear 0s, color .25s linear 0s; + color: #FFF; + + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter[rel="info"], +div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter.phpdebugbar-widgets-excluded[rel="info"] { + background-color: #5896e2; +} + +div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter[rel="error"], +div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter.phpdebugbar-widgets-excluded[rel="error"] { + background-color: #fa5661; +} + +div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter[rel="warning"], +div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter.phpdebugbar-widgets-excluded[rel="warning"] { + background-color: #f99400; +} + +div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter:hover { + color: #FFF; + opacity: 0.85; } -div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter { - background-color: #f4645f; +div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter.phpdebugbar-widgets-excluded { + opacity: 0.45; } a.phpdebugbar-tab:hover, span.phpdebugbar-indicator:hover, a.phpdebugbar-indicator:hover, a.phpdebugbar-close-btn:hover, +a.phpdebugbar-minimize-btn:hover, +a.phpdebugbar-maximize-btn:hover, a.phpdebugbar-open-btn:hover { background-color: #ebebeb; - transition: background-color .25s linear 0s, color .25s linear 0s; + /* transition: background-color .25s linear 0s, color .25s linear 0s; */ +} + +a.phpdebugbar-minimize-btn, +a.phpdebugbar-maximize-btn { + width: 28px!important; } a.phpdebugbar-tab.phpdebugbar-active { - background: #f4645f; + background: #fa262bbf; color: #fff; + text-shadow: 1px 1px #000; } a.phpdebugbar-tab.phpdebugbar-active span.phpdebugbar-badge { background-color: white; - color: #f4645f; + color: #fa262bbf; + text-shadow: 1px 1px #ebebeb; } a.phpdebugbar-tab span.phpdebugbar-badge { vertical-align: 0px; - padding: 2px 6px; - background: #f4645f; - font-size: 12px; + padding: 2px 8px 3px 8px; + text-align: center; + background: #fa262bbf; + font-size: 11px; + font-family: monospace; color: #fff; + text-shadow: 1px 1px rgb(46, 46, 46); border-radius: 10px; + top: -1px; + position: relative; } -div.phpdebugbar-openhandler .phpdebugbar-openhandler-header { - background-size: 20px; -} - -div.phpdebugbar-openhandler a { - color: #555; -} - -div.phpdebugbar-openhandler table { - table-layout: fixed; -} - -div.phpdebugbar-openhandler table td, -div.phpdebugbar-openhandler table th { - text-align: left; -} - -div.phpdebugbar-openhandler table td a { - display: block; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; +.phpdebugbar-indicator { + cursor: text; } -.phpdebugbar-indicator span.phpdebugbar-tooltip { - top: -36px; +.phpdebugbar-indicator span.phpdebugbar-tooltip, +div.phpdebugbar-mini-design a.phpdebugbar-tab:hover span.phpdebugbar-text { border: none; border-radius: 5px; background: #f5f5f5; font-size: 12px; + width: auto; + white-space: nowrap; + padding: 2px 18px; + text-shadow: none; + + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } -div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter { - margin: 0; - padding: 5px 8px; - border-radius: 0; - font-size: 12px; - transition: background-color .25s linear 0s, color .25s linear 0s; -} - -div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter:hover { - background-color: #ad4844; - color: #fff; +div.phpdebugbar-mini-design a.phpdebugbar-tab:hover span.phpdebugbar-text { + left: 0px; + right: auto; } .phpdebugbar-widgets-toolbar > .fa { @@ -205,14 +495,47 @@ div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-w } ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item { - padding: 15px 10px; + padding: 7px 10px; border: none; font-family: inherit; overflow: visible; +} + +.phpdebugbar-widgets-sqlqueries ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item { display: flex; flex-wrap: wrap; } +.phpdebugbar-widgets-templates ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item { + display: block; +} + +.phpdebugbar-widgets-templates ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item, +.phpdebugbar-widgets-mails ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item { + line-height: 15px; +} + +.phpdebugbar-widgets-mails ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item { + cursor: pointer; + display: block; +} + +.phpdebugbar-widgets-mails ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-subject { + display: inline-block; + margin-right: 15px; +} + +.phpdebugbar-widgets-mails ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-headers { + margin: 10px 0px; + padding: 7px 10px; + border-left: 2px solid #ddd; + line-height: 17px; +} + +.phpdebugbar-widgets-list .phpdebugbar-widgets-list-item .phpdebugbar-widgets-name { + height: 15px; +} + .phpdebugbar-widgets-sql.phpdebugbar-widgets-name { font-weight: bold; } @@ -220,68 +543,115 @@ ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item { ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-sql { flex: 1; margin-right: 5px; - cursor: text; } ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-duration { - /*flex: 0 0 auto;*/ margin-left: auto; margin-right: 5px; } ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-database { - /*flex: 0 0 auto;*/ margin-left: auto; } ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-stmt-id { - /*flex: 0 0 auto;*/ margin-left: auto; margin-right: 5px; } -ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-params { - background-color: rgba(255, 255, 255, .5); - flex: 1 1 auto; - margin: 10px 100% 10px 0; - max-width: 100%; +ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item table.phpdebugbar-widgets-params { + background-color: #fdfdfd; + margin: 10px 0px; + font-size: 12px; + border-left: 2px solid #cecece; +} + +div.phpdebugbar-widgets-templates table.phpdebugbar-widgets-params th, +div.phpdebugbar-widgets-templates table.phpdebugbar-widgets-params td { + padding: 1px 10px!important; +} + +div.phpdebugbar-widgets-templates table.phpdebugbar-widgets-params th { + padding: 2px 10px!important; + background-color: #efefef; +} + +div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params td.phpdebugbar-widgets-name { + width: auto; +} + +div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params td.phpdebugbar-widgets-name .phpdebugbar-fa { + position: relative; + top: 1px; + margin-left: 3px; } ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item:nth-child(even) { background-color: #f9f9f9; } -div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-error:before { - font-size: 12px; - color: #e74c3c; +div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value:before { + font-family: PhpDebugbarFontAwesome; + content: "\f005"; + color: #333; + font-size: 13px; + margin-right: 8px; + float: left; } -div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-warning:before { - font-size: 12px; - color: #f1c40f; +div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-info { + color: #5896e2; +} + +div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-info:before { + font-family: PhpDebugbarFontAwesome; + content: "\f05a"; + color: #5896e2; + font-size: 15px; } div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-error { color: #e74c3c; } -.phpdebugbar-widgets-value.phpdebugbar-widgets-warning { - color: #f1c40f; +div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-error:before { + color: #fa5661; + font-size: 15px; +} + +div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-warning:before { + color: #FF9800; + font-size: 13px; +} + +div.phpdebugbar-widgets-messages .phpdebugbar-widgets-value.phpdebugbar-widgets-warning { + color: #FF9800; +} + +div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item pre.sf-dump { + display: inline-block; + position: relative; + top: -1px; } div.phpdebugbar-widgets-sqlqueries { line-height: 20px; } -div.phpdebugbar-widgets-sqlqueries .phpdebugbar-widgets-status { - background: none !important; - font-family: inherit !important; - font-weight: 400 !important; +div.phpdebugbar-panel div.phpdebugbar-widgets-status { + padding: 10px 15px!important; + line-height: 10px; + border: 1px solid #ddd; + border-radius: 3px; } div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params th, div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params td { - padding: 5px 10px; + padding: 4px 10px; +} + +div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params th { + background-color: #efefef; } div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params td.phpdebugbar-widgets-name { @@ -294,12 +664,12 @@ div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params td.phpdebugb text-align: left; } -ul.phpdebugbar-widgets-list ul.phpdebugbar-widgets-table-list { - text-align: left; +div.phpdebugbar-widgets-templates .phpdebugbar-widgets-list-item table.phpdebugbar-widgets-params { + width: auto!important; } -ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-table-list-item { - /*padding: 5px 10px;*/ +ul.phpdebugbar-widgets-list ul.phpdebugbar-widgets-table-list { + text-align: left; } .phpdebugbar-text-muted { @@ -310,10 +680,56 @@ ul.phpdebugbar-widgets-cache a.phpdebugbar-widgets-forget { float: right; font-size: 12px; padding: 0 4px; - background: #f4645f; + background: #fa262bbf; margin: 0 2px; border-radius: 4px; color: #fff; text-decoration: none; line-height: 1.5rem; } + +a.phpdebugbar-tab i { + line-height: 20px; +} + +div.phpdebugbar-mini-design a.phpdebugbar-tab { + border-right: none; +} + +div.phpdebugbar-header-right > a { + height: 20px; + width: 20px; + background-position: center; +} + +div.phpdebugbar-panel { + width: calc(100% - 20px); + height: calc(100% - 20px); + padding: 10px; +} + +div.phpdebugbar-panel table { + margin: 10px 0px!important; + width: 100%!important; +} + +div.phpdebugbar-panel table .phpdebugbar-widgets-name { + font-size: 13px; +} + +dl.phpdebugbar-widgets-kvlist > :nth-child(4n-1), +dl.phpdebugbar-widgets-kvlist > :nth-child(4n) { + background-color: #f9f9f9; +} + +.phpdebugbar pre.sf-dump:after { + clear: none!important; +} + +div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-message { + color: #dd1044; +} + +div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item > div { + display: none; +} From e0d1f870babc9a3b2c9d49493358a7129af6f06c Mon Sep 17 00:00:00 2001 From: Tristan Mouchet Date: Mon, 13 Apr 2020 14:48:22 +0100 Subject: [PATCH 125/585] Fixed issues on chrome / Edge --- src/Resources/laravel-debugbar.css | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index bbec32fcb..879b63ffa 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -434,14 +434,15 @@ a.phpdebugbar-maximize-btn { } a.phpdebugbar-tab.phpdebugbar-active { - background: #fa262bbf; + background: #fa5661; + background-image: none; color: #fff; text-shadow: 1px 1px #000; } a.phpdebugbar-tab.phpdebugbar-active span.phpdebugbar-badge { background-color: white; - color: #fa262bbf; + color: #fa5661; text-shadow: 1px 1px #ebebeb; } @@ -449,7 +450,7 @@ a.phpdebugbar-tab span.phpdebugbar-badge { vertical-align: 0px; padding: 2px 8px 3px 8px; text-align: center; - background: #fa262bbf; + background: #fa5661; font-size: 11px; font-family: monospace; color: #fff; @@ -590,6 +591,10 @@ ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item:nth-child(even) { background-color: #f9f9f9; } +div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value { + display: inline-flex; +} + div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value:before { font-family: PhpDebugbarFontAwesome; content: "\f005"; @@ -680,7 +685,7 @@ ul.phpdebugbar-widgets-cache a.phpdebugbar-widgets-forget { float: right; font-size: 12px; padding: 0 4px; - background: #fa262bbf; + background: #fa5661; margin: 0 2px; border-radius: 4px; color: #fff; From cf08a670a3ff46d82bbffb567864736d9fb4990f Mon Sep 17 00:00:00 2001 From: Tristan Mouchet Date: Wed, 15 Apr 2020 19:25:15 +0100 Subject: [PATCH 126/585] Improved contrast and widget-status --- src/Resources/laravel-debugbar.css | 76 +++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 17 deletions(-) diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index 879b63ffa..ab80ee02e 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -118,7 +118,7 @@ div.phpdebugbar-openhandler table th { div.phpdebugbar-openhandler table th, div.phpdebugbar-openhandler table tr:nth-child(2n) { - background-color: #f7f7f7; + background-color: #efefef; } div.phpdebugbar-openhandler table th:nth-child(1), div.phpdebugbar-openhandler table td:nth-child(1), /* Date */ @@ -154,7 +154,7 @@ div.phpdebugbar-resize-handle { width: 100%; background: none; cursor: ns-resize; - border-top: 1px solid #ccc; + border-top: none; border-bottom: 0px; background-color: #fa5661; } @@ -309,32 +309,33 @@ dl.phpdebugbar-widgets-kvlist dd { } ul.phpdebugbar-widgets-timeline .phpdebugbar-widgets-measure { - height: 25px; - line-height: 25px; + height: 26px; + line-height: 27px; border: none; } -ul.phpdebugbar-widgets-timeline li:nth-child(even) { - background-color: #f9f9f9; -} - ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-value { - height: 15px; - background-color:#3a96bd; + height: 16px; + background-color: #63abca; + border-bottom: 2px solid #477e96; } ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label, ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-collector { top: 0px; color: #fff; + font-size: 11px; + text-shadow: 1px 1px #000; } ul.phpdebugbar-widgets-timeline > li:nth-child(n+2) span.phpdebugbar-widgets-label { color: #000; + text-shadow: none; } ul.phpdebugbar-widgets-timeline li .phpdebugbar-widgets-value span.phpdebugbar-widgets-label { color: #fff; + text-shadow: 1px 1px #000; } div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar { @@ -502,6 +503,11 @@ ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item { overflow: visible; } +ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item:hover, +ul.phpdebugbar-widgets-timeline li:hover { + background-color: initial; +} + .phpdebugbar-widgets-sqlqueries ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item { display: flex; flex-wrap: wrap; @@ -588,7 +594,7 @@ div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params td.phpdebugb } ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item:nth-child(even) { - background-color: #f9f9f9; + background-color: #f5f5f5; } div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value { @@ -605,7 +611,7 @@ div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugb } div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-info { - color: #5896e2; + color: #1299DA; } div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-info:before { @@ -643,11 +649,31 @@ div.phpdebugbar-widgets-sqlqueries { line-height: 20px; } + div.phpdebugbar-panel div.phpdebugbar-widgets-status { - padding: 10px 15px!important; - line-height: 10px; - border: 1px solid #ddd; - border-radius: 3px; + padding: 9px 20px!important; + width: calc(100% - 20px); + margin-left: -10px; + margin-top: -10px; + line-height: 11px!important; + font-weight: bold!important; + background: #f5f5f5!important; + border-bottom: 1px solid #cecece!important; +} + +div.phpdebugbar-panel div.phpdebugbar-widgets-status > * { + color: #383838!important; +} + +div.phpdebugbar-panel div.phpdebugbar-widgets-status > span:first-child:before { + font-family: PhpDebugbarFontAwesome; + content: "\f05a"; + color: #737373; + text-shadow: 1px 1px #fff; + font-size: 14px; + position: relative; + top: 1px; + margin-right: 8px; } div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params th, @@ -707,6 +733,11 @@ div.phpdebugbar-header-right > a { background-position: center; } +div.phpdebugbar-header-right .phpdebugbar-indicator > i.phpdebugbar-fa { + vertical-align: baseline; + margin-top: 2px; +} + div.phpdebugbar-panel { width: calc(100% - 20px); height: calc(100% - 20px); @@ -724,7 +755,7 @@ div.phpdebugbar-panel table .phpdebugbar-widgets-name { dl.phpdebugbar-widgets-kvlist > :nth-child(4n-1), dl.phpdebugbar-widgets-kvlist > :nth-child(4n) { - background-color: #f9f9f9; + background-color: #f5f5f5; } .phpdebugbar pre.sf-dump:after { @@ -738,3 +769,14 @@ div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item span.phpdebu div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item > div { display: none; } + + +div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-database:before, +div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-duration:before, +div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-memory:before, +div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-row-count:before, +div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-copy-clipboard:before, +div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-stmt-id:before, +div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-param-count:before { + margin-right: 6px!important; +} From e12a57329531cfd9200a3eb334752cb0ab8475ba Mon Sep 17 00:00:00 2001 From: Tristan Mouchet Date: Wed, 15 Apr 2020 19:36:33 +0100 Subject: [PATCH 127/585] Fixed timeline line height --- src/Resources/laravel-debugbar.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index ab80ee02e..a7953515d 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -309,8 +309,8 @@ dl.phpdebugbar-widgets-kvlist dd { } ul.phpdebugbar-widgets-timeline .phpdebugbar-widgets-measure { - height: 26px; - line-height: 27px; + height: 28px; + line-height: 28px; border: none; } From d1277d7f924f40b94c5152cda05613458cb2f704 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 16 Apr 2020 09:47:31 +0200 Subject: [PATCH 128/585] Update screenshot --- readme.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/readme.md b/readme.md index f3a3d25a6..2ce334bfb 100644 --- a/readme.md +++ b/readme.md @@ -3,17 +3,14 @@ [![Latest Stable Version](https://poser.pugx.org/barryvdh/laravel-debugbar/version.png)](https://packagist.org/packages/barryvdh/laravel-debugbar) [![Total Downloads](https://poser.pugx.org/barryvdh/laravel-debugbar/d/total.png)](https://packagist.org/packages/barryvdh/laravel-debugbar) -### Note for v3: Debugbar is now enabled by requiring the package, but still needs APP_DEBUG=true by default! - -### For Laravel < 5.5, please use the [2.4 branch](https://github.com/barryvdh/laravel-debugbar/tree/2.4)! - -This is a package to integrate [PHP Debug Bar](http://phpdebugbar.com/) with Laravel 5. +This is a package to integrate [PHP Debug Bar](http://phpdebugbar.com/) with Laravel. It includes a ServiceProvider to register the debugbar and attach it to the output. You can publish assets and configure it through Laravel. It bootstraps some Collectors to work with Laravel and implements a couple custom DataCollectors, specific for Laravel. It is configured to display Redirects and (jQuery) Ajax Requests. (Shown in a dropdown) Read [the documentation](http://phpdebugbar.com/docs/) for more configuration options. -![Screenshot](https://cloud.githubusercontent.com/assets/973269/4270452/740c8c8c-3ccb-11e4-8d9a-5a9e64f19351.png) +![Debugbar 3.3 Screenshot](https://user-images.githubusercontent.com/973269/79428890-196cc680-7fc7-11ea-8229-189f5eac9009.png) + Note: Use the DebugBar only in development. It can slow the application down (because it has to gather data). So when experiencing slowness, try disabling some of the collectors. @@ -50,13 +47,13 @@ Require this package with composer. It is recommended to only require the packag composer require barryvdh/laravel-debugbar --dev ``` -Laravel 5.5 uses Package Auto-Discovery, so doesn't require you to manually add the ServiceProvider. +Laravel uses Package Auto-Discovery, so doesn't require you to manually add the ServiceProvider. The Debugbar will be enabled when `APP_DEBUG` is `true`. > If you use a catch-all/fallback route, make sure you load the Debugbar ServiceProvider before your own App ServiceProviders. -### Laravel 5.5+: +### Laravel without auto-discovery: If you don't use auto-discovery, add the ServiceProvider to the providers array in config/app.php From b94978651cf7992ae77d026b154a2057e1eb78bc Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 16 Apr 2020 10:42:10 +0200 Subject: [PATCH 129/585] Use list for Models collector + sort --- src/DataCollector/ModelsCollector.php | 42 ++++++++++++++++----------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/DataCollector/ModelsCollector.php b/src/DataCollector/ModelsCollector.php index 3ce356c38..88d4e82ba 100644 --- a/src/DataCollector/ModelsCollector.php +++ b/src/DataCollector/ModelsCollector.php @@ -2,15 +2,16 @@ namespace Barryvdh\Debugbar\DataCollector; -use Barryvdh\Debugbar\DataFormatter\SimpleFormatter; -use DebugBar\DataCollector\MessagesCollector; +use DebugBar\DataCollector\DataCollector; +use DebugBar\DataCollector\DataCollectorInterface; +use DebugBar\DataCollector\Renderable; use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Support\Str; /** * Collector for Models. */ -class ModelsCollector extends MessagesCollector +class ModelsCollector extends DataCollector implements DataCollectorInterface, Renderable { public $models = []; @@ -19,9 +20,6 @@ class ModelsCollector extends MessagesCollector */ public function __construct(Dispatcher $events) { - parent::__construct('models'); - $this->setDataFormatter(new SimpleFormatter()); - $events->listen('eloquent.*', function ($event, $models) { if (Str::contains($event, 'eloquent.retrieved')) { foreach (array_filter($models) as $model) { @@ -34,21 +32,31 @@ public function __construct(Dispatcher $events) public function collect() { - foreach ($this->models as $type => $count) { - $this->addMessage($count, $type); - } + ksort($this->models, SORT_NUMERIC); - return [ - 'count' => array_sum($this->models), - 'messages' => $this->getMessages(), - ]; + return array_reverse($this->models); } - public function getWidgets() + /** + * {@inheritDoc} + */ + public function getName() { - $widgets = parent::getWidgets(); - $widgets['models']['icon'] = 'cubes'; + return 'models'; + } - return $widgets; + /** + * {@inheritDoc} + */ + public function getWidgets() + { + return [ + "models" => [ + "icon" => "cubes", + "widget" => "PhpDebugBar.Widgets.HtmlVariableListWidget", + "map" => "models", + "default" => "{}" + ] + ]; } } From ade85aeaddd17540ca3b28c17d78ea013c2078de Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 16 Apr 2020 10:55:00 +0200 Subject: [PATCH 130/585] Update timeline layout --- src/Resources/laravel-debugbar.css | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index a7953515d..dd46f8a64 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -323,14 +323,8 @@ ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-value { ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label, ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-collector { top: 0px; - color: #fff; - font-size: 11px; - text-shadow: 1px 1px #000; -} - -ul.phpdebugbar-widgets-timeline > li:nth-child(n+2) span.phpdebugbar-widgets-label { color: #000; - text-shadow: none; + font-size: 11px; } ul.phpdebugbar-widgets-timeline li .phpdebugbar-widgets-value span.phpdebugbar-widgets-label { @@ -338,6 +332,10 @@ ul.phpdebugbar-widgets-timeline li .phpdebugbar-widgets-value span.phpdebugbar-w text-shadow: 1px 1px #000; } +ul.phpdebugbar-widgets-timeline table.phpdebugbar-widgets-params { + font-size: 11px; +} + div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar { width: calc(100% - 20px); padding: 4px 0px 4px; From 67ced7c6a7451b8bb079d92a57d75f1111f3342d Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 16 Apr 2020 11:09:05 +0200 Subject: [PATCH 131/585] Tweak btn padding --- src/Resources/laravel-debugbar.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index dd46f8a64..2c19c6a24 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -262,7 +262,7 @@ a.phpdebugbar-restore-btn { div.phpdebugbar-header > div > * { font-size: 13px; - padding: 5px 8px; + padding: 5px; } div.phpdebugbar-header .phpdebugbar-tab { From 7deb654bb299c0b68b8b530aa716db2f404230aa Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 16 Apr 2020 17:00:47 +0200 Subject: [PATCH 132/585] Update laravel-debugbar.css --- src/Resources/laravel-debugbar.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index 2c19c6a24..d83a197ef 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -435,8 +435,8 @@ a.phpdebugbar-maximize-btn { a.phpdebugbar-tab.phpdebugbar-active { background: #fa5661; background-image: none; - color: #fff; - text-shadow: 1px 1px #000; + color: #fff !important; + text-shadow: 1px 1px #bf3039; } a.phpdebugbar-tab.phpdebugbar-active span.phpdebugbar-badge { From fb847b4075d333daad3d7356eef2ac9b18e52d13 Mon Sep 17 00:00:00 2001 From: Jonathan Reinink Date: Thu, 16 Apr 2020 11:08:47 -0400 Subject: [PATCH 133/585] Update text shadow on badges --- src/Resources/laravel-debugbar.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index d83a197ef..362455685 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -453,7 +453,7 @@ a.phpdebugbar-tab span.phpdebugbar-badge { font-size: 11px; font-family: monospace; color: #fff; - text-shadow: 1px 1px rgb(46, 46, 46); + text-shadow: 1px 1px #bf3039; border-radius: 10px; top: -1px; position: relative; From 787a4afb7d3a441d4c6683aedd60ac9a18bdade5 Mon Sep 17 00:00:00 2001 From: Jonathan Reinink Date: Thu, 16 Apr 2020 11:13:51 -0400 Subject: [PATCH 134/585] Enabled models collector by default --- config/debugbar.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/debugbar.php b/config/debugbar.php index 3275f105a..d1fcc58b0 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -111,7 +111,7 @@ 'views' => true, // Views with their data 'route' => true, // Current route information 'auth' => false, // Display Laravel authentication status - 'gate' => true, // Display Laravel Gate checks + 'gate' => true, // Display Laravel Gate checks 'session' => true, // Display session data 'symfony_request' => true, // Only one can be enabled.. 'mail' => true, // Catch mail messages @@ -122,7 +122,7 @@ 'files' => false, // Show the included files 'config' => false, // Display config settings 'cache' => false, // Display cache events - 'models' => false, // Display models + 'models' => true, // Display models ], /* From 95c31aab33689cd4572d27038186886f4bfa63ae Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 16 Apr 2020 22:19:40 +0200 Subject: [PATCH 135/585] Add badge to Model Collector --- src/DataCollector/ModelsCollector.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/DataCollector/ModelsCollector.php b/src/DataCollector/ModelsCollector.php index 88d4e82ba..ddbfab75c 100644 --- a/src/DataCollector/ModelsCollector.php +++ b/src/DataCollector/ModelsCollector.php @@ -14,6 +14,7 @@ class ModelsCollector extends DataCollector implements DataCollectorInterface, Renderable { public $models = []; + public $count = 0; /** * @param Dispatcher $events @@ -25,6 +26,7 @@ public function __construct(Dispatcher $events) foreach (array_filter($models) as $model) { $class = get_class($model); $this->models[$class] = ($this->models[$class] ?? 0) + 1; + $this->count++; } } }); @@ -34,7 +36,7 @@ public function collect() { ksort($this->models, SORT_NUMERIC); - return array_reverse($this->models); + return ['data' => array_reverse($this->models), 'count' => $this->count]; } /** @@ -54,8 +56,12 @@ public function getWidgets() "models" => [ "icon" => "cubes", "widget" => "PhpDebugBar.Widgets.HtmlVariableListWidget", - "map" => "models", + "map" => "models.data", "default" => "{}" + ], + 'models:badge' => [ + 'map' => 'models.count', + 'default' => 0 ] ]; } From 2a0cb80d790e96a9a1473ae9a72dc24d4de04e4d Mon Sep 17 00:00:00 2001 From: Luigi Cruz Date: Sat, 18 Apr 2020 23:18:03 +0800 Subject: [PATCH 136/585] Fix hydrated model not displaying --- src/LaravelDebugbar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 1bdc28519..e9a0b9df7 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -403,7 +403,7 @@ function ($event, $params) use ($queryCollector) { } } - if ($this->shouldCollect('models', false)) { + if ($this->shouldCollect('models', true)) { try { $modelsCollector = $this->app->make('Barryvdh\Debugbar\DataCollector\ModelsCollector'); $this->addCollector($modelsCollector); From 57f2219f6d9efe41ed1bc880d86701c52f261bf5 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Tue, 5 May 2020 12:53:32 +0200 Subject: [PATCH 137/585] Treat Livewire like Ajax requests Should render livewire requests like normal XHR requests --- src/LaravelDebugbar.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 1bdc28519..8eecec0c9 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -789,8 +789,8 @@ protected function isDebugbarRequest() */ protected function isJsonRequest(Request $request) { - // If XmlHttpRequest, return true - if ($request->isXmlHttpRequest()) { + // If XmlHttpRequest or Live, return true + if ($request->isXmlHttpRequest() || $request->headers->get('X-Livewire')) { return true; } From b9cb587a45ed8b840ee77f95d7fac83367dfb2f6 Mon Sep 17 00:00:00 2001 From: Dima Gula Date: Wed, 6 May 2020 20:47:09 +0300 Subject: [PATCH 138/585] Exculde 'horizon' path --- config/debugbar.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/debugbar.php b/config/debugbar.php index d1fcc58b0..87c411836 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -16,7 +16,8 @@ 'enabled' => env('DEBUGBAR_ENABLED', null), 'except' => [ - 'telescope*' + 'telescope*', + 'horizon*' ], /* From deb877ffb4aa69a6ca158eb1dc78ca00d6f0a6cd Mon Sep 17 00:00:00 2001 From: Matt Lantz Date: Mon, 11 May 2020 17:09:37 -0400 Subject: [PATCH 139/585] Because its screens have lots of space --- src/Resources/laravel-debugbar.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index 362455685..fa0d35d3e 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -301,11 +301,11 @@ dl.phpdebugbar-widgets-kvlist dd.phpdebugbar-widgets-value.phpdebugbar-widgets-p } dl.phpdebugbar-widgets-kvlist dt { - width: 200px; + width: 25%; } dl.phpdebugbar-widgets-kvlist dd { - margin-left: 210px; + margin-left: 25%; } ul.phpdebugbar-widgets-timeline .phpdebugbar-widgets-measure { From d8a6f5b7ce8ee2ab6b34a12f53cd2be23dd62c54 Mon Sep 17 00:00:00 2001 From: vdauchy Date: Tue, 12 May 2020 14:48:40 -0400 Subject: [PATCH 140/585] Add returned value to measure() Change-Id: I52e822caaee73bfc70f93e1512710e7893bd362b --- composer.json | 2 +- src/LaravelDebugbar.php | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index eddc364bc..a3677e485 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ ], "require": { "php": ">=7.0", - "maximebf/debugbar": "^1.15.1", + "maximebf/debugbar": "^1.16.3", "illuminate/routing": "^5.5|^6|^7", "illuminate/session": "^5.5|^6|^7", "illuminate/support": "^5.5|^6|^7", diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 8eecec0c9..a88c54372 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -900,16 +900,18 @@ public function addMeasure($label, $start, $end) * * @param string $label * @param \Closure $closure + * @return mixed */ public function measure($label, \Closure $closure) { if ($this->hasCollector('time')) { /** @var \DebugBar\DataCollector\TimeDataCollector $collector */ $collector = $this->getCollector('time'); - $collector->measure($label, $closure); + $result = $collector->measure($label, $closure); } else { - $closure(); + $result = $closure(); } + return $result; } /** From 1d211ea7b10b5ac2dcbb5f9fc3c92a069fe61fe7 Mon Sep 17 00:00:00 2001 From: James Fenwick Date: Thu, 21 May 2020 17:15:00 +0100 Subject: [PATCH 141/585] Correctly label Response objects from policies Fixes #979 --- src/DataCollector/GateCollector.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/DataCollector/GateCollector.php b/src/DataCollector/GateCollector.php index 921df7439..f9dce319a 100644 --- a/src/DataCollector/GateCollector.php +++ b/src/DataCollector/GateCollector.php @@ -4,6 +4,7 @@ use Barryvdh\Debugbar\DataFormatter\SimpleFormatter; use DebugBar\DataCollector\MessagesCollector; +use Illuminate\Auth\Access\Response; use Illuminate\Contracts\Auth\Access\Gate; use Illuminate\Contracts\Auth\Authenticatable; use Symfony\Component\VarDumper\Cloner\VarCloner; @@ -38,6 +39,11 @@ public function addCheck($user = null, $ability, $result, $arguments = []) $label = $result ? 'success' : 'error'; + // Response::allowed() was added in Laravel 6.x + if ($result instanceof Response && method_exists($result, 'allowed')) { + $label = $result->allowed() ? 'success' : 'error'; + } + $this->addMessage([ 'ability' => $ability, 'result' => $result, From 3d810992237fd70cf7456b4f1be4beb62eb32b4a Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Sat, 23 May 2020 11:30:34 +0200 Subject: [PATCH 142/585] Basic Livewire collector --- config/debugbar.php | 1 + src/DataCollector/LivewireCollector.php | 82 +++++++++++++++++++++++++ src/LaravelDebugbar.php | 11 ++++ 3 files changed, 94 insertions(+) create mode 100644 src/DataCollector/LivewireCollector.php diff --git a/config/debugbar.php b/config/debugbar.php index d1fcc58b0..73791dc2e 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -123,6 +123,7 @@ 'config' => false, // Display config settings 'cache' => false, // Display cache events 'models' => true, // Display models + 'livewire' => true, // Display Livewire (when available) ], /* diff --git a/src/DataCollector/LivewireCollector.php b/src/DataCollector/LivewireCollector.php new file mode 100644 index 000000000..800e9a96b --- /dev/null +++ b/src/DataCollector/LivewireCollector.php @@ -0,0 +1,82 @@ +getData()['_instance']; + + // Create an unique name for each compoent + $key = $component->getName() . ' #' .$component->id; + + $data = [ + 'data' => $component->getPublicPropertiesDefinedBySubClass(), + ]; + + if ($request->request->get('id') == $component->id) { + $data['oldData'] = $request->request->get('data'); + $data['actionQueue'] = $request->request->get('actionQueue'); + } + + $data['name'] = $component->getName(); + $data['view'] = $view->name(); + $data['component'] = get_class($component); + $data['id'] = $component->id; + + $this->data[$key] = $this->formatVar($data); + }); + } + + public function collect() + { + return ['data' => $this->data, 'count' => count($this->data)]; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'livewire'; + } + + /** + * {@inheritDoc} + */ + public function getWidgets() + { + return [ + "livewire" => [ + "icon" => "bolt", + "widget" => "PhpDebugBar.Widgets.VariableListWidget", + "map" => "livewire.data", + "default" => "{}" + ], + 'livewire:badge' => [ + 'map' => 'livewire.count', + 'default' => 0 + ] + ]; + } +} diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 8eecec0c9..677a5be7b 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -412,6 +412,17 @@ function ($event, $params) use ($queryCollector) { } } + if ($this->shouldCollect('livewire', true) && $this->app->bound('livewire')) { + try { + $livewireCollector = $this->app->make('Barryvdh\Debugbar\DataCollector\LivewireCollector'); + $this->addCollector($livewireCollector); + } catch (\Exception $e){ + $this->addThrowable( + new Exception('Cannot add Livewire Collector: ' . $e->getMessage(), $e->getCode(), $e) + ); + } + } + if ($this->shouldCollect('mail', true) && class_exists('Illuminate\Mail\MailServiceProvider')) { try { $mailer = $this->app['mailer']->getSwiftMailer(); From f4c1daa15be6a505efb0a9bb7eab3ceea10e32d3 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Sun, 14 Jun 2020 18:42:04 +0200 Subject: [PATCH 143/585] Disable hints by default --- config/debugbar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/debugbar.php b/config/debugbar.php index 5b54a3e5d..003860dc8 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -149,7 +149,7 @@ 'enabled' => false, 'types' => ['SELECT'], // // workaround ['SELECT'] only. https://github.com/barryvdh/laravel-debugbar/issues/888 ['SELECT', 'INSERT', 'UPDATE', 'DELETE']; for MySQL 5.6.3+ ], - 'hints' => true, // Show hints for common mistakes + 'hints' => false, // Show hints for common mistakes ], 'mail' => [ 'full_log' => false From a41ea1343fe95a57543f4b24861acbd1bd71bcb6 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Sun, 14 Jun 2020 18:43:08 +0200 Subject: [PATCH 144/585] Only load 5 most relevant traces --- src/DataCollector/QueryCollector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 5a96fe6a5..25092701d 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -139,7 +139,7 @@ public function addQuery($query, $bindings, $time, $connection) if ($this->findSource) { try { - $source = $this->findSource(); + $source = array_slice($this->findSource(), 0, 5); } catch (\Exception $e) { } } From 971f70a00bf129aa028791db58784244a4fbe531 Mon Sep 17 00:00:00 2001 From: Eric Fletcher Date: Thu, 25 Jun 2020 01:32:03 -0400 Subject: [PATCH 145/585] Move route definition into separate file (#1050) * Create debugbar-routes.php * Update ServiceProvider.php * Update debugbar-routes.php --- src/ServiceProvider.php | 41 ++--------------------------------------- src/debugbar-routes.php | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 39 deletions(-) create mode 100644 src/debugbar-routes.php diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 9078fe8e5..a3975ee6f 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -26,6 +26,8 @@ public function register() { $configPath = __DIR__ . '/../config/debugbar.php'; $this->mergeConfigFrom($configPath, 'debugbar'); + + $this->loadRoutesFrom(realpath(__DIR__.'/debugbar-routes.php')); $this->app->alias( DataFormatter::class, @@ -66,45 +68,6 @@ public function boot() $configPath = __DIR__ . '/../config/debugbar.php'; $this->publishes([$configPath => $this->getConfigPath()], 'config'); - $routeConfig = [ - 'namespace' => 'Barryvdh\Debugbar\Controllers', - 'prefix' => $this->app['config']->get('debugbar.route_prefix'), - 'domain' => $this->app['config']->get('debugbar.route_domain'), - 'middleware' => [DebugbarEnabled::class], - ]; - - $this->getRouter()->group($routeConfig, function($router) { - $router->get('open', [ - 'uses' => 'OpenHandlerController@handle', - 'as' => 'debugbar.openhandler', - ]); - - $router->get('clockwork/{id}', [ - 'uses' => 'OpenHandlerController@clockwork', - 'as' => 'debugbar.clockwork', - ]); - - $router->get('telescope/{id}', [ - 'uses' => 'TelescopeController@show', - 'as' => 'debugbar.telescope', - ]); - - $router->get('assets/stylesheets', [ - 'uses' => 'AssetController@css', - 'as' => 'debugbar.assets.css', - ]); - - $router->get('assets/javascript', [ - 'uses' => 'AssetController@js', - 'as' => 'debugbar.assets.js', - ]); - - $router->delete('cache/{key}/{tags?}', [ - 'uses' => 'CacheController@delete', - 'as' => 'debugbar.cache.delete', - ]); - }); - $this->registerMiddleware(InjectDebugbar::class); } diff --git a/src/debugbar-routes.php b/src/debugbar-routes.php new file mode 100644 index 000000000..c8f82d577 --- /dev/null +++ b/src/debugbar-routes.php @@ -0,0 +1,40 @@ + 'Barryvdh\Debugbar\Controllers', + 'prefix' => $this->app['config']->get('debugbar.route_prefix'), + 'domain' => $this->app['config']->get('debugbar.route_domain'), + 'middleware' => [DebugbarEnabled::class], +]; + +$this->app['router']->group($routeConfig, function($router) { + $router->get('open', [ + 'uses' => 'OpenHandlerController@handle', + 'as' => 'debugbar.openhandler', + ]); + + $router->get('clockwork/{id}', [ + 'uses' => 'OpenHandlerController@clockwork', + 'as' => 'debugbar.clockwork', + ]); + + $router->get('telescope/{id}', [ + 'uses' => 'TelescopeController@show', + 'as' => 'debugbar.telescope', + ]); + + $router->get('assets/stylesheets', [ + 'uses' => 'AssetController@css', + 'as' => 'debugbar.assets.css', + ]); + + $router->get('assets/javascript', [ + 'uses' => 'AssetController@js', + 'as' => 'debugbar.assets.js', + ]); + + $router->delete('cache/{key}/{tags?}', [ + 'uses' => 'CacheController@delete', + 'as' => 'debugbar.cache.delete', + ]); +}); From f015e1b76d1a097fa4e3ae9e85a5696428bf75de Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Sat, 27 Jun 2020 17:44:54 +0200 Subject: [PATCH 146/585] Revert "Move route definition into separate file (#1050)" (#1054) This reverts commit 971f70a00bf129aa028791db58784244a4fbe531. --- src/ServiceProvider.php | 41 +++++++++++++++++++++++++++++++++++++++-- src/debugbar-routes.php | 40 ---------------------------------------- 2 files changed, 39 insertions(+), 42 deletions(-) delete mode 100644 src/debugbar-routes.php diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index a3975ee6f..9078fe8e5 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -26,8 +26,6 @@ public function register() { $configPath = __DIR__ . '/../config/debugbar.php'; $this->mergeConfigFrom($configPath, 'debugbar'); - - $this->loadRoutesFrom(realpath(__DIR__.'/debugbar-routes.php')); $this->app->alias( DataFormatter::class, @@ -68,6 +66,45 @@ public function boot() $configPath = __DIR__ . '/../config/debugbar.php'; $this->publishes([$configPath => $this->getConfigPath()], 'config'); + $routeConfig = [ + 'namespace' => 'Barryvdh\Debugbar\Controllers', + 'prefix' => $this->app['config']->get('debugbar.route_prefix'), + 'domain' => $this->app['config']->get('debugbar.route_domain'), + 'middleware' => [DebugbarEnabled::class], + ]; + + $this->getRouter()->group($routeConfig, function($router) { + $router->get('open', [ + 'uses' => 'OpenHandlerController@handle', + 'as' => 'debugbar.openhandler', + ]); + + $router->get('clockwork/{id}', [ + 'uses' => 'OpenHandlerController@clockwork', + 'as' => 'debugbar.clockwork', + ]); + + $router->get('telescope/{id}', [ + 'uses' => 'TelescopeController@show', + 'as' => 'debugbar.telescope', + ]); + + $router->get('assets/stylesheets', [ + 'uses' => 'AssetController@css', + 'as' => 'debugbar.assets.css', + ]); + + $router->get('assets/javascript', [ + 'uses' => 'AssetController@js', + 'as' => 'debugbar.assets.js', + ]); + + $router->delete('cache/{key}/{tags?}', [ + 'uses' => 'CacheController@delete', + 'as' => 'debugbar.cache.delete', + ]); + }); + $this->registerMiddleware(InjectDebugbar::class); } diff --git a/src/debugbar-routes.php b/src/debugbar-routes.php deleted file mode 100644 index c8f82d577..000000000 --- a/src/debugbar-routes.php +++ /dev/null @@ -1,40 +0,0 @@ - 'Barryvdh\Debugbar\Controllers', - 'prefix' => $this->app['config']->get('debugbar.route_prefix'), - 'domain' => $this->app['config']->get('debugbar.route_domain'), - 'middleware' => [DebugbarEnabled::class], -]; - -$this->app['router']->group($routeConfig, function($router) { - $router->get('open', [ - 'uses' => 'OpenHandlerController@handle', - 'as' => 'debugbar.openhandler', - ]); - - $router->get('clockwork/{id}', [ - 'uses' => 'OpenHandlerController@clockwork', - 'as' => 'debugbar.clockwork', - ]); - - $router->get('telescope/{id}', [ - 'uses' => 'TelescopeController@show', - 'as' => 'debugbar.telescope', - ]); - - $router->get('assets/stylesheets', [ - 'uses' => 'AssetController@css', - 'as' => 'debugbar.assets.css', - ]); - - $router->get('assets/javascript', [ - 'uses' => 'AssetController@js', - 'as' => 'debugbar.assets.js', - ]); - - $router->delete('cache/{key}/{tags?}', [ - 'uses' => 'CacheController@delete', - 'as' => 'debugbar.cache.delete', - ]); -}); From bbf7f2521ae84b78ee7bcbb40498752fe2e65beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Nikolaou?= Date: Sat, 27 Jun 2020 19:22:09 +0300 Subject: [PATCH 147/585] Optimize models collector (#1051) * Optimize models collector Use a wildcard to listen to the `eloquent.retrieved:*` events instead of listening to all the `eloquent.*` events. * Add testsfor ModelsCollector --- .gitignore | 4 +- composer.json | 12 +++- phpunit.xml.dist | 32 ++++++++++ src/DataCollector/ModelsCollector.php | 13 ++--- tests/DataCollector/ModelsCollectorTest.php | 65 +++++++++++++++++++++ tests/TestCase.php | 51 ++++++++++++++++ 6 files changed, 165 insertions(+), 12 deletions(-) create mode 100644 phpunit.xml.dist create mode 100644 tests/DataCollector/ModelsCollectorTest.php create mode 100644 tests/TestCase.php diff --git a/.gitignore b/.gitignore index 2c1fc0c14..08dc6ae98 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ +/build /vendor composer.phar composer.lock -.DS_Store \ No newline at end of file +.DS_Store +.phpunit.result.cache diff --git a/composer.json b/composer.json index a3677e485..6d11b0e45 100644 --- a/composer.json +++ b/composer.json @@ -18,6 +18,10 @@ "symfony/debug": "^3|^4|^5", "symfony/finder": "^3|^4|^5" }, + "require-dev": { + "orchestra/testbench": "^3.5|^4.0|^5.0", + "phpunit/phpunit": "^6.0|^7.0|^8.5|^9.0" + }, "autoload": { "psr-4": { "Barryvdh\\Debugbar\\": "src/" @@ -26,6 +30,11 @@ "src/helpers.php" ] }, + "autoload-dev": { + "psr-4": { + "Barryvdh\\Debugbar\\Tests\\": "tests" + } + }, "minimum-stability": "dev", "prefer-stable": true, "extra": { @@ -40,8 +49,5 @@ "Debugbar": "Barryvdh\\Debugbar\\Facade" } } - }, - "require-dev": { - "laravel/framework": "5.5.x" } } diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 000000000..11da2e94f --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,32 @@ + + + + + tests + + + + + src/ + + + + + + + + + + + + + diff --git a/src/DataCollector/ModelsCollector.php b/src/DataCollector/ModelsCollector.php index ddbfab75c..0ac99cf09 100644 --- a/src/DataCollector/ModelsCollector.php +++ b/src/DataCollector/ModelsCollector.php @@ -6,7 +6,6 @@ use DebugBar\DataCollector\DataCollectorInterface; use DebugBar\DataCollector\Renderable; use Illuminate\Contracts\Events\Dispatcher; -use Illuminate\Support\Str; /** * Collector for Models. @@ -21,13 +20,11 @@ class ModelsCollector extends DataCollector implements DataCollectorInterface, R */ public function __construct(Dispatcher $events) { - $events->listen('eloquent.*', function ($event, $models) { - if (Str::contains($event, 'eloquent.retrieved')) { - foreach (array_filter($models) as $model) { - $class = get_class($model); - $this->models[$class] = ($this->models[$class] ?? 0) + 1; - $this->count++; - } + $events->listen('eloquent.retrieved:*', function ($event, $models) { + foreach (array_filter($models) as $model) { + $class = get_class($model); + $this->models[$class] = ($this->models[$class] ?? 0) + 1; + $this->count++; } }); } diff --git a/tests/DataCollector/ModelsCollectorTest.php b/tests/DataCollector/ModelsCollectorTest.php new file mode 100644 index 000000000..e96d4c0a9 --- /dev/null +++ b/tests/DataCollector/ModelsCollectorTest.php @@ -0,0 +1,65 @@ +loadLaravelMigrations(); + + $this->debugbar()->boot(); + + /** @var \Barryvdh\Debugbar\DataCollector\ModelsCollector $collector */ + $collector = $this->debugbar()->getCollector('models'); + + User::create([ + 'name' => 'John Doe', + 'email' => 'john@example.com', + 'password' => Hash::make('password'), + ]); + + User::create([ + 'name' => 'Jane Doe', + 'email' => 'jane@example.com', + 'password' => Hash::make('password'), + ]); + + $this->assertEquals( + ['data' => [], 'count' => 0], + $collector->collect() + ); + + User::first(); + + $this->assertEquals( + ['data' => [User::class => 1], 'count' => 1], + $collector->collect() + ); + + Person::all(); + + $this->assertEquals( + ['data' => [User::class => 1, Person::class => 2], 'count' => 3], + $collector->collect() + ); + } +} + +class User extends Model +{ + protected $table = 'users'; + protected $guarded = []; +} + +class Person extends User +{ +} diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 000000000..eb13371d0 --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,51 @@ + Facade::class]; + } + + public function getEnvironmentSetUp($app) + { + } + + /** + * Get the Laravel Debugbar instance. + * + * @return \Barryvdh\Debugbar\LaravelDebugbar + */ + public function debugbar() + { + return $this->debugbar ?? $this->debugbar = $this->app->debugbar; + } +} From db074ffa7d5a18e5e8750fb9482f96c5ddf89540 Mon Sep 17 00:00:00 2001 From: lifecodeof <42251538+lifecodeof@users.noreply.github.com> Date: Sun, 19 Jul 2020 14:20:42 +0300 Subject: [PATCH 148/585] fixed Server Timing API (#1061) * fixed Server Timing API * escaped double quote from label --- src/LaravelDebugbar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index a592f6be6..e144af638 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -1084,7 +1084,7 @@ protected function addServerTimingHeaders(Response $response) $headers = []; foreach ($collector->collect()['measures'] as $k => $m) { - $headers[] = sprintf('%d=%F; "%s"', $k, $m['duration'] * 1000, str_replace('"', "'", $m['label'])); + $headers[] = sprintf('app;desc="%s";dur=%F', str_replace('"', "'", $m['label']), $m['duration'] * 1000); } $response->headers->set('Server-Timing', $headers, false); From 72d54ea09f1e566f38059ca118430f025cd10b62 Mon Sep 17 00:00:00 2001 From: Ankur Kumar Date: Sun, 19 Jul 2020 18:09:48 +0530 Subject: [PATCH 149/585] Create .gitattributes (#1062) --- .gitattributes | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..41015da35 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +* text=auto + +/.github export-ignore +/tests export-ignore +.editorconfig export-ignore +.gitattributes export-ignore +.gitignore export-ignore +changelog.md export-ignore +phpunit.xml.dist export-ignore From ac034425d959a459f133b2da4ba714f018833aac Mon Sep 17 00:00:00 2001 From: Oleksii Prudkyi Date: Mon, 27 Jul 2020 11:14:55 +0300 Subject: [PATCH 150/585] Query collector - support of non-pdo database drivers (#1065) Fixes #1064, when there is no pdo connection : - provides fallback method for $pdo->quote - disables EXPLAINs --- src/DataCollector/QueryCollector.php | 29 +++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 25092701d..46757fd5b 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -106,11 +106,16 @@ public function addQuery($query, $bindings, $time, $connection) $startTime = $endTime - $time; $hints = $this->performQueryAnalysis($query); - $pdo = $connection->getPdo(); + $pdo = null; + try { + $pdo = $connection->getPdo(); + } catch (\Exception $e) { + // ignore error for non-pdo laravel drivers + } $bindings = $connection->prepareBindings($bindings); // Run EXPLAIN on this query (if needed) - if ($this->explainQuery && preg_match('/^\s*('.implode('|', $this->explainTypes).') /i', $query)) { + if ($this->explainQuery && $pdo && preg_match('/^\s*('.implode('|', $this->explainTypes).') /i', $query)) { $statement = $pdo->prepare('EXPLAIN ' . $query); $statement->execute($bindings); $explainResults = $statement->fetchAll(\PDO::FETCH_CLASS); @@ -128,7 +133,11 @@ public function addQuery($query, $bindings, $time, $connection) // Mimic bindValue and only quote non-integer and non-float data types if (!is_int($binding) && !is_float($binding)) { - $binding = $pdo->quote($binding); + if ($pdo) { + $binding = $pdo->quote($binding); + } else { + $binding = $this->emulateQuote($binding); + } } $query = preg_replace($regex, $binding, $query, 1); @@ -160,6 +169,20 @@ public function addQuery($query, $bindings, $time, $connection) } } + /** + * Mimic mysql_real_escape_string + * + * @param string $value + * @return string + */ + protected function emulateQuote($value) + { + $search = ["\\", "\x00", "\n", "\r", "'", '"', "\x1a"]; + $replace = ["\\\\","\\0","\\n", "\\r", "\'", '\"', "\\Z"]; + + return "'" . str_replace($search, $replace, $value) . "'"; + } + /** * Explainer::performQueryAnalysis() * From c0ba718c95eadeadd0d8d7ee4ab7e42fd99260da Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Tue, 28 Jul 2020 21:31:11 +0200 Subject: [PATCH 151/585] Create stale.yml --- .github/stale.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 000000000..36cd06661 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,24 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 60 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - bug + - enhancement + - discussion +# Label to use when marking an issue as stale +staleLabel: stale +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. + + If this issue is still present on the latest version of this library on supported Laravel versions, + please let us know by replying to this issue so we can investigate further. + + Thank you for your contribution! Apologies for any delayed response on our side. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false +# Limit to only `issues` or `pulls` +only: issues From 31f660f8d1c1ae83f30ca548389e9d1d56addd3b Mon Sep 17 00:00:00 2001 From: Ernestas Kvedaras Date: Sat, 1 Aug 2020 16:18:22 +0200 Subject: [PATCH 152/585] Add Laravel / PHP prefixes to their version tooltips (#1073) --- src/DataCollector/LaravelCollector.php | 2 +- src/DataCollector/PhpInfoCollector.php | 17 +++++++++++++++++ src/LaravelDebugbar.php | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 src/DataCollector/PhpInfoCollector.php diff --git a/src/DataCollector/LaravelCollector.php b/src/DataCollector/LaravelCollector.php index ec327deb6..4201058e2 100644 --- a/src/DataCollector/LaravelCollector.php +++ b/src/DataCollector/LaravelCollector.php @@ -50,7 +50,7 @@ public function getWidgets() return [ "version" => [ "icon" => "github", - "tooltip" => "Version", + "tooltip" => "Laravel Version", "map" => "laravel.version", "default" => "" ], diff --git a/src/DataCollector/PhpInfoCollector.php b/src/DataCollector/PhpInfoCollector.php new file mode 100644 index 000000000..764b9ccb3 --- /dev/null +++ b/src/DataCollector/PhpInfoCollector.php @@ -0,0 +1,17 @@ + Date: Tue, 11 Aug 2020 11:07:29 +0200 Subject: [PATCH 153/585] Add dark mode (#1074) * Add dark mode * Add a new config option to control dark mode. Supported modes: auto, dark, light Optimize dark mode css * Implement dark mode using multiple css files * Add Dracula theme for HighlighJS Based on https://github.com/dracula/highlightjs/blob/master/dracula.css * Update laravel-debugbar-dark-mode.css Co-authored-by: Barry vd. Heuvel --- config/debugbar.php | 10 + src/JavascriptRenderer.php | 11 + .../laravel-debugbar-dark-mode-media-end.css | 1 + ...laravel-debugbar-dark-mode-media-start.css | 1 + src/Resources/laravel-debugbar-dark-mode.css | 265 ++++++++++++++++++ src/Resources/laravel-debugbar.css | 5 +- 6 files changed, 290 insertions(+), 3 deletions(-) create mode 100644 src/Resources/laravel-debugbar-dark-mode-media-end.css create mode 100644 src/Resources/laravel-debugbar-dark-mode-media-start.css create mode 100644 src/Resources/laravel-debugbar-dark-mode.css diff --git a/config/debugbar.php b/config/debugbar.php index 003860dc8..af3eb405b 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -202,4 +202,14 @@ | To override default domain, specify it as a non-empty value. */ 'route_domain' => null, + + /* + |-------------------------------------------------------------------------- + | DebugBar theme + |-------------------------------------------------------------------------- + | + | Switches between light and dark theme. If set to auto it will respect system preferences + | Possible values: auto, light, dark + */ + 'theme' => 'auto', ]; diff --git a/src/JavascriptRenderer.php b/src/JavascriptRenderer.php index df4423258..e2514b83d 100644 --- a/src/JavascriptRenderer.php +++ b/src/JavascriptRenderer.php @@ -21,6 +21,17 @@ public function __construct(DebugBar $debugBar, $baseUrl = null, $basePath = nul $this->cssVendors['fontawesome'] = __DIR__ . '/Resources/vendor/font-awesome/style.css'; $this->jsFiles['laravel-sql'] = __DIR__ . '/Resources/sqlqueries/widget.js'; $this->jsFiles['laravel-cache'] = __DIR__ . '/Resources/cache/widget.js'; + + $theme = config('debugbar.theme', 'auto'); + switch ($theme) { + case 'dark': + $this->cssFiles['laravel-dark'] = __DIR__ . '/Resources/laravel-debugbar-dark-mode.css'; + break; + case 'auto': + $this->cssFiles['laravel-dark-0'] = __DIR__ . '/Resources/laravel-debugbar-dark-mode-media-start.css'; + $this->cssFiles['laravel-dark-1'] = __DIR__ . '/Resources/laravel-debugbar-dark-mode.css'; + $this->cssFiles['laravel-dark-2'] = __DIR__ . '/Resources/laravel-debugbar-dark-mode-media-end.css'; + } } /** diff --git a/src/Resources/laravel-debugbar-dark-mode-media-end.css b/src/Resources/laravel-debugbar-dark-mode-media-end.css new file mode 100644 index 000000000..ff30235f0 --- /dev/null +++ b/src/Resources/laravel-debugbar-dark-mode-media-end.css @@ -0,0 +1 @@ +} \ No newline at end of file diff --git a/src/Resources/laravel-debugbar-dark-mode-media-start.css b/src/Resources/laravel-debugbar-dark-mode-media-start.css new file mode 100644 index 000000000..5b1a64706 --- /dev/null +++ b/src/Resources/laravel-debugbar-dark-mode-media-start.css @@ -0,0 +1 @@ +@media (prefers-color-scheme: dark) { \ No newline at end of file diff --git a/src/Resources/laravel-debugbar-dark-mode.css b/src/Resources/laravel-debugbar-dark-mode.css new file mode 100644 index 000000000..298be0934 --- /dev/null +++ b/src/Resources/laravel-debugbar-dark-mode.css @@ -0,0 +1,265 @@ +/* Dark mode */ + +div.phpdebugbar, +div.phpdebugbar-openhandler { + --color-gray-100: #F7FAFC; + --color-gray-200: #EDF2F7; + --color-gray-300: #E2E8F0; + --color-gray-400: #CBD5E0; + --color-gray-500: #A0AEC0; + --color-gray-600: #718096; + --color-gray-700: #4A5568; + --color-gray-800: #2D3748; + --color-gray-900: #1A202C; + --color-red-vivid: #FF0040; +} + +div.phpdebugbar, +div.phpdebugbar-openhandler { + background: var(--color-gray-800); +} + +div.phpdebugbar, +div.phpdebugbar-openhandler, +div.phpdebugbar div.phpdebugbar-header > div > *, +div.phpdebugbar ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label, +div.phpdebugbar ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-collector, +div.phpdebugbar code.phpdebugbar-widgets-sql span.hljs-keyword, +div.phpdebugbar-openhandler .phpdebugbar-openhandler-header, +div.phpdebugbar-openhandler .phpdebugbar-openhandler-header a { + color: var(--color-gray-200); +} + +div.phpdebugbar-openhandler, +div.phpdebugbar div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar, +div.phpdebugbar div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item pre.phpdebugbar-widgets-file, +div.phpdebugbar ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item table.phpdebugbar-widgets-params, +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > a, +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions button, +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form input, +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form select { + border-color: var(--color-gray-600); +} + +div.phpdebugbar div.phpdebugbar-header, +div.phpdebugbar div.phpdebugbar-panel div.phpdebugbar-widgets-status > span:first-child:before, +div.phpdebugbar-openhandler table th, +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > a, +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions button, +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form input, +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form select { + text-shadow: 1px 1px var(--color-gray-700); +} + +div.phpdebugbar div.phpdebugbar-header > div > select, +div.phpdebugbar ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item table.phpdebugbar-widgets-params, +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form input, +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form select, +div.phpdebugbar input[type='text'], +div.phpdebugbar input[type='password'] { + background-color: var(--color-gray-800); +} + +div.phpdebugbar div.phpdebugbar-header, +div.phpdebugbar a.phpdebugbar-restore-btn, +div.phpdebugbar-openhandler .phpdebugbar-openhandler-header, +div.phpdebugbar dl.phpdebugbar-widgets-kvlist > :nth-child(4n-1), +div.phpdebugbar dl.phpdebugbar-widgets-kvlist > :nth-child(4n), +div.phpdebugbar ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item:nth-child(even), +div.phpdebugbar .hljs, +div.phpdebugbar div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params th, +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > a, +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions button, +div.phpdebugbar div.phpdebugbar-widgets-templates table.phpdebugbar-widgets-params th { + background-color: var(--color-gray-900); +} + +div.phpdebugbar .phpdebugbar-widgets-mails ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-headers, +div.phpdebugbar ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item table.phpdebugbar-widgets-params { + border-left-color: var(--color-gray-600); +} + +div.phpdebugbar a.phpdebugbar-tab:hover, +div.phpdebugbar span.phpdebugbar-indicator:hover, +div.phpdebugbar a.phpdebugbar-indicator:hover, +div.phpdebugbar a.phpdebugbar-close-btn:hover, +div.phpdebugbar a.phpdebugbar-minimize-btn:hover, +div.phpdebugbar a.phpdebugbar-maximize-btn:hover, +div.phpdebugbar a.phpdebugbar-open-btn:hover, +div.phpdebugbar-openhandler table th, +div.phpdebugbar-openhandler table tr:nth-child(2n), +div.phpdebugbar div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar { + background-color: var(--color-gray-700); +} + +div.phpdebugbar .phpdebugbar-indicator span.phpdebugbar-tooltip, +div.phpdebugbar div.phpdebugbar-mini-design a.phpdebugbar-tab:hover span.phpdebugbar-text, +div.phpdebugbar pre.sf-dump, +div.phpdebugbar .hljs, +div.phpdebugbar code.phpdebugbar-widgets-sql span.hljs-operator { + color: var(--color-gray-100); +} + +div.phpdebugbar div.phpdebugbar-panel div.phpdebugbar-widgets-status > span:first-child:before, +div.phpdebugbar-openhandler a { + color: var(--color-gray-500); +} + +div.phpdebugbar .phpdebugbar-indicator span.phpdebugbar-tooltip, +div.phpdebugbar div.phpdebugbar-mini-design a.phpdebugbar-tab:hover span.phpdebugbar-text { + background: var(--color-gray-900); +} + +div.phpdebugbar .hljs-tag .hljs-value, +div.phpdebugbar .hljs-phpdoc, +div.phpdebugbar .tex .hljs-formula, +div.phpdebugbar div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-message { + color: var(--color-red-vivid); +} + +div.phpdebugbar div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-filename, +div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-database, +div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-duration, +div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-memory, +div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-row-count, +div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-copy-clipboard, +div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-stmt-id, +div.phpdebugbar .phpdebugbar-text-muted, +div.phpdebugbar-openhandler .phpdebugbar-text-muted +{ + color: var(--color-gray-600); +} + +div.phpdebugbar-openhandler { + border-top-color: #fa5661; +} + +div.phpdebugbar div.phpdebugbar-header .phpdebugbar-tab { + border-left-color: var(--color-gray-800); +} + +div.phpdebugbar div.phpdebugbar-body { + border-top-color: var(--color-gray-800); +} + +div.phpdebugbar a.phpdebugbar-restore-btn { + border-right-color: var(--color-gray-800) !important; +} + +div.phpdebugbar span.phpdebugbar-indicator, +div.phpdebugbar a.phpdebugbar-indicator, +div.phpdebugbar a.phpdebugbar-close-btn { + border-right-color: var(--color-gray-800); +} + +div.phpdebugbar div.phpdebugbar-panel div.phpdebugbar-widgets-status { + background-color: var(--color-gray-900) !important; + border-bottom-color: var(--color-gray-800) !important; +} + +div.phpdebugbar div.phpdebugbar-widgets-templates div.phpdebugbar-widgets-status { + background: var(--color-gray-900) !important; +} + +div.phpdebugbar div.phpdebugbar-panel div.phpdebugbar-widgets-status > * { + color: var(--color-gray-200) !important; +} + +div.phpdebugbar div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-render-time, +div.phpdebugbar div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-memory, +div.phpdebugbar div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-param-count, +div.phpdebugbar div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-type { + color: var(--color-gray-600) !important; +} + +div.phpdebugbar div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params td { + border-color: var(--color-gray-600) !important; +} + +div.phpdebugbar code, +div.phpdebugbar pre { + color: #FFF; +} + +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > a, +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions button, +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form input, +div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form select, +div.phpdebugbar input[type='text'], +div.phpdebugbar input[type='password'] { + color: var(--color-gray-300); +} + +div.phpdebugbar a.phpdebugbar-minimize-btn { + background: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201792%201792%22%20id%3D%22chevron-down%22%3E%3Cpath%20d%3D%22M1683%20808l-742%20741q-19%2019-45%2019t-45-19l-742-741q-19-19-19-45.5t19-45.5l166-165q19-19%2045-19t45%2019l531%20531%20531-531q19-19%2045-19t45%2019l166%20165q19%2019%2019%2045.5t-19%2045.5z%22%20style%3D%22fill%3A%20%23EDF2F7%22%2F%3E%3C%2Fsvg%3E) no-repeat 6px 6px / 14px 14px; +} + +div.phpdebugbar a.phpdebugbar-maximize-btn { + background: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201792%201792%22%20id%3D%22chevron-up%22%3E%3Cpath%20d%3D%22M1683%201331l-166%20165q-19%2019-45%2019t-45-19l-531-531-531%20531q-19%2019-45%2019t-45-19l-166-165q-19-19-19-45.5t19-45.5l742-741q19-19%2045-19t45%2019l742%20741q19%2019%2019%2045.5t-19%2045.5z%22%20style%3D%22fill%3A%20%23EDF2F7%22%2F%3E%3C%2Fsvg%3E) no-repeat 6px 6px / 14px 14px; +} + +div.phpdebugbar a.phpdebugbar-open-btn { + background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAOCAYAAADJ7fe0AAABL0lEQVQ4T63RvWqEQBAA4LkjdX46WQh5hoDpFnyAgGzKWKQJdgFfIT8vIKSwCSEQHyJN4AptomAlZMXAVRuxWAwibqMGBUVygdtAplmWmf1mmVnAP8RiNDjne0KINQDsz9wCIXSwrc+AMMa6tm0hz3PozzGWyyUoirJhIISm5n1yuIRh2G3rNs+rqvo7IoTIMMbnkthqXjf9JAiCO0LIR9d1T5JQXzbMbEA8z/vEGB9TSt/KsjyURZIkeTQM43JAHMe5JYS8Zlm2appG1gDXdbFt2/44IIVS+lCW5amswBh70XX9DADEuOIjzvlaCCFrgO/7F5ZlPU8rTtP0viiKK1mhqqpU07STfrATEsfxV13Xu7JIFEU3pmlej/XjTDAA7MgiAPAOANlP5A/vN0u/AXUIgA+u4l6FAAAAAElFTkSuQmCC) no-repeat 8px 6px; +} + +div.phpdebugbar a.phpdebugbar-close-btn { + background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAABUElEQVQ4T52SMUsDMRTH3xUPChHCxeko3YrUqeFGEexym3boJ9DBufhJpLODfoIbTrcuFcSxxMki3Uq5yZRAAweRVp70HdfWQZolb3i/l/x/iQd7Lm9PDjbA2Wz24HmerFarbSGEwaFaa57n+XC1WqlarXZNBxUgQsaYK+ccCCHeGWPn2GStfdFat3zfB875I8EFOBqNPpbLZRObsQnh9YktHIarUqmMoyg6wboApZTtfr9/zxhrEIw7QdbaSa/Xu1FKDTdAADiQUp6VYcpTgl4B4HsbhCRJjsIwfPN9/7hs2zn3mWXZabfb/dqRg/ZIBF2PmigzCiPbRcbpdKrQXjkTguXMKKxer8uNqw4Gg6cgCC7WT/ArAuty5vl8/hzH8eV2xmaapnec88baHooAEmaMmXQ6nVsAGO/IAQB8x0MAUGQPbSMPAAuC/gL//XV/ALsvnQ+MsaHgAAAAAElFTkSuQmCC) no-repeat 9px 6px; +} + + +/* Dracula Theme v1.2.5 + * + * https://github.com/dracula/highlightjs + * + * Copyright 2016-present, All rights reserved + * + * Code licensed under the MIT license + * + * @author Denis Ciccale + * @author Zeno Rocha + */ + +div.phpdebugbar .hljs-built_in, +div.phpdebugbar .hljs-selector-tag, +div.phpdebugbar .hljs-section, +div.phpdebugbar .hljs-link { + color: #8be9fd; +} + +div.phpdebugbar .hljs-keyword { + color: #ff79c6; +} + +div.phpdebugbar .hljs, +div.phpdebugbar .hljs-subst { + color: #f8f8f2; +} + +div.phpdebugbar .hljs-title { + color: #50fa7b; +} + +div.phpdebugbar .hljs-string, +div.phpdebugbar .hljs-meta, +div.phpdebugbar .hljs-name, +div.phpdebugbar .hljs-type, +div.phpdebugbar .hljs-attr, +div.phpdebugbar .hljs-symbol, +div.phpdebugbar .hljs-bullet, +div.phpdebugbar .hljs-addition, +div.phpdebugbar .hljs-variable, +div.phpdebugbar .hljs-template-tag, +div.phpdebugbar .hljs-template-variable { + color: #f1fa8c; +} + +div.phpdebugbar .hljs-comment, +div.phpdebugbar .hljs-quote, +div.phpdebugbar .hljs-deletion { + color: #6272a4; +} + +div.phpdebugbar .hljs-literal, +div.phpdebugbar .hljs-number { + color: #bd93f9; +} diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index fa0d35d3e..db2cc92c2 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -1,4 +1,3 @@ - /* Force Laravel Whoops exception handler to be displayed under the debug bar */ .Whoops.container { z-index: 5999999; @@ -383,7 +382,7 @@ div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-w text-shadow: 1px 1px #585858; transition: background-color .25s linear 0s, color .25s linear 0s; color: #FFF; - + -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; @@ -473,7 +472,7 @@ div.phpdebugbar-mini-design a.phpdebugbar-tab:hover span.phpdebugbar-text { white-space: nowrap; padding: 2px 18px; text-shadow: none; - + -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; From 864c8808cce3b8a33691ff9717a5a78d68765931 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Tue, 11 Aug 2020 12:28:32 +0200 Subject: [PATCH 154/585] Update laravel-debugbar-dark-mode.css --- src/Resources/laravel-debugbar-dark-mode.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Resources/laravel-debugbar-dark-mode.css b/src/Resources/laravel-debugbar-dark-mode.css index 298be0934..96a2221a3 100644 --- a/src/Resources/laravel-debugbar-dark-mode.css +++ b/src/Resources/laravel-debugbar-dark-mode.css @@ -130,6 +130,10 @@ div.phpdebugbar-openhandler .phpdebugbar-text-muted color: var(--color-gray-600); } +div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item.phpdebugbar-widgets-sql-duplicate { + background-color: #6f6200; +} + div.phpdebugbar-openhandler { border-top-color: #fa5661; } From 9e785aa5584e8839fd43070202dd7f2e912db51c Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Tue, 11 Aug 2020 12:30:51 +0200 Subject: [PATCH 155/585] Update laravel-debugbar-dark-mode.css --- src/Resources/laravel-debugbar-dark-mode.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Resources/laravel-debugbar-dark-mode.css b/src/Resources/laravel-debugbar-dark-mode.css index 96a2221a3..93c182614 100644 --- a/src/Resources/laravel-debugbar-dark-mode.css +++ b/src/Resources/laravel-debugbar-dark-mode.css @@ -134,6 +134,10 @@ div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-i background-color: #6f6200; } +div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value:before { + color: #7B7B7B; +} + div.phpdebugbar-openhandler { border-top-color: #fa5661; } From 0a09f4cb8879e4e31394553b953410cd941fcda7 Mon Sep 17 00:00:00 2001 From: Ernestas Kvedaras Date: Tue, 11 Aug 2020 12:51:44 +0200 Subject: [PATCH 156/585] Improve widgets contrast for duplicate queries background (#1080) --- src/Resources/laravel-debugbar-dark-mode.css | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Resources/laravel-debugbar-dark-mode.css b/src/Resources/laravel-debugbar-dark-mode.css index 93c182614..66d52dc8e 100644 --- a/src/Resources/laravel-debugbar-dark-mode.css +++ b/src/Resources/laravel-debugbar-dark-mode.css @@ -198,6 +198,16 @@ div.phpdebugbar input[type='password'] { color: var(--color-gray-300); } +div.phpdebugbar div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item.phpdebugbar-widgets-sql-duplicate span.phpdebugbar-widgets-filename, +div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item.phpdebugbar-widgets-sql-duplicate span.phpdebugbar-widgets-database, +div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item.phpdebugbar-widgets-sql-duplicate span.phpdebugbar-widgets-duration, +div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item.phpdebugbar-widgets-sql-duplicate span.phpdebugbar-widgets-memory, +div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item.phpdebugbar-widgets-sql-duplicate span.phpdebugbar-widgets-row-count, +div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item.phpdebugbar-widgets-sql-duplicate span.phpdebugbar-widgets-copy-clipboard, +div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item.phpdebugbar-widgets-sql-duplicate span.phpdebugbar-widgets-stmt-id { + color: var(--color-gray-500); +} + div.phpdebugbar a.phpdebugbar-minimize-btn { background: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201792%201792%22%20id%3D%22chevron-down%22%3E%3Cpath%20d%3D%22M1683%20808l-742%20741q-19%2019-45%2019t-45-19l-742-741q-19-19-19-45.5t19-45.5l166-165q19-19%2045-19t45%2019l531%20531%20531-531q19-19%2045-19t45%2019l166%20165q19%2019%2019%2045.5t-19%2045.5z%22%20style%3D%22fill%3A%20%23EDF2F7%22%2F%3E%3C%2Fsvg%3E) no-repeat 6px 6px / 14px 14px; } From 1175e4528c3644c026135f79551d3f4c9dc081bd Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Sat, 15 Aug 2020 16:02:05 +0200 Subject: [PATCH 157/585] Inject in head instead of body (#1084) --- src/LaravelDebugbar.php | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 93fbc76f0..fecdf4cf5 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -873,18 +873,39 @@ public function injectDebugbar(Response $response) $renderer->setOpenHandlerUrl($openHandlerUrl); } - $renderedContent = $renderer->renderHead() . $renderer->render(); + $head = $renderer->renderHead(); + $widget = $renderer->render(); + // Try to put the js/css directly before the + $pos = strripos($content, ''); + if (false !== $pos) { + $content = substr($content, 0, $pos) . $head . substr($content, $pos); + } else { + // Append the head before the widget + $widget = $head . $widget; + } + + // Try to put the widget at the end, directly before the $pos = strripos($content, ''); if (false !== $pos) { - $content = substr($content, 0, $pos) . $renderedContent . substr($content, $pos); + $content = substr($content, 0, $pos) . $widget . substr($content, $pos); } else { - $content = $content . $renderedContent; + $content = $content . $widget; + } + + $original = null; + if ($response instanceof \Illuminate\Http\Response && $response->getOriginalContent()) { + $original = $response->getOriginalContent(); } // Update the new content and reset the content length $response->setContent($content); $response->headers->remove('Content-Length'); + + // Restore original response (eg. the View or Ajax data) + if ($original) { + $response->original = $original; + } } /** From 3fd8648110c8d0c68fa1b4499995d04a161bdfd5 Mon Sep 17 00:00:00 2001 From: Jonathan Zarate Date: Sun, 16 Aug 2020 06:48:25 -0500 Subject: [PATCH 158/585] Fix comma on config file (#1082) --- config/debugbar.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/config/debugbar.php b/config/debugbar.php index af3eb405b..48f5e8c4c 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -17,7 +17,7 @@ 'enabled' => env('DEBUGBAR_ENABLED', null), 'except' => [ 'telescope*', - 'horizon*' + 'horizon*', ], /* @@ -37,7 +37,7 @@ 'driver' => 'file', // redis, file, pdo, custom 'path' => storage_path('debugbar'), // For file driver 'connection' => null, // Leave null for default connection (Redis/PDO) - 'provider' => '' // Instance of StorageInterface for custom driver + 'provider' => '', // Instance of StorageInterface for custom driver ], /* @@ -152,19 +152,19 @@ 'hints' => false, // Show hints for common mistakes ], 'mail' => [ - 'full_log' => false + 'full_log' => false, ], 'views' => [ 'data' => false, //Note: Can slow down the application, because the data can be quite large.. ], 'route' => [ - 'label' => true // show complete route on bar + 'label' => true, // show complete route on bar ], 'logs' => [ - 'file' => null + 'file' => null, ], 'cache' => [ - 'values' => true // collect cache values + 'values' => true, // collect cache values ], ], From b67283a1009ee9fdda95047638ee97aef3c20a2e Mon Sep 17 00:00:00 2001 From: Ernestas Kvedaras Date: Sun, 16 Aug 2020 13:49:02 +0200 Subject: [PATCH 159/585] Add option to show copy button for each query. Disabled by default (#1072) --- config/debugbar.php | 1 + src/DataCollector/QueryCollector.php | 14 +++++++++++ src/LaravelDebugbar.php | 4 +++ src/Resources/sqlqueries/widget.js | 37 ++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+) diff --git a/config/debugbar.php b/config/debugbar.php index 48f5e8c4c..333cc3b1a 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -150,6 +150,7 @@ 'types' => ['SELECT'], // // workaround ['SELECT'] only. https://github.com/barryvdh/laravel-debugbar/issues/888 ['SELECT', 'INSERT', 'UPDATE', 'DELETE']; for MySQL 5.6.3+ ], 'hints' => false, // Show hints for common mistakes + 'show_copy' => false, // Show copy button next to the query ], 'mail' => [ 'full_log' => false, diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 46757fd5b..f98920deb 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -18,6 +18,7 @@ class QueryCollector extends PDOCollector protected $explainQuery = false; protected $explainTypes = ['SELECT']; // ['SELECT', 'INSERT', 'UPDATE', 'DELETE']; for MySQL 5.6.3+ protected $showHints = false; + protected $showCopyButton = false; protected $reflection = []; protected $backtraceExcludePaths = [ '/vendor/laravel/framework/src/Illuminate/Database', @@ -54,6 +55,16 @@ public function setShowHints($enabled = true) $this->showHints = $enabled; } + /** + * Show or hide copy button next to the queries + * + * @param boolean $enabled + */ + public function setShowCopyButton($enabled = true) + { + $this->showCopyButton = $enabled; + } + /** * Enable/disable finding the source * @@ -162,6 +173,7 @@ public function addQuery($query, $bindings, $time, $connection) 'explain' => $explainResults, 'connection' => $connection->getDatabaseName(), 'hints' => $this->showHints ? $hints : null, + 'show_copy' => $this->showCopyButton, ]; if ($this->timeCollector !== null) { @@ -425,6 +437,7 @@ public function collectTransactionEvent($event, $connection) 'explain' => [], 'connection' => $connection->getDatabaseName(), 'hints' => null, + 'show_copy' => false, ]; } @@ -454,6 +467,7 @@ public function collect() 'params' => [], 'bindings' => $query['bindings'], 'hints' => $query['hints'], + 'show_copy' => $query['show_copy'], 'backtrace' => array_values($query['source']), 'duration' => $query['time'], 'duration_str' => ($query['type'] == 'transaction') ? '' : $this->formatDuration($query['time']), diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index fecdf4cf5..29ab4642a 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -321,6 +321,10 @@ function ($level, $message = null, $context = null) use ($logger) { $queryCollector->setShowHints(true); } + if ($this->app['config']->get('debugbar.options.db.show_copy', false)) { + $queryCollector->setShowCopyButton(true); + } + $this->addCollector($queryCollector); try { diff --git a/src/Resources/sqlqueries/widget.js b/src/Resources/sqlqueries/widget.js index 4959e0837..0b1104471 100644 --- a/src/Resources/sqlqueries/widget.js +++ b/src/Resources/sqlqueries/widget.js @@ -25,6 +25,33 @@ this.set('exclude', excludedLabels); }, + onCopyToClipboard: function (el) { + var code = $(el).parent('li').find('code').get(0); + var copy = function () { + try { + document.execCommand('copy'); + alert('Query copied to the clipboard'); + } catch (err) { + console.log('Oops, unable to copy'); + } + }; + var select = function (node) { + if (document.selection) { + var range = document.body.createTextRange(); + range.moveToElementText(node); + range.select(); + } else if (window.getSelection) { + var range = document.createRange(); + range.selectNodeContents(node); + window.getSelection().removeAllRanges(); + window.getSelection().addRange(range); + } + copy(); + window.getSelection().removeAllRanges(); + }; + select(code); + }, + render: function() { this.$status = $('
    ').addClass(csscls('status')).appendTo(this.$el); @@ -71,6 +98,16 @@ li.addClass(csscls('error')); li.append($('').addClass(csscls('error')).text("[" + stmt.error_code + "] " + stmt.error_message)); } + if (stmt.show_copy) { + $('') + .addClass(csscls('copy-clipboard')) + .css('cursor', 'pointer') + .on('click', function (event) { + self.onCopyToClipboard(this); + event.stopPropagation(); + }) + .appendTo(li); + } var table = $('
    Metadata
    ').addClass(csscls('params')).appendTo(li); From b942837dacb61694bdcecad64f434152a77763e8 Mon Sep 17 00:00:00 2001 From: Cyril Mizzi Date: Sun, 16 Aug 2020 13:55:21 +0200 Subject: [PATCH 160/585] [queries] Support double question mark (#1060) * fix(collector/query): support double question mark escape * fix(tests/datacollector/query): remove unused uses statements * feat(formatter/query): replace escaped question mark into single one * feat(tests/collector/query): improve test --- src/DataCollector/QueryCollector.php | 10 +++--- src/DataFormatter/QueryFormatter.php | 5 ++- tests/DataCollector/QueryCollectorTest.php | 38 ++++++++++++++++++++++ 3 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 tests/DataCollector/QueryCollectorTest.php diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index f98920deb..8f929992a 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -139,7 +139,7 @@ public function addQuery($query, $bindings, $time, $connection) // nested in quotes, while we iterate through the bindings // and substitute placeholders by suitable values. $regex = is_numeric($key) - ? "/\?(?=(?:[^'\\\']*'[^'\\\']*')*[^'\\\']*$)/" + ? "/(?ORDER BY RAND()
    is slow, try to avoid if you can. - You can read this - or this'; + You can read this + or this'; } if (strpos($query, '!=') !== false) { $hints[] = 'The != operator is not standard. Use the <> operator to test for inequality instead.'; @@ -229,8 +229,8 @@ protected function performQueryAnalysis($query) $hints[] = 'LIMIT without ORDER BY causes non-deterministic results, depending on the query execution plan'; } if (preg_match('/LIKE\\s[\'"](%.*?)[\'"]/i', $query, $matches)) { - $hints[] = 'An argument has a leading wildcard character: ' . $matches[1]. '. - The predicate with this argument is not sargable and cannot use an index if one exists.'; + $hints[] = 'An argument has a leading wildcard character: ' . $matches[1]. '. + The predicate with this argument is not sargable and cannot use an index if one exists.'; } return $hints; } diff --git a/src/DataFormatter/QueryFormatter.php b/src/DataFormatter/QueryFormatter.php index aa0db8c7b..0ad33bc0d 100644 --- a/src/DataFormatter/QueryFormatter.php +++ b/src/DataFormatter/QueryFormatter.php @@ -15,7 +15,10 @@ class QueryFormatter extends DataFormatter */ public function formatSql($sql) { - return trim(preg_replace("/\s*\n\s*/", "\n", $sql)); + $sql = preg_replace("/\?(?=(?:[^'\\\']*'[^'\\']*')*[^'\\\']*$)(?:\?)/", '?', $sql); + $sql = trim(preg_replace("/\s*\n\s*/", "\n", $sql)); + + return $sql; } /** diff --git a/tests/DataCollector/QueryCollectorTest.php b/tests/DataCollector/QueryCollectorTest.php new file mode 100644 index 000000000..16bcc5941 --- /dev/null +++ b/tests/DataCollector/QueryCollectorTest.php @@ -0,0 +1,38 @@ +loadLaravelMigrations(); + + $this->debugbar()->boot(); + + /** @var \Barryvdh\Debugbar\DataCollector\QueryCollector $collector */ + $collector = $this->debugbar()->getCollector('queries'); + $collector->addQuery( + "SELECT ('[1, 2, 3]'::jsonb ?? ?) as a, ('[4, 5, 6]'::jsonb ??| ?) as b, 'hello world ? example ??' as c", + [3, '{4}'], + 0, + $this->app['db']->connection() + ); + + tap($collector->collect(), function (array $collection) { + $this->assertEquals(1, $collection['nb_statements']); + + tap(Arr::first($collection['statements']), function (array $statement) { + $this->assertEquals([3, '{4}'], $statement['bindings']); + $this->assertEquals("SELECT ('[1, 2, 3]'::jsonb ? 3) as a, ('[4, 5, 6]'::jsonb ?| '{4}') as b, 'hello world ? example ??' as c", $statement['sql']); + }); + }); + } +} From 28149253410010e5a0d6c08871f8096eb8ac0490 Mon Sep 17 00:00:00 2001 From: Eric Fletcher Date: Sun, 16 Aug 2020 07:57:39 -0400 Subject: [PATCH 161/585] Move route definition into separate file - v2 (#1055) * Create debugbar-routes.php * Update ServiceProvider.php * Update debugbar-routes.php * Fix missing import --- src/ServiceProvider.php | 41 ++--------------------------------------- src/debugbar-routes.php | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 39 deletions(-) create mode 100644 src/debugbar-routes.php diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 9078fe8e5..a3975ee6f 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -26,6 +26,8 @@ public function register() { $configPath = __DIR__ . '/../config/debugbar.php'; $this->mergeConfigFrom($configPath, 'debugbar'); + + $this->loadRoutesFrom(realpath(__DIR__.'/debugbar-routes.php')); $this->app->alias( DataFormatter::class, @@ -66,45 +68,6 @@ public function boot() $configPath = __DIR__ . '/../config/debugbar.php'; $this->publishes([$configPath => $this->getConfigPath()], 'config'); - $routeConfig = [ - 'namespace' => 'Barryvdh\Debugbar\Controllers', - 'prefix' => $this->app['config']->get('debugbar.route_prefix'), - 'domain' => $this->app['config']->get('debugbar.route_domain'), - 'middleware' => [DebugbarEnabled::class], - ]; - - $this->getRouter()->group($routeConfig, function($router) { - $router->get('open', [ - 'uses' => 'OpenHandlerController@handle', - 'as' => 'debugbar.openhandler', - ]); - - $router->get('clockwork/{id}', [ - 'uses' => 'OpenHandlerController@clockwork', - 'as' => 'debugbar.clockwork', - ]); - - $router->get('telescope/{id}', [ - 'uses' => 'TelescopeController@show', - 'as' => 'debugbar.telescope', - ]); - - $router->get('assets/stylesheets', [ - 'uses' => 'AssetController@css', - 'as' => 'debugbar.assets.css', - ]); - - $router->get('assets/javascript', [ - 'uses' => 'AssetController@js', - 'as' => 'debugbar.assets.js', - ]); - - $router->delete('cache/{key}/{tags?}', [ - 'uses' => 'CacheController@delete', - 'as' => 'debugbar.cache.delete', - ]); - }); - $this->registerMiddleware(InjectDebugbar::class); } diff --git a/src/debugbar-routes.php b/src/debugbar-routes.php new file mode 100644 index 000000000..f8897e0ba --- /dev/null +++ b/src/debugbar-routes.php @@ -0,0 +1,40 @@ + 'Barryvdh\Debugbar\Controllers', + 'prefix' => $this->app['config']->get('debugbar.route_prefix'), + 'domain' => $this->app['config']->get('debugbar.route_domain'), + 'middleware' => [\Barryvdh\Debugbar\Middleware\DebugbarEnabled::class], +]; + +$this->app['router']->group($routeConfig, function($router) { + $router->get('open', [ + 'uses' => 'OpenHandlerController@handle', + 'as' => 'debugbar.openhandler', + ]); + + $router->get('clockwork/{id}', [ + 'uses' => 'OpenHandlerController@clockwork', + 'as' => 'debugbar.clockwork', + ]); + + $router->get('telescope/{id}', [ + 'uses' => 'TelescopeController@show', + 'as' => 'debugbar.telescope', + ]); + + $router->get('assets/stylesheets', [ + 'uses' => 'AssetController@css', + 'as' => 'debugbar.assets.css', + ]); + + $router->get('assets/javascript', [ + 'uses' => 'AssetController@js', + 'as' => 'debugbar.assets.js', + ]); + + $router->delete('cache/{key}/{tags?}', [ + 'uses' => 'CacheController@delete', + 'as' => 'debugbar.cache.delete', + ]); +}); From 1ba75b5f9368e1f9f1455c5954ffc1f684d017d6 Mon Sep 17 00:00:00 2001 From: Brian Danchilla Date: Sun, 16 Aug 2020 06:54:34 -0600 Subject: [PATCH 162/585] check if a new config setting "debugbar.options.db.slow_threshold" is set. if so, only add the current query to the collector if it is slower then this setting (#534) --- src/LaravelDebugbar.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 29ab4642a..ca27dcebe 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -346,7 +346,11 @@ function ($query, $bindings = null, $time = null, $connectionName = null) use ($ $connection = $db->connection($connectionName); } - $queryCollector->addQuery((string) $query, $bindings, $time, $connection); + //allow collecting only queries slower than a specified amount of milliseconds + $threshold = $this->app['config']->get('debugbar.options.db.slow_threshold', false); + if (!$threshold || $time > $threshold) { + $queryCollector->addQuery((string)$query, $bindings, $time, $connection); + } } ); } catch (\Exception $e) { From 1112fc1cdc0ba22c09b007b3cb4ce6f34f295092 Mon Sep 17 00:00:00 2001 From: Mohannad Najjar Date: Sun, 16 Aug 2020 16:03:38 +0300 Subject: [PATCH 163/585] Fix docblock: `message` parameter type (#933) --- src/Facade.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Facade.php b/src/Facade.php index 9c1f75926..510e93984 100644 --- a/src/Facade.php +++ b/src/Facade.php @@ -3,17 +3,17 @@ /** * @method static \Barryvdh\Debugbar\LaravelDebugbar addCollector(\DebugBar\DataCollector\DataCollectorInterface $collector) * @method static void addMessage(mixed $message, string $label = 'info') - * @method static void alert(string $message) - * @method static void critical(string $message) - * @method static void debug(string $message) - * @method static void emergency(string $message) - * @method static void error(string $message) + * @method static void alert(mixed $message) + * @method static void critical(mixed $message) + * @method static void debug(mixed $message) + * @method static void emergency(mixed $message) + * @method static void error(mixed $message) * @method static \Barryvdh\Debugbar\LaravelDebugbar getCollector(string $name) * @method static bool hasCollector(string $name) - * @method static void info(string $message) - * @method static void log(string $message) - * @method static void notice(string $message) - * @method static void warning(string $message) + * @method static void info(mixed $message) + * @method static void log(mixed $message) + * @method static void notice(mixed $message) + * @method static void warning(mixed $message) * * @see \Barryvdh\Debugbar\LaravelDebugbar */ From 9ed8404ab63321151c77c90f55bb3507565a76db Mon Sep 17 00:00:00 2001 From: Eimantas Kateiva Date: Sun, 16 Aug 2020 16:06:08 +0300 Subject: [PATCH 164/585] Fix issue with MonologCollector collector for Laravel >= v5.6 (#982) Logging was refactored in Laravel 5.6. `getMonolog()` method does not exist on a new Illuminate\Log\Logger --- src/LaravelDebugbar.php | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index ca27dcebe..bf18c83a2 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -272,7 +272,7 @@ function ($level, $message = null, $context = null) use ($logger) { } ); } else { - $this->addCollector(new MonologCollector($this->app['log']->getMonolog())); + $this->addCollector(new MonologCollector($this->getMonologLogger())); } } catch (\Exception $e) { $this->addThrowable( @@ -1119,4 +1119,24 @@ protected function addServerTimingHeaders(Response $response) $response->headers->set('Server-Timing', $headers, false); } } + + /** + * @return \Monolog\Logger + * @throws Exception + */ + private function getMonologLogger() + { + // The logging was refactored in Laravel 5.6 + if ($this->checkVersion('5.6')) { + $logger = $this->app['log']->getLogger(); + } else { + $logger = $this->app['log']->getMonolog(); + } + + if (get_class($logger) !== 'Monolog\Logger') { + throw new Exception('Logger is not a Monolog\Logger instance'); + } + + return $logger; + } } From abe044eaf032fb04ff4361695387789d7b249502 Mon Sep 17 00:00:00 2001 From: Eric Fletcher Date: Sun, 16 Aug 2020 12:23:49 -0400 Subject: [PATCH 165/585] Fixing use-case where routes file is included in context where $this->app doesn't exist. (#1085) --- src/debugbar-routes.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/debugbar-routes.php b/src/debugbar-routes.php index f8897e0ba..b0894611d 100644 --- a/src/debugbar-routes.php +++ b/src/debugbar-routes.php @@ -2,12 +2,12 @@ $routeConfig = [ 'namespace' => 'Barryvdh\Debugbar\Controllers', - 'prefix' => $this->app['config']->get('debugbar.route_prefix'), - 'domain' => $this->app['config']->get('debugbar.route_domain'), + 'prefix' => app('config')->get('debugbar.route_prefix'), + 'domain' => app('config')->get('debugbar.route_domain'), 'middleware' => [\Barryvdh\Debugbar\Middleware\DebugbarEnabled::class], ]; -$this->app['router']->group($routeConfig, function($router) { +app('router')->group($routeConfig, function($router) { $router->get('open', [ 'uses' => 'OpenHandlerController@handle', 'as' => 'debugbar.openhandler', From 5d11919868fe6f38bd625ccfc31057a792419891 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Sun, 16 Aug 2020 19:09:44 +0200 Subject: [PATCH 166/585] Run unit tests (#1086) * Run unit tests * Add scripts, cs fixer * Update composer.json --- .github/workflows/run-tests.yml | 39 +++++++++++++++++++++++++++++++++ composer.json | 10 +++++++-- 2 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/run-tests.yml diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml new file mode 100644 index 000000000..25ceba3b6 --- /dev/null +++ b/.github/workflows/run-tests.yml @@ -0,0 +1,39 @@ +name: Tests + +on: [push, pull_request] + +jobs: + php-tests: + runs-on: ubuntu-latest + timeout-minutes: 15 + env: + COMPOSER_NO_INTERACTION: 1 + + strategy: + matrix: + php: [7.4, 7.3, 7.2] + laravel: [7.*, 6.*, 5.5.*] + dependency-version: [prefer-lowest, prefer-stable] + exclude: + - laravel: 5.5.* + php: 7.4 + + name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: none + + - name: Install dependencies + run: | + composer require "laravel/framework:${{ matrix.laravel }}" --no-update --no-progress + composer update --${{ matrix.dependency-version }} --prefer-dist --no-suggest --no-progress + + - name: Execute Unit Tests + run: composer test diff --git a/composer.json b/composer.json index 6d11b0e45..d4b2f6405 100644 --- a/composer.json +++ b/composer.json @@ -19,8 +19,9 @@ "symfony/finder": "^3|^4|^5" }, "require-dev": { - "orchestra/testbench": "^3.5|^4.0|^5.0", - "phpunit/phpunit": "^6.0|^7.0|^8.5|^9.0" + "orchestra/testbench": "^3.5.11|^4.0|^5.0", + "phpunit/phpunit": "^6.0|^7.0|^8.5|^9.0", + "squizlabs/php_codesniffer": "^3.5" }, "autoload": { "psr-4": { @@ -49,5 +50,10 @@ "Debugbar": "Barryvdh\\Debugbar\\Facade" } } + }, + "scripts": { + "check-style": "phpcs -p --standard=PSR12 config/ resources/ src/ tests/", + "fix-style": "phpcbf -p --standard=PSR12 config/ resources/ src/ tests/", + "test": "phpunit" } } From 51c8ea3ab2c56664ebc9718ca68e342827bd7851 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Sun, 16 Aug 2020 19:45:35 +0200 Subject: [PATCH 167/585] PSR-12 + action (#1087) --- .github/workflows/run-tests.yml | 33 ++++++ composer.json | 4 +- config/debugbar.php | 2 +- ...20000_create_phpdebugbar_storage_table.php | 3 +- src/Console/ClearCommand.php | 11 +- src/Controllers/AssetController.php | 12 +- src/Controllers/BaseController.php | 9 +- src/Controllers/CacheController.php | 5 +- src/Controllers/OpenHandlerController.php | 8 +- src/Controllers/TelescopeController.php | 4 +- src/DataCollector/CacheCollector.php | 3 +- src/DataCollector/EventCollector.php | 5 +- src/DataCollector/FilesCollector.php | 3 +- src/DataCollector/GateCollector.php | 4 +- src/DataCollector/LivewireCollector.php | 4 +- src/DataCollector/LogsCollector.php | 1 + src/DataCollector/MultiAuthCollector.php | 6 +- src/DataCollector/PhpInfoCollector.php | 1 + src/DataCollector/QueryCollector.php | 16 ++- src/DataCollector/RequestCollector.php | 21 ++-- src/DataCollector/RouteCollector.php | 26 +++-- src/DataCollector/ViewCollector.php | 2 +- src/DataFormatter/QueryFormatter.php | 6 +- src/DataFormatter/SimpleFormatter.php | 3 +- src/Facade.php | 10 +- src/JavascriptRenderer.php | 5 +- src/LaravelDebugbar.php | 106 ++++++++++-------- src/LumenServiceProvider.php | 4 +- src/Middleware/DebugbarEnabled.php | 5 +- src/Middleware/InjectDebugbar.php | 5 +- src/Resources/cache/widget.js | 16 +-- src/Resources/sqlqueries/widget.js | 29 ++--- src/ServiceProvider.php | 22 ++-- src/Storage/FilesystemStorage.php | 8 +- src/Support/Clockwork/ClockworkCollector.php | 1 - src/Support/Clockwork/Converter.php | 16 +-- src/Twig/Extension/Debug.php | 8 +- src/Twig/Extension/Dump.php | 10 +- src/Twig/Extension/Stopwatch.php | 4 +- src/Twig/Node/StopwatchNode.php | 4 +- src/Twig/TokenParser/StopwatchTokenParser.php | 4 +- src/debugbar-routes.php | 2 +- tests/DataCollector/ModelsCollectorTest.php | 16 +-- tests/DataCollector/QueryCollectorTest.php | 8 +- tests/Models/Person.php | 8 ++ tests/Models/User.php | 11 ++ 46 files changed, 308 insertions(+), 186 deletions(-) rename {src => database}/migrations/2014_12_01_120000_create_phpdebugbar_storage_table.php (98%) create mode 100644 tests/Models/Person.php create mode 100644 tests/Models/User.php diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 25ceba3b6..fde2e9a7a 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -37,3 +37,36 @@ jobs: - name: Execute Unit Tests run: composer test + + fix-style: + name: Fix Code Style + timeout-minutes: 15 + runs-on: ubuntu-latest + env: + COMPOSER_NO_INTERACTION: 1 + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + coverage: none + tools: composer:v2 + + - name: Install dependencies + run: | + composer update --prefer-dist --no-suggest --no-progress + + - run: composer fix-style + continue-on-error: true + + # Revert modifications so they don't get commited 💥 + - run: git checkout -- composer.json + + - uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: composer fix-style + commit_author: laravel-debugbar \ No newline at end of file diff --git a/composer.json b/composer.json index d4b2f6405..6576f8ba4 100644 --- a/composer.json +++ b/composer.json @@ -52,8 +52,8 @@ } }, "scripts": { - "check-style": "phpcs -p --standard=PSR12 config/ resources/ src/ tests/", - "fix-style": "phpcbf -p --standard=PSR12 config/ resources/ src/ tests/", + "check-style": "phpcs -p --standard=PSR12 config/ src/ tests/", + "fix-style": "phpcbf -p --standard=PSR12 config/ src/ tests/", "test": "phpunit" } } diff --git a/config/debugbar.php b/config/debugbar.php index 333cc3b1a..ada83c6d3 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -147,7 +147,7 @@ 'timeline' => false, // Add the queries to the timeline 'explain' => [ // Show EXPLAIN output on queries 'enabled' => false, - 'types' => ['SELECT'], // // workaround ['SELECT'] only. https://github.com/barryvdh/laravel-debugbar/issues/888 ['SELECT', 'INSERT', 'UPDATE', 'DELETE']; for MySQL 5.6.3+ + 'types' => ['SELECT'], // Deprecated setting, is always only SELECT ], 'hints' => false, // Show hints for common mistakes 'show_copy' => false, // Show copy button next to the query diff --git a/src/migrations/2014_12_01_120000_create_phpdebugbar_storage_table.php b/database/migrations/2014_12_01_120000_create_phpdebugbar_storage_table.php similarity index 98% rename from src/migrations/2014_12_01_120000_create_phpdebugbar_storage_table.php rename to database/migrations/2014_12_01_120000_create_phpdebugbar_storage_table.php index cb6487076..7e2637cee 100644 --- a/src/migrations/2014_12_01_120000_create_phpdebugbar_storage_table.php +++ b/database/migrations/2014_12_01_120000_create_phpdebugbar_storage_table.php @@ -1,4 +1,5 @@ index('meta_uri'); $table->index('meta_ip'); $table->index('meta_method'); - }); + }); } /** * Reverse the migrations. diff --git a/src/Console/ClearCommand.php b/src/Console/ClearCommand.php index 64c7dd28d..abafae915 100644 --- a/src/Console/ClearCommand.php +++ b/src/Console/ClearCommand.php @@ -1,4 +1,6 @@ -debugbar->boot(); if ($storage = $this->debugbar->getStorage()) { - try - { + try { $storage->clear(); - } catch(\InvalidArgumentException $e) { + } catch (\InvalidArgumentException $e) { // hide InvalidArgumentException if storage location does not exist - if(strpos($e->getMessage(), 'does not exist') === false) { + if (strpos($e->getMessage(), 'does not exist') === false) { throw $e; } } diff --git a/src/Controllers/AssetController.php b/src/Controllers/AssetController.php index 00c58300f..646ab44ab 100644 --- a/src/Controllers/AssetController.php +++ b/src/Controllers/AssetController.php @@ -1,4 +1,6 @@ -dumpAssetsToString('js'); $response = new Response( - $content, 200, [ + $content, + 200, + [ 'Content-Type' => 'text/javascript', ] ); @@ -36,7 +40,9 @@ public function css() $content = $renderer->dumpAssetsToString('css'); $response = new Response( - $content, 200, [ + $content, + 200, + [ 'Content-Type' => 'text/css', ] ); diff --git a/src/Controllers/BaseController.php b/src/Controllers/BaseController.php index 36dfe5b4d..3d2f15f7d 100644 --- a/src/Controllers/BaseController.php +++ b/src/Controllers/BaseController.php @@ -1,10 +1,13 @@ -debugbar = $debugbar; - if ($request->hasSession()){ + if ($request->hasSession()) { $request->session()->reflash(); } @@ -38,7 +41,7 @@ public function __construct(Request $request, LaravelDebugbar $debugbar) { $this->debugbar = $debugbar; - if ($request->hasSession()){ + if ($request->hasSession()) { $request->session()->reflash(); } } diff --git a/src/Controllers/CacheController.php b/src/Controllers/CacheController.php index 3d04305ab..c347700b6 100644 --- a/src/Controllers/CacheController.php +++ b/src/Controllers/CacheController.php @@ -1,4 +1,6 @@ -json(compact('success')); } - } diff --git a/src/Controllers/OpenHandlerController.php b/src/Controllers/OpenHandlerController.php index 77056f403..c2e95b300 100644 --- a/src/Controllers/OpenHandlerController.php +++ b/src/Controllers/OpenHandlerController.php @@ -1,4 +1,6 @@ -handle(null, false, false); return new Response( - $data, 200, [ + $data, + 200, + [ 'Content-Type' => 'application/json' ] ); diff --git a/src/Controllers/TelescopeController.php b/src/Controllers/TelescopeController.php index e98118eda..dcc4d2f6c 100644 --- a/src/Controllers/TelescopeController.php +++ b/src/Controllers/TelescopeController.php @@ -1,4 +1,6 @@ - 'forgotten', ]; - public function __construct($requestStartTime = null, $collectValues) + public function __construct($requestStartTime, $collectValues) { parent::__construct(); diff --git a/src/DataCollector/EventCollector.php b/src/DataCollector/EventCollector.php index d3974a411..07b67af2e 100644 --- a/src/DataCollector/EventCollector.php +++ b/src/DataCollector/EventCollector.php @@ -1,4 +1,5 @@ events->getListeners($name) as $i => $listener) { - // Check if it's an object + method name if (is_array($listener) && count($listener) > 1 && is_object($listener[0])) { list($class, $method) = $listener; @@ -53,7 +53,8 @@ public function onWildcardEvent($name = null, $data = []) // Format the closure to a readable format $filename = ltrim(str_replace(base_path(), '', $reflector->getFileName()), '/'); - $listener = $reflector->getName() . ' (' . $filename . ':' . $reflector->getStartLine() . '-' . $reflector->getEndLine() . ')'; + $lines = $reflector->getStartLine() . '-' . $reflector->getEndLine(); + $listener = $reflector->getName() . ' (' . $filename . ':' . $lines . ')'; } else { // Not sure if this is possible, but to prevent edge cases $listener = $this->getDataFormatter()->formatVar($listener); diff --git a/src/DataCollector/FilesCollector.php b/src/DataCollector/FilesCollector.php index 4c56837c4..2371b95e6 100644 --- a/src/DataCollector/FilesCollector.php +++ b/src/DataCollector/FilesCollector.php @@ -34,7 +34,8 @@ public function collect() foreach ($files as $file) { // Skip the files from Debugbar, they are only loaded for Debugging and confuse the output. // Of course some files are stil always loaded (ServiceProvider, Facade etc) - if (strpos($file, 'vendor/maximebf/debugbar/src') !== false || strpos( + if ( + strpos($file, 'vendor/maximebf/debugbar/src') !== false || strpos( $file, 'vendor/barryvdh/laravel-debugbar/src' ) !== false diff --git a/src/DataCollector/GateCollector.php b/src/DataCollector/GateCollector.php index f9dce319a..e08235332 100644 --- a/src/DataCollector/GateCollector.php +++ b/src/DataCollector/GateCollector.php @@ -22,12 +22,12 @@ public function __construct(Gate $gate) { parent::__construct('gate'); $this->setDataFormatter(new SimpleFormatter()); - $gate->after(function ($user = null, $ability, $result, $arguments = []) { + $gate->after(function ($user, $ability, $result, $arguments = []) { $this->addCheck($user, $ability, $result, $arguments); }); } - public function addCheck($user = null, $ability, $result, $arguments = []) + public function addCheck($user, $ability, $result, $arguments = []) { $userKey = 'user'; $userId = null; diff --git a/src/DataCollector/LivewireCollector.php b/src/DataCollector/LivewireCollector.php index 800e9a96b..fe8d29d86 100644 --- a/src/DataCollector/LivewireCollector.php +++ b/src/DataCollector/LivewireCollector.php @@ -23,12 +23,12 @@ class LivewireCollector extends DataCollector implements DataCollectorInterface, public function __construct(Request $request) { // Listen to Livewire views - Livewire::listen('view:render', function(View $view) use ($request) { + Livewire::listen('view:render', function (View $view) use ($request) { /** @var \Livewire\Component $component */ $component = $view->getData()['_instance']; // Create an unique name for each compoent - $key = $component->getName() . ' #' .$component->id; + $key = $component->getName() . ' #' . $component->id; $data = [ 'data' => $component->getPublicPropertiesDefinedBySubClass(), diff --git a/src/DataCollector/LogsCollector.php b/src/DataCollector/LogsCollector.php index 2950e9506..28ed6e14f 100644 --- a/src/DataCollector/LogsCollector.php +++ b/src/DataCollector/LogsCollector.php @@ -1,4 +1,5 @@ guards as $guardName => $config) { + foreach ($this->guards as $guardName => $config) { try { $guard = $this->auth->guard($guardName); if ($this->hasUser($guard)) { $user = $guard->user(); - if(!is_null($user)) { + if (!is_null($user)) { $data['guards'][$guardName] = $this->getUserInformation($user); $names .= $guardName . ": " . $data['guards'][$guardName]['name'] . ', '; } @@ -166,5 +165,4 @@ public function getWidgets() return $widgets; } - } diff --git a/src/DataCollector/PhpInfoCollector.php b/src/DataCollector/PhpInfoCollector.php index 764b9ccb3..0c955e900 100644 --- a/src/DataCollector/PhpInfoCollector.php +++ b/src/DataCollector/PhpInfoCollector.php @@ -1,4 +1,5 @@ prepareBindings($bindings); // Run EXPLAIN on this query (if needed) - if ($this->explainQuery && $pdo && preg_match('/^\s*('.implode('|', $this->explainTypes).') /i', $query)) { + if ($this->explainQuery && $pdo && preg_match('/^\s*(' . implode('|', $this->explainTypes) . ') /i', $query)) { $statement = $pdo->prepare('EXPLAIN ' . $query); $statement->execute($bindings); $explainResults = $statement->fetchAll(\PDO::FETCH_CLASS); @@ -210,6 +210,7 @@ protected function emulateQuote($value) */ protected function performQueryAnalysis($query) { + // @codingStandardsIgnoreStart $hints = []; if (preg_match('/^\\s*SELECT\\s*`?[a-zA-Z0-9]*`?\\.?\\*/i', $query)) { $hints[] = 'Use SELECT * only if you need all columns from table'; @@ -229,10 +230,12 @@ protected function performQueryAnalysis($query) $hints[] = 'LIMIT without ORDER BY causes non-deterministic results, depending on the query execution plan'; } if (preg_match('/LIKE\\s[\'"](%.*?)[\'"]/i', $query, $matches)) { - $hints[] = 'An argument has a leading wildcard character: ' . $matches[1]. '. + $hints[] = 'An argument has a leading wildcard character: ' . $matches[1] . '. The predicate with this argument is not sargable and cannot use an index if one exists.'; } return $hints; + + // @codingStandardsIgnoreEnd } /** @@ -275,7 +278,8 @@ protected function parseTrace($index, array $trace) return $frame; } - if (isset($trace['class']) && + if ( + isset($trace['class']) && isset($trace['file']) && !$this->fileIsInExcludedPath($trace['file']) ) { @@ -369,7 +373,7 @@ protected function findViewFromHash($hash) $this->reflection['viewfinderViews'] = $property; } - foreach ($property->getValue($finder) as $name => $path){ + foreach ($property->getValue($finder) as $name => $path) { if (sha1($path) == $hash || md5($path) == $hash) { return $name; } @@ -476,9 +480,9 @@ public function collect() ]; //Add the results from the explain as new rows - foreach($query['explain'] as $explain){ + foreach ($query['explain'] as $explain) { $statements[] = [ - 'sql' => ' - EXPLAIN #' . $explain->id . ': `' . $explain->table . '` (' . $explain->select_type . ')', + 'sql' => " - EXPLAIN # {$explain->id}: `{$explain->table}` ({$explain->select_type})", 'type' => 'explain', 'params' => $explain, 'row_count' => $explain->rows, diff --git a/src/DataCollector/RequestCollector.php b/src/DataCollector/RequestCollector.php index 3a243781a..25760a4e2 100644 --- a/src/DataCollector/RequestCollector.php +++ b/src/DataCollector/RequestCollector.php @@ -30,7 +30,7 @@ class RequestCollector extends DataCollector implements DataCollectorInterface, * Create a new SymfonyRequestCollector * * @param \Symfony\Component\HttpFoundation\Request $request - * @param \Symfony\Component\HttpFoundation\Request $response + * @param \Symfony\Component\HttpFoundation\Response $response * @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session */ public function __construct($request, $response, $session = null, $currentRequestId = null) @@ -116,8 +116,10 @@ public function collect() } foreach ($data['request_server'] as $key => $value) { - if (Str::is('*_KEY', $key) || Str::is('*_PASSWORD', $key) - || Str::is('*_SECRET', $key) || Str::is('*_PW', $key)) { + if ( + Str::is('*_KEY', $key) || Str::is('*_PASSWORD', $key) + || Str::is('*_SECRET', $key) || Str::is('*_PW', $key) + ) { $data['request_server'][$key] = '******'; } } @@ -129,7 +131,7 @@ public function collect() if (isset($data['request_server']['PHP_AUTH_PW'])) { $data['request_server']['PHP_AUTH_PW'] = '******'; } - ; + ; foreach ($data as $key => $var) { if (!is_string($data[$key])) { @@ -137,7 +139,6 @@ public function collect() } else { $data[$key] = e($data[$key]); } - } $htmlData = []; @@ -147,7 +148,7 @@ public function collect() ])->type('debugbar'); Telescope::$entriesQueue[] = $entry; $url = route('debugbar.telescope', [$entry->uuid]); - $htmlData['telescope'] = 'View in Telescope'; + $htmlData['telescope'] = 'View in Telescope'; } return $htmlData + $data; @@ -172,10 +173,10 @@ private function getCookieHeader($name, $value, $expires, $path, $domain, $secur } $cookie .= '; expires=' . substr( - \DateTime::createFromFormat('U', $expires, new \DateTimeZone('UTC'))->format('D, d-M-Y H:i:s T'), - 0, - -5 - ); + \DateTime::createFromFormat('U', $expires, new \DateTimeZone('UTC'))->format('D, d-M-Y H:i:s T'), + 0, + -5 + ); } if ($domain) { diff --git a/src/DataCollector/RouteCollector.php b/src/DataCollector/RouteCollector.php index 2df08fbb3..53c9b673b 100644 --- a/src/DataCollector/RouteCollector.php +++ b/src/DataCollector/RouteCollector.php @@ -49,22 +49,26 @@ protected function getRouteInformation($route) return []; } $uri = head($route->methods()) . ' ' . $route->uri(); - $action = $route->getAction(); + $action = $route->getAction(); $result = [ - 'uri' => $uri ?: '-', + 'uri' => $uri ?: '-', ]; $result = array_merge($result, $action); - if (isset($action['controller']) && is_string($action['controller']) && strpos($action['controller'], '@') !== false) { - list($controller, $method) = explode('@', $action['controller']); - if(class_exists($controller) && method_exists($controller, $method)) { - $reflector = new \ReflectionMethod($controller, $method); - } + if ( + isset($action['controller']) + && is_string($action['controller']) + && strpos($action['controller'], '@') !== false + ) { + list($controller, $method) = explode('@', $action['controller']); + if (class_exists($controller) && method_exists($controller, $method)) { + $reflector = new \ReflectionMethod($controller, $method); + } unset($result['uses']); - } elseif (isset($action['uses']) && $action['uses'] instanceof \Closure) { + } elseif (isset($action['uses']) && $action['uses'] instanceof \Closure) { $reflector = new \ReflectionFunction($action['uses']); $result['uses'] = $this->formatVar($result['uses']); } @@ -74,9 +78,9 @@ protected function getRouteInformation($route) $result['file'] = $filename . ':' . $reflector->getStartLine() . '-' . $reflector->getEndLine(); } - if ($middleware = $this->getMiddleware($route)) { - $result['middleware'] = $middleware; - } + if ($middleware = $this->getMiddleware($route)) { + $result['middleware'] = $middleware; + } diff --git a/src/DataCollector/ViewCollector.php b/src/DataCollector/ViewCollector.php index b7c657e16..46a33166b 100644 --- a/src/DataCollector/ViewCollector.php +++ b/src/DataCollector/ViewCollector.php @@ -88,7 +88,7 @@ public function addView(View $view) 'type' => $type, ]; - if ( $this->getXdebugLink($path)) { + if ($this->getXdebugLink($path)) { $template['xdebug_link'] = $this->getXdebugLink($path); } diff --git a/src/DataFormatter/QueryFormatter.php b/src/DataFormatter/QueryFormatter.php index 0ad33bc0d..8b77c6144 100644 --- a/src/DataFormatter/QueryFormatter.php +++ b/src/DataFormatter/QueryFormatter.php @@ -15,10 +15,10 @@ class QueryFormatter extends DataFormatter */ public function formatSql($sql) { - $sql = preg_replace("/\?(?=(?:[^'\\\']*'[^'\\']*')*[^'\\\']*$)(?:\?)/", '?', $sql); - $sql = trim(preg_replace("/\s*\n\s*/", "\n", $sql)); + $sql = preg_replace("/\?(?=(?:[^'\\\']*'[^'\\']*')*[^'\\\']*$)(?:\?)/", '?', $sql); + $sql = trim(preg_replace("/\s*\n\s*/", "\n", $sql)); - return $sql; + return $sql; } /** diff --git a/src/DataFormatter/SimpleFormatter.php b/src/DataFormatter/SimpleFormatter.php index 0a1fc9380..0b06c2bb6 100644 --- a/src/DataFormatter/SimpleFormatter.php +++ b/src/DataFormatter/SimpleFormatter.php @@ -60,7 +60,8 @@ private function exportValue($value, $depth = 1, $deep = false) } if ($deep) { - return sprintf("[\n%s%s\n%s]", $indent, implode(sprintf(", \n%s", $indent), $a), str_repeat(' ', $depth - 1)); + $args = [$indent, implode(sprintf(", \n%s", $indent), $a), str_repeat(' ', $depth - 1)]; + return sprintf("[\n%s%s\n%s]", $args); } $s = sprintf('[%s]', implode(', ', $a)); diff --git a/src/Facade.php b/src/Facade.php index 510e93984..a4e15b4b1 100644 --- a/src/Facade.php +++ b/src/Facade.php @@ -1,14 +1,18 @@ -app; // Set custom error handler - if ($app['config']->get('debugbar.error_handler' , false)) { + if ($app['config']->get('debugbar.error_handler', false)) { set_error_handler([$this, 'handleError']); } @@ -147,7 +148,7 @@ public function boot() if ($this->shouldCollect('time', true)) { $this->addCollector(new TimeDataCollector()); - if ( ! $this->isLumen()) { + if (! $this->isLumen()) { $this->app->booted( function () use ($debugbar) { $startTime = $this->app['request']->server('REQUEST_TIME_FLOAT'); @@ -190,7 +191,6 @@ function () use ($debugbar) { $eventCollector = new EventCollector($startTime); $this->addCollector($eventCollector); $this->app['events']->subscribe($eventCollector); - } catch (\Exception $e) { $this->addThrowable( new Exception( @@ -218,7 +218,9 @@ function ($view, $data = []) use ($debugbar) { } catch (\Exception $e) { $this->addThrowable( new Exception( - 'Cannot add ViewCollector to Laravel Debugbar: ' . $e->getMessage(), $e->getCode(), $e + 'Cannot add ViewCollector to Laravel Debugbar: ' . $e->getMessage(), + $e->getCode(), + $e ) ); } @@ -277,7 +279,9 @@ function ($level, $message = null, $context = null) use ($logger) { } catch (\Exception $e) { $this->addThrowable( new Exception( - 'Cannot add LogsCollector to Laravel Debugbar: ' . $e->getMessage(), $e->getCode(), $e + 'Cannot add LogsCollector to Laravel Debugbar: ' . $e->getMessage(), + $e->getCode(), + $e ) ); } @@ -285,7 +289,8 @@ function ($level, $message = null, $context = null) use ($logger) { if ($this->shouldCollect('db', true) && isset($this->app['db'])) { $db = $this->app['db']; - if ($debugbar->hasCollector('time') && $this->app['config']->get( + if ( + $debugbar->hasCollector('time') && $this->app['config']->get( 'debugbar.options.db.timeline', false ) @@ -329,14 +334,22 @@ function ($level, $message = null, $context = null) use ($logger) { try { $db->listen( - function ($query, $bindings = null, $time = null, $connectionName = null) use ($db, $queryCollector) { + function ( + $query, + $bindings = null, + $time = null, + $connectionName = null + ) use ( + $db, + $queryCollector + ) { if (!$this->shouldCollect('db', true)) { return; // Issue 776 : We've turned off collecting after the listener was attached } // Laravel 5.2 changed the way some core events worked. We must account for // the first argument being an "event object", where arguments are passed // via object properties, instead of individual arguments. - if ( $query instanceof \Illuminate\Database\Events\QueryExecuted ) { + if ($query instanceof \Illuminate\Database\Events\QueryExecuted) { $bindings = $query->bindings; $time = $query->time; $connection = $query->connection; @@ -420,7 +433,7 @@ function ($event, $params) use ($queryCollector) { try { $modelsCollector = $this->app->make('Barryvdh\Debugbar\DataCollector\ModelsCollector'); $this->addCollector($modelsCollector); - } catch (\Exception $e){ + } catch (\Exception $e) { // No Models collector } } @@ -429,7 +442,7 @@ function ($event, $params) use ($queryCollector) { try { $livewireCollector = $this->app->make('Barryvdh\Debugbar\DataCollector\LivewireCollector'); $this->addCollector($livewireCollector); - } catch (\Exception $e){ + } catch (\Exception $e) { $this->addThrowable( new Exception('Cannot add Livewire Collector: ' . $e->getMessage(), $e->getCode(), $e) ); @@ -440,7 +453,8 @@ function ($event, $params) use ($queryCollector) { try { $mailer = $this->app['mailer']->getSwiftMailer(); $this->addCollector(new SwiftMailCollector($mailer)); - if ($this->app['config']->get('debugbar.options.mail.full_log') && $this->hasCollector( + if ( + $this->app['config']->get('debugbar.options.mail.full_log') && $this->hasCollector( 'messages' ) ) { @@ -449,7 +463,9 @@ function ($event, $params) use ($queryCollector) { } catch (\Exception $e) { $this->addThrowable( new Exception( - 'Cannot add MailCollector to Laravel Debugbar: ' . $e->getMessage(), $e->getCode(), $e + 'Cannot add MailCollector to Laravel Debugbar: ' . $e->getMessage(), + $e->getCode(), + $e ) ); } @@ -462,7 +478,9 @@ function ($event, $params) use ($queryCollector) { } catch (\Exception $e) { $this->addThrowable( new Exception( - 'Cannot add LogsCollector to Laravel Debugbar: ' . $e->getMessage(), $e->getCode(), $e + 'Cannot add LogsCollector to Laravel Debugbar: ' . $e->getMessage(), + $e->getCode(), + $e ) ); } @@ -471,29 +489,31 @@ function ($event, $params) use ($queryCollector) { $this->addCollector(new FilesCollector($app)); } - if ($this->shouldCollect('auth', false)) { - try { - $guards = $this->app['config']->get('auth.guards', []); - $authCollector = new MultiAuthCollector($app['auth'], $guards); - - $authCollector->setShowName( - $this->app['config']->get('debugbar.options.auth.show_name') - ); - $this->addCollector($authCollector); - } catch (\Exception $e) { - $this->addThrowable( - new Exception( - 'Cannot add AuthCollector to Laravel Debugbar: ' . $e->getMessage(), $e->getCode(), $e - ) - ); - } - } + if ($this->shouldCollect('auth', false)) { + try { + $guards = $this->app['config']->get('auth.guards', []); + $authCollector = new MultiAuthCollector($app['auth'], $guards); + + $authCollector->setShowName( + $this->app['config']->get('debugbar.options.auth.show_name') + ); + $this->addCollector($authCollector); + } catch (\Exception $e) { + $this->addThrowable( + new Exception( + 'Cannot add AuthCollector to Laravel Debugbar: ' . $e->getMessage(), + $e->getCode(), + $e + ) + ); + } + } if ($this->shouldCollect('gate', false)) { try { $gateCollector = $this->app->make('Barryvdh\Debugbar\DataCollector\GateCollector'); $this->addCollector($gateCollector); - } catch (\Exception $e){ + } catch (\Exception $e) { // No Gate collector } } @@ -505,7 +525,6 @@ function ($event, $params) use ($queryCollector) { $cacheCollector = new CacheCollector($startTime, $collectValues); $this->addCollector($cacheCollector); $this->app['events']->subscribe($cacheCollector); - } catch (\Exception $e) { $this->addThrowable( new Exception( @@ -676,7 +695,7 @@ public function modifyResponse(Request $request, Response $response) } } - if ($this->app->bound(SessionManager::class)){ + if ($this->app->bound(SessionManager::class)) { /** @var \Illuminate\Session\SessionManager $sessionManager */ $sessionManager = $app->make(SessionManager::class); @@ -702,7 +721,8 @@ public function modifyResponse(Request $request, Response $response) if ($this->shouldCollect('symfony_request', true) && !$this->hasCollector('request')) { try { - $this->addCollector(new RequestCollector($request, $response, $sessionManager, $this->getCurrentRequestId())); + $reqId = $this->getCurrentRequestId(); + $this->addCollector(new RequestCollector($request, $response, $sessionManager, $reqId)); } catch (\Exception $e) { $this->addThrowable( new Exception( @@ -715,7 +735,6 @@ public function modifyResponse(Request $request, Response $response) } if ($app['config']->get('debugbar.clockwork') && ! $this->hasCollector('clockwork')) { - try { $this->addCollector(new ClockworkCollector($request, $response, $sessionManager)); } catch (\Exception $e) { @@ -747,16 +766,15 @@ public function modifyResponse(Request $request, Response $response) if ($app['config']->get('debugbar.add_ajax_timing', false)) { $this->addServerTimingHeaders($response); } - } catch (\Exception $e) { $app['log']->error('Debugbar exception: ' . $e->getMessage()); } } elseif ( ($response->headers->has('Content-Type') && - strpos($response->headers->get('Content-Type'), 'html') === false) - || $request->getRequestFormat() !== 'html' - || $response->getContent() === false - || $this->isJsonRequest($request) + strpos($response->headers->get('Content-Type'), 'html') === false) || + $request->getRequestFormat() !== 'html' || + $response->getContent() === false || + $this->isJsonRequest($request) ) { try { // Just collect + store data, don't inject it. @@ -1013,7 +1031,7 @@ public function __call($method, $args) { $messageLevels = ['emergency', 'alert', 'critical', 'error', 'warning', 'notice', 'info', 'debug', 'log']; if (in_array($method, $messageLevels)) { - foreach($args as $arg) { + foreach ($args as $arg) { $this->addMessage($arg, $method); } } @@ -1097,7 +1115,7 @@ protected function addClockworkHeaders(Response $response) $prefix = $this->app['config']->get('debugbar.route_prefix'); $response->headers->set('X-Clockwork-Id', $this->getCurrentRequestId(), true); $response->headers->set('X-Clockwork-Version', 1, true); - $response->headers->set('X-Clockwork-Path', $prefix .'/clockwork/', true); + $response->headers->set('X-Clockwork-Path', $prefix . '/clockwork/', true); } /** diff --git a/src/LumenServiceProvider.php b/src/LumenServiceProvider.php index a3542753d..ed6297115 100644 --- a/src/LumenServiceProvider.php +++ b/src/LumenServiceProvider.php @@ -1,4 +1,6 @@ -debugbar->modifyResponse($request, $response); return $response; - } /** diff --git a/src/Resources/cache/widget.js b/src/Resources/cache/widget.js index ad7d967fe..5314eacec 100644 --- a/src/Resources/cache/widget.js +++ b/src/Resources/cache/widget.js @@ -1,4 +1,4 @@ -(function($) { +(function ($) { var csscls = PhpDebugBar.utils.makecsscls('phpdebugbar-widgets-'); @@ -14,39 +14,39 @@ className: csscls('timeline cache'), - onForgetClick: function(e, el) { + onForgetClick: function (e, el) { e.stopPropagation(); $.ajax({ url: $(el).attr("data-url"), type: 'DELETE', - success: function(result) { + success: function (result) { $(el).fadeOut(200); } }); }, - render: function() { + render: function () { LaravelCacheWidget.__super__.render.apply(this); - this.bindAttr('data', function(data) { + this.bindAttr('data', function (data) { if (data.measures) { var self = this; - var lines = this.$el.find('.'+csscls('measure')); + var lines = this.$el.find('.' + csscls('measure')); for (var i = 0; i < data.measures.length; i++) { var measure = data.measures[i]; var m = lines[i]; if (measure.params && !$.isEmptyObject(measure.params)) { - if (measure.params.delete && measure.params.key) { $('') .addClass(csscls('forget')) .text('forget') .attr('data-url', measure.params.delete) - .one('click', function(e) { self.onForgetClick(e, this); }) + .one('click', function (e) { + self.onForgetClick(e, this); }) .appendTo(m); } } diff --git a/src/Resources/sqlqueries/widget.js b/src/Resources/sqlqueries/widget.js index 0b1104471..189da8cb0 100644 --- a/src/Resources/sqlqueries/widget.js +++ b/src/Resources/sqlqueries/widget.js @@ -1,4 +1,4 @@ -(function($) { +(function ($) { var csscls = PhpDebugBar.utils.makecsscls('phpdebugbar-widgets-'); @@ -12,11 +12,11 @@ className: csscls('sqlqueries'), - onFilterClick: function(el) { + onFilterClick: function (el) { $(el).toggleClass(csscls('excluded')); var excludedLabels = []; - this.$toolbar.find(csscls('.filter') + csscls('.excluded')).each(function() { + this.$toolbar.find(csscls('.filter') + csscls('.excluded')).each(function () { excludedLabels.push(this.rel); }); @@ -52,14 +52,14 @@ select(code); }, - render: function() { + render: function () { this.$status = $('
    ').addClass(csscls('status')).appendTo(this.$el); this.$toolbar = $('
    ').addClass(csscls('toolbar')).appendTo(this.$el); var filters = [], self = this; - this.$list = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function(li, stmt) { + this.$list = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function (li, stmt) { if (stmt.type === 'transaction') { $('').addClass(csscls('sql')).addClass(csscls('name')).text(stmt.sql).appendTo(li); } else { @@ -86,9 +86,10 @@ .addClass(csscls('filter')) .text(stmt.connection) .attr('rel', stmt.connection) - .on('click', function() { self.onFilterClick(this); }) + .on('click', function () { + self.onFilterClick(this); }) .appendTo(self.$toolbar); - if (filters.length>1) { + if (filters.length > 1) { self.$toolbar.show(); self.$list.$el.css("margin-bottom","20px"); } @@ -120,7 +121,7 @@ var $span = $('').addClass('phpdebugbar-text-muted'); var index = 0; - var $bindings = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function(li, binding) { + var $bindings = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function (li, binding) { var $index = $span.clone().text(index++ + '.'); li.append($index, ' ', binding).removeClass(csscls('list-item')).addClass(csscls('table-list-item')); }}); @@ -143,7 +144,7 @@ var $name = $('').addClass(csscls('name')).html('Hints ' + $icon); var $value = $('').addClass(csscls('value')); - var $hints = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function(li, hint) { + var $hints = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function (li, hint) { li.append(hint).removeClass(csscls('list-item')).addClass(csscls('table-list-item')); }}); @@ -165,7 +166,7 @@ var $value = $('').addClass(csscls('value')); var $span = $('').addClass('phpdebugbar-text-muted'); - var $backtrace = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function(li, source) { + var $backtrace = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function (li, source) { var $parts = [ $span.clone().text(source.index + '.'), ' ', @@ -201,7 +202,7 @@ } } - li.css('cursor', 'pointer').click(function() { + li.css('cursor', 'pointer').click(function () { if (table.is(':visible')) { table.hide(); } else { @@ -211,14 +212,14 @@ }}); this.$list.$el.appendTo(this.$el); - this.bindAttr('data', function(data) { + this.bindAttr('data', function (data) { this.$list.set('data', data.statements); this.$status.empty(); var stmt; // Search for duplicate statements. for (var sql = {}, duplicate = 0, i = 0; i < data.statements.length; i++) { - if(data.statements[i].type === 'query') { + if (data.statements[i].type === 'query') { stmt = data.statements[i].sql; if (data.statements[i].bindings && data.statements[i].bindings.length) { stmt += JSON.stringify(data.statements[i].bindings); @@ -238,7 +239,7 @@ for (i = 0; i < sql[stmt].keys.length; i++) { this.$list.$el.find('.' + csscls('list-item')).eq(sql[stmt].keys[i]) .addClass(csscls('sql-duplicate')) - .addClass(csscls('sql-duplicate-'+duplicate)); + .addClass(csscls('sql-duplicate-' + duplicate)); } } } diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index a3975ee6f..ef9ecc43a 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -1,4 +1,6 @@ -mergeConfigFrom($configPath, 'debugbar'); - $this->loadRoutesFrom(realpath(__DIR__.'/debugbar-routes.php')); + $this->loadRoutesFrom(realpath(__DIR__ . '/debugbar-routes.php')); $this->app->alias( DataFormatter::class, @@ -37,19 +39,19 @@ public function register() $this->app->singleton(LaravelDebugbar::class, function () { $debugbar = new LaravelDebugbar($this->app); - if ($this->app->bound(SessionManager::class)) { - $sessionManager = $this->app->make(SessionManager::class); - $httpDriver = new SymfonyHttpDriver($sessionManager); - $debugbar->setHttpDriver($httpDriver); - } + if ($this->app->bound(SessionManager::class)) { + $sessionManager = $this->app->make(SessionManager::class); + $httpDriver = new SymfonyHttpDriver($sessionManager); + $debugbar->setHttpDriver($httpDriver); + } return $debugbar; - } - ); + }); $this->app->alias(LaravelDebugbar::class, 'debugbar'); - $this->app->singleton('command.debugbar.clear', + $this->app->singleton( + 'command.debugbar.clear', function ($app) { return new Console\ClearCommand($app['debugbar']); } diff --git a/src/Storage/FilesystemStorage.php b/src/Storage/FilesystemStorage.php index 1ea11aee7..327f2ebdb 100644 --- a/src/Storage/FilesystemStorage.php +++ b/src/Storage/FilesystemStorage.php @@ -67,9 +67,11 @@ public function makeFilename($id) */ protected function garbageCollect() { - foreach (Finder::create()->files()->name('*.json')->date('< ' . $this->gc_lifetime . ' hour ago')->in( - $this->dirname - ) as $file) { + foreach ( + Finder::create()->files()->name('*.json')->date('< ' . $this->gc_lifetime . ' hour ago')->in( + $this->dirname + ) as $file + ) { $this->files->delete($file->getRealPath()); } } diff --git a/src/Support/Clockwork/ClockworkCollector.php b/src/Support/Clockwork/ClockworkCollector.php index 752132b20..3923a9fe6 100644 --- a/src/Support/Clockwork/ClockworkCollector.php +++ b/src/Support/Clockwork/ClockworkCollector.php @@ -87,5 +87,4 @@ public function collect() return $data; } - } diff --git a/src/Support/Clockwork/Converter.php b/src/Support/Clockwork/Converter.php index 409cdf846..79dfb695a 100644 --- a/src/Support/Clockwork/Converter.php +++ b/src/Support/Clockwork/Converter.php @@ -1,6 +1,9 @@ - [], 'description' => $measure['label'], @@ -79,7 +82,7 @@ public function convert($data) } if (isset($data['messages'])) { - foreach($data['messages']['messages'] as $message) { + foreach ($data['messages']['messages'] as $message) { $output['log'][] = [ 'message' => $message['message'], 'time' => $message['time'], @@ -90,7 +93,7 @@ public function convert($data) if (isset($data['queries'])) { $queries = $data['queries']; - foreach($queries['statements'] as $statement){ + foreach ($queries['statements'] as $statement) { if ($statement['type'] === 'explain') { continue; } @@ -121,7 +124,7 @@ public function convert($data) } if (isset($data['swiftmailer_mails'])) { - foreach($data['swiftmailer_mails']['mails'] as $mail) { + foreach ($data['swiftmailer_mails']['mails'] as $mail) { $output['emailsData'][] = [ 'data' => [ 'to' => $mail['to'], @@ -134,5 +137,4 @@ public function convert($data) return $output; } - } diff --git a/src/Twig/Extension/Debug.php b/src/Twig/Extension/Debug.php index 1b815e53f..42bc2cb32 100644 --- a/src/Twig/Extension/Debug.php +++ b/src/Twig/Extension/Debug.php @@ -1,4 +1,6 @@ - true, 'needs_environment' => true] + 'debug', + [$this, 'debug'], + ['needs_context' => true, 'needs_environment' => true] ), ]; } diff --git a/src/Twig/Extension/Dump.php b/src/Twig/Extension/Dump.php index 4a4f697ef..b42fafa42 100644 --- a/src/Twig/Extension/Dump.php +++ b/src/Twig/Extension/Dump.php @@ -1,4 +1,6 @@ - ['html'], 'needs_context' => true, 'needs_environment' => true] + 'dump', + [$this, 'dump'], + ['is_safe' => ['html'], 'needs_context' => true, 'needs_environment' => true] ), ]; } @@ -79,6 +83,6 @@ public function dump(Twig_Environment $env, $context) } } - return '
    '.$output.'
    '; + return '
    ' . $output . '
    '; } } diff --git a/src/Twig/Extension/Stopwatch.php b/src/Twig/Extension/Stopwatch.php index 993d8ddbd..80a012bdb 100644 --- a/src/Twig/Extension/Stopwatch.php +++ b/src/Twig/Extension/Stopwatch.php @@ -1,4 +1,6 @@ - [\Barryvdh\Debugbar\Middleware\DebugbarEnabled::class], ]; -app('router')->group($routeConfig, function($router) { +app('router')->group($routeConfig, function ($router) { $router->get('open', [ 'uses' => 'OpenHandlerController@handle', 'as' => 'debugbar.openhandler', diff --git a/tests/DataCollector/ModelsCollectorTest.php b/tests/DataCollector/ModelsCollectorTest.php index e96d4c0a9..1a4925eb5 100644 --- a/tests/DataCollector/ModelsCollectorTest.php +++ b/tests/DataCollector/ModelsCollectorTest.php @@ -2,8 +2,9 @@ namespace Barryvdh\Debugbar\Tests\DataCollector; +use Barryvdh\Debugbar\Tests\Models\Person; +use Barryvdh\Debugbar\Tests\Models\User; use Barryvdh\Debugbar\Tests\TestCase; -use Illuminate\Database\Eloquent\Model; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\Hash; @@ -11,8 +12,7 @@ class ModelsCollectorTest extends TestCase { use RefreshDatabase; - /** @test */ - public function it_collects_retrieved_models() + public function testItCollectsRetrievedModels() { $this->loadLaravelMigrations(); @@ -53,13 +53,3 @@ public function it_collects_retrieved_models() ); } } - -class User extends Model -{ - protected $table = 'users'; - protected $guarded = []; -} - -class Person extends User -{ -} diff --git a/tests/DataCollector/QueryCollectorTest.php b/tests/DataCollector/QueryCollectorTest.php index 16bcc5941..e4127fea4 100644 --- a/tests/DataCollector/QueryCollectorTest.php +++ b/tests/DataCollector/QueryCollectorTest.php @@ -10,8 +10,7 @@ class QueryCollectorTest extends TestCase { use RefreshDatabase; - /** @test */ - public function it_replaces_question_marks_bindings_correctly() + public function testItReplacesQuestionMarksBindingsCorrectly() { $this->loadLaravelMigrations(); @@ -31,7 +30,10 @@ public function it_replaces_question_marks_bindings_correctly() tap(Arr::first($collection['statements']), function (array $statement) { $this->assertEquals([3, '{4}'], $statement['bindings']); - $this->assertEquals("SELECT ('[1, 2, 3]'::jsonb ? 3) as a, ('[4, 5, 6]'::jsonb ?| '{4}') as b, 'hello world ? example ??' as c", $statement['sql']); + $this->assertEquals(<< Date: Mon, 17 Aug 2020 14:59:58 +0200 Subject: [PATCH 168/585] Test injecting Debugbar (#1088) * Test injecting Debugbar * composer fix-style * Update phpunit.xml.dist * Override enabled * composer fix-style * Cleanup Co-authored-by: laravel-debugbar --- tests/DataCollector/ModelsCollectorTest.php | 4 +- tests/DataCollector/QueryCollectorTest.php | 4 +- tests/DebugbarTest.php | 55 +++++++++++++++++++++ tests/TestCase.php | 45 ++++++++++++++--- 4 files changed, 98 insertions(+), 10 deletions(-) create mode 100644 tests/DebugbarTest.php diff --git a/tests/DataCollector/ModelsCollectorTest.php b/tests/DataCollector/ModelsCollectorTest.php index 1a4925eb5..33eb1d191 100644 --- a/tests/DataCollector/ModelsCollectorTest.php +++ b/tests/DataCollector/ModelsCollectorTest.php @@ -16,10 +16,10 @@ public function testItCollectsRetrievedModels() { $this->loadLaravelMigrations(); - $this->debugbar()->boot(); + debugbar()->boot(); /** @var \Barryvdh\Debugbar\DataCollector\ModelsCollector $collector */ - $collector = $this->debugbar()->getCollector('models'); + $collector = debugbar()->getCollector('models'); User::create([ 'name' => 'John Doe', diff --git a/tests/DataCollector/QueryCollectorTest.php b/tests/DataCollector/QueryCollectorTest.php index e4127fea4..c27dbb338 100644 --- a/tests/DataCollector/QueryCollectorTest.php +++ b/tests/DataCollector/QueryCollectorTest.php @@ -14,10 +14,10 @@ public function testItReplacesQuestionMarksBindingsCorrectly() { $this->loadLaravelMigrations(); - $this->debugbar()->boot(); + debugbar()->boot(); /** @var \Barryvdh\Debugbar\DataCollector\QueryCollector $collector */ - $collector = $this->debugbar()->getCollector('queries'); + $collector = debugbar()->getCollector('queries'); $collector->addQuery( "SELECT ('[1, 2, 3]'::jsonb ?? ?) as a, ('[4, 5, 6]'::jsonb ??| ?) as b, 'hello world ? example ??' as c", [3, '{4}'], diff --git a/tests/DebugbarTest.php b/tests/DebugbarTest.php new file mode 100644 index 000000000..2fc9c30c5 --- /dev/null +++ b/tests/DebugbarTest.php @@ -0,0 +1,55 @@ +resolving(LaravelDebugbar::class, function ($debugbar) { + $refObject = new \ReflectionObject($debugbar); + $refProperty = $refObject->getProperty('enabled'); + $refProperty->setAccessible(true); + $refProperty->setValue($debugbar, true); + }); + } + + public function testItInjectsOnPlainText() + { + $crawler = $this->call('GET', 'web/plain'); + + $this->assertTrue(Str::contains($crawler->content(), 'debugbar')); + $this->assertEquals(200, $crawler->getStatusCode()); + } + + public function testItInjectsOnHtml() + { + $crawler = $this->call('GET', 'web/html'); + + $this->assertTrue(Str::contains($crawler->content(), 'debugbar')); + $this->assertEquals(200, $crawler->getStatusCode()); + } + + public function testItDoesntInjectOnJson() + { + $crawler = $this->call('GET', 'api/ping'); + + $this->assertFalse(Str::contains($crawler->content(), 'debugbar')); + $this->assertEquals(200, $crawler->getStatusCode()); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index eb13371d0..3c81598fa 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -4,6 +4,7 @@ use Barryvdh\Debugbar\Facade; use Barryvdh\Debugbar\ServiceProvider; +use Illuminate\Routing\Router; use Orchestra\Testbench\TestCase as Orchestra; class TestCase extends Orchestra @@ -35,17 +36,49 @@ protected function getPackageAliases($app) return ['Debugbar' => Facade::class]; } - public function getEnvironmentSetUp($app) + /** + * Define environment setup. + * + * @param \Illuminate\Foundation\Application $app + * + * @return void + */ + protected function getEnvironmentSetUp($app) { + /** @var Router $router */ + $router = $app['router']; + + $this->addWebRoutes($router); + $this->addApiRoutes($router); } /** - * Get the Laravel Debugbar instance. - * - * @return \Barryvdh\Debugbar\LaravelDebugbar + * @param Router $router + */ + protected function addWebRoutes(Router $router) + { + $router->get('web/plain', [ + 'uses' => function () { + return 'PONG'; + } + ]); + + $router->get('web/html', [ + 'uses' => function () { + return 'Pong'; + } + ]); + } + + /** + * @param Router $router */ - public function debugbar() + protected function addApiRoutes(Router $router) { - return $this->debugbar ?? $this->debugbar = $this->app->debugbar; + $router->get('api/ping', [ + 'uses' => function () { + return response()->json(['status' => 'pong']); + } + ]); } } From fefd8cda640575270c23b0fe75dcb4e4b6eec7b9 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Mon, 17 Aug 2020 15:07:41 +0200 Subject: [PATCH 169/585] Add theme to asset url --- src/JavascriptRenderer.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/JavascriptRenderer.php b/src/JavascriptRenderer.php index bb0bae215..ee30c8e86 100644 --- a/src/JavascriptRenderer.php +++ b/src/JavascriptRenderer.php @@ -52,7 +52,8 @@ public function setUrlGenerator($url) public function renderHead() { $cssRoute = route('debugbar.assets.css', [ - 'v' => $this->getModifiedTime('css') + 'v' => $this->getModifiedTime('css'), + 'theme' => config('debugbar.theme', 'auto'), ]); $jsRoute = route('debugbar.assets.js', [ From f07317f2ea05281918ac1d4e7200f5fa3d40d0a8 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Mon, 17 Aug 2020 15:17:24 +0200 Subject: [PATCH 170/585] Use composer2 (#1090) --- .github/workflows/run-tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index fde2e9a7a..d2c91e8e5 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -29,6 +29,7 @@ jobs: with: php-version: ${{ matrix.php }} coverage: none + tools: composer:v2 - name: Install dependencies run: | @@ -69,4 +70,4 @@ jobs: - uses: stefanzweifel/git-auto-commit-action@v4 with: commit_message: composer fix-style - commit_author: laravel-debugbar \ No newline at end of file + commit_author: laravel-debugbar From 5fd2937cd073fd7634c64bbad53366f035168258 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Mon, 17 Aug 2020 15:27:00 +0200 Subject: [PATCH 171/585] Test split actions (#1091) * Split unit/cs actions * Create fix-cs.yml * Update fix-cs.yml --- .github/workflows/fix-cs.yml | 37 +++++++++++++++++++++++++++++++++ .github/workflows/run-tests.yml | 35 +------------------------------ 2 files changed, 38 insertions(+), 34 deletions(-) create mode 100644 .github/workflows/fix-cs.yml diff --git a/.github/workflows/fix-cs.yml b/.github/workflows/fix-cs.yml new file mode 100644 index 000000000..87a9c75da --- /dev/null +++ b/.github/workflows/fix-cs.yml @@ -0,0 +1,37 @@ +name: Fix Codestyle + +on: [push, pull_request] + +jobs: + fix-style: + name: Fix Code Style + timeout-minutes: 15 + runs-on: ubuntu-latest + env: + COMPOSER_NO_INTERACTION: 1 + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + coverage: none + tools: composer:v2 + + - name: Install dependencies + run: | + composer update --prefer-dist --no-suggest --no-progress + + - run: composer fix-style + continue-on-error: true + + # Revert modifications so they don't get commited 💥 + - run: git checkout -- composer.json + + - uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: composer fix-style + commit_author: laravel-debugbar diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index d2c91e8e5..c0b9235cd 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -1,4 +1,4 @@ -name: Tests +name: Unit Tests on: [push, pull_request] @@ -38,36 +38,3 @@ jobs: - name: Execute Unit Tests run: composer test - - fix-style: - name: Fix Code Style - timeout-minutes: 15 - runs-on: ubuntu-latest - env: - COMPOSER_NO_INTERACTION: 1 - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: 7.4 - coverage: none - tools: composer:v2 - - - name: Install dependencies - run: | - composer update --prefer-dist --no-suggest --no-progress - - - run: composer fix-style - continue-on-error: true - - # Revert modifications so they don't get commited 💥 - - run: git checkout -- composer.json - - - uses: stefanzweifel/git-auto-commit-action@v4 - with: - commit_message: composer fix-style - commit_author: laravel-debugbar From 4fe9d74eb1b11c3bf5bcb1e4caeaea75c173a433 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Tue, 18 Aug 2020 09:44:50 +0200 Subject: [PATCH 172/585] Update fix-cs.yml --- .github/workflows/fix-cs.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/fix-cs.yml b/.github/workflows/fix-cs.yml index 87a9c75da..c92491dd9 100644 --- a/.github/workflows/fix-cs.yml +++ b/.github/workflows/fix-cs.yml @@ -1,6 +1,9 @@ name: Fix Codestyle -on: [push, pull_request] +on: + push: + branches: + - master jobs: fix-style: From 37dc7a30da0df03b320e6af46abbfebac34026ef Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 20 Aug 2020 12:09:26 +0200 Subject: [PATCH 173/585] Feat test dusk (#1092) * Test with Dusk * No UI * composer fix-style * Update BrowserTestCase.php * Update matrix * Update run-tests.yml * Update composer.json * Start with 6.x Co-authored-by: laravel-debugbar --- .github/workflows/run-tests.yml | 19 ++++--- .gitignore | 1 + composer.json | 4 +- tests/BrowserTestCase.php | 36 +++++++++++++ tests/DebugbarBrowserTest.php | 91 +++++++++++++++++++++++++++++++++ tests/TestCase.php | 2 - 6 files changed, 144 insertions(+), 9 deletions(-) create mode 100644 tests/BrowserTestCase.php create mode 100644 tests/DebugbarBrowserTest.php diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index c0b9235cd..f9a4f94b4 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -12,11 +12,8 @@ jobs: strategy: matrix: php: [7.4, 7.3, 7.2] - laravel: [7.*, 6.*, 5.5.*] + laravel: [6.*, 7.*] dependency-version: [prefer-lowest, prefer-stable] - exclude: - - laravel: 5.5.* - php: 7.4 name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} @@ -33,8 +30,18 @@ jobs: - name: Install dependencies run: | - composer require "laravel/framework:${{ matrix.laravel }}" --no-update --no-progress - composer update --${{ matrix.dependency-version }} --prefer-dist --no-suggest --no-progress + composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update + composer update --${{ matrix.dependency-version }} --prefer-dist --no-progress + + - name: Update Dusk Chromedriver + run: vendor/bin/dusk-updater detect --auto-update - name: Execute Unit Tests run: composer test + + - name: Upload Failed Screenshots + uses: actions/upload-artifact@v2-preview + if: failure() + with: + name: screenshots + path: tests/Browser/screenshots/* diff --git a/.gitignore b/.gitignore index 08dc6ae98..7639d5a9a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ composer.phar composer.lock .DS_Store .phpunit.result.cache +/tests/Browser \ No newline at end of file diff --git a/composer.json b/composer.json index 6576f8ba4..d7fa826de 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ } ], "require": { - "php": ">=7.0", + "php": ">=7.1", "maximebf/debugbar": "^1.16.3", "illuminate/routing": "^5.5|^6|^7", "illuminate/session": "^5.5|^6|^7", @@ -20,6 +20,8 @@ }, "require-dev": { "orchestra/testbench": "^3.5.11|^4.0|^5.0", + "orchestra/testbench-dusk": "^3.5.11|^4.0|^5.0", + "orchestra/dusk-updater": "^1", "phpunit/phpunit": "^6.0|^7.0|^8.5|^9.0", "squizlabs/php_codesniffer": "^3.5" }, diff --git a/tests/BrowserTestCase.php b/tests/BrowserTestCase.php new file mode 100644 index 000000000..cdeae841f --- /dev/null +++ b/tests/BrowserTestCase.php @@ -0,0 +1,36 @@ + Facade::class]; + } +} diff --git a/tests/DebugbarBrowserTest.php b/tests/DebugbarBrowserTest.php new file mode 100644 index 000000000..b23c0423e --- /dev/null +++ b/tests/DebugbarBrowserTest.php @@ -0,0 +1,91 @@ +set('app.debug', true); + + /** @var Router $router */ + $router = $app['router']; + + $this->addWebRoutes($router); + $this->addApiRoutes($router); + + \Orchestra\Testbench\Dusk\Options::withoutUI(); + } + + /** + * @param Router $router + */ + protected function addWebRoutes(Router $router) + { + $router->get('web/plain', [ + 'uses' => function () { + return 'PONG'; + } + ]); + + $router->get('web/html', [ + 'uses' => function () { + return 'HTMLPONG'; + } + ]); + } + + /** + * @param Router $router + */ + protected function addApiRoutes(Router $router) + { + $router->get('api/ping', [ + 'uses' => function () { + return response()->json(['status' => 'pong']); + } + ]); + } + + public function testItInjectsOnPlainText() + { + $this->browse(function ($browser) { + $browser->visit('web/plain') + ->assertSee('PONG') + ->waitFor('.phpdebugbar') + ->assertSee('GET web/plain'); + }); + } + + public function testItInjectsOnHtml() + { + $this->browse(function ($browser) { + $browser->visit('web/html') + ->assertSee('HTMLPONG') + ->waitFor('.phpdebugbar') + ->assertSee('GET web/html'); + }); + } + + public function testItDoesntInjectOnJson() + { + $this->browse(function ($browser) { + $browser->visit('api/ping') + ->assertSee('pong') + ->assertSourceMissing('debugbar') + ->assertDontSee('GET api/ping'); + }); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index 3c81598fa..207bc78f5 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -9,8 +9,6 @@ class TestCase extends Orchestra { - /** @var \Barryvdh\Debugbar\LaravelDebugbar */ - private $debugbar; /** * Get package providers. From ba28d4a8e64a319f6067a6392298dc0fd4fc1304 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 20 Aug 2020 12:25:52 +0200 Subject: [PATCH 174/585] Bump to Laravel 6.x minimum --- composer.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index d7fa826de..76050eed6 100644 --- a/composer.json +++ b/composer.json @@ -10,19 +10,19 @@ } ], "require": { - "php": ">=7.1", + "php": ">=7.2", "maximebf/debugbar": "^1.16.3", - "illuminate/routing": "^5.5|^6|^7", - "illuminate/session": "^5.5|^6|^7", - "illuminate/support": "^5.5|^6|^7", - "symfony/debug": "^3|^4|^5", - "symfony/finder": "^3|^4|^5" + "illuminate/routing": "^6|^7", + "illuminate/session": "^6|^7", + "illuminate/support": "^6|^7", + "symfony/debug": "^4.3|^5", + "symfony/finder": "^4.3|^5" }, "require-dev": { - "orchestra/testbench": "^3.5.11|^4.0|^5.0", - "orchestra/testbench-dusk": "^3.5.11|^4.0|^5.0", + "orchestra/testbench": "^4.0|^5.0", + "orchestra/testbench-dusk": "^4.0|^5.0", "orchestra/dusk-updater": "^1", - "phpunit/phpunit": "^6.0|^7.0|^8.5|^9.0", + "phpunit/phpunit": "^8.5|^9.0", "squizlabs/php_codesniffer": "^3.5" }, "autoload": { From 845fd342f681f08992dc8d0d253b3b8d3b452d14 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 20 Aug 2020 12:28:00 +0200 Subject: [PATCH 175/585] Update readme.md --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index 2ce334bfb..74d3b9cc8 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,5 @@ ## Laravel Debugbar +![Unit Tests](https://github.com/barryvdh/laravel-debugbar/workflows/Unit%20Tests/badge.svg) [![Packagist License](https://poser.pugx.org/barryvdh/laravel-debugbar/license.png)](http://choosealicense.com/licenses/mit/) [![Latest Stable Version](https://poser.pugx.org/barryvdh/laravel-debugbar/version.png)](https://packagist.org/packages/barryvdh/laravel-debugbar) [![Total Downloads](https://poser.pugx.org/barryvdh/laravel-debugbar/d/total.png)](https://packagist.org/packages/barryvdh/laravel-debugbar) From 8023cf6d06a529f1c8aed99ec5503950ffec8c98 Mon Sep 17 00:00:00 2001 From: hebbet Date: Thu, 27 Aug 2020 17:06:51 +0200 Subject: [PATCH 176/585] allow laravel 8.x (#1095) --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 76050eed6..d30613bfc 100644 --- a/composer.json +++ b/composer.json @@ -12,9 +12,9 @@ "require": { "php": ">=7.2", "maximebf/debugbar": "^1.16.3", - "illuminate/routing": "^6|^7", - "illuminate/session": "^6|^7", - "illuminate/support": "^6|^7", + "illuminate/routing": "^6|^7|^8", + "illuminate/session": "^6|^7|^8", + "illuminate/support": "^6|^7|^8", "symfony/debug": "^4.3|^5", "symfony/finder": "^4.3|^5" }, From 7c0d143e0d3e05a27eae8cbea2f4b323c32b8f5c Mon Sep 17 00:00:00 2001 From: ARCANEDEV Date: Thu, 27 Aug 2020 21:03:27 +0100 Subject: [PATCH 177/585] Update composer.json (#1096) Related: #1095 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index d30613bfc..8bcebdf0b 100644 --- a/composer.json +++ b/composer.json @@ -42,7 +42,7 @@ "prefer-stable": true, "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.5-dev" }, "laravel": { "providers": [ From 05b74ee384b5b11d96df10ab1a50ac40153e6b17 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Fri, 28 Aug 2020 11:29:30 +0200 Subject: [PATCH 178/585] Test Laravel 8 (#1097) * Test Laravel 8 --- .github/workflows/run-tests.yml | 5 ++++- composer.json | 4 +--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index f9a4f94b4..ee34bab6f 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -12,8 +12,11 @@ jobs: strategy: matrix: php: [7.4, 7.3, 7.2] - laravel: [6.*, 7.*] + laravel: [8.*, 6.*, 7.*] dependency-version: [prefer-lowest, prefer-stable] + exclude: + - laravel: 8.* + php: 7.2 name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} diff --git a/composer.json b/composer.json index 8bcebdf0b..bb508768e 100644 --- a/composer.json +++ b/composer.json @@ -19,9 +19,7 @@ "symfony/finder": "^4.3|^5" }, "require-dev": { - "orchestra/testbench": "^4.0|^5.0", - "orchestra/testbench-dusk": "^4.0|^5.0", - "orchestra/dusk-updater": "^1", + "orchestra/testbench-dusk": "^4|^5|^6", "phpunit/phpunit": "^8.5|^9.0", "squizlabs/php_codesniffer": "^3.5" }, From 8e25b3b926d9f433e67c5faaf3c27d64b6aed31e Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Fri, 28 Aug 2020 12:03:16 +0200 Subject: [PATCH 179/585] Avoid duplicate tests (#1098) --- .github/workflows/run-tests.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index ee34bab6f..a6787b5f5 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -1,6 +1,14 @@ name: Unit Tests -on: [push, pull_request] +on: + push: + branches: + - master + pull_request: + branches: + - "*" + schedule: + - cron: '0 0 * * *' jobs: php-tests: From 1180dca59006aa4e58fed9057322c3861b3d1c23 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Sun, 30 Aug 2020 09:03:31 +0200 Subject: [PATCH 180/585] Override sf-dump colors in Dark Mode --- src/Resources/laravel-debugbar-dark-mode.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Resources/laravel-debugbar-dark-mode.css b/src/Resources/laravel-debugbar-dark-mode.css index 66d52dc8e..4c55466cd 100644 --- a/src/Resources/laravel-debugbar-dark-mode.css +++ b/src/Resources/laravel-debugbar-dark-mode.css @@ -100,6 +100,12 @@ div.phpdebugbar code.phpdebugbar-widgets-sql span.hljs-operator { color: var(--color-gray-100); } +div.phpdebugbar pre.sf-dump .sf-dump-public, +div.phpdebugbar pre.sf-dump .sf-dump-protected, +div.phpdebugbar pre.sf-dump .sf-dump-private { + color: var(--color-gray-100) !important; +} + div.phpdebugbar div.phpdebugbar-panel div.phpdebugbar-widgets-status > span:first-child:before, div.phpdebugbar-openhandler a { color: var(--color-gray-500); From 233c10688f4c1a6e66ed2ef123038b1363d1bedc Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Mon, 7 Sep 2020 13:32:39 -0600 Subject: [PATCH 181/585] Fix "array to string conversion" in 3.5.0 (#1102) This commit broke the debugbar: https://github.com/barryvdh/laravel-debugbar/commit/51c8ea3ab2c56664ebc9718ca68e342827bd7851#diff-8a2d53e88e43b04de0cc4f64882d91feR63 since sprintf() uses multiple arguments, it doesn't accept an array of arguments. --- src/DataFormatter/SimpleFormatter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DataFormatter/SimpleFormatter.php b/src/DataFormatter/SimpleFormatter.php index 0b06c2bb6..9825e9133 100644 --- a/src/DataFormatter/SimpleFormatter.php +++ b/src/DataFormatter/SimpleFormatter.php @@ -61,7 +61,7 @@ private function exportValue($value, $depth = 1, $deep = false) if ($deep) { $args = [$indent, implode(sprintf(", \n%s", $indent), $a), str_repeat(' ', $depth - 1)]; - return sprintf("[\n%s%s\n%s]", $args); + return sprintf("[\n%s%s\n%s]", ...$args); } $s = sprintf('[%s]', implode(', ', $a)); From 01f630772d1bbee44bb6dad3a9ad1446bd2fa0d9 Mon Sep 17 00:00:00 2001 From: Martijn Gastkemper Date: Sat, 3 Oct 2020 10:52:55 +0200 Subject: [PATCH 182/585] Add debug function to Collection like dump (#1115) --- readme.md | 3 +++ src/ServiceProvider.php | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/readme.md b/readme.md index 74d3b9cc8..ed4291a9e 100644 --- a/readme.md +++ b/readme.md @@ -132,6 +132,9 @@ There are also helper functions available for the most common calls: // All arguments will be dumped as a debug message debug($var1, $someString, $intValue, $object); +// `$collection->debug()` will return the collection and dump it as a debug message. Like `$collection->dump()` +collect([$var1, $someString])->debug(); + start_measure('render','Time for rendering'); stop_measure('render'); add_measure('now', LARAVEL_START, microtime(true)); diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index ef9ecc43a..02a5af486 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -9,6 +9,7 @@ use Illuminate\Contracts\Http\Kernel; use Illuminate\Routing\Router; use Illuminate\Session\SessionManager; +use Illuminate\Support\Collection; class ServiceProvider extends \Illuminate\Support\ServiceProvider { @@ -58,6 +59,11 @@ function ($app) { ); $this->commands(['command.debugbar.clear']); + + Collection::macro('debug', function () { + debug($this); + return $this; + }); } /** From 9bddf89abaec063bb4d1753ba87e1b1d5620e729 Mon Sep 17 00:00:00 2001 From: Pataar Date: Tue, 6 Oct 2020 16:17:05 +0200 Subject: [PATCH 183/585] Add integration tests (#1118) * Add Integration tests * Escape backslashes * Change package name * Remove one backslash while escaping provider --- .github/workflows/run-integration-tests.yml | 99 +++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 .github/workflows/run-integration-tests.yml diff --git a/.github/workflows/run-integration-tests.yml b/.github/workflows/run-integration-tests.yml new file mode 100644 index 000000000..6e626fb24 --- /dev/null +++ b/.github/workflows/run-integration-tests.yml @@ -0,0 +1,99 @@ +name: Integration Tests + +on: + push: + branches: + - master + pull_request: + branches: + - "*" + schedule: + - cron: '0 0 * * *' + +jobs: + php-lumen-integration-tests: + runs-on: ubuntu-latest + timeout-minutes: 15 + env: + COMPOSER_NO_INTERACTION: 1 + strategy: + matrix: + php: [7.4, 7.3, 7.2] + lumen: [7.*, 6.*] + name: P${{ matrix.php }} - Lumen${{ matrix.lumen }} + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + path: src + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: none + tools: composer:v2 + + - name: Install dependencies + run: | + composer create-project --prefer-dist laravel/lumen:${{ matrix.lumen }} --no-progress sample + cd sample + composer update --prefer-stable --prefer-dist --no-progress + - name: Add package from source + run: | + cd sample + sed -e 's|"type": "project",|&\n"repositories": [ { "type": "path", "url": "../src" } ],|' -i composer.json + composer require --dev "barryvdh/laravel-debugbar:*" + - name: Insert service provider + run: sed -e 's|// \$app->register(App\\\Providers\\\EventServiceProvider::class);|&\n$app->register(\\Barryvdh\\Debugbar\\LumenServiceProvider::class);|' -i sample/bootstrap/app.php + + - name: Execute clear run + run: | + cd sample + php artisan debugbar:clear + - name: Check file count in logs + run: | + if [ `ls -1q "sample/storage/logs/" | wc -l` -gt 0 ];then exit 1;fi + php-laravel-integration-tests: + runs-on: ubuntu-latest + timeout-minutes: 15 + env: + COMPOSER_NO_INTERACTION: 1 + strategy: + matrix: + php: [7.4, 7.3, 7.2] + laravel: [8.*, 7.*, 6.*] + exclude: + - laravel: 8.* + php: 7.2 + name: P${{ matrix.php }} - Laravel${{ matrix.laravel }} + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + path: src + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: none + tools: composer:v2 + + - name: Install dependencies + run: | + composer create-project --prefer-dist laravel/laravel:${{ matrix.laravel }} --no-progress sample + cd sample + composer update --prefer-stable --prefer-dist --no-progress + - name: Add package from source + run: | + cd sample + sed -e 's|"type": "project",|&\n"repositories": [ { "type": "path", "url": "../src" } ],|' -i composer.json + composer require --dev "barryvdh/laravel-debugbar:*" + - name: Execute generate run + run: | + cd sample + php artisan debugbar:clear + - name: Check file count in logs + run: | + if [ `ls -1q "sample/storage/logs/" | wc -l` -gt 0 ];then exit 1;fi From 618911a182495e8e39903b4c2a7976c84d1b7bdc Mon Sep 17 00:00:00 2001 From: Pascal Baljet Date: Mon, 7 Dec 2020 11:54:58 +0100 Subject: [PATCH 184/585] Enable PHP 8.0 build (#1129) * WIP * Trigger notification * Update run-integration-tests.yml * Update composer.json * Update run-integration-tests.yml --- .github/workflows/run-integration-tests.yml | 13 ++++++++++--- .github/workflows/run-tests.yml | 4 ++-- composer.json | 1 + 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/run-integration-tests.yml b/.github/workflows/run-integration-tests.yml index 6e626fb24..fb2edd370 100644 --- a/.github/workflows/run-integration-tests.yml +++ b/.github/workflows/run-integration-tests.yml @@ -18,8 +18,15 @@ jobs: COMPOSER_NO_INTERACTION: 1 strategy: matrix: - php: [7.4, 7.3, 7.2] - lumen: [7.*, 6.*] + php: [8.0, 7.4, 7.3, 7.2] + lumen: [8.*, 7.*, 6.*] + exclude: + - lumen: 8.* + php: 7.2 + - lumen: 7.* + php: 8.0 + - lumen: 6.* + php: 8.0 name: P${{ matrix.php }} - Lumen${{ matrix.lumen }} steps: - name: Checkout code @@ -61,7 +68,7 @@ jobs: COMPOSER_NO_INTERACTION: 1 strategy: matrix: - php: [7.4, 7.3, 7.2] + php: [8.0, 7.4, 7.3, 7.2] laravel: [8.*, 7.*, 6.*] exclude: - laravel: 8.* diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index a6787b5f5..bf46843bd 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -19,7 +19,7 @@ jobs: strategy: matrix: - php: [7.4, 7.3, 7.2] + php: [8.0, 7.4, 7.3, 7.2] laravel: [8.*, 6.*, 7.*] dependency-version: [prefer-lowest, prefer-stable] exclude: @@ -38,7 +38,7 @@ jobs: php-version: ${{ matrix.php }} coverage: none tools: composer:v2 - + - name: Install dependencies run: | composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update diff --git a/composer.json b/composer.json index bb508768e..2c312022e 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ "symfony/finder": "^4.3|^5" }, "require-dev": { + "mockery/mockery": "^1.3.3", "orchestra/testbench-dusk": "^4|^5|^6", "phpunit/phpunit": "^8.5|^9.0", "squizlabs/php_codesniffer": "^3.5" From 66ce2a74c97a2ba37e0b8c7bd4963c612734919b Mon Sep 17 00:00:00 2001 From: laravel-debugbar Date: Mon, 7 Dec 2020 10:56:03 +0000 Subject: [PATCH 185/585] composer fix-style --- src/DataCollector/LogsCollector.php | 4 ++-- src/DataCollector/ViewCollector.php | 2 +- src/ServiceProvider.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/DataCollector/LogsCollector.php b/src/DataCollector/LogsCollector.php index 28ed6e14f..ca2b3bcb8 100644 --- a/src/DataCollector/LogsCollector.php +++ b/src/DataCollector/LogsCollector.php @@ -27,12 +27,12 @@ public function getLogsFile() { // default daily rotating logs (Laravel 5.0) $path = storage_path() . '/logs/laravel-' . date('Y-m-d') . '.log'; - + // single file logs if (!file_exists($path)) { $path = storage_path() . '/logs/laravel.log'; } - + return $path; } diff --git a/src/DataCollector/ViewCollector.php b/src/DataCollector/ViewCollector.php index 46a33166b..7af996665 100644 --- a/src/DataCollector/ViewCollector.php +++ b/src/DataCollector/ViewCollector.php @@ -55,7 +55,7 @@ public function addView(View $view) { $name = $view->getName(); $path = $view->getPath(); - + if (!is_object($path)) { if ($path) { $path = ltrim(str_replace(base_path(), '', realpath($path)), '/'); diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 02a5af486..1ebd08229 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -29,7 +29,7 @@ public function register() { $configPath = __DIR__ . '/../config/debugbar.php'; $this->mergeConfigFrom($configPath, 'debugbar'); - + $this->loadRoutesFrom(realpath(__DIR__ . '/debugbar-routes.php')); $this->app->alias( From 759f2df890a2da62440f147bbfc046071842ef27 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Mon, 21 Dec 2020 09:30:32 +0100 Subject: [PATCH 186/585] Add extensions to php test (#1139) Add extensions to Github actions --- .github/workflows/run-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index bf46843bd..a18e01f92 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -38,6 +38,7 @@ jobs: php-version: ${{ matrix.php }} coverage: none tools: composer:v2 + extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif - name: Install dependencies run: | From d6fe211b8f2d36ade3357df2a8576f1ae3103420 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 6 Jan 2021 11:19:00 +0100 Subject: [PATCH 187/585] Add AppStorage driver (#1141) * Add AppStorage driver * Rename storage driver to SocketStorage --- config/debugbar.php | 4 +- src/LaravelDebugbar.php | 6 ++ src/Storage/SocketStorage.php | 100 ++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 src/Storage/SocketStorage.php diff --git a/config/debugbar.php b/config/debugbar.php index ada83c6d3..37c2f6891 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -34,10 +34,12 @@ */ 'storage' => [ 'enabled' => true, - 'driver' => 'file', // redis, file, pdo, custom + 'driver' => 'file', // redis, file, pdo, socket, custom 'path' => storage_path('debugbar'), // For file driver 'connection' => null, // Leave null for default connection (Redis/PDO) 'provider' => '', // Instance of StorageInterface for custom driver + 'hostname' => '127.0.0.1', // Hostname to use with the "socket" driver + 'port' => 2304, // Port to use with the "socket" driver ], /* diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 79b849431..bb9691a2f 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -15,6 +15,7 @@ use Barryvdh\Debugbar\DataCollector\SessionCollector; use Barryvdh\Debugbar\DataCollector\RequestCollector; use Barryvdh\Debugbar\DataCollector\ViewCollector; +use Barryvdh\Debugbar\Storage\SocketStorage; use Barryvdh\Debugbar\Storage\FilesystemStorage; use DebugBar\Bridge\MonologCollector; use DebugBar\Bridge\SwiftMailer\SwiftLogCollector; @@ -1099,6 +1100,11 @@ protected function selectStorage(DebugBar $debugbar) $class = $config->get('debugbar.storage.provider'); $storage = $this->app->make($class); break; + case 'app': + $hostname = $config->get('debugbar.storage.hostname', '127.0.0.1'); + $port = $config->get('debugbar.storage.port', 2304); + $storage = new SocketStorage($hostname, $port); + break; case 'file': default: $path = $config->get('debugbar.storage.path'); diff --git a/src/Storage/SocketStorage.php b/src/Storage/SocketStorage.php new file mode 100644 index 000000000..04334d0f2 --- /dev/null +++ b/src/Storage/SocketStorage.php @@ -0,0 +1,100 @@ +hostname = $hostname; + $this->port = $port; + } + + /** + * @inheritDoc + */ + function save($id, $data) + { + $socketIsFresh = !$this->socket; + + if (!$this->socket = $this->socket ?: $this->createSocket()) { + return false; + } + + $encodedPayload = json_encode([ + 'id' => $id, + 'base_path' => base_path(), + 'app' => config('app.name'), + 'data' => $data, + ]); + + $encodedPayload = strlen($encodedPayload).'#'.$encodedPayload; + + set_error_handler([self::class, 'nullErrorHandler']); + try { + if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) { + return true; + } + if (!$socketIsFresh) { + stream_socket_shutdown($this->socket, \STREAM_SHUT_RDWR); + fclose($this->socket); + $this->socket = $this->createSocket(); + } + if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) { + return true; + } + } finally { + restore_error_handler(); + } + } + + private static function nullErrorHandler($t, $m) + { + // no-op + } + + protected function createSocket() + { + set_error_handler([self::class, 'nullErrorHandler']); + try { + return stream_socket_client("tcp://{$this->hostname}:{$this->port}"); + } finally { + restore_error_handler(); + } + } + + /** + * @inheritDoc + */ + function get($id) + { + // + } + + /** + * @inheritDoc + */ + function find(array $filters = array(), $max = 20, $offset = 0) + { + // + } + + /** + * @inheritDoc + */ + function clear() + { + // + } +} \ No newline at end of file From ef326fdd9ea83910b13840b918b69c580f897a61 Mon Sep 17 00:00:00 2001 From: laravel-debugbar Date: Wed, 6 Jan 2021 10:20:43 +0000 Subject: [PATCH 188/585] composer fix-style --- src/Storage/SocketStorage.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Storage/SocketStorage.php b/src/Storage/SocketStorage.php index 04334d0f2..6b242b2f7 100644 --- a/src/Storage/SocketStorage.php +++ b/src/Storage/SocketStorage.php @@ -39,7 +39,7 @@ function save($id, $data) 'data' => $data, ]); - $encodedPayload = strlen($encodedPayload).'#'.$encodedPayload; + $encodedPayload = strlen($encodedPayload) . '#' . $encodedPayload; set_error_handler([self::class, 'nullErrorHandler']); try { @@ -97,4 +97,4 @@ function clear() { // } -} \ No newline at end of file +} From cae0a8d1cb89b0f0522f65e60465e16d738e069b Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Wed, 6 Jan 2021 15:21:44 +0100 Subject: [PATCH 189/585] Fix storage name when creating the storage instance (#1143) --- src/LaravelDebugbar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index bb9691a2f..eda8c9281 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -1100,7 +1100,7 @@ protected function selectStorage(DebugBar $debugbar) $class = $config->get('debugbar.storage.provider'); $storage = $this->app->make($class); break; - case 'app': + case 'socket': $hostname = $config->get('debugbar.storage.hostname', '127.0.0.1'); $port = $config->get('debugbar.storage.port', 2304); $storage = new SocketStorage($hostname, $port); From bfd60f1262580bdd838160f64c02f93081e5f712 Mon Sep 17 00:00:00 2001 From: Ahmed Mohamed Abd El Ftah Date: Mon, 5 Apr 2021 19:24:22 +0200 Subject: [PATCH 190/585] Laravel Octane Compatibility (#1165) * Add octane compatibility * Modify bindings --- src/ServiceProvider.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 1ebd08229..066031e69 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -37,11 +37,11 @@ public function register() DataFormatterInterface::class ); - $this->app->singleton(LaravelDebugbar::class, function () { - $debugbar = new LaravelDebugbar($this->app); + $this->app->singleton(LaravelDebugbar::class, function ($app) { + $debugbar = new LaravelDebugbar($app); - if ($this->app->bound(SessionManager::class)) { - $sessionManager = $this->app->make(SessionManager::class); + if ($app->bound(SessionManager::class)) { + $sessionManager = $app->make(SessionManager::class); $httpDriver = new SymfonyHttpDriver($sessionManager); $debugbar->setHttpDriver($httpDriver); } From e120edf2b936d3ce8e25d55f9fa12e221109d2ba Mon Sep 17 00:00:00 2001 From: Daniel Alm Date: Mon, 5 Apr 2021 19:44:47 +0200 Subject: [PATCH 191/585] Add a few more secret keys to possibly sanitize. (#1164) --- src/DataCollector/RequestCollector.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/DataCollector/RequestCollector.php b/src/DataCollector/RequestCollector.php index 25760a4e2..cc3fe866f 100644 --- a/src/DataCollector/RequestCollector.php +++ b/src/DataCollector/RequestCollector.php @@ -119,6 +119,7 @@ public function collect() if ( Str::is('*_KEY', $key) || Str::is('*_PASSWORD', $key) || Str::is('*_SECRET', $key) || Str::is('*_PW', $key) + || Str::is('*_TOKEN', $key) || Str::is('*_PASS', $key) ) { $data['request_server'][$key] = '******'; } From 9c5f09bac42e83efc5ccfa4c705b5a184d834ac1 Mon Sep 17 00:00:00 2001 From: Kyrax324 <72662771+Kyrax324@users.noreply.github.com> Date: Tue, 6 Apr 2021 01:45:15 +0800 Subject: [PATCH 192/585] Update debugbar.php (#1160) - allow to set debugbar theme in env --- config/debugbar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/debugbar.php b/config/debugbar.php index 37c2f6891..19e2fb0e0 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -214,5 +214,5 @@ | Switches between light and dark theme. If set to auto it will respect system preferences | Possible values: auto, light, dark */ - 'theme' => 'auto', + 'theme' => env('DEBUGBAR_THEME', 'auto'), ]; From a0895370f3655e6a65744d048c40e676be949f10 Mon Sep 17 00:00:00 2001 From: Supian M Date: Tue, 6 Apr 2021 01:47:14 +0800 Subject: [PATCH 193/585] Fix Closure Middleware Can't Converted As String (#1146) --- src/DataCollector/RouteCollector.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/DataCollector/RouteCollector.php b/src/DataCollector/RouteCollector.php index 53c9b673b..9e17b8fb3 100644 --- a/src/DataCollector/RouteCollector.php +++ b/src/DataCollector/RouteCollector.php @@ -2,6 +2,7 @@ namespace Barryvdh\Debugbar\DataCollector; +use Closure; use DebugBar\DataCollector\DataCollector; use DebugBar\DataCollector\Renderable; use Illuminate\Http\Request; @@ -95,7 +96,9 @@ protected function getRouteInformation($route) */ protected function getMiddleware($route) { - return implode(', ', $route->middleware()); + return implode(', ', array_map(function ($middleware) { + return $middleware instanceof Closure ? 'Closure' : $middleware; + }, $route->middleware())); } /** From 5b75044d45650843a186b7e733ff037dca9d5f50 Mon Sep 17 00:00:00 2001 From: BinotaLIU Date: Tue, 6 Apr 2021 01:47:44 +0800 Subject: [PATCH 194/585] Exclude higher order proxy by default (#1135) --- src/DataCollector/QueryCollector.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 19db0f84e..3e56eff9b 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -21,6 +21,7 @@ class QueryCollector extends PDOCollector protected $showCopyButton = false; protected $reflection = []; protected $backtraceExcludePaths = [ + '/vendor/laravel/framework/src/Illuminate/Support/HigherOrderTapProxy', '/vendor/laravel/framework/src/Illuminate/Database', '/vendor/laravel/framework/src/Illuminate/Events', '/vendor/barryvdh/laravel-debugbar', From b8af309dea71eab3f2c942652969f518130228ee Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Tue, 6 Apr 2021 20:11:42 +0200 Subject: [PATCH 195/585] Use request instance --- src/Controllers/OpenHandlerController.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Controllers/OpenHandlerController.php b/src/Controllers/OpenHandlerController.php index c2e95b300..02cc3c61d 100644 --- a/src/Controllers/OpenHandlerController.php +++ b/src/Controllers/OpenHandlerController.php @@ -4,15 +4,16 @@ use Barryvdh\Debugbar\Support\Clockwork\Converter; use DebugBar\OpenHandler; +use Illuminate\Http\Request; use Illuminate\Http\Response; class OpenHandlerController extends BaseController { - public function handle() + public function handle(Request $request) { $openHandler = new OpenHandler($this->debugbar); - $data = $openHandler->handle(null, false, false); + $data = $openHandler->handle($request->input(), false, false); return new Response( $data, From 6420113d90bb746423fa70b9940e9e7c26ebc121 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Wed, 7 Apr 2021 13:19:20 +0200 Subject: [PATCH 196/585] Fix duration for Octane with Swoole Fixes https://github.com/barryvdh/laravel-debugbar/issues/1166 --- src/LaravelDebugbar.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index eda8c9281..79c2541d6 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -147,15 +147,13 @@ public function boot() } if ($this->shouldCollect('time', true)) { - $this->addCollector(new TimeDataCollector()); + $startTime = $app['request']->server('REQUEST_TIME_FLOAT'); + $this->addCollector(new TimeDataCollector($startTime)); - if (! $this->isLumen()) { + if (! $this->isLumen() && $startTime) { $this->app->booted( - function () use ($debugbar) { - $startTime = $this->app['request']->server('REQUEST_TIME_FLOAT'); - if ($startTime) { - $debugbar['time']->addMeasure('Booting', $startTime, microtime(true)); - } + function () use ($debugbar, $startTime) { + $debugbar['time']->addMeasure('Booting', $startTime, microtime(true)); } ); } From 6a4bf30a965447268aa23bc4b2456e021762134a Mon Sep 17 00:00:00 2001 From: Josh Date: Wed, 12 May 2021 21:59:24 +0100 Subject: [PATCH 197/585] fix: db collector octane compatibility (#1179) This patch should address #1174, although it does so rather crudely. A better solution might be to re-register the event listeners on each request or re-bind the closures somehow, although I'm interested to hear any feedback on this. --- src/LaravelDebugbar.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 79c2541d6..164587d61 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -342,7 +342,7 @@ function ( $db, $queryCollector ) { - if (!$this->shouldCollect('db', true)) { + if (!app(static::class)->shouldCollect('db', true)) { return; // Issue 776 : We've turned off collecting after the listener was attached } // Laravel 5.2 changed the way some core events worked. We must account for @@ -359,7 +359,7 @@ function ( } //allow collecting only queries slower than a specified amount of milliseconds - $threshold = $this->app['config']->get('debugbar.options.db.slow_threshold', false); + $threshold = app('config')->get('debugbar.options.db.slow_threshold', false); if (!$threshold || $time > $threshold) { $queryCollector->addQuery((string)$query, $bindings, $time, $connection); } From 81d3370a7eac3f614a357a87f8f9e7b6e0e61f7f Mon Sep 17 00:00:00 2001 From: Muhittin Tan Date: Thu, 13 May 2021 10:01:12 +0300 Subject: [PATCH 198/585] Remove unnecessary, deprecated defer codes (#1138) Since Laravel 5.8 , deferred providers should implement DeferrableProvider contract instead of using $defer . $defer is deprecated. https://laravel.com/docs/5.8/upgrade#deferred-service-providers Also since this provider is not deferred It is unnecessary to have a provides method Ref : https://laravel.com/docs/8.x/providers#deferred-providers --- src/ServiceProvider.php | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 066031e69..7f3dd7464 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -13,13 +13,6 @@ class ServiceProvider extends \Illuminate\Support\ServiceProvider { - /** - * Indicates if loading of the provider is deferred. - * - * @var bool - */ - protected $defer = false; - /** * Register the service provider. * @@ -119,14 +112,4 @@ protected function registerMiddleware($middleware) $kernel = $this->app[Kernel::class]; $kernel->pushMiddleware($middleware); } - - /** - * Get the services provided by the provider. - * - * @return array - */ - public function provides() - { - return ['debugbar', 'command.debugbar.clear', DataFormatterInterface::class, LaravelDebugbar::class]; - } } From 455e5f09ade861ec35788174386f8635c583e0ee Mon Sep 17 00:00:00 2001 From: James Anelay Date: Thu, 13 May 2021 08:01:29 +0100 Subject: [PATCH 199/585] Add expanation of how AJAX requests are identified. (#1134) --- config/debugbar.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/debugbar.php b/config/debugbar.php index 19e2fb0e0..dea925c14 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -67,6 +67,9 @@ | you can use this option to disable sending the data through the headers. | | Optionally, you can also send ServerTiming headers on ajax requests for the Chrome DevTools. + | + | Note for your request to be identified as ajax requests they must either send the header + | X-Requested-With with the value XMLHttpRequest (most JS libraries send this), or have application/json as a Accept header. */ 'capture_ajax' => true, From 49c1e07a86eaa6c908b787d05bd8cab57ff86bb6 Mon Sep 17 00:00:00 2001 From: Igor Date: Thu, 13 May 2021 09:01:57 +0200 Subject: [PATCH 200/585] Refactor `OpenHandlerController::handle()` (#1120) --- src/Controllers/OpenHandlerController.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Controllers/OpenHandlerController.php b/src/Controllers/OpenHandlerController.php index 02cc3c61d..5563c2945 100644 --- a/src/Controllers/OpenHandlerController.php +++ b/src/Controllers/OpenHandlerController.php @@ -15,13 +15,7 @@ public function handle(Request $request) $openHandler = new OpenHandler($this->debugbar); $data = $openHandler->handle($request->input(), false, false); - return new Response( - $data, - 200, - [ - 'Content-Type' => 'application/json' - ] - ); + return response()->json($data); } /** From fb5383132f3775cfcf859e4c77b74427d0158d9b Mon Sep 17 00:00:00 2001 From: Pataar Date: Thu, 13 May 2021 09:04:33 +0200 Subject: [PATCH 201/585] Add debug_backtrace_limit configuration option (#1114) * Add debug_backtrace_limit configuration option * Fix whitespace Co-authored-by: Barry vd. Heuvel --- config/debugbar.php | 10 ++++++++++ src/DataCollector/QueryCollector.php | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/config/debugbar.php b/config/debugbar.php index dea925c14..2ab294b67 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -218,4 +218,14 @@ | Possible values: auto, light, dark */ 'theme' => env('DEBUGBAR_THEME', 'auto'), + + /* + |-------------------------------------------------------------------------- + | Backtrace stack limit + |-------------------------------------------------------------------------- + | + | By default, the DebugBar limits the number of frames returned by the 'debug_backtrace()' function. + | If you need larger stacktraces, you can increase this number. Setting it to 0 will result in no limit. + */ + 'debug_backtrace_limit' => 50, ]; diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 3e56eff9b..0b2e6e64d 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -246,7 +246,7 @@ protected function performQueryAnalysis($query) */ protected function findSource() { - $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT, 50); + $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT, app('config')->get('debugbar.debug_backtrace_limit', 50)); $sources = []; From 88fd9cfa144b06b2549e9d487fdaec68265e791e Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 13 May 2021 22:18:35 +0200 Subject: [PATCH 202/585] Revert "Refactor `OpenHandlerController::handle()` (#1120)" (#1183) This reverts commit 49c1e07a86eaa6c908b787d05bd8cab57ff86bb6. --- src/Controllers/OpenHandlerController.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Controllers/OpenHandlerController.php b/src/Controllers/OpenHandlerController.php index 5563c2945..02cc3c61d 100644 --- a/src/Controllers/OpenHandlerController.php +++ b/src/Controllers/OpenHandlerController.php @@ -15,7 +15,13 @@ public function handle(Request $request) $openHandler = new OpenHandler($this->debugbar); $data = $openHandler->handle($request->input(), false, false); - return response()->json($data); + return new Response( + $data, + 200, + [ + 'Content-Type' => 'application/json' + ] + ); } /** From eb61c2f6c5b61a969e66bc1df36ef5568ecad9a0 Mon Sep 17 00:00:00 2001 From: Denis Chmel Date: Tue, 1 Jun 2021 04:19:47 -0400 Subject: [PATCH 203/585] Queries tab: show duration as a progressbar on a background (#1190) --- src/DataCollector/QueryCollector.php | 15 ++++++++++++++- src/Resources/laravel-debugbar.css | 16 ++++++++++++++++ src/Resources/sqlqueries/widget.js | 8 ++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 0b2e6e64d..2039f987d 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -207,7 +207,7 @@ protected function emulateQuote($value) * @version $Id$ * @access public * @param string $query - * @return string + * @return string[] */ protected function performQueryAnalysis($query) { @@ -492,6 +492,19 @@ public function collect() } } + if ($totalTime > 0) { + // For showing background measure on Queries tab + $start_percent = 0; + foreach ($statements as $i => $statement) { + $width_percent = $statement['duration'] / $totalTime * 100; + $statements[$i] = array_merge($statement, [ + 'start_percent' => round($start_percent, 3), + 'width_percent' => round($width_percent, 3), + ]); + $start_percent += $width_percent; + } + } + $nb_statements = array_filter($queries, function ($query) { return $query['type'] == 'query'; }); diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index db2cc92c2..62170efa3 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -777,3 +777,19 @@ div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-stmt-id:before, div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-param-count:before { margin-right: 6px!important; } + +.phpdebugbar-widgets-list-item .phpdebugbar-widgets-bg-measure { + position: absolute; + top: 0; + width: 100%; + height: 100%; + overflow: hidden; + pointer-events: none; +} + +.phpdebugbar-widgets-bg-measure .phpdebugbar-widgets-value { + position: absolute; + height: 100%; + opacity: 0.2; + background: red; +} diff --git a/src/Resources/sqlqueries/widget.js b/src/Resources/sqlqueries/widget.js index 189da8cb0..988dc2da9 100644 --- a/src/Resources/sqlqueries/widget.js +++ b/src/Resources/sqlqueries/widget.js @@ -65,6 +65,14 @@ } else { $('').addClass(csscls('sql')).html(PhpDebugBar.Widgets.highlight(stmt.sql, 'sql')).appendTo(li); } + if (stmt.width_percent) { + $('
    ').addClass(csscls('bg-measure')).append( + $('
    ').addClass(csscls('value')).css({ + left: stmt.start_percent + '%', + width: Math.max(stmt.width_percent, 0.01) + '%', + }) + ).appendTo(li); + } if (stmt.duration_str) { $('').addClass(csscls('duration')).text(stmt.duration_str).appendTo(li); } From 4c5f34a6f84ffac3f29a7c23c2192a06c0dd785d Mon Sep 17 00:00:00 2001 From: Victor Dauchy <26772554+vdauchy@users.noreply.github.com> Date: Tue, 1 Jun 2021 10:30:18 +0200 Subject: [PATCH 204/585] Add view generation time to timeline tab when 'time' and 'view' collectors are enabled. (#1178) * Add view 'time' when 'time' and 'view' collectors are enabled. * Use Debugbar instance to activate the tracking. * Update debugbar.php * Update ServiceProvider.php Co-authored-by: Barry vd. Heuvel --- config/debugbar.php | 1 + src/DebugbarViewEngine.php | 53 ++++++++++++++++++++++++++++++++++++++ src/ServiceProvider.php | 40 ++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 src/DebugbarViewEngine.php diff --git a/config/debugbar.php b/config/debugbar.php index 2ab294b67..019280127 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -161,6 +161,7 @@ 'full_log' => false, ], 'views' => [ + 'timeline' => false, // Add the views to the timeline (Experimental) 'data' => false, //Note: Can slow down the application, because the data can be quite large.. ], 'route' => [ diff --git a/src/DebugbarViewEngine.php b/src/DebugbarViewEngine.php new file mode 100644 index 000000000..7e8dc7c18 --- /dev/null +++ b/src/DebugbarViewEngine.php @@ -0,0 +1,53 @@ +engine = $engine; + $this->laravelDebugbar = $laravelDebugbar; + } + + /** + * @param string $path + * @param array $data + * @return string + */ + public function get($path, array $data = []) + { + return $this->laravelDebugbar->measure($path, function () use ($path, $data) { + return $this->engine->get($path, $data); + }); + } + + /** + * NOTE: This is done to support other Engine swap (example: Livewire). + * @param $name + * @param $arguments + * @return mixed + */ + public function __call($name, $arguments) + { + return $this->engine->$name(...$arguments); + } +} diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 7f3dd7464..82aed0955 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -7,9 +7,12 @@ use DebugBar\DataFormatter\DataFormatter; use DebugBar\DataFormatter\DataFormatterInterface; use Illuminate\Contracts\Http\Kernel; +use Illuminate\Foundation\Application; use Illuminate\Routing\Router; use Illuminate\Session\SessionManager; use Illuminate\Support\Collection; +use Illuminate\View\Engines\EngineResolver; +use Barryvdh\Debugbar\Facade as DebugBar; class ServiceProvider extends \Illuminate\Support\ServiceProvider { @@ -51,6 +54,43 @@ function ($app) { } ); + $this->app->extend( + 'view.engine.resolver', + function (EngineResolver $resolver, Application $application): EngineResolver { + $laravelDebugbar = $application->make(LaravelDebugbar::class); + + $shouldTrackViewTime = $laravelDebugbar->isEnabled() && + $laravelDebugbar->shouldCollect('time', true) && + $laravelDebugbar->shouldCollect('views', true) && + $application['config']->get('debugbar.options.views.timeline', false); + + if (! $shouldTrackViewTime) { + /* Do not swap the engine to save performance */ + return $resolver; + } + + return new class($resolver, $laravelDebugbar) extends EngineResolver { + + private $laravelDebugbar; + + public function __construct(EngineResolver $resolver, LaravelDebugbar $laravelDebugbar) + { + foreach ($resolver->resolvers as $engine => $resolver) { + $this->register($engine, $resolver); + } + $this->laravelDebugbar = $laravelDebugbar; + } + + public function register($engine, \Closure $resolver) + { + parent::register($engine, function () use ($resolver) { + return new DebugbarViewEngine($resolver(), $this->laravelDebugbar); + }); + } + }; + } + ); + $this->commands(['command.debugbar.clear']); Collection::macro('debug', function () { From fbc8bd2c0ac268cea68aa57355c21d827832657c Mon Sep 17 00:00:00 2001 From: laravel-debugbar Date: Tue, 1 Jun 2021 08:31:01 +0000 Subject: [PATCH 205/585] composer fix-style --- src/ServiceProvider.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 82aed0955..08e2107c2 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -61,7 +61,7 @@ function (EngineResolver $resolver, Application $application): EngineResolver { $shouldTrackViewTime = $laravelDebugbar->isEnabled() && $laravelDebugbar->shouldCollect('time', true) && - $laravelDebugbar->shouldCollect('views', true) && + $laravelDebugbar->shouldCollect('views', true) && $application['config']->get('debugbar.options.views.timeline', false); if (! $shouldTrackViewTime) { @@ -69,7 +69,7 @@ function (EngineResolver $resolver, Application $application): EngineResolver { return $resolver; } - return new class($resolver, $laravelDebugbar) extends EngineResolver { + return new class ($resolver, $laravelDebugbar) extends EngineResolver { private $laravelDebugbar; From b8914ce1c60117d175242de46758e45f87d68827 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Tue, 1 Jun 2021 10:44:22 +0200 Subject: [PATCH 206/585] Shorten path --- src/DebugbarViewEngine.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/DebugbarViewEngine.php b/src/DebugbarViewEngine.php index 7e8dc7c18..d808193ae 100644 --- a/src/DebugbarViewEngine.php +++ b/src/DebugbarViewEngine.php @@ -35,7 +35,9 @@ public function __construct(Engine $engine, LaravelDebugbar $laravelDebugbar) */ public function get($path, array $data = []) { - return $this->laravelDebugbar->measure($path, function () use ($path, $data) { + $shortPath = ltrim(str_replace(base_path(), '', realpath($path)), '/'); + + return $this->laravelDebugbar->measure($shortPath, function () use ($path, $data) { return $this->engine->get($path, $data); }); } From bc99f4c52aec0636ecb3aae4576ce84c5773bae2 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Tue, 1 Jun 2021 10:46:17 +0200 Subject: [PATCH 207/585] Limit query in timeline to 100 chars --- src/DataCollector/QueryCollector.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 2039f987d..cd6a2d9ac 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -4,6 +4,7 @@ use DebugBar\DataCollector\PDO\PDOCollector; use DebugBar\DataCollector\TimeDataCollector; +use Illuminate\Support\Str; /** * Collects data about SQL statements executed with PDO @@ -178,7 +179,7 @@ public function addQuery($query, $bindings, $time, $connection) ]; if ($this->timeCollector !== null) { - $this->timeCollector->addMeasure($query, $startTime, $endTime); + $this->timeCollector->addMeasure(Str::limit($query, 100), $startTime, $endTime); } } From 977670923c57d53e250cfb0eda3c4dbdf6844017 Mon Sep 17 00:00:00 2001 From: Lorenzo Kniss <59545226+lrnz-nabooki@users.noreply.github.com> Date: Wed, 2 Jun 2021 16:40:25 +1000 Subject: [PATCH 208/585] Fix: Skip the loop when the 'duration' key is not set in the '' array. That key is not present when the statement is of type 'explain' (#1192) --- src/DataCollector/QueryCollector.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index cd6a2d9ac..4d2aa6d57 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -481,7 +481,7 @@ public function collect() 'connection' => $query['connection'], ]; - //Add the results from the explain as new rows + // Add the results from the explain as new rows foreach ($query['explain'] as $explain) { $statements[] = [ 'sql' => " - EXPLAIN # {$explain->id}: `{$explain->table}` ({$explain->select_type})", @@ -496,18 +496,26 @@ public function collect() if ($totalTime > 0) { // For showing background measure on Queries tab $start_percent = 0; + foreach ($statements as $i => $statement) { + + if (! isset($statement['duration'])) { + continue; + } + $width_percent = $statement['duration'] / $totalTime * 100; + $statements[$i] = array_merge($statement, [ 'start_percent' => round($start_percent, 3), 'width_percent' => round($width_percent, 3), ]); + $start_percent += $width_percent; } } $nb_statements = array_filter($queries, function ($query) { - return $query['type'] == 'query'; + return $query['type'] === 'query'; }); $data = [ From f6f0f895a33cac801286a74355d146bb5384a5da Mon Sep 17 00:00:00 2001 From: laravel-debugbar Date: Wed, 2 Jun 2021 06:42:22 +0000 Subject: [PATCH 209/585] composer fix-style --- src/DataCollector/QueryCollector.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 4d2aa6d57..aafc6b4c1 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -498,11 +498,10 @@ public function collect() $start_percent = 0; foreach ($statements as $i => $statement) { - if (! isset($statement['duration'])) { continue; } - + $width_percent = $statement['duration'] / $totalTime * 100; $statements[$i] = array_merge($statement, [ From e207604767a29f0d4d2baeeca75776fd66d208a9 Mon Sep 17 00:00:00 2001 From: Laravel Freelancer NL <36150929+LaravelFreelancerNL@users.noreply.github.com> Date: Sat, 5 Jun 2021 14:08:41 +0200 Subject: [PATCH 210/585] Handle array and object bindings (#1194) * Format array and object bindings to strings * Format Array & Object bindings to string --- src/DataFormatter/QueryFormatter.php | 10 +++++ tests/DataFormatter/QueryFormatterTest.php | 49 ++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 tests/DataFormatter/QueryFormatterTest.php diff --git a/src/DataFormatter/QueryFormatter.php b/src/DataFormatter/QueryFormatter.php index 8b77c6144..e536f4584 100644 --- a/src/DataFormatter/QueryFormatter.php +++ b/src/DataFormatter/QueryFormatter.php @@ -33,6 +33,16 @@ public function checkBindings($bindings) if (is_string($binding) && !mb_check_encoding($binding, 'UTF-8')) { $binding = '[BINARY DATA]'; } + + if (is_array($binding)) { + $binding = $this->checkBindings($binding); + $binding = '[' . implode(',', $binding) . ']'; + } + + if (is_object($binding)) { + $binding = json_encode($binding); + } + } return $bindings; diff --git a/tests/DataFormatter/QueryFormatterTest.php b/tests/DataFormatter/QueryFormatterTest.php new file mode 100644 index 000000000..2451f78aa --- /dev/null +++ b/tests/DataFormatter/QueryFormatterTest.php @@ -0,0 +1,49 @@ +checkBindings($bindings); + + $this->assertSame($output, ["some string", "[string,Another ' string,[nested,array]]"]); + } + + public function testItFormatsObjectBindings() + { + $object = new \StdClass; + $object->attribute1 = 'test'; + + $bindings = [ + 'some string', + $object + ]; + + $queryFormatter = new QueryFormatter; + + $output = $queryFormatter->checkBindings($bindings); + + $this->assertSame($output, ['some string', '{"attribute1":"test"}']); + } + +} \ No newline at end of file From ee3313c9dc4e2386c3662984913f53d4236d0418 Mon Sep 17 00:00:00 2001 From: laravel-debugbar Date: Sat, 5 Jun 2021 12:09:56 +0000 Subject: [PATCH 211/585] composer fix-style --- src/DataFormatter/QueryFormatter.php | 1 - tests/DataFormatter/QueryFormatterTest.php | 9 ++++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/DataFormatter/QueryFormatter.php b/src/DataFormatter/QueryFormatter.php index e536f4584..7a94e40f2 100644 --- a/src/DataFormatter/QueryFormatter.php +++ b/src/DataFormatter/QueryFormatter.php @@ -42,7 +42,6 @@ public function checkBindings($bindings) if (is_object($binding)) { $binding = json_encode($binding); } - } return $bindings; diff --git a/tests/DataFormatter/QueryFormatterTest.php b/tests/DataFormatter/QueryFormatterTest.php index 2451f78aa..fbffa0f03 100644 --- a/tests/DataFormatter/QueryFormatterTest.php +++ b/tests/DataFormatter/QueryFormatterTest.php @@ -22,7 +22,7 @@ public function testItFormatsArrayBindings() ], ]; - $queryFormatter = new QueryFormatter; + $queryFormatter = new QueryFormatter(); $output = $queryFormatter->checkBindings($bindings); @@ -31,7 +31,7 @@ public function testItFormatsArrayBindings() public function testItFormatsObjectBindings() { - $object = new \StdClass; + $object = new \StdClass(); $object->attribute1 = 'test'; $bindings = [ @@ -39,11 +39,10 @@ public function testItFormatsObjectBindings() $object ]; - $queryFormatter = new QueryFormatter; + $queryFormatter = new QueryFormatter(); $output = $queryFormatter->checkBindings($bindings); $this->assertSame($output, ['some string', '{"attribute1":"test"}']); } - -} \ No newline at end of file +} From 7904e3b31659ce1cb37bc4a9b0d7b44579456d74 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Sat, 12 Jun 2021 10:09:17 +0200 Subject: [PATCH 212/585] Update FUNDING.yml --- .github/FUNDING.yml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 798753326..41f9ae41c 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,12 +1,5 @@ # These are supported funding model platforms github: barryvdh -patreon: # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -otechie: # Replace with a single Otechie username -custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] +custom: ['https://fruitcake.nl'] + From 70b89754913fd89fef16d0170a91dbc2a5cd633a Mon Sep 17 00:00:00 2001 From: Tadhg Boyle Date: Mon, 14 Jun 2021 07:29:26 -0700 Subject: [PATCH 213/585] Add option to toggle query background on/off (#1196) * Add option to toggle query background on/off * provide Application instance to QueryCollector * Implement requested changes --- config/debugbar.php | 1 + src/DataCollector/QueryCollector.php | 39 ++++++++++++++++++---------- src/LaravelDebugbar.php | 2 ++ 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/config/debugbar.php b/config/debugbar.php index 019280127..e7f6b0999 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -150,6 +150,7 @@ 'backtrace' => true, // Use a backtrace to find the origin of the query in your files. 'backtrace_exclude_paths' => [], // Paths to exclude from backtrace. (in addition to defaults) 'timeline' => false, // Add the queries to the timeline + 'duration_background' => true, // Show shaded background on each query relative to how long it took to execute. 'explain' => [ // Show EXPLAIN output on queries 'enabled' => false, 'types' => ['SELECT'], // Deprecated setting, is always only SELECT diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index aafc6b4c1..f75d6ef63 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -16,6 +16,7 @@ class QueryCollector extends PDOCollector protected $renderSqlWithParams = false; protected $findSource = false; protected $middleware = []; + protected $durationBackground = true; protected $explainQuery = false; protected $explainTypes = ['SELECT']; // ['SELECT', 'INSERT', 'UPDATE', 'DELETE']; for MySQL 5.6.3+ protected $showHints = false; @@ -89,6 +90,16 @@ public function mergeBacktraceExcludePaths(array $excludePaths) $this->backtraceExcludePaths = array_merge($this->backtraceExcludePaths, $excludePaths); } + /** + * Enable/disable the shaded duration background on queries + * + * @param bool $enabled + */ + public function setDurationBackground($enabled) + { + $this->durationBackground = $enabled; + } + /** * Enable/disable the EXPLAIN queries * @@ -493,23 +504,25 @@ public function collect() } } - if ($totalTime > 0) { - // For showing background measure on Queries tab - $start_percent = 0; + if ($this->durationBackground) { + if ($totalTime > 0) { + // For showing background measure on Queries tab + $start_percent = 0; - foreach ($statements as $i => $statement) { - if (! isset($statement['duration'])) { - continue; - } + foreach ($statements as $i => $statement) { + if (!isset($statement['duration'])) { + continue; + } - $width_percent = $statement['duration'] / $totalTime * 100; + $width_percent = $statement['duration'] / $totalTime * 100; - $statements[$i] = array_merge($statement, [ - 'start_percent' => round($start_percent, 3), - 'width_percent' => round($width_percent, 3), - ]); + $statements[$i] = array_merge($statement, [ + 'start_percent' => round($start_percent, 3), + 'width_percent' => round($width_percent, 3), + ]); - $start_percent += $width_percent; + $start_percent += $width_percent; + } } } diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 164587d61..4c69f06c2 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -316,6 +316,8 @@ function ($level, $message = null, $context = null) use ($logger) { $queryCollector->mergeBacktraceExcludePaths($excludePaths); } + $queryCollector->setDurationBackground($this->app['config']->get('debugbar.options.db.duration_background')); + if ($this->app['config']->get('debugbar.options.db.explain.enabled')) { $types = $this->app['config']->get('debugbar.options.db.explain.types'); $queryCollector->setExplainSource(true, $types); From 032b5ffa0833473535e3f00c208c071679c01b8b Mon Sep 17 00:00:00 2001 From: Martin Krisell Date: Mon, 21 Jun 2021 09:15:38 +0200 Subject: [PATCH 214/585] Fix binding with dollar not presented correctly in query (#1199) --- src/DataCollector/QueryCollector.php | 2 +- tests/DataCollector/QueryCollectorTest.php | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index f75d6ef63..898cd0c0c 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -164,7 +164,7 @@ public function addQuery($query, $bindings, $time, $connection) } } - $query = preg_replace($regex, $binding, $query, 1); + $query = preg_replace($regex, addcslashes($binding, '$'), $query, 1); } } diff --git a/tests/DataCollector/QueryCollectorTest.php b/tests/DataCollector/QueryCollectorTest.php index c27dbb338..58ceaabef 100644 --- a/tests/DataCollector/QueryCollectorTest.php +++ b/tests/DataCollector/QueryCollectorTest.php @@ -37,4 +37,25 @@ public function testItReplacesQuestionMarksBindingsCorrectly() }); }); } + + public function testDollarBindingsArePresentedCorrectly() + { + debugbar()->boot(); + + /** @var \Barryvdh\Debugbar\DataCollector\QueryCollector $collector */ + $collector = debugbar()->getCollector('queries'); + $collector->addQuery( + "SELECT a FROM b WHERE c = ? AND d = ? AND e = ?", + ['$10', '$2y$10_DUMMY_BCRYPT_HASH', '$_$$_$$$_$2_$3'], + 0, + $this->app['db']->connection() + ); + + tap(Arr::first($collector->collect()['statements']), function (array $statement) { + $this->assertEquals( + "SELECT a FROM b WHERE c = '$10' AND d = '$2y$10_DUMMY_BCRYPT_HASH' AND e = '\$_$\$_$$\$_$2_$3'", + $statement['sql'] + ); + }); + } } From dc0df2d90ec63921cfa7518a539088dc2d2108d8 Mon Sep 17 00:00:00 2001 From: laravel-debugbar Date: Mon, 21 Jun 2021 07:19:42 +0000 Subject: [PATCH 215/585] composer fix-style --- tests/DataCollector/QueryCollectorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/DataCollector/QueryCollectorTest.php b/tests/DataCollector/QueryCollectorTest.php index 58ceaabef..878fa20d3 100644 --- a/tests/DataCollector/QueryCollectorTest.php +++ b/tests/DataCollector/QueryCollectorTest.php @@ -53,7 +53,7 @@ public function testDollarBindingsArePresentedCorrectly() tap(Arr::first($collector->collect()['statements']), function (array $statement) { $this->assertEquals( - "SELECT a FROM b WHERE c = '$10' AND d = '$2y$10_DUMMY_BCRYPT_HASH' AND e = '\$_$\$_$$\$_$2_$3'", + "SELECT a FROM b WHERE c = '$10' AND d = '$2y$10_DUMMY_BCRYPT_HASH' AND e = '\$_$\$_$$\$_$2_$3'", $statement['sql'] ); }); From a75a2ee197587071ec40058a295810a0b59cef2b Mon Sep 17 00:00:00 2001 From: Jacob Baker-Kretzmar Date: Mon, 23 Aug 2021 12:07:53 -0400 Subject: [PATCH 216/585] Register Telescope route only if Telescope is installed (#1203) --- src/debugbar-routes.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/debugbar-routes.php b/src/debugbar-routes.php index 663b4ddbc..c8c87301f 100644 --- a/src/debugbar-routes.php +++ b/src/debugbar-routes.php @@ -18,10 +18,12 @@ 'as' => 'debugbar.clockwork', ]); - $router->get('telescope/{id}', [ - 'uses' => 'TelescopeController@show', - 'as' => 'debugbar.telescope', - ]); + if (class_exists(\Laravel\Telescope\Telescope::class)) { + $router->get('telescope/{id}', [ + 'uses' => 'TelescopeController@show', + 'as' => 'debugbar.telescope', + ]); + } $router->get('assets/stylesheets', [ 'uses' => 'AssetController@css', From a552b6892743310fe72be2af65cae9406955efd4 Mon Sep 17 00:00:00 2001 From: Ziding Zhang Date: Mon, 13 Sep 2021 18:44:48 +0100 Subject: [PATCH 217/585] Create SECURITY.md (#1227) A simple instruction for security researchers. Closes #1226 --- SECURITY.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..c6138bc29 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,5 @@ +# Security Policy + +## Reporting a Vulnerability + +Please report security issues to `barryvdh@gmail.com` From d3ba4f64e5142ced75d0d8262e2454a75257895a Mon Sep 17 00:00:00 2001 From: Henrique Date: Sun, 19 Sep 2021 05:06:07 +0900 Subject: [PATCH 218/585] Add information about Laravel Octane (#1229) Not including the LaravelDebugbar in the "flush" list results in the debugbar displaying data for older requests. (When using the Swoole driver) I have not tested with Roadrunner but it should avoid any eventual problems there as well. --- readme.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/readme.md b/readme.md index ed4291a9e..ecd61a60b 100644 --- a/readme.md +++ b/readme.md @@ -78,6 +78,16 @@ You can also only display the js or css vendors, by setting it to 'js' or 'css'. php artisan vendor:publish --provider="Barryvdh\Debugbar\ServiceProvider" ``` +### Laravel with Octane: + +Make sure to add LaravelDebugbar to your flush list in `config/octane.php`. + +```php + 'flush' => [ + \Barryvdh\Debugbar\LaravelDebugbar::class, + ], +``` + ### Lumen: For Lumen, register a different Provider in `bootstrap/app.php`: From fc69fd6293e4e9d0f0181bb56219e9b3b3c4dd1f Mon Sep 17 00:00:00 2001 From: Julian Hundeloh Date: Wed, 22 Sep 2021 14:28:00 +0200 Subject: [PATCH 219/585] fix: support explain with pgsql (#1207) * fix: support explain with pgsql * fix: record driver in transactions --- src/DataCollector/QueryCollector.php | 31 +++++++++++++++++++++------- src/LaravelDebugbar.php | 2 +- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 898cd0c0c..c2ee9bffa 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -185,6 +185,7 @@ public function addQuery($query, $bindings, $time, $connection) 'source' => $source, 'explain' => $explainResults, 'connection' => $connection->getDatabaseName(), + 'driver' => $connection->getConfig('driver'), 'hints' => $this->showHints ? $hints : null, 'show_copy' => $this->showCopyButton, ]; @@ -453,6 +454,7 @@ public function collectTransactionEvent($event, $connection) 'source' => $source, 'explain' => [], 'connection' => $connection->getDatabaseName(), + 'driver' => $connection->getConfig('driver'), 'hints' => null, 'show_copy' => false, ]; @@ -493,14 +495,27 @@ public function collect() ]; // Add the results from the explain as new rows - foreach ($query['explain'] as $explain) { - $statements[] = [ - 'sql' => " - EXPLAIN # {$explain->id}: `{$explain->table}` ({$explain->select_type})", - 'type' => 'explain', - 'params' => $explain, - 'row_count' => $explain->rows, - 'stmt_id' => $explain->id, - ]; + if ($query['driver'] === 'pgsql') { + $explainer = trim(implode("\n", array_map(function ($explain) { + return $explain->{'QUERY PLAN'}; + }, $query['explain']))); + + if ($explainer) { + $statements[] = [ + 'sql' => " - EXPLAIN: {$explainer}", + 'type' => 'explain', + ]; + } + } else { + foreach ($query['explain'] as $explain) { + $statements[] = [ + 'sql' => " - EXPLAIN # {$explain->id}: `{$explain->table}` ({$explain->select_type})", + 'type' => 'explain', + 'params' => $explain, + 'row_count' => $explain->rows, + 'stmt_id' => $explain->id, + ]; + } } } diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 4c69f06c2..61e168e7a 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -1137,7 +1137,7 @@ protected function addServerTimingHeaders(Response $response) $headers = []; foreach ($collector->collect()['measures'] as $k => $m) { - $headers[] = sprintf('app;desc="%s";dur=%F', str_replace('"', "'", $m['label']), $m['duration'] * 1000); + $headers[] = sprintf('app;desc="%s";dur=%F', str_replace(array("\n", "\r"), ' ', str_replace('"', "'", $m['label'])), $m['duration'] * 1000); } $response->headers->set('Server-Timing', $headers, false); From 665e514f209eb0e30618d6b25aafc9da38354a64 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 21 Oct 2021 12:56:47 +0200 Subject: [PATCH 220/585] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 2c312022e..595a81a75 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,7 @@ "prefer-stable": true, "extra": { "branch-alias": { - "dev-master": "3.5-dev" + "dev-master": "3.6-dev" }, "laravel": { "providers": [ From 3c2d678269ba60e178bcd93e36f6a91c36b727f1 Mon Sep 17 00:00:00 2001 From: Ahmed shamim Date: Thu, 21 Oct 2021 16:57:31 +0600 Subject: [PATCH 221/585] fix: #1236 maximebf/debugbar version issue (#1237) * fix: #1236 maximebf/debugbar version issue * Update composer.json Co-authored-by: Barry vd. Heuvel --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 595a81a75..fff17ea4b 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ ], "require": { "php": ">=7.2", - "maximebf/debugbar": "^1.16.3", + "maximebf/debugbar": "^1.17.2", "illuminate/routing": "^6|^7|^8", "illuminate/session": "^6|^7|^8", "illuminate/support": "^6|^7|^8", From eee8bcc68c268b03f89765ccc0d9730a8c997fed Mon Sep 17 00:00:00 2001 From: Henrique Date: Thu, 28 Oct 2021 19:13:40 +0900 Subject: [PATCH 222/585] Avoid passing null to functions as it is deprecated in PHP 8.1 (#1249) https://wiki.php.net/rfc/deprecate_null_to_scalar_internal_arg --- src/JavascriptRenderer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/JavascriptRenderer.php b/src/JavascriptRenderer.php index ee30c8e86..56c3a8c02 100644 --- a/src/JavascriptRenderer.php +++ b/src/JavascriptRenderer.php @@ -147,7 +147,7 @@ protected function makeUriRelativeTo($uri, $root) return $uris; } - if (substr($uri, 0, 1) === '/' || preg_match('/^([a-zA-Z]+:\/\/|[a-zA-Z]:\/|[a-zA-Z]:\\\)/', $uri)) { + if (substr($uri ?? '', 0, 1) === '/' || preg_match('/^([a-zA-Z]+:\/\/|[a-zA-Z]:\/|[a-zA-Z]:\\\)/', $uri ?? '')) { return $uri; } return rtrim($root, '/') . "/$uri"; From 62b2749a4737e95d0e4f76823348d60881a6ffcc Mon Sep 17 00:00:00 2001 From: Steven Kemp Date: Thu, 4 Nov 2021 08:30:48 -0400 Subject: [PATCH 223/585] Return the result from the callback in `measure` helper (#1251) --- src/helpers.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/helpers.php b/src/helpers.php index 0cd00444f..69e6e3e61 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -73,9 +73,10 @@ function add_measure($label, $start, $end) * * @param string $label * @param \Closure $closure + * @return mixed */ function measure($label, \Closure $closure) { - debugbar()->measure($label, $closure); + return debugbar()->measure($label, $closure); } } From 7c05586a546092dc977b7685a0dc5a29ee6c6bee Mon Sep 17 00:00:00 2001 From: Andrew Minion Date: Thu, 4 Nov 2021 07:31:23 -0500 Subject: [PATCH 224/585] Feature/ide links full path (#1224) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * use absolute path to file * add the actual change 🤦 --- src/DataCollector/ViewCollector.php | 2 +- tests/DataCollector/ViewCollectorTest.php | 30 +++++++++++++++++++++++ tests/TestCase.php | 6 +++++ tests/resources/views/dashboard.blade.php | 3 +++ 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 tests/DataCollector/ViewCollectorTest.php create mode 100644 tests/resources/views/dashboard.blade.php diff --git a/src/DataCollector/ViewCollector.php b/src/DataCollector/ViewCollector.php index 7af996665..7a1b0c577 100644 --- a/src/DataCollector/ViewCollector.php +++ b/src/DataCollector/ViewCollector.php @@ -89,7 +89,7 @@ public function addView(View $view) ]; if ($this->getXdebugLink($path)) { - $template['xdebug_link'] = $this->getXdebugLink($path); + $template['xdebug_link'] = $this->getXdebugLink(realpath($view->getPath())); } $this->templates[] = $template; diff --git a/tests/DataCollector/ViewCollectorTest.php b/tests/DataCollector/ViewCollectorTest.php new file mode 100644 index 000000000..c4a98b20e --- /dev/null +++ b/tests/DataCollector/ViewCollectorTest.php @@ -0,0 +1,30 @@ +boot(); + + /** @var \Barryvdh\Debugbar\DataCollector\ViewCollector $collector */ + $collector = debugbar()->getCollector('views'); + $collector->addView( + view('dashboard') + ); + + tap(Arr::first($collector->collect()['templates']), function (array $template) { + $this->assertEquals( + 'vscode://file/'.realpath(__DIR__.'/../resources/views/dashboard.blade.php').':1', + $template['xdebug_link']['url'], + ); + }); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index 207bc78f5..00e478eff 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -48,6 +48,7 @@ protected function getEnvironmentSetUp($app) $this->addWebRoutes($router); $this->addApiRoutes($router); + $this->addViewPaths(); } /** @@ -79,4 +80,9 @@ protected function addApiRoutes(Router $router) } ]); } + + protected function addViewPaths() + { + config(['view.paths' => array_merge(config('view.paths'), [__DIR__.'/resources/views'])]); + } } diff --git a/tests/resources/views/dashboard.blade.php b/tests/resources/views/dashboard.blade.php new file mode 100644 index 000000000..ddb34bc29 --- /dev/null +++ b/tests/resources/views/dashboard.blade.php @@ -0,0 +1,3 @@ +
    +

    Basic view

    +
    From 69f44e74f4a73fac35510029727a04f0f081af24 Mon Sep 17 00:00:00 2001 From: Maritaria Date: Thu, 4 Nov 2021 13:31:37 +0100 Subject: [PATCH 225/585] Document `Debugbar::measure` on the facade. (#1217) --- src/Facade.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Facade.php b/src/Facade.php index a4e15b4b1..12b457fad 100644 --- a/src/Facade.php +++ b/src/Facade.php @@ -18,6 +18,7 @@ * @method static void log(mixed $message) * @method static void notice(mixed $message) * @method static void warning(mixed $message) + * @method static mixed measure(string $label, \Closure $closure) * * @see \Barryvdh\Debugbar\LaravelDebugbar */ From 3e72847500b6ca8529b358be6d187ce03a1b0f28 Mon Sep 17 00:00:00 2001 From: Maritaria Date: Thu, 4 Nov 2021 13:32:31 +0100 Subject: [PATCH 226/585] Rename Facade to Debugbar (#1218) Creating the facade as a class named Debugbar enables better auto completion for IDEs and the like. To avoid naming conflicts it exists in a separate folder Facades. For backwards compatibility of existing code the current Facade persists as a class that extends the Debugbar facade class. This should allow for a minor update to the package with a deprecation notice on existing code importing the Facade explicitly. --- composer.json | 2 +- readme.md | 4 ++-- src/Facade.php | 12 ++++-------- src/Facades/Debugbar.php | 33 +++++++++++++++++++++++++++++++++ tests/BrowserTestCase.php | 4 ++-- tests/TestCase.php | 4 ++-- 6 files changed, 44 insertions(+), 15 deletions(-) create mode 100644 src/Facades/Debugbar.php diff --git a/composer.json b/composer.json index fff17ea4b..65bb34dc2 100644 --- a/composer.json +++ b/composer.json @@ -48,7 +48,7 @@ "Barryvdh\\Debugbar\\ServiceProvider" ], "aliases": { - "Debugbar": "Barryvdh\\Debugbar\\Facade" + "Debugbar": "Barryvdh\\Debugbar\\Facades\\Debugbar" } } }, diff --git a/readme.md b/readme.md index ecd61a60b..a87deac8f 100644 --- a/readme.md +++ b/readme.md @@ -38,7 +38,7 @@ And the default collectors: - MemoryCollector - ExceptionsCollector -It also provides a Facade interface for easy logging Messages, Exceptions and Time +It also provides a facade interface (`Debugbar`) for easy logging Messages, Exceptions and Time ## Installation @@ -65,7 +65,7 @@ Barryvdh\Debugbar\ServiceProvider::class, If you want to use the facade to log messages, add this to your facades in app.php: ```php -'Debugbar' => Barryvdh\Debugbar\Facade::class, +'Debugbar' => Barryvdh\Debugbar\Facades\Debugbar::class, ``` The profiler is enabled by default, if you have APP_DEBUG=true. You can override that in the config (`debugbar.enabled`) or by setting `DEBUGBAR_ENABLED` in your `.env`. See more options in `config/debugbar.php` diff --git a/src/Facade.php b/src/Facade.php index 12b457fad..933040d63 100644 --- a/src/Facade.php +++ b/src/Facade.php @@ -20,15 +20,11 @@ * @method static void warning(mixed $message) * @method static mixed measure(string $label, \Closure $closure) * + * @deprecated Renamed to \Barryvdh\Debugbar\Facades\Debugbar + * @see \Barryvdh\Debugbar\Facades\Debugbar + * * @see \Barryvdh\Debugbar\LaravelDebugbar */ -class Facade extends \Illuminate\Support\Facades\Facade +class Facade extends \Barryvdh\Debugbar\Facades\Debugbar { - /** - * {@inheritDoc} - */ - protected static function getFacadeAccessor() - { - return LaravelDebugbar::class; - } } diff --git a/src/Facades/Debugbar.php b/src/Facades/Debugbar.php new file mode 100644 index 000000000..3e7929b9f --- /dev/null +++ b/src/Facades/Debugbar.php @@ -0,0 +1,33 @@ + Facade::class]; + return ['Debugbar' => Debugbar::class]; } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 00e478eff..ceac18d5e 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,7 +2,7 @@ namespace Barryvdh\Debugbar\Tests; -use Barryvdh\Debugbar\Facade; +use Barryvdh\Debugbar\Facades\Debugbar; use Barryvdh\Debugbar\ServiceProvider; use Illuminate\Routing\Router; use Orchestra\Testbench\TestCase as Orchestra; @@ -31,7 +31,7 @@ protected function getPackageProviders($app) */ protected function getPackageAliases($app) { - return ['Debugbar' => Facade::class]; + return ['Debugbar' => Debugbar::class]; } /** From 1dbe9893e809d7f5f9d4f238ef1db2f022c29f7d Mon Sep 17 00:00:00 2001 From: laravel-debugbar Date: Thu, 4 Nov 2021 12:35:25 +0000 Subject: [PATCH 227/585] composer fix-style --- tests/DataCollector/ViewCollectorTest.php | 2 +- tests/TestCase.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/DataCollector/ViewCollectorTest.php b/tests/DataCollector/ViewCollectorTest.php index c4a98b20e..1b3c6a31f 100644 --- a/tests/DataCollector/ViewCollectorTest.php +++ b/tests/DataCollector/ViewCollectorTest.php @@ -22,7 +22,7 @@ public function testIdeLinksAreAbsolutePaths() tap(Arr::first($collector->collect()['templates']), function (array $template) { $this->assertEquals( - 'vscode://file/'.realpath(__DIR__.'/../resources/views/dashboard.blade.php').':1', + 'vscode://file/' . realpath(__DIR__ . '/../resources/views/dashboard.blade.php') . ':1', $template['xdebug_link']['url'], ); }); diff --git a/tests/TestCase.php b/tests/TestCase.php index ceac18d5e..c3335f35f 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -83,6 +83,6 @@ protected function addApiRoutes(Router $router) protected function addViewPaths() { - config(['view.paths' => array_merge(config('view.paths'), [__DIR__.'/resources/views'])]); + config(['view.paths' => array_merge(config('view.paths'), [__DIR__ . '/resources/views'])]); } } From b44c9c4ea799da95a6e68285c3d202a0b370ae76 Mon Sep 17 00:00:00 2001 From: Bennett Date: Thu, 11 Nov 2021 15:49:57 -0600 Subject: [PATCH 228/585] PDO Exception Fix (#1254) Prevent PDO Exception for drivers that don't support quoting (DB2 for IBM iSeries). --- src/DataCollector/QueryCollector.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index c2ee9bffa..30a71d333 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -158,7 +158,10 @@ public function addQuery($query, $bindings, $time, $connection) // Mimic bindValue and only quote non-integer and non-float data types if (!is_int($binding) && !is_float($binding)) { if ($pdo) { - $binding = $pdo->quote($binding); + try{ + $binding = $pdo->quote($binding); + } catch(\Exception $e) { + } } else { $binding = $this->emulateQuote($binding); } From e06041c79c5914057fecff6773bb294eae18fd7e Mon Sep 17 00:00:00 2001 From: laravel-debugbar Date: Thu, 11 Nov 2021 21:51:53 +0000 Subject: [PATCH 229/585] composer fix-style --- src/DataCollector/QueryCollector.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 30a71d333..94ff9d4ff 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -158,9 +158,9 @@ public function addQuery($query, $bindings, $time, $connection) // Mimic bindValue and only quote non-integer and non-float data types if (!is_int($binding) && !is_float($binding)) { if ($pdo) { - try{ + try { $binding = $pdo->quote($binding); - } catch(\Exception $e) { + } catch (\Exception $e) { } } else { $binding = $this->emulateQuote($binding); From 0f12adcffff17e4012763b6655d93882c194d3a7 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Fri, 12 Nov 2021 09:12:57 +0100 Subject: [PATCH 230/585] Check test for xdebug (#1256) * Update ViewCollectorTest.php * Update ViewCollectorTest.php --- tests/DataCollector/ViewCollectorTest.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/DataCollector/ViewCollectorTest.php b/tests/DataCollector/ViewCollectorTest.php index 1b3c6a31f..676738470 100644 --- a/tests/DataCollector/ViewCollectorTest.php +++ b/tests/DataCollector/ViewCollectorTest.php @@ -12,6 +12,13 @@ class ViewCollectorTest extends TestCase public function testIdeLinksAreAbsolutePaths() { + if (!ini_get('xdebug.file_link_format')) { + $this->markTestSkipped( + 'The Xdebug extension is not available.' + ); + return; + } + debugbar()->boot(); /** @var \Barryvdh\Debugbar\DataCollector\ViewCollector $collector */ @@ -23,7 +30,7 @@ public function testIdeLinksAreAbsolutePaths() tap(Arr::first($collector->collect()['templates']), function (array $template) { $this->assertEquals( 'vscode://file/' . realpath(__DIR__ . '/../resources/views/dashboard.blade.php') . ':1', - $template['xdebug_link']['url'], + $template['xdebug_link']['url'] ); }); } From 9853edfc6c980d7af7264f1acf6146b292636470 Mon Sep 17 00:00:00 2001 From: laravel-debugbar Date: Fri, 12 Nov 2021 08:13:49 +0000 Subject: [PATCH 231/585] composer fix-style --- tests/DataCollector/ViewCollectorTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/DataCollector/ViewCollectorTest.php b/tests/DataCollector/ViewCollectorTest.php index 676738470..bea31f9aa 100644 --- a/tests/DataCollector/ViewCollectorTest.php +++ b/tests/DataCollector/ViewCollectorTest.php @@ -12,13 +12,13 @@ class ViewCollectorTest extends TestCase public function testIdeLinksAreAbsolutePaths() { - if (!ini_get('xdebug.file_link_format')) { + if (!ini_get('xdebug.file_link_format')) { $this->markTestSkipped( - 'The Xdebug extension is not available.' + 'The Xdebug extension is not available.' ); return; } - + debugbar()->boot(); /** @var \Barryvdh\Debugbar\DataCollector\ViewCollector $collector */ From 1ede251f9f58c226f33f6acc9da34855dd4eb17c Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 18 Nov 2021 15:19:47 +0200 Subject: [PATCH 232/585] Add support editor to use when clicking file name. (#1258) Co-authored-by: Viktor Mazur --- config/debugbar.php | 41 +++++++++++++++ src/DataCollector/RouteCollector.php | 74 +++++++++++++++++++++++++++- 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/config/debugbar.php b/config/debugbar.php index e7f6b0999..bcfc11fe0 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -42,6 +42,47 @@ 'port' => 2304, // Port to use with the "socket" driver ], + /* + |-------------------------------------------------------------------------- + | Editor + |-------------------------------------------------------------------------- + | + | Choose your preferred editor to use when clicking file name. + | + | Supported: "phpstorm", "vscode", "vscode-insiders", "vscodium", "textmate", "emacs", + | "sublime", "atom", "nova", "macvim", "idea", "netbeans", + | "xdebug", "espresso" + | + */ + + 'editor' => env('DEBUGBAR_EDITOR', 'phpstorm'), + + /* + |-------------------------------------------------------------------------- + | Remote Path Mapping + |-------------------------------------------------------------------------- + | + | If you are using a remote dev server, like Laravel Homestead, Docker, or + | even a remote VPS, it will be necessary to specify your path mapping. + | + | Leaving one, or both of these, empty or null will not trigger the remote + | URL changes and Debugbar will treat your editor links as local files. + | + | "remote_sites_path" is an absolute base path for your sites or projects + | in Homestead, Vagrant, Docker, or another remote development server. + | + | Example value: "/home/vagrant/Code" + | + | "local_sites_path" is an absolute base path for your sites or projects + | on your local computer where your IDE or code editor is running on. + | + | Example values: "/Users//Code", "C:\Users\\Documents\Code" + | + */ + + 'remote_sites_path' => env('DEBUGBAR_REMOTE_SITES_PATH', ''), + 'local_sites_path' => env('DEBUGBAR_LOCAL_SITES_PATH', ''), + /* |-------------------------------------------------------------------------- | Vendors diff --git a/src/DataCollector/RouteCollector.php b/src/DataCollector/RouteCollector.php index 9e17b8fb3..5e947e188 100644 --- a/src/DataCollector/RouteCollector.php +++ b/src/DataCollector/RouteCollector.php @@ -9,6 +9,7 @@ use Illuminate\Routing\Route; use Illuminate\Routing\Router; use Illuminate\Support\Facades\Config; +use InvalidArgumentException; /** * Based on Illuminate\Foundation\Console\RoutesCommand for Taylor Otwell @@ -24,6 +25,28 @@ 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', + '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; @@ -76,7 +99,12 @@ protected function getRouteInformation($route) if (isset($reflector)) { $filename = ltrim(str_replace(base_path(), '', $reflector->getFileName()), '/'); - $result['file'] = $filename . ':' . $reflector->getStartLine() . '-' . $reflector->getEndLine(); + + if ($href = $this->getEditorHref($reflector->getFileName(), $reflector->getStartLine())) { + $result['file'] = sprintf('
    %s:%s-%s', $href, $filename, $reflector->getStartLine(), $reflector->getEndLine()); + } else { + $result['file'] = sprintf('%s:%s-%s', $filename, $reflector->getStartLine(), $reflector->getEndLine()); + } } if ($middleware = $this->getMiddleware($route)) { @@ -117,7 +145,7 @@ public function getWidgets() $widgets = [ "route" => [ "icon" => "share", - "widget" => "PhpDebugBar.Widgets.VariableListWidget", + "widget" => "PhpDebugBar.Widgets.HtmlVariableListWidget", "map" => "route", "default" => "{}" ] @@ -145,4 +173,46 @@ 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); + } } From 8a74874093bdd98e48fe2e2d1eb4c52cab23a4ab Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Fri, 3 Dec 2021 17:13:03 +0100 Subject: [PATCH 233/585] Tweak vendor paths --- src/DataCollector/QueryCollector.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 94ff9d4ff..0690ca3d6 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -23,9 +23,10 @@ class QueryCollector extends PDOCollector protected $showCopyButton = false; protected $reflection = []; protected $backtraceExcludePaths = [ - '/vendor/laravel/framework/src/Illuminate/Support/HigherOrderTapProxy', + '/vendor/laravel/framework/src/Illuminate/Support', '/vendor/laravel/framework/src/Illuminate/Database', '/vendor/laravel/framework/src/Illuminate/Events', + '/vendor/october/rain', '/vendor/barryvdh/laravel-debugbar', ]; From 14105aa63bd86165119bbfe3b6fe5372b3120f0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6hler?= Date: Sun, 12 Dec 2021 22:12:37 +0100 Subject: [PATCH 234/585] Update Debugbar.php (#1267) corrected the namespace for LaravelDebugbar in Barryvdh\Debugbar\Facades\Debugbar. This was causing the error "Target class [Barryvdh\Debugbar\Facades\LaravelDebugbar] does not exist" --- src/Facades/Debugbar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Facades/Debugbar.php b/src/Facades/Debugbar.php index 3e7929b9f..b4003579e 100644 --- a/src/Facades/Debugbar.php +++ b/src/Facades/Debugbar.php @@ -28,6 +28,6 @@ class Debugbar extends \Illuminate\Support\Facades\Facade */ protected static function getFacadeAccessor() { - return LaravelDebugbar::class; + return \Barryvdh\Debugbar\LaravelDebugbar::class; } } From 043a81b3feeb607c543a27cc5a8a24158fde6a7d Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Tue, 14 Dec 2021 15:32:48 +0100 Subject: [PATCH 235/585] Update callgraph color in darkmode --- src/Resources/laravel-debugbar-dark-mode.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Resources/laravel-debugbar-dark-mode.css b/src/Resources/laravel-debugbar-dark-mode.css index 4c55466cd..69baff9b9 100644 --- a/src/Resources/laravel-debugbar-dark-mode.css +++ b/src/Resources/laravel-debugbar-dark-mode.css @@ -130,6 +130,7 @@ div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-i div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-row-count, div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-copy-clipboard, div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-stmt-id, +div.phpdebugbar .phpdebugbar-widgets-callgraph pre, div.phpdebugbar .phpdebugbar-text-muted, div.phpdebugbar-openhandler .phpdebugbar-text-muted { From 5e97575db42dcc6322365a5d2b43a3ad3c40987e Mon Sep 17 00:00:00 2001 From: laravel-debugbar Date: Tue, 14 Dec 2021 14:33:43 +0000 Subject: [PATCH 236/585] composer fix-style --- src/Controllers/OpenHandlerController.php | 1 - src/Controllers/TelescopeController.php | 1 - src/DataFormatter/QueryFormatter.php | 1 - src/ServiceProvider.php | 1 - src/Storage/SocketStorage.php | 1 - src/Support/Clockwork/Converter.php | 1 - tests/DebugbarBrowserTest.php | 1 - tests/Models/Person.php | 1 - tests/TestCase.php | 1 - 9 files changed, 9 deletions(-) diff --git a/src/Controllers/OpenHandlerController.php b/src/Controllers/OpenHandlerController.php index 02cc3c61d..e5c059fc3 100644 --- a/src/Controllers/OpenHandlerController.php +++ b/src/Controllers/OpenHandlerController.php @@ -9,7 +9,6 @@ class OpenHandlerController extends BaseController { - public function handle(Request $request) { $openHandler = new OpenHandler($this->debugbar); diff --git a/src/Controllers/TelescopeController.php b/src/Controllers/TelescopeController.php index dcc4d2f6c..8f263b4c9 100644 --- a/src/Controllers/TelescopeController.php +++ b/src/Controllers/TelescopeController.php @@ -12,7 +12,6 @@ class TelescopeController extends BaseController { - public function show(EntriesRepository $storage, $uuid) { diff --git a/src/DataFormatter/QueryFormatter.php b/src/DataFormatter/QueryFormatter.php index 7a94e40f2..e7c1696b9 100644 --- a/src/DataFormatter/QueryFormatter.php +++ b/src/DataFormatter/QueryFormatter.php @@ -6,7 +6,6 @@ class QueryFormatter extends DataFormatter { - /** * Removes extra spaces at the beginning and end of the SQL query and its lines. * diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 08e2107c2..c7cc68b65 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -70,7 +70,6 @@ function (EngineResolver $resolver, Application $application): EngineResolver { } return new class ($resolver, $laravelDebugbar) extends EngineResolver { - private $laravelDebugbar; public function __construct(EngineResolver $resolver, LaravelDebugbar $laravelDebugbar) diff --git a/src/Storage/SocketStorage.php b/src/Storage/SocketStorage.php index 6b242b2f7..23f8b4809 100644 --- a/src/Storage/SocketStorage.php +++ b/src/Storage/SocketStorage.php @@ -6,7 +6,6 @@ class SocketStorage implements StorageInterface { - protected $hostname; protected $port; protected $socket; diff --git a/src/Support/Clockwork/Converter.php b/src/Support/Clockwork/Converter.php index 79dfb695a..dbd4c05ff 100644 --- a/src/Support/Clockwork/Converter.php +++ b/src/Support/Clockwork/Converter.php @@ -4,7 +4,6 @@ class Converter { - /** * Convert the phpdebugbar data to Clockwork format. * diff --git a/tests/DebugbarBrowserTest.php b/tests/DebugbarBrowserTest.php index b23c0423e..6619cfe32 100644 --- a/tests/DebugbarBrowserTest.php +++ b/tests/DebugbarBrowserTest.php @@ -6,7 +6,6 @@ class DebugbarBrowserTest extends BrowserTestCase { - /** * Define environment setup. * diff --git a/tests/Models/Person.php b/tests/Models/Person.php index 34a409ec3..05aa72206 100644 --- a/tests/Models/Person.php +++ b/tests/Models/Person.php @@ -4,5 +4,4 @@ class Person extends User { - } diff --git a/tests/TestCase.php b/tests/TestCase.php index c3335f35f..161e91660 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -9,7 +9,6 @@ class TestCase extends Orchestra { - /** * Get package providers. * From 80b9040c61f3772433d6e588d44325770b1bbb62 Mon Sep 17 00:00:00 2001 From: Kris Date: Tue, 14 Dec 2021 15:43:41 +0100 Subject: [PATCH 237/585] The type attribute is unnecessary for JavaScript resources. (#1265) --- src/JavascriptRenderer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/JavascriptRenderer.php b/src/JavascriptRenderer.php index 56c3a8c02..0cf3983c4 100644 --- a/src/JavascriptRenderer.php +++ b/src/JavascriptRenderer.php @@ -64,10 +64,10 @@ public function renderHead() $jsRoute = preg_replace('/\Ahttps?:/', '', $jsRoute); $html = ""; - $html .= ""; + $html .= ""; if ($this->isJqueryNoConflictEnabled()) { - $html .= '' . "\n"; + $html .= '' . "\n"; } $html .= $this->getInlineHtml(); From 33bf3c13096da1d81810f89a7827f3a197847a39 Mon Sep 17 00:00:00 2001 From: Amir Hossein <68776630+amirHossein5@users.noreply.github.com> Date: Tue, 14 Dec 2021 18:13:50 +0330 Subject: [PATCH 238/585] Fix direction rtl for children of debugbar div (#1263) * Fix rtl direction for children of debugbar div * fix space after * --- src/Resources/laravel-debugbar.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index 62170efa3..fd704c880 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -11,6 +11,11 @@ div.phpdebugbar { z-index: 6000000; } +div.phpdebugbar * { + direction: ltr; + text-align: left; +} + div.phpdebugbar-openhandler-overlay { z-index: 6000001; cursor: pointer; From ccf109f8755dcc7e58779d1aeb1051b04e0b4bef Mon Sep 17 00:00:00 2001 From: "Daniel S. Deboer" Date: Tue, 14 Dec 2021 09:45:18 -0500 Subject: [PATCH 239/585] [fix] Emulate quote bindings for drivers that don't support quoting (#1219) Co-authored-by: Barry vd. Heuvel --- src/DataCollector/QueryCollector.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 0690ca3d6..17c3f69eb 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -162,6 +162,7 @@ public function addQuery($query, $bindings, $time, $connection) try { $binding = $pdo->quote($binding); } catch (\Exception $e) { + $binding = $this->emulateQuote($binding); } } else { $binding = $this->emulateQuote($binding); From 81b130f4fb27eae9fe450c264bdd2bdad11a775d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Nikolaou?= Date: Sat, 18 Dec 2021 16:58:20 +0200 Subject: [PATCH 240/585] Add editor support for vscode remote links (#1268) --- config/debugbar.php | 3 ++- src/DataCollector/RouteCollector.php | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/config/debugbar.php b/config/debugbar.php index bcfc11fe0..fe3b192d7 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -49,7 +49,8 @@ | | Choose your preferred editor to use when clicking file name. | - | Supported: "phpstorm", "vscode", "vscode-insiders", "vscodium", "textmate", "emacs", + | Supported: "phpstorm", "vscode", "vscode-insiders", "vscode-remote", + | "vscode-insiders-remote", "vscodium", "textmate", "emacs", | "sublime", "atom", "nova", "macvim", "idea", "netbeans", | "xdebug", "espresso" | diff --git a/src/DataCollector/RouteCollector.php b/src/DataCollector/RouteCollector.php index 5e947e188..a255302f3 100644 --- a/src/DataCollector/RouteCollector.php +++ b/src/DataCollector/RouteCollector.php @@ -39,6 +39,8 @@ class RouteCollector extends DataCollector implements Renderable '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', From f92fe967b40b36ad1ee8ed2fd59c05ae67a1ebba Mon Sep 17 00:00:00 2001 From: Armin Linzbauer Date: Tue, 21 Dec 2021 19:20:10 +0100 Subject: [PATCH 241/585] Update Dependencies for Laravel 9 (#1271) --- composer.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 65bb34dc2..4eb9180e1 100644 --- a/composer.json +++ b/composer.json @@ -12,11 +12,11 @@ "require": { "php": ">=7.2", "maximebf/debugbar": "^1.17.2", - "illuminate/routing": "^6|^7|^8", - "illuminate/session": "^6|^7|^8", - "illuminate/support": "^6|^7|^8", - "symfony/debug": "^4.3|^5", - "symfony/finder": "^4.3|^5" + "illuminate/routing": "^6|^7|^8|^9", + "illuminate/session": "^6|^7|^8|^9", + "illuminate/support": "^6|^7|^8|^9", + "symfony/debug": "^4.3|^5|^6", + "symfony/finder": "^4.3|^5|^6" }, "require-dev": { "mockery/mockery": "^1.3.3", From 0548fa6a563e0e80f4ee8a17b93ecc8602e56cde Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Sun, 30 Jan 2022 13:42:06 +0100 Subject: [PATCH 242/585] Update readme.md --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index a87deac8f..54eaafba1 100644 --- a/readme.md +++ b/readme.md @@ -3,6 +3,7 @@ [![Packagist License](https://poser.pugx.org/barryvdh/laravel-debugbar/license.png)](http://choosealicense.com/licenses/mit/) [![Latest Stable Version](https://poser.pugx.org/barryvdh/laravel-debugbar/version.png)](https://packagist.org/packages/barryvdh/laravel-debugbar) [![Total Downloads](https://poser.pugx.org/barryvdh/laravel-debugbar/d/total.png)](https://packagist.org/packages/barryvdh/laravel-debugbar) +[![Fruitcake](https://img.shields.io/badge/Powered%20By-Fruitcake-b2bc35.svg)](https://fruitcake.nl/) This is a package to integrate [PHP Debug Bar](http://phpdebugbar.com/) with Laravel. It includes a ServiceProvider to register the debugbar and attach it to the output. You can publish assets and configure it through Laravel. From 4ad282a9af271bd0647e0353eb5aed2e8520773e Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Sun, 30 Jan 2022 18:37:50 +0100 Subject: [PATCH 243/585] Test Laravel 9 (#1282) --- .github/workflows/run-tests.yml | 8 +++++++- composer.json | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index a18e01f92..bc6d932a8 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -20,11 +20,17 @@ jobs: strategy: matrix: php: [8.0, 7.4, 7.3, 7.2] - laravel: [8.*, 6.*, 7.*] + laravel: [9.*, 8.*, 6.*, 7.*] dependency-version: [prefer-lowest, prefer-stable] exclude: - laravel: 8.* php: 7.2 + - laravel: 9.* + php: 7.2 + - laravel: 9.* + php: 7.3 + - laravel: 9.* + php: 7.4 name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} diff --git a/composer.json b/composer.json index 4eb9180e1..c632bc244 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ }, "require-dev": { "mockery/mockery": "^1.3.3", - "orchestra/testbench-dusk": "^4|^5|^6", + "orchestra/testbench-dusk": "^4|^5|^6|^7", "phpunit/phpunit": "^8.5|^9.0", "squizlabs/php_codesniffer": "^3.5" }, From c90b0f7520d4d5fcc17dede1fbd5b989a5b5960c Mon Sep 17 00:00:00 2001 From: Faissal Wahabali Date: Tue, 8 Feb 2022 13:11:35 +0100 Subject: [PATCH 244/585] fix sql overflow (#1285) --- src/Resources/laravel-debugbar.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index fd704c880..424c6ad49 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -552,6 +552,7 @@ ul.phpdebugbar-widgets-timeline li:hover { ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-sql { flex: 1; margin-right: 5px; + max-width: 100%; } ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-duration { From bafb3903c0ab8590b192e256ca564f0846b689e0 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Tue, 8 Feb 2022 13:14:02 +0100 Subject: [PATCH 245/585] Force value as string Fixes #1284 --- src/DataCollector/QueryCollector.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 17c3f69eb..fbeaf81b5 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -160,7 +160,7 @@ public function addQuery($query, $bindings, $time, $connection) if (!is_int($binding) && !is_float($binding)) { if ($pdo) { try { - $binding = $pdo->quote($binding); + $binding = $pdo->quote((string) $binding); } catch (\Exception $e) { $binding = $this->emulateQuote($binding); } @@ -211,7 +211,7 @@ protected function emulateQuote($value) $search = ["\\", "\x00", "\n", "\r", "'", '"', "\x1a"]; $replace = ["\\\\","\\0","\\n", "\\r", "\'", '\"', "\\Z"]; - return "'" . str_replace($search, $replace, $value) . "'"; + return "'" . str_replace($search, $replace, (string) $value) . "'"; } /** From b96f9820aaf1ff9afe945207883149e1c7afb298 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Wed, 9 Feb 2022 08:52:32 +0100 Subject: [PATCH 246/585] Skip Mailer in Laravel 9 Fixes #1289 --- src/LaravelDebugbar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 61e168e7a..1842aa5de 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -450,7 +450,7 @@ function ($event, $params) use ($queryCollector) { } } - if ($this->shouldCollect('mail', true) && class_exists('Illuminate\Mail\MailServiceProvider')) { + if ($this->shouldCollect('mail', true) && class_exists('Illuminate\Mail\MailServiceProvider') && $this->checkVersion('9.0', '<')) { try { $mailer = $this->app['mailer']->getSwiftMailer(); $this->addCollector(new SwiftMailCollector($mailer)); From 29d7d90678f968d279eb083fccfea1824132c976 Mon Sep 17 00:00:00 2001 From: Jakub Dibala Date: Thu, 24 Feb 2022 20:28:35 +0100 Subject: [PATCH 247/585] Fix htmlentities PHP warning (#1291) * Fix htmlentities PHP warning * Update QueryFormatter.php Co-authored-by: Barry vd. Heuvel --- src/DataFormatter/QueryFormatter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DataFormatter/QueryFormatter.php b/src/DataFormatter/QueryFormatter.php index e7c1696b9..f8574f5d6 100644 --- a/src/DataFormatter/QueryFormatter.php +++ b/src/DataFormatter/QueryFormatter.php @@ -55,7 +55,7 @@ public function checkBindings($bindings) public function escapeBindings($bindings) { foreach ($bindings as &$binding) { - $binding = htmlentities($binding, ENT_QUOTES, 'UTF-8', false); + $binding = htmlentities((string) $binding, ENT_QUOTES, 'UTF-8', false); } return $bindings; From b2d39072d094ccd060e27125352985d77aa60dc0 Mon Sep 17 00:00:00 2001 From: Sevan Nerse Date: Tue, 15 Mar 2022 20:31:06 +0300 Subject: [PATCH 248/585] fix style (#1266) --- src/ServiceProvider.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index c7cc68b65..10a5a7cfc 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -34,7 +34,7 @@ public function register() ); $this->app->singleton(LaravelDebugbar::class, function ($app) { - $debugbar = new LaravelDebugbar($app); + $debugbar = new LaravelDebugbar($app); if ($app->bound(SessionManager::class)) { $sessionManager = $app->make(SessionManager::class); @@ -42,7 +42,7 @@ public function register() $debugbar->setHttpDriver($httpDriver); } - return $debugbar; + return $debugbar; }); $this->app->alias(LaravelDebugbar::class, 'debugbar'); From e7c3f56e54258d5e2939e68d453ce9db0c086406 Mon Sep 17 00:00:00 2001 From: Alexandre D'Eschambeault Date: Wed, 4 May 2022 07:45:10 -0400 Subject: [PATCH 249/585] fix: load routes and commands in the service provider's boot method (#1197) --- src/ServiceProvider.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 10a5a7cfc..11ea596e7 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -26,8 +26,6 @@ public function register() $configPath = __DIR__ . '/../config/debugbar.php'; $this->mergeConfigFrom($configPath, 'debugbar'); - $this->loadRoutesFrom(realpath(__DIR__ . '/debugbar-routes.php')); - $this->app->alias( DataFormatter::class, DataFormatterInterface::class @@ -90,8 +88,6 @@ public function register($engine, \Closure $resolver) } ); - $this->commands(['command.debugbar.clear']); - Collection::macro('debug', function () { debug($this); return $this; @@ -108,7 +104,13 @@ public function boot() $configPath = __DIR__ . '/../config/debugbar.php'; $this->publishes([$configPath => $this->getConfigPath()], 'config'); + $this->loadRoutesFrom(realpath(__DIR__ . '/debugbar-routes.php')); + $this->registerMiddleware(InjectDebugbar::class); + + if ($this->app->runningInConsole()) { + $this->commands(['command.debugbar.clear']); + } } /** From 814b36a08a60f4159cdcbb1c466a6a0027440b6c Mon Sep 17 00:00:00 2001 From: TinaH Date: Wed, 8 Jun 2022 17:03:05 +0200 Subject: [PATCH 250/585] Turbolinks compatibility (#1320) --- src/JavascriptRenderer.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/JavascriptRenderer.php b/src/JavascriptRenderer.php index 0cf3983c4..428b15a9d 100644 --- a/src/JavascriptRenderer.php +++ b/src/JavascriptRenderer.php @@ -63,11 +63,11 @@ public function renderHead() $cssRoute = preg_replace('/\Ahttps?:/', '', $cssRoute); $jsRoute = preg_replace('/\Ahttps?:/', '', $jsRoute); - $html = ""; - $html .= ""; + $html = ""; + $html .= ""; if ($this->isJqueryNoConflictEnabled()) { - $html .= '' . "\n"; + $html .= '' . "\n"; } $html .= $this->getInlineHtml(); From 3372ed65e6d2039d663ed19aa699956f9d346271 Mon Sep 17 00:00:00 2001 From: Julius Kiekbusch Date: Mon, 11 Jul 2022 11:26:42 +0200 Subject: [PATCH 251/585] Remove symfony/debug dependency (#1321) * Migrate from symfony/debug to symfony/error-handler * Fix wrong version constraints * Remove Laravel 6 Support * Remove lumen 6 from test matrix * Remove symfony/error-handler --- .github/workflows/run-integration-tests.yml | 6 ++---- .github/workflows/run-tests.yml | 2 +- composer.json | 13 ++++++------- src/Middleware/InjectDebugbar.php | 12 ++++-------- 4 files changed, 13 insertions(+), 20 deletions(-) diff --git a/.github/workflows/run-integration-tests.yml b/.github/workflows/run-integration-tests.yml index fb2edd370..56759976d 100644 --- a/.github/workflows/run-integration-tests.yml +++ b/.github/workflows/run-integration-tests.yml @@ -19,14 +19,12 @@ jobs: strategy: matrix: php: [8.0, 7.4, 7.3, 7.2] - lumen: [8.*, 7.*, 6.*] + lumen: [8.*, 7.*] exclude: - lumen: 8.* php: 7.2 - lumen: 7.* php: 8.0 - - lumen: 6.* - php: 8.0 name: P${{ matrix.php }} - Lumen${{ matrix.lumen }} steps: - name: Checkout code @@ -69,7 +67,7 @@ jobs: strategy: matrix: php: [8.0, 7.4, 7.3, 7.2] - laravel: [8.*, 7.*, 6.*] + laravel: [8.*, 7.*] exclude: - laravel: 8.* php: 7.2 diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index bc6d932a8..6f10c965b 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -20,7 +20,7 @@ jobs: strategy: matrix: php: [8.0, 7.4, 7.3, 7.2] - laravel: [9.*, 8.*, 6.*, 7.*] + laravel: [9.*, 8.*, 7.*] dependency-version: [prefer-lowest, prefer-stable] exclude: - laravel: 8.* diff --git a/composer.json b/composer.json index c632bc244..b911a5e3d 100644 --- a/composer.json +++ b/composer.json @@ -10,17 +10,16 @@ } ], "require": { - "php": ">=7.2", + "php": ">=7.2.5", "maximebf/debugbar": "^1.17.2", - "illuminate/routing": "^6|^7|^8|^9", - "illuminate/session": "^6|^7|^8|^9", - "illuminate/support": "^6|^7|^8|^9", - "symfony/debug": "^4.3|^5|^6", - "symfony/finder": "^4.3|^5|^6" + "illuminate/routing": "^7|^8|^9", + "illuminate/session": "^7|^8|^9", + "illuminate/support": "^7|^8|^9", + "symfony/finder": "^5|^6" }, "require-dev": { "mockery/mockery": "^1.3.3", - "orchestra/testbench-dusk": "^4|^5|^6|^7", + "orchestra/testbench-dusk": "^5|^6|^7", "phpunit/phpunit": "^8.5|^9.0", "squizlabs/php_codesniffer": "^3.5" }, diff --git a/src/Middleware/InjectDebugbar.php b/src/Middleware/InjectDebugbar.php index 2b4e3e158..48be02d8a 100644 --- a/src/Middleware/InjectDebugbar.php +++ b/src/Middleware/InjectDebugbar.php @@ -2,14 +2,13 @@ namespace Barryvdh\Debugbar\Middleware; -use Error; use Closure; use Exception; use Illuminate\Http\Request; use Barryvdh\Debugbar\LaravelDebugbar; use Illuminate\Contracts\Container\Container; use Illuminate\Contracts\Debug\ExceptionHandler; -use Symfony\Component\Debug\Exception\FatalThrowableError; +use Throwable; class InjectDebugbar { @@ -65,10 +64,7 @@ public function handle($request, Closure $next) try { /** @var \Illuminate\Http\Response $response */ $response = $next($request); - } catch (Exception $e) { - $response = $this->handleException($request, $e); - } catch (Error $error) { - $e = new FatalThrowableError($error); + } catch (Throwable $e) { $response = $this->handleException($request, $e); } @@ -84,11 +80,11 @@ public function handle($request, Closure $next) * (Copy from Illuminate\Routing\Pipeline by Taylor Otwell) * * @param $passable - * @param Exception $e + * @param Throwable $e * @return mixed * @throws Exception */ - protected function handleException($passable, Exception $e) + protected function handleException($passable, $e) { if (! $this->container->bound(ExceptionHandler::class) || ! $passable instanceof Request) { throw $e; From 119f0bcaafaba4e00e9a3215fa57b7b36c10447b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20K=C3=BCndig?= <8600029+tobias-kuendig@users.noreply.github.com> Date: Wed, 3 Aug 2022 14:39:53 +0200 Subject: [PATCH 252/585] Implemented support for Twig 3 (#1318) * Implemented support for Twig 3 * Fixed v2 namespace * Use the same notation everywhere * Removed import * Use clearer naming * Fixed another Twig_Token param * Fixed StopwatchTokenParser * Fixed namespaces * Renamed variable * Refactored Twig compatibility to common base classes * Refactor + Stopwatch fix --- src/Twig/Extension/Debug.php | 15 ++++++--- src/Twig/Extension/Dump.php | 18 +++++++---- src/Twig/Extension/Extension.php | 10 ++++++ src/Twig/Extension/Stopwatch.php | 3 +- src/Twig/Node/Node.php | 10 ++++++ src/Twig/Node/StopwatchNode.php | 29 +++++++++++++---- src/Twig/TokenParser/StopwatchTokenParser.php | 32 +++++++++++++++---- src/Twig/TokenParser/TokenParser.php | 10 ++++++ 8 files changed, 102 insertions(+), 25 deletions(-) create mode 100644 src/Twig/Extension/Extension.php create mode 100644 src/Twig/Node/Node.php create mode 100644 src/Twig/TokenParser/TokenParser.php diff --git a/src/Twig/Extension/Debug.php b/src/Twig/Extension/Debug.php index 42bc2cb32..a70b69a4f 100644 --- a/src/Twig/Extension/Debug.php +++ b/src/Twig/Extension/Debug.php @@ -10,7 +10,7 @@ /** * Access Laravels auth class in your Twig templates. */ -class Debug extends Twig_Extension +class Debug extends Extension { /** * @var \Barryvdh\Debugbar\LaravelDebugbar @@ -44,8 +44,15 @@ public function getName() */ public function getFunctions() { + // Maintain compatibility with Twig 2 and 3. + $simpleFunction = 'Twig_SimpleFunction'; + + if (!class_exists($simpleFunction)) { + $simpleFunction = '\Twig\TwigFunction'; + } + return [ - new Twig_SimpleFunction( + new $simpleFunction( 'debug', [$this, 'debug'], ['needs_context' => true, 'needs_environment' => true] @@ -57,10 +64,10 @@ public function getFunctions() * Based on Twig_Extension_Debug / twig_var_dump * (c) 2011 Fabien Potencier * - * @param Twig_Environment $env + * @param \Twig_Environment|\Twig\Environment $env * @param $context */ - public function debug(Twig_Environment $env, $context) + public function debug($env, $context) { if (!$env->isDebug() || !$this->debugbar) { return; diff --git a/src/Twig/Extension/Dump.php b/src/Twig/Extension/Dump.php index b42fafa42..edd17ba03 100644 --- a/src/Twig/Extension/Dump.php +++ b/src/Twig/Extension/Dump.php @@ -3,14 +3,11 @@ namespace Barryvdh\Debugbar\Twig\Extension; use DebugBar\DataFormatter\DataFormatterInterface; -use Twig_Environment; -use Twig_Extension; -use Twig_SimpleFunction; /** * Dump variables using the DataFormatter */ -class Dump extends Twig_Extension +class Dump extends Extension { /** * @var \DebugBar\DataFormatter\DataFormatter @@ -40,8 +37,15 @@ public function getName() */ public function getFunctions() { + // Maintain compatibility with Twig 2 and 3. + $simpleFunction = '\Twig_SimpleFunction'; + + if (!class_exists($simpleFunction)) { + $simpleFunction = '\Twig\TwigFunction'; + } + return [ - new Twig_SimpleFunction( + new $simpleFunction( 'dump', [$this, 'dump'], ['is_safe' => ['html'], 'needs_context' => true, 'needs_environment' => true] @@ -53,12 +57,12 @@ public function getFunctions() * Based on Twig_Extension_Debug / twig_var_dump * (c) 2011 Fabien Potencier * - * @param Twig_Environment $env + * @param \Twig_Environment|\Twig\Environment $env * @param $context * * @return string */ - public function dump(Twig_Environment $env, $context) + public function dump($env, $context) { $output = ''; diff --git a/src/Twig/Extension/Extension.php b/src/Twig/Extension/Extension.php new file mode 100644 index 000000000..282ecf03b --- /dev/null +++ b/src/Twig/Extension/Extension.php @@ -0,0 +1,10 @@ + */ -class StopwatchNode extends \Twig_Node +class StopwatchNode extends Node { + /** + * @param \Twig_NodeInterface|\Twig\Node\Node $name + * @param $body + * @param \Twig_Node_Expression_AssignName|\Twig\Node\Expression\AssignNameExpression $var + * @param $lineno + * @param $tag + */ public function __construct( - \Twig_NodeInterface $name, + $name, $body, - \Twig_Node_Expression_AssignName $var, + $var, $lineno = 0, $tag = null ) { parent::__construct(['body' => $body, 'name' => $name, 'var' => $var], [], $lineno, $tag); } - public function compile(\Twig_Compiler $compiler) + /** + * @param \Twig_Compiler|\Twig\Compiler $env + * @return void + */ + public function compile($compiler) { + // Maintain compatibility with Twig 2 and 3. + $extension = \Barryvdh\Debugbar\Twig\Extension\Stopwatch::class; + if (class_exists('\Twig_Node')) { + $extension = 'stopwatch'; + } + $compiler ->addDebugInfo($this) ->write('') @@ -28,11 +45,11 @@ public function compile(\Twig_Compiler $compiler) ->raw(' = ') ->subcompile($this->getNode('name')) ->write(";\n") - ->write("\$this->env->getExtension('stopwatch')->getDebugbar()->startMeasure(") + ->write(sprintf("\$this->env->getExtension('%s')->getDebugbar()->startMeasure(", $extension)) ->subcompile($this->getNode('var')) ->raw(");\n") ->subcompile($this->getNode('body')) - ->write("\$this->env->getExtension('stopwatch')->getDebugbar()->stopMeasure(") + ->write(sprintf("\$this->env->getExtension('%s')->getDebugbar()->stopMeasure(", $extension)) ->subcompile($this->getNode('var')) ->raw(");\n"); } diff --git a/src/Twig/TokenParser/StopwatchTokenParser.php b/src/Twig/TokenParser/StopwatchTokenParser.php index cb197a208..896af4992 100644 --- a/src/Twig/TokenParser/StopwatchTokenParser.php +++ b/src/Twig/TokenParser/StopwatchTokenParser.php @@ -9,7 +9,7 @@ * * @author Wouter J */ -class StopwatchTokenParser extends \Twig_TokenParser +class StopwatchTokenParser extends TokenParser { protected $debugbarAvailable; @@ -18,7 +18,10 @@ public function __construct($debugbarAvailable) $this->debugbarAvailable = $debugbarAvailable; } - public function parse(\Twig_Token $token) + /** + * @param \Twig_Token|\Twig\Token $token + */ + public function parse($token) { $lineno = $token->getLine(); $stream = $this->parser->getStream(); @@ -26,17 +29,31 @@ public function parse(\Twig_Token $token) // {% stopwatch 'bar' %} $name = $this->parser->getExpressionParser()->parseExpression(); - $stream->expect(\Twig_Token::BLOCK_END_TYPE); + // Maintain compatibility with Twig 2 and 3. + if (class_exists("\Twig_Token")) { + $blockEndType = \Twig_Token::BLOCK_END_TYPE; + } else { + $blockEndType = \Twig\Token::BLOCK_END_TYPE; + } + + $stream->expect($blockEndType); // {% endstopwatch %} $body = $this->parser->subparse([$this, 'decideStopwatchEnd'], true); - $stream->expect(\Twig_Token::BLOCK_END_TYPE); + $stream->expect($blockEndType); + + // Maintain compatibility with Twig 2 and 3. + if (class_exists("\Twig_Node_Expression_AssignName")) { + $assignNameExpression = new \Twig_Node_Expression_AssignName($this->parser->getVarName(), $token->getLine()); + } else { + $assignNameExpression = new \Twig\Node\Expression\AssignNameExpression($this->parser->getVarName(), $token->getLine()); + } if ($this->debugbarAvailable) { return new StopwatchNode( $name, $body, - new \Twig_Node_Expression_AssignName($this->parser->getVarName(), $token->getLine()), + $assignNameExpression, $lineno, $this->getTag() ); @@ -50,7 +67,10 @@ public function getTag() return 'stopwatch'; } - public function decideStopwatchEnd(\Twig_Token $token) + /** + * @param \Twig_Token|\Twig\Token $token + */ + public function decideStopwatchEnd($token) { return $token->test('endstopwatch'); } diff --git a/src/Twig/TokenParser/TokenParser.php b/src/Twig/TokenParser/TokenParser.php new file mode 100644 index 000000000..4a0b6d1cb --- /dev/null +++ b/src/Twig/TokenParser/TokenParser.php @@ -0,0 +1,10 @@ + Date: Wed, 3 Aug 2022 14:42:59 +0200 Subject: [PATCH 253/585] Fix PHP 8.2 deprecation (#1322) * Remove deprecated dynamic name property on ViewCollector * Remove unused import on ViewCollector --- src/DataCollector/ViewCollector.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/DataCollector/ViewCollector.php b/src/DataCollector/ViewCollector.php index 7a1b0c577..e72097d8f 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 Symfony\Component\VarDumper\Cloner\VarCloner; class ViewCollector extends TwigCollector { @@ -21,7 +20,6 @@ public function __construct($collectData = true) { $this->setDataFormatter(new SimpleFormatter()); $this->collect_data = $collectData; - $this->name = 'views'; $this->templates = []; } From ef8f2fa23f20c0d1899a7a03b6ce7034ad708a5e Mon Sep 17 00:00:00 2001 From: pataar Date: Sat, 15 Oct 2022 00:05:55 +0200 Subject: [PATCH 254/585] style: fix the code style, and update the workflow (#1352) * style: fix the code style * add style permission * fix the workflow --- .github/workflows/fix-cs.yml | 9 ++++++--- src/Twig/Extension/Extension.php | 8 ++++++-- src/Twig/Node/Node.php | 8 ++++++-- src/Twig/TokenParser/TokenParser.php | 8 ++++++-- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/.github/workflows/fix-cs.yml b/.github/workflows/fix-cs.yml index c92491dd9..3b6b49063 100644 --- a/.github/workflows/fix-cs.yml +++ b/.github/workflows/fix-cs.yml @@ -1,10 +1,13 @@ -name: Fix Codestyle +name: Fix Code Style on: push: branches: - master +permissions: + contents: write + jobs: fix-style: name: Fix Code Style @@ -20,7 +23,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 7.4 + php-version: 8.1 coverage: none tools: composer:v2 @@ -31,7 +34,7 @@ jobs: - run: composer fix-style continue-on-error: true - # Revert modifications so they don't get commited 💥 + # Revert modifications so they don't get committed 💥 - run: git checkout -- composer.json - uses: stefanzweifel/git-auto-commit-action@v4 diff --git a/src/Twig/Extension/Extension.php b/src/Twig/Extension/Extension.php index 282ecf03b..dda2a6171 100644 --- a/src/Twig/Extension/Extension.php +++ b/src/Twig/Extension/Extension.php @@ -4,7 +4,11 @@ // Maintain compatibility with Twig 2 and 3. if (class_exists('\Twig_Extension')) { - abstract class Extension extends \Twig_Extension {} + abstract class Extension extends \Twig_Extension + { + } } else { - abstract class Extension extends \Twig\Extension\AbstractExtension {} + abstract class Extension extends \Twig\Extension\AbstractExtension + { + } } diff --git a/src/Twig/Node/Node.php b/src/Twig/Node/Node.php index bf81c1b9e..f35ea7b7a 100644 --- a/src/Twig/Node/Node.php +++ b/src/Twig/Node/Node.php @@ -4,7 +4,11 @@ // Maintain compatibility with Twig 2 and 3. if (class_exists('\Twig_Node')) { - abstract class Node extends \Twig_Node {} + abstract class Node extends \Twig_Node + { + } } else { - abstract class Node extends \Twig\Node\Node {} + abstract class Node extends \Twig\Node\Node + { + } } diff --git a/src/Twig/TokenParser/TokenParser.php b/src/Twig/TokenParser/TokenParser.php index 4a0b6d1cb..2ff179628 100644 --- a/src/Twig/TokenParser/TokenParser.php +++ b/src/Twig/TokenParser/TokenParser.php @@ -4,7 +4,11 @@ // Maintain compatibility with Twig 2 and 3. if (class_exists('\Twig_TokenParser')) { - abstract class TokenParser extends \Twig_TokenParser {} + abstract class TokenParser extends \Twig_TokenParser + { + } } else { - abstract class TokenParser extends \Twig\TokenParser\AbstractTokenParser {} + abstract class TokenParser extends \Twig\TokenParser\AbstractTokenParser + { + } } From 430c52b3fe73d9aa3a6736eab57dfdb45d6d9648 Mon Sep 17 00:00:00 2001 From: pataar Date: Sat, 15 Oct 2022 00:06:45 +0200 Subject: [PATCH 255/585] fix(exception): use the correct typing for the 'addThrowable' method (#1351) --- src/LaravelDebugbar.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 1842aa5de..b3fedc39f 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -34,6 +34,7 @@ use DebugBar\Storage\PdoStorage; use DebugBar\Storage\RedisStorage; use Exception; +use Throwable; use Illuminate\Contracts\Foundation\Application; use Illuminate\Session\SessionManager; use Illuminate\Support\Str; @@ -635,7 +636,7 @@ public function addException(Exception $e) /** * Adds an exception to be profiled in the debug bar * - * @param Exception $e + * @param Throwable $e */ public function addThrowable($e) { From 74d71878f7dd79fa0b9a744f293201cfcea34851 Mon Sep 17 00:00:00 2001 From: Sergiy Petrov Date: Mon, 31 Oct 2022 15:06:00 +0200 Subject: [PATCH 256/585] Update run-tests.yml (#3) (#1357) test against php 8.1 --- .github/workflows/run-integration-tests.yml | 24 +++++++++++++++++---- .github/workflows/run-tests.yml | 4 +++- composer.json | 8 +++---- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/.github/workflows/run-integration-tests.yml b/.github/workflows/run-integration-tests.yml index 56759976d..81ad84f4e 100644 --- a/.github/workflows/run-integration-tests.yml +++ b/.github/workflows/run-integration-tests.yml @@ -18,13 +18,21 @@ jobs: COMPOSER_NO_INTERACTION: 1 strategy: matrix: - php: [8.0, 7.4, 7.3, 7.2] - lumen: [8.*, 7.*] + php: [8.1, 8.0, 7.4, 7.3, 7.2] + lumen: [9.*, 8.*, 7.*] exclude: - lumen: 8.* php: 7.2 + - lumen: 9.* + php: 7.2 + - lumen: 9.* + php: 7.3 + - lumen: 9.* + php: 7.4 - lumen: 7.* php: 8.0 + - lumen: 7.* + php: 8.1 name: P${{ matrix.php }} - Lumen${{ matrix.lumen }} steps: - name: Checkout code @@ -66,11 +74,19 @@ jobs: COMPOSER_NO_INTERACTION: 1 strategy: matrix: - php: [8.0, 7.4, 7.3, 7.2] - laravel: [8.*, 7.*] + php: [8.1, 8.0, 7.4, 7.3, 7.2] + laravel: [9.*, 8.*, 7.*] exclude: - laravel: 8.* php: 7.2 + - laravel: 9.* + php: 7.2 + - laravel: 9.* + php: 7.3 + - laravel: 9.* + php: 7.4 + - laravel: 7.* + php: 8.1 name: P${{ matrix.php }} - Laravel${{ matrix.laravel }} steps: - name: Checkout code diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 6f10c965b..fea6a5a0d 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -19,7 +19,7 @@ jobs: strategy: matrix: - php: [8.0, 7.4, 7.3, 7.2] + php: [8.1, 8.0, 7.4, 7.3, 7.2] laravel: [9.*, 8.*, 7.*] dependency-version: [prefer-lowest, prefer-stable] exclude: @@ -31,6 +31,8 @@ jobs: php: 7.3 - laravel: 9.* php: 7.4 + - laravel: 7.* + php: 8.1 name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} diff --git a/composer.json b/composer.json index b911a5e3d..c5030ae3d 100644 --- a/composer.json +++ b/composer.json @@ -12,15 +12,15 @@ "require": { "php": ">=7.2.5", "maximebf/debugbar": "^1.17.2", - "illuminate/routing": "^7|^8|^9", - "illuminate/session": "^7|^8|^9", - "illuminate/support": "^7|^8|^9", + "illuminate/routing": "^7|^8.67|^9", + "illuminate/session": "^7|^8.67|^9", + "illuminate/support": "^7|^8.67|^9", "symfony/finder": "^5|^6" }, "require-dev": { "mockery/mockery": "^1.3.3", "orchestra/testbench-dusk": "^5|^6|^7", - "phpunit/phpunit": "^8.5|^9.0", + "phpunit/phpunit": "^8.5.30|^9.0", "squizlabs/php_codesniffer": "^3.5" }, "autoload": { From 44f66c9266c9fe89a0ffe3c95537f27548bff856 Mon Sep 17 00:00:00 2001 From: erikn69 Date: Mon, 31 Oct 2022 08:57:06 -0500 Subject: [PATCH 257/585] PHP 8.2 Build (#1353) * PHP 8.2 Build * Update run-tests.yml * Update run-tests.yml Co-authored-by: Barry vd. Heuvel --- .github/workflows/run-tests.yml | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index fea6a5a0d..12a8f811e 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -19,19 +19,23 @@ jobs: strategy: matrix: - php: [8.1, 8.0, 7.4, 7.3, 7.2] - laravel: [9.*, 8.*, 7.*] + php: [8.2, 8.1, 8.0, 7.4, 7.3, 7.2] + laravel: [^9, ^8, ^7] dependency-version: [prefer-lowest, prefer-stable] exclude: - - laravel: 8.* + - laravel: ^7* + php: 8.1 + - laravel: ^7 + php: 8.2 + - laravel: ^8 php: 7.2 - - laravel: 9.* + - laravel: ^9 php: 7.2 - - laravel: 9.* + - laravel: ^9 php: 7.3 - - laravel: 9.* + - laravel: ^9 php: 7.4 - - laravel: 7.* + - laravel: ^7 php: 8.1 name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} @@ -50,7 +54,7 @@ jobs: - name: Install dependencies run: | - composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update + composer require "laravel/framework:${{ matrix.laravel }}" "nesbot/carbon:>=2.62.1" --no-interaction --no-update composer update --${{ matrix.dependency-version }} --prefer-dist --no-progress - name: Update Dusk Chromedriver From 9bb3381172922b2fb8eaf3170079a440415d313c Mon Sep 17 00:00:00 2001 From: pafernandez-oesia <96843912+pafernandez-oesia@users.noreply.github.com> Date: Mon, 31 Oct 2022 15:50:44 +0100 Subject: [PATCH 258/585] Fix dark mode styles #1123 (#1334) * Fix #1123 Add selector for ListWidget styles in dark mode. * Fix label style for ListWidget --- src/Resources/laravel-debugbar-dark-mode.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Resources/laravel-debugbar-dark-mode.css b/src/Resources/laravel-debugbar-dark-mode.css index 69baff9b9..50f7832d2 100644 --- a/src/Resources/laravel-debugbar-dark-mode.css +++ b/src/Resources/laravel-debugbar-dark-mode.css @@ -24,6 +24,8 @@ div.phpdebugbar-openhandler, div.phpdebugbar div.phpdebugbar-header > div > *, div.phpdebugbar ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label, div.phpdebugbar ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-collector, +div.phpdebugbar ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item, +div.phpdebugbar ul.phpdebugbar-widgets-list li span.phpdebugbar-widgets-label, div.phpdebugbar code.phpdebugbar-widgets-sql span.hljs-keyword, div.phpdebugbar-openhandler .phpdebugbar-openhandler-header, div.phpdebugbar-openhandler .phpdebugbar-openhandler-header a { From 615ce4732d0d5c828bd060d1e2dc9b65f7369509 Mon Sep 17 00:00:00 2001 From: Mike Wink <85063626+mikewink@users.noreply.github.com> Date: Mon, 31 Oct 2022 15:51:34 +0100 Subject: [PATCH 259/585] Update Querycollector with SQLite Explain support (#1315) This adds support for SQLite EXPLAIN statement. There is no support for EXPLAIN QUERY PLAN yet, as this would require more changes to the package config. --- src/DataCollector/QueryCollector.php | 35 ++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index fbeaf81b5..f11695c65 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -511,6 +511,41 @@ public function collect() 'type' => 'explain', ]; } + } elseif ($query['driver'] === 'sqlite') { + $vmi = ''; + $vmi .= " + + + + + + + + + "; + + foreach ($query['explain'] as $explain) { + $vmi .= " + + + + + + + + + "; + } + + $vmi .= '
    AddressOpcodeP1P2P3P4P5Comment
    {$explain->addr}{$explain->opcode}{$explain->p1}{$explain->p2}{$explain->p3}{$explain->p4}{$explain->p5}{$explain->comment}
    '; + + $statements[] = [ + 'sql' => " - EXPLAIN:", + 'type' => 'explain', + 'params' => [ + 'Virtual Machine Instructions' => $vmi, + ] + ]; } else { foreach ($query['explain'] as $explain) { $statements[] = [ From bfdfbafb87fb4aae273a95a67c905aed29ca8c50 Mon Sep 17 00:00:00 2001 From: Lee Taylor <48020564+leettaylor@users.noreply.github.com> Date: Mon, 31 Oct 2022 14:52:28 +0000 Subject: [PATCH 260/585] Add config option to exclude paths from ViewCollector (#1327) * Add views.exclude_paths config option * Pass through excludePaths to the ViewCollector * Exclude files matching paths from view collector * Use strpos for check --- config/debugbar.php | 1 + src/DataCollector/ViewCollector.php | 11 ++++++++++- src/LaravelDebugbar.php | 3 ++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/config/debugbar.php b/config/debugbar.php index fe3b192d7..137b2b080 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -206,6 +206,7 @@ 'views' => [ 'timeline' => false, // Add the views to the timeline (Experimental) 'data' => false, //Note: Can slow down the application, because the data can be quite large.. + 'exclude_paths' => [], // Add the paths which you don't want to appear in the views ], 'route' => [ 'label' => true, // show complete route on bar diff --git a/src/DataCollector/ViewCollector.php b/src/DataCollector/ViewCollector.php index e72097d8f..e21b90a45 100644 --- a/src/DataCollector/ViewCollector.php +++ b/src/DataCollector/ViewCollector.php @@ -10,17 +10,20 @@ class ViewCollector extends TwigCollector { protected $templates = []; protected $collect_data; + protected $exclude_paths; /** * Create a ViewCollector * * @param bool $collectData Collects view data when tru + * @param string[] $excludePaths Paths to exclude from collection */ - public function __construct($collectData = true) + public function __construct($collectData = true, $excludePaths = []) { $this->setDataFormatter(new SimpleFormatter()); $this->collect_data = $collectData; $this->templates = []; + $this->exclude_paths = $excludePaths; } public function getName() @@ -69,6 +72,12 @@ public function addView(View $view) $path = ''; } + foreach ($this->exclude_paths as $excludePath) { + if (strpos($path, $excludePath) !== false) { + return; + } + } + if (!$this->collect_data) { $params = array_keys($view->getData()); } else { diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index b3fedc39f..f44040769 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -205,7 +205,8 @@ function () use ($debugbar, $startTime) { if ($this->shouldCollect('views', true) && isset($this->app['events'])) { try { $collectData = $this->app['config']->get('debugbar.options.views.data', true); - $this->addCollector(new ViewCollector($collectData)); + $excludePaths = $this->app['config']->get('debugbar.options.views.exclude_paths', []); + $this->addCollector(new ViewCollector($collectData, $excludePaths)); $this->app['events']->listen( 'composing:*', function ($view, $data = []) use ($debugbar) { From 06782a09cb288dd6cf306f70c237e804c0962a16 Mon Sep 17 00:00:00 2001 From: Dave James Miller Date: Mon, 31 Oct 2022 14:55:20 +0000 Subject: [PATCH 261/585] Delete changelog.md (#1302) --- changelog.md | 86 ---------------------------------------------------- 1 file changed, 86 deletions(-) delete mode 100644 changelog.md diff --git a/changelog.md b/changelog.md deleted file mode 100644 index 072b56141..000000000 --- a/changelog.md +++ /dev/null @@ -1,86 +0,0 @@ -# Changelog for Laravel Debugbar - -## 1.8.4 (2014-10-31) - -- Add Redis/PDO storage options - -## 1.8.3 (2014-11-23) - -- Base EventCollector on TimeData Collector - -## 1.8.2 (2014-11-18) - -- Use XHR handler instead of jQuery handler - -## 1.8.1 (2014-11-14) - -- Fix compatability with Symfony 2.3 (Laravel 4.) - -## 1.8.0 (2014-10-31) - -- Fix L5 compatability -- add hints + explain options to QueryLogger -- update to Debugbar 1.10.x -- new ViewCollector layout with more information - -## 1.7.7 (2014-09-15) - -- Make it compatible with Laravel 5.0-dev -- Allow anonymous function as `enabled` setting (for IP checks etc) -- Escape query bindings, to prevent executing of scripts/html - -## 1.7.6 (2014-09-12) - -- Fix reflash bug -- Fix caching of debugbar assets - -## 1.7.5 (2014-09-12) - -- Reflash data for all debugbar requests - -## 1.7.4 (2014-09-08) - -- Rename assets routes to prevent Nginx conflicts - -## 1.7.3 (2014-09-05) - -- Add helper functions (debug(), add/start/stop_measure() and measure() -- Collect data on responses that are not redirect/ajax/html also. - -## 1.7.2 (2014-09-04) - -- Fix 4.0 compatibility (problem with Controller namespace) -- Give deprecation notice instead of publishing assets. - -## 1.7.1 (2014-09-03) - -- Deprecated `debugbar:publish` command in favor of AssetController -- Fixed issue with detecting absolute paths in Windows - -## 1.7.0 (2014-09-03) - -- Use AssetController instead of publishing assets to the public folder. -- Inline fonts + images to base64 Data-URI -- Use PSR-4 file structure - -## 1.6.8 (2014-08-27) - -- Change OpenHandler layout -- Add backtrace option for query origin - -## 1.6.7 (2014-08-09) - -- Add Twig extensions for better integration with rcrowe/TwigBridge - -## 1.6.6 (2014-07-08) - -- Check if Requests wantsJSON instead of only isXmlHttpRequest -- Make sure closure for timing is run, even when disabled - -## 1.6.5 (2014-06-24) - -- Add Laravel style - -## 1.6.4 (2014-06-15) - -- Work on non-UTF-8 handling \ No newline at end of file From ea7695f3949aba7e731b0f982ad6ac42d65c5377 Mon Sep 17 00:00:00 2001 From: Faraz Samapoor Date: Mon, 31 Oct 2022 18:29:31 +0330 Subject: [PATCH 262/585] Removes repeated "and" from config file. (#1358) --- config/debugbar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/debugbar.php b/config/debugbar.php index 137b2b080..4581a9dce 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -92,7 +92,7 @@ | Vendor files are included by default, but can be set to false. | This can also be set to 'js' or 'css', to only include javascript or css vendor files. | Vendor files are for css: font-awesome (including fonts) and highlight.js (css files) - | and for js: jquery and and highlight.js + | and for js: jquery and highlight.js | So if you want syntax highlighting, set it to true. | jQuery is set to not conflict with existing jQuery scripts. | From 7a7315f99c69181d9db4030117850e9aa655ebd4 Mon Sep 17 00:00:00 2001 From: Ferdous Anam <40851904+ferdousanam@users.noreply.github.com> Date: Mon, 31 Oct 2022 21:00:07 +0600 Subject: [PATCH 263/585] View Editor Link (#1359) --- src/DataCollector/ViewCollector.php | 70 ++++++++++++++++++++- src/JavascriptRenderer.php | 1 + src/Resources/templates/widget.js | 98 +++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 src/Resources/templates/widget.js diff --git a/src/DataCollector/ViewCollector.php b/src/DataCollector/ViewCollector.php index e21b90a45..ef9ac3d8f 100644 --- a/src/DataCollector/ViewCollector.php +++ b/src/DataCollector/ViewCollector.php @@ -5,6 +5,7 @@ use Barryvdh\Debugbar\DataFormatter\SimpleFormatter; use DebugBar\Bridge\Twig\TwigCollector; use Illuminate\View\View; +use InvalidArgumentException; class ViewCollector extends TwigCollector { @@ -12,6 +13,30 @@ 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 * @@ -36,7 +61,7 @@ public function getWidgets() return [ 'views' => [ 'icon' => 'leaf', - 'widget' => 'PhpDebugBar.Widgets.TemplatesWidget', + 'widget' => 'PhpDebugBar.Widgets.LaravelViewTemplatesWidget', 'map' => 'views', 'default' => '[]' ], @@ -47,6 +72,36 @@ 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 * @@ -93,6 +148,7 @@ public function addView(View $view) 'param_count' => count($params), 'params' => $params, 'type' => $type, + 'editorLink' => $this->getEditorHref($view->getPath(), 0), ]; if ($this->getXdebugLink($path)) { @@ -111,4 +167,16 @@ 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/JavascriptRenderer.php b/src/JavascriptRenderer.php index 428b15a9d..9c967909e 100644 --- a/src/JavascriptRenderer.php +++ b/src/JavascriptRenderer.php @@ -23,6 +23,7 @@ public function __construct(DebugBar $debugBar, $baseUrl = null, $basePath = nul $this->cssVendors['fontawesome'] = __DIR__ . '/Resources/vendor/font-awesome/style.css'; $this->jsFiles['laravel-sql'] = __DIR__ . '/Resources/sqlqueries/widget.js'; $this->jsFiles['laravel-cache'] = __DIR__ . '/Resources/cache/widget.js'; + $this->jsFiles['laravel-view'] = __DIR__ . '/Resources/templates/widget.js'; $theme = config('debugbar.theme', 'auto'); switch ($theme) { diff --git a/src/Resources/templates/widget.js b/src/Resources/templates/widget.js new file mode 100644 index 000000000..bb39188b5 --- /dev/null +++ b/src/Resources/templates/widget.js @@ -0,0 +1,98 @@ +(function($) { + + var csscls = PhpDebugBar.utils.makecsscls('phpdebugbar-widgets-'); + + /** + * Widget for the displaying templates data + * + * Options: + * - data + */ + var TemplatesWidget = PhpDebugBar.Widgets.LaravelViewTemplatesWidget = PhpDebugBar.Widget.extend({ + + className: csscls('templates'), + + render: function() { + this.$status = $('
    ').addClass(csscls('status')).appendTo(this.$el); + + this.$list = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function(li, tpl) { + $('').addClass(csscls('name')).text(tpl.name).appendTo(li); + + if (typeof tpl.editorLink !== 'undefined' && tpl.editorLink !== null) { + $('') + .addClass(csscls('editor-link')) + .on('click', function (event) { + event.stopPropagation(); + }) + .appendTo(li); + } + if (typeof tpl.xdebug_link !== 'undefined' && tpl.xdebug_link !== null) { + if (tpl.xdebug_link.ajax) { + $('').on('click', function () { + $.ajax(tpl.xdebug_link.url); + }).addClass(csscls('editor-link')).appendTo(li); + } else { + $('').addClass(csscls('editor-link')).appendTo(li); + } + } + if (tpl.render_time_str) { + $('').addClass(csscls('render-time')).text(tpl.render_time_str).appendTo(li); + } + if (tpl.memory_str) { + $('').addClass(csscls('memory')).text(tpl.memory_str).appendTo(li); + } + if (typeof(tpl.param_count) != 'undefined') { + $('').addClass(csscls('param-count')).text(tpl.param_count).appendTo(li); + } + if (typeof(tpl.type) != 'undefined' && tpl.type) { + $('').addClass(csscls('type')).text(tpl.type).appendTo(li); + } + if (tpl.params && !$.isEmptyObject(tpl.params)) { + var table = $('
    Params
    ').addClass(csscls('params')).appendTo(li); + for (var key in tpl.params) { + if (typeof tpl.params[key] !== 'function') { + table.append('' + key + '
    ' + tpl.params[key] + '
    '); + } + } + li.css('cursor', 'pointer').click(function() { + if (table.is(':visible')) { + table.hide(); + } else { + table.show(); + } + }); + } + }}); + this.$list.$el.appendTo(this.$el); + this.$callgraph = $('
    ').addClass(csscls('callgraph')).appendTo(this.$el); + + this.bindAttr('data', function(data) { + this.$list.set('data', data.templates); + this.$status.empty(); + this.$callgraph.empty(); + + var sentence = data.sentence || "templates were rendered"; + $('').text(data.nb_templates + " " + sentence).appendTo(this.$status); + + if (data.accumulated_render_time_str) { + this.$status.append($('').addClass(csscls('render-time')).text(data.accumulated_render_time_str)); + } + if (data.memory_usage_str) { + this.$status.append($('').addClass(csscls('memory')).text(data.memory_usage_str)); + } + if (data.nb_blocks > 0) { + $('
    ').text(data.nb_blocks + " blocks were rendered").appendTo(this.$status); + } + if (data.nb_macros > 0) { + $('
    ').text(data.nb_macros + " macros were rendered").appendTo(this.$status); + } + if (typeof data.callgraph !== 'undefined') { + this.$callgraph.html(data.callgraph); + } + }); + } + + }); + +})(PhpDebugBar.$); From 7590abc024d9b6108ba9e69e29b94f4679b5f283 Mon Sep 17 00:00:00 2001 From: Aldo Karendra Date: Tue, 10 Jan 2023 18:09:53 +0700 Subject: [PATCH 264/585] fix: deprecated dynamic property on PHP 8.2 (#1370) * fix: deprecated dynamic property on PHP 8.2 Fixing deprecated on PHP 8.2 : - Creation of dynamic property Barryvdh\Debugbar\DataFormatter\QueryFormatter::$cloner is deprecated Reference : https://php.watch/versions/8.2/dynamic-properties-deprecated * fix: deprecated dynamic property on PHP 8.2 Fixing deprecated on PHP 8.2 : - Creation of dynamic property Barryvdh\Debugbar\DataFormatter\SimpleFormatter::$cloner is deprecated Reference : https://php.watch/versions/8.2/dynamic-properties-deprecated * fix: deprecated dynamic property on PHP 8.2 --- src/DataFormatter/QueryFormatter.php | 1 + src/DataFormatter/SimpleFormatter.php | 1 + 2 files changed, 2 insertions(+) diff --git a/src/DataFormatter/QueryFormatter.php b/src/DataFormatter/QueryFormatter.php index f8574f5d6..dc49853e6 100644 --- a/src/DataFormatter/QueryFormatter.php +++ b/src/DataFormatter/QueryFormatter.php @@ -4,6 +4,7 @@ use DebugBar\DataFormatter\DataFormatter; +#[\AllowDynamicProperties] class QueryFormatter extends DataFormatter { /** diff --git a/src/DataFormatter/SimpleFormatter.php b/src/DataFormatter/SimpleFormatter.php index 9825e9133..369e1be3f 100644 --- a/src/DataFormatter/SimpleFormatter.php +++ b/src/DataFormatter/SimpleFormatter.php @@ -9,6 +9,7 @@ * * @see https://github.com/symfony/symfony/blob/v3.4.4/src/Symfony/Component/HttpKernel/DataCollector/Util/ValueExporter.php */ +#[\AllowDynamicProperties] class SimpleFormatter extends DataFormatter { /** From 47f5852dba2ae586509ea305acec935d9001a971 Mon Sep 17 00:00:00 2001 From: erikn69 Date: Thu, 12 Jan 2023 10:13:32 -0500 Subject: [PATCH 265/585] Laravel 10.x Support (#1380) --- .github/workflows/run-tests.yml | 14 ++++++++++---- composer.json | 8 ++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 12a8f811e..48111fe4f 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -20,10 +20,10 @@ jobs: strategy: matrix: php: [8.2, 8.1, 8.0, 7.4, 7.3, 7.2] - laravel: [^9, ^8, ^7] + laravel: [^10, ^9, ^8, ^7] dependency-version: [prefer-lowest, prefer-stable] exclude: - - laravel: ^7* + - laravel: ^7 php: 8.1 - laravel: ^7 php: 8.2 @@ -35,8 +35,14 @@ jobs: php: 7.3 - laravel: ^9 php: 7.4 - - laravel: ^7 - php: 8.1 + - laravel: ^10 + php: 7.2 + - laravel: ^10 + php: 7.3 + - laravel: ^10 + php: 7.4 + - laravel: ^10 + php: 8.0 name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} diff --git a/composer.json b/composer.json index c5030ae3d..5e626ee15 100644 --- a/composer.json +++ b/composer.json @@ -12,14 +12,14 @@ "require": { "php": ">=7.2.5", "maximebf/debugbar": "^1.17.2", - "illuminate/routing": "^7|^8.67|^9", - "illuminate/session": "^7|^8.67|^9", - "illuminate/support": "^7|^8.67|^9", + "illuminate/routing": "^7|^8.67|^9|^10", + "illuminate/session": "^7|^8.67|^9|^10", + "illuminate/support": "^7|^8.67|^9|^10", "symfony/finder": "^5|^6" }, "require-dev": { "mockery/mockery": "^1.3.3", - "orchestra/testbench-dusk": "^5|^6|^7", + "orchestra/testbench-dusk": "^5|^6|^7|^8", "phpunit/phpunit": "^8.5.30|^9.0", "squizlabs/php_codesniffer": "^3.5" }, From 6e922688caabc831be67b0b237fd3b1f3928d5dd Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Wed, 1 Feb 2023 17:22:49 +0100 Subject: [PATCH 266/585] Debugbar for Laravel --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 54eaafba1..5bac5ccd9 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -## Laravel Debugbar +## Debugbar for Laravel ![Unit Tests](https://github.com/barryvdh/laravel-debugbar/workflows/Unit%20Tests/badge.svg) [![Packagist License](https://poser.pugx.org/barryvdh/laravel-debugbar/license.png)](http://choosealicense.com/licenses/mit/) [![Latest Stable Version](https://poser.pugx.org/barryvdh/laravel-debugbar/version.png)](https://packagist.org/packages/barryvdh/laravel-debugbar) From a67e03c63dce7d512ccd98a4e6031c7af47e88d3 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Sat, 4 Feb 2023 16:30:33 +0100 Subject: [PATCH 267/585] Add editorconfig, fix style, skip resources (#1385) * Add editorconfig, fix style, skip resources * Minimum dev --- .editorconfig | 12 ++++++++++++ .github/workflows/run-integration-tests.yml | 2 ++ composer.json | 4 ++-- src/DataCollector/QueryCollector.php | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..2e7acaf5d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false \ No newline at end of file diff --git a/.github/workflows/run-integration-tests.yml b/.github/workflows/run-integration-tests.yml index 81ad84f4e..b881bef14 100644 --- a/.github/workflows/run-integration-tests.yml +++ b/.github/workflows/run-integration-tests.yml @@ -51,6 +51,7 @@ jobs: run: | composer create-project --prefer-dist laravel/lumen:${{ matrix.lumen }} --no-progress sample cd sample + composer config minimum-stability dev composer update --prefer-stable --prefer-dist --no-progress - name: Add package from source run: | @@ -105,6 +106,7 @@ jobs: run: | composer create-project --prefer-dist laravel/laravel:${{ matrix.laravel }} --no-progress sample cd sample + composer config minimum-stability dev composer update --prefer-stable --prefer-dist --no-progress - name: Add package from source run: | diff --git a/composer.json b/composer.json index 5e626ee15..5313e1db4 100644 --- a/composer.json +++ b/composer.json @@ -52,8 +52,8 @@ } }, "scripts": { - "check-style": "phpcs -p --standard=PSR12 config/ src/ tests/", - "fix-style": "phpcbf -p --standard=PSR12 config/ src/ tests/", + "check-style": "phpcs -p --standard=PSR12 config/ src/ tests/ --ignore=src/Resources/* ", + "fix-style": "phpcbf -p --standard=PSR12 config/ src/ tests/ --ignore=src/Resources*", "test": "phpunit" } } diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index f11695c65..153834b8a 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -545,7 +545,7 @@ public function collect() 'params' => [ 'Virtual Machine Instructions' => $vmi, ] - ]; + ]; } else { foreach ($query['explain'] as $explain) { $statements[] = [ From 06b16eece7194e8a5addd628942552e7c6d03d68 Mon Sep 17 00:00:00 2001 From: Geoffrey Rose Date: Sat, 4 Feb 2023 07:33:08 -0800 Subject: [PATCH 268/585] font-size: initial; (#1381) --- src/Resources/laravel-debugbar.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index 424c6ad49..346e55cc0 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -13,6 +13,7 @@ div.phpdebugbar { div.phpdebugbar * { direction: ltr; + font-size: initial; text-align: left; } From 3dea98905873eb1dc93753f55494afaa1e3c432e Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 4 Feb 2023 18:33:21 +0300 Subject: [PATCH 269/585] Add db.slow_threshold to default config (#1375) --- config/debugbar.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/debugbar.php b/config/debugbar.php index 4581a9dce..5c4643cf7 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -198,7 +198,8 @@ 'types' => ['SELECT'], // Deprecated setting, is always only SELECT ], 'hints' => false, // Show hints for common mistakes - 'show_copy' => false, // Show copy button next to the query + 'show_copy' => false, // Show copy button next to the query, + 'slow_threshold' => false, // Only track queries that last longer than this time in ms ], 'mail' => [ 'full_log' => false, From 3001b7d43a46f75bd5d58d556da90de7b94f6b88 Mon Sep 17 00:00:00 2001 From: Huzaifa Saif Ur Rehman Date: Sat, 4 Feb 2023 20:34:08 +0500 Subject: [PATCH 270/585] feat: json_encode context with JSON_PRETTY_PRINT flag (#1374) --- src/LaravelDebugbar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index f44040769..5cf221095 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -260,7 +260,7 @@ function ($level, $message = null, $context = null) use ($logger) { try { $logMessage = (string) $message; if (mb_check_encoding($logMessage, 'UTF-8')) { - $logMessage .= (!empty($context) ? ' ' . json_encode($context) : ''); + $logMessage .= (!empty($context) ? ' ' . json_encode($context, JSON_PRETTY_PRINT) : ''); } else { $logMessage = "[INVALID UTF-8 DATA]"; } From c9fb100934bc51485df770d9a3f9ecce12b2ce6f Mon Sep 17 00:00:00 2001 From: Tom <26380378+azgooon@users.noreply.github.com> Date: Sat, 4 Feb 2023 15:34:21 +0000 Subject: [PATCH 271/585] Update ViewCollector.php (#1378) Fixes error: "Creation of dynamic property Barryvdh\Debugbar\DataCollector\ViewCollector::$name is deprecated" --- src/DataCollector/ViewCollector.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/DataCollector/ViewCollector.php b/src/DataCollector/ViewCollector.php index ef9ac3d8f..b0ae1baa4 100644 --- a/src/DataCollector/ViewCollector.php +++ b/src/DataCollector/ViewCollector.php @@ -9,6 +9,7 @@ class ViewCollector extends TwigCollector { + protected $name; protected $templates = []; protected $collect_data; protected $exclude_paths; From 8aeccb1909a6c0ad8cbf935e751f927fa5e56fca Mon Sep 17 00:00:00 2001 From: Samuel Date: Sat, 4 Feb 2023 15:35:10 +0000 Subject: [PATCH 272/585] QueryCollector: Exception -> Throwable (#1366) --- src/DataCollector/QueryCollector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 153834b8a..f07aa3bf7 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -134,7 +134,7 @@ public function addQuery($query, $bindings, $time, $connection) $pdo = null; try { $pdo = $connection->getPdo(); - } catch (\Exception $e) { + } catch (\Throwable $e) { // ignore error for non-pdo laravel drivers } $bindings = $connection->prepareBindings($bindings); From eb01216141e62433178c52b0cbdb785b45bae871 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Sat, 4 Feb 2023 16:47:28 +0100 Subject: [PATCH 273/585] Drop lower PHP/Laravel versions (#1386) * Drop lower PHP versions * Dev stability * Fix versions --- .github/workflows/run-integration-tests.yml | 36 ++++++--------------- .github/workflows/run-tests.yml | 22 ++----------- composer.json | 10 +++--- 3 files changed, 16 insertions(+), 52 deletions(-) diff --git a/.github/workflows/run-integration-tests.yml b/.github/workflows/run-integration-tests.yml index b881bef14..49877b8fe 100644 --- a/.github/workflows/run-integration-tests.yml +++ b/.github/workflows/run-integration-tests.yml @@ -18,21 +18,11 @@ jobs: COMPOSER_NO_INTERACTION: 1 strategy: matrix: - php: [8.1, 8.0, 7.4, 7.3, 7.2] - lumen: [9.*, 8.*, 7.*] + php: [8.2, 8.1, 8.0] + lumen: [9.*] exclude: - - lumen: 8.* - php: 7.2 - - lumen: 9.* - php: 7.2 - - lumen: 9.* - php: 7.3 - - lumen: 9.* - php: 7.4 - - lumen: 7.* + - lumen: 10.* php: 8.0 - - lumen: 7.* - php: 8.1 name: P${{ matrix.php }} - Lumen${{ matrix.lumen }} steps: - name: Checkout code @@ -49,7 +39,7 @@ jobs: - name: Install dependencies run: | - composer create-project --prefer-dist laravel/lumen:${{ matrix.lumen }} --no-progress sample + composer create-project --prefer-dist laravel/lumen:${{ matrix.lumen }} --stability=dev --no-progress sample cd sample composer config minimum-stability dev composer update --prefer-stable --prefer-dist --no-progress @@ -75,19 +65,11 @@ jobs: COMPOSER_NO_INTERACTION: 1 strategy: matrix: - php: [8.1, 8.0, 7.4, 7.3, 7.2] - laravel: [9.*, 8.*, 7.*] + php: [8.2, 8.1, 8.0] + laravel: [10.*, 9.*] exclude: - - laravel: 8.* - php: 7.2 - - laravel: 9.* - php: 7.2 - - laravel: 9.* - php: 7.3 - - laravel: 9.* - php: 7.4 - - laravel: 7.* - php: 8.1 + - laravel: 10.* + php: 8.0 name: P${{ matrix.php }} - Laravel${{ matrix.laravel }} steps: - name: Checkout code @@ -104,7 +86,7 @@ jobs: - name: Install dependencies run: | - composer create-project --prefer-dist laravel/laravel:${{ matrix.laravel }} --no-progress sample + composer create-project --prefer-dist laravel/laravel:${{ matrix.laravel }} --stability=dev --no-progress sample cd sample composer config minimum-stability dev composer update --prefer-stable --prefer-dist --no-progress diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 48111fe4f..f42ee34bf 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -19,28 +19,10 @@ jobs: strategy: matrix: - php: [8.2, 8.1, 8.0, 7.4, 7.3, 7.2] - laravel: [^10, ^9, ^8, ^7] + php: [8.2, 8.1, 8.0] + laravel: [^10, ^9] dependency-version: [prefer-lowest, prefer-stable] exclude: - - laravel: ^7 - php: 8.1 - - laravel: ^7 - php: 8.2 - - laravel: ^8 - php: 7.2 - - laravel: ^9 - php: 7.2 - - laravel: ^9 - php: 7.3 - - laravel: ^9 - php: 7.4 - - laravel: ^10 - php: 7.2 - - laravel: ^10 - php: 7.3 - - laravel: ^10 - php: 7.4 - laravel: ^10 php: 8.0 diff --git a/composer.json b/composer.json index 5313e1db4..4312ed4ab 100644 --- a/composer.json +++ b/composer.json @@ -10,12 +10,12 @@ } ], "require": { - "php": ">=7.2.5", + "php": "^8.0", "maximebf/debugbar": "^1.17.2", - "illuminate/routing": "^7|^8.67|^9|^10", - "illuminate/session": "^7|^8.67|^9|^10", - "illuminate/support": "^7|^8.67|^9|^10", - "symfony/finder": "^5|^6" + "illuminate/routing": "^9|^10", + "illuminate/session": "^9|^10", + "illuminate/support": "^9|^10", + "symfony/finder": "^6" }, "require-dev": { "mockery/mockery": "^1.3.3", From 5d4ee5c21ede2a3f7ae092937aa2cff828fc1daf Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 20 Feb 2023 11:41:14 +0300 Subject: [PATCH 274/585] Show only duplicated queries (#1394) --- src/Resources/sqlqueries/widget.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/Resources/sqlqueries/widget.js b/src/Resources/sqlqueries/widget.js index 988dc2da9..ebed4f08d 100644 --- a/src/Resources/sqlqueries/widget.js +++ b/src/Resources/sqlqueries/widget.js @@ -259,6 +259,24 @@ if (duplicate) { t.append(", " + duplicate + " of which were duplicated"); t.append(", " + (data.nb_statements - duplicate) + " unique"); + + // add toggler for displaying only duplicated queries + var duplicatedText = "Show only duplicated"; + var allText = "Show All"; + var id = "phpdebugbar-show-duplicates"; + t.append(". " + duplicatedText + ""); + + $(".phpdebugbar #" + id).click(function () { + var $this = $(this); + $this.toggleClass("shown_duplicated"); + $this.text($this.hasClass("shown_duplicated") ? allText : duplicatedText); + $(".phpdebugbar-widgets-sqlqueries .phpdebugbar-widgets-list-item") + .not(".phpdebugbar-widgets-sql-duplicate") + .toggle(); + + return false; + }); + } if (data.accumulated_duration_str) { this.$status.append($('').addClass(csscls('duration')).text(data.accumulated_duration_str)); From a84a666a6e9251d97e587105331ddee3857bd98b Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Tue, 21 Feb 2023 14:31:53 +0100 Subject: [PATCH 275/585] Revert "font-size: initial; (#1381)" (#1395) This reverts commit 06b16eece7194e8a5addd628942552e7c6d03d68. --- src/Resources/laravel-debugbar.css | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index 346e55cc0..424c6ad49 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -13,7 +13,6 @@ div.phpdebugbar { div.phpdebugbar * { direction: ltr; - font-size: initial; text-align: left; } From e3c7995370e417509c4a261439ffb64396107a32 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Tue, 21 Feb 2023 15:15:44 +0100 Subject: [PATCH 276/585] Fix restore btn styling (#1396) --- src/Resources/laravel-debugbar.css | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index 424c6ad49..3d595265d 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -13,6 +13,7 @@ div.phpdebugbar { div.phpdebugbar * { direction: ltr; + text-align: left; } @@ -154,7 +155,7 @@ div.phpdebugbar-openhandler table td a { div.phpdebugbar-resize-handle { display: block!important; height: 3px; - margin-top: -4px; + margin-top: -3px; width: 100%; background: none; cursor: ns-resize; @@ -218,11 +219,9 @@ div.phpdebugbar-body { div.phpdebugbar-header { min-height: 30px; line-height: 20px; - padding-left: 39px; text-shadow: 1px 1px #FFF; } -div.phpdebugbar-header, a.phpdebugbar-restore-btn, div.phpdebugbar-openhandler .phpdebugbar-openhandler-header { background: #f5f5f5 url(data:image/png;base64,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) no-repeat 5px 3px; @@ -258,10 +257,15 @@ div.phpdebugbar-openhandler-header { a.phpdebugbar-restore-btn { border-right-color: #ddd!important; - height: 20px; + height: 22px; width: 23px; background-position: center; background-size: 21px; + background-color: white; +} + +.phpdebugbar:not(.phpdebugbar-closed) a.phpdebugbar-restore-btn { + border-right: none; } div.phpdebugbar-header > div > * { @@ -424,6 +428,7 @@ a.phpdebugbar-tab:hover, span.phpdebugbar-indicator:hover, a.phpdebugbar-indicator:hover, a.phpdebugbar-close-btn:hover, +a.phpdebugbar-restore-btn:hover, a.phpdebugbar-minimize-btn:hover, a.phpdebugbar-maximize-btn:hover, a.phpdebugbar-open-btn:hover { From 5db4f7d9524ea4cca009bc44aab89a9bb443bc20 Mon Sep 17 00:00:00 2001 From: Patrick Date: Tue, 21 Feb 2023 14:16:55 +0000 Subject: [PATCH 277/585] Update LaravelDebugbar.php (#1391) Fix Lumen / Illuminate 9 version detection --- src/LaravelDebugbar.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 5cf221095..54e827641 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -103,6 +103,9 @@ public function __construct($app = null) $this->app = $app; $this->version = $app->version(); $this->is_lumen = Str::contains($this->version, 'Lumen'); + if ($this->is_lumen) { + $this->version = Str::betweenFirst($app->version(), '(', ')'); + } } /** From f217b9022a0270bdd59061fd0b3990ee2d007559 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Tue, 21 Feb 2023 15:20:06 +0100 Subject: [PATCH 278/585] Update branch alias --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 4312ed4ab..10b871c66 100644 --- a/composer.json +++ b/composer.json @@ -40,7 +40,7 @@ "prefer-stable": true, "extra": { "branch-alias": { - "dev-master": "3.6-dev" + "dev-master": "3.8-dev" }, "laravel": { "providers": [ From 8548ee32a3325f2b3e71b105065a4394f9604f86 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Tue, 21 Feb 2023 15:20:45 +0100 Subject: [PATCH 279/585] Bump required version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 10b871c66..f1dc1ee46 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ ], "require": { "php": "^8.0", - "maximebf/debugbar": "^1.17.2", + "maximebf/debugbar": "^1.18.2", "illuminate/routing": "^9|^10", "illuminate/session": "^9|^10", "illuminate/support": "^9|^10", From aff3235fecb4104203b1e62c32239c56530eee32 Mon Sep 17 00:00:00 2001 From: G4Zz0L1 Date: Tue, 21 Feb 2023 15:21:02 +0100 Subject: [PATCH 280/585] Update Debugbar to include autoshow setting (#1393) I think that's a valid point to include, due to the fact that in a lot of software with the autoshow set to true it's impossible to use the debugbar. I'm in Laravel Nova right now, every time a notification ajax gets sent, the request I'm debugging gets removed by that setting. I've tried to add a middleware to enable that setting, but it breaks other things in beetween. Right now this it's the most polished solution. Let me know if it's ok! Thanks! --- src/LaravelDebugbar.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 54e827641..cfc6db4ad 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -897,9 +897,12 @@ function (&$item) { */ public function injectDebugbar(Response $response) { + $config = $this->app['config']; $content = $response->getContent(); $renderer = $this->getJavascriptRenderer(); + $autoShow = $config->get('debugbar.ajax_handler_auto_show', true); + $renderer->setAjaxHandlerAutoShow($autoShow); if ($this->getStorage()) { $openHandlerUrl = route('debugbar.openhandler'); $renderer->setOpenHandlerUrl($openHandlerUrl); From 92e31a5593b144328c5f7ab0f4390db5c8f82204 Mon Sep 17 00:00:00 2001 From: Faissal Wahabali Date: Wed, 22 Feb 2023 20:02:18 +0100 Subject: [PATCH 281/585] fix openhandler close icon position (#1398) --- src/Resources/laravel-debugbar.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index 3d595265d..20aa438c8 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -235,6 +235,7 @@ div.phpdebugbar-openhandler .phpdebugbar-openhandler-header { } div.phpdebugbar-openhandler .phpdebugbar-openhandler-header a { + display: flex; cursor: pointer; } From bdfe99cbbfaa880a5126732a2806a82380be0a89 Mon Sep 17 00:00:00 2001 From: erikn69 Date: Wed, 10 May 2023 02:57:13 -0500 Subject: [PATCH 282/585] (fix) upgrade find view from hash (#1415) --- src/DataCollector/QueryCollector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index f07aa3bf7..8765a2539 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -393,7 +393,7 @@ protected function findViewFromHash($hash) } foreach ($property->getValue($finder) as $name => $path) { - if (sha1($path) == $hash || md5($path) == $hash) { + if (hash('xxh128', 'v2' . $path) == $hash || sha1('v2' . $path) == $hash) { return $name; } } From 8ac7c19d1af8ec4d0717279978fe3d477ac1a48d Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Mon, 3 Jul 2023 12:08:21 +0200 Subject: [PATCH 283/585] Check path in ViewCollector (#1424) --- src/DataCollector/ViewCollector.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/DataCollector/ViewCollector.php b/src/DataCollector/ViewCollector.php index b0ae1baa4..2e898e15d 100644 --- a/src/DataCollector/ViewCollector.php +++ b/src/DataCollector/ViewCollector.php @@ -112,18 +112,17 @@ public function addView(View $view) { $name = $view->getName(); $path = $view->getPath(); + $type = ''; - if (!is_object($path)) { - if ($path) { - $path = ltrim(str_replace(base_path(), '', realpath($path)), '/'); - } + if ($path && is_string($path)) { + $path = ltrim(str_replace(base_path(), '', realpath($path)), '/'); if (substr($path, -10) == '.blade.php') { $type = 'blade'; } else { $type = pathinfo($path, PATHINFO_EXTENSION); } - } else { + } elseif (is_object($path)) { $type = get_class($view); $path = ''; } From 23f6174bff720bc4ecce767c07fe0a79a175eca8 Mon Sep 17 00:00:00 2001 From: erikn69 Date: Thu, 6 Jul 2023 09:39:12 -0500 Subject: [PATCH 284/585] Fix php 8.0 - must be a valid hashing algorithm (#1419) --- src/DataCollector/QueryCollector.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 8765a2539..8824067c0 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -392,8 +392,10 @@ protected function findViewFromHash($hash) $this->reflection['viewfinderViews'] = $property; } + $xxh128Exists = in_array('xxh128', hash_algos()); + foreach ($property->getValue($finder) as $name => $path) { - if (hash('xxh128', 'v2' . $path) == $hash || sha1('v2' . $path) == $hash) { + if (($xxh128Exists && hash('xxh128', 'v2' . $path) == $hash) || sha1('v2' . $path) == $hash) { return $name; } } From cf4554d1dd7805a6a737dcb0f620c80d36850108 Mon Sep 17 00:00:00 2001 From: erikn69 Date: Mon, 17 Jul 2023 10:04:38 -0500 Subject: [PATCH 285/585] Reverting #1359 unnecessary file (#1427) --- src/DataCollector/ViewCollector.php | 2 +- src/JavascriptRenderer.php | 1 - src/Resources/templates/widget.js | 98 ----------------------------- 3 files changed, 1 insertion(+), 100 deletions(-) delete mode 100644 src/Resources/templates/widget.js diff --git a/src/DataCollector/ViewCollector.php b/src/DataCollector/ViewCollector.php index 2e898e15d..1cefe5c7a 100644 --- a/src/DataCollector/ViewCollector.php +++ b/src/DataCollector/ViewCollector.php @@ -62,7 +62,7 @@ public function getWidgets() return [ 'views' => [ 'icon' => 'leaf', - 'widget' => 'PhpDebugBar.Widgets.LaravelViewTemplatesWidget', + 'widget' => 'PhpDebugBar.Widgets.TemplatesWidget', 'map' => 'views', 'default' => '[]' ], diff --git a/src/JavascriptRenderer.php b/src/JavascriptRenderer.php index 9c967909e..428b15a9d 100644 --- a/src/JavascriptRenderer.php +++ b/src/JavascriptRenderer.php @@ -23,7 +23,6 @@ public function __construct(DebugBar $debugBar, $baseUrl = null, $basePath = nul $this->cssVendors['fontawesome'] = __DIR__ . '/Resources/vendor/font-awesome/style.css'; $this->jsFiles['laravel-sql'] = __DIR__ . '/Resources/sqlqueries/widget.js'; $this->jsFiles['laravel-cache'] = __DIR__ . '/Resources/cache/widget.js'; - $this->jsFiles['laravel-view'] = __DIR__ . '/Resources/templates/widget.js'; $theme = config('debugbar.theme', 'auto'); switch ($theme) { diff --git a/src/Resources/templates/widget.js b/src/Resources/templates/widget.js deleted file mode 100644 index bb39188b5..000000000 --- a/src/Resources/templates/widget.js +++ /dev/null @@ -1,98 +0,0 @@ -(function($) { - - var csscls = PhpDebugBar.utils.makecsscls('phpdebugbar-widgets-'); - - /** - * Widget for the displaying templates data - * - * Options: - * - data - */ - var TemplatesWidget = PhpDebugBar.Widgets.LaravelViewTemplatesWidget = PhpDebugBar.Widget.extend({ - - className: csscls('templates'), - - render: function() { - this.$status = $('
    ').addClass(csscls('status')).appendTo(this.$el); - - this.$list = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function(li, tpl) { - $('').addClass(csscls('name')).text(tpl.name).appendTo(li); - - if (typeof tpl.editorLink !== 'undefined' && tpl.editorLink !== null) { - $('') - .addClass(csscls('editor-link')) - .on('click', function (event) { - event.stopPropagation(); - }) - .appendTo(li); - } - if (typeof tpl.xdebug_link !== 'undefined' && tpl.xdebug_link !== null) { - if (tpl.xdebug_link.ajax) { - $('').on('click', function () { - $.ajax(tpl.xdebug_link.url); - }).addClass(csscls('editor-link')).appendTo(li); - } else { - $('').addClass(csscls('editor-link')).appendTo(li); - } - } - if (tpl.render_time_str) { - $('').addClass(csscls('render-time')).text(tpl.render_time_str).appendTo(li); - } - if (tpl.memory_str) { - $('').addClass(csscls('memory')).text(tpl.memory_str).appendTo(li); - } - if (typeof(tpl.param_count) != 'undefined') { - $('').addClass(csscls('param-count')).text(tpl.param_count).appendTo(li); - } - if (typeof(tpl.type) != 'undefined' && tpl.type) { - $('').addClass(csscls('type')).text(tpl.type).appendTo(li); - } - if (tpl.params && !$.isEmptyObject(tpl.params)) { - var table = $('
    Params
    ').addClass(csscls('params')).appendTo(li); - for (var key in tpl.params) { - if (typeof tpl.params[key] !== 'function') { - table.append('' + key + '
    ' + tpl.params[key] + '
    '); - } - } - li.css('cursor', 'pointer').click(function() { - if (table.is(':visible')) { - table.hide(); - } else { - table.show(); - } - }); - } - }}); - this.$list.$el.appendTo(this.$el); - this.$callgraph = $('
    ').addClass(csscls('callgraph')).appendTo(this.$el); - - this.bindAttr('data', function(data) { - this.$list.set('data', data.templates); - this.$status.empty(); - this.$callgraph.empty(); - - var sentence = data.sentence || "templates were rendered"; - $('').text(data.nb_templates + " " + sentence).appendTo(this.$status); - - if (data.accumulated_render_time_str) { - this.$status.append($('').addClass(csscls('render-time')).text(data.accumulated_render_time_str)); - } - if (data.memory_usage_str) { - this.$status.append($('').addClass(csscls('memory')).text(data.memory_usage_str)); - } - if (data.nb_blocks > 0) { - $('
    ').text(data.nb_blocks + " blocks were rendered").appendTo(this.$status); - } - if (data.nb_macros > 0) { - $('
    ').text(data.nb_macros + " macros were rendered").appendTo(this.$status); - } - if (typeof data.callgraph !== 'undefined') { - this.$callgraph.html(data.callgraph); - } - }); - } - - }); - -})(PhpDebugBar.$); From 56a2dc1da9d3219164074713983eef68996386cf Mon Sep 17 00:00:00 2001 From: Benny Rahmat <46495960+akunbeben@users.noreply.github.com> Date: Wed, 26 Jul 2023 12:57:49 +0800 Subject: [PATCH 286/585] feat: update LivewireCollector to supports Livewire v3 (#1436) --- src/DataCollector/LivewireCollector.php | 21 +++++++++++++++++++++ src/LaravelDebugbar.php | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/DataCollector/LivewireCollector.php b/src/DataCollector/LivewireCollector.php index fe8d29d86..7aed46f51 100644 --- a/src/DataCollector/LivewireCollector.php +++ b/src/DataCollector/LivewireCollector.php @@ -12,6 +12,7 @@ use Illuminate\Support\Fluent; use Illuminate\Support\Str; use Livewire\Livewire; +use Livewire\Component; /** * Collector for Models. @@ -46,6 +47,26 @@ public function __construct(Request $request) $this->data[$key] = $this->formatVar($data); }); + + Livewire::listen('render', function (Component $component) use ($request) { + // Create an unique name for each compoent + $key = $component->getName() . ' #' . $component->getId(); + + $data = [ + 'data' => $component->all(), + ]; + + if ($request->request->get('id') == $component->getId()) { + $data['oldData'] = $request->request->get('data'); + $data['actionQueue'] = $request->request->get('actionQueue'); + } + + $data['name'] = $component->getName(); + $data['component'] = get_class($component); + $data['id'] = $component->getId(); + + $this->data[$key] = $this->formatVar($data); + }); } public function collect() diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index cfc6db4ad..9fa8476e1 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -838,7 +838,7 @@ protected function isDebugbarRequest() protected function isJsonRequest(Request $request) { // If XmlHttpRequest or Live, return true - if ($request->isXmlHttpRequest() || $request->headers->get('X-Livewire')) { + if ($request->isXmlHttpRequest() || $request->headers->has('X-Livewire')) { return true; } From 7d906ef86a810c3204cc887d585ac2011063ca1a Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Wed, 23 Aug 2023 13:58:37 +0200 Subject: [PATCH 287/585] Clarify development usage --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 5bac5ccd9..ea4731ffc 100644 --- a/readme.md +++ b/readme.md @@ -14,7 +14,7 @@ Read [the documentation](http://phpdebugbar.com/docs/) for more configuration op ![Debugbar 3.3 Screenshot](https://user-images.githubusercontent.com/973269/79428890-196cc680-7fc7-11ea-8229-189f5eac9009.png) -Note: Use the DebugBar only in development. It can slow the application down (because it has to gather data). So when experiencing slowness, try disabling some of the collectors. +> Note: Use the DebugBar only in development. Do not use Debugbar on public websites, as it will leak information from stored requests (by design). It can also slow the application down (because it has to gather data). So when experiencing slowness, try disabling some of the collectors. This package includes some custom collectors: - QueryCollector: Show all queries, including binding + timing From 4635ad96904ca6168068f08e32247b853674dc22 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 24 Aug 2023 12:37:14 +0200 Subject: [PATCH 288/585] Improve masking of request collector (#1442) --- src/DataCollector/RequestCollector.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/DataCollector/RequestCollector.php b/src/DataCollector/RequestCollector.php index cc3fe866f..16d180cf3 100644 --- a/src/DataCollector/RequestCollector.php +++ b/src/DataCollector/RequestCollector.php @@ -125,8 +125,16 @@ public function collect() } } - if (isset($data['request_headers']['php-auth-pw'])) { - $data['request_headers']['php-auth-pw'] = '******'; + if (isset($data['request_request']['password'])) { + $data['request_request']['password'] = '******'; + } + + if (isset($data['request_headers']['authorization'][0])) { + $data['request_headers']['authorization'][0] = substr($data['request_headers']['authorization'][0], 0, 12) . '******'; + } + + if (isset($data['request_headers']['php-auth-pw'][0])) { + $data['request_headers']['php-auth-pw'][0] = '******'; } if (isset($data['request_server']['PHP_AUTH_PW'])) { From 9aeb524444042926657990a33ef8365e6b3020fa Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 24 Aug 2023 13:48:43 +0200 Subject: [PATCH 289/585] Drop lowest versions (#1441) Drop lowest to fix Chromedriver --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index f42ee34bf..d9d050333 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -21,7 +21,7 @@ jobs: matrix: php: [8.2, 8.1, 8.0] laravel: [^10, ^9] - dependency-version: [prefer-lowest, prefer-stable] + dependency-version: [prefer-stable] exclude: - laravel: ^10 php: 8.0 From cde481e21461be9986dca00c8e314559b55bba41 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 24 Aug 2023 14:10:48 +0200 Subject: [PATCH 290/585] Secure storage by default (#1443) Disable the OpenHandler for storage by default, need to explicitly enable to browse previous requests. --- config/debugbar.php | 4 +++ readme.md | 8 ++++- src/Controllers/OpenHandlerController.php | 41 +++++++++++++++++++++-- 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/config/debugbar.php b/config/debugbar.php index 5c4643cf7..e68132016 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -31,9 +31,13 @@ | By default, file storage (in the storage folder) is used. Redis and PDO | can also be used. For PDO, run the package migrations first. | + | Warning: Enabling storage.open will allow everyone to access previous request, + | do not enable open storage in publicly available environments! + | Specify a callback if you want to limit based on IP or authentication. */ 'storage' => [ 'enabled' => true, + 'open' => env('DEBUGBAR_OPEN_STORAGE', false), // Can be bool or callback. 'driver' => 'file', // redis, file, pdo, socket, custom 'path' => storage_path('debugbar'), // For file driver 'connection' => null, // Leave null for default connection (Redis/PDO) diff --git a/readme.md b/readme.md index ea4731ffc..a747891ae 100644 --- a/readme.md +++ b/readme.md @@ -14,7 +14,7 @@ Read [the documentation](http://phpdebugbar.com/docs/) for more configuration op ![Debugbar 3.3 Screenshot](https://user-images.githubusercontent.com/973269/79428890-196cc680-7fc7-11ea-8229-189f5eac9009.png) -> Note: Use the DebugBar only in development. Do not use Debugbar on public websites, as it will leak information from stored requests (by design). It can also slow the application down (because it has to gather data). So when experiencing slowness, try disabling some of the collectors. +### Note: Use the DebugBar only in development. Do not use Debugbar on publicly accessible websites, as it will leak information from stored requests (by design). It can also slow the application down (because it has to gather data). So when experiencing slowness, try disabling some of the collectors. This package includes some custom collectors: - QueryCollector: Show all queries, including binding + timing @@ -183,6 +183,12 @@ You can enable or disable the debugbar during run time. NB. Once enabled, the collectors are added (and could produce extra overhead), so if you want to use the debugbar in production, disable in the config and only enable when needed. +## Storage + +Debugbar remembers previous requests, which you can view using the Browse button on the right. This will only work if you enable `debugbar.storage.open` in the config. +Make sure you only do this on local development, because otherwise other people will be able to view previous requests. +In general, Debugbar should only be used locally or at least restricted by IP. +It's possible to pass a callback, which will receive the Request object, so you can determine access to the OpenHandler storage. ## Twig Integration diff --git a/src/Controllers/OpenHandlerController.php b/src/Controllers/OpenHandlerController.php index e5c059fc3..0d8acb07b 100644 --- a/src/Controllers/OpenHandlerController.php +++ b/src/Controllers/OpenHandlerController.php @@ -3,16 +3,47 @@ namespace Barryvdh\Debugbar\Controllers; use Barryvdh\Debugbar\Support\Clockwork\Converter; +use DebugBar\DebugBarException; use DebugBar\OpenHandler; use Illuminate\Http\Request; use Illuminate\Http\Response; class OpenHandlerController extends BaseController { + /** + * Check if the storage is open for inspecting. + * + * @param Request $request + * @return bool + */ + protected function isStorageOpen(Request $request) + { + $open = config('debugbar.storage.open'); + + if (is_callable($open)) { + return call_user_func($open, [$request]); + } + + return $open; + } + public function handle(Request $request) { - $openHandler = new OpenHandler($this->debugbar); - $data = $openHandler->handle($request->input(), false, false); + if ($this->isStorageOpen($request)) { + $openHandler = new OpenHandler($this->debugbar); + $data = $openHandler->handle($request->input(), false, false); + } else { + $data = [ + [ + 'datetime' => date("Y-m-d H:i:s"), + 'id' => null, + 'ip' => $request->getClientIp(), + 'method' => 'ERROR', + 'uri' => '!! To enable public access to previous requests, set debugbar.storage.open, or DEBUGBAR_OPEN_STORAGE to true in you config !!', + 'utime' => microtime(true), + ] + ]; + } return new Response( $data, @@ -30,8 +61,12 @@ public function handle(Request $request) * @return mixed * @throws \DebugBar\DebugBarException */ - public function clockwork($id) + public function clockwork(Request $request, $id) { + if (!$this->isStorageOpen($request)) { + throw new DebugBarException(" o enable public access to previous requests, set debugbar.storage.open, or DEBUGBAR_OPEN_STORAGE to true in you config"); + } + $request = [ 'op' => 'get', 'id' => $id, From a9daaa22157acf25d1bbac184d1673bb9d868e3d Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 24 Aug 2023 14:18:28 +0200 Subject: [PATCH 291/585] Clarify text (#1444) --- src/Controllers/OpenHandlerController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Controllers/OpenHandlerController.php b/src/Controllers/OpenHandlerController.php index 0d8acb07b..0057adf92 100644 --- a/src/Controllers/OpenHandlerController.php +++ b/src/Controllers/OpenHandlerController.php @@ -39,7 +39,7 @@ public function handle(Request $request) 'id' => null, 'ip' => $request->getClientIp(), 'method' => 'ERROR', - 'uri' => '!! To enable public access to previous requests, set debugbar.storage.open, or DEBUGBAR_OPEN_STORAGE to true in you config !!', + 'uri' => '!! To enable public access to previous requests, set debugbar.storage.open to true in your config, or enable DEBUGBAR_OPEN_STORAGE if you did not publish the config. !!', 'utime' => microtime(true), ] ]; @@ -64,7 +64,7 @@ public function handle(Request $request) public function clockwork(Request $request, $id) { if (!$this->isStorageOpen($request)) { - throw new DebugBarException(" o enable public access to previous requests, set debugbar.storage.open, or DEBUGBAR_OPEN_STORAGE to true in you config"); + throw new DebugBarException("To enable public access to previous requests, set debugbar.storage.open to true in your config, or enable DEBUGBAR_OPEN_STORAGE if you did not publish the config."); } $request = [ From af7739f6c3329a73f6fb2fae2d96e71d1d28d570 Mon Sep 17 00:00:00 2001 From: erikn69 Date: Thu, 24 Aug 2023 07:18:47 -0500 Subject: [PATCH 292/585] Bump dracula theme (#1439) --- src/Resources/laravel-debugbar-dark-mode.css | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Resources/laravel-debugbar-dark-mode.css b/src/Resources/laravel-debugbar-dark-mode.css index 50f7832d2..f616dba66 100644 --- a/src/Resources/laravel-debugbar-dark-mode.css +++ b/src/Resources/laravel-debugbar-dark-mode.css @@ -195,7 +195,7 @@ div.phpdebugbar div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-par div.phpdebugbar code, div.phpdebugbar pre { - color: #FFF; + color: #f1fa8c; } div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > a, @@ -262,7 +262,8 @@ div.phpdebugbar .hljs-subst { color: #f8f8f2; } -div.phpdebugbar .hljs-title { +div.phpdebugbar .hljs-title, +div.phpdebugbar .hljs-meta-keyword { color: #50fa7b; } @@ -286,6 +287,10 @@ div.phpdebugbar .hljs-deletion { color: #6272a4; } +div.phpdebugbar .hljs-strong { + font-weight: bold; +} + div.phpdebugbar .hljs-literal, div.phpdebugbar .hljs-number { color: #bd93f9; From 51d4a3033f5e5372e6679892e63516e224486cfd Mon Sep 17 00:00:00 2001 From: aivanov07 <42939853+aivanov07@users.noreply.github.com> Date: Thu, 24 Aug 2023 15:20:58 +0300 Subject: [PATCH 293/585] fix when forget button is unclickable if the cache name is too long (#1440) --- src/Resources/laravel-debugbar.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Resources/laravel-debugbar.css b/src/Resources/laravel-debugbar.css index 20aa438c8..c2163c7ea 100644 --- a/src/Resources/laravel-debugbar.css +++ b/src/Resources/laravel-debugbar.css @@ -334,6 +334,9 @@ ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-collector { top: 0px; color: #000; font-size: 11px; + overflow: hidden; + text-overflow: ellipsis; + width: 90%; } ul.phpdebugbar-widgets-timeline li .phpdebugbar-widgets-value span.phpdebugbar-widgets-label { From 8b7b8940ab2692fb526e1a0cb5d561fd51a0ae3b Mon Sep 17 00:00:00 2001 From: erikn69 Date: Thu, 24 Aug 2023 07:21:30 -0500 Subject: [PATCH 294/585] Fix normalizeFilename when multiple matches (#1430) --- src/DataCollector/QueryCollector.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 8824067c0..2ca8cfcc2 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -433,7 +433,14 @@ protected function normalizeFilename($path) if (file_exists($path)) { $path = realpath($path); } - return str_replace(base_path(), '', $path); + + $basepath = base_path(); + + if (! str_starts_with($path, $basepath)) { + return $path; + } + + return substr($path, strlen($basepath)); } /** From 5e8e4a3ac7b66b9219daee9202d7dd92276f0da6 Mon Sep 17 00:00:00 2001 From: Sachin Bahukhandi Date: Thu, 24 Aug 2023 17:51:53 +0530 Subject: [PATCH 295/585] Fixed the command debugbar:clear not being triggered via the Artisan::call(Browser) (#1411) --- src/ServiceProvider.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 11ea596e7..4ba25e814 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -108,9 +108,7 @@ public function boot() $this->registerMiddleware(InjectDebugbar::class); - if ($this->app->runningInConsole()) { - $this->commands(['command.debugbar.clear']); - } + $this->commands(['command.debugbar.clear']); } /** From b4997907a2cd987e2601ee90ad406abe14eae2a8 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 24 Aug 2023 14:24:50 +0200 Subject: [PATCH 296/585] Config CS --- config/debugbar.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/debugbar.php b/config/debugbar.php index e68132016..41b69f53f 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -31,13 +31,13 @@ | By default, file storage (in the storage folder) is used. Redis and PDO | can also be used. For PDO, run the package migrations first. | - | Warning: Enabling storage.open will allow everyone to access previous request, - | do not enable open storage in publicly available environments! + | Warning: Enabling storage.open will allow everyone to access previous + | request, do not enable open storage in publicly available environments! | Specify a callback if you want to limit based on IP or authentication. */ 'storage' => [ 'enabled' => true, - 'open' => env('DEBUGBAR_OPEN_STORAGE', false), // Can be bool or callback. + 'open' => env('DEBUGBAR_OPEN_STORAGE', false), // bool/callback. 'driver' => 'file', // redis, file, pdo, socket, custom 'path' => storage_path('debugbar'), // For file driver 'connection' => null, // Leave null for default connection (Redis/PDO) From 070a313fd744f67348906bc879e544c4dda36be9 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Thu, 24 Aug 2023 14:28:59 +0200 Subject: [PATCH 297/585] Update debugbar.php (#1445) --- config/debugbar.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/debugbar.php b/config/debugbar.php index 41b69f53f..6b688786a 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -31,13 +31,13 @@ | By default, file storage (in the storage folder) is used. Redis and PDO | can also be used. For PDO, run the package migrations first. | - | Warning: Enabling storage.open will allow everyone to access previous - | request, do not enable open storage in publicly available environments! + | Warning: Enabling storage.open will allow everyone to access previous + | request, do not enable open storage in publicly available environments! | Specify a callback if you want to limit based on IP or authentication. */ 'storage' => [ 'enabled' => true, - 'open' => env('DEBUGBAR_OPEN_STORAGE', false), // bool/callback. + 'open' => env('DEBUGBAR_OPEN_STORAGE', false), // bool/callback. 'driver' => 'file', // redis, file, pdo, socket, custom 'path' => storage_path('debugbar'), // For file driver 'connection' => null, // Leave null for default connection (Redis/PDO) From bfd0131c146973cab164e50f5cdd8a67cc60cab1 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Fri, 25 Aug 2023 20:43:57 +0200 Subject: [PATCH 298/585] Limit OpenStorage check to 'find' (#1447) Allow get for direct access, eg. for ajax calls. --- src/Controllers/OpenHandlerController.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Controllers/OpenHandlerController.php b/src/Controllers/OpenHandlerController.php index 0057adf92..363e7ac9a 100644 --- a/src/Controllers/OpenHandlerController.php +++ b/src/Controllers/OpenHandlerController.php @@ -29,7 +29,7 @@ protected function isStorageOpen(Request $request) public function handle(Request $request) { - if ($this->isStorageOpen($request)) { + if ($request->input('op') === 'get' || $this->isStorageOpen($request)) { $openHandler = new OpenHandler($this->debugbar); $data = $openHandler->handle($request->input(), false, false); } else { @@ -63,10 +63,6 @@ public function handle(Request $request) */ public function clockwork(Request $request, $id) { - if (!$this->isStorageOpen($request)) { - throw new DebugBarException("To enable public access to previous requests, set debugbar.storage.open to true in your config, or enable DEBUGBAR_OPEN_STORAGE if you did not publish the config."); - } - $request = [ 'op' => 'get', 'id' => $id, From 27b088aaad2adba74a7a73a47cb8d11277b551e3 Mon Sep 17 00:00:00 2001 From: erikn69 Date: Sun, 27 Aug 2023 08:34:05 -0500 Subject: [PATCH 299/585] ci: Use GitHub Actions V3 (#1448) --- .github/workflows/fix-cs.yml | 2 +- .github/workflows/run-integration-tests.yml | 4 ++-- .github/workflows/run-tests.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/fix-cs.yml b/.github/workflows/fix-cs.yml index 3b6b49063..127ae952d 100644 --- a/.github/workflows/fix-cs.yml +++ b/.github/workflows/fix-cs.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/run-integration-tests.yml b/.github/workflows/run-integration-tests.yml index 49877b8fe..7e9dc0dc3 100644 --- a/.github/workflows/run-integration-tests.yml +++ b/.github/workflows/run-integration-tests.yml @@ -26,7 +26,7 @@ jobs: name: P${{ matrix.php }} - Lumen${{ matrix.lumen }} steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: path: src @@ -73,7 +73,7 @@ jobs: name: P${{ matrix.php }} - Laravel${{ matrix.laravel }} steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: path: src diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index d9d050333..3138180eb 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -30,7 +30,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 From 88aef02d095141be312f4f5f77e4199e3347e6cd Mon Sep 17 00:00:00 2001 From: erikn69 Date: Fri, 15 Sep 2023 11:27:25 -0500 Subject: [PATCH 300/585] Remove Laravel <5.2 code (#1461) --- src/LaravelDebugbar.php | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index 9fa8476e1..a0642eace 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -340,30 +340,15 @@ function ($level, $message = null, $context = null) use ($logger) { try { $db->listen( - function ( - $query, - $bindings = null, - $time = null, - $connectionName = null - ) use ( - $db, - $queryCollector - ) { + function (\Illuminate\Database\Events\QueryExecuted $query) use ($db, $queryCollector) { if (!app(static::class)->shouldCollect('db', true)) { return; // Issue 776 : We've turned off collecting after the listener was attached } - // Laravel 5.2 changed the way some core events worked. We must account for - // the first argument being an "event object", where arguments are passed - // via object properties, instead of individual arguments. - if ($query instanceof \Illuminate\Database\Events\QueryExecuted) { - $bindings = $query->bindings; - $time = $query->time; - $connection = $query->connection; - - $query = $query->sql; - } else { - $connection = $db->connection($connectionName); - } + + $bindings = $query->bindings; + $time = $query->time; + $connection = $query->connection; + $query = $query->sql; //allow collecting only queries slower than a specified amount of milliseconds $threshold = app('config')->get('debugbar.options.db.slow_threshold', false); From 8a3bab78027f29c1e37d26b83413c93fe82a1310 Mon Sep 17 00:00:00 2001 From: Yannik Sembritzki Date: Tue, 19 Sep 2023 16:17:37 +0200 Subject: [PATCH 301/585] Add nonce to "; + $html .= ""; if ($this->isJqueryNoConflictEnabled()) { - $html .= '' . "\n"; + $html .= "jQuery.noConflict(true);" . "\n"; } - $html .= $this->getInlineHtml(); + $inlineHtml = $this->getInlineHtml(); + if ($nonce != '') { + $inlineHtml = preg_replace("/ + From aa600c97fab378443dbf324eaffc0bbae4529896 Mon Sep 17 00:00:00 2001 From: Tobias Petry Date: Mon, 16 Sep 2024 13:51:02 +0200 Subject: [PATCH 467/585] Explain Improvements (#1670) * support mariadb explain * fix for really old mysql versions (not support by laravel anymore) * disable visual explain for mariadb * disable visual explain for mariadb * indentation whitespace was lost for pgsql explain * make visual explain check only when running an explain (its not needed before) --- src/Controllers/QueriesController.php | 17 +++++++---- src/DataCollector/QueryCollector.php | 3 +- src/Resources/queries/widget.js | 21 ++++++++------ src/Support/Explain.php | 41 ++++++++++++++++++++++----- 4 files changed, 59 insertions(+), 23 deletions(-) diff --git a/src/Controllers/QueriesController.php b/src/Controllers/QueriesController.php index a98201b3f..314a9b1c2 100644 --- a/src/Controllers/QueriesController.php +++ b/src/Controllers/QueriesController.php @@ -21,14 +21,21 @@ public function explain(Request $request) } try { - $data = match ($request->json('mode')) { - 'visual' => (new Explain())->generateVisualExplain($request->json('connection'), $request->json('query'), $request->json('bindings'), $request->json('hash')), - default => (new Explain())->generateRawExplain($request->json('connection'), $request->json('query'), $request->json('bindings'), $request->json('hash')), - }; + $explain = new Explain(); + + if ($request->json('mode') === 'visual') { + return response()->json([ + 'success' => true, + 'data' => $explain->generateVisualExplain($request->json('connection'), $request->json('query'), $request->json('bindings'), $request->json('hash')), + ]); + } return response()->json([ 'success' => true, - 'data' => $data, + 'data' => $explain->generateRawExplain($request->json('connection'), $request->json('query'), $request->json('bindings'), $request->json('hash')), + 'visual' => $explain->isVisualExplainSupported($request->json('connection')) ? [ + 'confirm' => $explain->confirmVisualExplain($request->json('connection')), + ] : null, ]); } catch (Exception $e) { return response()->json([ diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index a1d80ed26..19fec2a64 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -500,7 +500,7 @@ public function collect() } $canExplainQuery = match (true) { - in_array($query['driver'], ['mysql', 'pgsql']) => $query['bindings'] !== null && preg_match('/^\s*(' . implode('|', $this->explainTypes) . ') /i', $query['query']), + in_array($query['driver'], ['mariadb', 'mysql', 'pgsql']) => $query['bindings'] !== null && preg_match('/^\s*(' . implode('|', $this->explainTypes) . ') /i', $query['query']), default => false, }; @@ -523,7 +523,6 @@ public function collect() 'connection' => $connectionName, 'explain' => $this->explainQuery && $canExplainQuery ? [ 'url' => route('debugbar.queries.explain'), - 'visual-confirm' => (new Explain())->confirm($query['connection']), 'driver' => $query['driver'], 'connection' => $query['connection'], 'query' => $query['query'], diff --git a/src/Resources/queries/widget.js b/src/Resources/queries/widget.js index 9b9f5cd5c..42c33d0b0 100644 --- a/src/Resources/queries/widget.js +++ b/src/Resources/queries/widget.js @@ -41,7 +41,7 @@ return isCopied; }, - explainMysql: function ($element, statement, rows) { + explainMysql: function ($element, statement, rows, visual) { const headings = []; for (const key in rows[0]) { headings.push($('').text(key)); @@ -60,27 +60,30 @@ $table.find('thead').append($('').append(headings)); $table.find('tbody').append(values); - $element.append([$table, this.explainVisual(statement)]); + $element.append($table); + if (visual) { + $element.append(this.explainVisual(statement, visual.confirm)); + } }, - explainPgsql: function ($element, statement, rows) { + explainPgsql: function ($element, statement, rows, visual) { const $ul = $('