diff --git a/CHANGELOG b/CHANGELOG index 44d5938e..aaef1988 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,8 @@ Development Version ------------------- -Nothing yet. +* Escape literal dollar signs in PHP output so SQL text is not interpreted + as PHP string interpolation. Release 0.6.0 (Aug 13, 2026) diff --git a/sqlparse/filters/output.py b/sqlparse/filters/output.py index d4e20f29..7c16914e 100644 --- a/sqlparse/filters/output.py +++ b/sqlparse/filters/output.py @@ -117,7 +117,8 @@ def _process(self, stream, varname, has_nl): # quote cannot break out of the generated string literal # (GHSA-3496-9g83-7v6x). else: - token.value = token.value.replace('\\', '\\\\').replace('"', '\\"') + token.value = (token.value.replace('\\', '\\\\') + .replace('"', '\\"').replace('$', '\\$')) # Put the token yield sql.Token(T.Text, token.value) diff --git a/tests/test_format.py b/tests/test_format.py index 93495067..bb6859b1 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -800,3 +800,14 @@ def test_strip_ws_removes_trailing_ws_in_groups(): # issue782 strip_whitespace=True) expected = '(where foo = bar) from' assert formatted == expected + + +@pytest.mark.parametrize('query, expected', [ + ("SELECT '$name'", r'''$sql = "SELECT '\$name'";'''), + ('SELECT $body$text$body$', r'''$sql = "SELECT \$body\$text\$body\$";'''), + ('SELECT "$table"', r'''$sql = "SELECT \"\$table\"";'''), + ('SELECT 1 /* $name */', r'''$sql = "SELECT 1 /* \$name */";'''), + ("SELECT '\\$name'", r'''$sql = "SELECT '\\\$name'";'''), +]) +def test_php_output_preserves_literal_dollar_signs(query, expected): + assert sqlparse.format(query, output_format='php') == expected