Created
September 14, 2024 09:43
-
-
Save LachlanArthur/7946eaa9daa26f73bf416abbe4852734 to your computer and use it in GitHub Desktop.
Profiling middleware in Laravel 11 with the Laravel DebugBar
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Providers; | |
use App\Http\Middleware\Profiled; | |
use Closure; | |
use DebugBar\DebugBar; | |
use Illuminate\Contracts\Http\Kernel as HttpKernelContract; | |
use Illuminate\Http\Request; | |
use Illuminate\Routing\Events\RouteMatched; | |
use Illuminate\Routing\Router; | |
use Illuminate\Support\ServiceProvider; | |
class MiddlewareProfiling extends ServiceProvider | |
{ | |
/** | |
* Register any application services. | |
*/ | |
public function register(): void | |
{ | |
// | |
} | |
/** | |
* Bootstrap any application services. | |
*/ | |
public function boot(): void | |
{ | |
if (class_exists(DebugBar::class) && !$this->app->runningInConsole()) { | |
$this->app->booted($this->enableProfiling(...)); | |
} | |
} | |
protected function enableProfiling() | |
{ | |
/** | |
* @var \Illuminate\Foundation\Http\Kernel $kernel | |
*/ | |
$kernel = $this->app->get(HttpKernelContract::class); | |
$kernel->setGlobalMiddleware(array_map($this->wrapMiddleware(...), $kernel->getGlobalMiddleware())); | |
\Event::listen(function (RouteMatched $routeMatched) { | |
$router = $this->app->make(Router::class); | |
$routeMatched->route->computedMiddleware = array_map($this->wrapMiddleware(...), $router->gatherRouteMiddleware($routeMatched->route)); | |
$routeMatched->route->computedMiddleware[] = fn(Request $request, Closure $next) => measure('Route response', fn() => $next($request)); | |
}); | |
} | |
protected function wrapMiddleware($middleware) | |
{ | |
if (is_string($middleware)) { | |
return Profiled::class . ':' . base64_encode($middleware); | |
} | |
if ($middleware instanceof Closure) { | |
return Profiled::wrapClosure($middleware); | |
} | |
\Log::warning('Failed to profile middleware', ['middleware' => $middleware]); | |
return $middleware; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Http\Middleware; | |
use Closure; | |
use Illuminate\Http\Request; | |
use Symfony\Component\HttpFoundation\Response; | |
class Profiled | |
{ | |
public static function wrapClosure(Closure $middleware) | |
{ | |
return fn(Request $request, Closure $next) => (new static)->handle($request, $next, $middleware); | |
} | |
/** | |
* Handle an incoming request. | |
* | |
* @param Request $request | |
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next | |
* @param class-string|Closure $middleware | |
* @return Response | |
*/ | |
public function handle(Request $request, Closure $next, $middleware): Response | |
{ | |
$name = $this->middlewareName($middleware); | |
start_measure("{$name} [Entry]"); | |
$response = $this->proxy($middleware, $request, function (Request $request) use ($name, $next) { | |
stop_measure("{$name} [Entry]"); | |
$response = $next($request); | |
start_measure("{$name} [Exit]"); | |
return $response; | |
}); | |
stop_measure("{$name} [Exit]"); | |
return $response; | |
} | |
protected function middlewareName($middleware): string | |
{ | |
if (is_string($middleware)) { | |
return base64_decode($middleware); | |
} | |
if ($middleware instanceof Closure) { | |
$reflect = new \Laravel\SerializableClosure\Support\ReflectionClosure($middleware); | |
return $reflect->getShortName(); | |
} | |
throw new \InvalidArgumentException('Invalid middleware'); | |
} | |
protected function proxy($middleware, Request $request, Closure $next): Response | |
{ | |
if (is_string($middleware)) { | |
[$name, $parameters] = $this->parseMiddlewareString(base64_decode($middleware)); | |
return app()->make($name)->handle($request, $next, ...$parameters); | |
} | |
if ($middleware instanceof Closure) { | |
return $middleware($request, $next); | |
} | |
throw new \InvalidArgumentException('Invalid middleware'); | |
} | |
/** | |
* Parse full middleware string to get name and parameters. | |
* | |
* @param string $middleware | |
* @return array | |
* | |
* @see \Illuminate\Pipeline\Pipeline::parsePipeString | |
*/ | |
protected function parseMiddlewareString(string $middleware) | |
{ | |
[$name, $parameters] = array_pad(explode(':', $middleware, 2), 2, []); | |
if (is_string($parameters)) { | |
$parameters = explode(',', $parameters); | |
} | |
return [$name, $parameters]; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment