PgIdentifierEscapedAsLiteral
warning native on by default quick-fix PHP ≥ 5.4Group: Probable bugs
Escape PostgreSQL identifiers with the identifier API.
Example
<?php
function f($c) { $t=pg_escape_literal($c,'inventory'); pg_query($c,"SELECT * FROM $t"); }Reported:
- line 2: Escape PostgreSQL identifiers with the identifier API.
Detection
- pg_escape_literal result occupies a proven single table identifier after FROM, JOIN, UPDATE, INTO or TABLE in a constant SQL skeleton passed to pg_query. Exclude values, expression positions, qualified identifier assembly and ambiguous SQL.
- The keyword is read inside the innermost parenthesis group still open at the operand. An open group counts only when it is a subquery (it starts with SELECT or WITH); any other open group is a function call or an expression list, where FROM introduces a value (
substring(a FROM $x),extract(year FROM $x),trim(both FROM $x)). - FROM directly preceded by DISTINCT is the comparison
IS [NOT] DISTINCT FROMand takes a value. - A statement starting with COPY is never a table position for this rule:
COPY t FROM $xnames a file. - Unbalanced closing parentheses before the operand make the SQL ambiguous.
- The keyword is read inside the innermost parenthesis group still open at the operand. An open group counts only when it is a subquery (it starts with SELECT or WITH); any other open group is a function call or an expression list, where FROM introduces a value (
- Resolve builtin functions, extension classes, constants and method receivers, including aliases and supported named arguments. Inspect reachable operations in one lexical scope with bounded local flow.
- Emit one finding per violating operation. Never infer policy from names or comments. When the proof is inconclusive, nothing is reported.
Exceptions (no report)
Parentheses around the producer do not bypass consumer checks. Captures into nested scopes and exhausted consumer budgets withhold the fix, while retaining the finding at the proven identifier use.
- Exclude shadowed or unresolved symbols, incomplete code, unknown required constants, ambiguous receiver identity and unsupported versions. Reassignment, escaping aliases, and unknown calls invalidate dependent flow facts.
- Both @custos-ignore PgIdentifierEscapedAsLiteral and @noinspection PgIdentifierEscapedAsLiteral suppress this native rule.
Fix
Replace only the builtin function name pg_escape_literal with pg_escape_identifier when every use of the local result is a proven identifier position. Preserve leading qualification and do not change arguments.
Uses are not only those written after the call. When the call sits in a loop, a read of the assigned variable placed earlier in that loop receives the result on the next iteration, so any such read withholds the fix; reads before the loop do not. A goto in the same scope withholds the fix as well, and so does a call to compact or get_defined_vars, which read locals without naming them. A later read that several definitions can reach (after a branch or a loop that reassigns the variable) may still carry the result, so it withholds the fix too; a plain overwrite of the variable is not a use.
Use exact byte edits and preserve comments, evaluation count, argument order and supported PHP syntax. Withhold the fix whenever its prerequisites cannot be proven.
PHP versions
Apply throughout PHP 5.3–8.5 only where the extension API and syntax exist. PostgreSQL literal/identifier escaping requires PHP 5.4.4 or later. Do not flag API unavailability through this rule.
Options
None. Use normal rule configuration to enable or disable the inspection.
Configure
In custos.json:
{
"rules": {
"PgIdentifierEscapedAsLiteral": {
"enabled": false
}
}
}Suppress
Before the statement or declaration (or the first statement of the file):
// @custos-ignore PgIdentifierEscapedAsLiteral