Pular para o conteúdo
smallPict
Começar Grátis

Engineering20 min read

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

CI/CD para plugins WordPress no GitHub Actions: WPCS, PHPStan e testes automatizados no PHP 7.4 a 8.5, com um gate pré-deploy antes do WordPress.org.

smallPict Engineering

Engineering

O plugin WordPress da smallPict converte uploads para WebP e AVIF e os entrega aos visitantes. Quando criamos a tag de uma release, o GitHub Actions faz o commit dela no repositório SVN do WordPress.org, e de lá ela chega a todos os sites que têm o plugin instalado. Sites com atualizações automáticas ativadas instalam a release sem que ninguém revise a mudança. O plugin declara Requires PHP: 7.4 e Requires at least: 5.9, então o mesmo build precisa rodar tanto em uma hospedagem compartilhada ainda no PHP 7.4 quanto em um container com PHP 8.5.

Um fatal error em um plugin pode derrubar o wp-admin. Um valor sem escape em uma página de configurações é um bug de XSS em todos os sites ao mesmo tempo. Uma verificação de nonce ausente transforma uma ação administrativa em alvo de CSRF. Nenhum desses problemas pode ser corrigido discretamente: a correção exige outra release, e alguns sites continuam com a versão quebrada até ela chegar.

Este artigo descreve nosso pipeline de CI/CD para o plugin WordPress: os WordPress Coding Standards (WPCS), o PHPStan e os testes automatizados que rodam em sete versões do PHP, e o gate pré-deploy antes do WordPress.org. Tudo o que vem a seguir é do repositório smallpict-wp na release 1.2.9.

Principais pontos

  • Todo pull request para master roda WPCS, PHPStan e 9 scripts de testes automatizados no PHP 7.4, 8.0, 8.1, 8.2, 8.3, 8.4 e 8.5 em paralelo no GitHub Actions.
  • O WPCS exige input sanitizado, output com escape, nonces e queries preparadas. O PHPStan no nível 5 encontra erros de tipo e branches mortos a partir dos stubs do WordPress, sem executar o WordPress.
  • Uma tag de release roda o WPCS de novo nas sete versões do PHP, e o job que faz o commit no SVN do WordPress.org só começa depois que as sete passam.

Visão geral do pipeline de CI/CD do plugin WordPress

Dois workflows do GitHub Actions protegem o plugin.

WorkflowDispara emJobsVerificações
phpcs.yml (PHP Code Quality & Standards)push para master ou develop, pull requests para masterum job por versão do PHP: 7.4, 8.0, 8.1, 8.2, 8.3, 8.4, 8.5WPCS, PHPStan, testes automatizados
deploy-all.yml (Deploy to WordPress.org)push de uma tag v*WPCS em cada uma das 7 versões do PHP, depois deployWPCS, depois build e commit no SVN

Nota sobre a cobertura de versões do PHP: As capturas de tela deste artigo mostram nossas execuções de CI anteriores, que cobriam do PHP 7.4 ao 8.3. Hoje, a matriz de testes do GitHub Actions foi ampliada para cobrir do PHP 7.4 ao PHP 8.5, garantindo compatibilidade contínua com versões modernas do PHP sem regressões em ambientes legados.

Todas as ferramentas são dependências de desenvolvimento do Composer, então nada disso chega aos usuários:

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"}

Testes em matriz de versões do PHP no GitHub Actions

O que muda entre o PHP 7.4 e o PHP 8.5

O PHP 7.4 e o PHP 8.5 executam o mesmo código-fonte do plugin de formas diferentes. Algumas das mudanças que afetam o código de plugins WordPress:

  • O PHP 8.0 fez muitas funções internas lançarem um TypeError para um argumento do tipo errado, onde o 7.4 retornava null com um warning. Código que passava um array para strlen() continuava rodando no 7.4 e para no 8.0.
  • O PHP 8.0 mudou a forma como um número é comparado com uma string não numérica. 0 == "abc" é true no 7.4 e false no 8.0, o que inverte sem aviso algumas comparações frouxas em valores de opções.
  • O PHP 8.0 adicionou str_starts_with() e funções relacionadas. Nosso image processor chama str_starts_with(). No PHP 7.4 isso só funciona porque o WordPress 5.9 e posteriores trazem um polyfill, garantido pelo nosso Requires at least: 5.9.
  • O PHP 8.1 tornou obsoleto (deprecated) passar null para parâmetros não anuláveis de funções internas. strtolower( wp_parse_url( $url, PHP_URL_HOST ) ) funciona no 7.4 e registra uma deprecation no 8.1 quando $url é relativa e o host é null. Algumas hospedagens imprimem deprecations na página.
  • O PHP 8.2 tornou obsoletas as propriedades dinâmicas. Atribuir um valor a uma propriedade não declarada em uma das suas próprias classes registra uma deprecation a cada requisição.

