Removendo links Markdown em PHP preservando o texto visível e o alt das imagens
Introdução
Às vezes você quer obter texto puro a partir de Markdown, mas não quer perder as partes legíveis para humanos.
Se a sua entrada contém [Laravel docs](https://laravel.com) ou , uma regex ingênua de "remover tudo que estiver entre parênteses" normalmente destrói o texto útil junto com a URL. Para previews, indexação de busca, pipelines de scraping e pré-processamento para IA, esse é o tradeoff errado.
O que você normalmente quer é isto:
- preservar o texto visível dos links normais
- preservar o alt das imagens
- remover links vazios e imagens vazias
- tratar corretamente casos onde uma imagem está aninhada dentro de um link
Neste artigo, vamos percorrer uma utilidade pequena em PHP que faz exatamente isso.
O Resultado Que Queremos
Estas são as transformações desejadas:
[Laravel docs](https://laravel.com)
=> Laravel docs

=> Diagrama de arquitetura
[](/post/system-design)
=> Diagrama de arquitetura
[](/internal-link)
=> ""

=> ""Esse comportamento é especialmente útil quando Markdown é apenas um formato intermediário e o objetivo real é obter texto puro limpo.
A Utilidade
Aqui está a implementação principal:
class MarkdownUtil
{
public static function removeLinks($markdown)
{
$patternWithImageAltText = '/!\[(.*?)\]\((.*?)\)/s';
$patternWithLinkText = '/\[(.*?)\]\((?![^\[]*?\!\[)(.*?)\)/s';
$markdown = preg_replace('/!\[\]\((.*?)\)/', '', $markdown);
$markdown = preg_replace_callback($patternWithImageAltText, function ($matches) {
return $matches[1];
}, $markdown);
$markdown = preg_replace_callback($patternWithLinkText, function ($matches) {
return $matches[1];
}, $markdown);
$markdown = preg_replace('/\[\]\((.*?)\)/', '', $markdown);
return $markdown;
}
}O método é curto, mas a ordem das substituições importa mais do que as regexes isoladamente.
Por Que a Ordem Importa
Esta utilidade funciona como um pipeline de quatro etapas:
- remover imagens vazias
- converter imagens para o seu alt
- converter links normais para o seu texto visível
- remover links vazios
Essa sequência evita que Markdown aninhado seja interpretado da forma errada.
Considere esta entrada:
[](/post/system-design)Se você processar os links primeiro, corre o risco de tratar toda a imagem interna como se fosse texto genérico de link. Ao processar as imagens primeiro, a entrada se torna:
[Diagrama de arquitetura](/post/system-design)Depois disso, o passe de links normais pode reduzi-la com segurança para:
Diagrama de arquiteturaEssa é a ideia principal do design: simplificar o Markdown em etapas em vez de tentar resolver tudo com uma única regex gigante.
O Padrão de Imagem
A regex para imagens é:
$patternWithImageAltText = '/!\[(.*?)\]\((.*?)\)/s';Ela captura:
- o alt dentro de
![ ... ] - o destino da imagem dentro de
( ... )
O callback retorna apenas o primeiro grupo capturado:
return $matches[1];Então isto:
vira isto:
Diagrama de arquiteturaO modificador /s torna o padrão mais tolerante a Markdown em várias linhas, porque . também pode corresponder a quebras de linha. Isso ajuda quando o texto do link, o texto alternativo ou o destino se quebram em mais de uma linha.
O Padrão de Link
A regex para links normais é:
$patternWithLinkText = '/\[(.*?)\]\((?![^\[]*?\!\[)(.*?)\)/s';A parte importante é o negative lookahead:
(?![^\[]*?\!\[)Isso ajuda o padrão a não consumir conteúdo com formato de imagem como se fosse o corpo de um link comum. Na prática, isso deixa o passe de links mais seguro perto de Markdown aninhado.
Assim como no caso das imagens, o callback retorna apenas o texto legível:
return $matches[1];Então isto:
[Laravel docs](https://laravel.com)vira isto:
Laravel docsRemovendo Nós Markdown Vazios
A utilidade remove explicitamente duas formas que não acrescentam texto legível:
$markdown = preg_replace('/!\[\]\((.*?)\)/', '', $markdown);
$markdown = preg_replace('/\[\]\((.*?)\)/', '', $markdown);Isso cobre:
- imagens vazias como
 - links vazios como
[](/internal-link)
É um detalhe pequeno, mas importante. Se você os mantiver, a saída pode terminar com placeholders sem sentido ou pontuação solta.
Casos de Uso Reais
Esse tipo de utilidade é útil quando Markdown não é o formato final:
- converter HTML raspado para Markdown e depois comprimi-lo como texto puro
- construir pipelines de busca ou indexação que devam ignorar URLs cruas
- gerar previews de conteúdo onde o texto visível importa mais do que o destino do link
- limpar entrada para IA ou para sumários antes de enviar para outra etapa do pipeline
Um fluxo especialmente prático é:
- converter HTML para Markdown
- substituir links e imagens pelo texto visível
- comprimir espaços
- salvar o resultado como texto puro para etapas posteriores
Assim você preserva o conteúdo legível enquanto remove ruído de links, URLs de tracking e imagens embutidas.
Casos de Teste Que Vale a Pena Manter
O valor desta utilidade não está apenas na regex. Ele está no conjunto de edge cases cobertos pelos testes.
Vale cobrir:
- links normais com texto
- links com query strings e caracteres especiais
- imagens com alt
- imagens com alt vazio
- imagens aninhadas dentro de links
- vários links e imagens na mesma string
- entrada vazia
- Markdown em várias linhas com imagens base64
Um exemplo representativo é este caso com imagem base64:
$text = <<<'EOT'
Hello,
the email was verified. 
EOT;
$expected = <<<'EOT'
Hello,
the email was verified. image
EOT;Esse teste prova que a utilidade não está limitada a imagens com caminhos curtos em formato de URL.
Onde Regex Basta e Onde Não Basta
Esta é uma solução pragmática com regex, não um parser completo de Markdown.
Ela não pretende cobrir todas as variações de Markdown. Por exemplo, links por referência como [docs][1], autolinks como <https://example.com>, casos com colchetes escapados e algumas URLs com parênteses podem permanecer sem alteração ou gerar uma saída imperfeita.
Isso é uma vantagem quando:
- você controla o formato do Markdown
- precisa de um helper leve e sem dependências
- se importa com uma transformação específica e não com compatibilidade total com CommonMark
Ela se torna uma opção mais fraca quando:
- a entrada pode conter Markdown muito irregular
- você precisa preservar formatação complexa e profundamente aninhada
- precisa suportar todos os edge cases da especificação Markdown
Para pipelines controlados dentro de uma aplicação, no entanto, esse tradeoff costuma ser exatamente o certo. Uma utilidade focada com regex é mais fácil de manter, mais fácil de testar e muito mais fácil de explicar.
Considerações Finais
A ideia útil aqui não é "usar regex em Markdown" de forma abstrata. É "usar uma transformação em etapas com intenção explícita".
Esta utilidade funciona porque define uma política clara:
- o texto visível dos links fica
- o alt das imagens fica
- nós vazios desaparecem
- casos aninhados são simplificados na ordem correta
Se você precisa obter texto puro limpo a partir de Markdown em PHP, essa política oferece uma solução compacta e pronta para produção sem precisar trazer um parser completo.
