Depois de diagnosticar a poluição e definir a regra de reconstrução, chegou a parte que separa teoria de resultado: execução do saneamento de URLs no WordPress. Este artigo continua diretamente o case iniciado em Saneamento de URLs no WordPress, agora com foco total na Limpeza de URLs Hotmart com WP-CLI.
Aqui, o objetivo não é “rodar um comando e torcer”. É operar como engenharia: gerar comandos, auditar antes, executar em lote e só então validar. Isso reduz risco de corromper dados serializados e protege configurações de plugins como Rank Math e blocos do Gutenberg.
Você verá como usei scripts geradores para criar um arquivo .sh, executar centenas de substituições com WP-CLI e, depois, evoluir para uma reconstrução agressiva quando o banco mostrou anomalias reais de sintaxe.
E quando a limpeza termina, começa a parte mais importante: provar que o dado ficou limpo. Essa validação completa, com auditoria final, caches e impacto em BI e conversão, está em “Auditoria final e impacto“.
Índice
Execução da Limpeza (Round 1)
Com o diagnóstico feito e a lógica definida, chegamos à etapa de execução. No entanto, em operações de grande escala, não rodamos comandos destrutivos diretamente no banco de dados sem um plano de voo validado.
A estratégia adotada foi a criação de “Scripts Geradores”. Em vez de o PHP executar a limpeza, ele foi programado para escrever um arquivo de texto .sh (Shell Script) contendo a lista exata de comandos que deveriam ser rodados.
Essa camada intermediária é crucial para a segurança. Ela permite que revisemos cada linha de comando antes de autorizar a execução. O script PHP atua como o arquiteto que desenha a planta, e o script Bash é o engenheiro que executa a obra.
O coração dessa separação é a Expressão Regular (Regex) de captura. Precisávamos de uma instrução que dissesse ao computador: “Ignore o protocolo e o domínio, pegue apenas o ID do produto e isole todo o resto para análise”.
Utilizamos um padrão de regex agressivo para capturar a URL inteira, desde o http até encontrar um caractere delimitador (espaço, aspas ou sinal de maior/menor), garantindo que nada ficasse para trás.
Abaixo, o código do gerador que varre o banco e cria o arquivo de execução dinâmica:
<?php
/**
* Gerador de Script de Limpeza (Round 1)
* Gera um arquivo .sh com comandos WP-CLI para execução em lote.
*/
global $wpdb;
// Regex para capturar URL completa com sujeira
$regex = "/https?:\/\/[^\s\"'<]*hotmart\.com\/[a-zA-Z0-9]+[^\s\"'<]*/i";
$dirty_list = [];
$commands = [];
// Varredura nos posts
$posts = $wpdb->get_results("SELECT post_content FROM {$wpdb->posts} WHERE post_content LIKE '%hotmart.com%'");
foreach ($posts as $p) {
if (preg_match_all($regex, $p->post_content, $matches)) {
foreach ($matches[0] as $url) {
$dirty_list[] = stripslashes($url);
}
}
}
// Processamento da Lista
$dirty_list = array_unique($dirty_list);
foreach ($dirty_list as $dirty_url) {
// Aplica o "Funil de Decisão" (Lógica descrita na Fase de Lógica do Algoritmo de Limpeza)
// Se a URL suja for diferente da limpa, gera o comando
if ($dirty_url !== $clean_url) {
// Escapa aspas para o Bash e gera a linha de comando
$safe_dirty = str_replace("'", "'\''", $dirty_url);
$commands[] = "wp search-replace '$safe_dirty' '$clean_url' --all-tables";
}
}
// Salva o arquivo final para auditoria e execução
file_put_contents('executar_limpeza.sh', implode("\n", $commands));
echo "Arquivo de lote gerado com " . count($commands) . " instruções.\n";
Após rodar este gerador, obtivemos o arquivo executar_limpeza.sh. Este arquivo serviu como um log prévio de alterações. Pude abrir o arquivo, ler as substituições propostas e confirmar se a lógica do checkoutMode estava sendo respeitada.
A execução final foi feita em lote via terminal (bash executar_limpeza.sh). O WP-CLI processou centenas de substituições sequenciais, tratando a serialização de dados do WordPress automaticamente e garantindo a integridade dos metadados.
Refinamento e Casos “Teimosos” (Round 2)
Mesmo após a execução em lote, a auditoria revelou que cerca de 30% das URLs ainda mantinham resíduos. A limpeza inicial, embora robusta, seguiu regras de sintaxe padrão. O problema é que o banco de dados continha erros de sintaxe.
Identificando resíduos persistentes
Encontramos o que chamo de “anomalias de concatenação”. Eram links onde o separador de parâmetros (o ponto de interrogação ?) estava ausente, fundindo o ID do produto com o rastreamento, gerando algo como ID123src=facebook.
Além disso, havia o problema do “Lixo HTML”. Em blocos do Gutenberg e Elementor, o final da URL frequentemente trazia códigos como u0022 ou u003c colados ao parâmetro de checkout, sobrevivendo ao primeiro filtro.
Mudança de Estratégia: Reconstrução Agressiva
Diante disso, mudamos a lógica. Paramos de tentar “limpar” a cauda da URL. Adotamos a “Reconstrução Agressiva”. O novo script ignora totalmente o que vem após o ID do produto.
A lógica passou a ser: capturar o ID alfanumérico e verificar se a string checkoutMode existe em qualquer lugar da sujeira subsequente.
Se existir, recriamos a URL do zero: Base + ID + ?checkoutMode=10. Se não existir, a URL morre no ID. Isso garante que erros de digitação, falta de separadores ou lixo de código sejam sumariamente eliminados.
Abaixo, a implementação dessa lógica de reconstrução baseada no ID:
// Trecho do Script de Reconstrução Agressiva
// Regex captura o ID (Grupo 1) e isola todo o resto (Grupo 2)
$regex_capture = "/https?:\/\/[^\/]*hotmart\.com\/([a-zA-Z0-9]+)(.*)/i";
if (preg_match($regex_capture, $dirty_url, $parts)) {
$id = $parts[1]; // O ID puro (ex: Q92999519K)
$tail = $parts[2]; // O lixo (ex: src=fb&checkoutMode=10u0022...)
// Tratamento para IDs "contaminados" sem separador (ex: IDsrc=...)
$stop_words = ['src', 'utm', 'sck', '?'];
foreach ($stop_words as $word) {
$pos = stripos($id, $word);
if ($pos !== false) {
$id = substr($id, 0, $pos); // Corta o ID antes da contaminação
}
}
// Reconstrói a URL limpa baseada apenas na intenção
$base = "https://go.hotmart.com/" . $id;
// Se checkoutMode existir em qualquer lugar do lixo, ele é recriado limpo
if (stripos($dirty_url, 'checkoutmode=10') !== false) {
$clean_url = $base . "?checkoutMode=10";
} else {
$clean_url = $base;
}
}
O Script Localizador: Rastreando o “Paciente Zero”
Para os casos onde nem a reconstrução automática funcionou, precisamos entender o contexto. Criamos um “Script Localizador” que não apenas listava o erro, mas apontava sua localização exata: ID do Post e Chave Meta.
Isso nos permitiu descobrir se o erro estava no conteúdo visível ou escondido em um campo de Schema (Rank Math) ou configuração de bloco (GenerateBlocks). Identificar o “Paciente Zero” é vital para decidir entre uma correção manual pontual ou um novo script de lote.
// Trecho do Localizador de Resíduos
// Varre o banco e aponta exatamente onde o link sujo está hospedado
foreach ($posts as $p) {
if (preg_match_all($regex, $p->post_content, $matches)) {
foreach ($matches[0] as $url) {
if (is_dirty($url)) {
echo "\nURL Suja: " . $url . "\n";
echo "Local: Arquivo ID " . $p->ID . " (" . get_permalink($p->ID) . ")\n";
}
}
}
}
Após a execução do saneamento de URLs no WordPress
Após a execução em lote, o resultado técnico mais importante não é “reduzir variações”. É garantir que o WordPress continue íntegro: metadados funcionais, blocos preservados e URLs padronizadas sem efeitos colaterais.
Mas execução não encerra o trabalho. Em engenharia de dados, o script que roda não é o fim, é o começo da pergunta: como provar que não sobrou resíduo?
No próximo artigo, eu mostro a auditoria pós-limpeza, o checklist de pós-operatório (cache, transients e CSS) e como traduzir esse saneamento em dados confiáveis para análise e decisão.