E se o código falasse a língua do seu negócio?
\n\nImagine: um gerente de vendas escreve uma regra de desconto em inglês simples, e o sistema a transforma automaticamente em código funcional. Ou um contador descreve um cálculo de imposto com a frase "Se o valor for maior que 1000 e o cliente for da UE, aplicar alíquota de 5%". Sem uma única linha de PHP. Parece ficção? Bem-vindo ao mundo das linguagens específicas de domínio (DSL) em PHP.
\n\nA maioria dos desenvolvedores conhece o PHP como linguagem para web, mas poucos usam seu superpoder — a criação de DSL. Não é apenas um truque técnico, mas uma ferramenta poderosa que transforma o caos das regras de negócio em código elegante e legível. Neste artigo, não vamos apenas falar de teoria; vamos construir um DSL em PHP funcional do zero. Você verá como o desenvolvimento de DSL pode simplificar sua vida e a dos seus clientes.
\n\nO que é DSL e por que você precisa dela em PHP?
\n\nDSL (Domain Specific Language) é uma linguagem feita sob medida para uma tarefa específica. Diferente de uma linguagem de programação geral (como PHP, Java ou Python), a DSL fala a língua do seu domínio de negócio. Exemplos: SQL para bancos de dados, expressões regulares para busca de padrões, HTML para marcação.
\n\nPor que criar uma DSL em PHP? Três razões principais:
\n\n- \n
- Legibilidade. O código em DSL parece uma instrução em português ou inglês. Não é preciso ser programador para entender a lógica. \n
- Segurança. Você controla o que o usuário pode fazer. Nada de eval ou shell_exec. \n
- Velocidade de desenvolvimento. Analistas de negócio e gerentes podem descrever regras por conta própria, sem sobrecarregar a equipe de desenvolvimento. \n
Em PHP, existem duas abordagens principais para criar linguagens específicas de domínio: DSLs internas (construídas sobre a sintaxe do PHP) e DSLs externas (que exigem um parser próprio). Vamos começar pela primeira — é mais simples e cobre 80% dos casos.
\n\nAbordagem 1: DSL Interna — Fluent Interface e a mágica dos métodos
\n\nUma DSL interna é quando você usa a sintaxe do PHP, mas a organiza de forma que pareça uma linguagem natural. O clássico é o fluent interface (interface fluente). Vamos escrever uma DSL para construir consultas SQL e sentir a mágica agora mesmo.
\n\nExemplo: DSL para construir consultas SQL
\n\nEm vez de escrever:
\n\n$query = "SELECT * FROM usuarios WHERE idade > 18 AND cidade = 'São Paulo' ORDER BY nome ASC LIMIT 10";\n\nQueremos escrever assim:
\n\n$query = QueryBuilder::table('usuarios')\n ->select('*')\n ->where('idade', '>', 18)\n ->where('cidade', '=', 'São Paulo')\n ->orderBy('nome', 'ASC')\n ->limit(10)\n ->get();\n\nConcorda que parece uma frase? A implementação é simples: cada método retorna $this, e no final o método get() monta a string.
\n\nclass QueryBuilder {\n private array $parts = [];\n\n public function table(string $name): self {\n $this->parts['table'] = $name;\n return $this;\n }\n\n public function select(string $columns = '*'): self {\n $this->parts['select'] = $columns;\n return $this;\n }\n\n public function where(string $column, string $operator, $value): self {\n $this->parts['where'][] = "$column $operator " . (is_string($value) ? "'$value'" : $value);\n return $this;\n }\n\n public function orderBy(string $column, string $direction = 'ASC'): self {\n $this->parts['order'] = "$column $direction";\n return $this;\n }\n\n public function limit(int $limit): self {\n $this->parts['limit'] = $limit;\n return $this;\n }\n\n public function get(): string {\n $sql = "SELECT {$this->parts['select']} FROM {$this->parts['table']}";\n if (!empty($this->parts['where'])) {\n $sql .= " WHERE " . implode(' AND