
Compartir:
Actor de formación con una disertación sobre la comedia, llegué al desarrollo de PHP a través de la escena de las reuniones. Puedes encontrarme hablando y escribiendo sobre tecnología, o tocando/comprando discos raros de mi colección de vinilos.
¡Limpiar! Limpieza de Aplicaciones PHP con PHPStan
Durante mi tiempo como desarrollador de PHP, la forma de escribir y distribuir código ha cambiado radicalmente. A principios de Symfony y Zend Framework las aplicaciones PHP-FIG no existían y los estándares de codificación quedaban a discreción de quien los escribiera. Durante los años que hemos visto la adopción generalizada de estándares PSRlas herramientas de análisis estático han sido algo irregulares. Hasta ahora, con la versión 1.0 de PHPStan. Celebremos esta ocasión repasando algunas de sus características.
Lenguajes compilados, su cazador de errores preventivo
Una de las grandes ventajas de utilizar un lenguaje compilado como Java o C# es que el tiempo de compilación fallará completamente si tu código no es seguro(aunque esto es fácil de decir para mí, ya que no son las 2 de la mañana, con mi décimo café de la noche). Con PHP es un lenguaje interpretado, no tenemos el mismo lujo.
Interpretado como compilado: CI + herramientas
Gracias a la gran cantidad de herramientas DevOps que tenemos a nuestra disposición en el desarrollo web moderno y el análisis estático, lo que ocurre es que disponemos de las mismas herramientas pero a través de diferentes medios. Como ese es el caso no puedo abogar cuánto Te recomiendo que tengas algo similar al entorno que voy a exponer. Entonces, ¿para qué quieres estas herramientas? Veamos un ejemplo.
El escenario
Es común tratar de elegir un tema que sea divertido, o aplicable a lo que estás escribiendo cuando se trata de herramientas como esta. Pero, para este artículo voy a presentarles un escenario que me he encontrado personalmente una y otra vez en entornos de agencia:
"¡Ayuda! Alguien más construyó mi aplicación PHP y necesito que alguien la rescate y se haga cargo de su mantenimiento porque las características X/Y/Z necesitan ser construidas, ¡pero las características A/B/C ni siquiera funcionan bien!"
Hacerse cargo de la base de código/proyecto de otra persona es siempre una completa lotería. Si te haces cargo porque necesita nuevas funcionalidades y ya es un desastre plagado de deudas técnicas, sabes que tienes que solucionar eso antes de tocar nada más. Peor aún, muchos de estos proyectos (en mi experiencia) tienden a llegar sin absolutamente ninguna prueba para auto-documentar el código. Veamos un ejemplo clásico, que he visto una y otra vez:
$someData = \MyNamespace\MyORM\MyRepository::findAllBySomething(SOMETHING);
foreach ($someData as $myEntity) {
$myEntity->doTheThing();
}
Usted no escribió esa clase de entidad o el método de repositorio. No tienen typehints porque esto fue escrito originalmente en PHP5.3, o el desarrollador no usó ninguno. Está bien si tu ORM devuelve un array de las mismas entidades, pero un error, un resultado nulo en el valor de retorno de findAllBySomething() y doTheThing() arrojará un error fatal.
Es hora de configurar PHPStan en él.