Cada um desses problemas só aparece quando o código é analisado ou executado na versão do PHP que se comporta de forma diferente. Por isso o pipeline roda todo pull request para master nas sete versões ao mesmo tempo.

A matriz do GitHub Actions

Execução de CI de um plugin WordPress no GitHub Actions: testes em matriz do PHP com cinco jobs WPCS e PHPStan em paralelo no PHP 7.4, 8.0, 8.1, 8.2 e 8.3, todos aprovados

Figura 1: O workflow PHP Code Quality & Standards no merge do trabalho da 1.2.0 em master. Cada versão do PHP é um job separado, e o job do PHP 8.3 mostra os steps que todo job executa: PHPCS, PHPStan e depois os testes automatizados.

Nota sobre a cobertura de versões do PHP: As capturas de tela acima mostram nossas execuções de CI anteriores, que cobriam do PHP 7.4 ao 8.3. Hoje, a matriz de testes do GitHub Actions foi ampliada para cobrir do PHP 7.4 ao PHP 8.5, garantindo compatibilidade contínua com versões modernas do PHP sem regressões em ambientes legados.

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 mantém o valor padrão true, então quando uma versão falha, o GitHub cancela os jobs que ainda estão rodando. Um pull request quebrado custa pouco, ao preço de não ver o resultado de todas as versões na mesma execução.

O que a matriz prova, e o que não prova

As três verificações não tiram o mesmo proveito da matriz. O PHPStan analisa o código para a versão do PHP em que roda, e os testes executam o código nessa versão, então os dois podem dar uma resposta diferente em cada job. O PHPCS trabalha com tokens e, na prática, dá a mesma resposta nas sete; na matriz, ele prova principalmente que a toolchain instala e roda em cada versão. Verificar sintaxe e funções contra uma faixa de versões do PHP em uma única passada é tarefa do PHPCompatibility, que nosso ruleset ainda não inclui.

Automatizando os WordPress Coding Standards (WPCS)

Os sniffs de segurança que importam para um plugin

O WPCS é um conjunto de sniffs do PHP_CodeSniffer escritos para o WordPress. As regras que mais importam para a segurança de plugins estão no próprio ruleset WordPress:

  • WordPress.Security.ValidatedSanitizedInput sinaliza qualquer leitura de $_POST, $_GET, $_REQUEST ou $_COOKIE que não passe por unslash e sanitização.
  • WordPress.Security.EscapeOutput sinaliza qualquer echo ou print de um valor que não passou por uma função de escape como esc_html(), esc_attr() ou esc_url().
  • WordPress.Security.NonceVerification sinaliza um handler que lê dados da requisição sem verificar um nonce.
  • WordPress.DB.PreparedSQL e PreparedSQLPlaceholders sinalizam uma query montada por concatenação de strings em vez de $wpdb->prepare(), e placeholders que não correspondem aos seus argumentos.

Nosso ruleset do WPCS

phpcs.xml.dist adiciona WordPress-Extra e WordPress-Docs, fixa o text domain para os sniffs de tradução e exige que toda função, classe e constante global tenha nosso prefixo, para que o plugin não possa colidir com os nomes de outro plugin no mesmo site:

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>

Um handler AJAX que passa no WPCS

Este handler do plugin restaura em lotes as mídias enviadas para o armazenamento em nuvem. Todas as entradas externas são tratadas nas primeiras linhas:

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        )    );    // ...}

O nonce e a capability são verificados juntos, porque um nonce válido prova que a requisição veio da nossa página de administração, não que o usuário tenha permissão para executar a ação. $_POST['after'] passa por unslash e é convertido com absint() antes de chegar à query, e a query ainda passa por $wpdb->prepare() mesmo com o valor já sendo um inteiro.

