Avec un rythme désormais annuel, Laravel 13 est prévu pour le premier trimestre de cette année. Il est temps de faire un peu le point de ce qui va arriver. Ne vous attendez pas à une révolution, mais plutôt une simple évolution. Pas d'annonce extraordinaire, mais quelques éléments intéressants. Ce qui est visé est plutôt la stabilité et la sécurité. Pour Laravel 12, la correction des bugs sera assurée jusqu'en août 2026 et pour la sécurité jusqu'en février 2027. Donc pas d'urgence pour migrer vos applications !
Pour cet article, je me suis inspiré de celui-ci.
PHP 8.3
La version 12 se contentait de PHP 8.2. Avec la version 13, le minimum sera PHP 8.3. On va y gagner en performances.
Typage amélioré
- Nouvelle fonction pour valider la syntaxe JSON sans charger les données (plus efficace que json_decode) : json_validate()
- Typage plus précis pour unserialize() : peut maintenant être annoté avec array ou list.
- Constantes typées : amélioration du support des constantes de classe typées.
Classes et méthodes
- #[Override] attribute : permet de marquer explicitement les méthodes qui redéfinissent une méthode parente.
- Clonage profond (__clone) : meilleur support pour le clonage profond via __clone dans les classes readonly.
Nouvelles fonctions et extensions
- mb_str_pad() : Equivalent multioctet de str_pad().
- ldap_exop_sync() et ldap_connect_wallet() : nouvelles fonctions LDAP.
- posix_sysconf(), posix_pathconf() : nouvelles fonctions POSIX.
- Nouvelle extension Random : API améliorée pour la génération de nombres aléatoires.
Améliorations de performance
- Optimisations du JIT (Just-In-Time compiler).
- Améliorations de l'allocation mémoire pour les chaînes.
- Réduction de l'empreinte mémoire dans certains cas.
Laravel reverb
Laravel Reverb est le serveur WebSocket officiel de Laravel pour les applications en temps réel. Avant Reverb, les développeurs Laravel devaient utiliser des solutions tierces comme Pusher, Soketi, ou configurer manuellement un serveur WebSocket.
Fonctionnalités clés de Reverb
- Intégration native avec l'écosystème Laravel
- Broadcasting (diffusion) en temps réel
- Channels (canaux) : public, privé, de présence
- Haute performance (écrit en PHP avec extension PECL)
- Support SSL/TLS natif
Problème avant Laravel 13
Pour scaler Reverb horizontalement (plusieurs serveurs), vous deviez utiliser Redis :
Serveur 1 ↔ Redis ↔ Serveur 2
↑ ↑ ↑
Clients État partagé Clients
Redis stockait :
- Les connexions WebSocket actives
- Les messages en cours
- L'état des canaux
Solution avec Laravel 13 : driver database
Configuration simplifiée :
// config/reverb.php
'driver' => env('REVERB_DRIVER', 'database'), // 'redis' ou 'database'
'connections' => [
'database' => [
'table' => 'reverb_messages',
],
],
# .env
REVERB_DRIVER=database
REVERB_APP_ID=mon_app
REVERB_APP_KEY=app_key
REVERB_APP_SECRET=app_secret
Migration nécessaire :
php artisan reverb:install
Cela crée la table reverb_messages pour stocker l'état.
Architecture avec database driver :
[Client 1] ↔ [Serveur Reverb 1] ↔ [Base de données]
[Client 2] ↔ [Serveur Reverb 2] ↔ [Base de données]
↑ ↑ ↑
WebSocket Même DB partagée Stocke l'état
Workflow typique :
- Connexion WebSocket : le client se connecte à un serveur Reverb
- Stockage DB : le serveur enregistre la connexion dans la DB
- Broadcast événement :
event(new ChatMessage($message));
- Distribution : tous les serveurs lisent la DB et envoient aux clients concernés
Avantages du driver database
Pour les petites/moyennes applications :
- Moins de dépendances : pas besoin de Redis
- Setup simplifié : une seule base de données
- Coût réduit : évite un service Redis externalisé
- Maintenance unique : tout dans votre DB principale
Scénarios idéaux :
- Applications avec < 10,000 connexions concurrentes
- Chat interne d'entreprise
- Outils collaboratifs
- Tableaux de bord en temps réel
- Notifications live
Le renouvellement intelligent du cache
Le problème traditionnel
Avant Laravel 13, pour prolonger la durée de vie d'une entrée cache :
// Méthode classique (inefficace)
$data = Cache::get('user:123:preferences'); // 1. Lecture
if ($data) {
Cache::put('user:123:preferences', $data, now()->addHours(2)); // 2. Ré-écriture
}
Problèmes :
- 2 opérations (get + put)
- Double transfert réseau si cache distant (Redis, Memcached)
- Données resérialisées inutilement
La solution : Cache::touch()
// Nouvelle méthode en Laravel 13
Cache::touch('user:123:preferences', now()->addHours(2));
Une seule opération atomique qui :
- Étend le TTL sans toucher aux données
- Évite la resérialisation
- Réduit le trafic réseau de moitié
Cas d'usage pratiques
Sessions utilisateur "toujours actives"
// Middleware pour sessions actives
class KeepSessionAlive
{
public function handle($request, $next)
{
$response = $next($request);
if (Auth::check()) {
// Extend session cache sans recharger les données
Cache::touch('session:'.Auth::id(), now()->addHour());
}
return $response;
}
}
Rate limiting intelligent
// Garder le compteur "actif" si l'utilisateur est actif
public function trackApiRequest($userId)
{
$key = "api_requests:{$userId}";
// Incrémenter le compteur
$count = Cache::increment($key);
// Réinitialiser le TTL à chaque requête
Cache::touch($key, now()->addMinutes(5));
return $count;
}
Panier d'achat qui n'expire pas
// Quand l'utilisateur modifie son panier
public function updateCart($userId, $item)
{
$cartKey = "cart:{$userId}";
// Mettre à jour le panier
$cart = $this->addToCart($userId, $item);
// Réinitialiser le timer d'expiration (30 jours)
Cache::touch($cartKey, now()->addDays(30));
return $cart;
}
Différence avec Cache::put()
| Aspect | Cache::put() | Cache::touch() |
| Données | Remplace les données | Garde les données intactes |
| Performance | Lecture + Écriture | Commande unique |
| Usage | Nouvelle valeur | Prolongation d'existence |
| Réseau |
2 allers-retours | 1 aller-retour |
// SCÉNARIO : Utilisateur actif sur une page
// Ancienne approche (inefficace)
public function updateLastSeen($userId)
{
$profile = Cache::get("user:{$userId}:profile");
Cache::put("user:{$userId}:profile", $profile, now()->addHour());
// Inutile : on n'a pas modifié le profil !
}
// Nouvelle approche (optimale)
public function updateLastSeen($userId)
{
// Juste prolonger l'existence
Cache::touch("user:{$userId}:profile", now()->addHour());
}
Améliorations du routage par sous-domaine
Le problème avant Laravel 13
// web.php ou api.php - Routage complexe
Route::domain('{account}.example.com')->group(function () {
Route::get('/', function ($account) {
// Gestion manuelle
$tenant = Tenant::where('subdomain', $account)->firstOrFail();
// Configuration contextuelle manuelle
Config::set('database.connections.tenant.database', $tenant->database);
return view('tenant.dashboard', compact('tenant'));
});
});
Nouvelle approche Laravel 13
Configuration centralisée
// config/domains.php (nouveau fichier)
return [
'admin' => [
'domain' => 'admin.example.com',
'paths' => [
'routes/admin.php',
'routes/api.php',
],
'middleware' => ['web', 'auth:admin'],
],
'tenant' => [
'domain' => '{account}.example.com',
'paths' => [
'routes/tenant.php',
],
'bindings' => [
'account' => \App\Models\Tenant::class,
],
'middleware' => ['web', 'tenant'],
],
];
Injection automatique de modèle
// routes/tenant.php
Route::get('/dashboard', function (Tenant $account) {
// $account est automatiquement résolu via le sous-domaine
return view('dashboard', ['tenant' => $account]);
});
Route::get('/users', [UserController::class, 'index']);
// Le Tenant est automatiquement disponible dans le contrôleur
Middleware contextuel automatique
// Pas besoin de définir manuellement
// Laravel applique automatiquement le middleware configuré
// et prépare l'environnement (DB, config, etc.)
Comment upgrader vers Laravel 13 ?
Les upgrades de Laravel sont souvent bloquées par des problèmes récurrents :
- Packages tiers incompatibles (50% des échecs)
- Changements breaking non détectés
- Tests qui passent, mais bugs en production
Voyons une stratégie qui aborde ces problèmes de manière systématique.
Etape 1 : les packages
composer outdated --direct
# Affiche uniquement vos packages directs (pas les dépendances des dépendances)
composer outdated --direct
# Exemple de sortie :
vendor/package current latest
laravel/framework v10.28.0 v10.29.0 # OK
spatie/laravel-permission 5.5.0 6.0.0 # Attention !
maatwebsite/excel 3.1.48 4.0.0 # Risque de breaking changes
Pourquoi --direct ?
- Vous contrôlez directement ces packages
- Les dépendances indirectes seront mises à jour avec leurs parents
composer why-not php 8.3
# Identifie les packages qui bloquent PHP 8.3
composer why-not php 8.3
# Exemple :
spatie/laravel-backup ^7.0 requires php ^8.1 -> your php version (8.3.0) does not satisfy that requirement.
# En réalité, c'est l'inverse : le package n'est PAS compatible 8.3
Actions concrètes à faire
# 1. Créer un fichier d'audit
echo "# Audit d'upgrade Laravel 13 - $(date)" > upgrade-audit.md
# 2. Lister tous les packages avec leur compatibilité
composer show --direct | grep -E "(laravel|spatie|maatwebsite)" >> upgrade-audit.md
# 3. Vérifier GitHub pour chaque package critique
PACKAGES=("spatie/laravel-permission" "barryvdh/laravel-debugbar" "laravel/telescope")
for pkg in "${PACKAGES[@]}"; do
echo "=== $pkg ===" >> upgrade-audit.md
# Vérifier les releases récentes
curl -s "https://api.github.com/repos/${pkg/\//\/}/releases/latest" | \
jq -r '.tag_name, .created_at' >> upgrade-audit.md
done
Étape 2 : rector
Qu'est-ce que Rector ?
Un outil qui réécrit automatiquement votre code PHP pour :
- Corriger du code déprécié
- Appliquer les nouvelles syntaxes
- Adapter aux breaking changes
Installation pas à pas
# 1. Installer Rector (en dev seulement)
composer require rector/rector --dev
# 2. Installer les règles spécifiques à Laravel
composer require driftingly/rector-laravel --dev
# 3. Initialiser la configuration
vendor/bin/rector init
# Crée un fichier rector.php à la racine
Configuration optimale pour Laravel 13
// rector.php
<?php
declare(strict_types=1);
use Rector\Config\RectorConfig;
use RectorLaravel\Set\LaravelSetList;
use Rector\Set\ValueObject\SetList;
return RectorConfig::configure()
->withPaths([
__DIR__ . '/app',
__DIR__ . '/config',
__DIR__ . '/database',
__DIR__ . '/routes',
__DIR__ . '/tests',
])
->withSkip([
// Exclure les fichiers générés
__DIR__ . '/vendor',
__DIR__ . '/storage',
__DIR__ . '/bootstrap/cache',
// Exclure des règles spécifiques si nécessaire
Rector\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector::class,
])
->withSets([
// Règles PHP générales
SetList::PHP_83,
// Règles spécifiques Laravel
LaravelSetList::LARAVEL_130,
// Règles de code quality (optionnel)
SetList::CODE_QUALITY,
SetList::DEAD_CODE,
])
->withComposerBased(
laravel: true, // Détecte automatiquement la version Laravel
composerJsonPath: __DIR__ . '/composer.json'
);
Exécution en sécurité
# 1. Voir ce qui va changer (SANS modifier)
vendor/bin/rector process --dry-run --ansi
# Sortie exemple :
[file] app/Http/Middleware/TrustHosts.php
[applied] Rector\Php81\Rector\Property\ReadOnlyPropertyRector
--- original
+++ new
@@ -8,7 +8,7 @@
*/
class TrustHosts extends Middleware
{
- private $subdomains;
+ private readonly array $subdomains;
# 2. Appliquer les changements
vendor/bin/rector process
# 3. Vérifier le diff
git diff --stat
# 15 fichiers modifiés, 45 insertions(+), 32 deletions(-)
Étape 3 : pyramide des tests
1. Unit Tests d'abord (rapides & isolés)
# Lancer uniquement les tests unitaires
php artisan test --testsuite=Unit --parallel
# AVANTAGE : Détecte immédiatement :
# - Problèmes de typage PHP 8.3
# - Constants typées incorrectes
# - Méthodes dépréciées dans le code business
Exemple de test qui échoue :
// app/Models/User.php
class User extends Authenticatable
{
public const string STATUS_ACTIVE = 'active'; // PHP 8.3
// ...
}
// tests/Unit/UserTest.php
public function test_status_constant()
{
// Ce test échouera IMMÉDIATEMENT si problème de type
$this->assertSame('active', User::STATUS_ACTIVE);
}
2. Feature Tests ensuite (Intégration)
# Tester les endpoints HTTP, auth, DB
php artisan test --testsuite=Feature --stop-on-failure
# Ce qui est testé :
# Routes Laravel 13
# Middleware
# Validation
# Sessions
# Cache
# Database (avec le nouveau driver Reverb si utilisé)
Scénario critique à tester :
// tests/Feature/AuthTest.php
public function test_login_with_new_password_requirements()
{
// Laravel 13 peut avoir de nouvelles règles de password
$response = $this->post('/register', [
'password' => 'short', // Peut échouer avec nouvelles règles
]);
$response->assertSessionHasErrors('password');
}
3. Browser Tests en dernier (lents mais complets)
# Dernière ligne de défense
php artisan dusk
# Teste :
# JavaScript + Laravel
# WebSockets (Reverb)
# Assets compilés
# Authentification frontend
Checklist finale avant déploiement
Package Audit
Tous les packages supportent PHP 8.3
Tous les packages supportent Laravel 13
Alternatives identifiées pour les packages bloquants
Code Changes
Rector exécuté sans erreurs
Code review des changements automatiques
Tests manuels sur les fonctionnalités critiques
Testing
Unit tests: 100% pass
Feature tests: 100% pass
Browser tests: exécutés sur les flows critiques
Performance test (cache, Reverb, etc.)
Infrastructure
PHP 8.3 installé sur les serveurs
Extensions PHP nécessaires (pour Reverb, etc.)
Configuration cache vérifiée (Redis/DB)
Backup de la base de données
Conclusion
Même s'il n'y a rien de vraiment marquant, Laravel 13 marque une étape importante dans l'évolution du framework, renforçant sa philosophie centrale : rendre le développement web élégant, productif et accessible.
Les trois piliers de Laravel 13
1. L'autonomie renforcée
- Reverb avec driver Database : plus besoin de Redis pour du temps réel à moyenne échelle
- Écosystème intégré : réduction des dépendances externes pour les fonctionnalités courantes
- Batteries incluses : des WebSockets au cache avancé, tout est dans la boîte
2. La performance intelligente
- Cache::touch() : optimisation fine des opérations de cache sans transfert inutile
- Améliorations du routage : injection automatique et configuration déclarative
- Optimisations PHP 8.3 : exploitation des dernières capacités du langage
3. L'évolutivité accessible
- Scalabilité graduelle : de la base de données unique au cluster Redis
- Architecture modulaire : routage par domaine natif pour les applications complexes
- Migration facilitée : des outils comme Rector qui transforment l'upgrade en processus fluide
Par bestmomo
Aucun commentaire