voltar ao blog

WordPress 7.0 e PHP moderno na prática: 6 recursos novos aplicados neste site

Semana passada eu parei tudo e fiz uma pergunta desconfortável para o meu próprio site: quanto do WordPress e do PHP modernos eu estou realmente usando? A resposta honesta era “menos do que eu recomendo aos clientes”. Então apliquei no tema da Logup, em produção, seis recursos recentes do WordPress (6.8 até o 7.0, que roda aqui) e do PHP — e este post mostra exatamente o que mudou, com código de verdade, e o que cada mudança entrega em performance, robustez e manutenção.

Eu sou o Filipe, fundador da Logup Digital, desenvolvedor full-stack há mais de 12 anos. O contexto: o site que você está lendo roda um tema próprio, sem page builder, num WordPress 7.0.4 com PHP 8.1. Tudo abaixo está no ar agora — não é lista de tendência, é changelog.

1. Navegação instantânea com Speculative Loading

Desde o WordPress 6.8 o core imprime regras da Speculation Rules API: uma tag <script type="speculationrules"> que instrui o navegador a carregar páginas antes do clique. O padrão do core é conservador (prefetch no início do clique). Eu subi o nível para prerender com gatilho no hover:

add_filter( 'wp_speculation_rules_configuration', static function ( $config ) {
    if ( is_array( $config ) ) {
        $config['mode']      = 'prerender';
        $config['eagerness'] = 'moderate';
    }
    return $config;
} );

Resultado prático: ao pousar o mouse (ou o dedo, no toque) sobre um link do blog, o navegador já renderiza a próxima página em segundo plano. O clique vira uma troca de tela instantânea. Sem plugin, sem JavaScript meu — e navegador sem suporte simplesmente ignora a tag.

Só que há uma pegadinha que quase ninguém comenta: página pré-renderizada executa JavaScript. Meu tracking first-party (que decide quais posts merecem investimento) contaria uma visita a cada hover — visita fantasma, métrica podre, decisão errada. A solução tem três linhas:

if (document.prerendering) {
    document.addEventListener('prerenderingchange', fireView, { once: true });
} else {
    fireView();
}

A visita só conta quando o visitante de fato ativa a página. Também excluí a página de contato da especulação, via wp_speculation_rules_href_exclude_paths. Se você usa GTM com pixels de conversão, confira isso hoje: há relatos documentados de “ghost visits” em sites WordPress 6.8+ exatamente por falta dessa guarda.

2. HTML API: aposentando regex que mexia em HTML

Aqui existe uma regra de negócio inegociável: o site nunca abre conversa de WhatsApp por conta própria — todo botão pede o número do visitante e quem chama sou eu. Posts antigos tinham links wa.me no corpo, e um filtro os reescrevia para o gatilho de captura usando expressão regular sobre o HTML.

Regex sobre HTML funciona até o dia em que não funciona: aspas simples, atributos em outra ordem, maiúsculas. Desde o WordPress 6.2 existe a HTML API, um parser de verdade dentro do core. O filtro novo:

$processor = new WP_HTML_Tag_Processor( $html );
while ( $processor->next_tag( 'a' ) ) {
    $href = (string) $processor->get_attribute( 'href' );
    if ( ! preg_match( '#^https?://(?:wa.me|api.whatsapp.com)/#i', $href ) ) {
        continue;
    }
    $processor->set_attribute( 'href', $contact );
    $processor->set_attribute( 'data-logup-capture', 'conteudo' );
    $processor->remove_attribute( 'target' );
    $processor->remove_attribute( 'rel' );
}
$html = $processor->get_updated_html();

O parser entende o markup como o navegador entende. O HTML nunca sai corrompido, e a regra mais importante do meu funil ficou à prova de edge case. Se o seu tema ou plugin faz preg_replace em cima de conteúdo HTML, essa é provavelmente a migração de melhor custo-benefício do WordPress moderno.

3. Interactivity API: o menu mobile virou declarativo

A Interactivity API é o framework reativo oficial do core (WordPress 6.5+), e a parte que pouca gente sabe: funciona em tema clássico, sem Gutenberg e sem build. O comportamento sai do JavaScript imperativo e vai para o próprio HTML, em diretivas:

<header class="navbar" data-wp-interactive="logup/menu"
        data-wp-class--is-open="state.menuOpen">
    <button data-wp-on--click="actions.toggle"
            data-wp-bind--aria-expanded="state.menuOpen"
            data-wp-bind--aria-label="state.menuLabel">…</button>
    <div class="mobile-menu" inert
         data-wp-bind--inert="!state.menuOpen">…</div>
</header>

E o estado vive num módulo ES de vinte e poucas linhas:

import { store } from "@wordpress/interactivity";

const { state } = store("logup/menu", {
    state: {
        get menuLabel() {
            return state.menuOpen ? "Fechar menu" : "Abrir menu";
        },
    },
    actions: {
        toggle() {
            state.menuOpen = !state.menuOpen;
        },
    },
});

