!C99Shell v. 2.5 [PHP 8 Update] [24.05.2025]!

Software: Apache. PHP/8.3.27 

uname -a: Linux pdx1-shared-a4-04 6.6.104-grsec-jammy+ #3 SMP Tue Sep 16 00:28:11 UTC 2025 x86_64 

uid=6659440(dh_z2jmpm) gid=2086089(pg10499364) groups=2086089(pg10499364)  

Safe-mode: OFF (not secure)

/usr/local/wp/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/   drwxr-xr-x
Free 712.22 GB of 879.6 GB (80.97%)
Home    Back    Forward    UPDIR    Refresh    Search    Buffer    Encoder    Tools    Proc.    FTP brute    Sec.    SQL    PHP-code    Update    Self remove    Logout    


Viewing file:     PreparedSQLPlaceholdersSniff.php (25.12 KB)      -rw-r--r--
Select action/file-type:
(+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php
/**
 * WordPress Coding Standard.
 *
 * @package WPCS\WordPressCodingStandards
 * @link    https://github.com/WordPress/WordPress-Coding-Standards
 * @license https://opensource.org/licenses/MIT MIT
 */

namespace WordPressCS\WordPress\Sniffs\DB;

use PHP_CodeSniffer\Util\Tokens;
use PHPCSUtils\Tokens\Collections;
use PHPCSUtils\Utils\Arrays;
use PHPCSUtils\Utils\PassedParameters;
use PHPCSUtils\Utils\TextStrings;
use WordPressCS\WordPress\Helpers\MinimumWPVersionTrait;
use WordPressCS\WordPress\Helpers\WPDBTrait;
use WordPressCS\WordPress\Sniff;

/**
 * Checks for incorrect use of the $wpdb->prepare method.
 *
 * Checks the following issues:
 * - The only placeholders supported are: %d, %f (%F), %s, %i, and their variations.
 * - Literal % signs need to be properly escaped as `%%`.
 * - Simple placeholders (%d, %f, %F, %s, %i) should be left unquoted in the query string.
 * - Complex placeholders - numbered and formatted variants - will not be quoted
 *   automagically by $wpdb->prepare(), so if used for values, should be quoted in
 *   the query string.
 *   The only exception to this is complex placeholders for %i. In that case, the
 *   replacement *will* still be backtick-quoted.
 * - Either an array of replacements should be passed matching the number of
 *   placeholders found or individual parameters for each placeholder should
 *   be passed.
 * - Wildcards for LIKE compare values should be passed in via a replacement parameter.
 *
 * The sniff allows for a specific pattern with a variable number of placeholders
 * created using code along the lines of:
 * `sprintf( 'query .... IN (%s) ...', implode( ',', array_fill( 0, count( $something ), '%s' ) ) )`.
 *
 * @link https://developer.wordpress.org/reference/classes/wpdb/prepare/
 * @link https://core.trac.wordpress.org/changeset/41496
 * @link https://core.trac.wordpress.org/changeset/41471
 * @link https://core.trac.wordpress.org/changeset/55151
 *
 * @since 0.14.0
 * @since 3.0.0 Support for the %i placeholder has been added
 *
 * @uses \WordPressCS\WordPress\Helpers\MinimumWPVersionTrait::$minimum_wp_version
 */
final class PreparedSQLPlaceholdersSniff extends Sniff {

    use MinimumWPVersionTrait;
    use WPDBTrait;

    /**
     * These regexes were originally copied from https://www.php.net/function.sprintf#93552
     * and adjusted for limitations in `$wpdb->prepare()`.
     *
     * Near duplicate of the one used in the WP.I18n sniff, but with fewer types allowed.
     *
     * Note: The regex delimiters and modifiers are not included to allow this regex to be
     * concatenated together with other regex partials.
     *
     * @since 0.14.0
     *
     * @var string
     */
    const PREPARE_PLACEHOLDER_REGEX = '(?:
        (?<![^%]%)                     # Don\'t match a literal % (%%), including when it could overlap with a placeholder.
        (?:
            %                          # Start of placeholder.
            (?:[0-9]+\\\\?\$)?         # Optional ordering of the placeholders.
            [+-]?                      # Optional sign specifier.
            (?:
                (?:0|\'.)?                 # Optional padding specifier - excluding the space.
                -?                         # Optional alignment specifier.
                [0-9]*                     # Optional width specifier.
                (?:\.(?:[ 0]|\'.)?[0-9]+)? # Optional precision specifier with optional padding character.
                |                      # Only recognize the space as padding in combination with a width specifier.
                (?:[ ])?                   # Optional space padding specifier.
                -?                         # Optional alignment specifier.
                [0-9]+                     # Width specifier.
                (?:\.(?:[ 0]|\'.)?[0-9]+)? # Optional precision specifier with optional padding character.
            )
            [dfFsi]                    # Type specifier.
        )
    )';

    /**
     * Similar to above, but for the placeholder types *not* supported.
     *
     * Note: all optional parts are forced to be greedy to allow for the negative look ahead
     * at the end to work.
     *
     * @since 0.14.0
     *
     * @var string
     */
    const UNSUPPORTED_PLACEHOLDER_REGEX = '`(?:
        (?<!%)                     # Don\'t match a literal % (%%).
        (
            %                          # Start of placeholder.
            (?!                        # Negative look ahead.
                %[^%]                       # Not a correct literal % (%%).
                |
                %%[dfFsi]                   # Nor a correct literal % (%%), followed by a simple placeholder.
            )
            (?:[0-9]+\\\\??\$)?+       # Optional ordering of the placeholders.
            [+-]?+                     # Optional sign specifier.
            (?:
                (?:0|\'.)?+                 # Optional padding specifier - excluding the space.
                -?+                         # Optional alignment specifier.
                [0-9]*+                     # Optional width specifier.
                (?:\.(?:[ 0]|\'.)?[0-9]+)?+ # Optional precision specifier with optional padding character.
                |                      # Only recognize the space as padding in combination with a width specifier.
                (?:[ ])?+                   # Optional space padding specifier.
                -?+                         # Optional alignment specifier.
                [0-9]++                     # Width specifier.
                (?:\.(?:[ 0]|\'.)?[0-9]+)?+ # Optional precision specifier with optional padding character.
            )
            (?![dfFsi])                # Negative look ahead: not one of the supported placeholders.
            (?:[^ \'"]*|$)             # but something else instead.
        )
    )`x';

    /**
     * List of $wpdb methods we are interested in.
     *
     * @since 0.14.0
     *
     * @var array
     */
    protected $target_methods = array(
        'prepare' => true,
    );

    /**
     * Storage for the stack pointer to the method call token.
     *
     * @since 0.14.0
     *
     * @var int
     */
    protected $methodPtr;

    /**
     * Simple regex snippet to recognize and remember quotes.
     *
     * @since 0.14.0
     *
     * @var string
     */
    private $regex_quote = '["\']';

    /**
     * Returns an array of tokens this test wants to listen for.
     *
     * @since 0.14.0
     *
     * @return array
     */
    public function register() {
        return array(
            \T_VARIABLE,
            \T_STRING,
        );
    }

    /**
     * Processes this test, when one of its tokens is encountered.
     *
     * @since 0.14.0
     *
     * @param int $stackPtr The position of the current token in the stack.
     *
     * @return void
     */
    public function process_token( $stackPtr ) {

        $this->set_minimum_wp_version();

        if ( ! $this->is_wpdb_method_call( $this->phpcsFile, $stackPtr, $this->target_methods ) ) {
            return;
        }

        $parameters = PassedParameters::getParameters( $this->phpcsFile, $this->methodPtr );
        if ( empty( $parameters ) ) {
            return;
        }

        $query = PassedParameters::getParameterFromStack( $parameters, 1, 'query' );
        if ( false === $query ) {
            return;
        }

        $text_string_tokens_found = false;
        $variable_found           = false;
        $sql_wildcard_found       = false;
        $total_placeholders       = 0;
        $total_parameters         = \count( $parameters );
        $valid_in_clauses         = array(
            'uses_in'          => 0,
            'implode_fill'     => 0,
            'adjustment_count' => 0,
        );
        $skip_from                = null;
        $skip_to                  = null;

        for ( $i = $query['start']; $i <= $query['end']; $i++ ) {
            // Skip over groups of tokens if they are part of an inline function call.
            if ( isset( $skip_from, $skip_to ) && $i >= $skip_from && $i <= $skip_to ) {
                $i = $skip_to;
                continue;
            }

            if ( ! isset( Tokens::$textStringTokens[ $this->tokens[ $i ]['code'] ] ) ) {
                if ( \T_VARIABLE === $this->tokens[ $i ]['code'] ) {
                    if ( '$wpdb' !== $this->tokens[ $i ]['content'] ) {
                        $variable_found = true;
                    }
                    continue;
                }

                // Detect a specific pattern for variable replacements in combination with `IN`.
                if ( \T_STRING === $this->tokens[ $i ]['code'] ) {

                    if ( 'sprintf' === strtolower( $this->tokens[ $i ]['content'] ) ) {
                        $sprintf_parameters = PassedParameters::getParameters( $this->phpcsFile, $i );

                        if ( ! empty( $sprintf_parameters ) ) {
                            /*
                             * Check for named params. sprintf() does not support this due to its variadic nature,
                             * and we cannot analyze the code correctly if it is used, so skip the whole sprintf()
                             * in that case.
                             */
                            $valid_sprintf = true;
                            foreach ( $sprintf_parameters as $param ) {
                                if ( isset( $param['name'] ) ) {
                                    $valid_sprintf = false;
                                    break;
                                }
                            }

                            if ( false === $valid_sprintf ) {
                                $next = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $i + 1 ), null, true );
                                if ( \T_OPEN_PARENTHESIS === $this->tokens[ $next ]['code']
                                    && isset( $this->tokens[ $next ]['parenthesis_closer'] )
                                ) {
                                    $skip_from = ( $i + 1 );
                                    $skip_to   = $this->tokens[ $next ]['parenthesis_closer'];
                                }

                                continue;
                            }

                            // We know for sure this sprintf() uses positional parameters, so this will be fine.
                            $skip_from  = ( $sprintf_parameters[1]['end'] + 1 );
                            $last_param = end( $sprintf_parameters );
                            $skip_to    = ( $last_param['end'] + 1 );

                            $valid_in_clauses['implode_fill']     += $this->analyse_sprintf( $sprintf_parameters );
                            $valid_in_clauses['adjustment_count'] += ( \count( $sprintf_parameters ) - 1 );
                        }
                        unset( $sprintf_parameters, $valid_sprintf, $last_param );

                    } elseif ( 'implode' === strtolower( $this->tokens[ $i ]['content'] ) ) {
                        $prev = $this->phpcsFile->findPrevious(
                            Tokens::$emptyTokens + array( \T_STRING_CONCAT => \T_STRING_CONCAT ),
                            ( $i - 1 ),
                            $query['start'],
                            true
                        );

                        if ( isset( Tokens::$textStringTokens[ $this->tokens[ $prev ]['code'] ] ) ) {
                            $prev_content = TextStrings::stripQuotes( $this->tokens[ $prev ]['content'] );
                            $regex_quote  = $this->get_regex_quote_snippet( $prev_content, $this->tokens[ $prev ]['content'] );

                            // Only examine the implode if preceded by an ` IN (`.
                            if ( preg_match( '`\s+IN\s*\(\s*(' . $regex_quote . ')?$`i', $prev_content, $match ) > 0 ) {

                                if ( isset( $match[1] ) && $regex_quote !== $this->regex_quote ) {
                                    $this->phpcsFile->addError(
                                        'Dynamic placeholder generation should not have surrounding quotes.',
                                        $prev,
                                        'QuotedDynamicPlaceholderGeneration'
                                    );
                                }

                                if ( $this->analyse_implode( $i ) === true ) {
                                    ++$valid_in_clauses['uses_in'];
                                    ++$valid_in_clauses['implode_fill'];

                                    $next = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $i + 1 ), null, true );
                                    if ( \T_OPEN_PARENTHESIS === $this->tokens[ $next ]['code']
                                        && isset( $this->tokens[ $next ]['parenthesis_closer'] )
                                    ) {
                                        $skip_from = ( $i + 1 );
                                        $skip_to   = $this->tokens[ $next ]['parenthesis_closer'];
                                    }
                                }
                            }
                            unset( $next, $prev_content, $regex_quote, $match );
                        }
                        unset( $prev );
                    }
                }

                continue;
            }

            $text_string_tokens_found = true;
            $content                  = $this->tokens[ $i ]['content'];

            $regex_quote = $this->regex_quote;
            if ( isset( Tokens::$stringTokens[ $this->tokens[ $i ]['code'] ] ) ) {
                $content     = TextStrings::stripQuotes( $content );
                $regex_quote = $this->get_regex_quote_snippet( $content, $this->tokens[ $i ]['content'] );
            }

            if ( \T_DOUBLE_QUOTED_STRING === $this->tokens[ $i ]['code']
                || \T_HEREDOC === $this->tokens[ $i ]['code']
            ) {
                // Only interested in actual query text, so strip out variables.
                $stripped_content = TextStrings::stripEmbeds( $content );
                if ( $stripped_content !== $content ) {
                    $vars_without_wpdb = array_filter(
                        TextStrings::getEmbeds( $content ),
                        static function ( $symbol ) {
                            return preg_match( '`^\{?\$\{?wpdb\??->`', $symbol ) !== 1;
                        }
                    );

                    $content = $stripped_content;

                    if ( ! empty( $vars_without_wpdb ) ) {
                        $variable_found = true;
                    }
                }
                unset( $stripped_content, $vars_without_wpdb );
            }

            $placeholders = preg_match_all( '`' . self::PREPARE_PLACEHOLDER_REGEX . '`x', $content, $matches );
            if ( $placeholders > 0 ) {
                $total_placeholders += $placeholders;
            }

            /*
             * Analyze the query for incorrect LIKE queries.
             *
             * - `LIKE %s` is the only correct one.
             * - `LIKE '%s'` or `LIKE "%s"` will not be reported here, but in the quote check.
             * - Any other `LIKE` statement should be reported, either for using `LIKE` without
             *   SQL wildcards or for not passing the SQL wildcards via the replacement.
             */
            $regex = '`\s+LIKE\s*(?:(' . $regex_quote . ')(?!%s(?:\1|$))(?P<content>.*?)(?:\1|$)|(?:concat\((?![^\)]*%s[^\)]*\))(?P<concat>[^\)]*))\))`i';
            if ( preg_match_all( $regex, $content, $matches ) > 0 ) {
                $walk = array();
                if ( ! empty( $matches['content'] ) ) {
                    $matches['content'] = array_filter( $matches['content'] );
                    if ( ! empty( $matches['content'] ) ) {
                        $walk[] = 'content';
                    }
                }
                if ( ! empty( $matches['concat'] ) ) {
                    $matches['concat'] = array_filter( $matches['concat'] );
                    if ( ! empty( $matches['concat'] ) ) {
                        $walk[] = 'concat';
                    }
                }

                if ( ! empty( $walk ) ) {
                    foreach ( $walk as $match_key ) {
                        foreach ( $matches[ $match_key ] as $index => $match ) {
                            $data = array( $matches[0][ $index ] );

                            // Both a `%` as well as a `_` are wildcards in SQL.
                            if ( strpos( $match, '%' ) === false && strpos( $match, '_' ) === false ) {
                                $this->phpcsFile->addWarning(
                                    'Unless you are using SQL wildcards, using LIKE is inefficient. Use a straight compare instead. Found: %s.',
                                    $i,
                                    'LikeWithoutWildcards',
                                    $data
                                );
                            } else {
                                $sql_wildcard_found = true;

                                if ( strpos( $match, '%s' ) === false ) {
                                    $this->phpcsFile->addError(
                                        'SQL wildcards for a LIKE query should be passed in through a replacement parameter. Found: %s.',
                                        $i,
                                        'LikeWildcardsInQuery',
                                        $data
                                    );
                                } else {
                                    $this->phpcsFile->addError(
                                        'SQL wildcards for a LIKE query should be passed in through a replacement parameter and the variable part of the replacement should be escaped using "esc_like()". Found: %s.',
                                        $i,
                                        'LikeWildcardsInQueryWithPlaceholder',
                                        $data
                                    );
                                }
                            }

                            /*
                             * Don't throw `UnescapedLiteral`, `UnsupportedPlaceholder` or `QuotedPlaceholder`
                             * for this part of the SQL query.
                             */
                            $content = preg_replace( '`' . preg_quote( $match, '`' ) . '`', '', $content, 1 );
                        }
                    }
                }
                unset( $matches, $index, $match, $data );
            }

            if ( strpos( $content, '%' ) === false ) {
                continue;
            }

            /*
             * Analyze the query for unsupported placeholders.
             */
            if ( preg_match_all( self::UNSUPPORTED_PLACEHOLDER_REGEX, $content, $matches ) > 0 ) {
                if ( ! empty( $matches[0] ) ) {
                    foreach ( $matches[0] as $match ) {
                        if ( '%' === $match ) {
                            $this->phpcsFile->addError(
                                'Found unescaped literal "%%" character.',
                                $i,
                                'UnescapedLiteral',
                                array( $match )
                            );
                        } else {
                            $this->phpcsFile->addError(
                                'Unsupported placeholder used in $wpdb->prepare(). Found: "%s".',
                                $i,
                                'UnsupportedPlaceholder',
                                array( $match )
                            );
                        }
                    }
                }
                unset( $match, $matches );
            }

            if ( $this->wp_version_compare( $this->minimum_wp_version, '6.2', '<' ) ) {
                if ( preg_match_all( '`' . self::PREPARE_PLACEHOLDER_REGEX . '`x', $content, $matches ) > 0 ) {
                    if ( ! empty( $matches[0] ) ) {
                        foreach ( $matches[0] as $match ) {
                            if ( 'i' === substr( $match, -1 ) ) {
                                $this->phpcsFile->addError(
                                    'The %%i modifier is only supported in WP 6.2 or higher. Found: "%s".',
                                    $i,
                                    'UnsupportedIdentifierPlaceholder',
                                    array( $match )
                                );
                            }
                        }
                    }
                }
                unset( $match, $matches );
            }

            /*
             * Analyze the query for single/double quoted simple value placeholders
             * Identifiers are checked separately.
             */
            $regex = '`(' . $regex_quote . ')%[dfFs]\1`';
            if ( preg_match_all( $regex, $content, $matches ) > 0 ) {
                if ( ! empty( $matches[0] ) ) {
                    foreach ( $matches[0] as $match ) {
                        $this->phpcsFile->addError(
                            'Simple placeholders should not be quoted in the query string in $wpdb->prepare(). Found: %s.',
                            $i,
                            'QuotedSimplePlaceholder',
                            array( $match )
                        );
                    }
                }
                unset( $match, $matches );
            }

            /*
             * Analyze the query for quoted identifier placeholders.
             */
            $regex = '/(' . $regex_quote . '|`)(?<placeholder>' . self::PREPARE_PLACEHOLDER_REGEX . ')\1/x';
            if ( preg_match_all( $regex, $content, $matches ) > 0 ) {
                if ( ! empty( $matches ) ) {
                    foreach ( $matches['placeholder'] as $index => $match ) {
                        if ( 'i' === substr( $match, -1 ) ) {
                            $this->phpcsFile->addError(
                                'Placeholders used for identifiers (%%i) in the query string in $wpdb->prepare() are always quoted automagically. Please remove the surrounding quotes. Found: %s',
                                $i,
                                'QuotedIdentifierPlaceholder',
                                array( $matches[0][ $index ] )
                            );
                        }
                    }
                }
                unset( $index, $match, $matches );
            }

            /*
             * Analyze the query for unquoted complex placeholders.
             */
            $regex = '`(?<!' . $regex_quote . ')' . self::PREPARE_PLACEHOLDER_REGEX . '(?!' . $regex_quote . ')`x';
            if ( preg_match_all( $regex, $content, $matches ) > 0 ) {
                if ( ! empty( $matches[0] ) ) {
                    foreach ( $matches[0] as $match ) {
                        if ( substr( $match, -1 ) !== 'i' && preg_match( '`^%[dfFsi]$`', $match ) !== 1 ) { // Identifiers must always be unquoted.
                            $this->phpcsFile->addWarning(
                                'Complex placeholders used for values in the query string in $wpdb->prepare() will NOT be quoted automagically. Found: %s.',
                                $i,
                                'UnquotedComplexPlaceholder',
                                array( $match )
                            );
                        }
                    }
                }
                unset( $match, $matches );
            }

            /*
             * Check for an ` IN (%s)` clause.
             */
            $found_in = preg_match_all( '`\s+IN\s*\(\s*%s\s*\)`i', $content, $matches );
            if ( $found_in > 0 ) {
                $valid_in_clauses['uses_in'] += $found_in;
            }
            unset( $found_in );
        }

        if ( false === $text_string_tokens_found ) {
            // Query string passed in as a variable or function call, nothing to examine.
            return;
        }

        if ( 0 === $total_placeholders ) {
            if ( 1 === $total_parameters ) {
                if ( false === $variable_found && false === $sql_wildcard_found ) {
                    /*
                     * Only throw this warning if the PreparedSQL sniff won't throw one about
                     * variables being found.
                     * Also don't throw it if we just advised to use a replacement variable to pass a
                     * string containing an SQL wildcard.
                     */
                    $this->phpcsFile->addWarning(
                        'It is not necessary to prepare a query which doesn\'t use variable replacement.',
                        $i,
                        'UnnecessaryPrepare'
                    );
                }
            } elseif ( 0 === $valid_in_clauses['uses_in'] ) {
                $this->phpcsFile->addWarning(
                    'Replacement variables found, but no valid placeholders found in the query.',
                    $i,
                    'UnfinishedPrepare'
                );
            }

            return;
        }

        if ( 1 === $total_parameters ) {
            $this->phpcsFile->addError(
                'Placeholders found in the query passed to $wpdb->prepare(), but no replacements found. Expected %d replacement(s) parameters.',
                $stackPtr,
                'MissingReplacements',
                array( $total_placeholders )
            );
            return;
        }

        $replacements = $parameters;
        unset( $replacements['query'], $replacements[1] ); // Remove the query param, whether passed positionally or named.

        // The parameters may have been passed as an array in the variadic $args parameter.
        $args_param = PassedParameters::getParameterFromStack( $parameters, 2, 'args' );
        if ( false !== $args_param && 2 === $total_parameters ) {
            $next = $this->phpcsFile->findNext(
                Tokens::$emptyTokens,
                $args_param['start'],
                ( $args_param['end'] + 1 ),
                true
            );

            if ( false !== $next
                && ( \T_ARRAY === $this->tokens[ $next ]['code']
                    || ( isset( Collections::shortArrayListOpenTokensBC()[ $this->tokens[ $next ]['code'] ] )
                        && Arrays::isShortArray( $this->phpcsFile, $next ) === true ) )
            ) {
                $replacements = PassedParameters::getParameters( $this->phpcsFile, $next );
            }
        }

        $total_replacements  = \count( $replacements );
        $total_placeholders -= $valid_in_clauses['adjustment_count'];

        // Bow out when `IN` clauses have been used which appear to be correct.
        if ( $valid_in_clauses['uses_in'] > 0
            && $valid_in_clauses['uses_in'] === $valid_in_clauses['implode_fill']
            && 1 === $total_replacements
        ) {
            return;
        }

        /*
         * Verify that the correct amount of replacements have been passed.
         */
        if ( $total_replacements !== $total_placeholders ) {
            $this->phpcsFile->addWarning(
                'Incorrect number of replacements passed to $wpdb->prepare(). Found %d replacement parameters, expected %d.',
                $stackPtr,
                'ReplacementsWrongNumber',
                array( $total_replacements, $total_placeholders )
            );
        }
    }

    /**
     * Retrieve a regex snippet to recognize and remember quotes based on the quote style
     * used in the original string (if any).
     *
     * This allows for recognizing `"` and `\'` in single quoted strings,
     * recognizing `'` and `\"` in double quotes strings and `'` and `"`when the quote
     * style is unknown or it is a non-quoted string (heredoc/nowdoc and such).
     *
     * @since 0.14.0
     *
     * @param string $stripped_content Text string content without surrounding quotes.
     * @param string $original_content Original content for the same text string.
     *
     * @return string
     */
    protected function get_regex_quote_snippet( $stripped_content, $original_content ) {
        $regex_quote = $this->regex_quote;

        if ( $original_content !== $stripped_content ) {
            $quote_style = $original_content[0];

            if ( '"' === $quote_style ) {
                $regex_quote = '\\\\"|\'';
            } elseif ( "'" === $quote_style ) {
                $regex_quote = '"|\\\\\'';
            }
        }

        return $regex_quote;
    }

    /**
     * Analyze a sprintf() query wrapper to see if it contains a specific code pattern
     * to deal correctly with `IN` queries.
     *
     * The pattern we are searching for is:
     * `sprintf( 'query ....', implode( ',', array_fill( 0, count( $something ), '%s' ) ) )`
     *
     * @since 0.14.0
     *
     * @param array $sprintf_params Parameters details for the sprintf call.
     *
     * @return int The number of times the pattern was found in the replacements.
     */
    protected function analyse_sprintf( $sprintf_params ) {
        $found = 0;

        unset( $sprintf_params[1] ); // Remove the positionally passed $format param.

        foreach ( $sprintf_params as $sprintf_param ) {
            $implode = $this->phpcsFile->findNext(
                Tokens::$emptyTokens + array( \T_NS_SEPARATOR => \T_NS_SEPARATOR ),
                $sprintf_param['start'],
                $sprintf_param['end'],
                true
            );
            if ( \T_STRING === $this->tokens[ $implode ]['code']
                && 'implode' === strtolower( $this->tokens[ $implode ]['content'] )
            ) {
                if ( $this->analyse_implode( $implode ) === true ) {
                    ++$found;
                }
            }
        }

        return $found;
    }

    /**
     * Analyze an implode() function call to see if it contains a specific code pattern
     * to dynamically create placeholders.
     *
     * The pattern we are searching for is:
     * `implode( ',', array_fill( 0, count( $something ), '%s' ) )`
     *
     * This pattern presumes unquoted placeholders!
     *
     * Identifiers (%i) are not supported, as this function is designed to work
     * with `IN()`, which contains a list of values. In the future, it should
     * be possible to simplify code using the implode/array_fill pattern to
     * use a variable number of identifiers, e.g. `CONCAT(%...i)`,
     * https://core.trac.wordpress.org/ticket/54042
     *
     * @since 0.14.0
     *
     * @param int $implode_token The stackPtr to the implode function call.
     *
     * @return bool True if the pattern is found, false otherwise.
     */
    protected function analyse_implode( $implode_token ) {
        $implode_params = PassedParameters::getParameters( $this->phpcsFile, $implode_token );
        if ( empty( $implode_params ) || \count( $implode_params ) !== 2 ) {
            return false;
        }

        $implode_separator_param = PassedParameters::getParameterFromStack( $implode_params, 1, 'separator' );
        if ( false === $implode_separator_param
            || preg_match( '`^(["\']), ?\1$`', $implode_separator_param['clean'] ) !== 1
        ) {
            return false;
        }

        $implode_array_param = PassedParameters::getParameterFromStack( $implode_params, 2, 'array' );
        if ( false === $implode_array_param ) {
            return false;
        }

        $array_fill = $this->phpcsFile->findNext(
            Tokens::$emptyTokens + array( \T_NS_SEPARATOR => \T_NS_SEPARATOR ),
            $implode_array_param['start'],
            $implode_array_param['end'],
            true
        );

        if ( \T_STRING !== $this->tokens[ $array_fill ]['code']
            || 'array_fill' !== strtolower( $this->tokens[ $array_fill ]['content'] )
        ) {
            return false;
        }

        $array_fill_value_param = PassedParameters::getParameter( $this->phpcsFile, $array_fill, 3, 'value' );
        if ( false === $array_fill_value_param ) {
            return false;
        }

        if ( "'%i'" === $array_fill_value_param['clean']
            || '"%i"' === $array_fill_value_param['clean']
        ) {
            $firstNonEmpty = $this->phpcsFile->findNext( Tokens::$emptyTokens, $array_fill_value_param['start'], $array_fill_value_param['end'], true );

            $this->phpcsFile->addError(
                'The %i placeholder cannot be used within SQL `IN()` clauses.',
                $firstNonEmpty,
                'IdentifierWithinIN'
            );
            return false;
        }

        return (bool) preg_match( '`^(["\'])%[dfFs]\1$`', $array_fill_value_param['clean'] );
    }
}

:: Command execute ::

Enter:
 
Select:
 

:: Search ::
  - regexp 

:: Upload ::
 
[ Read-Only ]

:: Make Dir ::
 
[ Read-Only ]
:: Make File ::
 
[ Read-Only ]

:: Go Dir ::
 
:: Go File ::
 

--[ c99shell v. 2.5 [PHP 8 Update] [24.05.2025] | Generation time: 0.0131 ]--