Conozca su estrategia
Aunque es fácil decir "utilice PHPStan", si tiene una aplicación heredada o con muchas deudas tecnológicas, necesitará una estrategia en lugar de simplemente lanzar cosas por ahí a ver qué pasa. En primer lugar, deberá familiarizarse con los niveles de reglas.
Niveles de las reglas
PHPStan está estructurado para funcionar con determinados niveles de reglas, numerados del 0 al 9:
comprobaciones básicas, clases desconocidas, funciones desconocidas, métodos desconocidos invocados en
$this, número incorrecto de argumentos pasados a esos métodos y funciones, variables siempre indefinidasvariables posiblemente indefinidas, métodos mágicos desconocidos y propiedades en clases con
__cally__getmétodos desconocidos comprobados en todas las expresiones (no sólo
$this), validación de PHPDocstipos de retorno, tipos asignados a propiedades
comprobación básica de código muerto - siempre falso
instanceofy otras comprobaciones de tipo, ramaselsebifurcaciones muertas, código inalcanzable tras el retorno, etc.comprobación de los tipos de argumentos pasados a métodos y funciones
informar de la falta de typehints
informar de tipos de unión parcialmente erróneos - si llama a un método que sólo existe en algunos tipos de un tipo de unión, el nivel 7 empieza a informar de ello; otras situaciones posiblemente incorrectas
informe sobre la llamada a métodos y el acceso a propiedades de tipos anulables
sea estricto con el tipo
mixedla única operación permitida que se puede hacer con él es pasarlo a otromixed
Por eso es importante su estrategia. Si tiene un proyecto heredado escrito por otra persona y dispara el ejecutor de tareas PHPStan en el nivel 9, es posible que se sienta abrumado por los resultados que produce. ¡Todo está roto! Para refactorizar, sugeriría lo siguiente:
Fíjese hitos para cada nivel identificado, y empiece poco a poco.
La inversión a largo plazo se amortizará con el tiempo (hablaremos de las tuberías en breve), pero fija el nivel máximo al que estás dispuesto a llegar cuando clasifiques "arreglada la deuda tecnológica" bajo tu propia "definición de hecho"
Un buen objetivo de facto para un proyecto heredado es superar el nivel 6 de las normas. Es en este punto donde su código puede pasar de un estado de "peligro" a "correcto". Esto haría que el nivel de regla 6 su línea de base).
Esto es muy importanteAsegúrese de asignar tiempo (sprints, tickets de Jira desglosados para los masoquistas) para arreglar lo que PHPStan está marcando en cada nivel de regla. Corregir la deuda tecnológica no es fácil en muchos casos, y no tienes ni idea de qué tipo de fallos de lógica de negocio-dominio podría haber en tu aplicación.
Al establecer los objetivos incrementales para los niveles de regla, asegúrese de configurar su canalización antes de confirmar los cambios para no introducir nuevos olores en el código durante la refactorización. Para configurar su canalización necesitará establecer su línea de basea la que llegaremos más adelante.
Tuberías
En el mundo de DevOps, hay una cantidad algo abrumadora de opciones de herramientas disponibles para resolver tus problemas. Para este caso, estoy ofreciendo sólo un enfoque, pero es menos complejo que otras opciones disponibles. Una vez que hayas establecido tu estrategia, es hora de configurar tu pipeline para que no comprometamos ningún código nuevo que no haya pasado primero por PHPStan.
Barreras de defensa: local frente a servidor
Me gusta introducir herramientas para eliminar cualquier posibilidad de puntos únicos de fallo, y como resultado de ese cinismo recomiendo encarecidamente que ejecute su análisis estático tanto en las máquinas de los desarrolladores locales así como comprobaciones CI del lado del servidor en su repositorio.
Local
Compositor + PHPStan
En primer lugar, querrás instalar PHPStan dentro de tu proyecto. Vamos a utilizar composer para esto, trabajando bajo la suposición de que, con suerte, su código heredado utiliza la gestión de paquetes. Si no es así, puede instalar composer y usar composer init para crear un nuevo proyecto.
Para instalar PHPStan, ejecute lo siguiente:
Añadimos --dev ya que no lo necesitamos para la producción (¡en teoría!).
Configuración: establecimiento de la línea de base
Esta es una característica muy interesante de PHPStan. Su línea de base establece el "punto cero" de su aplicación, de modo que cualquier error actual que exista dentro del Nivel de Regla de su elección se ignora hasta que decida tratar con ellospero al mismo tiempo puede imponer un nivel de regla para cualquier nuevo cambio cometido. Un enfoque sensato, como se indica en la estrategia, sería establecer una línea de base en el nivel de regla 6:
Todo el código nuevo que se incluya en el proyecto deberá cumplir el nivel 6 de las normas.
A continuación, puede establecer los objetivos de deuda tecnológica para los niveles inferiores, tal y como se identifican en sus objetivos estratégicos.
Para crear su línea de base, ejecute lo siguiente:
Ahora tendrá su configuración de base establecida en el archivo especificado (phpstan.neon), que guarda un resumen detallado de los errores por archivo.
Ahora querrá que PHPStan evite los commits a su repositorio antes de que que puedan ser enviados a su fuente. Para ello, utilizamos ganchos Git.
Ganchos Git
De alguna manera me tomó años darme cuenta de que git realmente instala ganchos como estándar en un nuevo repositorio git en git init. Puedes leer más sobre los hooks de git aquí. Vamos a editar el hook pre-commit hook. Siempre y cuando no hayas tocado ningún hook antes en tu proyecto, puedes habilitar el hook pre-commit renombrándolo - ejecuta esto desde la raíz de tu proyecto:
Ahora abre el archivo, borra el contenido y copia lo siguiente:
Ahora que ha habilitado pre-commitPHPStan se disparará antes de cada commit y analizará contra la línea de base para cualquier archivo nuevo que haya sido cambiado en el commit de git. ¡No más código confirmado maloliente!
Es posible que desee ajustar el disparador de la línea de comandos cuando suba de nivel, de modo que cuando deba cambiar (o desee activar otras funciones de PHPStan), cambie los analysisResult=$(vendor/bin/phpstan analyse $gitDiffFiles) argumentos de línea.
Del lado del servidor
Cuanta más defensa pueda poner para su código, mejor. Ejecutar PHPStan del lado del servidor después de un push a su código como parte de su Integración Continua es algo imprescindible. Para este ejemplo, vamos a utilizar las Acciones de Github, pero ten en cuenta que puedes configurar esto con el mismo nivel de funcionalidad en CircleCI, Bitbucket Pipelines, Gitlab CI/CDo Jenkins. Aquí está un flujo de trabajo de acciones de ejemplo establecido en Github , la construcción de su código con un Ubuntu Ubuntu:
---
name: build
on: [ push, pull_request ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
name: Build example
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.0
extensions: json, mbstring
coverage: pcov
env:
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Run PHPStan
run: vendor/bin/phpstan analyse .El comando bajo "Ejecutar PHPStan" puede ser configurado a sus requerimientos de la misma manera que puede configurar el comando cuando ejecuta PHPStan localmente. He escrito este flujo de trabajo para ejecutar PHPStan en un nivel predeterminado en todos los archivos dentro del proyecto (este flujo de trabajo no se ha disparado composer aún, por lo que no tendrá el paso innecesario e ineficiente de ejecutarlo en su vendor carpeta) por lo que aquí me gustaría recomendar tener una configuración para tirar en que establece el nivel de regla de todo el proyecto.
Su proyecto heredado dispone ahora de una estrategia para depurar el código y de conductos para evitar que aparezcan nuevos errores en los commits, al tiempo que se realizan análisis con respecto a la línea de base de todo el código existente. Este tipo de configuración puede darle mucha más confianza a la hora de comprometerse con el proyecto, a la vez que le proporciona información sobre las áreas que probablemente necesiten refactorización para eliminar la deuda tecnológica.
Por último, pero no menos importante: análisis estático frente a pruebas
Digo esto en voz alta, especialmente para la gente de atrás: PHPStan y cualquier otra herramienta de análisis estático no sustituye a las pruebas. La forma en que yo enmarcaría su uso es que un conjunto de pruebas y PHPStan se complementan en la evaluación de la calidad de su código.
Es un error creer que se necesita poco o nada un conjunto de pruebas. Lo más importante aquí es que el análisis estático no puede probar la lógica de su dominio. Aunque pueda parecer una afirmación obvia, cabe señalar que puede resultar confusa, ya que PHPStan puede eliminar la necesidad de ciertas pruebas. Un ejemplo de esto sería una prueba instanceOf que afirma que una clase que se está creando es el resultado final de un proceso. PHPStan puede eliminar este requisito, ya que proporciona el análisis necesario para eliminar este posible error, pero no conoce de antemano su lógica de dominio necesaria - esto es lo que usted hacer necesita probar.
Y recuerde, ¡hay alternativas!
¿Lo has probado? ¿No te gusta demasiado? Cada uno tiene sus preferencias, y aunque yo le canto las cuarenta a Ondřej por su trabajo en PHPStan, vale la pena señalar que hay varias otras herramientas que realizan el mismo trabajo o que se pueden utilizar junto con PHPStan:
Gracias
Un agradecimiento especial a Ondřej Mirtes por sus consejos y por todo el trabajo que ha realizado para lanzar esta impresionante herramienta.
Compartir:
Actor de formación con una disertación sobre la comedia, llegué al desarrollo de PHP a través de la escena de las reuniones. Puedes encontrarme hablando y escribiendo sobre tecnología, o tocando/comprando discos raros de mi colección de vinilos.