Saltar al contenido
smallPict

Engineering21 min read

CI/CD para plugins de WordPress: WPCS, PHPStan y PHP 7.4 a 8.5

CI/CD para plugins de WordPress en GitHub Actions: WPCS, PHPStan y pruebas en PHP 7.4 a 8.5, más un control previo al despliegue en WordPress.org.

smallPict Engineering

Engineering

El plugin de WordPress de smallPict convierte las imágenes subidas a WebP y AVIF y las sirve a los visitantes. Cuando etiquetamos una versión, GitHub Actions hace commit de ella en el repositorio SVN de WordPress.org, y desde ahí llega a cada sitio que tiene el plugin instalado. Los sitios con las actualizaciones automáticas activadas la instalan sin que nadie revise el cambio. El plugin declara Requires PHP: 7.4 y Requires at least: 5.9, así que un mismo build tiene que funcionar tanto en un hosting compartido que sigue en PHP 7.4 como en un contenedor con PHP 8.5.

Un error fatal en un plugin puede dejar inaccesible wp-admin. Un valor sin escapar en una página de ajustes es una vulnerabilidad XSS en todos los sitios a la vez. Una comprobación de nonce que falta convierte una acción de administración en un objetivo de CSRF. Nada de esto se puede corregir en silencio: la corrección necesita otra versión, y algunos sitios conservan la versión defectuosa hasta que llega.

Este artículo describe nuestro pipeline de CI/CD para el plugin de WordPress: los WordPress Coding Standards (WPCS), PHPStan y las pruebas automatizadas que se ejecutan en siete versiones de PHP, y el control previo al despliegue que se sitúa antes de WordPress.org. Todo lo que sigue procede del repositorio smallpict-wp en la versión 1.2.9.

Puntos clave

  • Cada pull request hacia master ejecuta WPCS, PHPStan y 9 scripts de pruebas automatizadas en PHP 7.4, 8.0, 8.1, 8.2, 8.3, 8.4 y 8.5, en paralelo en GitHub Actions.
  • WPCS exige entradas saneadas, salidas escapadas, nonces y consultas preparadas. PHPStan en el nivel 5 encuentra errores de tipo y ramas muertas a partir de stubs de WordPress, sin ejecutar WordPress.
  • Un tag de versión vuelve a ejecutar WPCS en las siete versiones de PHP, y el job que hace commit en el SVN de WordPress.org solo arranca cuando las siete pasan.

El pipeline de CI/CD del plugin de WordPress de un vistazo

Dos workflows de GitHub Actions protegen el plugin.

WorkflowSe ejecuta conJobsComprobaciones
phpcs.yml (PHP Code Quality & Standards)push a master o develop, pull requests hacia masterun job por versión de PHP: 7.4, 8.0, 8.1, 8.2, 8.3, 8.4, 8.5WPCS, PHPStan, pruebas automatizadas
deploy-all.yml (Deploy to WordPress.org)push de un tag v*WPCS en cada una de las 7 versiones de PHP, después deployWPCS, después build y commit en SVN

Nota sobre la cobertura de versiones de PHP: Las capturas de pantalla de este artículo muestran nuestras ejecuciones de CI anteriores, que cubrían de PHP 7.4 a 8.3. Hoy, nuestra matriz de pruebas en GitHub Actions se ha ampliado y cubre de PHP 7.4 a PHP 8.5, lo que garantiza una compatibilidad continua con los entornos de ejecución de PHP modernos sin regresiones en los entornos heredados.

Todas las herramientas son dependencias de desarrollo de Composer, así que ninguna llega a los usuarios:

JSON
"require-dev": {    "wp-coding-standards/wpcs": "^3.4.1",    "phpcsstandards/php_codesniffer": "^3.0",    "phpstan/phpstan": "^2.1",    "szepeviktor/phpstan-wordpress": "^2.0 || ^1.3.5",    "phpstan/extension-installer": "^1.4"}

Pruebas en matriz con varias versiones de PHP en GitHub Actions

Qué cambia entre PHP 7.4 y PHP 8.5

