A arquitetura de plugins no WordPress frequentemente degenera em um caos procedural devido à abundância de hooks globais e funções espalhadas.
Muitos desenvolvedores que chegam de outros ecossistemas sentem a necessidade de importar frameworks pesados ou componentes do Laravel para tentar organizar a casa.
No entanto, nós sabemos que a verdadeira maestria técnica reside em construir arquiteturas limpas usando apenas o PHP moderno, sem depender de “muletas” de terceiros.
Adicionar o core do Laravel dentro de um plugin WordPress gera um overhead de memória brutal, incompatível com servidores configurados para máxima eficiência (usando PHP-FPM ou Swoole, por exemplo).
Nesta masterclass, vamos projetar uma Service Layer robusta e um Container de Injeção de Dependência (DI) nativo do zero.
O objetivo é isolar completamente as regras de negócio do seu plugin da infraestrutura de banco de dados do WordPress.
Isso garantirá que seu código obedeça aos princípios SOLID e seja 100% coberto por testes unitários rápidos e confiáveis.
Prepare-se para uma imersão profunda em engenharia de software aplicada ao ecossistema PHP.
Dissecando o Princípio da Inversão de Dependência
O princípio da Inversão de Dependência (a letra “D” do acrônimo SOLID) postula duas regras fundamentais para o design de software orientado a objetos.
Primeiro, módulos de alto nível (nossas regras de negócio) não devem depender de módulos de baixo nível (como o banco de dados MySQL ou a API do WordPress).
Ambos devem depender de abstrações (Interfaces).
Segundo, as abstrações não devem depender de detalhes; os detalhes é que devem depender das abstrações.
Quando você escreve um código onde uma classe de serviço instancia diretamente a classe wpdb, você criou um acoplamento forte.
Se o banco de dados mudar, ou se você quiser testar o serviço sem um banco real, você estará bloqueado.
A Injeção de Dependência resolve isso passando as instâncias necessárias através do construtor da classe.
Didaticamente, isso significa que a classe não “cria” suas ferramentas, ela “pede” por elas e as recebe de um orquestrador superior.
Construindo o Container DI Nativo
Para gerenciar essa entrega de dependências, precisamos de um Container de Injeção de Dependência.
Em vez de importar o IlluminateContainer, construiremos um micro-container usando a poderosa Reflection API do PHP.
A Reflection API permite que o nosso código inspecione a si mesmo durante a execução (Runtime).
O container olhará para o construtor de uma classe, lerá quais interfaces ou classes ela exige, instanciará essas dependências automaticamente e as injetará.
Este processo é conhecido como “Autowiring”.
Embora a reflexão tenha um micro-custo de performance, o ganho arquitetural em projetos complexos é imensurável, e ferramentas de cache como o Opcache mitigam quase todo esse impacto em produção.
<?php
namespace MundoPHPArquitetura;
use ReflectionClass;
use Exception;
class ContainerDI {
private array $instancias = [];
private array $binds = [];
// Mapeia uma Interface para uma Classe Concreta
public function bind(string $interface, string $concreta): void {
$this->binds[$interface] = $concreta;
}
// Resolve e instancia a classe solicitada com Autowiring
public function get(string $classe) {
if (isset($this->instancias[$classe])) {
return $this->instancias[$classe];
}
$alvo = $this->binds[$classe] ?? $classe;
$reflexao = new ReflectionClass($alvo);
if (!$reflexao->isInstantiable()) {
throw new Exception("A classe {$alvo} não pode ser instanciada.");
}
$construtor = $reflexao->getConstructor();
// Se não houver construtor, basta instanciar direto
if (is_null($construtor)) {
$instancia = new $alvo();
$this->instancias[$classe] = $instancia;
return $instancia;
}
$parametros = $construtor->getParameters();
$dependencias = [];
// Autowiring: Resolve dependências recursivamente
foreach ($parametros as $param) {
$tipo = $param->getType();
if (!$tipo || $tipo->isBuiltin()) {
throw new Exception("Não é possível resolver parâmetros nativos via autowiring.");
}
$dependencias[] = $this->get($tipo->getName());
}
$instancia = $reflexao->newInstanceArgs($dependencias);
$this->instancias[$classe] = $instancia;
return $instancia;
}
}
A Camada de Repositório (Repository Pattern)
Com o nosso Container pronto, precisamos criar a camada que efetivamente fala com o banco de dados.
O Padrão de Repositório atua como uma coleção em memória para o resto do sistema.
O Service Layer não deve saber escrever queries SQL; ele apenas pede ao Repositório para “salvar um usuário” ou “buscar um produto”.
Isso isola completamente as complexidades do objeto global $wpdb.
Vamos definir uma interface para o nosso repositório de clientes e, em seguida, criar a implementação concreta para o MySQL do WordPress.
Esta separação cristalina é o que permite substituir o MySQL por um banco NoSQL no futuro, alterando apenas uma classe.
<?php
namespace MundoPHPDominio;
// O Contrato (Interface)
interface ClienteRepositoryInterface {
public function buscarPorId(int $id): ?array;
public function salvar(array $dados): bool;
}
// A Implementação de Infraestrutura
class ClienteRepositoryWPDB implements ClienteRepositoryInterface {
public function buscarPorId(int $id): ?array {
global $wpdb;
$query = $wpdb->prepare("SELECT * FROM wp_clientes WHERE id = %d", $id);
$resultado = $wpdb->get_row($query, ARRAY_A);
return $resultado ?: null;
}
public function salvar(array $dados): bool {
global $wpdb;
$inserido = $wpdb->insert('wp_clientes', $dados);
return $inserido !== false;
}
}
Orquestrando a Service Layer
O Service Layer (Camada de Serviço) é onde as regras de negócio puras habitam.
É aqui que você valida se um cliente pode receber um desconto, se o e-mail já está cadastrado, ou se o limite de crédito foi excedido.
Observe como a nossa classe de serviço não faz a menor ideia de que o WordPress existe.
Ela exige a interface do repositório no seu construtor.
Isso torna o código imune às atualizações do CMS.
Para criar um teste unitário com PHPUnit para esta classe, bastaria injetar um Repositório Mock (falso) que retorna dados estáticos, validando a lógica do desconto sem encostar no banco de dados.
<?php
namespace MundoPHPAplicacao;
use MundoPHPDominioClienteRepositoryInterface;
class GerenciadorClientesService {
// Injeção de Dependência pura via construtor
public function __construct(
private ClienteRepositoryInterface $repositorio
) {}
public function registrarNovoCliente(array $dados): array {
// Regra de Negócio: E-mail é obrigatório
if (empty($dados['email'])) {
throw new Exception("O e-mail não pode ser vazio.");
}
// Regra de Negócio: Sanitização antes de salvar
$dados_limpos = [
'nome' => sanitize_text_field($dados['nome']),
'email' => sanitize_email($dados['email'])
];
$sucesso = $this->repositorio->salvar($dados_limpos);
if (!$sucesso) {
throw new Exception("Falha catastrófica ao persistir os dados do cliente.");
}
// Disparo de eventos de domínio (desacoplado)
do_action('mundophp_cliente_registrado', $dados_limpos);
return ['status' => 'sucesso', 'mensagem' => 'Cliente ativado.'];
}
}
Iniciando o Plugin (O Bootstrap)
O ponto final dessa arquitetura é unir todas as peças no arquivo principal do seu plugin WordPress.
É aqui que configuramos o nosso Container DI, ensinando a ele que, sempre que alguém pedir pela ClienteRepositoryInterface, ele deve entregar uma instância de ClienteRepositoryWPDB.
Feito isso, pedimos ao container para nos dar uma instância do GerenciadorClientesService.
O container automaticamente perceberá que o serviço precisa do repositório, instanciará o repositório, injetará no serviço e nos devolverá o serviço pronto para uso.
Esta mágica de Autowiring reduz centenas de linhas de código de inicialização (boilerplate) a meras configurações limpas.
<?php
/**
* Plugin Name: MundoPHP Arquitetura Limpa
* Description: Exemplo definitivo de DI e Service Layer.
*/
use MundoPHPArquiteturaContainerDI;
use MundoPHPDominioClienteRepositoryInterface;
use MundoPHPDominioClienteRepositoryWPDB;
use MundoPHPAplicacaoGerenciadorClientesService;
// Hook de inicialização do plugin
add_action('plugins_loaded', function() {
// 1. Inicializa o Container Global
$container = new ContainerDI();
// 2. Registra os Bindings (Contratos)
$container->bind(
ClienteRepositoryInterface::class,
ClienteRepositoryWPDB::class
);
// 3. Resolução automática (Autowiring em ação)
// O Container sabe exatamente como montar esta classe complexa
$clienteService = $container->get(GerenciadorClientesService::class);
// Exemplo de rota ou processamento recebendo POST
if (isset($_POST['novo_cliente_action'])) {
try {
$clienteService->registrarNovoCliente($_POST);
} catch (Exception $e) {
error_log("Erro de Negócio: " . $e->getMessage());
}
}
});
A Revolução da Qualidade de Código
A transição de scripts procedurais gigantescos para esta arquitetura Clean e orientada a objetos é o que separa um desenvolvedor sênior de um programador comum.
Com o uso do PHP 8.3 e suas propriedades tipadas (typed properties), garantimos que o contrato de dados seja estrito.
A injeção de dependência previne a criação de “Objetos Deus” (God Objects) que sabem fazer tudo.
Cada classe no nosso sistema agora tem apenas um único motivo para mudar (Princípio da Responsabilidade Única).
O MundoPHP defende que a educação tecnológica de alto nível deve focar nos fundamentos da ciência da computação, e não apenas nas conveniências efêmeras dos frameworks da moda.
Domine a base da linguagem, entenda profundamente os padrões de projeto, e você será capaz de criar sistemas indestrutíveis, escaláveis e preparados para as demandas extremas do mercado moderno.


