P
Recherches récentes

CodeIgniter 4 : notre choix, nos raisons

17 vues

Le choix d'un framework, un engagement

Le choix d'un framework, ce n'est pas un détail d'architecture : c'est un engagement de plusieurs années. On vit avec ses forces et ses travers à chaque ligne de code, chaque montée de version, chaque recrutement. Mieux vaut le choisir les yeux ouverts.

Quand on a posé les fondations de Punky Tools, on a choisi CodeIgniter 4. Pas par habitude, pas par effet de mode — par conviction. Et trois ans plus tard, c'est toujours la colonne vertébrale de tout ce qu'on construit.

Voici nos raisons, sans langue de bois : la lisibilité, la sobriété, les chiffres, les comparaisons avec les poids lourds du PHP, du code concret — et aussi, honnêtement, ce que ça coûte.

La lisibilité avant la mode

Beaucoup de frameworks PHP empilent les couches d'abstraction jusqu'à ce qu'on ne sache plus ce qui se passe vraiment. Conteneurs d'injection, façades, événements magiques, résolution dynamique… C'est puissant, mais quand un bug surgit, on creuse parfois dix niveaux avant de toucher le sol.

CI4 fait le pari inverse : un cœur léger et explicite, qu'on comprend en lisant le code. Un appel ressemble à un appel. Une route pointe vers un contrôleur, sans intermédiaire occulte. Quand un problème arrive, on remonte la pile sans se perdre.

Ce n'est pas un détail de confort : c'est ce qui rend une base maintenable sur la durée, et transmissible. Un développeur qui arrive sur le projet lit le code et comprend — il n'a pas à décoder une cathédrale d'abstractions avant d'être productif.

L'explicite vieillit mieux que le magique. Le magique impressionne ; l'explicite se dépanne à trois heures du matin.

Un cœur léger : les chiffres

CI4 est sous licence MIT — libre, sans abonnement, sans piège de licence. Sa version 4 initiale date de février 2020 ; on en est aujourd'hui à la branche 4.7 (sortie début 2026), ce qui représente six années de maturation sans rupture brutale. C'est rassurant pour un produit qu'on maintient dans le temps.

