Vue d’ensemble du Slim Application Skeleton introduit dans Laravel 11, avec l’implémentation interne allant de Application::configure() à ApplicationBuilder.
Laravel 11 introduit le « Slim Application Skeleton », une refonte majeure de la structure d’application. Le changement le plus important est la suppression de la dispersion de la configuration au profit d’une centralisation dans un seul endroit : bootstrap/app.php.
Élément
Laravel 10 et antérieur
Laravel 11+
Kernel HTTP
app/Http/Kernel.php
Supprimé (intégré au framework)
Kernel Console
app/Console/Kernel.php
Supprimé (déplacé dans routes/console.php)
Gestionnaire d’exceptions
app/Exceptions/Handler.php
Supprimé (centralisé dans bootstrap/app.php)
Service providers
5 fichiers
Un seul fichier AppServiceProvider.php
Fichiers de routes
web.php / api.php par défaut
Seul web.php par défaut, api.php en opt-in
Bootstrap
Configuration dispersée dans plusieurs fichiers
Centralisée dans bootstrap/app.php
Base de données par défaut
MySQL/PostgreSQL
SQLite
Ces changements concernent les nouveaux projets. Les applications Laravel 10 existantes mises à niveau continuent de fonctionner avec l’ancienne structure telle quelle.
Ce seul fichier permet de configurer le routage, les middlewares et la gestion des exceptions. La configuration qui était dispersée dans app/Http/Kernel.php, app/Console/Kernel.php et app/Exceptions/Handler.php sous Laravel 10 est ici centralisée.
Ce fichier est l’endroit où enregistrer les service providers. Sous Laravel 10, ils étaient déclarés dans le tableau providers de config/app.php, mais ils sont désormais séparés dans bootstrap/providers.php. Par défaut sous Laravel 11, seul AppServiceProvider est présent.
Lorsque vous installez un package avec composer require, celui-ci peut mettre à jour automatiquement bootstrap/providers.php. config/app.php n’est pas devenu obsolète, mais l’emplacement recommandé pour les nouveaux enregistrements est désormais bootstrap/providers.php.
Le kernel HTTP a été intégré dans Illuminate\Foundation\Http\Kernel au sein du framework. La personnalisation des middlewares se fait via withMiddleware() dans bootstrap/app.php.
Les deux responsabilités du kernel Console ont été séparées. Les commandes Artisan sont placées dans app/Console/Commands/ et détectées automatiquement, et les tâches planifiées sont écrites dans routes/console.php.
// Laravel 10 et antérieur : app/Console/Kernel.phpprotected function schedule(Schedule $schedule): void{ $schedule->command('emails:send')->daily();}
Le gestionnaire d’exceptions a été intégré dans Illuminate\Foundation\Exceptions\Handler au sein du framework. La personnalisation se fait via withExceptions() dans bootstrap/app.php.
// Laravel 10 et antérieur : app/Exceptions/Handler.phppublic function register(): void{ $this->reportable(function (InvalidOrderException $e) { // ... });}
Application::configure() est une méthode statique de Illuminate\Foundation\Application.
// Extrait de Illuminate\Foundation\Applicationpublic static function configure(?string $basePath = null){ $basePath = match (true) { is_string($basePath) => $basePath, default => static::inferBasePath(), }; return (new Configuration\ApplicationBuilder(new static($basePath))) ->withKernels() ->withEvents() ->withCommands() ->withProviders();}
Cette méthode effectue les opérations suivantes :
Détermine le répertoire racine de l’application à partir de basePath
Crée une instance de Application
L’enveloppe dans ApplicationBuilder et applique les paramètres par défaut
Retourne l’instance de ApplicationBuilder
Le point important est que withKernels() / withEvents() / withCommands() / withProviders()sont déjà appelées à l’intérieur de configure(). Il n’est pas nécessaire de les appeler à nouveau dans bootstrap/app.php.
Application::configure(basePath: dirname(__DIR__)) // retourne ApplicationBuilder ->withRouting(...) // configure le routage et retourne $this ->withMiddleware(...) // configure les middlewares et retourne $this ->withExceptions(...) // configure la gestion des exceptions et retourne $this ->create(); // retourne l'instance Application
L’appel de create() extrait l’instance Application depuis ApplicationBuilder, et c’est cette instance Application que bootstrap/app.php retourne (return).
Flux depuis la requête jusqu’au démarrage de l’application
public/index.php sert de point d’entrée, charge bootstrap/app.php et récupère l’Application. Le HTTP Kernel fait ensuite passer la requête par le pipeline de middlewares, puis le routeur la dispatche vers le contrôleur.
En interne, un callback est enregistré via AppRouteServiceProvider::loadRoutesUsing(), puis AppRouteServiceProvider est enregistré au moment du booting de l’application.
// Traitement interne de withRouting() (version simplifiée)protected function buildRoutingCallback(...){ return function () use ($web, $api, $pages, $health, $apiPrefix, $then) { if (is_string($api) || is_array($api)) { Route::middleware('api')->prefix($apiPrefix)->group($api); } if (is_string($health)) { Route::get($health, function () { Event::dispatch(new DiagnosingHealth); return response(View::file(...), status: 200); }); } if (is_string($web) || is_array($web)) { Route::middleware('web')->group($web); } if (is_callable($then)) { $then($this->app); } };}
Points clés :
Les routes api reçoivent automatiquement le groupe de middlewares api et le préfixe /api
En passant une chaîne à health, un endpoint de health check (par défaut /up) est automatiquement enregistré
Le chemin health est exclu même en mode maintenance (configuré via PreventRequestsDuringMaintenance::except())
Notez que api est enregistré avant web. Si vous définissez des routes web et API sur le même chemin, la route API est prioritaire
En passant une chaîne à pages, le routage Laravel Folio est activé
withMiddleware() — personnalisation des middlewares
public function withMiddleware(?callable $callback = null){ $this->app->afterResolving(HttpKernel::class, function ($kernel) use ($callback) { $middleware = (new Middleware) ->redirectGuestsTo(fn () => route('login')); if (! is_null($callback)) { $callback($middleware); } $kernel->setGlobalMiddleware($middleware->getGlobalMiddleware()); $kernel->setMiddlewareGroups($middleware->getMiddlewareGroups()); $kernel->setMiddlewareAliases($middleware->getMiddlewareAliases()); // ... }); return $this;}
withMiddleware() exécute le callback après que HttpKernel ait été résolu. C’est parce qu’il utilise le hook afterResolving(). L’objet Middleware passé au callback propose de nombreuses méthodes de personnalisation.
->withMiddleware(function (Middleware $middleware) { // Ajouter un middleware global $middleware->append(MyGlobalMiddleware::class); // Ajouter un middleware au groupe web $middleware->web(append: [EnsureUserIsSubscribed::class]); // Remplacer un middleware dans le groupe api $middleware->api(replace: [ OldMiddleware::class => NewMiddleware::class, ]); // Configurer les exclusions CSRF $middleware->validateCsrfTokens(except: ['stripe/*', 'webhook/*']); // Modifier la redirection des utilisateurs non authentifiés $middleware->redirectGuestsTo('/custom-login'); // Définir l'ordre de priorité des middlewares $middleware->priority([ \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, ]);})
withExceptions() — configuration de la gestion des exceptions
public function withExceptions(?callable $using = null){ $this->app->singleton( \Illuminate\Contracts\Debug\ExceptionHandler::class, \Illuminate\Foundation\Exceptions\Handler::class ); if ($using !== null) { $this->app->afterResolving( \Illuminate\Foundation\Exceptions\Handler::class, fn ($handler) => $using(new Exceptions($handler)), ); } return $this;}
withExceptions() enregistre la classe Handler du framework en tant que singleton, puis configure le callback via afterResolving(). Un objet wrapper Exceptions est passé au callback.
->withExceptions(function (Exceptions $exceptions) { // Ne pas reporter certaines exceptions $exceptions->dontReport(MissedFlightException::class); // Rapport personnalisé pour certaines exceptions $exceptions->report(function (InvalidOrderException $e) { // Notification Slack par exemple }); // Personnaliser la réponse HTTP pour certaines exceptions $exceptions->render(function (NotFoundHttpException $e, Request $request) { if ($request->is('api/*')) { return response()->json(['message' => 'Not Found'], 404); } }); // Throttling (ne pas reporter la même exception en boucle) $exceptions->throttle(function (Throwable $e) { return Limit::perMinute(20); });})
withProviders() — enregistrement des service providers
public function withProviders(array $providers = [], bool $withBootstrapProviders = true){ RegisterProviders::merge( $providers, $withBootstrapProviders ? $this->app->getBootstrapProvidersPath() : null ); return $this;}
withProviders() est appelée par défaut à l’intérieur de Application::configure(), donc bootstrap/providers.php est chargé automatiquement. Pour passer des providers supplémentaires, il faut l’appeler explicitement dans bootstrap/app.php.
Application::configure(basePath: dirname(__DIR__)) ->withProviders([ // Ajouter des providers en plus du bootstrap/providers.php par défaut App\Providers\CustomServiceProvider::class, ]) ->withRouting(...) ->create();
Passer withBootstrapProviders: false empêche le chargement de bootstrap/providers.php. Sauf raison particulière, omettez cet argument.
Sous Laravel 10 et antérieur, app/Http/Kernel.php utilisait un style de listing des middlewares dans des tableaux. C’était proche d’un fichier de configuration, avec pour inconvénient un faible support du système de types PHP et des IDE.Laravel 11 est passé à un style callback withMiddleware(function (Middleware $middleware) { ... }). Cela permet la complétion typée et une écriture naturelle de configurations dynamiques utilisant conditions, boucles et autre logique.
De la « convention plutôt que configuration » à la « configuration explicite »
Rendre api.php opt-in a permis de résoudre le fait que le groupe de middlewares api était toujours chargé même dans les applications n’utilisant pas de routes API. Le principe est : les fonctionnalités non utilisées n’existent pas par défaut.
Utiliser afterResolving() dans withMiddleware() et withExceptions() permet d’éviter les problèmes d’ordre de configuration. Les méthodes de ApplicationBuilder sont appelées avant le démarrage complet de l’application, mais le traitement réel (application des paramètres au kernel) est exécuté de manière différée lors de la première résolution du kernel.
->withMiddleware(function (Middleware $middleware) { // Ajouter un middleware de vérification d'authentification aux routes web $middleware->web(append: [ \App\Http\Middleware\EnsureEmailIsVerified::class, ]); // Exclure certains middlewares des routes API $middleware->api(remove: [ \Illuminate\Session\Middleware\StartSession::class, ]); // Exclure les endpoints webhook du CSRF $middleware->validateCsrfTokens(except: [ 'webhook/*', 'stripe/webhook', ]); // Définir un alias pour certains middlewares $middleware->alias([ 'subscribed' => \App\Http\Middleware\EnsureUserIsSubscribed::class, ]);})
Les méthodes registered(), booting() et booted() de ApplicationBuilder ne font qu’enregistrer des callbacks sur l’Application. Habituellement, il n’est pas nécessaire de les utiliser, mais elles permettent des opérations spéciales comme modifier le Kernel dont la préparation est terminée via booted().