DSL in PHP: Writing Domain-Specific Languages for Business

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

What if code spoke the language of your business?

\n\n

Imagine: a sales manager writes a discount condition in plain English, and the system itself turns it into working code. Or an accountant describes a tax calculation with the phrase "If the amount is greater than 1000 and the client is from the EU, apply a 5% rate." Without a single line of PHP. Sounds like science fiction? Welcome to the world of domain-specific languages (DSL) in PHP.

\n\n

Most developers know PHP as a web language, but few use its superpower — creating DSLs. This is not just a technical trick, but a powerful tool that turns chaos of business rules into elegant, readable code. In this article, we will not just talk about theory, but build a working DSL in PHP from scratch. You will see how DSL development can simplify life for both you and your clients.

\n\n

What is a DSL and why do you need it in PHP?

\n\n

A DSL (Domain Specific Language) is a language tailored to a specific task. Unlike a general-purpose programming language (like PHP, Java, or Python), a DSL speaks the language of your domain. Examples: SQL for databases, regular expressions for pattern matching, HTML for layout.

\n\n

Why should you create a DSL in PHP? Three main reasons:

\n\n
    \n
  • Readability. DSL code looks like instructions in Russian or English. You don't need to be a programmer to understand the logic.
  • \n
  • Security. You control what the user can do. No eval or shell_exec.
  • \n
  • Development speed. Business analysts and managers can describe rules themselves without distracting the development team.
  • \n
\n\n

In PHP, there are two main approaches to creating domain-specific languages: internal DSLs (built on PHP syntax) and external DSLs (requiring their own parser). We will start with the first one — it is simpler and often covers 80% of tasks.

\n\n

Approach 1: Internal DSL — Fluent Interface and Method Magic

\n\n

An internal DSL is when you use PHP syntax but organize it so that it resembles natural language. The classic is a fluent interface. Let's write a DSL for building SQL queries to feel the magic right now.

\n\n

Example: DSL for Building SQL Queries

\n\n

Instead of writing:

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

We want to write like this:

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

Agree, it reads like a sentence. The implementation is simple: each method returns $this, and at the end, the get() method assembles the string.

\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