Hace casi 2 años PHP 7.4 llegó a su end-of-life, y en poco menos de 2 meses dejará de tener también soporte de seguridad. En consecuencia, en los últimos meses, a una gran parte de clientes y proveedores les ha surgido la necesidad de actualizar sus servidores y/o aplicaciones a PHP 8.
Como puedes ver en la siguiente tabla, desde Drupal.org se recomienda mantener nuestras aplicaciones en la versión 9.x, y a su vez utilizar PHP en su versión 8.0 como mínimo.
Hemos decidido recopilar en este post los pasos necesarios para que nuestras aplicaciones Drupal sean compatibles con esta versión de PHP, aportando nuestra experiencia para aquél que lo pudiera necesitar.
Hay que tener en cuenta que vamos a centrarnos en actualizar un proyecto de Composer basado en el boilerplate de Metadrop Drupal, pero estos pasos se pueden aplicar a cualquier Drupal Composer project. Avisamos de que se presuponen unos conocimientos previos de Docker y Drupal para comprender los pasos más complejos.
Los pasos:
1. Actualiza el entorno local
Nuestros entornos de desarrollo están basados en Docker, así que lo primero que tienes que hacer y que es fundamental, es cambiar la imagen PHP para que Docker pueda utilizar el contenedor PHP 8 adecuado.
En nuestro caso, utilizamos el boilerplate de Metadrop y éste a su vez utiliza la imagen wodby/drupal-php. Todas las etiquetas están listadas en Docker Hub, donde tenemos que localizar la que encaje con la versión que vamos a tener en nuestro entorno de producción.
Como ejemplo, buscamos la versión de PHP 8.1.9, la etiqueta que obtenemos del Docker Hub es "8.1-dev-4.37.9". Deberás editar el fichero docker-compose.yml
o el que defina los contenedores que estás usando en tu proyecto.
2. Cambia los requisitos de la plataforma
El siguiente paso es actualizar el Composer.json del proyecto para requerir la versión PHP 8 en la sección de requisitos de la plataforma. Añadiéndolo, estás forzando a Composer a operar usando PHP 8.1 como plataforma del proyecto. Si no lo haces, Composer utilizará la versión de PHP del entorno.
"config": {
...
"platform": {
"php": "8.1"
},
Nota: No todos los paquetes tienen la restricción de PHP, por lo que es posible que algunos paquetes deban ser actualizados y probados manualmente.
3. Deja que Composer haga su magia
Ha llegado uno de los momentos críticos. Tienes que comprobar que tu entorno y composer están bien configurados, y que los paquetes se pueden obtener de forma correcta. Para ello, dentro del contenedor, lanza:
$ composer update
Acepta los plugins durante el proceso. Composer intentará resolver los requerimientos del proyecto y actualizar los paquetes en base a ello, por eso el entorno debe ser lo más parecido a producción.
Se actualizarán las dependencias directas, y se mostrarán las dependencias que no puedan ser actualizadas debido a algún conflicto de restricción del proyecto de Composer. En estos casos será necesario resolver el problema manualmente de acuerdo a las necesidades de cada proyecto.
Nota: Si no quieres actualizar los paquetes de Composer, pero quieres saber qué paquetes no son compatibles o necesitan ser actualizados, puedes ejecutar el comando con el parámetro dry-run.
composer update --dry-run
4. Revisa que el código sea compatible
Para la revisión de compatibilidad del código, hemos usado PHP Compatibility, un plugin de PHP Code Sniffer, que analiza el código en busca de compatibilidades entre versiones. Al ser un analizador de código estático, no es infalible porque no ejecuta el código, pero te puede dar pistas más que interesantes para evitar posibles problemas.
Nota: Se requiere la rama Dev porque la versión estable actual (9.3.5) tiene algunos problemas que ya han sido resueltos y fusionados en la rama develop.
Primero, añádelo a composer:
composer require "phpcompatibility/php-compatibility:dev-develop as 9.3.6" \
--dev
A continuación, ejecuta el Code Sniffer especificando la carpeta a comprobar.
Recomendamos hacerlo en los módulos personalizados y también en los contrib. Ten en cuenta que es muy posible que los errores mostrados por el Code Sniffer ya hayan sido corregidos por la comunidad Open Source, así que probablemente la actualización del módulo o librería sea suficiente para solucionarlos. Para lanzar PHP Code Sniffer con este plugin, ejecuta lo siguiente:
./vendor/bin/phpcs -p <folder> --standard=PHPCompatibility
No prestes atención a los informes de archivos JS o CSS, estos no supondrán ningún problema de compatiblidad. Si es necesario, en el siguiente ejemplo más complejo, puedes ver cómo ignorarlos:
./vendor/bin/phpcs -p web/modules/custom/ --standard=PHPCompatibility \
--runtime-set testVersion 8.0 --extensions=php,module,install,inc \
--report-full=./tmp/drupal9-php8-compatibility.txt --ignore=*/tests/*
Analizando un poco los parámetros podemos ver que sirven para lo siguiente:
--runtime-set testVersion 8.0- : To run all the checks for PHP 8.0 and above.
--extensions=php,module,install,inc : To specific extensions files.
--report-full=<folder/file.txt> : To save the report to a file.
--ignore=*/tests/*” : To ignore specific folders.
5. Revisa que todo funciona correctamente
Para el proceso de revisión, hemos seguido el siguiente workflow en nuestro entorno local:
- Borra los logs de errores para empezar de cero.
- Ejecuta las baterías de pruebas automáticas que uses habitualmente.
- Haz también comprobaciones manuales de los puntos críticos.
- Por último, revisa los logs (Watchdog por ejemplo) para saber si se introdujo alguna advertencia o error relacionado con la actualización a PHP 8.
Es muy útil que, para evitar perder errores ocultos, tengas el informe de logs lo más limpio, claro y estructurado posible.
Después de ejecutar la batería de tests (nosotros utilizamos Behat) y hacer las pruebas manuales es posible que aparezcan mensajes de funcionalidades obsoletas en el informe. Si tienes activado Watchdog, puedes consultar los errores desde la UI de Drupal o directamente desde base de datos con la siguiente consulta:
select count(*) count, message, variables, max(location)
from watchdog
where message like '%deprecated%'
or variables like '%deprecated%'
group by variables, message;
6. Actualiza Drush si es necesario
La versión 10 de Drush tiene ciertos problemas con PHP 8, por lo que es conveniente trabajar directamente con la versión 11.
$ composer require drush/drush:^11
Después de actualizar Drush, al ejecutar sus comandos, algunos módulos pueden disparar un mensaje de depuración, esto no es crítico, y el mensaje aparecerá al lanzar comandos con “verbose”:
[debug] votingapi commands loaded even though its constraint (^9)
is incompatible with Drush 11.1.1.
Broaden the constraint in modules/contrib/votingapi/composer.json
(see 'extra\drush\services' section) to remove this message
Como dice el mensaje, los comandos funcionan, pero es necesario especificar en el composer.json del módulo la constrain para la versión 11. Puedes encontrar una issue para resolverlo en un módulo en soporte de Drush 11.
7. Comprueba que el proyecto es compatible con las versiones anteriores de PHP
Este paso es opcional, pero necesario si los servidores van a estar en la versión anterior en el momento de desplegarlo.
A. Primero, fija en Composer a la versión de PHP deseada (por ejemplo: PHP 7.4.3) tal como hiciste en el punto 2 (cambia los requisitos de la plataforma), y comprueba el status con drush de la siguiente forma:
$ drush status
B. Si aparece el siguiente mensaje quiere decir que algún paquete está requiriendo la versión 8 de PHP (en caso de que no aparezca, salta directamente al punto D).
Composer detected issues in your platform:
Your Composer dependencies require a PHP version ">= 8.1.0".
You are running 7.4.3.
Para sacar un listado de paquetes que requieren PHP 8, lanza el siguiente comando y analiza en cuáles no aparece PHP 7 como compatible:
$ composer why php | egrep "requires php \([^0-9]*8\.\d*\)"
C. En este caso tenemos instalado symfony/string
en su versión 6 y está requiriendo PHP 8.1. Tenemos que ver de qué paquete es dependencia symfony/string
, y analizar si alguna de esas versiones que nos permita instalar es compatible con PHP 7. Siguiendo el ejemplo, este es el comando y la conclusión:
$ composer why symfony/string
chi-teck/drupal-code-generator 2.5.3 requires symfony/string (^5.1 || ^6)
Como puedes ver, chi-teck/drupal-code-generator
ha requerido la instalación de symfony/string
en su versión 5.1 o 6. Manualmente puedes ver el composer.json
de la versión 5.1 de symfony/string
(la versión anterior a la que tenemos instalada), y verificar si es compatible con PHP 7. Si es compatible, puedes hacer un downgrade de la versión sin ningún problema:
$ composer require symfony/string:^5.1
Repite el proceso para todos los paquetes que marquen como requisito mínimo la versión de PHP 8, como decíamos al principio, siempre y cuando necesites que tu aplicación siga siendo compatible con PHP 7.
D. En último lugar, elimina el platform del composer.json
(punto 2: cambia los requisitos de la plataforma) y ejecuta:
$ composer update –lock
8. Despliegue y conclusión
Ha llegado el momento del despliegue. En esta guía hemos explicado cómo actualizar nuestra aplicación Drupal a PHP 8, pero manteniendo la retrocompatbilidad con PHP 7. Esto permitirá desplegar a producción sin necesidad de sincronizar con la actualización de los servidores a PHP 8. Si no has podido o no has requerido mantener esa retrocompatibilidad, debes tener en cuenta que el servidor deberá estar actualizado antes de hacer el despliegue, si no, probablemente la aplicación no funcione (y pases un día complicado).
Como declaración final, esta guía es genérica, así que úsala como referencia y adáptala al contexto de tu proyecto.