PHP 7.4 y PHP 8.5 ejecutan el mismo código fuente del plugin de forma distinta. Estos son algunos de los cambios que afectan al código de un plugin de WordPress:

  • Desde PHP 8.0, muchas funciones internas lanzan un TypeError ante un argumento de tipo incorrecto, donde 7.4 devolvía null con una advertencia. El código que pasaba un array a strlen() seguía ejecutándose en 7.4 y se detiene en 8.0.
  • PHP 8.0 cambió cómo se compara un número con una cadena no numérica. 0 == "abc" es true en 7.4 y false en 8.0, lo que invierte sin avisar algunas comparaciones laxas sobre valores de opciones.
  • PHP 8.0 añadió str_starts_with() y funciones relacionadas. Nuestro procesador de imágenes llama a str_starts_with(). En PHP 7.4 eso funciona solo porque WordPress 5.9 y posteriores incluyen un polyfill, algo que garantiza nuestro Requires at least: 5.9.
  • PHP 8.1 declaró obsoleto pasar null a parámetros no anulables de funciones internas. strtolower( wp_parse_url( $url, PHP_URL_HOST ) ) funciona sin problemas en 7.4 y registra un aviso de obsolescencia en 8.1 cuando $url es relativa y el host es null. Algunos hostings imprimen los avisos de obsolescencia en la página.
  • PHP 8.2 declaró obsoletas las propiedades dinámicas. Asignar un valor a una propiedad no declarada en una de sus propias clases registra un aviso de obsolescencia en cada petición.

Cada uno de estos cambios solo aparece cuando el código se analiza o se ejecuta en la versión de PHP que se comporta de forma distinta. Por eso el pipeline ejecuta cada pull request hacia master en las siete versiones a la vez.

La matriz de GitHub Actions

Ejecución de CI de un plugin de WordPress en GitHub Actions: pruebas en matriz de PHP con cinco jobs paralelos de WPCS y PHPStan en PHP 7.4, 8.0, 8.1, 8.2 y 8.3, todos superados

Figura 1: El workflow PHP Code Quality & Standards para el merge del trabajo de la 1.2.0 en master. Cada versión de PHP es un job independiente, y el job de PHP 8.3 muestra los pasos que ejecuta cada job: PHPCS, PHPStan y, después, las pruebas automatizadas.

Nota sobre la cobertura de versiones de PHP: Las capturas de pantalla anteriores muestran nuestras ejecuciones de CI anteriores, que cubrían de PHP 7.4 a 8.3. Hoy, nuestra matriz de pruebas en GitHub Actions se ha ampliado y cubre de PHP 7.4 a PHP 8.5, lo que garantiza una compatibilidad continua con los entornos de ejecución de PHP modernos sin regresiones en los entornos heredados.

YAML
jobs:  code-quality:    name: WPCS & PHPStan (PHP ${{ matrix.php-version }})    runs-on: ubuntu-26.04    strategy:      matrix:        php-version: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5']    steps:      - uses: actions/checkout@v7      - uses: shivammathur/setup-php@v2        with:          php-version: ${{ matrix.php-version }}          tools: composer:v2, cs2pr          ini-values: memory_limit=2G          coverage: none      - run: composer install --prefer-dist --no-progress --no-interaction      - name: Run PHPCS (WordPress Coding Standards)        run: vendor/bin/phpcs -q --standard=phpcs.xml.dist --report=checkstyle | cs2pr      - name: Run PHPStan (WordPress Static Analysis)        run: vendor/bin/phpstan analyse --memory-limit=2G      - name: Run the standalone tests        run: |          for t in tests/test_*.php; do            echo "== $t"            php "$t" || exit 1          done

fail-fast mantiene su valor predeterminado, true, así que cuando falla una versión, GitHub cancela los jobs que siguen en ejecución. Una pull request defectuosa cuesta poco, a cambio de no ver el resultado de cada versión en la misma ejecución.

Lo que la matriz demuestra y lo que no