Supressões exigem o nome do sniff e um motivo

A linha phpcs:ignore acima é intencional. O WPCS alerta sobre queries diretas ao banco de dados sem object caching, o que é um bom conselho para leituras no front-end e errado para uma ação administrativa cujas linhas mudam a cada chamada. Nossa regra é que todo ignore cite o sniff exato e dê o motivo depois de --. Um // phpcs:ignore sozinho não passa na revisão.

Na CI, o PHPCS gera um relatório checkstyle e o cs2pr o transforma em anotações no diff do pull request, para que quem revisa veja cada violação na linha que a causou em vez de em um log.

Encontrando falhas de lógica com a análise estática do PHPStan

O WPCS lê o código linha por linha. Ele sabe que falta uma chamada de escape em um echo, mas não que tipo uma variável contém nem se um branch pode chegar a ser executado. O PHPStan monta um modelo dos tipos que passam por cada função e aponta o código que o contradiz: chamar um método que não existe, passar um string|false onde se exige um string, comparar valores que nunca podem ser iguais, condições sempre verdadeiras ou sempre falsas.

Usando o PHPStan com o WordPress sem executar o WordPress

szepeviktor/phpstan-wordpress fornece ao PHPStan as assinaturas das funções e classes do core do WordPress por meio de arquivos stub, além de algumas extensões de tipo específicas do WordPress. phpstan/extension-installer registra o pacote automaticamente. O PHPStan lê essas declarações e nunca carrega o WordPress nem um banco de dados.

Nossa configuração do PHPStan

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

O nível 5 verifica o tipo de cada argumento passado para uma função ou método, além das verificações dos níveis inferiores sobre classes, métodos e propriedades desconhecidos, tipos de retorno e código morto. treatPhpDocTypesAsCertain: false diz ao PHPStan para não tratar nossos próprios docblocks @param como prova. O WordPress repassa o que quer que um filtro retorne, então um parâmetro documentado como int ainda pode chegar como string vindo de outro plugin, e queremos que o PHPStan mantenha as verificações defensivas que tratam disso. O arquivo de bootstrap define as constantes do plugin (SMALLPICT_PATH, SMALLPICT_URL e outras), que normalmente são definidas em tempo de execução.

Log da análise estática do PHPStan para WordPress no GitHub Actions: step do WPCS sem erros, PHPStan analisou 11 de 11 arquivos do plugin com "[OK] No errors"

Figura 2: Os steps do PHPCS e do PHPStan no job do PHP 8.3. O PHPCS não imprime nada quando não há nada a reportar. O PHPStan analisa os 11 arquivos do plugin (o arquivo principal e as 10 classes em includes/) e não reporta erros.

Um achado real do PHPStan: a linha 626 do image processor

Em 2 de outubro de 2026, o pipeline falhou no PHPStan com um erro:

PHPStan detectando um bug em um plugin WordPress: "Right side of && is always true" em includes/class-smallpict-image-processor.php linha 626, regra booleanAnd.rightAlwaysTrue, job falhou no PHP 8.2

Figura 3: O job do PHP 8.2 parou no step do PHPStan com booleanAnd.rightAlwaysTrue na linha 626 de includes/class-smallpict-image-processor.php. Na época, o plugin tinha 9 arquivos nos caminhos analisados.

O código é a última etapa de uma conversão. Quando a configuração Formato de saída do plugin está em WebP ou AVIF, o arquivo convertido passa a ser o anexo e o upload original é excluído para economizar espaço em disco. As outras opções, "AVIF, com WebP como alternativa" e "Manter o formato original", mantêm os arquivos JPEG e PNG originais. Este era o branch de exclusão:

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 );}

O PHPStan afirmava que file_exists( $main_file_path ) é sempre verdadeiro naquele ponto. O motivo está 150 linhas antes, no início do mesmo 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.' );}

O método só continua quando o arquivo existe, e o PHPStan leva essa informação adiante até a linha 626.

