¿Y si el código hablara el idioma de tu negocio?
\n\nImagina 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\nLa 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\nUn 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
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\nEnfoque 1: DSL interno — Fluent Interface y la magia de los métodos
\n\nUn 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\nEjemplo: DSL para construir consultas SQL
\n\nEn lugar de escribir:
\n\n$query = "SELECT * FROM users WHERE age > 18 AND city = 'Madrid' ORDER BY name ASC LIMIT 10";\n\nQueremos 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\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