Las tres comprobaciones no obtienen el mismo valor de la matriz. PHPStan analiza el código para la versión de PHP en la que se ejecuta, y las pruebas ejecutan el código en esa versión, así que ambas pueden dar una respuesta distinta en cada job. PHPCS trabaja sobre tokens y en la práctica da la misma respuesta en las siete; en la matriz demuestra sobre todo que las herramientas se instalan y se ejecutan en cada versión. Comprobar la sintaxis y las funciones contra un rango de versiones de PHP en una sola pasada es tarea de PHPCompatibility, que nuestro ruleset aún no incluye.

Automatizar los WordPress Coding Standards (WPCS)

Los sniffs de seguridad que importan en un plugin

WPCS es un conjunto de sniffs de PHP_CodeSniffer escritos para WordPress. Las reglas más importantes para la seguridad de un plugin están en el propio ruleset WordPress:

  • WordPress.Security.ValidatedSanitizedInput marca cualquier lectura de $_POST, $_GET, $_REQUEST o $_COOKIE a la que no se le quitan las barras invertidas o cuyo valor no se sanea.
  • WordPress.Security.EscapeOutput marca cualquier echo o print de un valor que no ha pasado por una función de escape como esc_html(), esc_attr() o esc_url().
  • WordPress.Security.NonceVerification marca un handler que lee datos de la petición sin verificar un nonce.
  • WordPress.DB.PreparedSQL y PreparedSQLPlaceholders marcan una consulta construida por concatenación de cadenas en lugar de con $wpdb->prepare(), y los placeholders que no coinciden con sus argumentos.

Nuestro ruleset de WPCS

phpcs.xml.dist añade WordPress-Extra y WordPress-Docs, fija el text domain para los sniffs de traducción y exige que cada función, clase y constante global lleve nuestro prefijo, para que el plugin no pueda colisionar con los nombres de otro plugin en el mismo sitio:

XML
<rule ref="WordPress">    <exclude name="Universal.Arrays.DisallowShortArraySyntax"/></rule><rule ref="WordPress-Extra"/><rule ref="WordPress-Docs"/>
<rule ref="WordPress.WP.I18n">    <properties>        <property name="text_domain" type="array">            <element value="smallpict"/>        </property>    </properties></rule>
<rule ref="WordPress.NamingConventions.PrefixAllGlobals">    <properties>        <property name="prefixes" type="array">            <element value="smallpict"/>            <element value="SmallPict"/>            <element value="SMALLPICT"/>        </property>    </properties></rule>

Un handler AJAX que pasa WPCS

Este handler del plugin restaura por lotes los medios trasladados al almacenamiento en la nube. Cada frontera de entrada se gestiona en sus primeras líneas:

PHP
public function ajax_restore_offloaded() {    if ( ! check_ajax_referer( 'smallpict_restore_offloaded', 'nonce', false ) || ! current_user_can( 'manage_options' ) ) {        wp_send_json_error( array( 'message' => 'forbidden' ), 403 );    }    global $wpdb;    $after = isset( $_POST['after'] ) ? absint( wp_unslash( $_POST['after'] ) ) : 0;    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- admin action, the rows change on every call.    $ids = $wpdb->get_col(        $wpdb->prepare(            "SELECT DISTINCT post_id FROM {$wpdb->postmeta} WHERE meta_key = %s AND meta_value = %s AND post_id > %d ORDER BY post_id ASC LIMIT %d",            '_smallpict_offloaded',            '1',            $after,            self::RESTORE_BATCH        )    );    // ...}

El nonce y la capability se comprueban juntos, porque un nonce válido demuestra que la petición viene de nuestra página de administración, no que el usuario pueda realizar la acción. A $_POST['after'] se le quitan las barras y se convierte con absint() antes de llegar a la consulta, y la consulta sigue pasando por $wpdb->prepare() aunque el valor ya sea un entero.

Las supresiones necesitan un nombre de sniff y un motivo

La línea phpcs:ignore de arriba es deliberada. WPCS avisa de las consultas directas a la base de datos sin caché de objetos, lo cual es un buen consejo para lecturas en el front-end y un error para una acción de administración cuyas filas cambian en cada llamada. Nuestra regla es que cada ignore nombre el sniff exacto y dé el motivo después de --. Un // phpcs:ignore a secas no pasa la revisión.

En CI, PHPCS escribe un informe checkstyle y cs2pr lo convierte en anotaciones sobre el diff de la pull request, de modo que el revisor ve cada infracción en la línea que la causó y no en un log.

Detectar fallos de lógica con el análisis estático de PHPStan

WPCS lee el código línea a línea. Sabe que a un echo le falta una llamada de escape, pero no qué tipo contiene una variable ni si una rama puede ejecutarse alguna vez. PHPStan construye un modelo de los tipos que fluyen por cada función e informa del código que lo contradice: llamar a un método que no existe, pasar un string|false donde se requiere un string, comparar valores que nunca pueden ser iguales, condiciones que siempre son verdaderas o siempre falsas.

Usar PHPStan con WordPress sin ejecutar WordPress

szepeviktor/phpstan-wordpress proporciona a PHPStan las firmas de las funciones y clases del núcleo de WordPress mediante archivos stub, además de algunas extensiones de tipos específicas de WordPress. phpstan/extension-installer lo registra automáticamente. PHPStan lee esas declaraciones y nunca carga WordPress ni una base de datos.

Nuestra configuración de PHPStan

YAML
parameters:    level: 5    paths:        - smallpict.php        - includes/        - admin/    treatPhpDocTypesAsCertain: false    bootstrapFiles:        - phpstan-bootstrap.php

El nivel 5 comprueba el tipo de cada argumento que se pasa a una función o método, además de lo que ya comprueban los niveles inferiores: clases, métodos y propiedades desconocidos, tipos de retorno y código muerto. treatPhpDocTypesAsCertain: false indica a PHPStan que no tome nuestros propios docblocks @param como prueba. WordPress pasa lo que devuelva un filtro, así que un parámetro documentado como int puede llegar como cadena desde otro plugin, y queremos que PHPStan mantenga las comprobaciones defensivas que cubren ese caso. El archivo bootstrap define las constantes del plugin (SMALLPICT_PATH, SMALLPICT_URL y otras), que normalmente se establecen en tiempo de ejecución.

Log del análisis estático de WordPress con PHPStan en GitHub Actions: paso de WPCS sin incidencias, PHPStan analizó 11 de 11 archivos del plugin con «[OK] No errors»

Figura 2: Los pasos de PHPCS y PHPStan del job de PHP 8.3. PHPCS no imprime nada cuando no hay nada que informar. PHPStan analiza los 11 archivos del plugin (el archivo principal y las 10 clases de includes/) y no informa de errores.

Un hallazgo real de PHPStan: la línea 626 del procesador de imágenes

El 2 de octubre de 2026, el pipeline falló en PHPStan con un único error:

PHPStan detecta un bug en un plugin de WordPress: «Right side of && is always true» en includes/class-smallpict-image-processor.php, línea 626, regla booleanAnd.rightAlwaysTrue, job fallido en PHP 8.2

Figura 3: El job de PHP 8.2 se detuvo en el paso de PHPStan con booleanAnd.rightAlwaysTrue en la línea 626 de includes/class-smallpict-image-processor.php. En ese momento el plugin tenía 9 archivos en las rutas analizadas.

El código es el último paso de una conversión. Cuando el ajuste Formato de salida del plugin es WebP o AVIF, el archivo convertido pasa a ser el adjunto y la subida original se elimina para ahorrar espacio en disco. Las otras opciones, «AVIF, con WebP como respaldo» y «Mantener el formato original», conservan los archivos JPEG y PNG originales. Esta era la rama de borrado:

PHP
// C. Pure 1-to-1 Mode: Delete raw uncompressed source file to free 100% hosting space.if ( ( $is_single_webp || $is_single_avif ) && $canonical_path !== $main_file_path && file_exists( $main_file_path ) ) {    wp_delete_file( $main_file_path );}

Según PHPStan, file_exists( $main_file_path ) siempre es verdadero en ese punto. La razón está 150 líneas más arriba, al principio del mismo método:

PHP
$main_file_path = get_attached_file( $attachment_id );if ( ! $main_file_path || ! file_exists( $main_file_path ) ) {    return new WP_Error( 'file_missing', 'File not found on disk.' );}

El método solo continúa cuando el archivo existe, y PHPStan arrastra ese hecho hasta la línea 626.

No aceptamos el hallazgo sin más. file_exists() consulta el sistema de archivos, y la respuesta puede cambiar entre dos llamadas. PHPStan recuerda por defecto el resultado de una llamada así (la opción rememberPossiblyImpureFunctionValues), suponiendo que nada entre medias ha modificado el disco. Antes de eliminar la comprobación, confirmamos dos cosas leyendo el código:

  1. Nada entre la guarda y la línea 626 borra o mueve el archivo fuente. La conversión escribe cada salida en un archivo temporal y la mueve a una ruta nueva, y las únicas llamadas a wp_delete_file() sobre esa ruta eliminan archivos temporales. El paso de traslado a la nube, que sí borra archivos locales, se ejecuta después de la línea 626.
  2. wp_delete_file() tolera de todos modos un archivo inexistente. La función pasa la ruta por el filtro wp_delete_file y llama a @unlink(), que no causa ningún daño si el archivo ya no está.

Con ambas cosas confirmadas, la condición era código muerto, y el código muerto en una rama de borrado lleva al siguiente lector a pensar que el archivo puede desaparecer a mitad del método. La corrección la eliminó:

DIFF
-if ( ( $is_single_webp || $is_single_avif ) && $canonical_path !== $main_file_path && file_exists( $main_file_path ) ) {+if ( ( $is_single_webp || $is_single_avif ) && $canonical_path !== $main_file_path ) {     wp_delete_file( $main_file_path ); }

El hallazgo no señalaba un fallo que rompiera la ejecución. Su valor estaba en lo que decía del método: las suposiciones del código se habían alejado de su estructura, justo en la rama que borra archivos de los usuarios. La lección que sacamos es más acotada que «confíe en el analizador». Un aviso de «siempre verdadero» o «siempre falso» sobre una función de sistema de archivos, de red o de tiempo merece una comprobación manual de lo que ocurre entre las dos llamadas antes de quitar la condición.

Pruebas automatizadas para un plugin de WordPress sin PHPUnit

El último paso de cada job de la matriz ejecuta los archivos de tests/. No son tests de PHPUnit. Cada archivo es un script PHP simple que define stubs mínimos para las funciones de WordPress que necesita (get_option(), update_option(), sanitize_key(), etc.), carga la clase del plugin que se prueba, la ejercita y termina con un código de salida distinto de cero en el primer fallo:

Shell
for t in tests/test_.php do  echo "== $t"  php "$t"  exit 1done

Hoy hay 9 scripts de este tipo. Cubren las rutas en las que un error le cuesta a un cliente datos o dinero: el pipeline de conversión a WebP y AVIF y sus fallbacks, la entrega de imágenes, el traslado y el borrado de archivos en el almacenamiento en la nube, la restauración por lotes de archivos trasladados, el comportamiento cuando una cuenta deja de pagar, la cuota de almacenamiento y la desinstalación. La prueba de desinstalación ejecuta las consultas reales de desinstalación contra una base de datos SQLite en memoria y comprueba lo que queda:

PHP
check(    array( '_smallpict_cdn_base', '_smallpict_cdn_key', '_smallpict_offloaded', '_smallpictx', '_wp_attached_file' ) === $meta,    'offload markers and foreign meta remain (got ' . implode( ',', $meta ) . ')');

Esa aserción protege una decisión fácil de deshacer por accidente. Al desinstalar el plugin se eliminan sus ajustes, pero se conservan los post meta que registran qué archivos existen solo en el almacenamiento en la nube, para que un sitio que reinstale el plugin pueda seguir encontrando sus imágenes. Un DELETE ... LIKE '_smallpict%' descuidado eliminaría esas marcas, y la prueba falla si eso ocurre.

Elegimos scripts simples en lugar de la suite de pruebas PHPUnit de WordPress por velocidad y coste de configuración: no necesitan servidor de base de datos ni una copia de WordPress, y los 9 se ejecutan en menos de un segundo, así que ejecutarlos en siete versiones de PHP apenas añade tiempo a una pull request. La contrapartida es que los stubs solo modelan lo que escribimos en ellos. El comportamiento que depende del núcleo real de WordPress, como el manejo de metadatos propio de la biblioteca de medios, queda fuera de estas pruebas.

El control previo al despliegue en WordPress.org

Las versiones se publican mediante un workflow aparte. Hacer push de un tag como v1.2.9 lo inicia, y el job que hace commit en el SVN de WordPress.org declara una dependencia de la matriz de lint:

YAML
jobs:  lint:    name: WPCS Check (PHP ${{ matrix.php-version }})    runs-on: ubuntu-26.04    strategy:      matrix:        php-version: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5']    steps:      - uses: actions/checkout@v7      - uses: shivammathur/setup-php@v2        with:          php-version: ${{ matrix.php-version }}      - run: composer install --prefer-dist --no-progress --no-interaction      - name: PHP CodeSniffer Check        run: vendor/bin/phpcs --standard=phpcs.xml.dist
  deploy:    name: Deploy    needs: lint    runs-on: ubuntu-26.04    permissions:      contents: read    steps:      - uses: actions/checkout@v7      - uses: shivammathur/setup-php@v2        with:          php-version: '7.4'      - name: Build WP.org Version        run: |          chmod +x build-wporg.sh          ./build-wporg.sh      - name: Deploy to WordPress.org        uses: 10up/action-wordpress-plugin-deploy@stable        with:          generate-zip: false        env:          SVN_USERNAME: ${{ secrets.SVN_USERNAME }}          SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}          SLUG: smallpict          BUILD_DIR: build-wporg/smallpict          ASSETS_DIR: .wordpress-org

needs: lint significa que el job deploy solo arranca cuando los siete jobs lint terminan con éxito. Si WPCS falla en cualquier versión, GitHub omite deploy y no se hace ningún commit en SVN. El paso de build se ejecuta en PHP 7.4, la versión más antigua que admitimos. El token del job tiene acceso de solo lectura al repositorio; su única escritura va al SVN de WordPress.org, con credenciales guardadas como secrets del repositorio.

Control previo al despliegue en el workflow de GitHub Actions Deploy to WordPress.org: WPCS Check superado en PHP 7.4, 8.0, 8.1, 8.2 y 8.3 antes de ejecutarse el job Deploy

Figura 4: El workflow Deploy to WordPress.org. Los cinco jobs WPCS Check se ejecutan primero; el job Deploy, debajo de ellos, solo se ejecuta cuando los cinco pasan.

El control vuelve a ejecutar WPCS sobre el commit etiquetado en lugar de fiarse de la ejecución anterior de la pull request. Un tag puede apuntar a cualquier commit, incluido uno subido directamente o uno cuyas comprobaciones nunca terminaron, y el workflow de publicación no tiene otra forma de saber qué está entregando.

Ejecutar las comprobaciones de CI en local

Los scripts de Composer envuelven los mismos comandos que ejecuta la CI, de modo que un fallo se puede reproducir antes de hacer push:

Shell
composer installcomposer lint        # vendor/bin/phpcs --standard=phpcs.xml.distcomposer lint-fix    # vendor/bin/phpcbf: fixes spacing and formatting sniffscomposer phpstan     # vendor/bin/phpstan analysefor t in tests/test_.php do php "$t"  break done

Para reproducir un fallo de un job concreto de la matriz, ejecute los mismos comandos con esa versión de PHP, por ejemplo con la imagen oficial después de composer install:

Shell
docker run --rm -v "$PWD":/app -w /app php:7.4-cli \  sh -c 'vendor/bin/phpstan analyse --memory-limit=2G && for t in tests/test_*.php; do php "$t" || exit 1; done'

phpcbf solo corrige lo que un sniff puede corregir de forma mecánica. Los hallazgos de escape, saneamiento y nonces requieren que una persona decida qué función encaja en el contexto, y no los corregimos automáticamente.

Lo que el pipeline aún no cubre

Hay tres carencias en nuestra lista:

  1. El control previo al despliegue solo ejecuta WPCS. PHPStan y las pruebas automatizadas se ejecutan en los push a master y develop y en las pull requests, no en el tag de versión. Añadir ambos pasos al job lint del workflow de despliegue cierra esa carencia.
  2. Nuestro ruleset no incluye PHPCompatibility. Añadir phpcompatibility/phpcompatibility-wp con una testVersion de 7.4- marcaría en una sola ejecución la sintaxis y las funciones que no existen en 7.4.
  3. Las pruebas usan stubs en lugar de una instalación real de WordPress. Un job de integración contra WordPress 5.9 (nuestro mínimo) y la última versión cubriría el comportamiento del núcleo que los stubs no pueden reproducir.

Preguntas frecuentes

¿Por qué son esenciales las pruebas en matriz de PHP 7.4 a 8.5 para los plugins de WordPress?

WordPress.org entrega la misma versión a todos los sitios, y la cabecera del plugin (Requires PHP: 7.4) es el único filtro sobre qué versiones de PHP lo instalan. Entre 7.4 y 8.5, PHP cambió cómo tratan las funciones internas los argumentos de tipo incorrecto, cómo se comparan los números con las cadenas y qué patrones generan avisos de obsolescencia, de modo que un código correcto en una versión puede provocar un error fatal o registrar advertencias en otra. Una ejecución de CI en una sola versión de PHP solo demuestra que el plugin funciona en esa versión. Ejecutar PHPStan y las pruebas en cada versión compatible detecta esas diferencias antes de que la versión llegue a los usuarios.

¿Sustituye WPCS a las auditorías de seguridad tradicionales?

No. WPCS comprueba patrones en la línea donde aparecen: una variable de la petición leída sin sanear, un echo sin escapar, un handler sin comprobación de nonce, una consulta sin $wpdb->prepare(). No puede saber si un handler comprueba la capability correcta, si la función de escape encaja con el contexto de salida, si una petición del lado del servidor va a una URL que controla un usuario o si un usuario puede acceder a los datos de otro. Un comentario phpcs:ignore también puede silenciar un sniff. Tratamos WPCS como el mínimo que deja fuera los errores conocidos y revisamos a mano los cambios relevantes para la seguridad.

¿Cómo maneja PHPStan las funciones del núcleo de WordPress sin ejecutar código?

PHPStan nunca ejecuta el código que analiza. Lee declaraciones y docblocks para construir un modelo de tipos. Para WordPress, szepeviktor/phpstan-wordpress proporciona archivos stub: las firmas, los tipos de parámetros y los tipos de retorno de las funciones y clases del núcleo de WordPress, sin su cuerpo. PHPStan lee esos stubs como si fueran WordPress, así que puede comprobar que una llamada a wp_delete_file() o get_attached_file() pasa y recibe los tipos correctos. Las constantes que WordPress o el plugin definen en tiempo de ejecución van en un archivo bootstrap, que PHPStan carga antes del análisis.

El plugin está en WordPress.org como smallPict. La instalación y los ajustes de Formato de salida se explican en la guía de instalación y en la referencia de ajustes de imagen.

Escrito por smallPict Engineering

Engineering

Desarrollamos conversión de imágenes y entrega por CDN para sitios web y aplicaciones.

Pruébalo con tus propias imágenes.

20 MB al mes gratis. Unos dos minutos de configuración en WordPress.

Más sobre rendimiento de imágenes, Core Web Vitals y almacenamiento en la nube.

Todos los artículos

Cloud & CDN3 min read

SmallPict API/SDK: BYOK vs. CDN administrada, ¿cuál deberías elegir?

Ignite no incluye CDN (los resultados vuelven directamente a tu aplicación); Velocity y Momentum te permiten elegir entre el almacenamiento y la CDN de SmallPict (la opción predeterminada) o tu propio almacenamiento y tu propia CDN. Así puedes decidir qué modelo se adapta a tu proyecto.