Não aceitamos o achado sem questionar. file_exists() consulta o sistema de arquivos, e a resposta pode mudar entre duas chamadas. Por padrão, o PHPStan memoriza o resultado de uma chamada desse tipo (a opção rememberPossiblyImpureFunctionValues), assumindo que nada no meio do caminho alterou o disco. Antes de remover a verificação, confirmamos duas coisas lendo o código:

  1. Nada entre a guarda e a linha 626 exclui ou move o arquivo de origem. A conversão grava cada saída em um arquivo temporário e o move para um novo caminho, e as únicas chamadas a wp_delete_file() nesse caminho removem arquivos temporários. A etapa de offload, que de fato exclui arquivos locais, roda depois da linha 626.
  2. wp_delete_file() tolera um arquivo ausente de qualquer forma. Ela passa o caminho pelo filtro wp_delete_file e chama @unlink(), que não causa nenhum dano quando o arquivo já não existe.

Com as duas coisas confirmadas, a condição era código morto, e código morto em um branch de exclusão leva o próximo leitor a pensar que o arquivo pode sumir no meio do método. A correção a removeu:

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 ); }

O achado não era um crash. O valor dele estava no que dizia sobre o método: as premissas do código tinham se afastado da sua estrutura, justamente no único branch que exclui arquivos de usuários. A lição que tiramos é mais restrita do que "confie no analisador". Um aviso de condição sempre verdadeira ou sempre falsa sobre uma função de sistema de arquivos, rede ou tempo merece uma verificação manual do que acontece entre as duas chamadas antes de remover a condição.

Testes automatizados para um plugin WordPress sem PHPUnit

O último step de cada job da matriz executa os arquivos em tests/. Não são testes PHPUnit. Cada arquivo é um script PHP simples que define stubs mínimos para as funções do WordPress de que precisa (get_option(), update_option(), sanitize_key() e assim por diante), carrega a classe do plugin em teste, a exercita e sai com código diferente de zero na primeira falha:

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

Hoje existem 9 scripts desse tipo. Eles cobrem os caminhos em que um erro custa dados ou dinheiro a um cliente: o pipeline de conversão para WebP e AVIF e seus fallbacks, a entrega de imagens, o offload e a exclusão de arquivos no armazenamento em nuvem, a restauração em lotes de arquivos enviados para a nuvem, o comportamento quando uma conta para de pagar, a cota de armazenamento e a desinstalação. O teste de desinstalação roda as queries reais de desinstalação contra um banco SQLite em memória e verifica o que sobra:

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 ) . ')');

Essa asserção protege uma decisão fácil de desfazer por acidente. Desinstalar o plugin remove as configurações dele, mas mantém os post meta que registram quais arquivos existem apenas no armazenamento em nuvem, para que um site que reinstale o plugin ainda encontre suas imagens. Um DELETE ... LIKE '_smallpict%' descuidado removeria esses marcadores, e o teste falha se isso acontecer.

Escolhemos scripts simples em vez da suíte de testes PHPUnit do WordPress por velocidade e custo de setup: eles não precisam de servidor de banco de dados nem de um checkout do WordPress, e os 9 rodam em menos de um segundo, então executá-los em sete versões do PHP quase não acrescenta tempo a um pull request. A contrapartida é que os stubs só modelam o que escrevemos neles. Comportamentos que dependem do core real do WordPress, como o tratamento de metadados da própria biblioteca de mídia, não são cobertos por esses testes.

O gate pré-deploy para o WordPress.org

As releases saem por um workflow separado. O push de uma tag como v1.2.9 o dispara, e o job que faz o commit no SVN do WordPress.org declara uma dependência da 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 o job deploy só começa quando os sete jobs lint terminam com sucesso. Se o WPCS falhar em qualquer versão, o GitHub pula o deploy e nenhum commit no SVN acontece. O step de build roda no PHP 7.4, a versão mais antiga que suportamos. O token do job tem acesso somente leitura ao repositório; a única escrita dele é no SVN do WordPress.org, com credenciais guardadas como secrets do repositório.

Gate pré-deploy no workflow Deploy to WordPress.org do GitHub Actions: WPCS Check aprovado no PHP 7.4, 8.0, 8.1, 8.2 e 8.3 antes da execução do job Deploy

Figura 4: O workflow Deploy to WordPress.org. Os cinco jobs WPCS Check rodam primeiro; o job Deploy abaixo deles só roda depois que os cinco passam.

O gate roda o WPCS de novo no commit da tag em vez de confiar na execução anterior do pull request. Uma tag pode apontar para qualquer commit, inclusive um enviado diretamente ou um cujas verificações nunca terminaram, e o workflow de release não tem outra forma de saber o que está publicando.

