🧹 Mantén tu código PHP impoluto con PHP CS Fixer
Si alguna vez has trabajado con alguien en desarrollo de software me apuesto a que te ha pasado que abres un archivo que acaba de modificar otra persona y te sangran los ojos porque hay espacios donde deberían ir tabs (o viceversa), llaves en la misma línea, en la siguiente… un caos.
Para evitar esto y no perder amigos en los Code Reviews, existe PHP CS Fixer.

¿Qué es esto?
PHP-CS-FIXER que viene a significar (PHP Coding Standards Fixer) es una herramienta que corrige automáticamente el estilo de tu código para que siga los estándares que tú (o la comunidad, como PSR-12) defináis. Es como tener a un senior revisando cada espacio y punto y coma, pero sin que te juzgue y te mire mal.
1. ¿Qué hace PHP CS Fixer?
¿Qué es PSR-12?
Antes de meternos con la herramienta, una aclaración rápida por si andas perdido: PSR son las siglas de PHP Standards Recommendations, las recomendaciones oficiales de estilo que publica el PHP-FIG. Son como las reglas de tráfico del código PHP.
Lo básico: etiquetas PHP, encoding UTF-8, nombres de clases en PascalCase, constantes en MAYÚSCULAS.
El estándar moderno. Extiende PSR-1 con reglas para indentación (4 espacios), llaves, parámetros, imports... Es el que usarás el 99% del tiempo.
PHP CS Fixer puede aplicar PSR-12 automáticamente con solo añadir '@PSR12' => true en la config. No tienes que saberte las reglas de memoria, la herramienta las aplica por ti.
🛠️ Instalación
Lo más fácil es usar Composer. Ejecuta esto en tu proyecto:
composer require --dev friendsofphp/php-cs-fixer
Nota: Lo instalamos como dependencia de desarrollo (
--dev) porque no lo necesitas en producción. PHP CS Fixer solo se usa para formatear código, no es una librería que tu aplicación necesite para funcionar.
2. ¿Por qué se instala con --dev en Composer?
Si prefieres tenerlo instalado de forma global (para usarlo en cualquier proyecto sin añadirlo a cada composer.json):
composer global require friendsofphp/php-cs-fixer
Con la instalación global lo ejecutas directamente con php-cs-fixer fix en lugar de vendor/bin/php-cs-fixer fix.
⚙️ Configuración
Aunque puedes usarlo sin configurar, lo ideal es crear un archivo .php-cs-fixer.dist.php en la raíz de tu proyecto o dónde quieras, porque le puedes pasar el path por parámetro. En este fichero defines tus reglas.
Un ejemplo sencillo y robusto para empezar:
<?php
$finder = (new PhpCsFixer\Finder())
->in(__DIR__)
->exclude('var')
->exclude('vendor');
return (new PhpCsFixer\Config())
->setRules([
'@PSR12' => true,
'array_syntax' => ['syntax' => 'short'],
'no_unused_imports' => true,
])
->setFinder($finder);
¿Qué hace esto?
- Finder: Le dice dónde buscar archivos (en
__DIR__o sea, todo el proyecto) y qué ignorar (var,vendor). - Rules:
@PSR12: Aplica todo el estándar PSR-12 (el moderno).array_syntax: Fuerza usar[]en lugar dearray().no_unused_imports: Borra losuseque no estés usando.
📋 Reglas más útiles
PHP CS Fixer tiene más de 200 reglas. Aquí van las que más uso y que te recomiendo añadir a cualquier proyecto:
| Regla | ¿Qué hace? | ¿Risky? |
|---|---|---|
no_unused_imports | Elimina los use que no se usan | No |
ordered_imports | Ordena los imports alfabéticamente | No |
single_quote | Convierte comillas dobles a simples donde no hace falta interpolación | No |
no_useless_else | Quita else innecesarios después de un return | No |
trailing_comma_in_multiline | Añade coma final en arrays multilínea (diffs más limpios) | No |
declare_strict_types | Añade declare(strict_types=1) a cada archivo | Sí |
strict_param | Usa las versiones estrictas de funciones como in_array | Sí |
@Symfony | Paquete de reglas completo del framework Symfony (incluye PSR-12 + extras) | No |
Las reglas marcadas como Risky pueden cambiar el comportamiento de tu código, no solo su formato. Por ejemplo, declare_strict_types hace que PHP sea estricto con los tipos, y si tienes código que pasa un "123" a una función que espera un int, puede petarte donde antes no petaba. Úsalas con criterio y ejecuta tus tests después.
Para activar reglas risky necesitas añadir ->setRiskyAllowed(true) en tu config.
4. ¿Qué son las reglas 'risky' en PHP CS Fixer?
🔥 Configuración más completa
Si quieres ponerte sargento de verdad, aquí tienes una configuración que uso yo para proyectos grandes. Activa reglas risky que cambian lógica levemente para mejorar rendimiento y seguridad, y formatea hasta el último rincón.
<?php
$finder = (new PhpCsFixer\Finder())
->in(__DIR__)
->exclude('var')
->exclude('vendor');
return (new PhpCsFixer\Config())
->setRiskyAllowed(true)
->setRules([
'@PSR12' => true,
'@Symfony' => true,
'array_syntax' => ['syntax' => 'short'],
'no_unused_imports' => true,
'ordered_imports' => ['sort_algorithm' => 'alpha'],
'no_useless_else' => true,
'no_useless_return' => true,
'concat_space' => ['spacing' => 'one'],
'binary_operator_spaces' => [
'default' => 'align_single_space_minimal',
],
'method_chaining_indentation' => true,
'strict_param' => true,
'declare_strict_types' => true,
'single_quote' => true,
'trailing_comma_in_multiline' => ['elements' => ['arrays']],
])
->setFinder($finder);
Cuidado:
strict_paramydeclare_strict_typespueden romper funcionalidad si tu código no es estricto con los tipos. Ejecuta tus tests después de un primerfixpara detectar posibles problemas.
🚀 Cómo usarlo
Para revisar si hay errores (sin hacer cambios en el código):
vendor/bin/php-cs-fixer fix --dry-run --diff
3. ¿Qué hace el flag --dry-run?
Para arreglar los errores automáticamente y sin piedad:
vendor/bin/php-cs-fixer fix
Algunos flags útiles:
No toca nada, solo te dice qué cambiaría. Perfecto para CI/CD.
Muestra el diff exacto de cada cambio. Así ves qué va a hacer antes de aceptarlo.
Muestra info detallada: qué reglas aplica, qué archivos toca... Útil para debuggear.
Permite especificar un archivo de config diferente al por defecto. --config=.php-cs-fixer.dist.php
⚡ El Truco del Makefile
Escribir ese comando todo el rato es un poco tostón. Si usas un Makefile en tu proyecto, añade esto:
fix-code:
vendor/bin/php-cs-fixer fix
check-code:
vendor/bin/php-cs-fixer fix --dry-run --diff
Ahora solo tienes que ejecutar en tu terminal:
make fix-code
¡EA! Código limpio en un segundo.
🪝 Pre-commit Hook: Que no se te escape nada
El Makefile está bien, pero depende de que te acuerdes de ejecutarlo. ¿Sabes qué no depende de tu memoria? Un pre-commit hook de Git. Cada vez que hagas git commit, se ejecuta automáticamente el fixer sobre los archivos que vas a commitear.
Crea el archivo .git/hooks/pre-commit en tu proyecto:
#!/bin/sh
# Solo archivos PHP staged
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep "\.php$")
if [ -z "$STAGED_FILES" ]; then
exit 0
fi
echo "🧹 Ejecutando PHP CS Fixer..."
# Ejecutar solo sobre los archivos staged
for FILE in $STAGED_FILES; do
vendor/bin/php-cs-fixer fix "$FILE" --quiet
git add "$FILE"
done
echo "✅ Código formateado."
Dale permisos de ejecución:
chmod +x .git/hooks/pre-commit
Ahora cada vez que hagas un git commit, el hook formatea automáticamente los archivos PHP que vayas a commitear. Si no hay archivos PHP, no hace nada. Sin fricciones.
Ojo: Los hooks de
.git/hooks/no se comparten con el equipo porque.gitno se sube al repositorio. Si quieres que todo el equipo use el hook, mira herramientas como GrumPHP o CaptainHook que gestionan esto.
🤖 Integración Continua (CI/CD) en GitHub
De nada sirve tener reglas si nadie las cumple. La mejor forma de evitar que entre código bobo a tu rama main es crear un guardián en tu repositorio.
Crea un archivo en .github/workflows/php-style.yml:
name: Check PHP Style
on: [push, pull_request]
jobs:
php-cs-fixer:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3' # Tu versión de PHP
extensions: mbstring, intl
tools: composer:v2
- name: Install dependencies
run: composer install --no-progress --prefer-dist
- name: Run PHP CS Fixer
# Si falla (exit code != 0), el pipeline se pone rojo 🔴
run: vendor/bin/php-cs-fixer fix --dry-run --diff --verbose
¿Qué conseguimos con esto?
- Cada vez que alguien hace un
pusho abre unaPull Request, GitHub ejecuta este escáner. - Usamos
--dry-run: No modifica los archivos, solo avisa. - Si hay errores de estilo, el comando falla y bloquea el merge (si tienes las reglas de la rama protegidas).
📝 Consejos y errores comunes
-
Git Ignore: PHP CS Fixer crea un archivo de caché
.php-cs-fixer.cache. Añádelo a tu.gitignorepara no subirlo al repo. -
El primer fix da miedo: Cuando ejecutas PHP CS Fixer por primera vez en un proyecto grande, prepárate para un diff de cientos de archivos. Mi consejo: haz el primer
fixen un commit exclusivo (algo como “Apply PHP CS Fixer”) para que no se mezcle con lógica de negocio y los diffs futuros sean legibles. -
No mezcles
@PSR12con@Symfonysin saber qué hace cada uno:@Symfonyya incluye@PSR12, así que no necesitas poner los dos. Si los pones, no pasa nada malo, pero es redundante. Lo que sí puede pasar es que@Symfonyactive reglas que no te gustan y tengas que desactivarlas explícitamente. -
Cuidado con las reglas risky en código legacy: Si tienes un proyecto viejo sin tests, no actives
declare_strict_typesystrict_parama lo loco. Pueden romper cosas que antes “funcionaban” porque PHP era flexible con los tipos. Primero tests, después strict. -
Integración con el IDE: Tanto PHPStorm como VS Code (con la extensión de PHP CS Fixer) permiten ejecutar el fixer al guardar. Así no tienes que acordarte de nada, cada vez que le das a Ctrl+S se formatea solo.
Conclusión
EA con todo esto y sobretodo si compartes un proyecto con más gente, se acabarán las disputas sobre los espacios y demás historias, y todo el código estará exactamente igual formateado solo con ejecutar PHP-CS-FIXER.
La combinación perfecta es: config en el proyecto + pre-commit hook + CI/CD en GitHub. Así te aseguras de que ni tú ni nadie del equipo puede meter código mal formateado aunque quiera.
¡Nos vemos en el siguiente commit o en los bares🍻! 🚀
Pon a prueba lo aprendido
5. ¿Qué estándar aplica la regla '@PSR12' de PHP CS Fixer?
6. ¿Por qué es buena idea hacer el primer 'fix' en un commit exclusivo?
7. ¿Qué problema tiene activar 'declare_strict_types' en un proyecto legacy sin tests?
8. ¿Qué combinación te asegura que nadie del equipo puede meter código mal formateado?