Côté socle : PHP 8.2+ requis (et jusqu'à PHP 8.5 supporté), ce qui permet de profiter du JIT, du config caching, du file locator caching et du preloading — autant de gains CPU en production.

Surtout, là où un écosystème « complet » tire des dizaines de paquets Composer transitifs à l'installation, le cœur de CI4 se contente d'une poignée de dépendances. Moins de code tiers, c'est moins de surface d'attaque, moins de conflits de versions, et un démarrage plus rapide. La devise officielle ne ment pas : « le petit framework aux fonctionnalités puissantes ».

Concrètement, sur un serveur OVH/Plesk classique, cette sobriété se traduit par une empreinte mémoire contenue et un temps de bootstrap court — sans avoir besoin d'une infrastructure démesurée.

Le routing : clair et explicite

Le routing donne le ton. Chez CI4, on déclare ses routes à la main, et on lit la table de routage comme un sommaire de l'application. Pas de découverte implicite obscure : ce qui est routé est écrit.

Routes.php
$routes->get('articles', 'ArticleController::index');
$routes->get('articles/(:segment)', 'ArticleController::show/$1');
$routes->post('articles', 'ArticleController::create');

// Groupe protégé par un filtre
$routes->group('starshield', ['filter' => 'session'], function ($routes) {
    $routes->resource('posts');
});

Les placeholders typés ((:segment), (:num)…) gardent les URLs propres, les groupes appliquent un préfixe et un filtre d'un coup, et l'ordre des routes est maîtrisé — les routes spécifiques avant les attrape-tout. C'est limpide, et ça se débogue à l'œil.

Ce caractère explicite a un coût minime à l'écriture, largement compensé par la clarté à la relecture. On sait toujours pourquoi une URL mène quelque part.

L'ORM léger : Model + Query Builder

CI4 ne livre pas un ORM monumental à la Doctrine, ni l'omniprésence d'un Active Record qui cache tout. Il propose un Model simple et un Query Builder qui colle de près au SQL — sans qu'on perde le contrôle de ce qui part vraiment en base.

ArticleModel.php
class ArticleModel extends Model
{
    protected $table         = 'articles';
    protected $primaryKey    = 'id';
    protected $allowedFields = ['title', 'slug', 'body', 'status'];
    protected $useTimestamps = true;
}

// Query Builder : lisible, proche du SQL
$articles = $model
    ->where('status', 'published')
    ->orderBy('published_at', 'DESC')
    ->findAll(10);

On garde la lisibilité d'une requête chainée, mais on peut toujours descendre au SQL brut quand c'est justifié. Pas de surprise de performance cachée derrière une relation « lazy » qui déclenche cent requêtes sans prévenir.

Pour nos besoins — multilingue, relations, traductions — on a bâti nos propres traits par-dessus (jointures, HasTranslations…). Le Model léger de CI4 se laisse étendre sans se battre contre lui.

Les migrations et le schéma

La base de données se versionne comme le code. Les migrations CI4, portées par la classe Forge, décrivent le schéma en PHP : on crée, on modifie, on revient en arrière, et tout le monde travaille sur la même structure.

Migration_Articles.php
public function up()
{
    $this->forge->addField([
        'id'           => ['type' => 'INT', 'unsigned' => true, 'auto_increment' => true],
        'title'        => ['type' => 'VARCHAR', 'constraint' => 255],
        'slug'         => ['type' => 'VARCHAR', 'constraint' => 255],
        'published_at' => ['type' => 'DATETIME', 'null' => true],
    ]);
    $this->forge->addKey('id', true);
    $this->forge->createTable('articles');
}

Couplées aux seeders pour les données de départ, les migrations rendent un nouvel environnement reproductible en une commande Spark. Plus de « ça marche sur ma machine » : la structure est dans le dépôt, point.

C'est un exemple de la philosophie CI4 : un outil simple, qui fait une chose bien, sans imposer un workflow rigide autour.

Shield : l'authentification, sans la réinventer

L'authentification, c'est le genre de brique qu'on ne veut surtout pas écrire soi-même — trop sensible, trop de pièges. CI4 a sa réponse officielle : Shield, maintenu par l'équipe du framework.

auth.php
// Une zone réservée aux connectés
$routes->group('starshield', ['filter' => 'session'], function ($routes) {
    // back-office Punky
});

// Vérifier un droit dans un contrôleur
if (! auth()->user()->can('posts.edit')) {
    return redirect()->back()->with('error', 'Accès refusé');
}

Sessions, tokens d'API, gestion des groupes et des permissions, garde-fous de sécurité : Shield couvre l'essentiel proprement. Chez nous, tout le back-office vit derrière le préfixe starshield, protégé par le filtre de session.

C'est exactement l'équilibre qu'on cherche : une brique officielle, solide, pour ce qui doit l'être — et la liberté de coder nous-mêmes le reste.

Tasks & Queue : l'asynchrone propre

Tout ne doit pas se faire pendant que le visiteur attend. Envoi d'e-mails, génération de miniatures, synchronisation de catalogue, génération de sitemap… ça part en tâche de fond.

queue.php
// Empiler un job asynchrone
service('queue')->push('emails', SendNewsletter::class, [
    'campaignId' => $id,
]);

// Le worker tourne via Spark :
// php spark queue:work emails

CI4 dispose de Tasks (planification type cron) et d'une Queue pour les jobs asynchrones. On empile, un worker dépile : la requête du visiteur reste rapide, et le travail lourd se fait ailleurs.

On a juste appris une subtilité : lancer une commande Spark depuis un contexte HTTP/FPM demande un peu de soin (PHP n'y définit pas les flux CLI). Une fois le pattern en place, c'est réglo.

Performance et sobriété

Un framework léger, c'est un démarrage rapide et une empreinte mémoire contenue. Sur des serveurs OVH/Plesk classiques — pas des clusters à rallonge — ça fait toute la différence entre une page nerveuse et une page qui traîne.

cache.php
$menu = cache('menu:main');

if ($menu === null) {
    $menu = $this->navModel->tree();
    cache()->save('menu:main', $menu, 3600); // 1 h
}

return $menu;

Couplé à Redis et à une vraie discipline de cache, CI4 tient la charge sans infrastructure démesurée. On mémorise ce qui est coûteux mais stable, on invalide finement, et la base respire.

À cela s'ajoutent les optimisations natives de PHP 8.2+ (JIT, preloading) et les caches internes du framework (config, file locator). La sobriété n'est pas qu'une posture : elle se mesure en millisecondes et en factures de serveur.

CI4 face à Laravel et Symfony

Soyons clairs : Laravel est excellent. Son écosystème est immense, sa productivité immédiate, sa communauté énorme. Symfony est une référence d'ingénierie pour les applications d'entreprise complexes. Le « meilleur » framework n'existe pas dans l'absolu — seulement le mieux adapté à un contexte.

CritèreCodeIgniter 4LaravelSymfony
Approcheminimaliste, explicitefull-stack, conventionsmodulaire (bundles)
ORMQuery Builder + Model légerEloquent (Active Record)Doctrine (Data Mapper)
Conteneur / injectionservices explicites (service())conteneur IoC + façadesconteneur DI + autowiring
Dépendances Composerune poignéedes dizaines (transitives)des dizaines de composants
Moteur de templatesvues PHP / BladeOneBladeTwig
Périmètre intégrérouting, migrations, validationqueue, mail, broadcasting, cache…noyau + bundles à la demande
Courbe d'apprentissagerapidemoyenneraide
Empreinte / bootstrapfaibleplus lourdevariable selon config
Idéal pour…maîtrise, maintenance longueproductivité, gros écosystèmeapps d'entreprise complexes

Laravel apporte un périmètre fonctionnel énorme immédiatement — au prix d'une abstraction plus poussée (façades, conteneur IoC) et de dizaines de dépendances transitives. Symfony offre une modularité et une rigueur d'ingénierie incomparables (conteneur DI, autowiring, bundles) — au prix d'une courbe d'apprentissage raide. CI4 vise un autre point d'équilibre : un Query Builder lisible plutôt qu'un ORM lourd, des services explicites plutôt qu'un conteneur omniprésent, assez de structure pour ne pas réinventer la roue, assez de légèreté pour rester maître du code.

Pour un produit dont on contrôle chaque ligne et qu'on fait vivre des années, ce point d'équilibre est exactement celui qu'on voulait.

Et face aux micro-frameworks ?

À l'autre extrême, on trouve les micro-frameworks (type Slim) ou le « PHP nu ». Encore plus légers, encore plus libres — mais il faut alors tout construire : routing structuré, validation, migrations, sécurité, conventions.

CI4 se place pile au bon endroit : il donne une colonne vertébrale (structure, conventions, outils essentiels) sans le poids d'un écosystème complet. On ne repart pas de zéro à chaque projet, et on ne traîne pas non plus une usine à gaz.

C'est ce « juste milieu » qui a emporté la décision : la liberté d'un petit framework, avec les garde-fous d'un grand.

Comment on l'étend : nos modules Punky

La légèreté de CI4 paie surtout quand on étend. Tout Punky Tools est découpé en modules autonomes, sur le namespace Punky\{Module}, qui se branchent sur le cœur sans le modifier.

Module.php
namespace Punky\Blog;

class Module extends PunkyBaseModule
{
    // Un module déclare ses routes, son menu admin
    // et ses providers (sitemap, recherche…)
    public function getSitemapProviders(): array
    {
        return [new PostSitemapProvider()];
    }
}

Chaque module déclare ses routes, son menu admin, ses providers (sitemap, recherche…) via une auto-découverte. Ajouter une fonctionnalité, c'est ajouter un module — pas patcher le cœur. Le modèle suit fidèlement la logique de modules de CI4.

Résultat : on réutilise la même architecture d'un site client à l'autre, et on capitalise. Un nouveau besoin se traduit par une nouvelle brique, qui resservira ailleurs.

Le revers de la médaille

Parce qu'il y en a un, et qu'on préfère le dire. CI4 demande de construire davantage soi-même : moins de paquets « tout-en-un » prêts à l'emploi que dans l'écosystème Laravel, une communauté plus petite, moins de tutoriels pour chaque cas tordu.

Pour nous, c'est un avantage : on maîtrise ce qu'on écrit, on n'hérite pas de comportements qu'on n'a pas choisis, et on sait exactement ce qui tourne en production. Le contrôle a un coût, on le paie volontiers.

Mais soyons justes : pour une équipe qui veut du clé-en-main immédiat, livrer vite avec un maximum de briques déjà faites, ce « à construire soi-même » sera un coût, pas un plaisir. Le bon outil dépend de ce qu'on valorise : la vitesse de départ, ou la maîtrise dans la durée.

Il y a aussi un enjeu d'équipe : le vivier de développeurs Laravel est plus large. On l'assume — on préfère des gens qui aiment comprendre le code à des gens qui cherchent une recette toute faite.

Un socle qui ne nous a pas lâchés

En trois ans, on a traversé plusieurs montées de version (jusqu'à la 4.7) sans réécriture douloureuse. Les changements de comportement sont documentés, les dépréciations annoncées, et la rétrocompatibilité prise au sérieux — ce qui, pour un produit qu'on maintient, vaut de l'or.

On a même rencontré des régressions ponctuelles (un filtre CSRF qui encodait des corps JSON, par exemple) — mais parce que le cœur est lisible, on a diagnostiqué et contourné vite. C'est tout l'intérêt d'un code qu'on peut lire : même ses bugs sont compréhensibles.

La sécurité et la maintenance suivent le rythme de PHP : le framework abandonne les versions de PHP en fin de vie et pousse à rester à jour. Contrainte saine, qui force la bonne hygiène.

Trois ans plus tard

On a fait notre choix en connaissance de cause : un framework lisible, sobre, sans magie noire, qu'on étend par modules et qu'on maîtrise de bout en bout. On en connaît les forces, et on assume le revers.

Et trois ans plus tard, avec Punky Tools en production sur des sites clients et l'agence elle-même, on le referait sans hésiter. La colonne vertébrale tient.

Léger, lisible, sans magie noire. Le meilleur framework, c'est celui dont on comprend chaque ligne.

Partager