DSL en PHP: crea lenguajes orientados al negocio

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

¿Y si el código hablara el idioma de tu negocio?

\n\n

Imagina que un gerente de ventas escribe una regla de descuento en un inglés sencillo y el sistema la convierte automáticamente en código funcional. O que un contador describe un cálculo fiscal con la frase «Si el total supera los 1000 y el cliente es de la UE, aplicar tasa del 5%». Sin una sola línea de PHP. ¿Suena a ciencia ficción? Bienvenido al mundo de los lenguajes específicos del dominio (DSL) en PHP.

\n\n

La mayoría de los desarrolladores conocen PHP como un lenguaje web, pero pocos aprovechan su superpoder: la creación de DSL. No es solo un truco técnico, sino una herramienta poderosa que transforma el caos de las reglas de negocio en código elegante y legible. En este artículo no solo hablaremos de teoría, sino que construiremos un DSL en PHP funcional desde cero. Verás cómo el desarrollo de DSL puede simplificar tu vida y la de tus clientes.

\n\n

¿Qué es un DSL y por qué usarlo en PHP?

\n\n

Un DSL (Domain Specific Language) es un lenguaje diseñado para una tarea concreta. A diferencia de un lenguaje de programación general (como PHP, Java o Python), el DSL habla el idioma de tu dominio. Ejemplos: SQL para bases de datos, expresiones regulares para búsqueda de patrones, HTML para maquetación.

\n\n

¿Por qué crear un DSL en PHP? Tres razones principales:

\n\n
    \n
  • Legibilidad. El código en DSL se lee como una instrucción en español o inglés. No hace falta ser programador para entender la lógica.
  • \n
  • Seguridad. Tú controlas lo que el usuario puede hacer. Nada de eval ni shell_exec.
  • \n
  • Velocidad de desarrollo. Analistas de negocio y gerentes pueden describir reglas por sí mismos, sin distraer al equipo de desarrollo.
  • \n
\n\n

En PHP existen dos enfoques principales para la creación de lenguajes específicos del dominio: los DSL internos (basados en la sintaxis de PHP) y los DSL externos (que requieren su propio analizador). Empezaremos con el primero, que es más sencillo y suele cubrir el 80% de los casos.

\n\n

Enfoque 1: DSL interno — Fluent Interface y la magia de los métodos

\n\n

Un DSL interno consiste en usar la sintaxis de PHP pero organizarla de forma que parezca lenguaje natural. El clásico es el fluent interface (interfaz fluida). Escribamos un DSL para construir consultas SQL y sentir la magia de inmediato.

\n\n

Ejemplo: DSL para construir consultas SQL

\n\n

En lugar de escribir:

\n\n
$query = "SELECT * FROM users WHERE age > 18 AND city = 'Madrid' ORDER BY name ASC LIMIT 10";
\n\n

Queremos escribir así:

\n\n
$query = QueryBuilder::table('users')\n    ->select('*')\n    ->where('age', '>', 18)\n    ->where('city', '=', 'Madrid')\n    ->orderBy('name', 'ASC')\n    ->limit(10)\n    ->get();
\n\n

¿Verdad que se lee como una frase? La implementación es simple: cada método devuelve $this, y al final el método get() arma la cadena.

\n\n
class 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                

Blogs

Book Recommendations