Rodando as verificações de CI localmente

Os scripts do Composer encapsulam os mesmos comandos que a CI executa, então uma falha pode ser reproduzida antes do 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 reproduzir a falha de um job específico da matriz, rode os mesmos comandos nessa versão do PHP, por exemplo com a imagem oficial depois do 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'

O phpcbf corrige apenas o que um sniff consegue corrigir mecanicamente. Achados de escape, sanitização e nonce exigem que uma pessoa decida qual função se encaixa no contexto, e não os corrigimos automaticamente.

O que o pipeline ainda não cobre

Temos três lacunas na lista:

  1. O gate pré-deploy roda apenas o WPCS. O PHPStan e os testes automatizados rodam em pushes para master e develop e em pull requests, não na tag de release. Adicionar os dois steps ao job lint do workflow de deploy fecha essa lacuna.
  2. Nosso ruleset não inclui o PHPCompatibility. Adicionar phpcompatibility/phpcompatibility-wp com um testVersion de 7.4- apontaria, em uma única execução, sintaxe e funções que não existem no 7.4.
  3. Os testes usam stubs em vez de uma instalação real do WordPress. Um job de integração contra o WordPress 5.9 (nosso mínimo) e a release mais recente cobriria o comportamento do core que os stubs não conseguem reproduzir.

FAQ

Por que testes em matriz do PHP 7.4 ao 8.5 são essenciais para plugins WordPress?

O WordPress.org entrega a mesma release a todos os sites, e o cabeçalho do plugin (Requires PHP: 7.4) é o único filtro sobre quais versões do PHP a instalam. Entre o 7.4 e o 8.5, o PHP mudou como as funções internas tratam argumentos do tipo errado, como números são comparados com strings e quais padrões geram deprecations, então um código correto em uma versão pode dar fatal error ou registrar warnings em outra. Uma execução de CI em uma única versão do PHP só prova que o plugin funciona nessa versão. Rodar o PHPStan e os testes em cada versão suportada pega essas diferenças antes que a release chegue aos usuários.

O WPCS substitui auditorias de segurança tradicionais?

Não. O WPCS verifica padrões na linha em que eles aparecem: uma variável da requisição lida sem sanitização, um echo sem escape, um handler sem verificação de nonce, uma query sem $wpdb->prepare(). Ele não consegue dizer se um handler verifica a capability certa, se a função de escape combina com o contexto de saída, se uma requisição do lado do servidor vai para uma URL controlada por um usuário ou se um usuário consegue acessar os dados de outro. Um comentário phpcs:ignore também pode silenciar um sniff. Tratamos o WPCS como o piso que barra erros conhecidos e revisamos manualmente as mudanças relevantes para a segurança.

Como o PHPStan lida com as funções do core do WordPress sem executar código?

O PHPStan nunca executa o código que analisa. Ele lê declarações e docblocks para montar um modelo de tipos. Para o WordPress, szepeviktor/phpstan-wordpress fornece arquivos stub: as assinaturas, os tipos de parâmetros e os tipos de retorno das funções e classes do core do WordPress, sem o corpo delas. O PHPStan lê esses stubs como se fossem o WordPress, então consegue verificar se uma chamada a wp_delete_file() ou get_attached_file() passa e recebe os tipos certos. Constantes que o WordPress ou o plugin definem em tempo de execução ficam em um arquivo de bootstrap, que o PHPStan carrega antes da análise.

O plugin está no WordPress.org como smallPict. A instalação e as configurações de Formato de saída estão no guia de instalação e na referência de configurações de imagem.

Escrito por smallPict Engineering

Engineering

Desenvolvemos conversão de imagens e entrega por CDN para sites e apps.

Teste com as suas próprias imagens.

20 MB por mês grátis. Cerca de dois minutos para configurar no WordPress.

Mais sobre desempenho de imagens, Core Web Vitals e armazenamento em nuvem.

Todos os artigos

Cloud & CDN3 min read

SmallPict API/SDK: BYOK vs. CDN Gerenciado, qual você deve escolher?

O Ignite não tem CDN (os resultados voltam direto para o seu app); no Velocity e no Momentum você escolhe entre o armazenamento e a CDN do SmallPict (o padrão) ou seu próprio armazenamento e sua própria CDN. Veja como decidir qual modelo combina com o seu projeto.