Atualizado em 19/06/2026
Toda customização de funcionalidade que você quer manter quando trocar de tema, atualizar o site ou migrar de servidor deveria estar em um plugin. Um plugin é apenas um diretório com pelo menos um arquivo PHP que o WordPress reconhece por um comentário especial no topo. A partir daí, ele se "pendura" no WordPress por meio de hooks. Este guia mostra, na ordem prática, como montar um do zero seguindo os padrões oficiais — para que o seu código não quebre na próxima atualização.
1. Por que um plugin, e não o functions.php do tema
A pergunta aparece sempre: "por que não jogo esse código no functions.php do tema?". Porque o functions.php pertence ao tema. No dia em que você trocar de tema, ou em que o tema receber uma atualização que sobrescreva o arquivo, todo o código somado ali some junto. Um plugin é independente: continua ativo mesmo que a aparência mude.
A linha divisória é clara:
- Tema (
functions.php): código de apresentação — registrar menus, áreas de widget, tamanhos de imagem, suportes daquele visual específico. - Plugin: código de funcionalidade — qualquer coisa que deva sobreviver à troca de tema (um shortcode, uma integração, um custom post type, regras de negócio).
- mu-plugin (
wp-content/mu-plugins/): código crítico que nunca deve ser desativado pelo painel — carregado sempre, sem aparecer na lista normal de plugins.
2. Estrutura mínima de um plugin
Um plugin pode ser um único arquivo, mas o padrão recomendado é uma pasta própria dentro de wp-content/plugins/, com o arquivo principal de mesmo nome:
wp-content/
└── plugins/
└── meu-plugin/
├── meu-plugin.php <-- arquivo principal (com o cabeçalho)
└── uninstall.php <-- limpeza ao desinstalar (opcional)
Use sempre minúsculas e hífens no nome da pasta e do arquivo (o slug do plugin). À medida que cresce, você adiciona subpastas como includes/, admin/ e assets/, mas o WordPress só exige uma coisa para reconhecer o plugin: o cabeçalho no arquivo principal.
3. O cabeçalho do plugin (obrigatório)
O WordPress identifica um plugin lendo um bloco de comentário no topo do arquivo principal. Sem ele — mais precisamente, sem a linha Plugin Name: — o arquivo nem aparece na tela de Plugins. Junte a isso a guarda de acesso direto e você tem o esqueleto inicial:
<?php
/**
* Plugin Name: Meu Plugin
* Description: Faz algo útil via hooks, sem mexer no tema.
* Version: 1.0.0
* Requires at least: 6.0
* Requires PHP: 7.4
* Author: Roger Takemiya
* License: GPL-2.0-or-later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: meu-plugin
*/
// Guarda: impede o acesso direto ao arquivo pela URL.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
Cada linha do cabeçalho tem função: Plugin Name é a única obrigatória; Version controla cache e atualizações; Text Domain liga o plugin às traduções; License declara a licença (o WordPress.org exige compatível com a GPL). Os campos e a sintaxe exata estão documentados em Header Requirements.
4. A guarda de segurança: ABSPATH
A constante ABSPATH só existe quando o WordPress foi carregado. Se alguém abrir meu-plugin.php direto no navegador, ela não estará definida e a linha if ( ! defined( 'ABSPATH' ) ) exit; encerra o script imediatamente — sem executar nada, sem vazar caminhos ou variáveis. Essa guarda deve estar no topo de todo arquivo PHP do plugin, não só no principal. É a primeira camada de uma estratégia maior de segurança no código, que inclui sanitizar entradas e escapar saídas.
5. Prefixe tudo (evite colisões)
Todos os plugins, o tema e o núcleo compartilham o mesmo escopo global. Se dois deles declararem uma função setup() ou um add_option( 'config', ... ), você terá um erro fatal ou dados sobrescritos. A defesa é prefixar tudo com algo único do seu projeto:
- Funções:
dwp_registrar_assets()em vez deregistrar_assets(). - Opções e meta:
dwp_versao_bancoem vez deversao. - Hooks personalizados:
do_action( 'dwp_apos_processar' ). - Classes:
DWP_Loader— ou, melhor ainda, umnamespacePHP, que dispensa o prefixo manual.
Escolha um prefixo curto e exclusivo (4 a 5 letras costuma bastar) e seja consistente. É barato adotar desde o início e caro de corrigir depois que o plugin já está em produção.
6. Use hooks, não código solto
Dentro do arquivo principal, o seu código não deve rodar "solto" — ele deve se registrar em hooks e aguardar o WordPress chamá-lo no momento certo. Assim você garante que as funções do WordPress já existam e que o seu código rode no contexto adequado (front-end, admin, cron). Um exemplo: enfileirar um script só no front-end.
<?php
add_action( 'wp_enqueue_scripts', 'dwp_enfileirar_assets' );
function dwp_enfileirar_assets() {
wp_enqueue_script(
'dwp-frontend',
plugins_url( 'assets/app.js', __FILE__ ),
array(), // dependências
'1.0.0', // versão (cache-busting)
true // carregar no rodapé
);
}
Repare no plugins_url( 'assets/app.js', __FILE__ ): ele monta a URL correta para um arquivo do plugin a partir do caminho do arquivo atual, sem você fixar URLs na unha. É a forma certa de referenciar assets dentro de um plugin.
7. Ativação, desativação e desinstalação
O ciclo de vida do plugin tem três momentos distintos, e confundi-los é um erro comum:
- Ativação (
register_activation_hook): roda uma única vez, ao ativar. Bom para criar uma opção inicial, agendar um evento de cron, criar tabelas ou rodarflush_rewrite_rules()depois de registrar um CPT. - Desativação (
register_deactivation_hook): roda ao desativar. Use para limpar o que não deve sobreviver à desativação, como agendamentos de cron — mas não apague dados aqui. - Desinstalação (
uninstall.php): roda ao remover o plugin de vez. É o lugar para apagar opções, meta e tabelas que o plugin criou.
<?php
register_activation_hook( __FILE__, 'dwp_na_ativacao' );
register_deactivation_hook( __FILE__, 'dwp_na_desativacao' );
function dwp_na_ativacao() {
// Cria uma opção inicial (sem sobrescrever se já existir).
add_option( 'dwp_versao_banco', '1.0.0' );
// Agenda um evento de cron diário, se ainda não houver.
if ( ! wp_next_scheduled( 'dwp_evento_diario' ) ) {
wp_schedule_event( time(), 'daily', 'dwp_evento_diario' );
}
// Se o plugin registra rewrite rules/CPT, atualize a estrutura.
flush_rewrite_rules();
}
function dwp_na_desativacao() {
// Limpa o agendamento — mas NÃO apague dados na desativação.
wp_clear_scheduled_hook( 'dwp_evento_diario' );
flush_rewrite_rules();
}
O primeiro argumento de register_activation_hook() é sempre __FILE__ do arquivo principal do plugin — é assim que o WordPress associa o callback ao plugin certo. Detalhes e ressalvas (o hook roda antes de o plugin estar totalmente carregado) estão em register_activation_hook().
uninstall.php: limpeza ao desinstalar
Para a desinstalação, o método preferido é criar um arquivo uninstall.php na raiz do plugin. O WordPress o executa automaticamente quando o usuário clica em "Excluir", mas somente nesse contexto — por isso a verificação da constante WP_UNINSTALL_PLUGIN no topo é obrigatória, para impedir que o arquivo rode em qualquer outra situação:
<?php
// uninstall.php — executado apenas ao excluir o plugin.
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
exit;
}
// Remove as opções criadas pelo plugin.
delete_option( 'dwp_versao_banco' );
// Em multisite, repita a limpeza por site, se necessário.
// Remova também meta de posts/usuários e tabelas próprias aqui.
Nunca apague dados do usuário na desativação: ele pode estar apenas testando outro plugin temporariamente e espera reencontrar tudo ao reativar. A remoção destrutiva pertence ao uninstall.php, que só roda na exclusão definitiva — e ainda assim convém oferecer uma opção para o usuário decidir se quer mesmo apagar os dados.
8. Onde colocar o código: plugin × tema × mu-plugin
Resumindo a decisão de onde cada trecho deve morar:
| Local | Sobrevive à troca de tema? | Pode desativar no painel? | Quando usar |
|---|---|---|---|
Plugin (plugins/) | Sim | Sim | Funcionalidade reutilizável: CPTs, shortcodes, integrações, regras de negócio |
functions.php do tema | Não | — | Apenas o que é específico daquele visual: menus, sidebars, suportes do tema |
mu-plugin (mu-plugins/) | Sim | Não (carrega sempre) | Código crítico que nunca deve ser desligado: regras de rede, configs forçadas |
9. Boas práticas que separam amador de profissional
- WordPress Coding Standards: siga o estilo oficial (indentação, espaçamento, nomes) descrito nos Coding Standards. Código padronizado é mais fácil de revisar e manter.
- Internacionalização (i18n): envolva todo texto visível em
__()ouesc_html__()usando o seuText Domain— ex.:esc_html__( 'Salvar', 'meu-plugin' ). Isso permite traduzir o plugin sem editar o código. - Segurança: sanitize tudo que entra (
sanitize_text_field(), etc.), escape tudo que sai (esc_html(),esc_attr(),esc_url()) e use nonces ecurrent_user_can()em ações sensíveis. Aprofunde em segurança no código WordPress. - Versionamento: mantenha o
Versiondo cabeçalho atualizado e use-o no cache-busting de assets e em migrações de banco.
Com esse esqueleto — cabeçalho, guarda ABSPATH, prefixo, hooks e os ganchos de ciclo de vida — você já tem um plugin que o WordPress reconhece, que sobrevive a atualizações e que não atrapalha outros plugins. O resto é construir a funcionalidade em cima dessa base sólida.
Perguntas frequentes
Por que não colocar tudo no functions.php do tema?
Porque o functions.php pertence ao tema. Se você trocar de tema, ou se o tema receber uma atualização que sobrescreva o arquivo, suas customizações desaparecem. Um plugin é independente do tema: continua ativo mesmo que você mude a aparência do site. A regra prática é simples — código de apresentação (o que é específico daquele visual) fica no tema; código de funcionalidade (que deve sobreviver à troca de tema) fica em um plugin.
O que o WordPress exige para reconhecer um arquivo como plugin?
Um bloco de comentário no topo do arquivo principal com, no mínimo, a linha Plugin Name:. Sem esse cabeçalho, o WordPress simplesmente não lista o arquivo em Plugins. As demais linhas (Description, Version, Author, License, Text Domain) são recomendadas e alimentam a tela de plugins e a internacionalização.
Para que serve o if ( ! defined( 'ABSPATH' ) ) exit;?
É uma guarda de segurança. Se alguém acessar o arquivo PHP do plugin diretamente pela URL (sem passar pelo WordPress), a constante ABSPATH não estará definida e o script encerra na hora, sem executar nada. É uma linha que deve estar no topo de praticamente todo arquivo PHP de um plugin ou tema. Veja mais em segurança no código.
Qual a diferença entre desativar e desinstalar um plugin?
Desativar apenas para a execução do plugin — o código e os dados (opções, tabelas, posts) continuam no banco. Isso dispara o register_deactivation_hook(), ideal para limpar agendamentos de cron. Desinstalar remove o plugin de vez e é o momento de apagar dados que ele criou — feito no arquivo uninstall.php ou via register_uninstall_hook(). Não apague dados na desativação: o usuário pode só estar testando outro plugin.
Preciso prefixar funções mesmo em um plugin pequeno?
Sim. O WordPress carrega todos os plugins no mesmo escopo global. Se o seu plugin declarar uma função get_data() e outro plugin (ou o tema) declarar a mesma, o PHP encerra com um erro fatal de função redeclarada. Prefixar tudo — funções, opções, hooks personalizados e nomes de classe — com algo único (ex.: dwp_ ou um namespace) evita essas colisões. É barato fazer e caro de corrigir depois.
Quando devo usar mu-plugins em vez de um plugin normal?
Os must-use plugins ficam em wp-content/mu-plugins/, são carregados sempre e não podem ser desativados pelo painel. Use-os para código crítico de infraestrutura que nunca deve ser desligado por engano (regras de uma rede multisite, configurações forçadas de um cliente). A contrapartida: não têm hooks de ativação/desativação e não aparecem na lista normal de plugins. Para a maioria dos casos, um plugin comum é a escolha certa.