Mago: linter, formatter y analyzer de PHP en un solo bicho escrito en Rust
Si llevas tiempo trabajando con PHP, seguro que tienes tu combinación de herramientas montada: 💡 PHPStan Analizador estático de PHP que encuentra errores sin ejecutar el código. Configurable por niveles (0-9), donde 9 es el más estricto. Más info → para el análisis estático, 💡 PHP-CS-Fixer Herramienta para formatear código PHP automáticamente según estándares como PSR-12 o PER-CS. Más info → para el formateo, quizá Psalm Psalm Otro analizador estático de PHP desarrollado por Vimeo. Similar a PHPStan pero con su propio enfoque de tipos. para algún proyecto… Cada una por su lado, cada una con su configuración, cada una arrancando su propio proceso PHP.
Funciona genial pero a veces puede llegar a ser lento. Sobre todo en proyectos gordos. Esperar un minuto largo a que PHPStan acabe de analizar tu código no es lo más motivante del mundo.
Pues resulta que ha aparecido una herramienta que intenta juntar todo eso en un solo binario escrito en 💡 Rust Lenguaje de programación de sistemas conocido por su velocidad, seguridad de memoria y concurrencia. Usado en herramientas como ripgrep, SWC o Deno. Más info → . Se llama Mago, y la idea es bastante ambiciosa: linter + formatter + analizador estático, todo en uno y muy rápido.
¿Suena demasiado bonito? Pos vamos a echarle un ogt sin dejarnos llevar por el hype.

