Panoramica completa dello Slim Application Skeleton introdotto in Laravel 11 e implementazione interna da Application::configure() ad ApplicationBuilder.
Con Laravel 11 la struttura dell’applicazione è stata rinnovata in modo profondo, sotto il nome di “Slim Application Skeleton”. Il cambiamento più grande è la fine della dispersione della configurazione: tutto confluisce in un unico bootstrap/app.php.
Elemento
Prima di Laravel 10
Da Laravel 11
Kernel HTTP
app/Http/Kernel.php
Rimosso (integrato nel framework)
Kernel Console
app/Console/Kernel.php
Rimosso (spostato in routes/console.php)
Exception handler
app/Exceptions/Handler.php
Rimosso (concentrato in bootstrap/app.php)
Service provider
5 file
Un solo AppServiceProvider.php
File delle rotte
web.php / api.php di default
Solo web.php di default, api.php opt-in
Bootstrap
Configurazione sparsa su più file
Concentrata in bootstrap/app.php
DB di default
MySQL/PostgreSQL
SQLite
Il cambiamento riguarda i nuovi progetti. L’aggiornamento di app Laravel 10 esistenti mantiene funzionante la vecchia struttura.
In questo unico file configuri routing, middleware e gestione delle eccezioni. Ciò che in Laravel 10 era diviso tra app/Http/Kernel.php, app/Console/Kernel.php e app/Exceptions/Handler.php è ora concentrato qui.
Questo è il file dove si registrano i service provider. In Laravel 10 lo indicavi nell’array providers di config/app.php: ora è stato spostato in bootstrap/providers.php. Nel default di Laravel 11 c’è solo AppServiceProvider.
Installando pacchetti con composer require, il pacchetto può aggiornare automaticamente bootstrap/providers.php. config/app.php non è ignorato, ma per nuove registrazioni il posto consigliato è ora bootstrap/providers.php.
Il Kernel HTTP è stato integrato in Illuminate\Foundation\Http\Kernel. La personalizzazione dei middleware si fa in bootstrap/app.php con withMiddleware().
I due ruoli del Kernel della console sono stati separati. I comandi Artisan vanno in app/Console/Commands/ (auto-scoperti), lo schedule si scrive in routes/console.php.
// Laravel 10 e precedenti: app/Console/Kernel.phpprotected function schedule(Schedule $schedule): void{ $schedule->command('emails:send')->daily();}
// da Laravel 11: routes/console.phpuse Illuminate\Support\Facades\Schedule;Schedule::command('emails:send')->daily();
Rimozione di app/Exceptions/Handler.php
L’exception handler è stato integrato in Illuminate\Foundation\Exceptions\Handler. La personalizzazione si fa in bootstrap/app.php con withExceptions().
// Laravel 10 e precedenti: app/Exceptions/Handler.phppublic function register(): void{ $this->reportable(function (InvalidOrderException $e) { // ... });}
Application::configure() è un metodo statico di Illuminate\Foundation\Application.
// da 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();}
Il metodo esegue questi passaggi.
Determina la root dell’app da basePath
Crea l’istanza Application
La avvolge con ApplicationBuilder applicando le configurazioni di default
Restituisce l’istanza ApplicationBuilder
Un dettaglio importante: withKernels() / withEvents() / withCommands() / withProviders() sono già stati chiamati dentro configure(). Non serve richiamarli in bootstrap/app.php.
Dal ricevimento della richiesta all’avvio dell’app
public/index.php è l’entry point: legge bootstrap/app.php e ottiene l’Application. Poi il Kernel HTTP fa passare la richiesta nella pipeline dei middleware e il router la dispatcha al controller.
withMiddleware() — personalizzazione dei middleware
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() esegue la callback dopo che HttpKernel è stato risolto: usa infatti l’hook afterResolving(). L’oggetto Middleware passato alla callback ha molti metodi di personalizzazione.
->withMiddleware(function (Middleware $middleware) { // aggiungi un middleware globale $middleware->append(MyGlobalMiddleware::class); // aggiungi al gruppo web $middleware->web(append: [EnsureUserIsSubscribed::class]); // sostituisci un middleware nel gruppo api $middleware->api(replace: [ OldMiddleware::class => NewMiddleware::class, ]); // esclusioni CSRF $middleware->validateCsrfTokens(except: ['stripe/*', 'webhook/*']); // cambia il redirect per gli utenti non autenticati $middleware->redirectGuestsTo('/custom-login'); // priorità dei middleware $middleware->priority([ \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, ]);})
withExceptions() — configurazione della gestione eccezioni
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() registra la classe Handler del framework come singleton e imposta la callback tramite afterResolving(). Alla callback viene passato un wrapper Exceptions.
->withExceptions(function (Exceptions $exceptions) { // non riportare eccezioni specifiche $exceptions->dontReport(MissedFlightException::class); // report personalizzato per un'eccezione specifica $exceptions->report(function (InvalidOrderException $e) { // notifica su Slack, ecc. }); // personalizza la response HTTP per un'eccezione specifica $exceptions->render(function (NotFoundHttpException $e, Request $request) { if ($request->is('api/*')) { return response()->json(['message' => 'Not Found'], 404); } }); // throttling (non riportare la stessa eccezione ripetuta) $exceptions->throttle(function (Throwable $e) { return Limit::perMinute(20); });})
withProviders() — registrazione dei service provider
public function withProviders(array $providers = [], bool $withBootstrapProviders = true){ RegisterProviders::merge( $providers, $withBootstrapProviders ? $this->app->getBootstrapProvidersPath() : null ); return $this;}
withProviders() viene chiamato di default dentro Application::configure(), quindi bootstrap/providers.php viene caricato automaticamente. Se vuoi passare provider aggiuntivi devi chiamarlo esplicitamente in bootstrap/app.php.
Application::configure(basePath: dirname(__DIR__)) ->withProviders([ // aggiungi provider oltre a quelli di bootstrap/providers.php di default App\Providers\CustomServiceProvider::class, ]) ->withRouting(...) ->create();
Passando withBootstrapProviders: false disabiliti il caricamento di bootstrap/providers.php. Non farlo se non hai un motivo specifico.
In Laravel 10 app/Http/Kernel.php elencava i middleware in array. È una scrittura vicina a un file di configurazione, poco supportata dal type system PHP e dagli IDE.Con Laravel 11 si è passati a uno stile a callback come withMiddleware(function (Middleware $middleware) { ... }). In questo modo ottieni autocompletamento dei tipi e puoi scrivere in modo naturale logica dinamica con condizioni e cicli.
Da “convention over configuration” a “configurazione esplicita”
api.php è diventato opt-in: nelle app che non usano API non ha più senso caricare sempre il gruppo api. Filosofia: ciò che non usi non è presente di default.
withMiddleware() e withExceptions() usano afterResolving() per evitare problemi d’ordine nella configurazione. I metodi di ApplicationBuilder vengono chiamati prima che l’app sia completamente avviata, ma le azioni concrete (applicare le impostazioni al Kernel) vengono differite fino alla prima risoluzione del Kernel.
->withMiddleware(function (Middleware $middleware) { // aggiungi controllo di verifica alle rotte web $middleware->web(append: [ \App\Http\Middleware\EnsureEmailIsVerified::class, ]); // rimuovi un middleware nelle rotte api $middleware->api(remove: [ \Illuminate\Session\Middleware\StartSession::class, ]); // escludi gli endpoint webhook dal CSRF $middleware->validateCsrfTokens(except: [ 'webhook/*', 'stripe/webhook', ]); // imposta un alias per un middleware specifico $middleware->alias([ 'subscribed' => \App\Http\Middleware\EnsureUserIsSubscribed::class, ]);})
registered(), booting() e booted() di ApplicationBuilder si limitano a registrare callback nell’Application. Di solito non ti servono, ma consentono operazioni particolari, come modificare il Kernel a booted() quando i preparativi di avvio sono completi.