From 7309e1e4fa47bc43a84cd60575b073c4ea2e7f5b Mon Sep 17 00:00:00 2001 From: Derrick Austin Date: Mon, 1 Jun 2026 19:10:02 -0500 Subject: [PATCH 1/3] Support Laravel Debugbar 4 (Laravel 13) barryvdh/laravel-debugbar ^4 is required for Laravel 13 support. This bumps the dependency from ^3.15 to ^4.2 and adapts the integration code: - Laravel Debugbar 4 removed Barryvdh\Debugbar\SymfonyHttpDriver and now configures its own LaravelHttpDriver, so the manual HTTP-driver wiring in Plugin::register() is removed (and the now-unused SymfonyHttpDriver / SessionManager imports). - Laravel Debugbar 4 performs renderer configuration inside getJavascriptRenderer(). WinterDebugbar now defers to the parent to build/cache the renderer and registers Winter's stylesheet via the renderer asset API, replacing the old custom JavascriptRenderer subclass (removed). - php >= 8.3 to match Laravel 13. PRs that depend on this: wintercms/storm#228, wintercms/winter#1487. --- Plugin.php | 14 +++----------- classes/JavascriptRenderer.php | 19 ------------------- classes/WinterDebugbar.php | 23 +++++++++++++++-------- composer.json | 4 ++-- 4 files changed, 20 insertions(+), 40 deletions(-) delete mode 100644 classes/JavascriptRenderer.php diff --git a/Plugin.php b/Plugin.php index 537cc53..0ea06ac 100644 --- a/Plugin.php +++ b/Plugin.php @@ -4,7 +4,6 @@ use Backend\Models\UserRole; use Barryvdh\Debugbar\Facades\Debugbar; use Barryvdh\Debugbar\LaravelDebugbar; -use Barryvdh\Debugbar\SymfonyHttpDriver; use Cms\Classes\Controller as CmsController; use Cms\Classes\Layout; use Cms\Classes\Page; @@ -12,7 +11,6 @@ use Event; use Illuminate\Contracts\Http\Kernel as HttpKernelContract; use Illuminate\Foundation\AliasLoader; -use Illuminate\Session\SessionManager; use System\Classes\CombineAssets; use System\Classes\PluginBase; use Twig\Extension\ProfilerExtension; @@ -61,16 +59,10 @@ public function register() $this->app->register(\Winter\Debugbar\Classes\ServiceProvider::class); // Replace the LaravelDebugbar with the WinterDebugbar + // Laravel Debugbar 4 configures its own HTTP driver (LaravelHttpDriver), so we only + // need to swap in Winter's Debugbar subclass for the custom JavascriptRenderer styling. $this->app->singleton(LaravelDebugbar::class, function ($app) { - $debugbar = new WinterDebugbar($app); - - if ($app->bound(SessionManager::class)) { - $sessionManager = $app->make(SessionManager::class); - $httpDriver = new SymfonyHttpDriver($sessionManager); - $debugbar->setHttpDriver($httpDriver); - } - - return $debugbar; + return new WinterDebugbar($app); }); // Register alias diff --git a/classes/JavascriptRenderer.php b/classes/JavascriptRenderer.php deleted file mode 100644 index 036410c..0000000 --- a/classes/JavascriptRenderer.php +++ /dev/null @@ -1,19 +0,0 @@ -cssFiles['winter'] = __DIR__ . '/../assets/css/debugbar.css'; - } -} diff --git a/classes/WinterDebugbar.php b/classes/WinterDebugbar.php index 2a7c25a..36d0306 100644 --- a/classes/WinterDebugbar.php +++ b/classes/WinterDebugbar.php @@ -1,21 +1,28 @@ jsRenderer === null) { - $this->jsRenderer = new JavascriptRenderer($this, $baseUrl, $basePath); + $alreadyBuilt = $this->jsRenderer !== null; + + $renderer = parent::getJavascriptRenderer($baseUrl, $basePath); + + if (!$alreadyBuilt) { + $renderer->addAssets(cssFiles: ['debugbar.css'], basePath: __DIR__ . '/../assets/css'); } - return $this->jsRenderer; + + return $renderer; } } diff --git a/composer.json b/composer.json index bf72003..e821af9 100644 --- a/composer.json +++ b/composer.json @@ -23,9 +23,9 @@ "source": "https://github.com/wintercms/wn-debugbar-plugin" }, "require": { - "php": ">=7.0", + "php": ">=8.3", "composer/installers": "~1.0", - "barryvdh/laravel-debugbar": "^3.15.0" + "barryvdh/laravel-debugbar": "^4.2" }, "replace": { "rainlab/debugbar-plugin": ">=1.0.5" From dd9b53fb185b7b20056dc35de3806e21c7430e4d Mon Sep 17 00:00:00 2001 From: Derrick Austin Date: Mon, 1 Jun 2026 19:55:39 -0500 Subject: [PATCH 2/3] Migrate to Fruitcake\LaravelDebugbar namespace for Laravel Debugbar 4 --- Plugin.php | 18 +++---- classes/ServiceProvider.php | 24 +++++++-- classes/WinterDebugbar.php | 4 +- middleware/InjectDebugbar.php | 66 ++++++++++++++++++++----- middleware/InterpretsAjaxExceptions.php | 2 +- twig/extension/Debug.php | 2 +- twig/extension/Stopwatch.php | 2 +- 7 files changed, 88 insertions(+), 30 deletions(-) diff --git a/Plugin.php b/Plugin.php index 0ea06ac..dcdc9b9 100644 --- a/Plugin.php +++ b/Plugin.php @@ -2,8 +2,8 @@ use Backend\Classes\Controller as BackendController; use Backend\Models\UserRole; -use Barryvdh\Debugbar\Facades\Debugbar; -use Barryvdh\Debugbar\LaravelDebugbar; +use Fruitcake\LaravelDebugbar\Facades\Debugbar; +use Fruitcake\LaravelDebugbar\LaravelDebugbar; use Cms\Classes\Controller as CmsController; use Cms\Classes\Layout; use Cms\Classes\Page; @@ -110,8 +110,8 @@ public function addGlobalCollectors() // Disable original models collector because it will be replaced Config::set('debugbar.collectors.models', false); - /** @var \Barryvdh\Debugbar\LaravelDebugbar $debugBar */ - $debugBar = $this->app->make(\Barryvdh\Debugbar\LaravelDebugbar::class); + /** @var \Fruitcake\LaravelDebugbar\LaravelDebugbar $debugBar */ + $debugBar = $this->app->make(\Fruitcake\LaravelDebugbar\LaravelDebugbar::class); $modelsCollector = $this->app->make(ModelsCollector::class); $debugBar->addCollector($modelsCollector); } @@ -122,8 +122,8 @@ public function addGlobalCollectors() */ public function addFrontendCollectors() { - /** @var \Barryvdh\Debugbar\LaravelDebugbar $debugBar */ - $debugBar = $this->app->make(\Barryvdh\Debugbar\LaravelDebugbar::class); + /** @var \Fruitcake\LaravelDebugbar\LaravelDebugbar $debugBar */ + $debugBar = $this->app->make(\Fruitcake\LaravelDebugbar\LaravelDebugbar::class); if (Config::get('debugbar.collectors.cms', true)) { // Disable route collector as the CMS collector presents this info instead @@ -156,8 +156,8 @@ public function addFrontendCollectors() */ public function addBackendCollectors() { - /** @var \Barryvdh\Debugbar\LaravelDebugbar $debugBar */ - $debugBar = $this->app->make(\Barryvdh\Debugbar\LaravelDebugbar::class); + /** @var \Fruitcake\LaravelDebugbar\LaravelDebugbar $debugBar */ + $debugBar = $this->app->make(\Fruitcake\LaravelDebugbar\LaravelDebugbar::class); if (Config::get('debugbar.collectors.backend', true)) { Event::listen('backend.page.beforeDisplay', function (BackendController $controller, $action, array $params) use ($debugBar) { @@ -175,7 +175,7 @@ public function addBackendCollectors() protected function registerCmsTwigExtensions() { $profile = new Profile; - $debugBar = $this->app->make(\Barryvdh\Debugbar\LaravelDebugbar::class); + $debugBar = $this->app->make(\Fruitcake\LaravelDebugbar\LaravelDebugbar::class); Event::listen('cms.page.beforeDisplay', function ($controller, $url, $page) use ($profile, $debugBar) { $twig = $controller->getTwig(); diff --git a/classes/ServiceProvider.php b/classes/ServiceProvider.php index 374fd0d..f3b5360 100644 --- a/classes/ServiceProvider.php +++ b/classes/ServiceProvider.php @@ -1,6 +1,7 @@ registerMiddleware(InjectDebugbar::class); + } + /** * Register the Debugbar Middleware * * @param string $middleware */ - protected function registerMiddleware($middleware) + protected function registerMiddleware(string $middleware): void { $kernel = $this->app[Kernel::class]; - $kernel->pushMiddleware(InjectDebugbar::class); + $kernel->pushMiddleware($middleware); } } diff --git a/classes/WinterDebugbar.php b/classes/WinterDebugbar.php index 36d0306..78c960d 100644 --- a/classes/WinterDebugbar.php +++ b/classes/WinterDebugbar.php @@ -1,7 +1,7 @@ app = $app; + $this->debugbar = $debugbar; + } + /** * Handle an incoming request. * @@ -20,18 +57,14 @@ class InjectDebugbar extends BaseMiddleware */ public function handle($request, Closure $next) { - if (!$this->debugbar->isEnabled() || $this->inExceptArray($request)) { + if (!$this->debugbar->isEnabled() || $this->debugbar->requestIsExcluded($request)) { return $next($request); } $this->debugbar->boot(); - try { - /** @var \Illuminate\Http\Response $response */ - $response = $next($request); - } catch (Throwable $e) { - $response = $this->handleException($request, $e); - } + /** @var \Illuminate\Http\Response $response */ + $response = $next($request); // Database table might not exist yet try { @@ -49,14 +82,21 @@ public function handle($request, Closure $next) $this->debugbar->setStorage(null); } - // Modify the response to add the Debugbar if allowed + // Only inject the Debugbar into the response if the user is allowed to view it if ( - ($user && $user->hasAccess('winter.debugbar.access_debugbar')) || - Config::get('winter.debugbar::allow_public_access', false) + !($user && $user->hasAccess('winter.debugbar.access_debugbar')) && + !Config::get('winter.debugbar::allow_public_access', false) ) { - $this->debugbar->modifyResponse($request, $response); + // Prevent the visual Debugbar from being injected while still allowing + // permitted requests to be stored (handled by setStorage above) + Config::set('debugbar.inject', false); } + // Modify the response to add the Debugbar (and store the request, when permitted). + // This flags the response as modified, so the base ServiceProvider's RequestHandled + // listener will skip re-processing it. + $this->debugbar->handleResponse($request, $response); + return $response; } } diff --git a/middleware/InterpretsAjaxExceptions.php b/middleware/InterpretsAjaxExceptions.php index f4a858a..961a38d 100644 --- a/middleware/InterpretsAjaxExceptions.php +++ b/middleware/InterpretsAjaxExceptions.php @@ -38,7 +38,7 @@ public function __construct(Application $app) */ public function handle($request, Closure $next) { - /** @var \Barryvdh\Debugbar\LaravelDebugbar $debugbar */ + /** @var \Fruitcake\LaravelDebugbar\LaravelDebugbar $debugbar */ $debugbar = $this->app['debugbar']; try { diff --git a/twig/extension/Debug.php b/twig/extension/Debug.php index d9ebd57..39e2709 100644 --- a/twig/extension/Debug.php +++ b/twig/extension/Debug.php @@ -13,7 +13,7 @@ class Debug extends TwigExtension { /** - * @var \Barryvdh\Debugbar\LaravelDebugbar + * @var \Fruitcake\LaravelDebugbar\LaravelDebugbar */ protected $debugbar; diff --git a/twig/extension/Stopwatch.php b/twig/extension/Stopwatch.php index e886d44..e8c0988 100644 --- a/twig/extension/Stopwatch.php +++ b/twig/extension/Stopwatch.php @@ -13,7 +13,7 @@ class Stopwatch extends TwigExtension { /** - * @var \Barryvdh\Debugbar\LaravelDebugbar + * @var \Fruitcake\LaravelDebugbar\LaravelDebugbar */ protected $debugbar; From 3b79e87f071074144f4c3354985b40de8453a523 Mon Sep 17 00:00:00 2001 From: Derrick Austin Date: Mon, 1 Jun 2026 20:29:03 -0500 Subject: [PATCH 3/3] Fix Laravel Debugbar 4 runtime compatibility - Resolve WinterDebugbar via the container so the new LaravelDebugbar(Application, Request) constructor dependencies are injected. - Guard the models collector with hasCollector() (Laravel Debugbar 4 registers a default models collector, causing a duplicate-collector exception). - Only register a Twig profile collector when the php-debugbar bridge class exists (php-debugbar no longer bundles one). --- Plugin.php | 12 +++++++++--- collectors/BackendCollector.php | 6 +++--- collectors/CmsCollector.php | 6 +++--- collectors/ComponentsCollector.php | 6 +++--- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/Plugin.php b/Plugin.php index dcdc9b9..9559e52 100644 --- a/Plugin.php +++ b/Plugin.php @@ -61,8 +61,10 @@ public function register() // Replace the LaravelDebugbar with the WinterDebugbar // Laravel Debugbar 4 configures its own HTTP driver (LaravelHttpDriver), so we only // need to swap in Winter's Debugbar subclass for the custom JavascriptRenderer styling. + // Resolve through the container so the constructor (Application $app, Request $request) + // dependencies are injected (Laravel Debugbar 4 requires both). $this->app->singleton(LaravelDebugbar::class, function ($app) { - return new WinterDebugbar($app); + return $app->make(WinterDebugbar::class); }); // Register alias @@ -113,7 +115,9 @@ public function addGlobalCollectors() /** @var \Fruitcake\LaravelDebugbar\LaravelDebugbar $debugBar */ $debugBar = $this->app->make(\Fruitcake\LaravelDebugbar\LaravelDebugbar::class); $modelsCollector = $this->app->make(ModelsCollector::class); - $debugBar->addCollector($modelsCollector); + if (!$debugBar->hasCollector($modelsCollector->getName())) { + $debugBar->addCollector($modelsCollector); + } } } @@ -189,9 +193,11 @@ protected function registerCmsTwigExtensions() } }); + // php-debugbar 2.x (shipped with Laravel Debugbar 4) no longer bundles a Twig profile + // collector, so only register one when the bridge class is actually available. if (class_exists(\DebugBar\Bridge\NamespacedTwigProfileCollector::class)) { $debugBar->addCollector(new \DebugBar\Bridge\NamespacedTwigProfileCollector($profile)); - } else { + } elseif (class_exists(\DebugBar\Bridge\TwigProfileCollector::class)) { $debugBar->addCollector(new \DebugBar\Bridge\TwigProfileCollector($profile)); } } diff --git a/collectors/BackendCollector.php b/collectors/BackendCollector.php index 5b6429b..27e3c4f 100644 --- a/collectors/BackendCollector.php +++ b/collectors/BackendCollector.php @@ -25,7 +25,7 @@ public function __construct(Controller $controller, $action, array $params = []) /** * {@inheritDoc} */ - public function collect() + public function collect(): array { $ajaxHandler = $this->controller->getAjaxHandler(); @@ -100,7 +100,7 @@ protected function getReflector($handler) /** * {@inheritDoc} */ - public function getName() + public function getName(): string { return 'backend'; } @@ -108,7 +108,7 @@ public function getName() /** * {@inheritDoc} */ - public function getWidgets() + public function getWidgets(): array { return [ 'route' => [ diff --git a/collectors/CmsCollector.php b/collectors/CmsCollector.php index 1523c33..4df4459 100644 --- a/collectors/CmsCollector.php +++ b/collectors/CmsCollector.php @@ -26,7 +26,7 @@ public function __construct(Controller $controller, $url, Page $page) /** * {@inheritDoc} */ - public function collect() + public function collect(): array { $ajaxHandler = $this->controller->getAjaxHandler(); @@ -103,7 +103,7 @@ protected function getReflector($handler) /** * {@inheritDoc} */ - public function getName() + public function getName(): string { return 'cms'; } @@ -111,7 +111,7 @@ public function getName() /** * {@inheritDoc} */ - public function getWidgets() + public function getWidgets(): array { return [ 'route' => [ diff --git a/collectors/ComponentsCollector.php b/collectors/ComponentsCollector.php index ca9d333..d87bbe6 100644 --- a/collectors/ComponentsCollector.php +++ b/collectors/ComponentsCollector.php @@ -28,7 +28,7 @@ public function __construct(Controller $controller, Page $page, Layout $layout) /** * {@inheritDoc} */ - public function collect() + public function collect(): array { /** @var ComponentBase[]|object $components */ $components = []; @@ -58,7 +58,7 @@ protected function makeComponentDetails($componentObj): array /** * {@inheritDoc} */ - public function getName() + public function getName(): string { return 'components'; } @@ -66,7 +66,7 @@ public function getName() /** * {@inheritDoc} */ - public function getWidgets() + public function getWidgets(): array { return [ 'components' => [