¿Qué es Mago exactamente?
Mago es un toolchain toolchain Conjunto de herramientas de desarrollo que trabajan juntas. En este caso: linter, formatter y analizador estático en un solo paquete. de PHP. Lo desarrolla Carthage Software y está escrito en Rust. Lo que nos ofrece es:
Detecta problemas de estilo, inconsistencias y code smells. Puede arreglar muchos automáticamente.
Formatea tu código PHP siguiendo PER-CS. Opinionado: no hay debate de tabs vs spaces.
Analizador estático que busca errores de tipos, null pointers y bugs lógicos.
Lo bonito de Mago es que todo va en un solo binario. No necesitas PHP instalado para usarlo, no necesitas configurar tres herramientas diferentes, y al usar un pipeline paralelo aprovecha todos los cores de tu máquina.
Instalación y primeros pasos
Instalarlo es sencillo. Tienes varias opciones:
# Via Composer (la más cómoda si ya usas PHP)
composer require --dev carthage-software/mago
# Via Homebrew (macOS/Linux)
brew install mago
O
curl --proto '=https' --tlsv1.2 -sSf https://carthage.software/mago.sh | bash
Para este paso mejor mirarse el GitHub por si hay cambios:
Una vez instalado, lo inicializas en tu proyecto:
El mago init detecta tu composer.json y te genera un mago.toml con la configuración básica. A partir de ahí, tienes los tres comandos principales: lint, format y analyze.
¿Es tan rápido como dicen?
Pues si, lo es, pero hay que tener cuidado con lo que se asegura en el artículo original, que habla de 10x más rápido que PHPStan y cosas así. La realidad tiene matices.
Lo que dicen los benchmarks oficiales
En las pruebas que publica el propio equipo de Mago, los números son impresionantes:
| Tarea | Mago | Alternativa | Diferencia |
|---|---|---|---|
| Análisis estático | 1.84s | PHPStan: 81.6s | ~44x |
| Linting | 0.55s | PHP-CS-Fixer: 49.6s | ~90x |
| Formateo | 0.36s | Pretty PHP: 31.4s | ~86x |
Lo que dicen las pruebas independientes
Las pruebas reales de la comunidad dicen otras cositas.
- Primera ejecución: Mago ~1.7s vs PHPStan ~6.4s → unas 3-4x más rápido. Que sigue siendo una burrada, ojo.
- Ejecuciones sucesivas: PHPStan gana porque cachea los resultados entre ejecuciones. Mago no tiene análisis incremental todavía, así que vuelve a analizar todo desde cero cada vez.
Entonces, ¿es más rápido? Sí, claramente en la primera ejecución. ¿10x? Depende del proyecto y del escenario. En tu flujo de trabajo habitual donde ejecutas PHPStan muchas veces al día y el caché ya está caliente, la diferencia real es menor de lo que venden.
En el linter mola más
Donde Mago lo peta de verdad es en el linting. Es rápido, detecta cosas útiles y el --fix automático funciona bien. Ejemplo de lo que puede pillarte:
<?php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Form\FormInterface; // Mago: import no usado
class UserController extends AbstractController
{
public function index(Request $request)
{
$users = $this->userRepository->findActive();
if(count($users) > 0) { // Mago: usa empty() o el método isEmpty()
return $this->json($users);
}
return $this->json([]);
}
}
Con mago lint --fix, te quita el import sobrante y te sugiere alternativas más limpias. Funciona rápido y es útil.
El formatter: opiniones y ya
Si has usado Prettier Prettier Formateador de código opinionado para JavaScript, TypeScript, CSS y otros lenguajes. Se caracteriza por no dar opciones: hay una sola forma de formatear. en el mundo JavaScript, el formatter de Mago te resultará familiar porque es opinionado. Sigue el estándar 💡 PER-CS PHP Evolving Recommendation - Coding Style. La evolución de PSR-12. Define cómo debe formatearse el código PHP: indentación, llaves, espacios, etc. Más info → y punto. No hay 47 opciones para discutir en la PR.
# Formatear todo
mago format
# Solo comprobar (perfecto para CI)
mago format --check
La configuración básica en mago.toml:
[formatter]
line_width = 120
indent_width = 4
Y se acabó la discusión. Tu código queda limpio, consistente y no tienes que perder ni un segundo debatiendo estilos.
El analizador estático: aquí viene la chicha
Vamos a la parte más interesante y también la más polémica. El analyzer de Mago intenta hacer lo mismo que PHPStan o Psalm: encontrar errores de tipos, null pointers y bugs lógicos sin ejecutar el código.
Ejemplo clásico que detecta:
<?php
namespace App\Service;
use App\Entity\Product;
class ProductService
{
// Mago detecta que find() puede devolver null
// y tú estás accediendo directamente a ->name
public function getProductName(int $id): string
{
$product = Product::find($id);
return $product->name; // Potencial null pointer
}
}
Te avisa de que find() puede devolver null y que estás accediendo a ->name sin comprobar. Esto es lo básico que cualquier analizador debería detectar.
Pero ojo, que aquí hay trampas
Y aquí es donde hay que ser honesto y frenar el hype. A día de hoy, el analizador de Mago no está al nivel de PHPStan, en mi opinión, por estas razones:
En proyectos grandes genera muchos avisos incorrectos, sobre todo con PHPDoc (@property, @method). Han ido mejorando, pero queda camino.
PHPStan cachea entre ejecuciones. Mago analiza todo desde cero cada vez. En el día a día eso se nota.
PHPStan lleva años en producción, miles de proyectos lo usan. Mago es relativamente nuevo y sigue evolucionando rápido.
PHPStan tiene extensiones para Symfony, Laravel, Doctrine... Mago tiene soporte básico para frameworks, pero no al mismo nivel.
Esto no significa que Mago sea malo. Significa que todavía no ha llegado donde necesita llegar para ser un sustituto real de PHPStan en proyectos serios.
¿Mago o PHPStan? La pregunta del millón
De momento está claro, no cambies PHPStan por Mago a día de hoy. PHPStan es una herramienta madura, probada y con un ecosistema enorme. Si la tienes configurada en nivel 6, 7 u 8, Mago no te va a dar esa misma cobertura todavía.
Pero eso no quiere decir que Mago no tenga sitio en tu flujo de trabajo. La combinación más sensata ahora mismo es:
- Usar Mago como linter/formatter (es rápido y funciona bien)
- Mantener PHPStan como analizador estático principal
- Ir probando el analyzer de Mago en proyectos nuevos o pequeños
- Estar atento a las releases, el proyecto avanza muy rápido
- Quitar PHPStan de un proyecto en producción para poner Mago
- Fiarte solo de Mago para análisis estático en proyectos críticos
- Ignorar Mago completamente — la velocidad es real y útil
Son herramientas compatibles. Se complementan. Puedes tener Mago para lint y format, donde ganas velocidad de verdad, y PHPStan para el análisis estático serio. No se pisan entre ellos.
Configuración para un proyecto Symfony
Si quieres probarlo en tu proyecto Symfony, aquí tienes un mago.toml de ejemplo:
[formatter]
line_width = 120
indent_width = 4
[linter]
level = "default"
[paths]
include = [
"src/",
"tests/",
"config/"
]
exclude = [
"vendor/",
"var/",
"node_modules/"
]
Y en tu composer.json:
composer require --dev carthage-software/mago
Para integrarlo con tu CI, puedes añadir los checks de lint y format sin tocar tu pipeline de PHPStan:
# En tu GitHub Actions o lo que uses
- name: Mago checks
run: |
mago lint
mago format --check
# Tu PHPStan de siempre, intacto
- name: PHPStan
run: vendor/bin/phpstan analyse
Así tienes lo mejor de los dos. La velocidad de Mago para lint/format y la fiabilidad de PHPStan para el análisis estático.
¿Y en el futuro? ¿Qué?
El proyecto parece moverse muy rápido. En el roadmap tienen cosas interesantes como análisis incremental, mejor soporte de frameworks (Symfony, Laravel, Doctrine), y mejoras en el parser de PHPDoc. Si consiguen cerrar la brecha con PHPStan en el analizador, la velocidad de Rust más la comodidad de tener todo en un solo binario pueden hacer que Mago sea la opción dominante a medio plazo.
Pero a día de hoy, es una herramienta prometedora que todavía tiene que demostrar en producción. La velocidad bruta está ahí, lo que falta es la madurez del análisis.
Ea, pos eso. Si trabajas con PHP, échale un ogt a Mago. No para lanzarte de cabeza a implementarlo en producción, sino para ir probándolo poco a poco. Lo del linter y el formatter sí que lo puedes empezar a usar ya sin miedo. El analizador, ya veremos en unos meses.
Nos vemos en los bares! 🍻
Pon a prueba lo aprendido
1. ¿En qué lenguaje está escrito Mago?
2. ¿Qué tres herramientas combina Mago en un solo binario?
3. ¿Por qué PHPStan puede ser más rápido que Mago en ejecuciones sucesivas?
4. ¿Cuál es la estrategia más sensata hoy para combinar Mago y PHPStan?
5. ¿Cuál es una limitación importante del analizador de Mago a día de hoy?