返回博客

PHP heredoc 里写 // 注释,SQL 直接炸:用 -- 不用 //

某天下午,我在一个后台管理系统的报表接口里修了一个查询逻辑,顺手在 SQL heredoc 里加了一行 // 修复说明php -l 检查通过,自信提交。然后上线之后,接口直接返回 500,错误日志里一行醒目的:

You have an error in your SQL syntax ... near '// 修复说明' at line 7

php -l 明明过了,为什么运行时报 SQL 语法错?

现象

  • php -l filename.php 输出 No syntax errors detected,一切正常
  • 线上调用报 1064 You have an error in your SQL syntax,错误 near 后面跟着 // 字符
  • 本地直接执行 PHP 脚本同样复现,与环境无关

根因

PHP 的 //PHP 解析器层的行注释。但 heredoc(<<<SQL ... SQL)是字符串字面量,PHP 解析器把它当作字符串内容整体传递,不会剥离其中的任何 PHP 风格注释

换句话说,// 出现在 heredoc 内,它就是字符串的一部分,会原封不动地被拼进最终 SQL。

当这段 SQL 送到 MySQL 时,MySQL 的注释语法只有三种:

  • -- (双横线 + 至少一个空格)
  • #(井号)
  • /* ... */(块注释)

MySQL 不认识 //,于是把 // 及其后面的内容当作 SQL token 解析,直接触发 1064。

坑:php -l 通过 ≠ SQL 语法正确

php -l 只校验 PHP 语法层,字符串内容它不管。IDE 语法高亮有时也会把 heredoc 内的 // 标灰——那是 IDE 的渲染逻辑,和运行时毫无关系。两个假信号叠加,特别容易让人放松警惕。

错误示例

$sql = <<<SQL
SELECT a.id, COUNT(*) AS total
FROM orders a
LEFT JOIN order_items b ON a.id = b.order_id
WHERE a.status = 1
  // 2026-05: 去掉了渠道过滤条件
  AND a.created_at LIKE '{$month}%'
GROUP BY a.id
SQL;
 
$rows = Db::query($sql);  // → MySQL 1064

正解

方案 1:用 -- (SQL 标准行注释,注意后面要有空格)

$sql = <<<SQL
SELECT a.id, COUNT(*) AS total
FROM orders a
LEFT JOIN order_items b ON a.id = b.order_id
WHERE a.status = 1
  -- 2026-05: 去掉了渠道过滤条件
  AND a.created_at LIKE '{$month}%'
GROUP BY a.id
SQL;

-- 后面必须跟至少一个空格,否则 MySQL 不识别。-- comment 是合法的,--comment 不是。

方案 2:用 /* */ 块注释(跨 DB 通用)

$sql = <<<SQL
SELECT a.id, COUNT(*) AS total
FROM orders a
WHERE a.status = 1
  /* 2026-05: 去掉了渠道过滤条件 */
  AND a.created_at LIKE '{$month}%'
SQL;

方案 3:把注释放到 PHP 层,不进 SQL

// 2026-05: 去掉了渠道过滤条件
$sql = <<<SQL
SELECT a.id, COUNT(*) AS total
FROM orders a
WHERE a.status = 1
  AND a.created_at LIKE '{$month}%'
SQL;
正解:SQL 内注释用 -- (带空格)或 /* */,// 只能活在 PHP 层

改完后用 grep -n '//' yourfile.php 检查,确认 heredoc 块内没有 // 残留。或者直接跑一次 EXPLAIN <sql>,比 php -l 更早发现 SQL 语法问题。

一句话外卖

PHP heredoc 是字符串,不是代码块——// 进去就是 SQL 的一部分,MySQL 不认,换 -- /* */