Disons tout haut ce que tout le monde pense tout bas : le Makefile de ton projet PHP est une deuxième codebase, écrite dans un langage que personne dans ton équipe ne maîtrise vraiment, tenue par des tabulations et des réponses Stack Overflow de 2011. On se raconte que ça va, parce que c'est « juste de l'automatisation ». Non, ça ne va pas. C'est de la logique non testée, non relue, qui décide de comment tu déploies, migres et reconstruis — et dès qu'il lui faut un if, quelqu'un greffe un script shell et te voilà à maintenir trois langages pour en livrer un seul. Ma position : si ton projet est en PHP, ton task runner devrait l'être aussi, et Castor est le premier outil qui rend cet argument convaincant.

Le pitch est presque suspect tellement il est simple. Tu écris des fonctions PHP ordinaires dans un fichier castor.php, tu leur colles un attribut #[AsTask], et Castor transforme chacune en commande CLI. La signature de la fonction devient l'interface : les paramètres obligatoires deviennent des arguments positionnels, les optionnels deviennent des options, et des attributs comme #[AsArgument] et #[AsOption] te laissent surcharger les défauts quand la convention ne colle pas. Pas de YAML, pas de DSL, pas de boilerplate d'enregistrement. Si tu as déjà câblé une commande Symfony Console à la main, tu as l'impression que quelqu'un a enfin supprimé le cérémonial en gardant le résultat.

Ce qui m'a convaincu, ce n'est pourtant pas la syntaxe — c'est la tuyauterie. Castor est une fine couche au-dessus de composants auxquels la plupart d'entre nous font déjà confiance : Symfony Console pour le CLI, Symfony Process derrière run(), Filesystem et Finder derrière fs(), nikic/php-parser pour découvrir tes tâches, spatie/ssh pour l'exécution distante, jolicode/jolinotif pour les notifications de bureau. Concrètement, ça change tout. Quand ta tâche de rebuild déraille, tu débogues une sémantique Symfony Process que tu comprends déjà, au lieu de deviner pourquoi Make a silencieusement avalé un code de sortie. Et quand une tâche a vraiment besoin de logique — réessayer une migration capricieuse, boucler sur des services, lever une exception sur une mauvaise réponse — tu écris le PHP que tu écrirais n'importe où ailleurs, avec de vraies exceptions au lieu de chaînes de && en croisant les doigts.

L'histoire des dépendances mérite sa propre mention, parce qu'elle règle ce qui empoisonne d'habitude les approches « scripte-le en PHP ». Castor garde son propre manifeste — castor.composer.json et castor.composer.lock — et installe les dépendances des tâches dans .castor/vendor/, complètement à part du composer.json de ton application. Lance castor composer require some/package et ton automatisation obtient un client HTTP ou un parseur YAML sans polluer les dépendances de production ni déclencher une bagarre de platform requirements. Besoin d'un outil CLI ponctuel ? castor execute friendsofphp/php-cs-fixer fix le télécharge et l'exécute sans toucher au moindre manifeste. Cette séparation, c'est la différence entre un jouet et quelque chose que tu peux défendre en code review.

Maintenant, le contre-argument honnête, parce qu'il est solide : Make est partout. Sur chaque runner CI, chaque machine d'ops, chaque poste de collègue, et il y sera encore dans vingt ans. Un make deploy ne demande aucun onboarding ; un castor deploy exige d'abord d'installer un outil. Si ton dépôt passe entre les mains de gens qui n'écrivent pas de PHP — des SRE, des gens du frontend, cet éternel fan de Go — un Makefile est une lingua franca et Castor un dialecte. Je ne vais pas balayer ça d'un revers de main. Mais Castor l'émousse plus que je ne le pensais : l'installateur tient en une ligne de curl depuis castor.jolicode.com, et le flag --static te donne un binaire autonome avec PHP embarqué, si bien que même une image CI nue ou un laptop sans PHP peut lancer tes tâches. L'ubiquité a un vrai coût, mais c'est désormais un coût unique, pas récurrent.

Il y a aussi une question de maturité qu'il faut nommer. Adopter un task runner, c'est parier qu'il survivra à l'automatisation de ton projet, et Make a quarante ans d'avance. Voici pourquoi j'atterris quand même sur Castor pour les équipes PHP : le risque n'est pas vraiment celui de Castor — c'est celui de Symfony. L'outil est surtout de la glu au-dessus de Console, Process, Filesystem et compagnie, des composants avec des bases d'installation énormes et des horizons de support longs. Si Castor disparaissait demain, ton castor.php reste du PHP ordinaire qui appelle des bibliothèques bien documentées ; le migrer prend un après-midi, pas des fouilles archéologiques. Essaie de dire la même chose d'un Makefile de 400 lignes avec des cibles .PHONY qui appellent du Bash qui appelle Docker qui appelle du Bash.

Si tu veux tester l'eau sans réécriture, commence là où la douleur est la plus faible : tes cibles de QA. Un namespace qa avec des tâches phpunit, csFixer et phpstan — chacune une fonction de dix lignes qui shelle via run() — plus une tâche qa:all qui appelle simplement les trois autres comme des fonctions PHP normales, avec des arguments nommés comme csFixer(dryRun: true). Ce dernier détail, c'est la killer feature que personne ne met en avant : la composition de tâches, c'est de la composition de fonctions. Pas d'invocations de sous-make, pas de rituels de passage de variables, juste du PHP qui appelle du PHP. Une fois que ton équipe y a goûté, les cibles Docker suivent toutes seules, et watch() qui relance les tâches au moindre changement de fichier remplace l'incantation entr que tu copiais-collais d'un projet à l'autre.

Alors voilà où j'attends tes objections, parce que je sais que cette chronique va agacer les loyalistes de Make — et certains d'entre vous ont des Makefiles qui méritent vraiment cette loyauté. Où mets-tu la limite ? Existe-t-il un seuil de taille ou de complexité en dessous duquel l'ubiquité de Make bat les tâches natives du langage, ou penses-tu que les équipes polyglottes rendent un runner PHP-only rédhibitoire, aussi bon soit-il ? Raconte-moi le Makefile que tu n'abandonnerais jamais — ou celui qui t'a fait craquer pour de bon.