symfony/http-kernel
Symfony HttpKernel provides a structured, event-driven workflow to turn HTTP Requests into Responses. Built on the EventDispatcher, it’s flexible enough for full-stack frameworks, micro-frameworks, and CMS platforms like Drupal.
Installation:
Laravel already includes symfony/http-kernel as a dependency (via symfony/http-foundation). No additional composer install is needed.
First Use Case: Custom Middleware:
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\EventDispatcher\EventDispatcher;
// In a service provider (e.g., AppServiceProvider)
public function boot()
{
$dispatcher = app(EventDispatcher::class);
$dispatcher->addListener(KernelEvents::REQUEST, function (RequestEvent $event) {
// Modify request before it reaches the controller
$event->getRequest()->attributes->set('custom_data', 'value');
});
}
Where to Look First:
app/Http/Kernel.php (extends Symfony\Component\HttpKernel\HttpKernel).app/Listeners/ or app/Events/ for kernel events.#[Cache], #[Route], or #[MapRequestPayload] in controllers.Workflow:
KernelEvents to tap into the request/response lifecycle.use Symfony\Component\HttpKernel\Event\ViewEvent;
$dispatcher->addListener(KernelEvents::VIEW, function (ViewEvent $event) {
if ($event->getController() instanceof Closure) {
$request = $event->getRequest();
\Log::info('Request payload:', $request->request->all());
}
});
Pattern:
Leverage Symfony’s #[MapRequestPayload] or #[MapUploadedFile] for complex argument binding.
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
class UserController extends Controller
{
public function store(
#[MapRequestPayload] array $payload,
#[MapUploadedFile] $avatar
) {
// $payload and $avatar are automatically resolved
}
}
Integration:
use Symfony\Component\HttpKernel\Attribute\Cache;
#[Cache(maxAge: 3600, public: true)]
public function dashboard(): Response
{
return response()->view('dashboard');
}
use Symfony\Component\HttpKernel\HttpCache\Store;
$cache = new Store($cachePool);
$fragment = $cache->get($request, 'sidebar_fragment');
Use Case: Trigger actions on KernelEvents::TERMINATE (e.g., analytics, cleanup).
$dispatcher->addListener(KernelEvents::TERMINATE, function (TerminateEvent $event) {
$response = $event->getResponse();
// Log response time or send telemetry
});
Pattern: Use HttpKernelInterface to dispatch sub-requests (e.g., for includes or microservices).
use Symfony\Component\HttpKernel\HttpKernelInterface;
public function includePartial(HttpKernelInterface $kernel)
{
$subRequest = $kernel->getRequest()->duplicate(
null,
null,
['_route' => 'partial.route']
);
return $kernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
}
Locale Leaks:
v8.0.8).TerminateEvent:
$event->getRequest()->getLocale() === 'en' ? 'en' : 'default';
Enum Validation:
MapRequestPayload may throw errors (fixed in v8.0.6).#[Assert\Type] or handle gracefully:
#[MapRequestPayload]
#[Assert\Type(type: "App\Enum\UserRole::class")]
public UserRole $role;
Cache Conflicts:
#[Cache] may ignore explicit Cache-Control headers (fixed in v8.0.1).Cache::new() with public: true or merge directives:
#[Cache(
maxAge: 3600,
public: true,
vary: ['Accept-Language']
)]
Sub-Request Service Reset:
v8.0.1).HttpKernelInterface::SUB_REQUEST flag:
$kernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
JSON Coercion:
v8.0.7).#[Assert\Type(type: "integer")].Kernel Event Dumping:
Enable Symfony’s event profiler in config/profiler.php:
'enabled' => env('APP_DEBUG'),
'collectors' => [
Symfony\Component\HttpKernel\Profiler\EventCollector::class,
],
Request Dumping:
Use dd($event->getRequest()) in listeners to inspect:
$request->headers.$request->attributes.$request->request->all().Cache Debugging:
Check HttpCacheStore logs or use Cache::get() with debug: true:
$cache = new Store($pool, [], true); // Enable debug mode
Custom Value Resolvers:
Extend Symfony\Component\HttpKernel\Controller\ValueResolverInterface for custom argument binding:
class CustomResolver implements ValueResolverInterface
{
public function resolve(Request $request, ResolutionEnvironment $environment)
{
return new CustomValue();
}
}
Register via HttpKernelInterface::setControllerResolver().
Event Subscribers: Group related listeners into a subscriber:
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class AnalyticsSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
KernelEvents::REQUEST => 'onRequest',
KernelEvents::RESPONSE => 'onResponse',
];
}
}
Kernel Extensions:
Override Laravel’s kernel by extending Illuminate\Foundation\Http\Kernel and injecting Symfony components:
class CustomKernel extends HttpKernel
{
protected function build(Request $request)
{
$this->addListener(KernelEvents::REQUEST, [$this, 'customLogic']);
}
}
Middleware Priority:
Symfony’s middleware runs after Laravel’s. Use ->prepend() or ->append() in app/Http/Kernel.php to control order.
Route Annotations:
Laravel’s #[Route] is a wrapper around Symfony’s Route. Ensure consistency with Symfony’s requirements and defaults.
Service Container:
Symfony’s HttpKernelInterface is bound as illuminate.contracts.HttpKernel. Override via:
$this->app->bind(HttpKernelInterface::class, function ($app) {
return new CustomKernel($app);
});
How can I help you explore Laravel packages today?