Skip to content

DB/PreparedSQL: incorrect error identification for namespaced function calls #2648

@rodrigoprimo

Description

@rodrigoprimo

Bug Description

The WordPress.DB.PreparedSQL sniff has a limitation in how it identifies and counts errors for namespaced function calls. It doesn't recognize namespaced names as a single unit, leading to incorrect error counting and identification.

Problem 1: Incorrect error counting for all namespaced calls

Multi-level namespace calls generate multiple errors (one for each namespace part) instead of recognizing it as a single namespaced function call. For example, namespace\Sub\custom_function( $foo ) should trigger two errors (one for the namespaced call namespace\Sub\custom_function and one for the unescaped variable $foo), but currently triggers four errors: "namespace", "Sub", "custom_function", and "$foo".

Problem 2: Missing errors when function name matches escaping/auto-escaped/formatting functions

When a namespaced function call matches a function name in $SQLEscapingFunctions, $SQLAutoEscapedFunctions, or FormattingFunctionsHelper::$formattingFunctions, the sniff treats the function name as if it were a global function call and skips checking the contents. For example, MyNamespace\absint( $foo ) should trigger two errors (one for MyNamespace\absint and one for $foo), but currently only triggers one error for "MyNamespace" because the sniff incorrectly treats absint() as a valid global escaping function and skips checking its contents.

Minimal Code Snippet

The issue happens when running this command:

vendor/bin/phpcs --standard=WordPress --sniffs=WordPress.DB.PreparedSQL test.php

... over a file containing this code:

<?php

// Problem 1:
$wpdb->query( "SELECT * FROM $wpdb->posts WHERE ID = " . namespace\Sub\custom_function( $foo ) );

// Problem 2:
$wpdb->query( "SELECT * FROM $wpdb->posts WHERE ID = " . MyNamespace\absint( $foo ) );

Expected behavior:

FILE: test.php
--------------------------------------------------------------------
FOUND 4 ERRORS AFFECTING 2 LINES
--------------------------------------------------------------------
 4 | ERROR | Use placeholders and $wpdb->prepare(); found namespace\Sub\custom_function
 4 | ERROR | Use placeholders and $wpdb->prepare(); found $foo
 7 | ERROR | Use placeholders and $wpdb->prepare(); found MyNamespace\absint
 7 | ERROR | Use placeholders and $wpdb->prepare(); found $foo
--------------------------------------------------------------------

Actual behavior:

FILE: test.php
--------------------------------------------------------------------------
FOUND 5 ERRORS AFFECTING 2 LINES
--------------------------------------------------------------------------
 4 | ERROR | Use placeholders and $wpdb->prepare(); found namespace
 4 | ERROR | Use placeholders and $wpdb->prepare(); found Sub
 4 | ERROR | Use placeholders and $wpdb->prepare(); found custom_function
 4 | ERROR | Use placeholders and $wpdb->prepare(); found $foo
 7 | ERROR | Use placeholders and $wpdb->prepare(); found MyNamespace
--------------------------------------------------------------------------

Error Code

WordPress.DB.PreparedSQL.NotPrepared

Environment

Question Answer
PHP version 8.4
PHP_CodeSniffer version 3.13.4
WordPressCS version develop
PHPCSUtils version 1.2.1
PHPCSExtra version 1.5.0
WordPressCS install type git clone
IDE (if relevant) N/A

Additional Context (optional)

The root cause is that the sniff processes tokens one by one without recognizing that a namespaced name should be treated as a single unit. With PHPCS 3.x tokenization, namespaced names are tokenized as separate T_STRING and T_NS_SEPARATOR tokens (e.g., namespace, \, Sub, \, custom_function), and the sniff processes each T_STRING individually.

Additionally, when processing T_STRING tokens, the sniff checks if the string matches any function in the escaping/auto-escaped/formatting function lists without first verifying whether the function is namespaced. If it finds a match, it skips to the end of the function call's parentheses, missing any variables inside.

It will be easier to fix this issue once support for PHPCS 3.x is dropped.

Tested Against develop Branch?

  • I have verified the issue still exists in the develop branch of WordPressCS.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions