k-kinzal / sql-parser
Lossless LALR(1) SQL parsers for MySQL, PostgreSQL, and SQLite, built from the official grammars
Requires
- php: ^8.1
- ext-zlib: *
Requires (Dev)
- brianium/paratest: ^7.3
- deptrac/deptrac: ^3.0
- fakerphp/faker: ^1.23
- friendsofphp/php-cs-fixer: ^3.95.25
- k-kinzal/bison-parser: dev-main
- k-kinzal/lemon-parser: dev-main
- k-kinzal/php-ai-toolkit: dev-main
- k-kinzal/sql-faker: dev-main
- nikic/php-fuzzer: ^0.0.11
- phpbench/phpbench: ^1.4
- phpcompatibility/php-compatibility: ^10.0.0-alpha2
- phpstan/phpstan: ^2.1
- phpstan/phpstan-strict-rules: ^2.0
- phpunit/phpunit: ^10.5
- squizlabs/php_codesniffer: ^4.0.4
Suggests
- k-kinzal/bison-parser: To rebuild the MySQL and PostgreSQL parse tables with bin/build-mysql.php and bin/build-pg.php
- k-kinzal/lemon-parser: To rebuild the SQLite parse tables with bin/build-sqlite.php
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-28 02:33:19 UTC
README
SQL Parser is a set of LALR(1) parsers for MySQL, PostgreSQL, and SQLite written in PHP. Each parser is generated from the official grammar of a server version (sql_yacc.yy for MySQL, gram.y for PostgreSQL, and parse.y for SQLite), and its lexer is a port of the server's own scanner, so text is tokenized and parsed the way the server does it. The syntax tree names the nonterminals of the upstream grammar, and parsing is lossless: a tree writes back the text it was parsed from, byte for byte, including comments and whitespace.
Requirements
- PHP 8.1+ with the zlib extension
Support Syntax
The following grammar versions are bundled. Pass the version tag to the parser; omitting it uses the default for that database.
MySQL
| Version | Version tag | Default |
|---|---|---|
| 5.6.51 | mysql-5.6.51 |
|
| 5.7.44 | mysql-5.7.44 |
|
| 8.0.44 | mysql-8.0.44 |
|
| 8.1.0 | mysql-8.1.0 |
|
| 8.2.0 | mysql-8.2.0 |
|
| 8.3.0 | mysql-8.3.0 |
|
| 8.4.7 | mysql-8.4.7 |
Yes |
| 9.0.1 | mysql-9.0.1 |
|
| 9.1.0 | mysql-9.1.0 |
PostgreSQL
| Version | Version tag | Default |
|---|---|---|
| 16.6 | pg-16.6 |
|
| 17.2 | pg-17.2 |
Yes |
SQLite
| Version | Version tag | Default |
|---|---|---|
| 3.47.2 | sqlite-3.47.2 |
Yes |
Installation
composer require k-kinzal/sql-parser
Usage
use SqlParser\MySql\MySqlParser; $parser = new MySqlParser('mysql-8.4.7'); $sql = 'SELECT id, name FROM users u WHERE u.id = ? ORDER BY id LIMIT 10'; $tree = $parser->parse($sql); $tree->name; // 'start_entry', the grammar's start symbol $tree->find('where_clause')[0]->text($sql); // 'WHERE u.id = ?' $tree->find('table_reference')[0]->text($sql); // 'users u' $tree->toString(); // the statement again, byte for byte
A MySQL parser reads text under a sql_mode, since ANSI_QUOTES, PIPES_AS_CONCAT, HIGH_NOT_PRECEDENCE, NO_BACKSLASH_ESCAPES and IGNORE_SPACE change how text is tokenized. Pass the value a session reports:
use SqlParser\MySql\MySqlParser; use SqlParser\MySql\SqlMode; $parser = new MySqlParser('mysql-8.4.7', SqlMode::fromString('ANSI_QUOTES,NO_BACKSLASH_ESCAPES')); $parser->tokenize('SELECT "x"')[1]->name; // 'IDENT_QUOTED'
A named placeholder such as :id is not in the languages of MySQL and PostgreSQL, which read only their own markers. Their parsers read :name as a parameter under ParameterSyntax::Named, as a dialect extension; SQLite reads it natively.
use SqlParser\Lexer\ParameterSyntax; use SqlParser\PostgreSql\PostgreSqlParser; $parser = new PostgreSqlParser(parameters: ParameterSyntax::Named); $parser->parse('SELECT id FROM users WHERE id = :id');
License
MIT License. See LICENSE for details.