
PHP-SQL-Parser 深度实战纯 PHP 实现 MySQL 方言 SQL 解析及在 ShowDoc 数据库文档自动生成中的落地应用【免费下载链接】showdocShowDoc is a tool greatly applicable for an IT team to share documents online一个非常适合IT团队的在线API文档、技术文档工具项目地址: https://gitcode.com/gh_mirrors/sh/showdoc本篇技术指南以 ShowDoc 开源仓库中内置的 PHP-SQL-Parser 官方 README 为主体结合仓库内的完整源码、Wiki 手册、示例与测试用例系统讲解这款纯 PHP 的 SQL 解析组件的定位、支持范围、核心 API、解析输出结构、反向生成Creator能力与可配置选项并深入剖析 ShowDoc 是如何借助它将CREATE TABLE语句自动转换成 Markdown 数据库文档表格的。读完本文你将掌握该组件的全部核心用法并能够复现其在文档工具中的典型集成模式。一、组件定位纯 PHP、非校验、聚焦 MySQL 方言PHP-SQL-Parser源码位于 server/vendor/greenlion/php-sql-parser/的官方定义是一句非常精炼的描述A pure PHP SQL (non validating) parser w/ focus on MySQL dialect of SQL理解这句话是使用该组件的前提它包含三层关键信息纯 PHP 实现不依赖任何 PECL 扩展如php-sql-parser之类的外部 C 扩展也不需要引入正则引擎之外的其他运行时依赖任何标准的 PHP 环境都能直接运行。非校验non validating它不负责校验 SQL 的合法性不会因为一条语法上存在问题的 SQL 而抛错拒绝。官方 README 明确说明It is expected that you will present syntactically valid queries.——调用方应当自行保证传入的是语法正确的语句解析器只负责尽力将其拆解为结构化数据。聚焦 MySQL 方言这是它的设计重心所在。这一设计取舍直接影响使用方式如果你需要在解析前先判断 SQL 是否合法需要自行配合其他校验手段而如果只是需要把一段已知正确的 SQL 结构化这个组件非常合适。二、支持范围完整支持 12 类 MySQL 语句README 给出了组件完整支持的 MySQL 语句类型清单语句类型说明SELECT查询语句INSERT插入语句UPDATE更新语句DELETE删除语句REPLACE替换语句RENAME重命名语句SHOW展示类语句SET设置类语句DROP删除对象语句CREATE INDEX创建索引CREATE TABLE建表语句EXPLAIN / DESCRIBE执行计划 / 表结构描述在 PHPSQLParser.php 中这些语句通过DefaultProcessor分发到各自独立的处理器processors完成解析例如SelectProcessor、InsertProcessor、CreateProcessor、DropProcessor、ShowProcessor、DescribeProcessor、ExplainProcessor等每个处理器都位于 processors 目录。对于上述列表之外的其他语句解析器不会做结构化拆解而是将其整体返回为一个token 数组即按词法切分后的原始单元序列信息密度远低于上述类型的结构化输出。关于方言兼容性README 指出由于 MySQL 方言与 SQL-92 标准非常接近因此该解析器对大多数需要 SQL 解析能力的数据库应用同样适用如果目标数据库方言差异较大可以通过修改保留字表reserved words来适配——这一点在 Parser-Manual 中有进一步说明。此外组件原生支持UNION、子查询subqueries和复合语句compound statements。三、获取与集成零外部依赖Composer 即可引入README 将无外部依赖作为该组件的一大卖点解析器是一个自包含的类self contained class没有外部依赖仅使用少量正则表达式。在 ShowDoc 仓库中该组件正是通过 Composer 作为依赖引入的见 composer.json使用命名空间PHPSQLParser。集成时只需composer require greenlion/php-sql-parser然后通过 Composer 自动加载即可使用例如 ShowDoc 的转换工具类中直接以use PHPSQLParser\PHPSQLParser;方式导入。四、核心 API构造器与 parse() 方法的两种用法根据 Parser-Manual 与源码PHPSQLParser提供两种等价的解析入口方式一通过构造器解析// 构造器内部会自动调用 parse() 方法 $parser new PHPSQLParser(select 1); print_r($parser-parsed);方式二显式调用 parse() 方法$parser new PHPSQLParser(); // parse() 返回解析树同时保存在 $parser-parsed 属性中 print_r($parser-parse(select 2)); // 获取最近一次解析的语句树 $save $parser-parsed;从 PHPSQLParser.php 源码 可以看到构造器的完整签名public function __construct($sql false, $calcPositions false, array $options array())$sql要解析的 SQL 语句字符串$calcPositions是否在输出中计算每个语法单元的字符位置[position]字段默认false$options解析选项数组后面专节说明。而parse()方法的实现逻辑是源码public function parse($sql, $calcPositions false) { $processor new DefaultProcessor($this-options); $queries $processor-process($sql); // calc the positions of some important tokens if ($calcPositions) { $calculator new PositionCalculator(); $queries $calculator-setPositionsWithinSQL($sql, $queries); } $this-parsed $queries; return $this-parsed; }可见解析分两阶段先由DefaultProcessor完成结构化拆解若开启$calcPositions再由PositionCalculator计算每个 token 在原始 SQL 字符串中的偏移位置。官方 README 与 Parser-Manual 均提醒位置计算需要额外时间如果业务不需要定位信息建议保持false以提升解析速度。此外PHPSQLParser还提供了三个自定义函数管理接口对应源码 addCustomFunction / removeCustomFunction / getCustomFunctions用于向解析器注册、移除或查询自定义函数 token从而让解析器识别项目中特有的函数名。五、解析输出结构按 SQL 子句分区的关联数组解析结果是一个以 SQL 子句名称为键的关联数组每个键对应查询中的一个 section如SELECT、FROM、WHEREsection 内每个元素代表一个语法单元列引用、运算符、字面量、子查询等。5.1 README 原始示例完整继承README 给出的官方示例查询SELECT STRAIGHT_JOIN a, b, c FROM some_table an_alias WHERE d 5;对应的print_r输出如下注意STRAIGHT_JOIN被收入OPTIONS分区SELECT分区内每一项都包含expr_type、base_expr、sub_tree、alias四个核心字段FROM分区内是表条目WHERE分区由 colref、operator、const 三元组构成Array ( [OPTIONS] Array ( [0] STRAIGHT_JOIN ) [SELECT] Array ( [0] Array ( [expr_type] colref [base_expr] a [sub_tree] [alias] a ) [1] Array ( [expr_type] colref [base_expr] b [sub_tree] [alias] b ) [2] Array ( [expr_type] colref [base_expr] c [sub_tree] [alias] c ) ) [FROM] Array ( [0] Array ( [table] some_table [alias] an_alias [join_type] JOIN [ref_type] [ref_clause] [base_expr] [sub_tree] ) ) [WHERE] Array ( [0] Array ( [expr_type] colref [base_expr] d [sub_tree] ) [1] Array ( [expr_type] operator [base_expr] [sub_tree] ) [2] Array ( [expr_type] const [base_expr] 5 [sub_tree] ) ) )5.2 字段语义解读结合 Parser-Manual 中的说明expr_type语法单元的类型标识详见下文 5.3 对照表base_expr该单元在原始 SQL 中的原文片段sub_tree子表达式树例如表达式12会被拆成const/operator/const三个子节点叶子节点通常为空alias别名信息可以是字符串也可能是包含as、name、base_expr、position的数组table/join_type/ref_type/ref_clause表条目专属字段分别表示表名、连接类型即使无连接条件也默认标为JOIN、连接引用方式ON/USING与引用条件。5.3 expr_type 取值对照表来自 ExpressionType所有可能的expr_type值由 ExpressionType.php 统一定义常用取值包括expr_type 值含义colref列引用const常量字面量operator运算符含and、or、in等expression表达式如12bracket_expression括号表达式subquery子查询in-listIN 列表aggregate_function聚合函数如 sumfunction/custom_function普通函数 / 自定义函数reserved保留字如 CASE、WHEN、EXISTSalias/pos别名 / 位置序号GROUP BY 1 中的 1table/view/database/schema表 / 视图 / 数据库 / 模式data-type/column-type数据类型建表语句中使用column-def列定义constraint/primary-key/foreign-key/unique-index/index各类约束与索引partition系列分区定义相关完整清单见 ExpressionType.php共 150 行常量定义。5.4 开启位置计算后的输出差异在 Parser-Manual 中展示了传入true计算位置后的输出每个单元都会额外携带[position]字段指示该 token 在原始 SQL 字符串中的字符偏移量。例如解析SELECT a from some_table an_alias WHERE d 5;时[SELECT] [0] [expr_type] colref [alias] [base_expr] a [sub_tree] [position] 8 [FROM] [0] [expr_type] table [table] some_table [alias] Array([as] , [name] an_alias, [base_expr] an_alias, [position] 29) [join_type] JOIN [base_expr] some_table an_alias [position] 18 [WHERE] [0] [expr_type] colref, [base_expr] d, [position] 45 [1] [expr_type] operator, [base_expr] , [position] 47 [2] [expr_type] const, [base_expr] 5, [position] 49position字段非常适合做高亮、定位、SQL 片段映射等场景。六、复杂语句解析能力验证Complex-Example 文档用一个几乎覆盖全部 SELECT 语法要素的查询展示了该组件的深度解析能力。该示例查询同时包含DISTINCT、INTO a1, a2, a3、FOR UPDATE、LOCK IN SHARE MODE等被归入OPTIONS分区算术表达式12、带别名as \c2 的表达式聚合函数sum(c2)、sum(c3) as sum_c3CASE WHEN ... THEN ... ELSE ... END条件表达式逐 token 拆解为reserved/colref/operator/const标量子查询(select c1c2 from t1 inner_t1 limit 1)递归生成嵌套的SELECT/FROM/LIMIT子树LEFT OUTER JOIN ... USING(c1,c2)、JOIN ... ON、JOIN ... USING(x)多种连接形式WHERE中的IN (1,2,3,apple)生成in-list、EXISTS (子查询)、括号逻辑表达式GROUP BY 1, 2、HAVING sum(c2) 1、ORDER BY 2, c1 DESC、LIMIT 0, 10。从 Complex-Example.md 的完整输出可以看到LIMIT分区被结构化为[offset] 0, [rowcount] 10ORDER分区中按位置排序的元素类型为pos、带direction字段ASC/DESC。这些结构化结果充分说明即使面对接近生产环境的复杂查询该组件也能输出可供程序消费的规整数据。此外README 与 example.php 还演示了UNION/UNION ALL含带括号的联合查询、ALTER TABLE ... ADD KEY、INSERT ... ON DUPLICATE KEY UPDATE、多表DELETE ... USING、SHOW TABLE STATUS、带反引号与混合引号的复杂标识符等场景。七、反向生成PHPSQLCreator解析之外该组件还提供配套的PHPSQLCreator源码见 PHPSQLCreator.php可将解析树重新生成 SQL 语句实现解析—修改—重组装的流水线。用法同样有两种方式一构造器$parser new PHPSQLParser(select 1); $creator new PHPSQLCreator($parser-parsed); echo $creator-created;方式二create() 方法$parser new PHPSQLParser(select 2); $creator new PHPSQLCreator(); echo $creator-create($parser-parsed); // SQL 保存在 $creator-created 属性 $save $creator-created;Creator 内部按语句类型委托给对应 Builder如SelectStatementBuilder、InsertStatementBuilder、CreateStatementBuilder、AlterStatementBuilder等全部位于 builders 目录共 100 个 Builder 类对不支持的语句类型会抛出UnsupportedFeatureException。八、解析选项consistent_sub_trees 与 ansi_quotesPHPSQLParser构造器的第三个参数接受选项数组由 Options.php 承载目前支持两个开关选项常量键名作用Options::CONSISTENT_SUB_TREESconsistent_sub_trees为true时使输出中的子表达式树结构保持一致Options::ANSI_QUOTESansi_quotes为true时按 ANSI 模式处理双引号将视为标识符引用而非字符串字面量以适配启用ANSI_QUOTESSQL 模式的数据库用法示例$parser new PHPSQLParser($sql, false, array( consistent_sub_trees true, ansi_quotes true, ));九、ShowDoc 落地实战CREATE TABLE 自动转 Markdown 表格关联 README 中支持 CREATE TABLE这一能力在 ShowDoc 中被转化为一个非常实用的功能——将建表 SQL 自动转换为数据库文档。ShowDoc 的转换工具类 server/Application/Api/Helper/Convert.class.php新架构中的等价实现位于 server/app/Common/Helper/Convert.php在文件头部通过use PHPSQLParser\PHPSQLParser;引入解析器并实现了两个核心方法。9.1 解析建表语句convertSqlToArray()convertSqlToArray()方法源码的核心逻辑$parser new PHPSQLParser(); $parsed $parser-parse($sql); if (!isset($parsed[CREATE])) { return null; } if ($parsed[CREATE][expr_type] table) { $fields $parsed[TABLE][create-def][sub_tree]; $tableName $parsed[TABLE][base_expr]; // 表名 ... }这里清晰展示了 README 所述解析结构的消费方式判断是否CREATE语句对应解析树中的CREATE分区通过expr_type table判断是否为建表语句从TABLE分区的create-def子树的sub_tree中逐个取出列定义遍历每个字段的sub_treesub_tree[0]是列名sub_tree[1]中包含类型、长度、可空、默认值、注释等信息跳过constraint类型条目与没有sub_tree[1][sub_tree]的行如PRIMARY KEY(id)这种非列定义行从列定义中提取data-type类型与length长度拼装为varchar (255)这样的类型串从TABLE分区的options中查找COMMENT选项提取表注释。最终返回结构化数组array( table 表名, comment 表注释, fields array( array(name 字段, type 类型, nullable 是/否, default 默认值, comment 说明), ... ), )9.2 渲染 MarkdownconvertSqlToMarkdownTable()拿到结构化数组后convertSqlToMarkdownTable()源码将其渲染为符合 ShowDoc 排版习惯的 Markdown 表格- 表名 表注释 | 字段 | 类型 | 允许空 | 默认 | 说明 | | --- | --- | --- | --- | --- | | id | int (11) | N | | 主键 | | name | varchar (255) | Y | | 名称 |方法内部将表名与注释拼装为列表行将字段数组以管道符分隔拼装成 Markdown 表格行最终输出一段可直接写入文档页的 Markdown。这正是该解析器在真实产品中的典型集成解析器输出结构 → 业务映射 → 目标格式渲染。十、测试与可靠性验证该组件在仓库内携带了非常完整的测试套件tests分为两个维度parser 用例tests/cases/parser/下 100 个测试文件覆盖 SELECT/INSERT/UPDATE/DELETE/DROP/SHOW/UNION/子查询/IN 列表/反引号/注释/变量/混合引号等各类语法场景每个用例通过 AbstractTestCase.php 与tests/expected/parser/下的 176 个.serialized期望输出逐项比对确保解析结果稳定creator 用例tests/cases/creator/下 70 个测试文件将解析树重新生成 SQL 后与tests/expected/creator/下的.sql期望文件比对验证解析—生成闭环的一致性。ShowDoc 自身的自动化测试也覆盖了相关能力可结合 server/tests/ 目录了解项目级验证。运行组件自带测试可执行 runtest.sh官方 example.php 则提供了一条命令式的快速体验路径php vendor/greenlion/php-sql-parser/examples/example.php十一、使用边界与注意事项综合 README 与源码在实际项目中使用该组件需要留意以下几点不做 SQL 校验语法错误不会被报告调用方需自行保证输入合法性ShowDoc 的convertSqlToArray()在try/catch中捕获异常解析异常时返回错误信息字符串而非崩溃这是一种务实的防御写法。性能非首要目标README 明确定位是完整、准确地支持 MySQL 方言优先于性能优化开启$calcPositions会进一步增加耗时按需开启。方言适配非 MySQL 方言下可能需按 Parser-Manual 调整保留字表开启ansi_quotes选项可适配 ANSI_QUOTES 模式的数据库。结构化覆盖范围仅 README 列出的 12 类语句获得完整结构化解析其余语句退化为 token 数组消费方需自行兜底如 ShowDoc 中非CREATE语句直接返回null。十二、总结PHP-SQL-Parser 以纯 PHP、零依赖、非校验、聚焦 MySQL 方言的极简定位提供了从解析PHPSQLParser到生成PHPSQLCreator的完整 SQL 结构化能力并凭借接近 SQL-92 的方言覆盖面获得了广泛的适用性。在 ShowDoc 中它承担着将CREATE TABLE建表语句自动解析为字段级结构化数据、进而渲染成 Markdown 数据库文档的关键职责——从 Convert.class.php 的消费方式可以看出这套解析树映射 渲染输出的模式非常适合文档生成、SQL 审计、语句改写、代码分析等场景值得开发者借鉴复用。【免费下载链接】showdocShowDoc is a tool greatly applicable for an IT team to share documents online一个非常适合IT团队的在线API文档、技术文档工具项目地址: https://gitcode.com/gh_mirrors/sh/showdoc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考