O import é resolvido pelo import map que o próprio WordPress imprime — Script Modules API, também do core. Zero webpack, zero node_modules no servidor. As ~20 linhas antigas de classList, aria-expanded e malabarismo de tabindex viraram atributos que qualquer pessoa lê no template. De bônus, troquei o truque de tabindex="-1" link a link pelo atributo nativo inert, que tira o menu fechado da navegação por teclado e dos leitores de tela de uma vez.

4. Abilities API: o site descrito para agentes de IA

Essa é a mais estratégica. O WordPress 6.9 introduziu (e o 7.0 consolidou) a Abilities API: um registro padronizado do que o site sabe fazer — cada “ability” declara entrada e saída em JSON Schema, quem pode executar e como. É a fundação que o core construiu para a era dos agentes de IA e do MCP.

Registrei a primeira ability do site: um snapshot do meu funil de leads (visitas do dia, leads, estágios, pipeline), que vira endpoint REST autenticado e autodescritivo:

wp_register_ability( 'logup/growth-snapshot', array(
    'label'               => 'Snapshot do growth',
    'description'         => 'Resumo do dia: leads, visitas, funil e pipeline.',
    'category'            => 'logup-growth',
    'output_schema'       => array( /* JSON Schema */ ),
    'execute_callback'    => 'logup_ability_growth_snapshot',
    'permission_callback' => static fn (): bool => current_user_can( 'manage_options' ),
    'meta'                => array(
        'annotations'  => array( 'readonly' => true, 'idempotent' => true ),
        'show_in_rest' => true,
    ),
) );

Na prática: um agente autorizado consegue perguntar “como está o funil hoje?” e receber resposta estruturada, com permissão verificada pelo core — sem eu escrever controller, rota ou documentação. Quando alguém me pergunta se o site da empresa está pronto para a era da IA, é disso que eu estou falando: não é colocar um chatbot na home, é o site expor capacidades de forma que máquinas entendam com segurança.

5. PHP moderno de verdade: enums no lugar de strings soltas

Código WordPress típico ainda é escrito como se fosse PHP 7.4, porque é o mínimo que o core exige. Mas o PHP 8.1 — que é o que roda na minha VPS — já oferece muita coisa que elimina categorias inteiras de bug. A mudança mais bonita foi nos estágios do funil de vendas, que eram strings soltas espalhadas pelo código:

enum LeadStage: string {
    case Prospectado = 'prospectado';
    case Abordado    = 'abordado';
    // …
    case Entregue    = 'entregue';
    case Perdido     = 'perdido';

    public function weight(): float {
        return match ( $this ) {
            self::Prospectado => 0.05,
            self::Abordado    => 0.10,
            self::Engajado    => 0.25,
            self::Qualificado => 0.40,
            self::Proposta    => 0.60,
            self::Fechado, self::Entregue => 1.00,
            self::Perdido     => 0.00,
        };
    }
}

Um typo de estágio agora explode na hora em vez de virar linha errada no banco. O match é exaustivo: se eu criar um estágio novo e esquecer o peso dele, o PHP acusa — o array antigo aceitava calado. E a regra de caixa (entrada no fechamento, saldo na entrega) virou uma expressão match de cinco linhas em vez de um if/elseif mutável. No pacote foram também str_contains, nullsafe ($post?->ID), arrow functions e mais dois enums (flags de conteúdo e eventos de tracking).

Transparência: rodo PHP 8.1 porque é o pacote do Ubuntu 22.04, com patches da Canonical até 2027. O 8.3 destravaria json_validate() e constantes tipadas; o 8.4, property hooks e array_find(); o 8.5, o pipe operator. Está no mapa — mas a lição é que dá para modernizar muito sem tocar no servidor: enums, match e readonly já estavam disponíveis aqui, parados, esperando alguém usar.

6. As miudezas que somam

  • FAQ com <details name="faq"> — o atributo name nativo do HTML faz accordion exclusivo (abrir um fecha o outro) com zero JavaScript.
  • wp_preload_resources — o preload das fontes saiu de printf manual no <head> para o filtro oficial do core, que deduplica e ordena.
  • Limpeza de código morto — o WordPress 7.0 removeu o suporte html5 de script/style (todo script já sai sem type); o tema parou de declarar o que não existe mais.

O que isso significa se o site é o do seu negócio

Nada acima é enfeite de portfólio. Navegação instantânea segura visitante impaciente em rede móvel. Parser em vez de regex significa que a regra que gera meu lead não quebra com um post formatado diferente. Enum no funil significa que o número que eu olho de manhã está certo. E a Abilities API significa que o site já fala a língua da próxima onda — a dos agentes.

Atualização: o round 2 desta modernização já está no ar — IA nativa no WordPress 7.0: briefing automático de cada lead, usando o AI Client do core e o índice automático que você vê no topo deste artigo.

O padrão que eu vejo com frequência é o contrário: site em WordPress desatualizado, PHP de 2019, tema que ninguém consegue manter, e uma agência trocando de plugin quando algo quebra. Se você quer saber em que ponto o seu está — e o que valeria modernizar primeiro, com custo realista — me deixa seu WhatsApp aqui embaixo que eu olho e te falo o que encontrei, sem compromisso e sem jargão.