diff --git a/.github/workflows/basic-qa.yml b/.github/workflows/basic-qa.yml
index 9b489f7a7e..78a3e47b8d 100644
--- a/.github/workflows/basic-qa.yml
+++ b/.github/workflows/basic-qa.yml
@@ -13,7 +13,8 @@ concurrency:
cancel-in-progress: true
env:
- PHPCS_DEV: '3.x-dev'
+ PHPCS_3_DEV: '3.x-dev'
+ PHPCS_4_DEV: '4.x-dev'
UTILS_DEV: 'dev-develop'
EXTRA_DEV: 'dev-develop'
@@ -21,9 +22,13 @@ jobs:
# Check code style of sniffs, rulesets and XML documentation.
# Check that all sniffs are feature complete.
sniffs:
- name: Run code sniffs
+ name: "Run code sniffs (PHPCS ${{ matrix.dependencies }})"
runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ dependencies: [ 'phpcs-3-dev', 'phpcs-4-dev' ]
+
steps:
- name: Checkout repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
@@ -45,7 +50,7 @@ jobs:
- name: "Composer: set PHPCS dependencies (dev)"
run: >
composer require --no-update --no-scripts --no-interaction
- squizlabs/php_codesniffer:"${{ env.PHPCS_DEV }}"
+ squizlabs/php_codesniffer:"${{ matrix.dependencies == 'phpcs-3-dev' && env.PHPCS_3_DEV || env.PHPCS_4_DEV }}"
phpcsstandards/phpcsutils:"${{ env.UTILS_DEV }}"
phpcsstandards/phpcsextra:"${{ env.EXTRA_DEV }}"
@@ -151,7 +156,7 @@ jobs:
strategy:
matrix:
php: [ 'latest' ]
- dependencies: [ 'lowest', 'stable', 'dev' ]
+ dependencies: [ 'lowest', 'stable', 'phpcs-3-dev', 'phpcs-4-dev' ]
name: "Ruleset test: PHP ${{ matrix.php }} on PHPCS ${{ matrix.dependencies }}"
@@ -170,10 +175,10 @@ jobs:
coverage: none
- name: "Composer: set PHPCS dependencies for tests (dev)"
- if: ${{ matrix.dependencies == 'dev' }}
+ if: ${{ matrix.dependencies == 'phpcs-3-dev' || matrix.dependencies == 'phpcs-4-dev' }}
run: >
composer require --no-update --no-scripts --no-interaction
- squizlabs/php_codesniffer:"${{ env.PHPCS_DEV }}"
+ squizlabs/php_codesniffer:"${{ matrix.dependencies == 'phpcs-3-dev' && env.PHPCS_3_DEV || env.PHPCS_4_DEV }}"
phpcsstandards/phpcsutils:"${{ env.UTILS_DEV }}"
phpcsstandards/phpcsextra:"${{ env.EXTRA_DEV }}"
@@ -216,21 +221,26 @@ jobs:
# Test for fixer conflicts by running the auto-fixers of the complete WPCS over the test case files.
# This is not an exhaustive test, but should give an early indication for typical fixer conflicts.
- # If only fixable errors are found, the exit code will be 1, which can be interpreted as success.
- # This check is only run against "dev" as conflicts can not be fixed for already released versions.
+ # If only fixable errors are found, the exit code will be 1 on PHPCS 3.x and 2 on PHPCS 4.x, nad the test succeeded.
+ # This check is only run against the dev versions as conflicts can not be fixed for already released versions.
- name: Test for fixer conflicts (fixes expected)
- if: ${{ matrix.dependencies == 'dev' }}
+ if: ${{ matrix.dependencies == 'phpcs-3-dev' || matrix.dependencies == 'phpcs-4-dev' }}
id: phpcbf
continue-on-error: true
run: |
set +e
- $(pwd)/vendor/bin/phpcbf -pq ./WordPress/Tests/ --standard=WordPress --extensions=inc --exclude=Generic.PHP.Syntax --report=summary --ignore=/WordPress/Tests/WP/GlobalVariablesOverrideUnitTest.7.inc
+ $(pwd)/vendor/bin/phpcbf -pq ./WordPress/Tests/ --standard=WordPress --extensions=inc --exclude=Generic.PHP.Syntax,Squiz.Functions.FunctionDuplicateArgument --report=summary --ignore=/WordPress/Tests/WP/GlobalVariablesOverrideUnitTest.7.inc
exitcode="$?"
echo "EXITCODE=$exitcode" >> "$GITHUB_OUTPUT"
exit "$exitcode"
- name: Fail the build on fixer conflicts and other errors
- if: ${{ steps.phpcbf.outputs.EXITCODE != 0 && steps.phpcbf.outputs.EXITCODE != 1 }}
+ if: >-
+ ${{
+ steps.phpcbf.outputs.EXITCODE != 0
+ && steps.phpcbf.outputs.EXITCODE != 1
+ && ! (matrix.dependencies == 'phpcs-4-dev' && steps.phpcbf.outputs.EXITCODE == 2)
+ }}
run: exit ${{ steps.phpcbf.outputs.EXITCODE }}
phpstan:
diff --git a/.github/workflows/quicktest.yml b/.github/workflows/quicktest.yml
index 5da0c17a35..93acf351f0 100644
--- a/.github/workflows/quicktest.yml
+++ b/.github/workflows/quicktest.yml
@@ -65,13 +65,25 @@ jobs:
if: ${{ matrix.dependencies == 'stable' }}
run: composer lint
- - name: Run the unit tests without code coverage
- if: ${{ github.event.repository.fork == true || github.ref_name != 'develop' }}
- run: composer run-tests
+ - name: Grab PHPCS version
+ id: phpcs_version
+ run: echo "VERSION=$(vendor/bin/phpcs --version | grep --only-matching --max-count=1 --extended-regexp '\b[0-9]+\.[0-9]+')" >> "$GITHUB_OUTPUT"
- - name: Run the unit tests with code coverage
- if: ${{ github.event.repository.fork == false && github.ref_name == 'develop' }}
- run: composer coverage
+ - name: Run the unit tests without code coverage (PHPCS 3.x)
+ if: ${{ (github.event.repository.fork == true || github.ref_name != 'develop') && startsWith( steps.phpcs_version.outputs.VERSION, '3.' ) }}
+ run: composer run-tests-phpcs3
+
+ - name: Run the unit tests without code coverage (PHPCS 4.x)
+ if: ${{ (github.event.repository.fork == true || github.ref_name != 'develop') && startsWith( steps.phpcs_version.outputs.VERSION, '4.' ) }}
+ run: composer run-tests-phpcs4
+
+ - name: Run the unit tests with code coverage (PHPCS 3.x)
+ if: ${{ github.event.repository.fork == false && github.ref_name == 'develop' && startsWith( steps.phpcs_version.outputs.VERSION, '3.' ) }}
+ run: composer coverage-phpcs3
+
+ - name: Run the unit tests with code coverage (PHPCS 4.x)
+ if: ${{ github.event.repository.fork == false && github.ref_name == 'develop' && startsWith( steps.phpcs_version.outputs.VERSION, '4.' ) }}
+ run: composer coverage-phpcs4
- name: Send coverage report to Codecov
if: ${{ success() && github.event.repository.fork == false && github.ref_name == 'develop' }}
diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml
index 3a9514793c..5ce3c5555e 100644
--- a/.github/workflows/unit-tests.yml
+++ b/.github/workflows/unit-tests.yml
@@ -15,7 +15,8 @@ concurrency:
cancel-in-progress: true
env:
- PHPCS_DEV: '3.x-dev'
+ PHPCS_3_DEV: '3.x-dev'
+ PHPCS_4_DEV: '4.x-dev'
UTILS_DEV: 'dev-develop'
EXTRA_DEV: 'dev-develop'
@@ -52,18 +53,31 @@ jobs:
# Test against dev versions of all dependencies with select PHP versions for early detection of issues.
- php: '7.2'
- dependencies: 'dev'
+ dependencies: 'phpcs-3-dev'
+ extensions: ''
+ coverage: false
+ - php: '7.2'
+ dependencies: 'phpcs-4-dev'
+ extensions: ''
+ coverage: false
+ - php: '8.1'
+ dependencies: 'phpcs-3-dev'
extensions: ''
coverage: false
- php: '8.1'
- dependencies: 'dev'
+ dependencies: 'phpcs-4-dev'
+ extensions: ''
+ coverage: false
+ - php: '8.4'
+ dependencies: 'phpcs-3-dev'
extensions: ''
coverage: false
- php: '8.5'
- dependencies: 'dev'
+ dependencies: 'phpcs-4-dev'
extensions: ''
coverage: false
+
# Add extra build to test against PHPCS 4.
#- php: '7.4'
# dependencies: '4.0.x-dev as 3.99.99'
@@ -83,7 +97,7 @@ jobs:
- name: Setup ini config
id: set_ini
run: |
- if [ "${{ matrix.dependencies }}" != "dev" ]; then
+ if [ "${{ matrix.dependencies }}" != "phpcs-3-dev" ] && [ "${{ matrix.dependencies }}" != "phpcs-4-dev" ]; then
echo 'PHP_INI=error_reporting=E_ALL & ~E_DEPRECATED, display_errors=On, display_startup_errors=On' >> "$GITHUB_OUTPUT"
else
echo 'PHP_INI=error_reporting=-1, display_errors=On, display_startup_errors=On' >> "$GITHUB_OUTPUT"
@@ -98,10 +112,10 @@ jobs:
tools: cs2pr
- name: "Composer: set PHPCS dependencies for tests (dev)"
- if: ${{ matrix.dependencies == 'dev' }}
+ if: ${{ matrix.dependencies == 'phpcs-3-dev' || matrix.dependencies == 'phpcs-4-dev' }}
run: >
composer require --no-update --no-scripts --no-interaction
- squizlabs/php_codesniffer:"${{ env.PHPCS_DEV }}"
+ squizlabs/php_codesniffer:"${{ matrix.dependencies == 'phpcs-3-dev' && env.PHPCS_3_DEV || env.PHPCS_4_DEV }}"
phpcsstandards/phpcsutils:"${{ env.UTILS_DEV }}"
phpcsstandards/phpcsextra:"${{ env.EXTRA_DEV }}"
@@ -127,13 +141,25 @@ jobs:
if: ${{ matrix.dependencies == 'stable' }}
run: composer lint -- --checkstyle | cs2pr
- - name: Run the unit tests without code coverage
- if: ${{ matrix.coverage == false || github.event.repository.fork == true }}
- run: composer run-tests
+ - name: Grab PHPCS version
+ id: phpcs_version
+ run: echo "VERSION=$(vendor/bin/phpcs --version | grep --only-matching --max-count=1 --extended-regexp '\b[0-9]+\.[0-9]+')" >> "$GITHUB_OUTPUT"
+
+ - name: Run the unit tests without code coverage (PHPCS 3.x)
+ if: ${{ startsWith( steps.phpcs_version.outputs.VERSION, '3.' ) && (matrix.coverage == false || github.event.repository.fork == true) }}
+ run: composer run-tests-phpcs3
+
+ - name: Run the unit tests without code coverage (PHPCS 4.x)
+ if: ${{ startsWith( steps.phpcs_version.outputs.VERSION, '4.' ) && (matrix.coverage == false || github.event.repository.fork == true) }}
+ run: composer run-tests-phpcs4
+
+ - name: Run the unit tests with code coverage (PHPCS 3.x)
+ if: ${{ startsWith( steps.phpcs_version.outputs.VERSION, '3.' ) && matrix.coverage == true && github.event.repository.fork == false }}
+ run: composer coverage-phpcs3
- - name: Run the unit tests with code coverage
- if: ${{ matrix.coverage == true && github.event.repository.fork == false }}
- run: composer coverage
+ - name: Run the unit tests with code coverage (PHPCS 4.x)
+ if: ${{ startsWith( steps.phpcs_version.outputs.VERSION, '4.' ) && matrix.coverage == true && github.event.repository.fork == false }}
+ run: composer coverage-phpcs4
- name: Send coverage report to Codecov
if: ${{ success() && matrix.coverage == true && github.event.repository.fork == false }}
diff --git a/README.md b/README.md
index 8cc41d9cd6..6ca8b4c0f5 100644
--- a/README.md
+++ b/README.md
@@ -248,6 +248,8 @@ At this moment, WordPressCS offer the following tools:
- `old_text_domain`: an array with one or more (old) text domain names which need to be replaced;
- `new_text_domain`: the correct (new) text domain as a string.
+ **Note**: When using PHP_CodeSniffer 4.0+, this sniff will fix the text domain everywhere for plugins, but it will **not** fix the text domain in the CSS file header for themes, as PHP_CodeSniffer 4.0+ does not support scanning CSS files. Manual intervention will be required to update the `Text Domain` header in the theme's CSS file.
+
## Contributing
diff --git a/Tests/bootstrap.php b/Tests/bootstrap.php
index fd4c952893..219c45f5f5 100644
--- a/Tests/bootstrap.php
+++ b/Tests/bootstrap.php
@@ -57,6 +57,16 @@
die( 1 );
}
+// Alias the PHPCS 3.x test case to the PHPCS 4.x name.
+if ( class_exists( 'PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest' ) === true
+ && class_exists( 'PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase' ) === false
+) {
+ class_alias(
+ 'PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest',
+ 'PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase'
+ );
+}
+
/*
* Set the PHPCS_IGNORE_TEST environment variable to ignore tests from other standards.
*/
diff --git a/WordPress-Docs/ruleset.xml b/WordPress-Docs/ruleset.xml
index bc64617386..2d30812df7 100644
--- a/WordPress-Docs/ruleset.xml
+++ b/WordPress-Docs/ruleset.xml
@@ -82,8 +82,15 @@
+
+
+
+
+
+
+
diff --git a/WordPress/AbstractArrayAssignmentRestrictionsSniff.php b/WordPress/AbstractArrayAssignmentRestrictionsSniff.php
index 9ca127b06b..b0bd93b9e0 100644
--- a/WordPress/AbstractArrayAssignmentRestrictionsSniff.php
+++ b/WordPress/AbstractArrayAssignmentRestrictionsSniff.php
@@ -158,7 +158,7 @@ public function process_token( $stackPtr ) {
$inst = array();
/*
- * Covers array assignments:
+ * Covers following forms of array assignments:
* `$foo = array( 'bar' => 'taz' );`
* `$foo['bar'] = $taz;`
*/
diff --git a/WordPress/AbstractClassRestrictionsSniff.php b/WordPress/AbstractClassRestrictionsSniff.php
index d380522b3a..1fd4d672a6 100644
--- a/WordPress/AbstractClassRestrictionsSniff.php
+++ b/WordPress/AbstractClassRestrictionsSniff.php
@@ -10,6 +10,7 @@
namespace WordPressCS\WordPress;
use PHP_CodeSniffer\Util\Tokens;
+use PHPCSUtils\BackCompat\Helper;
use PHPCSUtils\Tokens\Collections;
use PHPCSUtils\Utils\GetTokensAsString;
use PHPCSUtils\Utils\Namespaces;
@@ -145,13 +146,20 @@ public function is_targetted_token( $stackPtr ) {
if ( \T_DOUBLE_COLON === $token['code'] ) {
$nameEnd = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $stackPtr - 1 ), null, true );
- if ( \T_STRING !== $this->tokens[ $nameEnd ]['code'] ) {
+ if ( isset( Collections::nameTokens()[ $this->tokens[ $nameEnd ]['code'] ] ) === false ) {
// Hierarchy keyword or object stored in variable.
return false;
}
- $nameStart = ( $this->phpcsFile->findPrevious( Collections::namespacedNameTokens(), ( $nameEnd - 1 ), null, true ) + 1 );
- $classname = GetTokensAsString::noEmpties( $this->phpcsFile, $nameStart, $nameEnd );
+ $classname = $this->tokens[ $nameEnd ]['content'];
+ $nameStart = $nameEnd;
+
+ if ( \version_compare( Helper::getVersion(), '3.99.99', '<=' ) === true ) {
+ // For PHPCS 3.x, a namespaced class name is split over multiple tokens so it is necessary to combine then to get the class name.
+ $nameStart = ( $this->phpcsFile->findPrevious( Collections::namespacedNameTokens(), ( $nameEnd - 1 ), null, true ) + 1 );
+ $classname = GetTokensAsString::noEmpties( $this->phpcsFile, $nameStart, $nameEnd );
+ }
+
$classname = $this->get_namespaced_classname( $classname, ( $nameStart - 1 ) );
}
diff --git a/WordPress/AbstractFunctionParameterSniff.php b/WordPress/AbstractFunctionParameterSniff.php
index 7b369583ce..e9a2226f1f 100644
--- a/WordPress/AbstractFunctionParameterSniff.php
+++ b/WordPress/AbstractFunctionParameterSniff.php
@@ -62,7 +62,8 @@ public function getGroups() {
* @param int $stackPtr The position of the current token in the stack.
* @param string $group_name The name of the group which was matched.
* @param string $matched_content The token content (function name) which was matched
- * in lowercase.
+ * in lowercase. For T_NAME_FULLY_QUALIFIED tokens,
+ * the leading backslash is removed.
*
* @return int|void Integer stack pointer to skip forward or void to continue
* normal file processing.
diff --git a/WordPress/AbstractFunctionRestrictionsSniff.php b/WordPress/AbstractFunctionRestrictionsSniff.php
index 7f300cbc3f..b9961efc06 100644
--- a/WordPress/AbstractFunctionRestrictionsSniff.php
+++ b/WordPress/AbstractFunctionRestrictionsSniff.php
@@ -125,6 +125,7 @@ public function register() {
return array(
\T_STRING,
+ \T_NAME_FULLY_QUALIFIED,
);
}
@@ -201,10 +202,16 @@ public function process_token( $stackPtr ) {
return;
}
- // Preliminary check. If the content of the T_STRING is not one of the functions we're
+ $content = $this->tokens[ $stackPtr ]['content'];
+
+ if ( \T_NAME_FULLY_QUALIFIED === $this->tokens[ $stackPtr ]['code'] ) {
+ $content = \ltrim( $content, '\\' );
+ }
+
+ // Preliminary check. If the content of the name token is not one of the functions we're
// looking for, we can bow out before doing the heavy lifting of checking whether
// this is a function call.
- if ( preg_match( $this->prelim_check_regex, $this->tokens[ $stackPtr ]['content'] ) !== 1 ) {
+ if ( preg_match( $this->prelim_check_regex, $content ) !== 1 ) {
return;
}
@@ -286,7 +293,13 @@ public function is_targetted_token( $stackPtr ) {
* normal file processing.
*/
public function check_for_matches( $stackPtr ) {
- $token_content = strtolower( $this->tokens[ $stackPtr ]['content'] );
+ $content = $this->tokens[ $stackPtr ]['content'];
+
+ if ( \T_NAME_FULLY_QUALIFIED === $this->tokens[ $stackPtr ]['code'] ) {
+ $content = \ltrim( $content, '\\' );
+ }
+
+ $token_content = strtolower( $content );
$skip_to = array();
foreach ( $this->groups as $groupName => $group ) {
@@ -319,7 +332,8 @@ public function check_for_matches( $stackPtr ) {
* @param int $stackPtr The position of the current token in the stack.
* @param string $group_name The name of the group which was matched.
* @param string $matched_content The token content (function name) which was matched
- * in lowercase.
+ * in lowercase. For T_NAME_FULLY_QUALIFIED tokens,
+ * the leading backslash is removed.
*
* @return int|void Integer stack pointer to skip forward or void to continue
* normal file processing.
diff --git a/WordPress/Helpers/ArrayWalkingFunctionsHelper.php b/WordPress/Helpers/ArrayWalkingFunctionsHelper.php
index 76623d7dd9..d82596e436 100644
--- a/WordPress/Helpers/ArrayWalkingFunctionsHelper.php
+++ b/WordPress/Helpers/ArrayWalkingFunctionsHelper.php
@@ -71,7 +71,7 @@ public static function get_functions() {
* @return bool
*/
public static function is_array_walking_function( $functionName ) {
- return isset( self::$arrayWalkingFunctions[ strtolower( $functionName ) ] );
+ return isset( self::$arrayWalkingFunctions[ strtolower( ltrim( $functionName, '\\' ) ) ] );
}
/**
@@ -94,6 +94,11 @@ public static function get_callback_parameter( File $phpcsFile, $stackPtr ) {
}
$functionName = strtolower( $tokens[ $stackPtr ]['content'] );
+
+ if ( \T_NAME_FULLY_QUALIFIED === $tokens[ $stackPtr ]['code'] ) {
+ $functionName = ltrim( $functionName, '\\' );
+ }
+
if ( isset( self::$arrayWalkingFunctions[ $functionName ] ) === false ) {
return false;
}
diff --git a/WordPress/Helpers/ConstantsHelper.php b/WordPress/Helpers/ConstantsHelper.php
index 320c6312f8..8ef2b77d23 100644
--- a/WordPress/Helpers/ConstantsHelper.php
+++ b/WordPress/Helpers/ConstantsHelper.php
@@ -34,7 +34,8 @@
final class ConstantsHelper {
/**
- * Determine whether an arbitrary T_STRING token is the use of a global constant.
+ * Determine whether an arbitrary T_STRING or T_NAME_FULLY_QUALIFIED token is the use of a
+ * global constant.
*
* @since 1.0.0
* @since 3.0.0 - Moved from the Sniff class to this class.
@@ -42,7 +43,7 @@ final class ConstantsHelper {
* - The `$phpcsFile` parameter was added.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
- * @param int $stackPtr The position of the T_STRING token.
+ * @param int $stackPtr The position of the T_STRING or T_NAME_FULLY_QUALIFIED token.
*
* @return bool
*/
@@ -55,7 +56,9 @@ public static function is_use_of_global_constant( File $phpcsFile, $stackPtr ) {
}
// Is this one of the tokens this function handles ?
- if ( \T_STRING !== $tokens[ $stackPtr ]['code'] ) {
+ if ( \T_STRING !== $tokens[ $stackPtr ]['code']
+ && \T_NAME_FULLY_QUALIFIED !== $tokens[ $stackPtr ]['code']
+ ) {
return false;
}
@@ -91,6 +94,14 @@ public static function is_use_of_global_constant( File $phpcsFile, $stackPtr ) {
return false;
}
+ // If the token is a fully qualified name, ensure it does not include a namespace path.
+ if ( \T_NAME_FULLY_QUALIFIED === $tokens[ $stackPtr ]['code'] ) {
+ $trimmed = \ltrim( $tokens[ $stackPtr ]['content'], '\\' );
+ if ( \strpos( $trimmed, '\\' ) !== false ) {
+ return false;
+ }
+ }
+
if ( ContextHelper::is_token_namespaced( $phpcsFile, $stackPtr ) === true ) {
// Namespaced constant of the same name.
return false;
diff --git a/WordPress/Helpers/ContextHelper.php b/WordPress/Helpers/ContextHelper.php
index 1da3286715..78e6e7df70 100644
--- a/WordPress/Helpers/ContextHelper.php
+++ b/WordPress/Helpers/ContextHelper.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Tokens;
+use PHPCSUtils\BackCompat\Helper;
use PHPCSUtils\Tokens\Collections;
use PHPCSUtils\Utils\Parentheses;
use PHPCSUtils\Utils\PassedParameters;
@@ -113,6 +114,19 @@ final class ContextHelper {
),
);
+ /**
+ * List of tokens representing qualified names.
+ *
+ * @since 3.3.0
+ *
+ * @var array
+ */
+ private static $qualifiedNameTokens = array(
+ \T_NAME_FULLY_QUALIFIED => true,
+ \T_NAME_QUALIFIED => true,
+ \T_NAME_RELATIVE => true,
+ );
+
/**
* Check if a particular token acts - statically or non-statically - on an object.
*
@@ -163,6 +177,28 @@ public static function is_token_namespaced( File $phpcsFile, $stackPtr ) {
return false;
}
+ $isPhpcs3 = version_compare( Helper::getVersion(), '3.99.99', '<=' );
+
+ if ( true === $isPhpcs3 ) {
+ return self::is_token_namespaced_phpcs3( $phpcsFile, $stackPtr );
+ }
+
+ return self::is_token_namespaced_phpcs4( $phpcsFile, $stackPtr );
+ }
+
+ /**
+ * Check if a particular token is prefixed with a namespace when running PHPCS 3. Different
+ * methods are necessary because the tokenization of namespaced names changed between PHPCS 3
+ * and 4.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The index of the token in the stack.
+ *
+ * @return bool
+ */
+ private static function is_token_namespaced_phpcs3( File $phpcsFile, $stackPtr ) {
+ $tokens = $phpcsFile->getTokens();
+
$prev = $phpcsFile->findPrevious( Tokens::$emptyTokens, ( $stackPtr - 1 ), null, true );
if ( \T_NS_SEPARATOR !== $tokens[ $prev ]['code'] ) {
@@ -179,6 +215,35 @@ public static function is_token_namespaced( File $phpcsFile, $stackPtr ) {
return true;
}
+ /**
+ * Check if a particular token is prefixed with a namespace when running PHPCS 4. Different
+ * methods are necessary because the tokenization of namespaced names changed between PHPCS 3
+ * and 4.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The index of the token in the stack.
+ *
+ * @return bool
+ */
+ private static function is_token_namespaced_phpcs4( File $phpcsFile, $stackPtr ) {
+ $tokens = $phpcsFile->getTokens();
+
+ if ( \T_NAME_QUALIFIED === $tokens[ $stackPtr ]['code']
+ || \T_NAME_RELATIVE === $tokens[ $stackPtr ]['code']
+ ) {
+ return true;
+ }
+
+ // If the token is a fully qualified name, consider it as namespaced if it contains
+ // more than one namespace separator (i.e., not in the global namespace).
+ if ( \T_NAME_FULLY_QUALIFIED === $tokens[ $stackPtr ]['code']
+ && \substr_count( $tokens[ $stackPtr ]['content'], '\\' ) > 1 ) {
+ return true;
+ }
+
+ return false;
+ }
+
/**
* Check if a token is (part of) a parameter for a function call to a select list of functions.
*
@@ -186,7 +251,7 @@ public static function is_token_namespaced( File $phpcsFile, $stackPtr ) {
*
* For example: this function could be used to determine if the variable `$foo` is used
* in a global function call to the function `is_foo()`.
- * In that case, a call to this function would return the stackPtr to the T_STRING `is_foo`
+ * In that case, a call to this function would return the stackPtr to the name token `is_foo`
* for code like: `is_foo( $foo, 'some_other_param' )`, while it would return `false` for
* the following code `is_bar( $foo, 'some_other_param' )`.
*
@@ -215,7 +280,7 @@ public static function is_token_namespaced( File $phpcsFile, $stackPtr ) {
* or only `strtolower( $var )`.
* Defaults to `false`.
*
- * @return int|bool Stack pointer to the function call T_STRING token or false otherwise.
+ * @return int|bool Stack pointer to the function call name token or false otherwise.
*/
public static function is_in_function_call( File $phpcsFile, $stackPtr, array $valid_functions, $global_function = true, $allow_nested = false ) {
$valid_functions = array_change_key_case( $valid_functions, \CASE_LOWER );
@@ -232,11 +297,25 @@ public static function is_in_function_call( File $phpcsFile, $stackPtr, array $v
foreach ( $nested_parenthesis as $open => $close ) {
$prev_non_empty = $phpcsFile->findPrevious( Tokens::$emptyTokens, ( $open - 1 ), null, true );
- if ( false === $prev_non_empty || \T_STRING !== $tokens[ $prev_non_empty ]['code'] ) {
+ if ( false === $prev_non_empty || isset( Collections::nameTokens()[ $tokens[ $prev_non_empty ]['code'] ] ) === false ) {
continue;
}
- if ( isset( $valid_functions[ strtolower( $tokens[ $prev_non_empty ]['content'] ) ] ) === false ) {
+ $functionNameLC = \strtolower( $tokens[ $prev_non_empty ]['content'] );
+
+ if ( true === $global_function
+ && \T_NAME_FULLY_QUALIFIED === $tokens[ $prev_non_empty ]['code']
+ ) {
+ $functionNameLC = \ltrim( $functionNameLC, '\\' );
+ }
+
+ if ( false === $global_function
+ && isset( self::$qualifiedNameTokens[ $tokens[ $prev_non_empty ]['code'] ] ) === true
+ ) {
+ $functionNameLC = \substr( $functionNameLC, \strrpos( $functionNameLC, '\\' ) + 1 );
+ }
+
+ if ( isset( $valid_functions[ $functionNameLC ] ) === false ) {
if ( false === $allow_nested ) {
// Function call encountered, but not to one of the allowed functions.
return false;
@@ -382,6 +461,11 @@ public static function is_in_array_comparison( File $phpcsFile, $stackPtr ) {
$tokens = $phpcsFile->getTokens();
$function_name = strtolower( $tokens[ $function_ptr ]['content'] );
+
+ if ( \T_NAME_FULLY_QUALIFIED === $tokens[ $function_ptr ]['code'] ) {
+ $function_name = \ltrim( $function_name, '\\' );
+ }
+
if ( true === self::$arrayCompareFunctions[ $function_name ] ) {
return true;
}
diff --git a/WordPress/Helpers/EscapingFunctionsTrait.php b/WordPress/Helpers/EscapingFunctionsTrait.php
index b3fbf61d57..18470ced4a 100644
--- a/WordPress/Helpers/EscapingFunctionsTrait.php
+++ b/WordPress/Helpers/EscapingFunctionsTrait.php
@@ -226,7 +226,7 @@ final public function is_escaping_function( $functionName ) {
$this->addedCustomEscapingFunctions['escape'] = $this->customEscapingFunctions;
}
- return isset( $this->allEscapingFunctions[ strtolower( $functionName ) ] );
+ return isset( $this->allEscapingFunctions[ strtolower( ltrim( $functionName, '\\' ) ) ] );
}
/**
@@ -250,6 +250,6 @@ final public function is_auto_escaped_function( $functionName ) {
$this->addedCustomEscapingFunctions['autoescape'] = $this->customAutoEscapedFunctions;
}
- return isset( $this->allAutoEscapedFunctions[ strtolower( $functionName ) ] );
+ return isset( $this->allAutoEscapedFunctions[ strtolower( ltrim( $functionName, '\\' ) ) ] );
}
}
diff --git a/WordPress/Helpers/FormattingFunctionsHelper.php b/WordPress/Helpers/FormattingFunctionsHelper.php
index e04298ab46..dafd4aa9d8 100644
--- a/WordPress/Helpers/FormattingFunctionsHelper.php
+++ b/WordPress/Helpers/FormattingFunctionsHelper.php
@@ -55,6 +55,6 @@ final class FormattingFunctionsHelper {
* @return bool
*/
public static function is_formatting_function( $functionName ) {
- return isset( self::$formattingFunctions[ strtolower( $functionName ) ] );
+ return isset( self::$formattingFunctions[ strtolower( ltrim( $functionName, '\\' ) ) ] );
}
}
diff --git a/WordPress/Helpers/IsUnitTestTrait.php b/WordPress/Helpers/IsUnitTestTrait.php
index 0c9c77db13..b25ce2ccb3 100644
--- a/WordPress/Helpers/IsUnitTestTrait.php
+++ b/WordPress/Helpers/IsUnitTestTrait.php
@@ -138,7 +138,9 @@ final protected function get_all_test_classes() {
*/
$custom_test_classes = array();
if ( ! empty( $this->custom_test_classes ) ) {
- foreach ( $this->custom_test_classes as $v ) {
+ // PHPCS >= 4.0 converts empty string values in array properties to null.
+ // Filter out null values to avoid passing them to ltrim().
+ foreach ( array_filter( $this->custom_test_classes ) as $v ) {
$custom_test_classes[] = ltrim( $v, '\\' );
}
}
diff --git a/WordPress/Helpers/PrintingFunctionsTrait.php b/WordPress/Helpers/PrintingFunctionsTrait.php
index edd6dbe211..508f720b28 100644
--- a/WordPress/Helpers/PrintingFunctionsTrait.php
+++ b/WordPress/Helpers/PrintingFunctionsTrait.php
@@ -117,6 +117,6 @@ final public function get_printing_functions() {
* @return bool
*/
final public function is_printing_function( $functionName ) {
- return isset( $this->get_printing_functions()[ strtolower( $functionName ) ] );
+ return isset( $this->get_printing_functions()[ strtolower( ltrim( $functionName, '\\' ) ) ] );
}
}
diff --git a/WordPress/Helpers/SanitizationHelperTrait.php b/WordPress/Helpers/SanitizationHelperTrait.php
index 31661d169e..d8e0077677 100644
--- a/WordPress/Helpers/SanitizationHelperTrait.php
+++ b/WordPress/Helpers/SanitizationHelperTrait.php
@@ -239,7 +239,7 @@ final public function get_sanitizing_and_unslashing_functions() {
* @return bool
*/
final public function is_sanitizing_function( $functionName ) {
- return isset( $this->get_sanitizing_functions()[ strtolower( $functionName ) ] );
+ return isset( $this->get_sanitizing_functions()[ strtolower( ltrim( $functionName, '\\' ) ) ] );
}
/**
@@ -252,7 +252,7 @@ final public function is_sanitizing_function( $functionName ) {
* @return bool
*/
final public function is_sanitizing_and_unslashing_function( $functionName ) {
- return isset( $this->get_sanitizing_and_unslashing_functions()[ strtolower( $functionName ) ] );
+ return isset( $this->get_sanitizing_and_unslashing_functions()[ strtolower( ltrim( $functionName, '\\' ) ) ] );
}
/**
diff --git a/WordPress/Helpers/UnslashingFunctionsHelper.php b/WordPress/Helpers/UnslashingFunctionsHelper.php
index 2946b3acac..ed8eef514a 100644
--- a/WordPress/Helpers/UnslashingFunctionsHelper.php
+++ b/WordPress/Helpers/UnslashingFunctionsHelper.php
@@ -54,6 +54,6 @@ public static function get_functions() {
* @return bool
*/
public static function is_unslashing_function( $functionName ) {
- return isset( self::$unslashingFunctions[ strtolower( $functionName ) ] );
+ return isset( self::$unslashingFunctions[ strtolower( ltrim( $functionName, '\\' ) ) ] );
}
}
diff --git a/WordPress/Helpers/ValidationHelper.php b/WordPress/Helpers/ValidationHelper.php
index d5a22d7d56..eb0ef679b0 100644
--- a/WordPress/Helpers/ValidationHelper.php
+++ b/WordPress/Helpers/ValidationHelper.php
@@ -35,11 +35,12 @@ final class ValidationHelper {
* @var array
*/
private static $targets = array(
- \T_ISSET => 'construct',
- \T_EMPTY => 'construct',
- \T_STRING => 'function_call',
- \T_COALESCE => 'coalesce',
- \T_COALESCE_EQUAL => 'coalesce',
+ \T_ISSET => 'construct',
+ \T_EMPTY => 'construct',
+ \T_STRING => 'function_call',
+ \T_NAME_FULLY_QUALIFIED => 'function_call',
+ \T_COALESCE => 'coalesce',
+ \T_COALESCE_EQUAL => 'coalesce',
);
/**
@@ -218,8 +219,14 @@ public static function is_validated( File $phpcsFile, $stackPtr, $array_keys = a
break;
case 'function_call':
+ $contentLC = \strtolower( $tokens[ $i ]['content'] );
+
+ if ( \T_NAME_FULLY_QUALIFIED === $tokens[ $i ]['code'] ) {
+ $contentLC = \ltrim( $contentLC, '\\' );
+ }
+
// Only check calls to array_key_exists() and key_exists().
- if ( isset( self::$key_exists_functions[ strtolower( $tokens[ $i ]['content'] ) ] ) === false ) {
+ if ( isset( self::$key_exists_functions[ $contentLC ] ) === false ) {
continue 2;
}
diff --git a/WordPress/Helpers/WPDBTrait.php b/WordPress/Helpers/WPDBTrait.php
index 5843ea4225..05ff326ae0 100644
--- a/WordPress/Helpers/WPDBTrait.php
+++ b/WordPress/Helpers/WPDBTrait.php
@@ -65,9 +65,20 @@ final protected function is_wpdb_method_call( File $phpcsFile, $stackPtr, array
return false;
}
+ $contentLC = strtolower( $tokens[ $stackPtr ]['content'] );
+
+ // Not one of the possible token types.
+ if ( \T_VARIABLE !== $tokens[ $stackPtr ]['code']
+ && \T_STRING !== $tokens[ $stackPtr ]['code']
+ && \T_NAME_FULLY_QUALIFIED !== $tokens[ $stackPtr ]['code']
+ ) {
+ return false;
+ }
+
// Check for wpdb.
if ( ( \T_VARIABLE === $tokens[ $stackPtr ]['code'] && '$wpdb' !== $tokens[ $stackPtr ]['content'] )
- || ( \T_STRING === $tokens[ $stackPtr ]['code'] && 'wpdb' !== strtolower( $tokens[ $stackPtr ]['content'] ) )
+ || ( \T_STRING === $tokens[ $stackPtr ]['code'] && 'wpdb' !== $contentLC )
+ || ( \T_NAME_FULLY_QUALIFIED === $tokens[ $stackPtr ]['code'] && '\wpdb' !== $contentLC )
) {
return false;
}
diff --git a/WordPress/Helpers/WPHookHelper.php b/WordPress/Helpers/WPHookHelper.php
index b5b4f4a133..f6ef0de08b 100644
--- a/WordPress/Helpers/WPHookHelper.php
+++ b/WordPress/Helpers/WPHookHelper.php
@@ -99,7 +99,7 @@ public static function get_functions( $include_deprecated = true ) {
* for the format of the returned (single-dimensional) array.
*/
public static function get_hook_name_param( $function_name, array $parameters ) {
- $function_lc = strtolower( $function_name );
+ $function_lc = strtolower( ltrim( $function_name, '\\' ) );
if ( isset( self::$hookInvokeFunctions[ $function_lc ] ) === false ) {
return false;
}
diff --git a/WordPress/Sniffs/DB/DirectDatabaseQuerySniff.php b/WordPress/Sniffs/DB/DirectDatabaseQuerySniff.php
index 3271a41874..203154a6d1 100644
--- a/WordPress/Sniffs/DB/DirectDatabaseQuerySniff.php
+++ b/WordPress/Sniffs/DB/DirectDatabaseQuerySniff.php
@@ -238,7 +238,7 @@ public function process_token( $stackPtr ) {
$scopeEnd = $this->tokens[ $scope_function ]['scope_closer'];
for ( $i = ( $scopeStart + 1 ); $i < $scopeEnd; $i++ ) {
- if ( \T_STRING === $this->tokens[ $i ]['code'] ) {
+ if ( isset( Collections::nameTokens()[ $this->tokens[ $i ]['code'] ] ) ) {
$nextNonEmpty = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $i + 1 ), null, true );
if ( \T_OPEN_PARENTHESIS !== $this->tokens[ $nextNonEmpty ]['code'] ) {
@@ -247,6 +247,11 @@ public function process_token( $stackPtr ) {
$content = strtolower( $this->tokens[ $i ]['content'] );
+ if ( strpos( $content, '\\' ) !== false ) {
+ // Namespaced function call, get only the function name.
+ $content = substr( $content, strrpos( $content, '\\' ) + 1 );
+ }
+
if ( isset( $this->cacheDeleteFunctions[ $content ] ) ) {
if ( \in_array( $method, array( 'query', 'update', 'replace', 'delete' ), true ) ) {
diff --git a/WordPress/Sniffs/DB/PreparedSQLPlaceholdersSniff.php b/WordPress/Sniffs/DB/PreparedSQLPlaceholdersSniff.php
index ba72cabc3c..4e4fed1903 100644
--- a/WordPress/Sniffs/DB/PreparedSQLPlaceholdersSniff.php
+++ b/WordPress/Sniffs/DB/PreparedSQLPlaceholdersSniff.php
@@ -165,6 +165,7 @@ public function register() {
return array(
\T_VARIABLE,
\T_STRING,
+ \T_NAME_FULLY_QUALIFIED,
);
}
@@ -224,92 +225,89 @@ public function process_token( $stackPtr ) {
}
// 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 ( $this->is_global_function_call( $i, 'sprintf' ) ) {
+ $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 ( ! 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'];
}
- 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;
+ }
- 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 );
- // 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 );
- $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 ( $this->is_global_function_call( $i, 'implode' ) ) {
+ $ignore_tokens = Tokens::$emptyTokens + array(
+ \T_STRING_CONCAT => \T_STRING_CONCAT,
+ \T_NS_SEPARATOR => \T_NS_SEPARATOR,
+ );
- } elseif ( 'implode' === strtolower( $this->tokens[ $i ]['content'] ) ) {
- $ignore_tokens = Tokens::$emptyTokens + array(
- \T_STRING_CONCAT => \T_STRING_CONCAT,
- \T_NS_SEPARATOR => \T_NS_SEPARATOR,
- );
+ $prev = $this->phpcsFile->findPrevious(
+ $ignore_tokens,
+ ( $i - 1 ),
+ $query['start'],
+ true
+ );
- $prev = $this->phpcsFile->findPrevious(
- $ignore_tokens,
- ( $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'] );
- 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 ) {
- // 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 ( 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'];
- 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'];
- }
+ $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 );
+ unset( $next, $prev_content, $regex_quote, $match );
}
+ unset( $prev );
}
continue;
@@ -679,9 +677,7 @@ protected function analyse_sprintf( $sprintf_params ) {
$sprintf_param['end'],
true
);
- if ( \T_STRING === $this->tokens[ $implode ]['code']
- && 'implode' === strtolower( $this->tokens[ $implode ]['content'] )
- ) {
+ if ( $this->is_global_function_call( $implode, 'implode' ) ) {
if ( $this->analyse_implode( $implode ) === true ) {
++$found;
}
@@ -737,9 +733,7 @@ protected function analyse_implode( $implode_token ) {
true
);
- if ( \T_STRING !== $this->tokens[ $array_fill ]['code']
- || 'array_fill' !== strtolower( $this->tokens[ $array_fill ]['content'] )
- ) {
+ if ( ! $this->is_global_function_call( $array_fill, 'array_fill' ) ) {
return false;
}
@@ -763,4 +757,28 @@ protected function analyse_implode( $implode_token ) {
return (bool) preg_match( '`^(["\'])%[dfFs]\1$`', $array_fill_value_param['clean'] );
}
+
+ /**
+ * Check whether a token is an unqualified or fully qualified call to a given global function.
+ *
+ * Temporary, simplified helper: it only checks the token type and the normalized function name,
+ * reproducing this sniff's existing behavior. It does not verify call context, nor exclude method
+ * calls or partially qualified / namespace-relative names.
+ *
+ * As part of fixing https://github.com/WordPress/WordPress-Coding-Standards/issues/2720, this
+ * method should be replaced with a more robust and generic alternative.
+ *
+ * @param int $token_ptr The position of the token to examine.
+ * @param string $function_name The lowercase name of the global function to check for.
+ *
+ * @return bool
+ */
+ private function is_global_function_call( $token_ptr, $function_name ) {
+ $code = $this->tokens[ $token_ptr ]['code'];
+ if ( \T_STRING !== $code && \T_NAME_FULLY_QUALIFIED !== $code ) {
+ return false;
+ }
+
+ return ltrim( strtolower( $this->tokens[ $token_ptr ]['content'] ), '\\' ) === $function_name;
+ }
}
diff --git a/WordPress/Sniffs/DB/PreparedSQLSniff.php b/WordPress/Sniffs/DB/PreparedSQLSniff.php
index 05f0efc0db..7c49c53350 100644
--- a/WordPress/Sniffs/DB/PreparedSQLSniff.php
+++ b/WordPress/Sniffs/DB/PreparedSQLSniff.php
@@ -145,6 +145,7 @@ public function register() {
return array(
\T_VARIABLE,
\T_STRING,
+ \T_NAME_FULLY_QUALIFIED,
);
}
@@ -206,9 +207,15 @@ static function ( $symbol ) {
}
}
- if ( \T_STRING === $this->tokens[ $this->i ]['code'] ) {
+ if ( \T_STRING === $this->tokens[ $this->i ]['code']
+ || \T_NAME_FULLY_QUALIFIED === $this->tokens[ $this->i ]['code']
+ ) {
$content_lowercase = strtolower( $this->tokens[ $this->i ]['content'] );
+ if ( \T_NAME_FULLY_QUALIFIED === $this->tokens[ $this->i ]['code'] ) {
+ $content_lowercase = \ltrim( $content_lowercase, '\\' );
+ }
+
if (
isset( $this->SQLEscapingFunctions[ $content_lowercase ] )
|| isset( $this->SQLAutoEscapedFunctions[ $content_lowercase ] )
@@ -225,7 +232,7 @@ static function ( $symbol ) {
$this->i = $this->tokens[ $opening_paren ]['parenthesis_closer'];
continue;
}
- } elseif ( FormattingFunctionsHelper::is_formatting_function( $this->tokens[ $this->i ]['content'] ) ) {
+ } elseif ( FormattingFunctionsHelper::is_formatting_function( $content_lowercase ) ) {
continue;
}
}
diff --git a/WordPress/Sniffs/DateTime/CurrentTimeTimestampSniff.php b/WordPress/Sniffs/DateTime/CurrentTimeTimestampSniff.php
index 34bfe514f7..b08abe8bd8 100644
--- a/WordPress/Sniffs/DateTime/CurrentTimeTimestampSniff.php
+++ b/WordPress/Sniffs/DateTime/CurrentTimeTimestampSniff.php
@@ -161,7 +161,13 @@ public function process_parameters( $stackPtr, $group_name, $matched_content, $p
$this->phpcsFile->fixer->replaceToken( $i, '' );
}
- $this->phpcsFile->fixer->replaceToken( $stackPtr, 'time(' );
+ $replacement_content = 'time(';
+
+ if ( \T_NAME_FULLY_QUALIFIED === $this->tokens[ $stackPtr ]['code'] ) {
+ $replacement_content = '\time(';
+ }
+
+ $this->phpcsFile->fixer->replaceToken( $stackPtr, $replacement_content );
$this->phpcsFile->fixer->endChangeset();
}
}
diff --git a/WordPress/Sniffs/NamingConventions/PrefixAllGlobalsSniff.php b/WordPress/Sniffs/NamingConventions/PrefixAllGlobalsSniff.php
index 85c79f2c09..3869db7ba6 100644
--- a/WordPress/Sniffs/NamingConventions/PrefixAllGlobalsSniff.php
+++ b/WordPress/Sniffs/NamingConventions/PrefixAllGlobalsSniff.php
@@ -468,6 +468,7 @@ public function register() {
$parent = parent::register();
if ( ! empty( $parent ) ) {
$targets[] = \T_STRING;
+ $targets[] = \T_NAME_FULLY_QUALIFIED;
}
return $targets;
@@ -560,7 +561,9 @@ public function process_token( $stackPtr ) {
return $this->tokens[ $stackPtr ]['scope_closer'];
}
- if ( \T_STRING === $this->tokens[ $stackPtr ]['code'] ) {
+ if ( \T_STRING === $this->tokens[ $stackPtr ]['code']
+ || \T_NAME_FULLY_QUALIFIED === $this->tokens[ $stackPtr ]['code']
+ ) {
// Disallow excluding function groups for this sniff.
$this->exclude = array();
@@ -1233,6 +1236,9 @@ private function validate_prefixes() {
$prefixes = array();
$ns_prefixes = array();
foreach ( $this->prefixes as $key => $prefix ) {
+ // PHPCS >= 4.0 converts empty string values in array properties to null.
+ // Convert null back to empty string to preserve the "prefix too short" error.
+ $prefix = $prefix ?? '';
$prefixLC = strtolower( $prefix );
if ( isset( $this->prefix_blocklist[ $prefixLC ] ) ) {
diff --git a/WordPress/Sniffs/NamingConventions/ValidHookNameSniff.php b/WordPress/Sniffs/NamingConventions/ValidHookNameSniff.php
index 55dee3bd65..42936fca9e 100644
--- a/WordPress/Sniffs/NamingConventions/ValidHookNameSniff.php
+++ b/WordPress/Sniffs/NamingConventions/ValidHookNameSniff.php
@@ -10,6 +10,7 @@
namespace WordPressCS\WordPress\Sniffs\NamingConventions;
use PHP_CodeSniffer\Util\Tokens;
+use PHPCSUtils\Tokens\Collections;
use PHPCSUtils\Utils\TextStrings;
use WordPressCS\WordPress\AbstractFunctionParameterSniff;
use WordPressCS\WordPress\Helpers\WPHookHelper;
@@ -137,7 +138,7 @@ public function process_parameters( $stackPtr, $group_name, $matched_content, $p
// Skip over parameters passed to function calls.
if ( \T_OPEN_PARENTHESIS === $this->tokens[ $i ]['code']
- && ( \T_STRING === $this->tokens[ $last_non_empty ]['code']
+ && ( isset( Collections::nameTokens()[ $this->tokens[ $last_non_empty ]['code'] ] )
|| \T_VARIABLE === $this->tokens[ $last_non_empty ]['code'] )
&& isset( $this->tokens[ $i ]['parenthesis_closer'] )
) {
diff --git a/WordPress/Sniffs/PHP/NoSilencedErrorsSniff.php b/WordPress/Sniffs/PHP/NoSilencedErrorsSniff.php
index 06cc8879be..552bbbb9c1 100644
--- a/WordPress/Sniffs/PHP/NoSilencedErrorsSniff.php
+++ b/WordPress/Sniffs/PHP/NoSilencedErrorsSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Util\Tokens;
use PHPCSUtils\BackCompat\BCFile;
+use PHPCSUtils\Tokens\Collections;
use PHPCSUtils\Utils\GetTokensAsString;
use WordPressCS\WordPress\Helpers\RulesetPropertyHelper;
use WordPressCS\WordPress\Sniff;
@@ -184,6 +185,9 @@ public function register() {
public function process_token( $stackPtr ) {
// Handle the user-defined custom function list.
$this->customAllowedFunctionsList = RulesetPropertyHelper::merge_custom_array( $this->customAllowedFunctionsList, array(), false );
+ // PHPCS >= 4.0 converts empty string values in array properties to null.
+ // Filter out null values to avoid passing them to strtolower().
+ $this->customAllowedFunctionsList = array_filter( $this->customAllowedFunctionsList );
$this->customAllowedFunctionsList = array_map( 'strtolower', $this->customAllowedFunctionsList );
/*
@@ -193,10 +197,15 @@ public function process_token( $stackPtr ) {
* to allow the metrics to be more informative.
*/
$next_non_empty = $this->phpcsFile->findNext( $this->empty_tokens, ( $stackPtr + 1 ), null, true, null, true );
- if ( false !== $next_non_empty && \T_STRING === $this->tokens[ $next_non_empty ]['code'] ) {
+ if ( false !== $next_non_empty && isset( Collections::nameTokens()[ $this->tokens[ $next_non_empty ]['code'] ] ) === true ) {
$has_parenthesis = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $next_non_empty + 1 ), null, true, null, true );
if ( false !== $has_parenthesis && \T_OPEN_PARENTHESIS === $this->tokens[ $has_parenthesis ]['code'] ) {
$function_name = strtolower( $this->tokens[ $next_non_empty ]['content'] );
+
+ if ( \T_NAME_FULLY_QUALIFIED === $this->tokens[ $next_non_empty ]['code'] ) {
+ $function_name = \ltrim( $function_name, '\\' );
+ }
+
if ( ( true === $this->usePHPFunctionsList
&& isset( $this->allowedFunctionsList[ $function_name ] ) === true )
|| ( ! empty( $this->customAllowedFunctionsList )
diff --git a/WordPress/Sniffs/Security/EscapeOutputSniff.php b/WordPress/Sniffs/Security/EscapeOutputSniff.php
index b05732e823..2350b000e7 100644
--- a/WordPress/Sniffs/Security/EscapeOutputSniff.php
+++ b/WordPress/Sniffs/Security/EscapeOutputSniff.php
@@ -187,6 +187,7 @@ public function process_token( $stackPtr ) {
$start = ( $stackPtr + 1 );
switch ( $this->tokens[ $stackPtr ]['code'] ) {
+ case \T_NAME_FULLY_QUALIFIED:
case \T_STRING:
// Prevent exclusion of any of the function groups.
$this->exclude = array();
@@ -367,7 +368,8 @@ public function process_token( $stackPtr ) {
* @param int $stackPtr The position of the current token in the stack.
* @param string $group_name The name of the group which was matched.
* @param string $matched_content The token content (function name) which was matched
- * in lowercase.
+ * in lowercase. For T_NAME_FULLY_QUALIFIED tokens,
+ * the leading backslash is removed.
*
* @return int|void Integer stack pointer to skip forward or void to continue
* normal file processing.
@@ -574,9 +576,15 @@ protected function check_code_is_escaped( $start, $end, $code = 'OutputNotEscape
continue;
}
+ $content = $this->tokens[ $i ]['content'];
+
+ if ( \T_NAME_FULLY_QUALIFIED === $this->tokens[ $i ]['code'] ) {
+ $content = \ltrim( $content, '\\' );
+ }
+
// Ignore safe PHP native constants.
- if ( \T_STRING === $this->tokens[ $i ]['code']
- && isset( $this->safe_php_constants[ $this->tokens[ $i ]['content'] ] )
+ if ( ( \T_STRING === $this->tokens[ $i ]['code'] || \T_NAME_FULLY_QUALIFIED === $this->tokens[ $i ]['code'] )
+ && isset( $this->safe_php_constants[ $content ] )
&& ConstantsHelper::is_use_of_global_constant( $this->phpcsFile, $i )
) {
continue;
@@ -612,7 +620,7 @@ protected function check_code_is_escaped( $start, $end, $code = 'OutputNotEscape
}
// Check for use of *::class.
- if ( \T_STRING === $this->tokens[ $i ]['code']
+ if ( isset( Collections::nameTokens()[ $this->tokens[ $i ]['code'] ] )
|| \T_VARIABLE === $this->tokens[ $i ]['code']
|| isset( Collections::ooHierarchyKeywords()[ $this->tokens[ $i ]['code'] ] )
|| \T_NAMESPACE === $this->tokens[ $i ]['code']
@@ -678,9 +686,9 @@ protected function check_code_is_escaped( $start, $end, $code = 'OutputNotEscape
}
// Now check that the next token is a function call.
- if ( \T_STRING === $this->tokens[ $i ]['code'] ) {
+ if ( \T_STRING === $this->tokens[ $i ]['code'] || \T_NAME_FULLY_QUALIFIED === $this->tokens[ $i ]['code'] ) {
$ptr = $i;
- $functionName = $this->tokens[ $i ]['content'];
+ $functionName = $content;
$function_opener = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $i + 1 ), null, true );
$is_formatting_function = FormattingFunctionsHelper::is_formatting_function( $functionName );
diff --git a/WordPress/Sniffs/Security/NonceVerificationSniff.php b/WordPress/Sniffs/Security/NonceVerificationSniff.php
index 06b941fc79..2da979d31a 100644
--- a/WordPress/Sniffs/Security/NonceVerificationSniff.php
+++ b/WordPress/Sniffs/Security/NonceVerificationSniff.php
@@ -305,12 +305,16 @@ private function has_nonce_check( $stackPtr, array $cache_keys, $allow_nonce_aft
}
// If this isn't a function name, skip it.
- if ( \T_STRING !== $this->tokens[ $i ]['code'] ) {
+ if ( \T_STRING !== $this->tokens[ $i ]['code'] && \T_NAME_FULLY_QUALIFIED !== $this->tokens[ $i ]['code'] ) {
continue;
}
$content_lc = \strtolower( $this->tokens[ $i ]['content'] );
+ if ( \T_NAME_FULLY_QUALIFIED === $this->tokens[ $i ]['code'] ) {
+ $content_lc = \ltrim( $content_lc, '\\' );
+ }
+
// If this is one of the nonce verification functions, we can bail out.
if ( isset( $this->nonceVerificationFunctions[ $content_lc ] ) ) {
/*
diff --git a/WordPress/Sniffs/WP/AlternativeFunctionsSniff.php b/WordPress/Sniffs/WP/AlternativeFunctionsSniff.php
index 9a0a2a9d03..fda800cf56 100644
--- a/WordPress/Sniffs/WP/AlternativeFunctionsSniff.php
+++ b/WordPress/Sniffs/WP/AlternativeFunctionsSniff.php
@@ -213,7 +213,8 @@ public function getGroups() {
* @param int $stackPtr The position of the current token in the stack.
* @param string $group_name The name of the group which was matched.
* @param string $matched_content The token content (function name) which was matched
- * in lowercase.
+ * in lowercase. For T_NAME_FULLY_QUALIFIED tokens,
+ * the leading backslash is removed.
*
* @return int|void Integer stack pointer to skip forward or void to continue
* normal file processing.
diff --git a/WordPress/Sniffs/WP/CronIntervalSniff.php b/WordPress/Sniffs/WP/CronIntervalSniff.php
index 886eee073f..e0dd51cc5b 100644
--- a/WordPress/Sniffs/WP/CronIntervalSniff.php
+++ b/WordPress/Sniffs/WP/CronIntervalSniff.php
@@ -152,9 +152,20 @@ public function process_token( $stackPtr ) {
&& ( false !== $after && \T_CLOSE_PARENTHESIS === $this->tokens[ $after ]['code'] )
) {
// Ok, now see if we can find the function name.
- $beforeOpen = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $before - 1 ), null, true );
- if ( false !== $beforeOpen && \T_STRING === $this->tokens[ $beforeOpen ]['code'] ) {
- $found_function = $this->find_function_by_name( $this->tokens[ $beforeOpen ]['content'] );
+ $beforeOpen = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $before - 1 ), null, true );
+ $global_function_tokens = array(
+ \T_STRING => true,
+ \T_NAME_FULLY_QUALIFIED => true,
+ );
+
+ if ( false !== $beforeOpen && isset( $global_function_tokens[ $this->tokens[ $beforeOpen ]['code'] ] ) ) {
+ $function_name = $this->tokens[ $beforeOpen ]['content'];
+
+ if ( \T_NAME_FULLY_QUALIFIED === $this->tokens[ $beforeOpen ]['code'] ) {
+ $function_name = \ltrim( $function_name, '\\' );
+ }
+
+ $found_function = $this->find_function_by_name( $function_name );
if ( false !== $found_function ) {
$functionPtr = $found_function;
}
@@ -233,7 +244,13 @@ public function process_token( $stackPtr ) {
continue;
}
- $value .= $this->tokens[ $j ]['content'];
+ $content = $this->tokens[ $j ]['content'];
+
+ if ( \T_NAME_FULLY_QUALIFIED === $this->tokens[ $j ]['code'] ) {
+ $content = \ltrim( $content, '\\' );
+ }
+
+ $value .= $content;
}
if ( $parentheses_count > 0 ) {
diff --git a/WordPress/Sniffs/WP/DeprecatedFunctionsSniff.php b/WordPress/Sniffs/WP/DeprecatedFunctionsSniff.php
index 76647d9aff..d6fbefc411 100644
--- a/WordPress/Sniffs/WP/DeprecatedFunctionsSniff.php
+++ b/WordPress/Sniffs/WP/DeprecatedFunctionsSniff.php
@@ -1759,7 +1759,8 @@ public function getGroups() {
* @param string $group_name The name of the group which was matched. Will
* always be 'deprecated_functions'.
* @param string $matched_content The token content (function name) which was matched
- * in lowercase.
+ * in lowercase. For T_NAME_FULLY_QUALIFIED tokens,
+ * the leading backslash is removed.
*
* @return void
*/
diff --git a/WordPress/Sniffs/WP/DiscouragedConstantsSniff.php b/WordPress/Sniffs/WP/DiscouragedConstantsSniff.php
index bd3486205a..d0027614c6 100644
--- a/WordPress/Sniffs/WP/DiscouragedConstantsSniff.php
+++ b/WordPress/Sniffs/WP/DiscouragedConstantsSniff.php
@@ -72,7 +72,13 @@ final class DiscouragedConstantsSniff extends AbstractFunctionParameterSniff {
* normal file processing.
*/
public function process_token( $stackPtr ) {
- if ( isset( $this->target_functions[ strtolower( $this->tokens[ $stackPtr ]['content'] ) ] ) ) {
+ $content_lc = strtolower( $this->tokens[ $stackPtr ]['content'] );
+
+ if ( \T_NAME_FULLY_QUALIFIED === $this->tokens[ $stackPtr ]['code'] ) {
+ $content_lc = \ltrim( $content_lc, '\\' );
+ }
+
+ if ( isset( $this->target_functions[ $content_lc ] ) ) {
// Disallow excluding function groups for this sniff.
$this->exclude = array();
@@ -84,7 +90,8 @@ public function process_token( $stackPtr ) {
}
/**
- * Process an arbitrary T_STRING token to determine whether it is one of the target constants.
+ * Process an arbitrary T_STRING or T_NAME_FULLY_QUALIFIED token to determine whether it is one
+ * of the target constants.
*
* @since 0.14.0
*
@@ -95,6 +102,10 @@ public function process_token( $stackPtr ) {
public function process_arbitrary_tstring( $stackPtr ) {
$content = $this->tokens[ $stackPtr ]['content'];
+ if ( \T_NAME_FULLY_QUALIFIED === $this->tokens[ $stackPtr ]['code'] ) {
+ $content = \ltrim( $content, '\\' );
+ }
+
if ( ! isset( $this->discouraged_constants[ $content ] ) ) {
return;
}
diff --git a/WordPress/Sniffs/WP/GlobalVariablesOverrideSniff.php b/WordPress/Sniffs/WP/GlobalVariablesOverrideSniff.php
index 934c3e686b..985f5d281c 100644
--- a/WordPress/Sniffs/WP/GlobalVariablesOverrideSniff.php
+++ b/WordPress/Sniffs/WP/GlobalVariablesOverrideSniff.php
@@ -219,14 +219,15 @@ protected function process_variable_assignment( $stackPtr, $in_list = false ) {
$var_name = '';
$start = ( $bracketPtr + 1 );
for ( $ptr = $start; $ptr < $this->tokens[ $bracketPtr ]['bracket_closer']; $ptr++ ) {
+ $ignored_tokens = Collections::nameTokens();
+ $ignored_tokens[ \T_VARIABLE ] = \T_VARIABLE;
+ $ignored_tokens[ \T_DOUBLE_QUOTED_STRING ] = \T_DOUBLE_QUOTED_STRING;
+
/*
* If the globals array key contains a variable, constant, function call
* or interpolated variable, bow out.
*/
- if ( \T_VARIABLE === $this->tokens[ $ptr ]['code']
- || \T_STRING === $this->tokens[ $ptr ]['code']
- || \T_DOUBLE_QUOTED_STRING === $this->tokens[ $ptr ]['code']
- ) {
+ if ( isset( $ignored_tokens[ $this->tokens[ $ptr ]['code'] ] ) === true ) {
return;
}
diff --git a/WordPress/Tests/Arrays/ArrayDeclarationSpacingUnitTest.php b/WordPress/Tests/Arrays/ArrayDeclarationSpacingUnitTest.php
index 6a46292970..5b25eca51c 100644
--- a/WordPress/Tests/Arrays/ArrayDeclarationSpacingUnitTest.php
+++ b/WordPress/Tests/Arrays/ArrayDeclarationSpacingUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\Arrays;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the ArrayDeclarationSpacing sniff.
@@ -19,7 +19,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\Arrays\ArrayDeclarationSpacingSniff
*/
-final class ArrayDeclarationSpacingUnitTest extends AbstractSniffUnitTest {
+final class ArrayDeclarationSpacingUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/Arrays/ArrayIndentationUnitTest.php b/WordPress/Tests/Arrays/ArrayIndentationUnitTest.php
index 2e392f7296..650213a99b 100644
--- a/WordPress/Tests/Arrays/ArrayIndentationUnitTest.php
+++ b/WordPress/Tests/Arrays/ArrayIndentationUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\Arrays;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the ArrayIndentation sniff.
@@ -19,7 +19,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\Arrays\ArrayIndentationSniff
*/
-final class ArrayIndentationUnitTest extends AbstractSniffUnitTest {
+final class ArrayIndentationUnitTest extends AbstractSniffTestCase {
/**
* The tab width to use during testing.
diff --git a/WordPress/Tests/Arrays/ArrayKeySpacingRestrictionsUnitTest.php b/WordPress/Tests/Arrays/ArrayKeySpacingRestrictionsUnitTest.php
index aa2dc27890..96c8b867a9 100644
--- a/WordPress/Tests/Arrays/ArrayKeySpacingRestrictionsUnitTest.php
+++ b/WordPress/Tests/Arrays/ArrayKeySpacingRestrictionsUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\Arrays;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the ArrayKeySpacingRestrictions sniff.
@@ -19,7 +19,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\Arrays\ArrayKeySpacingRestrictionsSniff
*/
-final class ArrayKeySpacingRestrictionsUnitTest extends AbstractSniffUnitTest {
+final class ArrayKeySpacingRestrictionsUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/Arrays/MultipleStatementAlignmentUnitTest.php b/WordPress/Tests/Arrays/MultipleStatementAlignmentUnitTest.php
index 11a839fc13..6daa1b2a1c 100644
--- a/WordPress/Tests/Arrays/MultipleStatementAlignmentUnitTest.php
+++ b/WordPress/Tests/Arrays/MultipleStatementAlignmentUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\Arrays;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the Arrays.MultipleStatementAlignment sniff.
@@ -22,7 +22,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\Arrays\MultipleStatementAlignmentSniff
*/
-final class MultipleStatementAlignmentUnitTest extends AbstractSniffUnitTest {
+final class MultipleStatementAlignmentUnitTest extends AbstractSniffTestCase {
/**
* The tab width to use during testing.
diff --git a/WordPress/Tests/CodeAnalysis/AssignmentInTernaryConditionUnitTest.php b/WordPress/Tests/CodeAnalysis/AssignmentInTernaryConditionUnitTest.php
index 642a8a70fc..4fc10bafe6 100644
--- a/WordPress/Tests/CodeAnalysis/AssignmentInTernaryConditionUnitTest.php
+++ b/WordPress/Tests/CodeAnalysis/AssignmentInTernaryConditionUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\CodeAnalysis;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the AssignmentInTernaryCondition sniff.
@@ -18,7 +18,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\CodeAnalysis\AssignmentInTernaryConditionSniff
*/
-final class AssignmentInTernaryConditionUnitTest extends AbstractSniffUnitTest {
+final class AssignmentInTernaryConditionUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/CodeAnalysis/EscapedNotTranslatedUnitTest.php b/WordPress/Tests/CodeAnalysis/EscapedNotTranslatedUnitTest.php
index eee562f2d6..781a8ede89 100644
--- a/WordPress/Tests/CodeAnalysis/EscapedNotTranslatedUnitTest.php
+++ b/WordPress/Tests/CodeAnalysis/EscapedNotTranslatedUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\CodeAnalysis;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the EscapedNotTranslated sniff.
@@ -18,7 +18,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\CodeAnalysis\EscapedNotTranslatedSniff
*/
-final class EscapedNotTranslatedUnitTest extends AbstractSniffUnitTest {
+final class EscapedNotTranslatedUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/DB/DirectDatabaseQueryUnitTest.php b/WordPress/Tests/DB/DirectDatabaseQueryUnitTest.php
index 5a4fda7f4a..f3f6520cbd 100644
--- a/WordPress/Tests/DB/DirectDatabaseQueryUnitTest.php
+++ b/WordPress/Tests/DB/DirectDatabaseQueryUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\DB;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the DirectDatabaseQuery sniff.
@@ -21,7 +21,7 @@
* @covers \WordPressCS\WordPress\Helpers\RulesetPropertyHelper
* @covers \WordPressCS\WordPress\Sniffs\DB\DirectDatabaseQuerySniff
*/
-final class DirectDatabaseQueryUnitTest extends AbstractSniffUnitTest {
+final class DirectDatabaseQueryUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.inc b/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.inc
index 791df5a509..73817620ea 100644
--- a/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.inc
+++ b/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.inc
@@ -14,7 +14,7 @@ $sql = $wpdb->prepare( 'SELECT * FROM `table` WHERE id = ' . $id ); // OK - this
$sql = $wpdb->prepare( "SELECT * FROM `table` WHERE id = $id" ); // OK - this will be handled by the PreparedSQL sniff.
$sql = $wpdb->prepare( "SELECT * FROM `table` WHERE id = {$id['some%sing']}" ); // OK - this will be handled by the PreparedSQL sniff.
$sql = $wpdb?->prepare( 'SELECT * FROM ' . $wpdb->users ); // Warning.
-$sql = $wpdb->prepare( "SELECT * FROM `{$wpdb->users}`" ); // Warning.
+$sql = $wpdb->PREPARE( "SELECT * FROM `{$wpdb->users}`" ); // Warning.
$sql = $wpdb->prepare( "SELECT * FROM `{$wpdb->users}` WHERE id = $id" ); // OK - this will be handled by the PreparedSQL sniff.
/*
@@ -80,7 +80,7 @@ $where = $wpdb->prepare(
); // OK.
$where = $wpdb->prepare(
- sprintf(
+ \sprintf(
"{$wpdb->posts}.post_type IN (%s)
AND {$wpdb->posts}.post_status IN (%s)",
implode( ',', array_fill( 0, count($post_types), '%s' ), ),
@@ -99,7 +99,7 @@ $where = $wpdb->prepare(
); // OK.
$query = $wpdb->prepare(
- sprintf(
+ Sprintf(
'SELECT COUNT(ID)
FROM `%s`
WHERE ID IN (%s)
@@ -124,7 +124,7 @@ $results = $wpdb->get_results(
); // OK.
$query = $wpdb->prepare(
- sprintf(
+ \SPRINTF(
'SELECT COUNT(ID)
FROM `%s`
WHERE ID in (%s)
@@ -382,7 +382,7 @@ $where = $wpdb->prepare(
$where = $wpdb->prepare(
sprintf(
"{$wpdb->posts}.post_type IN (%s)",
- \implode( ',', array_fill( 0, count($post_types), '%s' ) )
+ \ImplodE( ',', array_fill( 0, count($post_types), '%s' ) )
),
$post_types
); // OK.
@@ -397,7 +397,7 @@ $where = $wpdb->prepare(
$where = $wpdb->prepare(
"{$wpdb->posts}.post_type IN ("
- . implode( ',', \array_fill( 0, count($post_types), '%s' ) )
+ . implode( ',', \Array_Fill( 0, count($post_types), '%s' ) )
. ") AND {$wpdb->posts}.post_status IN ("
. implode( ',', \array_fill( 0, count($post_statusses), '%s' ) )
. ')',
@@ -532,3 +532,280 @@ $sql = MyNamespace\wpdb::prepare( "SELECT * FROM $wpdb->users WHERE id = %d AND
$sql = \MyNamespace\WPDB::prepare( "SELECT * FROM $wpdb->users WHERE id = %d AND user_login = %s" ); // OK.
$sql = namespace\wpdb::prepare( "SELECT * FROM $wpdb->users WHERE id = %d AND user_login = %s" ); // Ok. The sniff should start flagging this once it can resolve relative namespaces as this test file is not namespaced.
$sql = namespace\Sub\wpdb::prepare( "SELECT * FROM $wpdb->users WHERE id = %d AND user_login = %s" ); // OK.
+
+/*
+ * Safeguard correct handling of namespaced implode() calls when nested as a parameter to sprintf() (except fully
+ * qualified calls to the global implode() function, which is already handled above).
+ */
+$where = $wpdb->prepare(
+ sprintf(
+ "{$wpdb->posts}.post_type IN (%s)",
+ MyNamespace\implode( ',', array_fill( 0, count($post_types), '%s' ) )
+ ),
+ $post_types
+);
+$where = $wpdb->prepare(
+ sprintf(
+ "{$wpdb->posts}.post_type IN (%s)",
+ \MyNamespace\implode( ',', array_fill( 0, count($post_types), '%s' ) )
+ ),
+ $post_types
+);
+$where = $wpdb->prepare( // This should NOT be flagged in the future once the sniff is able to resolve relative namespaces as this test file is not namespaced.
+ sprintf(
+ "{$wpdb->posts}.post_type IN (%s)",
+ namespace\implode( ',', array_fill( 0, count($post_types), '%s' ) )
+ ),
+ $post_types
+);
+$where = $wpdb->prepare(
+ sprintf(
+ "{$wpdb->posts}.post_type IN (%s)",
+ namespace\Sub\implode( ',', array_fill( 0, count($post_types), '%s' ) )
+ ),
+ $post_types
+);
+
+/*
+ * Safeguard correct handling of namespaced array_fill() calls when nested inside sprintf() (except fully qualified
+ * calls to the global array_fill() function, which is already handled above).
+ */
+$where = $wpdb->prepare(
+ sprintf(
+ "{$wpdb->posts}.post_type IN (%s)",
+ implode( ',', MyNamespace\array_fill( 0, count($post_types), '%s' ) )
+ ),
+ $post_types
+);
+$where = $wpdb->prepare(
+ sprintf(
+ "{$wpdb->posts}.post_type IN (%s)",
+ implode( ',', \MyNamespace\array_fill( 0, count($post_types), '%s' ) )
+ ),
+ $post_types
+);
+$where = $wpdb->prepare( // This should NOT be flagged in the future once the sniff is able to resolve relative namespaces as this test file is not namespaced.
+ sprintf(
+ "{$wpdb->posts}.post_type IN (%s)",
+ implode( ',', namespace\array_fill( 0, count($post_types), '%s' ) )
+ ),
+ $post_types
+);
+$where = $wpdb->prepare(
+ sprintf(
+ "{$wpdb->posts}.post_type IN (%s)",
+ implode( ',', namespace\Sub\array_fill( 0, count($post_types), '%s' ) )
+ ),
+ $post_types
+);
+
+/*
+ * Safeguard correct handling of namespaced implode() calls in direct string concatenation (when NOT nested inside
+ * sprintf()). Fully qualified calls to the global implode() are already handled above.
+ */
+$where = $wpdb->prepare(
+ "SELECT * FROM {$wpdb->posts} WHERE post_status = %s AND post_type IN ("
+ . MyNamespace\implode( ',', array_fill( 0, count($post_types), '%s' ) )
+ . ')',
+ 'publish'
+);
+$where = $wpdb->prepare(
+ "SELECT * FROM {$wpdb->posts} WHERE post_status = %s AND post_type IN ("
+ . \MyNamespace\implode( ',', array_fill( 0, count($post_types), '%s' ) )
+ . ')',
+ 'publish'
+);
+$where = $wpdb->prepare( // This should NOT be flagged in the future once the sniff is able to resolve relative namespaces as this test file is not namespaced.
+ "SELECT * FROM {$wpdb->posts} WHERE post_status = %s AND post_type IN ("
+ . namespace\implode( ',', array_fill( 0, count($post_types), '%s' ) )
+ . ')',
+ 'publish'
+);
+$where = $wpdb->prepare(
+ "SELECT * FROM {$wpdb->posts} WHERE post_status = %s AND post_type IN ("
+ . namespace\Sub\implode( ',', array_fill( 0, count($post_types), '%s' ) )
+ . ')',
+ 'publish'
+);
+
+/*
+ * Safeguard correct handling of namespaced array_fill() calls in direct string concatenation (when NOT nested inside
+ * sprintf()). Fully qualified calls to the global array_fill() are already handled above.
+ */
+$where = $wpdb->prepare(
+ "SELECT * FROM {$wpdb->posts} WHERE post_status = %s AND post_type IN ("
+ . implode( ',', MyNamespace\array_fill( 0, count($post_types), '%s' ) )
+ . ')',
+ 'publish'
+);
+$where = $wpdb->prepare(
+ "SELECT * FROM {$wpdb->posts} WHERE post_status = %s AND post_type IN ("
+ . implode( ',', \MyNamespace\array_fill( 0, count($post_types), '%s' ) )
+ . ')',
+ 'publish'
+);
+$where = $wpdb->prepare( // This should NOT be flagged in the future once the sniff is able to resolve relative namespaces as this test file is not namespaced.
+ "SELECT * FROM {$wpdb->posts} WHERE post_status = %s AND post_type IN ("
+ . implode( ',', namespace\array_fill( 0, count($post_types), '%s' ) )
+ . ')',
+ 'publish'
+);
+$where = $wpdb->prepare(
+ "SELECT * FROM {$wpdb->posts} WHERE post_status = %s AND post_type IN ("
+ . implode( ',', namespace\Sub\array_fill( 0, count($post_types), '%s' ) )
+ . ')',
+ 'publish'
+);
+
+/*
+ * Safeguard correct handling of all types of namespaced calls to sprintf() except fully qualified calls to the
+ * global sprintf() function, which is already handled above.
+ *
+ * On PHPCS 3.x these are false negatives: the sniff mistakes the namespaced sprintf() calls for the global
+ * sprintf() function and does not flag them. On PHPCS 4.x, the changed name tokenization means they are flagged.
+ * See: https://github.com/WordPress/WordPress-Coding-Standards/issues/2720
+ */
+$where = $wpdb->prepare(
+ MyNamespace\sprintf(
+ "{$wpdb->posts}.post_type IN (%s)",
+ implode( ',', array_fill( 0, count($post_types), '%s' ) )
+ ),
+ $post_types
+);
+$where = $wpdb->prepare(
+ \MyNamespace\sprintf(
+ "{$wpdb->posts}.post_type IN (%s)",
+ implode( ',', array_fill( 0, count($post_types), '%s' ) )
+ ),
+ $post_types
+);
+$where = $wpdb->prepare(
+ namespace\sprintf( // This should NOT be flagged in the future once the sniff is able to resolve relative namespaces as this test file is not namespaced.
+ "{$wpdb->posts}.post_type IN (%s)",
+ implode( ',', array_fill( 0, count($post_types), '%s' ) )
+ ),
+ $post_types
+);
+$where = $wpdb->prepare(
+ namespace\Sub\sprintf(
+ "{$wpdb->posts}.post_type IN (%s)",
+ implode( ',', array_fill( 0, count($post_types), '%s' ) )
+ ),
+ $post_types
+);
+
+/*
+ * Safeguard correct handling of method calls to methods named sprintf(), implode(), or array_fill().
+ * These should NOT be analyzed as global function calls.
+ */
+
+// sprintf() method calls. The sprintf() calls below are currently false negatives. The sniff incorrectly treats method
+// calls with the same name as global functions as calls to the global functions and does not flag them.
+// See: https://github.com/WordPress/WordPress-Coding-Standards/issues/2720
+$where = $wpdb->prepare(
+ $obj->sprintf(
+ "{$wpdb->posts}.post_type IN (%s)",
+ implode( ',', array_fill( 0, count($post_types), '%s' ) )
+ ),
+ $post_types
+);
+$where = $wpdb->prepare(
+ $obj?->sprintf(
+ "{$wpdb->posts}.post_type IN (%s)",
+ implode( ',', array_fill( 0, count($post_types), '%s' ) )
+ ),
+ $post_types
+);
+$where = $wpdb->prepare(
+ MyClass::sprintf(
+ "{$wpdb->posts}.post_type IN (%s)",
+ implode( ',', array_fill( 0, count($post_types), '%s' ) )
+ ),
+ $post_types
+);
+
+// implode() method calls nested inside sprintf().
+$where = $wpdb->prepare(
+ sprintf(
+ "{$wpdb->posts}.post_type IN (%s)",
+ $obj->implode( ',', array_fill( 0, count($post_types), '%s' ) )
+ ),
+ $post_types
+);
+$where = $wpdb->prepare(
+ sprintf(
+ "{$wpdb->posts}.post_type IN (%s)",
+ $obj?->implode( ',', array_fill( 0, count($post_types), '%s' ) )
+ ),
+ $post_types
+);
+$where = $wpdb->prepare(
+ sprintf(
+ "{$wpdb->posts}.post_type IN (%s)",
+ MyClass::implode( ',', array_fill( 0, count($post_types), '%s' ) )
+ ),
+ $post_types
+);
+
+// implode() method calls in direct string concatenation.
+$where = $wpdb->prepare(
+ "post_status = %s AND post_type IN ("
+ . $obj->implode( ',', array_fill( 0, count($post_types), '%s' ) )
+ . ')',
+ 'publish'
+);
+$where = $wpdb->prepare(
+ "post_status = %s AND post_type IN ("
+ . $obj?->implode( ',', array_fill( 0, count($post_types), '%s' ) )
+ . ')',
+ 'publish'
+);
+$where = $wpdb->prepare(
+ "post_status = %s AND post_type IN ("
+ . MyClass::implode( ',', array_fill( 0, count($post_types), '%s' ) )
+ . ')',
+ 'publish'
+);
+
+// array_fill() method calls nested inside implode() which is nested inside sprintf().
+$where = $wpdb->prepare(
+ sprintf(
+ "{$wpdb->posts}.post_type IN (%s)",
+ implode( ',', $obj->array_fill( 0, count($post_types), '%s' ) )
+ ),
+ $post_types
+);
+$where = $wpdb->prepare(
+ sprintf(
+ "{$wpdb->posts}.post_type IN (%s)",
+ implode( ',', $obj?->array_fill( 0, count($post_types), '%s' ) )
+ ),
+ $post_types
+);
+$where = $wpdb->prepare(
+ sprintf(
+ "{$wpdb->posts}.post_type IN (%s)",
+ implode( ',', MyClass::array_fill( 0, count($post_types), '%s' ) )
+ ),
+ $post_types
+);
+
+// array_fill() method calls nested inside implode() in direct string concatenation.
+$where = $wpdb->prepare(
+ "post_status = %s AND post_type IN ("
+ . implode( ',', $obj->array_fill( 0, count($post_types), '%s' ) )
+ . ')',
+ 'publish'
+);
+$where = $wpdb->prepare(
+ "post_status = %s AND post_type IN ("
+ . implode( ',', $obj?->array_fill( 0, count($post_types), '%s' ) )
+ . ')',
+ 'publish'
+);
+$where = $wpdb->prepare(
+ "post_status = %s AND post_type IN ("
+ . implode( ',', MyClass::array_fill( 0, count($post_types), '%s' ) )
+ . ')',
+ 'publish'
+);
diff --git a/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.php b/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.php
index 916567cf3e..bb09f4cf60 100644
--- a/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.php
+++ b/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.php
@@ -9,7 +9,8 @@
namespace WordPressCS\WordPress\Tests\DB;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
+use PHPCSUtils\BackCompat\Helper;
/**
* Unit test class for the PreparedSQLPlaceholders sniff.
@@ -18,7 +19,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\DB\PreparedSQLPlaceholdersSniff
*/
-final class PreparedSQLPlaceholdersUnitTest extends AbstractSniffUnitTest {
+final class PreparedSQLPlaceholdersUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
@@ -119,6 +120,9 @@ public function getErrorList() {
* @return array Key is the line number, value is the number of expected warnings.
*/
public function getWarningList() {
+ $phpcs_version = Helper::getVersion();
+ $is_phpcs_4 = version_compare( $phpcs_version, '3.99.99', '>' );
+
return array(
12 => 1,
16 => 1,
@@ -165,6 +169,49 @@ public function getWarningList() {
482 => 1,
490 => 1,
498 => 1,
+
+ // Namespaced sprintf/implode/array_fill calls.
+ 540 => 1,
+ 547 => 1,
+ 554 => 1,
+ 561 => 1,
+ 573 => 1,
+ 580 => 1,
+ 587 => 1,
+ 594 => 1,
+ 606 => 1,
+ 612 => 1,
+ 618 => 1,
+ 624 => 1,
+ 635 => 1,
+ 641 => 1,
+ 647 => 1,
+ 653 => 1,
+
+ /*
+ * Namespaced sprintf() calls.
+ *
+ * False negatives on PHPCS 3.x. Flagged on PHPCS 4.x due to the changed tokenization of
+ * namespaced names. See https://github.com/WordPress/WordPress-Coding-Standards/issues/2720.
+ */
+ 668 => ( true === $is_phpcs_4 ? 1 : 0 ),
+ 675 => ( true === $is_phpcs_4 ? 1 : 0 ),
+ 682 => ( true === $is_phpcs_4 ? 1 : 0 ),
+ 689 => ( true === $is_phpcs_4 ? 1 : 0 ),
+
+ // Method sprintf/implode/array_fill calls.
+ 728 => 1,
+ 735 => 1,
+ 742 => 1,
+ 751 => 1,
+ 757 => 1,
+ 763 => 1,
+ 771 => 1,
+ 778 => 1,
+ 785 => 1,
+ 794 => 1,
+ 800 => 1,
+ 806 => 1,
);
}
}
diff --git a/WordPress/Tests/DB/PreparedSQLUnitTest.1.inc b/WordPress/Tests/DB/PreparedSQLUnitTest.1.inc
index ac97d71ce8..6f5ca0f8c9 100644
--- a/WordPress/Tests/DB/PreparedSQLUnitTest.1.inc
+++ b/WordPress/Tests/DB/PreparedSQLUnitTest.1.inc
@@ -33,7 +33,7 @@ $wpdb->query( "SELECT * FROM $wpdb->posts WHERE post_title LIKE '" . esc_sql( $f
$wpdb->query( "SELECT * FROM $wpdb->posts WHERE ID = " . ABSINT( $foo ) . ";" ); // Ok.
// Test multi-line strings.
-$all_post_meta = $wpdb->get_results( $wpdb->prepare( sprintf(
+$all_post_meta = $wpdb->get_results( $wpdb->prepare( \SPRINTF(
'SELECT `post_id`, `meta_value`
FROM `%s`
WHERE `meta_key` = "sort_order"
@@ -45,7 +45,7 @@ $all_post_meta = $wpdb->get_results( $wpdb->prepare( sprintf(
$wpdb->query( "
SELECT *
FROM $wpdb->posts
- WHERE post_title LIKE '" . esc_sql( $foo ) . "';"
+ WHERE post_title LIKE '" . \esc_SQL( $foo ) . "';"
); // Ok.
$wpdb->query( $wpdb->prepare( "
@@ -157,3 +157,45 @@ MyNamespace\wpdb::prepare( "SELECT * FROM $wpdb->posts WHERE post_title LIKE '"
\MyNamespace\WPDB::prepare( "SELECT * FROM $wpdb->posts WHERE post_title LIKE '" . foo() . "';" ); // Ok.
namespace\wpdb::prepare( "SELECT * FROM $wpdb->posts WHERE post_title LIKE '" . foo() . "';" ); // Ok. The sniff should start flagging this once it can resolve relative namespaces as this test file is not namespaced.
namespace\Sub\wpdb::prepare( "SELECT * FROM $wpdb->posts WHERE post_title LIKE '" . foo() . "';" ); // Ok.
+
+/*
+ * Safeguard correct handling of all types of namespaced calls to PreparedSQLSniff::$SQLEscapingFunctions.
+ *
+ * Note: The sniff currently has a limitation in how it identifies and counts errors for namespaced function calls that
+ * match function names in $SQLEscapingFunctions, $SQLAutoEscapedFunctions, or
+ * FormattingFunctionsHelper::$formattingFunctions. When it encounters such a call, it 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, 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 its contents.
+ * Additionally, multi-level namespace calls like `namespace\Sub\count( $foo )` generate multiple errors (one for
+ * "namespace", one for "Sub") instead of recognizing it as a single namespaced function call. This will be easier to
+ * fix once only PHPCS 4 is supported. Reported in https://github.com/WordPress/WordPress-Coding-Standards/issues/2648.
+ */
+$wpdb->query( "SELECT * FROM $wpdb->posts WHERE ID = " . \absint( $foo ) );
+$wpdb->query( "SELECT * FROM $wpdb->posts WHERE ID = " . MyNamespace\esc_sql( $foo ) );
+$wpdb->query( "SELECT * FROM $wpdb->posts WHERE ID = " . \MyNamespace\intval( $foo, $bar ) );
+$wpdb->query( "SELECT * FROM $wpdb->posts WHERE ID = " . namespace\floatval( $foo ) ); // This should NOT be flagged in the future once the sniff is able to resolve relative namespaces.
+$wpdb->query( "SELECT * FROM $wpdb->posts WHERE ID = " . namespace\Sub\like_escape( $foo ) );
+
+/*
+ * Safeguard correct handling of all types of namespaced calls to PreparedSQLSniff::$SQLAutoEscapedFunctions.
+ *
+ * Note: See the comment above the $SQLEscapingFunctions tests for details about the sniff's current limitations.
+ */
+$wpdb->query( "SELECT * FROM $wpdb->posts WHERE ID = " . \count( $foo ) );
+$wpdb->query( "SELECT * FROM $wpdb->posts WHERE ID = " . \Count( $foo ) );
+$wpdb->query( "SELECT * FROM $wpdb->posts WHERE ID = " . MyNamespace\count( $foo ) );
+$wpdb->query( "SELECT * FROM $wpdb->posts WHERE ID = " . \MyNamespace\count( $foo ) );
+$wpdb->query( "SELECT * FROM $wpdb->posts WHERE ID = " . namespace\count( $foo ) ); // This should NOT be flagged in the future once the sniff is able to resolve relative namespaces.
+$wpdb->query( "SELECT * FROM $wpdb->posts WHERE ID = " . namespace\Sub\count( $foo ) );
+
+/*
+ * Safeguard correct handling of all types of namespaced calls to FormattingFunctionsHelper::$formattingFunctions.
+ *
+ * Note: See the comment above the $SQLEscapingFunctions tests for details about the sniff's current limitations.
+ */
+$wpdb->get_results( \sprintf( "SELECT * FROM $wpdb->posts WHERE ID = %s", intval( $id ) ) );
+$wpdb->get_results( MyNamespace\wp_sprintf( "SELECT * FROM $wpdb->posts WHERE ID = %s", intval( $id ) ) );
+$wpdb->get_results( \MyNamespace\sprintf( "SELECT * FROM $wpdb->posts WHERE ID = %s", intval( $id ) ) );
+$wpdb->get_results( namespace\wp_sprintf( "SELECT * FROM $wpdb->posts WHERE ID = %s", intval( $id ) ) ); // This should NOT be flagged in the future once the sniff is able to resolve relative namespaces.
+$wpdb->get_results( namespace\Sub\sprintf( "SELECT * FROM $wpdb->posts WHERE ID = %s", intval( $id ) ) );
diff --git a/WordPress/Tests/DB/PreparedSQLUnitTest.php b/WordPress/Tests/DB/PreparedSQLUnitTest.php
index 8d887dd044..76e20851e0 100644
--- a/WordPress/Tests/DB/PreparedSQLUnitTest.php
+++ b/WordPress/Tests/DB/PreparedSQLUnitTest.php
@@ -9,7 +9,8 @@
namespace WordPressCS\WordPress\Tests\DB;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
+use PHPCSUtils\BackCompat\Helper;
/**
* Unit test class for the PreparedSQL sniff.
@@ -20,10 +21,9 @@
*
* @covers \WordPressCS\WordPress\Helpers\ContextHelper::is_safe_casted
* @covers \WordPressCS\WordPress\Helpers\FormattingFunctionsHelper
- * @covers \WordPressCS\WordPress\Helpers\WPDBTrait
* @covers \WordPressCS\WordPress\Sniffs\DB\PreparedSQLSniff
*/
-final class PreparedSQLUnitTest extends AbstractSniffUnitTest {
+final class PreparedSQLUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
@@ -33,6 +33,9 @@ final class PreparedSQLUnitTest extends AbstractSniffUnitTest {
* @return array Key is the line number, value is the number of expected errors.
*/
public function getErrorList( $testFile = '' ) {
+ $phpcs_version = Helper::getVersion();
+ $is_phpcs_4 = version_compare( $phpcs_version, '3.99.99', '>' );
+
switch ( $testFile ) {
case 'PreparedSQLUnitTest.1.inc':
return array(
@@ -68,6 +71,18 @@ public function getErrorList( $testFile = '' ) {
132 => 2,
154 => 1,
155 => 1,
+ 175 => ( true === $is_phpcs_4 ? 2 : 1 ),
+ 176 => ( true === $is_phpcs_4 ? 3 : 1 ),
+ 177 => ( true === $is_phpcs_4 ? 2 : 1 ),
+ 178 => 2,
+ 187 => ( true === $is_phpcs_4 ? 2 : 1 ),
+ 188 => ( true === $is_phpcs_4 ? 2 : 1 ),
+ 189 => ( true === $is_phpcs_4 ? 2 : 1 ),
+ 190 => 2,
+ 198 => 1,
+ 199 => 1,
+ 200 => 1,
+ 201 => ( true === $is_phpcs_4 ? 1 : 2 ),
);
case 'PreparedSQLUnitTest.2.inc':
diff --git a/WordPress/Tests/DB/RestrictedClassesUnitTest.php b/WordPress/Tests/DB/RestrictedClassesUnitTest.php
index a9bb96546e..2844696d8d 100644
--- a/WordPress/Tests/DB/RestrictedClassesUnitTest.php
+++ b/WordPress/Tests/DB/RestrictedClassesUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\DB;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
use WordPressCS\WordPress\AbstractFunctionRestrictionsSniff;
/**
@@ -23,7 +23,7 @@
* @covers \WordPressCS\WordPress\Helpers\RulesetPropertyHelper
* @covers \WordPressCS\WordPress\Sniffs\DB\RestrictedClassesSniff
*/
-final class RestrictedClassesUnitTest extends AbstractSniffUnitTest {
+final class RestrictedClassesUnitTest extends AbstractSniffTestCase {
/**
* Add a number of extra restricted classes to unit test the abstract
diff --git a/WordPress/Tests/DB/RestrictedFunctionsUnitTest.php b/WordPress/Tests/DB/RestrictedFunctionsUnitTest.php
index 7b730c5f59..73ab9a2a5d 100644
--- a/WordPress/Tests/DB/RestrictedFunctionsUnitTest.php
+++ b/WordPress/Tests/DB/RestrictedFunctionsUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\DB;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
use WordPressCS\WordPress\AbstractFunctionRestrictionsSniff;
/**
@@ -21,7 +21,7 @@
* @covers \WordPressCS\WordPress\AbstractFunctionRestrictionsSniff
* @covers \WordPressCS\WordPress\Sniffs\DB\RestrictedFunctionsSniff
*/
-final class RestrictedFunctionsUnitTest extends AbstractSniffUnitTest {
+final class RestrictedFunctionsUnitTest extends AbstractSniffTestCase {
/**
* Add a number of extra restricted functions to unit test the abstract
diff --git a/WordPress/Tests/DB/SlowDBQueryUnitTest.php b/WordPress/Tests/DB/SlowDBQueryUnitTest.php
index 46ba3204d1..4bd5d67a33 100644
--- a/WordPress/Tests/DB/SlowDBQueryUnitTest.php
+++ b/WordPress/Tests/DB/SlowDBQueryUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\DB;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the SlowDBQuery sniff.
@@ -21,7 +21,7 @@
* @covers \WordPressCS\WordPress\AbstractArrayAssignmentRestrictionsSniff
* @covers \WordPressCS\WordPress\Sniffs\DB\SlowDBQuerySniff
*/
-final class SlowDBQueryUnitTest extends AbstractSniffUnitTest {
+final class SlowDBQueryUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/DateTime/CurrentTimeTimestampUnitTest.php b/WordPress/Tests/DateTime/CurrentTimeTimestampUnitTest.php
index 4525e58d79..ad7b25e22c 100644
--- a/WordPress/Tests/DateTime/CurrentTimeTimestampUnitTest.php
+++ b/WordPress/Tests/DateTime/CurrentTimeTimestampUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\DateTime;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the CurrentTimeTimestamp sniff.
@@ -18,7 +18,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\DateTime\CurrentTimeTimestampSniff
*/
-final class CurrentTimeTimestampUnitTest extends AbstractSniffUnitTest {
+final class CurrentTimeTimestampUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/DateTime/RestrictedFunctionsUnitTest.php b/WordPress/Tests/DateTime/RestrictedFunctionsUnitTest.php
index 8a37da1c3a..3610a48452 100644
--- a/WordPress/Tests/DateTime/RestrictedFunctionsUnitTest.php
+++ b/WordPress/Tests/DateTime/RestrictedFunctionsUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\DateTime;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the DateTime.RestrictedFunctions sniff.
@@ -18,7 +18,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\DateTime\RestrictedFunctionsSniff
*/
-final class RestrictedFunctionsUnitTest extends AbstractSniffUnitTest {
+final class RestrictedFunctionsUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/Files/FileNameStdInTest.xml b/WordPress/Tests/Files/FileNameStdInTest.xml
new file mode 100644
index 0000000000..acb124382b
--- /dev/null
+++ b/WordPress/Tests/Files/FileNameStdInTest.xml
@@ -0,0 +1,7 @@
+
+
+ Ruleset for testing FileName with STDIN.
+
+
+
+
diff --git a/WordPress/Tests/Files/FileNameUnitTest.php b/WordPress/Tests/Files/FileNameUnitTest.php
index c56d3661f2..7444544fb2 100644
--- a/WordPress/Tests/Files/FileNameUnitTest.php
+++ b/WordPress/Tests/Files/FileNameUnitTest.php
@@ -11,7 +11,7 @@
use PHP_CodeSniffer\Files\DummyFile;
use PHP_CodeSniffer\Ruleset;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
use PHPCSUtils\BackCompat\Helper;
use PHPCSUtils\TestUtils\ConfigDouble;
@@ -24,7 +24,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\Files\FileNameSniff
*/
-final class FileNameUnitTest extends AbstractSniffUnitTest {
+final class FileNameUnitTest extends AbstractSniffTestCase {
/**
* Error files with the expected nr of errors.
@@ -185,8 +185,7 @@ public function getWarningList() {
public function testStdIn() {
$config = new ConfigDouble();
Helper::setConfigData( 'installed_paths', dirname( dirname( __DIR__ ) ), true, $config );
- $config->standards = array( 'WordPress' );
- $config->sniffs = array( 'WordPress.Files.FileName' );
+ $config->standards = array( __DIR__ . '/FileNameStdInTest.xml' );
$ruleset = new Ruleset( $config );
diff --git a/WordPress/Tests/Helpers/ArrayWalkingFunctionsHelper/GetCallbackParameterUnitTest.inc b/WordPress/Tests/Helpers/ArrayWalkingFunctionsHelper/GetCallbackParameterUnitTest.inc
index 731fa04732..f148fb9365 100644
--- a/WordPress/Tests/Helpers/ArrayWalkingFunctionsHelper/GetCallbackParameterUnitTest.inc
+++ b/WordPress/Tests/Helpers/ArrayWalkingFunctionsHelper/GetCallbackParameterUnitTest.inc
@@ -19,3 +19,9 @@ array_map( 'sanitize_text_field', $array );
/* testMapDeepMixedCase */
Map_Deep( $array, 'esc_html' );
+
+/* testArrayMapFullyQualified */
+\array_map( 'esc_attr', $array );
+
+/* testMapDeepFullyQualifiedUppercase */
+\MAP_DEEP( $array, 'wp_kses_post' );
diff --git a/WordPress/Tests/Helpers/ArrayWalkingFunctionsHelper/GetCallbackParameterUnitTest.php b/WordPress/Tests/Helpers/ArrayWalkingFunctionsHelper/GetCallbackParameterUnitTest.php
index c59af9d8e9..18326a08ad 100644
--- a/WordPress/Tests/Helpers/ArrayWalkingFunctionsHelper/GetCallbackParameterUnitTest.php
+++ b/WordPress/Tests/Helpers/ArrayWalkingFunctionsHelper/GetCallbackParameterUnitTest.php
@@ -66,20 +66,28 @@ public static function dataGetCallbackParameter() {
'testMarker' => '/* testNotArrayWalkingFunction */',
'expectedContent' => false,
),
- 'callback_param_missing' => array(
+ 'callback_param_missing' => array(
'testMarker' => '/* testCallbackParamMissing */',
'expectedContent' => false,
),
// Cases where the callback parameter should be returned.
- 'array_map_callback' => array(
+ 'array_map_callback' => array(
'testMarker' => '/* testArrayMapCallback */',
'expectedContent' => "'sanitize_text_field'",
),
- 'map_deep_mixed_case' => array(
+ 'map_deep_mixed_case' => array(
'testMarker' => '/* testMapDeepMixedCase */',
'expectedContent' => "'esc_html'",
),
+ 'array_map_fully_qualified' => array(
+ 'testMarker' => '/* testArrayMapFullyQualified */',
+ 'expectedContent' => "'esc_attr'",
+ ),
+ 'map_deep_fully_qualified_uppercase' => array(
+ 'testMarker' => '/* testMapDeepFullyQualifiedUppercase */',
+ 'expectedContent' => "'wp_kses_post'",
+ ),
);
}
}
diff --git a/WordPress/Tests/Helpers/ArrayWalkingFunctionsHelper/IsArrayWalkingFunctionUnitTest.php b/WordPress/Tests/Helpers/ArrayWalkingFunctionsHelper/IsArrayWalkingFunctionUnitTest.php
index 16f3e0ac35..ab5e133cdd 100644
--- a/WordPress/Tests/Helpers/ArrayWalkingFunctionsHelper/IsArrayWalkingFunctionUnitTest.php
+++ b/WordPress/Tests/Helpers/ArrayWalkingFunctionsHelper/IsArrayWalkingFunctionUnitTest.php
@@ -55,6 +55,14 @@ public static function dataIsArrayWalkingFunction() {
'functionName' => 'mAp_DeEp',
'expectedResult' => true,
),
+ 'fully_qualified_name' => array(
+ 'functionName' => '\array_map',
+ 'expectedResult' => true,
+ ),
+ 'fully_qualified_name_uppercase' => array(
+ 'functionName' => '\MAP_DEEP',
+ 'expectedResult' => true,
+ ),
'not_an_array_walking_function' => array(
'functionName' => 'array_filter',
'expectedResult' => false,
diff --git a/WordPress/Tests/Helpers/ConstantsHelper/IsUseOfGlobalConstantUnitTest.inc b/WordPress/Tests/Helpers/ConstantsHelper/IsUseOfGlobalConstantUnitTest.inc
new file mode 100644
index 0000000000..01ffb520c0
--- /dev/null
+++ b/WordPress/Tests/Helpers/ConstantsHelper/IsUseOfGlobalConstantUnitTest.inc
@@ -0,0 +1,134 @@
+PHP_OS;
+
+/* testNullsafeObjectPropertyAccess */
+$obj?->PHP_OS;
+
+SomeClass::/* testClassConstantAccess */PHP_OS;
+
+class ScopeModifiersTest {
+ /* testPrecededByPublic */
+ public PHP_OS $prop1;
+
+ /* testPrecededByProtected */
+ protected PHP_OS $prop2;
+
+ /* testPrecededByPrivate */
+ private PHP_OS $prop3;
+
+ /* testPrecededByPublicSet */
+ public public(set) PHP_OS $prop4;
+
+ /* testPrecededByProtectedSet */
+ public protected(set) PHP_OS $prop5;
+
+ /* testPrecededByPrivateSet */
+ public private(set) PHP_OS $prop6;
+}
+
+/* testPartiallyQualifiedNamespacedConstant */
+MyNamespace\PHP_OS;
+
+/* testFullyQualifiedNamespacedConstant */
+\MyNamespace\PHP_OS;
+
+/* testNamespaceRelativeConstant */
+namespace\PHP_OS; // This should be considered use of a global constant in the future once the method is able to resolve relative namespaces.
+
+/* testNamespaceRelativeSubConstant */
+namespace\Sub\PHP_OS;
+
+class Foo {
+ /* testClassConstantDeclaration */
+ const PHP_OS = 1;
+}
+
+use const SomeNamespace\{/* testUseConstStatementGrouped */ PHP_OS, PHP_VERSION_ID};
+
+use Foo\{Bar, /* testUseStatementGrouped */ PHP_OS};
+
+/*
+ * The below should be recognized as use of a global constant.
+ */
+
+/* testEchoStatement */
+echo PHP_OS;
+
+/* testEchoStatementFullyQualified */
+echo \PHP_OS;
+
+// Note: this is counterintuitive behavior, as declaring a constant is not exactly the use of a constant. However, this
+// seems intentional considering the docblock of DiscouragedConstantsSniff states that it warns against both usage and
+// re-declaration of discouraged WP constants.
+/* testConstDeclaration */
+const my_const = 'something';
+const ABC = '123',
+ /* testConstDeclarationInList */ PHP_OS = 'something';
+
+/* testUseConstStatement */
+use const PHP_OS as SSP;
diff --git a/WordPress/Tests/Helpers/ConstantsHelper/IsUseOfGlobalConstantUnitTest.php b/WordPress/Tests/Helpers/ConstantsHelper/IsUseOfGlobalConstantUnitTest.php
new file mode 100644
index 0000000000..e75b4104fb
--- /dev/null
+++ b/WordPress/Tests/Helpers/ConstantsHelper/IsUseOfGlobalConstantUnitTest.php
@@ -0,0 +1,252 @@
+assertFalse(
+ ConstantsHelper::is_use_of_global_constant(
+ self::$phpcsFile,
+ -1
+ )
+ );
+ }
+
+ /**
+ * Test is_use_of_global_constant().
+ *
+ * @dataProvider dataIsUseOfGlobalConstant
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param bool $expectedResult The expected return value.
+ * @param int|null $tokenType Optional. The token type to use for the target token.
+ * @param string|null $tokenContent Optional. The token content to use for the target token.
+ *
+ * @return void
+ */
+ public function testIsUseOfGlobalConstant( $testMarker, $expectedResult, $tokenType = \T_STRING, $tokenContent = null ) {
+ $stackPtr = $this->getTargetToken( $testMarker, $tokenType, $tokenContent );
+ $result = ConstantsHelper::is_use_of_global_constant( self::$phpcsFile, $stackPtr );
+
+ $this->assertSame( $expectedResult, $result );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @see testIsUseOfGlobalConstant()
+ *
+ * @return array>
+ */
+ public static function dataIsUseOfGlobalConstant() {
+ $phpcs_version = Helper::getVersion();
+ $is_phpcs_4 = version_compare( $phpcs_version, '3.99.99', '>' );
+
+ return array(
+ // Cases that should return false.
+ 'variable_assignment' => array(
+ 'testMarker' => '/* testVariableAssignment */',
+ 'expectedResult' => false,
+ 'tokenType' => \T_VARIABLE,
+ ),
+ 'function_call' => array(
+ 'testMarker' => '/* testFunctionCall */',
+ 'expectedResult' => false,
+ ),
+ 'function_declaration' => array(
+ 'testMarker' => '/* testFunctionDeclaration */',
+ 'expectedResult' => false,
+ ),
+ 'class_instantiation' => array(
+ 'testMarker' => '/* testClassInstantiation */',
+ 'expectedResult' => false,
+ ),
+ 'static_method_call' => array(
+ 'testMarker' => '/* testStaticMethodCall */',
+ 'expectedResult' => false,
+ ),
+ 'static_class_constant_access' => array(
+ 'testMarker' => '/* testStaticClassConstantAccess */',
+ 'expectedResult' => false,
+ ),
+ 'class_name_resolution' => array(
+ 'testMarker' => '/* testClassNameResolution */',
+ 'expectedResult' => false,
+ ),
+ 'namespace_declaration' => array(
+ 'testMarker' => '/* testNamespaceDeclaration */',
+ 'expectedResult' => false,
+ ),
+ 'use_statement' => array(
+ 'testMarker' => '/* testUseStatement */',
+ 'expectedResult' => false,
+ ),
+ 'class_extends' => array(
+ 'testMarker' => '/* testClassExtends */',
+ 'expectedResult' => false,
+ ),
+ 'class_implements' => array(
+ 'testMarker' => '/* testClassImplements */',
+ 'expectedResult' => false,
+ ),
+ 'class_instantiation_no_parentheses' => array(
+ 'testMarker' => '/* testClassInstantiationNoParentheses */',
+ 'expectedResult' => false,
+ ),
+ 'instanceof' => array(
+ 'testMarker' => '/* testInstanceof */',
+ 'expectedResult' => false,
+ ),
+ 'preceded_by_insteadof' => array(
+ 'testMarker' => '/* testPrecededByInsteadOf */',
+ 'expectedResult' => false,
+ ),
+ 'goto_label' => array(
+ 'testMarker' => '/* testGotoLabel */',
+ 'expectedResult' => false,
+ ),
+ 'preceded_by_as' => array(
+ 'testMarker' => '/* testPrecededByAs */',
+ 'expectedResult' => false,
+ ),
+ 'class_declaration' => array(
+ 'testMarker' => '/* testClassDeclaration */',
+ 'expectedResult' => false,
+ ),
+ 'interface_declaration' => array(
+ 'testMarker' => '/* testInterfaceDeclaration */',
+ 'expectedResult' => false,
+ ),
+ 'trait_declaration' => array(
+ 'testMarker' => '/* testTraitDeclaration */',
+ 'expectedResult' => false,
+ ),
+ 'enum_declaration' => array(
+ 'testMarker' => '/* testEnumDeclaration */',
+ 'expectedResult' => false,
+ ),
+ 'object_property_access' => array(
+ 'testMarker' => '/* testObjectPropertyAccess */',
+ 'expectedResult' => false,
+ ),
+ 'nullsafe_object_property_access' => array(
+ 'testMarker' => '/* testNullsafeObjectPropertyAccess */',
+ 'expectedResult' => false,
+ ),
+ 'class_constant_access' => array(
+ 'testMarker' => '/* testClassConstantAccess */',
+ 'expectedResult' => false,
+ ),
+ 'preceded_by_public' => array(
+ 'testMarker' => '/* testPrecededByPublic */',
+ 'expectedResult' => false,
+ ),
+ 'preceded_by_protected' => array(
+ 'testMarker' => '/* testPrecededByProtected */',
+ 'expectedResult' => false,
+ ),
+ 'preceded_by_private' => array(
+ 'testMarker' => '/* testPrecededByPrivate */',
+ 'expectedResult' => false,
+ ),
+ 'preceded_by_public_set' => array(
+ 'testMarker' => '/* testPrecededByPublicSet */',
+ 'expectedResult' => false,
+ ),
+ 'preceded_by_protected_set' => array(
+ 'testMarker' => '/* testPrecededByProtectedSet */',
+ 'expectedResult' => false,
+ ),
+ 'preceded_by_private_set' => array(
+ 'testMarker' => '/* testPrecededByPrivateSet */',
+ 'expectedResult' => false,
+ ),
+ 'partially_qualified_namespaced_constant' => array(
+ 'testMarker' => '/* testPartiallyQualifiedNamespacedConstant */',
+ 'expectedResult' => false,
+ 'tokenType' => ( true === $is_phpcs_4 ? \T_NAME_QUALIFIED : \T_STRING ),
+ 'tokenContent' => ( true === $is_phpcs_4 ? 'MyNamespace\PHP_OS' : 'PHP_OS' ),
+ ),
+ 'fully_qualified_namespaced_constant' => array(
+ 'testMarker' => '/* testFullyQualifiedNamespacedConstant */',
+ 'expectedResult' => false,
+ 'tokenType' => ( true === $is_phpcs_4 ? \T_NAME_FULLY_QUALIFIED : \T_STRING ),
+ 'tokenContent' => ( true === $is_phpcs_4 ? '\MyNamespace\PHP_OS' : 'PHP_OS' ),
+ ),
+ 'namespace_relative_constant' => array(
+ 'testMarker' => '/* testNamespaceRelativeConstant */',
+ 'expectedResult' => false,
+ 'tokenType' => ( true === $is_phpcs_4 ? \T_NAME_RELATIVE : \T_STRING ),
+ 'tokenContent' => ( true === $is_phpcs_4 ? 'namespace\PHP_OS' : 'PHP_OS' ),
+ ),
+ 'namespace_relative_sub_constant' => array(
+ 'testMarker' => '/* testNamespaceRelativeSubConstant */',
+ 'expectedResult' => false,
+ 'tokenType' => ( true === $is_phpcs_4 ? \T_NAME_RELATIVE : \T_STRING ),
+ 'tokenContent' => ( true === $is_phpcs_4 ? 'namespace\Sub\PHP_OS' : 'PHP_OS' ),
+ ),
+ 'class_constant_declaration' => array(
+ 'testMarker' => '/* testClassConstantDeclaration */',
+ 'expectedResult' => false,
+ ),
+ 'use_const_statement_grouped' => array(
+ 'testMarker' => '/* testUseConstStatementGrouped */',
+ 'expectedResult' => false,
+ ),
+ 'use_statement_grouped' => array(
+ 'testMarker' => '/* testUseStatementGrouped */',
+ 'expectedResult' => false,
+ ),
+
+ // Cases that should return true.
+ 'echo_statement' => array(
+ 'testMarker' => '/* testEchoStatement */',
+ 'expectedResult' => true,
+ ),
+ 'echo_statement_fully_qualified' => array(
+ 'testMarker' => '/* testEchoStatementFullyQualified */',
+ 'expectedResult' => true,
+ 'tokenType' => ( true === $is_phpcs_4 ? \T_NAME_FULLY_QUALIFIED : \T_STRING ),
+ 'tokenContent' => ( true === $is_phpcs_4 ? '\PHP_OS' : 'PHP_OS' ),
+ ),
+ 'const_declaration' => array(
+ 'testMarker' => '/* testConstDeclaration */',
+ 'expectedResult' => true,
+ ),
+ 'const_declaration_in_list' => array(
+ 'testMarker' => '/* testConstDeclarationInList */',
+ 'expectedResult' => true,
+ ),
+ 'use_const_statement' => array(
+ 'testMarker' => '/* testUseConstStatement */',
+ 'expectedResult' => true,
+ 'tokenType' => \T_STRING,
+ 'tokenContent' => 'PHP_OS',
+ ),
+ );
+ }
+}
diff --git a/WordPress/Tests/Helpers/ContextHelper/IsInFunctionCallUnitTest.inc b/WordPress/Tests/Helpers/ContextHelper/IsInFunctionCallUnitTest.inc
index 8a20dade8a..746cff4d9f 100644
--- a/WordPress/Tests/Helpers/ContextHelper/IsInFunctionCallUnitTest.inc
+++ b/WordPress/Tests/Helpers/ContextHelper/IsInFunctionCallUnitTest.inc
@@ -31,11 +31,14 @@ another_function( /* testNestedInner */ valid_function1( /* testNestedInnerInsid
* is `false`.
*/
-MyNamespace\/* testNamespacedFunction */valid_function1( /* testNamespacedFunctionInsideCall */ some_function() );
-\MyNamespace\/* testFullyQualifiedNamespacedFunction */valid_function1(
+/* testNamespacedFunction */
+MyNamespace\valid_function1( /* testNamespacedFunctionInsideCall */ some_function() );
+/* testFullyQualifiedNamespacedFunction */
+\MyNamespace\valid_function1(
/* testFullyQualifiedNamespacedFunctionInsideCall */ null
);
-namespace\MyNamespace\/* testNamespaceRelativeFunction */valid_function2(
+/* testNamespaceRelativeFunction */
+namespace\MyNamespace\valid_function2(
/* testNamespaceRelativeFunctionInsideCall */ 3.14
);
MyClass::/* testStaticMethod */valid_function2(
@@ -61,7 +64,8 @@ valid_function1( middle_function( inner_function( /* testNestedMultipleLevelsIns
* `$global_function` is `false` and `$allow_nested` is `true`.
*/
-MyNamespace\/* testNestedBothNamespacedOuter */ valid_function1(
+/* testNestedBothNamespacedOuter */
+MyNamespace\valid_function1(
MyNamespace\other_function(
/* testNestedBothNamespacedOuterInsideCall */ 'value' . $var
)
diff --git a/WordPress/Tests/Helpers/ContextHelper/IsInFunctionCallUnitTest.php b/WordPress/Tests/Helpers/ContextHelper/IsInFunctionCallUnitTest.php
index c80188631b..49032ea50c 100644
--- a/WordPress/Tests/Helpers/ContextHelper/IsInFunctionCallUnitTest.php
+++ b/WordPress/Tests/Helpers/ContextHelper/IsInFunctionCallUnitTest.php
@@ -9,6 +9,7 @@
namespace WordPressCS\WordPress\Tests\Helpers\ContextHelper;
+use PHPCSUtils\BackCompat\Helper;
use PHPCSUtils\TestUtils\UtilityMethodTestCase;
use WordPressCS\WordPress\Helpers\ContextHelper;
@@ -206,16 +207,18 @@ public static function dataIsInFunctionCallShouldMatchRegardlessOfValidFunctions
* @dataProvider dataIsInFunctionCallWithNestedTrue
* @dataProvider dataIsInFunctionCallWithGlobalFalseNestedTrue
*
- * @param string $marker The comment which prefaces the target token.
- * @param int|string $tokenType The token type to search for.
- * @param bool $shouldMatch Whether `is_in_function_call()` should find a match.
- * @param string|null $expectedMarker The comment which prefaces the expected function name
- * in the test file (if a match is expected).
- * @param array $params The is_in_function_call() parameter values.
+ * @param string $marker The comment which prefaces the target token.
+ * @param int|string $tokenType The token type to search for.
+ * @param bool $shouldMatch Whether `is_in_function_call()` should find a match.
+ * @param string|null $expectedMarker The comment which prefaces the expected function name
+ * in the test file (if a match is expected).
+ * @param int|string $expectedFunctionTokenType The token type for the expected function token.
+ * @param string|null $expectedFunctionContent The content for the expected function token.
+ * @param array $params The is_in_function_call() parameter values.
*
* @return void
*/
- public function testIsInFunctionCall( $marker, $tokenType, $shouldMatch, $expectedMarker, $params ) {
+ public function testIsInFunctionCall( $marker, $tokenType, $shouldMatch, $expectedMarker, $expectedFunctionTokenType, $expectedFunctionContent, $params ) {
$insideFunctionPtr = $this->getTargetToken( $marker, $tokenType );
$result = ContextHelper::is_in_function_call(
self::$phpcsFile,
@@ -230,7 +233,7 @@ public function testIsInFunctionCall( $marker, $tokenType, $shouldMatch, $expect
$expected = false;
if ( true === $shouldMatch ) {
- $expected = $this->getTargetToken( $expectedMarker, \T_STRING );
+ $expected = $this->getTargetToken( $expectedMarker, $expectedFunctionTokenType, $expectedFunctionContent );
}
$this->assertSame( $expected, $result );
@@ -330,6 +333,9 @@ public static function dataIsInFunctionCallWithGlobalFalseNestedTrue() {
* @return array>>
*/
public static function dataIsInFunctionCall() {
+ $phpcs_version = Helper::getVersion();
+ $is_phpcs_4 = version_compare( $phpcs_version, '3.99.99', '>' );
+
$data = array(
// Cases that should never match (regardless of parameters).
'plain_assignment' => array(
@@ -372,10 +378,12 @@ public static function dataIsInFunctionCall() {
'expectedMarker' => '/* testUppercaseName */',
),
'fully_qualified' => array(
- 'marker' => '/* testFullyQualifiedInsideCall */',
- 'tokenType' => \T_LNUMBER,
- 'shouldMatch' => self::EXPECT_ALWAYS_MATCH,
- 'expectedMarker' => '/* testFullyQualified */',
+ 'marker' => '/* testFullyQualifiedInsideCall */',
+ 'tokenType' => \T_LNUMBER,
+ 'shouldMatch' => self::EXPECT_ALWAYS_MATCH,
+ 'expectedMarker' => '/* testFullyQualified */',
+ 'expectedFunctionTokenType' => ( true === $is_phpcs_4 ? \T_NAME_FULLY_QUALIFIED : \T_STRING ),
+ 'expectedFunctionContent' => ( true === $is_phpcs_4 ? '\valid_function2' : null ),
),
'nested_inner' => array(
'marker' => '/* testNestedInnerInsideCall */',
@@ -386,22 +394,28 @@ public static function dataIsInFunctionCall() {
// Cases that match only when `$global_function` is `false`.
'namespaced_function' => array(
- 'marker' => '/* testNamespacedFunctionInsideCall */',
- 'tokenType' => \T_STRING,
- 'shouldMatch' => self::EXPECT_NON_GLOBAL_ONLY,
- 'expectedMarker' => '/* testNamespacedFunction */',
+ 'marker' => '/* testNamespacedFunctionInsideCall */',
+ 'tokenType' => \T_STRING,
+ 'shouldMatch' => self::EXPECT_NON_GLOBAL_ONLY,
+ 'expectedMarker' => '/* testNamespacedFunction */',
+ 'expectedFunctionTokenType' => ( true === $is_phpcs_4 ? \T_NAME_QUALIFIED : \T_STRING ),
+ 'expectedFunctionContent' => ( true === $is_phpcs_4 ? 'MyNamespace\valid_function1' : 'valid_function1' ),
),
'fully_qualified_namespaced_function' => array(
- 'marker' => '/* testFullyQualifiedNamespacedFunctionInsideCall */',
- 'tokenType' => \T_NULL,
- 'shouldMatch' => self::EXPECT_NON_GLOBAL_ONLY,
- 'expectedMarker' => '/* testFullyQualifiedNamespacedFunction */',
+ 'marker' => '/* testFullyQualifiedNamespacedFunctionInsideCall */',
+ 'tokenType' => \T_NULL,
+ 'shouldMatch' => self::EXPECT_NON_GLOBAL_ONLY,
+ 'expectedMarker' => '/* testFullyQualifiedNamespacedFunction */',
+ 'expectedFunctionTokenType' => ( true === $is_phpcs_4 ? \T_NAME_FULLY_QUALIFIED : \T_STRING ),
+ 'expectedFunctionContent' => ( true === $is_phpcs_4 ? '\MyNamespace\valid_function1' : 'valid_function1' ),
),
'namespace_relative_function' => array(
- 'marker' => '/* testNamespaceRelativeFunctionInsideCall */',
- 'tokenType' => \T_DNUMBER,
- 'shouldMatch' => self::EXPECT_NON_GLOBAL_ONLY,
- 'expectedMarker' => '/* testNamespaceRelativeFunction */',
+ 'marker' => '/* testNamespaceRelativeFunctionInsideCall */',
+ 'tokenType' => \T_DNUMBER,
+ 'shouldMatch' => self::EXPECT_NON_GLOBAL_ONLY,
+ 'expectedMarker' => '/* testNamespaceRelativeFunction */',
+ 'expectedFunctionTokenType' => ( true === $is_phpcs_4 ? \T_NAME_RELATIVE : \T_STRING ),
+ 'expectedFunctionContent' => ( true === $is_phpcs_4 ? 'namespace\MyNamespace\valid_function2' : 'valid_function2' ),
),
'static_method' => array(
'marker' => '/* testStaticMethodInsideCall */',
@@ -436,10 +450,12 @@ public static function dataIsInFunctionCall() {
'expectedMarker' => '/* testNestedMultipleLevels */',
),
'nested_both_namespaced_outer' => array(
- 'marker' => '/* testNestedBothNamespacedOuterInsideCall */',
- 'tokenType' => \T_STRING_CONCAT,
- 'shouldMatch' => self::EXPECT_NON_GLOBAL_NESTED_ONLY,
- 'expectedMarker' => '/* testNestedBothNamespacedOuter */',
+ 'marker' => '/* testNestedBothNamespacedOuterInsideCall */',
+ 'tokenType' => \T_STRING_CONCAT,
+ 'shouldMatch' => self::EXPECT_NON_GLOBAL_NESTED_ONLY,
+ 'expectedMarker' => '/* testNestedBothNamespacedOuter */',
+ 'expectedFunctionTokenType' => ( true === $is_phpcs_4 ? \T_NAME_QUALIFIED : \T_STRING ),
+ 'expectedFunctionContent' => ( true === $is_phpcs_4 ? 'MyNamespace\valid_function1' : 'valid_function1' ),
),
// Safeguard: parentheses in other parameters should not confuse the method.
@@ -455,6 +471,12 @@ public static function dataIsInFunctionCall() {
if ( isset( $dataset['expectedMarker'] ) === false ) {
$data[ $key ]['expectedMarker'] = null;
}
+ if ( isset( $dataset['expectedFunctionTokenType'] ) === false ) {
+ $data[ $key ]['expectedFunctionTokenType'] = \T_STRING;
+ }
+ if ( isset( $dataset['expectedFunctionContent'] ) === false ) {
+ $data[ $key ]['expectedFunctionContent'] = null;
+ }
}
return $data;
diff --git a/WordPress/Tests/Helpers/ContextHelper/IsTokenNamespacedUnitTest.inc b/WordPress/Tests/Helpers/ContextHelper/IsTokenNamespacedUnitTest.inc
new file mode 100644
index 0000000000..40262226de
--- /dev/null
+++ b/WordPress/Tests/Helpers/ContextHelper/IsTokenNamespacedUnitTest.inc
@@ -0,0 +1,17 @@
+assertFalse( ContextHelper::is_token_namespaced( self::$phpcsFile, -1 ) );
+ }
+
+ /**
+ * Test is_token_namespaced().
+ *
+ * @dataProvider dataIsTokenNamespaced
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param bool $expectedResult The expected return value.
+ * @param string $tokenContent The token content to use for the target token.
+ *
+ * @return void
+ */
+ public function testIsTokenNamespaced( $testMarker, $expectedResult, $tokenContent ) {
+ $stackPtr = $this->getTargetToken( $testMarker, Collections::nameTokens(), $tokenContent );
+ $result = ContextHelper::is_token_namespaced( self::$phpcsFile, $stackPtr );
+
+ $this->assertSame( $expectedResult, $result );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @see testIsTokenNamespaced()
+ *
+ * @return array>
+ */
+ public static function dataIsTokenNamespaced() {
+ $isPhpcs3 = version_compare( Helper::getVersion(), '3.99.99', '<=' );
+
+ return array(
+ // Cases that should return false.
+ 'unqualified' => array(
+ 'testMarker' => '/* testUnqualified */',
+ 'expectedResult' => false,
+ 'tokenContent' => 'my_function',
+ ),
+ 'fully_qualified' => array(
+ 'testMarker' => '/* testFullyQualified */',
+ 'expectedResult' => false,
+ 'tokenContent' => ( $isPhpcs3 ? 'MY_CONSTANT' : '\MY_CONSTANT' ),
+ ),
+
+ // Cases that should return true.
+ 'partially_qualified' => array(
+ 'testMarker' => '/* testPartiallyQualified */',
+ 'expectedResult' => true,
+ 'tokenContent' => ( $isPhpcs3 ? 'my_function' : 'MyNamespace\my_function' ),
+ ),
+ 'fully_qualified_namespaced' => array(
+ 'testMarker' => '/* testFullyQualifiedNamespaced */',
+ 'expectedResult' => true,
+ 'tokenContent' => ( $isPhpcs3 ? 'MyClass' : '\MyNamespace\MyClass' ),
+ ),
+ 'namespace_relative' => array(
+ 'testMarker' => '/* testNamespaceRelative */',
+ 'expectedResult' => true,
+ 'tokenContent' => ( $isPhpcs3 ? 'MY_CONSTANT' : 'namespace\MY_CONSTANT' ),
+ ),
+ 'namespace_relative_sub' => array(
+ 'testMarker' => '/* testNamespaceRelativeSub */',
+ 'expectedResult' => true,
+ 'tokenContent' => ( $isPhpcs3 ? 'my_function' : 'namespace\Sub\my_function' ),
+ ),
+ );
+ }
+}
diff --git a/WordPress/Tests/Helpers/EscapingFunctionsTrait/IsAutoEscapedFunctionUnitTest.php b/WordPress/Tests/Helpers/EscapingFunctionsTrait/IsAutoEscapedFunctionUnitTest.php
new file mode 100644
index 0000000000..78cfb83a80
--- /dev/null
+++ b/WordPress/Tests/Helpers/EscapingFunctionsTrait/IsAutoEscapedFunctionUnitTest.php
@@ -0,0 +1,115 @@
+testClass = new class() {
+ use EscapingFunctionsTrait;
+ };
+ }
+
+ /**
+ * Test is_auto_escaped_function() with default auto escaped functions.
+ *
+ * @dataProvider dataIsAutoEscapedFunction
+ *
+ * @param string $functionName The function name to test.
+ * @param bool $expectedResult The expected return value.
+ *
+ * @return void
+ */
+ public function testIsAutoEscapedFunction( $functionName, $expectedResult ) {
+ $this->assertSame(
+ $expectedResult,
+ $this->testClass->is_auto_escaped_function( $functionName )
+ );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @see testIsAutoEscapedFunction()
+ *
+ * @return array>
+ */
+ public static function dataIsAutoEscapedFunction() {
+ return array(
+ 'lowercase_name' => array(
+ 'functionName' => 'bloginfo',
+ 'expectedResult' => true,
+ ),
+ 'mixedcase_name' => array(
+ 'functionName' => 'bOdY_ClAsS',
+ 'expectedResult' => true,
+ ),
+ 'fully_qualified_name' => array(
+ 'functionName' => '\allowed_tags',
+ 'expectedResult' => true,
+ ),
+ 'not_an_auto_escaped_function' => array(
+ 'functionName' => 'esc_html',
+ 'expectedResult' => false,
+ ),
+ );
+ }
+
+ /**
+ * Test that a custom auto-escaped function is recognized.
+ *
+ * @return void
+ */
+ public function testCustomAutoEscapedFunctionIsRecognized() {
+ $this->assertFalse( $this->testClass->is_auto_escaped_function( 'my_custom_auto_escaped' ) );
+ $this->testClass->customAutoEscapedFunctions = array( 'my_custom_auto_escaped' );
+ $this->assertTrue( $this->testClass->is_auto_escaped_function( 'my_custom_auto_escaped' ) );
+ $this->assertTrue( $this->testClass->is_auto_escaped_function( 'bloginfo' ) );
+ }
+
+ /**
+ * Test that the auto-escaped function list is updated when the custom
+ * auto-escaped functions are changed.
+ *
+ * @return void
+ */
+ public function testIsAutoEscapedFunctionUpdatesWhenCustomFunctionsChange() {
+ $this->testClass->customAutoEscapedFunctions = array( 'first_custom' );
+ $this->assertTrue( $this->testClass->is_auto_escaped_function( 'first_custom' ) );
+
+ $this->testClass->customAutoEscapedFunctions = array( 'second_custom' );
+ $this->assertTrue( $this->testClass->is_auto_escaped_function( 'second_custom' ) );
+ $this->assertFalse( $this->testClass->is_auto_escaped_function( 'first_custom' ) );
+ }
+}
diff --git a/WordPress/Tests/Helpers/EscapingFunctionsTrait/IsEscapingFunctionUnitTest.php b/WordPress/Tests/Helpers/EscapingFunctionsTrait/IsEscapingFunctionUnitTest.php
new file mode 100644
index 0000000000..d414081391
--- /dev/null
+++ b/WordPress/Tests/Helpers/EscapingFunctionsTrait/IsEscapingFunctionUnitTest.php
@@ -0,0 +1,115 @@
+testClass = new class() {
+ use EscapingFunctionsTrait;
+ };
+ }
+
+ /**
+ * Test is_escaping_function() with default escaping functions.
+ *
+ * @dataProvider dataIsEscapingFunction
+ *
+ * @param string $functionName The function name to test.
+ * @param bool $expectedResult The expected return value.
+ *
+ * @return void
+ */
+ public function testIsEscapingFunction( $functionName, $expectedResult ) {
+ $this->assertSame(
+ $expectedResult,
+ $this->testClass->is_escaping_function( $functionName )
+ );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @see testIsEscapingFunction()
+ *
+ * @return array>
+ */
+ public static function dataIsEscapingFunction() {
+ return array(
+ 'lowercase_name' => array(
+ 'functionName' => 'esc_html',
+ 'expectedResult' => true,
+ ),
+ 'mixedcase_name' => array(
+ 'functionName' => 'eSc_AtTr',
+ 'expectedResult' => true,
+ ),
+ 'fully_qualified_name' => array(
+ 'functionName' => '\esc_url',
+ 'expectedResult' => true,
+ ),
+ 'not_an_escaping_function' => array(
+ 'functionName' => 'printf',
+ 'expectedResult' => false,
+ ),
+ );
+ }
+
+ /**
+ * Test that a custom escaping function is recognized.
+ *
+ * @return void
+ */
+ public function testCustomEscapingFunctionIsRecognized() {
+ $this->assertFalse( $this->testClass->is_escaping_function( 'my_custom_escape' ) );
+ $this->testClass->customEscapingFunctions = array( 'my_custom_escape' );
+ $this->assertTrue( $this->testClass->is_escaping_function( 'my_custom_escape' ) );
+ $this->assertTrue( $this->testClass->is_escaping_function( 'esc_html' ) );
+ }
+
+ /**
+ * Test that the escaping function list is updated when the custom escaping
+ * functions are changed.
+ *
+ * @return void
+ */
+ public function testIsEscapingFunctionUpdatesWhenCustomFunctionsChange() {
+ $this->testClass->customEscapingFunctions = array( 'first_custom' );
+ $this->assertTrue( $this->testClass->is_escaping_function( 'first_custom' ) );
+
+ $this->testClass->customEscapingFunctions = array( 'second_custom' );
+ $this->assertTrue( $this->testClass->is_escaping_function( 'second_custom' ) );
+ $this->assertFalse( $this->testClass->is_escaping_function( 'first_custom' ) );
+ }
+}
diff --git a/WordPress/Tests/Helpers/FormattingFunctionsHelper/IsFormattingFunctionUnitTest.php b/WordPress/Tests/Helpers/FormattingFunctionsHelper/IsFormattingFunctionUnitTest.php
index e567d0f7c3..66784c7607 100644
--- a/WordPress/Tests/Helpers/FormattingFunctionsHelper/IsFormattingFunctionUnitTest.php
+++ b/WordPress/Tests/Helpers/FormattingFunctionsHelper/IsFormattingFunctionUnitTest.php
@@ -55,6 +55,10 @@ public static function dataIsFormattingFunction() {
'functionName' => 'iMpLoDe',
'expectedResult' => true,
),
+ 'fully_qualified_name' => array(
+ 'functionName' => '\sprintf',
+ 'expectedResult' => true,
+ ),
'not_a_formatting_function' => array(
'functionName' => 'printf',
'expectedResult' => false,
diff --git a/WordPress/Tests/Helpers/PrintingFunctionsTrait/IsPrintingFunctionUnitTest.php b/WordPress/Tests/Helpers/PrintingFunctionsTrait/IsPrintingFunctionUnitTest.php
new file mode 100644
index 0000000000..01515dc26c
--- /dev/null
+++ b/WordPress/Tests/Helpers/PrintingFunctionsTrait/IsPrintingFunctionUnitTest.php
@@ -0,0 +1,88 @@
+assertSame(
+ $expectedResult,
+ self::$testClass->is_printing_function( $functionName )
+ );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @see testIsPrintingFunction()
+ *
+ * @return array>
+ */
+ public static function dataIsPrintingFunction() {
+ return array(
+ 'lowercase_name' => array(
+ 'functionName' => 'printf',
+ 'expectedResult' => true,
+ ),
+ 'mixedcase_name' => array(
+ 'functionName' => 'vPrInTf',
+ 'expectedResult' => true,
+ ),
+ 'fully_qualified_name' => array(
+ 'functionName' => '\wp_die',
+ 'expectedResult' => true,
+ ),
+ 'not_a_printing_function' => array(
+ 'functionName' => 'echo',
+ 'expectedResult' => false,
+ ),
+ );
+ }
+}
diff --git a/WordPress/Tests/Helpers/SanitizationHelperTrait/GetSanitizingAndUnslashingFunctionsUnitTest.php b/WordPress/Tests/Helpers/SanitizationHelperTrait/GetSanitizingAndUnslashingFunctionsUnitTest.php
new file mode 100644
index 0000000000..bf97684826
--- /dev/null
+++ b/WordPress/Tests/Helpers/SanitizationHelperTrait/GetSanitizingAndUnslashingFunctionsUnitTest.php
@@ -0,0 +1,90 @@
+testClass = new class() {
+ use SanitizationHelperTrait;
+ };
+ }
+
+ /**
+ * Test that get_sanitizing_and_unslashing_functions() returns an array containing a known
+ * sanitizing and unslashing function.
+ *
+ * @return void
+ */
+ public function testGetSanitizingAndUnslashingFunctionsContainsKnownFunction() {
+ $result = $this->testClass->get_sanitizing_and_unslashing_functions();
+
+ $this->assertIsArray( $result );
+ $this->assertArrayHasKey( 'absint', $result );
+ $this->assertTrue( $result['absint'] );
+ }
+
+ /**
+ * Test that get_sanitizing_and_unslashing_functions() includes custom unslashing sanitizing functions.
+ *
+ * @return void
+ */
+ public function testGetSanitizingAndUnslashingFunctionsIncludesCustomFunctions() {
+ $defaultCount = count( $this->testClass->get_sanitizing_and_unslashing_functions() );
+ $this->testClass->customUnslashingSanitizingFunctions = array( 'my_custom_unslasher' );
+
+ $result = $this->testClass->get_sanitizing_and_unslashing_functions();
+
+ $this->assertCount( $defaultCount + 1, $result );
+ $this->assertArrayHasKey( 'my_custom_unslasher', $result );
+ $this->assertFalse( $result['my_custom_unslasher'] );
+ }
+
+ /**
+ * Test that get_sanitizing_and_unslashing_functions() updates the result when custom
+ * unslashing sanitizing functions are changed.
+ *
+ * @return void
+ */
+ public function testGetSanitizingAndUnslashingFunctionsUpdatesWhenCustomFunctionsChange() {
+ $this->testClass->customUnslashingSanitizingFunctions = array( 'first_custom' );
+ $result = $this->testClass->get_sanitizing_and_unslashing_functions();
+ $this->assertArrayHasKey( 'first_custom', $result );
+ $this->assertFalse( $result['first_custom'] );
+
+ $this->testClass->customUnslashingSanitizingFunctions = array( 'second_custom' );
+ $result = $this->testClass->get_sanitizing_and_unslashing_functions();
+ $this->assertArrayHasKey( 'second_custom', $result );
+ $this->assertFalse( $result['second_custom'] );
+ $this->assertArrayNotHasKey( 'first_custom', $result );
+ }
+}
diff --git a/WordPress/Tests/Helpers/SanitizationHelperTrait/GetSanitizingFunctionsUnitTest.php b/WordPress/Tests/Helpers/SanitizationHelperTrait/GetSanitizingFunctionsUnitTest.php
new file mode 100644
index 0000000000..28f2fbd15c
--- /dev/null
+++ b/WordPress/Tests/Helpers/SanitizationHelperTrait/GetSanitizingFunctionsUnitTest.php
@@ -0,0 +1,90 @@
+testClass = new class() {
+ use SanitizationHelperTrait;
+ };
+ }
+
+ /**
+ * Test that get_sanitizing_functions() returns an array containing a known
+ * sanitizing function.
+ *
+ * @return void
+ */
+ public function testGetSanitizingFunctionsContainsKnownFunction() {
+ $result = $this->testClass->get_sanitizing_functions();
+
+ $this->assertIsArray( $result );
+ $this->assertArrayHasKey( 'sanitize_text_field', $result );
+ $this->assertTrue( $result['sanitize_text_field'] );
+ }
+
+ /**
+ * Test that get_sanitizing_functions() includes custom sanitizing functions.
+ *
+ * @return void
+ */
+ public function testGetSanitizingFunctionsIncludesCustomFunctions() {
+ $defaultCount = count( $this->testClass->get_sanitizing_functions() );
+ $this->testClass->customSanitizingFunctions = array( 'my_custom_sanitizer' );
+
+ $result = $this->testClass->get_sanitizing_functions();
+
+ $this->assertCount( $defaultCount + 1, $result );
+ $this->assertArrayHasKey( 'my_custom_sanitizer', $result );
+ $this->assertFalse( $result['my_custom_sanitizer'] );
+ }
+
+ /**
+ * Test that get_sanitizing_functions() updates the result when custom
+ * sanitizing functions are changed.
+ *
+ * @return void
+ */
+ public function testGetSanitizingFunctionsUpdatesWhenCustomFunctionsChange() {
+ $this->testClass->customSanitizingFunctions = array( 'first_custom' );
+ $result = $this->testClass->get_sanitizing_functions();
+ $this->assertArrayHasKey( 'first_custom', $result );
+ $this->assertFalse( $result['first_custom'] );
+
+ $this->testClass->customSanitizingFunctions = array( 'second_custom' );
+ $result = $this->testClass->get_sanitizing_functions();
+ $this->assertArrayHasKey( 'second_custom', $result );
+ $this->assertFalse( $result['second_custom'] );
+ $this->assertArrayNotHasKey( 'first_custom', $result );
+ }
+}
diff --git a/WordPress/Tests/Helpers/SanitizationHelperTrait/IsOnlySanitizedUnitTest.inc b/WordPress/Tests/Helpers/SanitizationHelperTrait/IsOnlySanitizedUnitTest.inc
new file mode 100644
index 0000000000..f207261900
--- /dev/null
+++ b/WordPress/Tests/Helpers/SanitizationHelperTrait/IsOnlySanitizedUnitTest.inc
@@ -0,0 +1,41 @@
+assertFalse( self::$testClass->is_only_sanitized( self::$phpcsFile, -1 ) );
+ }
+
+ /**
+ * Test is_only_sanitized().
+ *
+ * @dataProvider dataIsOnlySanitized
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param bool $expectedResult The expected return value.
+ * @param int|string $tokenType The token type to search for. Defaults to T_VARIABLE.
+ *
+ * @return void
+ */
+ public function testIsOnlySanitized( $testMarker, $expectedResult, $tokenType = \T_VARIABLE ) {
+ $stackPtr = $this->getTargetToken( $testMarker, $tokenType );
+ $result = self::$testClass->is_only_sanitized(
+ self::$phpcsFile,
+ $stackPtr
+ );
+
+ $this->assertSame( $expectedResult, $result );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @see testIsOnlySanitized()
+ *
+ * @return array>
+ */
+ public static function dataIsOnlySanitized() {
+ return array(
+ // Cases where false should be returned.
+ 'not_sanitized_echo' => array(
+ 'testMarker' => '/* testNotSanitizedEcho */',
+ 'expectedResult' => false,
+ ),
+ 'inner_function_not_sanitizing' => array(
+ 'testMarker' => '/* testInnerFunctionNotSanitizing */',
+ 'expectedResult' => false,
+ ),
+ 'only_unslashed' => array(
+ 'testMarker' => '/* testOnlyUnslashed */',
+ 'expectedResult' => false,
+ ),
+ 'cast_nested_in_function' => array(
+ 'testMarker' => '/* testCastNestedInFunction */',
+ 'expectedResult' => false,
+ ),
+ 'sanitized_nested_in_function' => array(
+ 'testMarker' => '/* testSanitizedNestedInFunction */',
+ 'expectedResult' => false,
+ ),
+
+ // Cases where true should be returned.
+ 'single_sanitizing_function' => array(
+ 'testMarker' => '/* testSingleSanitizingFunction */',
+ 'expectedResult' => true,
+ ),
+ 'unslashing_sanitizing_function' => array(
+ 'testMarker' => '/* testUnslashingSanitizingFunction */',
+ 'expectedResult' => true,
+ ),
+ 'array_walking_sanitizing_callback' => array(
+ 'testMarker' => '/* testArrayWalkingSanitizingCallback */',
+ 'expectedResult' => true,
+ ),
+ 'in_unset' => array(
+ 'testMarker' => '/* testInUnset */',
+ 'expectedResult' => true,
+ ),
+ 'safe_cast' => array(
+ 'testMarker' => '/* testSafeCast */',
+ 'expectedResult' => true,
+ ),
+ 'string_token_sanitized' => array(
+ 'testMarker' => '/* testStringTokenSanitized */',
+ 'expectedResult' => true,
+ 'tokenType' => \T_STRING,
+ ),
+ );
+ }
+}
diff --git a/WordPress/Tests/Helpers/SanitizationHelperTrait/IsSanitizedUnitTest.inc b/WordPress/Tests/Helpers/SanitizationHelperTrait/IsSanitizedUnitTest.inc
new file mode 100644
index 0000000000..b58a73d47b
--- /dev/null
+++ b/WordPress/Tests/Helpers/SanitizationHelperTrait/IsSanitizedUnitTest.inc
@@ -0,0 +1,93 @@
+sanitize_text_field( /* testMethodCall */ $_POST['title'] );
+
+/* testStaticMethodCall */
+MyClass::sanitize_text_field( $_POST['title'] );
+
+/* testArrayWalkingNonSanitizingCallback */
+array_map( 'strtolower', $_POST['items'] );
+
+/* testArrayWalkingNonStringCallback */
+array_map( array( $obj, 'sanitize_text_field' ), $_POST['items'] );
+
+/* testArrayWalkingMissingCallback */
+map_deep( value: $_POST['items'] ); // Missing the required callback argument, but that's not the concern of the method.
+
+/*
+ * Test cases that should be considered sanitized.
+ */
+
+/* testInUnset */
+unset( $_COOKIE['temp'] );
+
+/* testSafeCast */
+(int) $_POST['count'];
+
+/* testSanitizingFunction */
+sanitize_text_field( $_GET['name'] );
+
+/* testSanitizingAndUnslashingFunction */
+\absint( $_POST['id'] );
+
+/* testUnslashedThenSanitized */
+sanitize_text_field( wp_unslash( $_POST['name'] ) );
+
+/* testArrayWalkingSanitizingCallback */
+array_map( 'sanitize_text_field', $_POST['items'] );
+
+sanitize_text_field( /* testStringTokenSanitized */ get_input() ); // T_STRING entry token: not reached via the sniffs that call the method (they only ever pass a T_VARIABLE), but the method supports any token type.
+
+/*
+ * Unslashing function call wrapped in a sanitizing function, across the namespace forms of the inner call.
+ * All of these currently return `true`.
+ *
+ * `\wp_unslash()` is the fully qualified global unslashing function, so `true` is correct in every version.
+ *
+ * `MyNamespace\wp_unslash()`, `\MyNamespace\wp_unslash()` and `namespace\Sub\wp_unslash()` are namespaced
+ * functions distinct from the global one, so the method should return `false`. They are false positives in
+ * PHPCS 3.x due to https://github.com/WordPress/WordPress-Coding-Standards/issues/2665. PHPCS 4.x returns
+ * `false` correctly.
+ *
+ * `namespace\wp_unslash()` is the global function in this non-namespaced file, so it should be `true`, but
+ * the method does not resolve relative names: like the namespaced forms it returns `true` in PHPCS 3.x (via
+ * the same bug) and `false` in PHPCS 4.x.
+ */
+/* testFullyQualifiedGlobalUnslashSanitized */
+sanitize_text_field( \wp_unslash( $_POST['foo'] ) );
+/* testPartiallyQualifiedUnslashSanitized */
+sanitize_text_field( MyNamespace\wp_unslash( $_POST['foo'] ) );
+/* testFullyQualifiedNamespacedUnslashSanitized */
+sanitize_text_field( \MyNamespace\wp_unslash( $_POST['foo'] ) );
+/* testNamespaceRelativeUnslashSanitized */
+sanitize_text_field( namespace\wp_unslash( $_POST['foo'] ) );
+/* testNamespaceRelativeSubUnslashSanitized */
+sanitize_text_field( namespace\Sub\wp_unslash( $_POST['foo'] ) );
diff --git a/WordPress/Tests/Helpers/SanitizationHelperTrait/IsSanitizedUnitTest.php b/WordPress/Tests/Helpers/SanitizationHelperTrait/IsSanitizedUnitTest.php
new file mode 100644
index 0000000000..34271673d7
--- /dev/null
+++ b/WordPress/Tests/Helpers/SanitizationHelperTrait/IsSanitizedUnitTest.php
@@ -0,0 +1,276 @@
+assertFalse( self::$testClass->is_sanitized( self::$phpcsFile, -1 ) );
+ }
+
+ /**
+ * Test is_sanitized().
+ *
+ * @dataProvider dataIsSanitized
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param bool $expectedResult The expected return value.
+ * @param int|string $tokenType The token type to search for. Defaults to T_VARIABLE.
+ *
+ * @return void
+ */
+ public function testIsSanitized( $testMarker, $expectedResult, $tokenType = \T_VARIABLE ) {
+ $stackPtr = $this->getTargetToken( $testMarker, $tokenType );
+ $result = self::$testClass->is_sanitized( self::$phpcsFile, $stackPtr );
+
+ $this->assertSame( $expectedResult, $result );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @see testIsSanitized()
+ *
+ * @return array>
+ */
+ public static function dataIsSanitized() {
+ $phpcs_version = Helper::getVersion();
+ $is_phpcs_4 = version_compare( $phpcs_version, '3.99.99', '>' );
+
+ return array(
+ // Cases where false should be returned.
+ 'not_within_function_call' => array(
+ 'testMarker' => '/* testNotWithinFunctionCall */',
+ 'expectedResult' => false,
+ ),
+ 'non_sanitizing_function' => array(
+ 'testMarker' => '/* testNonSanitizingFunction */',
+ 'expectedResult' => false,
+ ),
+ 'inner_function_not_sanitizing' => array(
+ 'testMarker' => '/* testInnerFunctionNotSanitizing */',
+ 'expectedResult' => false,
+ 'tokenType' => \T_STRING,
+ ),
+ 'unslashed_in_non_sanitizing_function' => array(
+ 'testMarker' => '/* testUnslashedInNonSanitizingFunction */',
+ 'expectedResult' => false,
+ ),
+ 'partially_qualified' => array(
+ 'testMarker' => '/* testPartiallyQualified */',
+ 'expectedResult' => false,
+ ),
+ 'fully_qualified_namespaced' => array(
+ 'testMarker' => '/* testFullyQualifiedNamespaced */',
+ 'expectedResult' => false,
+ ),
+ 'namespace_relative' => array(
+ 'testMarker' => '/* testNamespaceRelative */',
+ 'expectedResult' => false,
+ ),
+ 'namespace_relative_sub' => array(
+ 'testMarker' => '/* testNamespaceRelativeSub */',
+ 'expectedResult' => false,
+ ),
+ 'method_call' => array(
+ 'testMarker' => '/* testMethodCall */',
+ 'expectedResult' => false,
+ ),
+ 'static_method_call' => array(
+ 'testMarker' => '/* testStaticMethodCall */',
+ 'expectedResult' => false,
+ ),
+ 'array_walking_non_sanitizing_callback' => array(
+ 'testMarker' => '/* testArrayWalkingNonSanitizingCallback */',
+ 'expectedResult' => false,
+ ),
+ 'array_walking_non_string_callback' => array(
+ 'testMarker' => '/* testArrayWalkingNonStringCallback */',
+ 'expectedResult' => false,
+ ),
+ 'array_walking_missing_callback' => array(
+ 'testMarker' => '/* testArrayWalkingMissingCallback */',
+ 'expectedResult' => false,
+ ),
+
+ // Cases where true should be returned.
+ 'in_unset' => array(
+ 'testMarker' => '/* testInUnset */',
+ 'expectedResult' => true,
+ ),
+ 'safe_cast' => array(
+ 'testMarker' => '/* testSafeCast */',
+ 'expectedResult' => true,
+ ),
+ 'sanitizing_function' => array(
+ 'testMarker' => '/* testSanitizingFunction */',
+ 'expectedResult' => true,
+ ),
+ 'sanitizing_and_unslashing_function' => array(
+ 'testMarker' => '/* testSanitizingAndUnslashingFunction */',
+ 'expectedResult' => true,
+ ),
+ 'unslashed_then_sanitized' => array(
+ 'testMarker' => '/* testUnslashedThenSanitized */',
+ 'expectedResult' => true,
+ ),
+ 'array_walking_sanitizing_callback' => array(
+ 'testMarker' => '/* testArrayWalkingSanitizingCallback */',
+ 'expectedResult' => true,
+ ),
+ 'string_token_sanitized' => array(
+ 'testMarker' => '/* testStringTokenSanitized */',
+ 'expectedResult' => true,
+ 'tokenType' => \T_STRING,
+ ),
+ 'fully_qualified_global_unslash_sanitized' => array(
+ 'testMarker' => '/* testFullyQualifiedGlobalUnslashSanitized */',
+ 'expectedResult' => true,
+ ),
+
+ // Namespaced inner unslash calls: true in PHPCS 3.x, false in 4.x. See the test case file and #2665.
+ 'partially_qualified_unslash_sanitized' => array(
+ 'testMarker' => '/* testPartiallyQualifiedUnslashSanitized */',
+ 'expectedResult' => ( true === $is_phpcs_4 ) ? false : true,
+ ),
+ 'fully_qualified_namespaced_unslash_sanitized' => array(
+ 'testMarker' => '/* testFullyQualifiedNamespacedUnslashSanitized */',
+ 'expectedResult' => ( true === $is_phpcs_4 ) ? false : true,
+ ),
+ 'namespace_relative_unslash_sanitized' => array(
+ 'testMarker' => '/* testNamespaceRelativeUnslashSanitized */',
+ 'expectedResult' => ( true === $is_phpcs_4 ) ? false : true,
+ ),
+ 'namespace_relative_sub_unslash_sanitized' => array(
+ 'testMarker' => '/* testNamespaceRelativeSubUnslashSanitized */',
+ 'expectedResult' => ( true === $is_phpcs_4 ) ? false : true,
+ ),
+ );
+ }
+
+ /**
+ * Test that is_sanitized() invokes the unslash callback when the value is used
+ * without being unslashed, and not when the value has already been unslashed.
+ *
+ * @dataProvider dataIsSanitizedUnslashCallback
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param bool $expectedResult The expected return value of is_sanitized().
+ * @param bool $expectedCalled Whether the unslash callback is expected to be called.
+ *
+ * @return void
+ */
+ public function testIsSanitizedUnslashCallback( $testMarker, $expectedResult, $expectedCalled ) {
+ $stackPtr = $this->getTargetToken( $testMarker, \T_VARIABLE );
+ $callCount = 0;
+ $callArgs = array();
+ $callback = static function ( $phpcsFile, $ptr ) use ( &$callCount, &$callArgs ) {
+ ++$callCount;
+ $callArgs = array( $phpcsFile, $ptr );
+ };
+
+ $result = self::$testClass->is_sanitized( self::$phpcsFile, $stackPtr, $callback );
+
+ $this->assertSame( $expectedResult, $result, "Return value mismatch for $testMarker" );
+ $this->assertSame(
+ $expectedCalled ? 1 : 0,
+ $callCount,
+ "Unexpected number of unslash callback invocations for $testMarker"
+ );
+
+ if ( true === $expectedCalled ) {
+ $this->assertSame(
+ array( self::$phpcsFile, $stackPtr ),
+ $callArgs,
+ "The unslash callback received unexpected arguments for $testMarker"
+ );
+ }
+ }
+
+ /**
+ * Data provider.
+ *
+ * @see testIsSanitizedUnslashCallback()
+ *
+ * @return array>
+ */
+ public static function dataIsSanitizedUnslashCallback() {
+ return array(
+ 'not_within_function_call' => array(
+ 'testMarker' => '/* testNotWithinFunctionCall */',
+ 'expectedResult' => false,
+ 'expectedCalled' => true,
+ ),
+ 'non_sanitizing_function' => array(
+ 'testMarker' => '/* testNonSanitizingFunction */',
+ 'expectedResult' => false,
+ 'expectedCalled' => true,
+ ),
+ 'sanitized_not_unslashed' => array(
+ 'testMarker' => '/* testSanitizingFunction */',
+ 'expectedResult' => true,
+ 'expectedCalled' => true,
+ ),
+ 'unslashed_then_sanitized' => array(
+ 'testMarker' => '/* testUnslashedThenSanitized */',
+ 'expectedResult' => true,
+ 'expectedCalled' => false,
+ ),
+ 'unslashed_in_non_sanitizing_function' => array(
+ 'testMarker' => '/* testUnslashedInNonSanitizingFunction */',
+ 'expectedResult' => false,
+ 'expectedCalled' => false,
+ ),
+ 'sanitizing_and_unslashing_function' => array(
+ 'testMarker' => '/* testSanitizingAndUnslashingFunction */',
+ 'expectedResult' => true,
+ 'expectedCalled' => false,
+ ),
+ );
+ }
+}
diff --git a/WordPress/Tests/Helpers/SanitizationHelperTrait/IsSanitizingAndUnslashingFunctionUnitTest.php b/WordPress/Tests/Helpers/SanitizationHelperTrait/IsSanitizingAndUnslashingFunctionUnitTest.php
new file mode 100644
index 0000000000..e83e97c9f9
--- /dev/null
+++ b/WordPress/Tests/Helpers/SanitizationHelperTrait/IsSanitizingAndUnslashingFunctionUnitTest.php
@@ -0,0 +1,86 @@
+assertSame(
+ $expectedResult,
+ self::$testClass->is_sanitizing_and_unslashing_function( $functionName )
+ );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @see testIsSanitizingAndUnslashingFunction()
+ *
+ * @return array>
+ */
+ public static function dataIsSanitizingAndUnslashingFunction() {
+ return array(
+ 'lowercase_name' => array(
+ 'functionName' => 'absint',
+ 'expectedResult' => true,
+ ),
+ 'mixedcase_name' => array(
+ 'functionName' => 'iNtVaL',
+ 'expectedResult' => true,
+ ),
+ 'fully_qualified_name' => array(
+ 'functionName' => '\boolval',
+ 'expectedResult' => true,
+ ),
+ 'not_a_sanitizing_and_unslashing_function' => array(
+ 'functionName' => 'sanitize_text_field',
+ 'expectedResult' => false,
+ ),
+ );
+ }
+}
diff --git a/WordPress/Tests/Helpers/SanitizationHelperTrait/IsSanitizingFunctionUnitTest.php b/WordPress/Tests/Helpers/SanitizationHelperTrait/IsSanitizingFunctionUnitTest.php
new file mode 100644
index 0000000000..e585e40a79
--- /dev/null
+++ b/WordPress/Tests/Helpers/SanitizationHelperTrait/IsSanitizingFunctionUnitTest.php
@@ -0,0 +1,86 @@
+assertSame(
+ $expectedResult,
+ self::$testClass->is_sanitizing_function( $functionName )
+ );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @see testIsSanitizingFunction()
+ *
+ * @return array>
+ */
+ public static function dataIsSanitizingFunction() {
+ return array(
+ 'lowercase_name' => array(
+ 'functionName' => 'sanitize_text_field',
+ 'expectedResult' => true,
+ ),
+ 'mixedcase_name' => array(
+ 'functionName' => 'SaNiTiZe_EmAiL',
+ 'expectedResult' => true,
+ ),
+ 'fully_qualified_name' => array(
+ 'functionName' => '\sanitize_text_field',
+ 'expectedResult' => true,
+ ),
+ 'not_a_sanitizing_function' => array(
+ 'functionName' => 'printf',
+ 'expectedResult' => false,
+ ),
+ );
+ }
+}
diff --git a/WordPress/Tests/Helpers/UnslashingFunctionsHelper/IsUnslashingFunctionUnitTest.php b/WordPress/Tests/Helpers/UnslashingFunctionsHelper/IsUnslashingFunctionUnitTest.php
index c797cb2443..a486b799fd 100644
--- a/WordPress/Tests/Helpers/UnslashingFunctionsHelper/IsUnslashingFunctionUnitTest.php
+++ b/WordPress/Tests/Helpers/UnslashingFunctionsHelper/IsUnslashingFunctionUnitTest.php
@@ -55,6 +55,10 @@ public static function dataIsUnslashingFunction() {
'functionName' => 'sTrIpSlAsHeS_DeEp',
'expectedResult' => true,
),
+ 'fully_qualified_name' => array(
+ 'functionName' => '\stripslashes_from_strings_only',
+ 'expectedResult' => true,
+ ),
'not_an_unslashing_function' => array(
'functionName' => 'stripslashes',
'expectedResult' => false,
diff --git a/WordPress/Tests/Helpers/ValidationHelper/IsValidatedConstructNotFollowedByParenthesisUnitTest.inc b/WordPress/Tests/Helpers/ValidationHelper/IsValidatedConstructNotFollowedByParenthesisUnitTest.inc
new file mode 100644
index 0000000000..6d2d4f195d
--- /dev/null
+++ b/WordPress/Tests/Helpers/ValidationHelper/IsValidatedConstructNotFollowedByParenthesisUnitTest.inc
@@ -0,0 +1,4 @@
+ isset( $_POST['key'] );
+ echo /* testArrowFunctionValidationNotCounted */ $_POST['key'];
+}
+
+function test_construct_wrong_variable_or_key() {
+ if ( isset( $_GET['key'] ) || isset( $_POST['other'] ) ) {
+ echo /* testConstructWrongVariableOrKey */ $_POST['key'];
+ }
+}
+
+function test_construct_param_key_mismatch() {
+ if ( isset( $_POST['key'] ) ) {
+ echo /* testConstructParamKeyMismatch */ $_POST['key'];
+ }
+}
+
+function test_function_name_used_as_constant() {
+ echo ARRAY_KEY_EXISTS;
+ echo /* testFunctionNameUsedAsConstant */ $_POST['key'];
+}
+
+function test_function_call_in_attribute() {
+ #[array_key_exists('key', $_POST)]
+ echo /* testFunctionCallInAttribute */ $_POST['key'];
+}
+
+function test_function_call_non_global() {
+ if (
+ $obj->array_key_exists( 'key', $_POST )
+ || $obj?->key_exists( 'key', $_POST )
+ || MyClass::array_key_exists( 'key', $_POST )
+ ) {
+ echo /* testFunctionCallNonGlobal */ $_POST['key'];
+ }
+}
+
+function test_function_call_namespaced() {
+ if (
+ MyNamespace\array_key_exists( 'key', $_POST )
+ || \MyNamespace\key_exists( 'key', $_POST )
+ || namespace\array_key_exists( 'key', $_POST ) // This should be considered validated in the future once the method is able to resolve relative namespaces.
+ || namespace\Sub\key_exists( 'key', $_POST )
+ ) {
+ echo /* testFunctionCallNamespaced */ $_POST['key'];
+ }
+}
+
+function test_function_call_missing_parameters() {
+ if ( key_exists() ) {
+ echo /* testFunctionCallMissingParameters */ $_POST['key'];
+ }
+}
+
+function test_function_call_wrong_array_param() {
+ if (
+ key_exists( 'key', SOME_CONSTANT )
+ || array_key_exists( 'key', $_GET )
+ ) {
+ echo /* testFunctionCallWrongArrayParam */ $_POST['key'];
+ }
+}
+
+function test_function_call_mismatched_key() {
+ if ( array_key_exists( 'other', $_POST ) ) {
+ echo /* testFunctionCallMismatchedKey */ $_POST['key'];
+ }
+}
+
+function test_coalesce_no_match() {
+ $a = 'something' ?? 'default';
+ $_GET['key'] ??= 'default';
+ $_POST['other'] ??= 'default';
+ echo /* testCoalesceNoMatch */ $_POST['key'];
+}
+
+/*
+ * The below should be considered validated.
+ */
+
+function test_validated_with_isset() {
+ if ( isset( $_POST["key"] ) ) {
+ echo /* testValidatedWithIsset */ $_POST['key'];
+ }
+}
+
+function test_validated_with_empty() {
+ if ( empty( $_POST['key'] ) ) {
+ echo /* testValidatedWithEmpty */ $_POST['key'];
+ }
+}
+
+$closure = function() {
+ if ( empty( $_POST['key'] ) ) {
+ echo /* testValidatedInClosure */ $_POST['key'];
+ }
+};
+
+function test_function_call() {
+ if ( array_key_exists( 'key', $_POST ) ) {
+ echo /* testFunctionCall */ $_POST['key'];
+ }
+}
+
+function test_function_call_mixed_case() {
+ if ( Key_Exists( 'key', $_POST ) ) {
+ echo /* testFunctionCallMixedCase */ $_POST['key'];
+ }
+}
+
+function test_function_call_fully_qualified() {
+ if ( \array_key_exists( 'key', $_POST ) ) {
+ echo /* testFunctionCallFullyQualified */ $_POST['key'];
+ }
+}
+
+function test_function_call_fully_qualified_uppercase() {
+ if ( \KEY_EXISTS( 'key', $_POST ) ) {
+ echo /* testFunctionCallFullyQualifiedUppercase */ $_POST['key'];
+ }
+}
+
+function test_validated_with_null_coalesce() {
+ $_POST['key'] = $_POST['key'] ?? 'default';
+ echo /* testValidatedWithNullCoalesce */ $_POST['key'];
+}
+
+function test_validated_with_coalesce_equal() {
+ $_POST['key'] ??= 'default';
+ echo /* testValidatedWithCoalesceEqual */ $_POST['key'];
+}
+
+/*
+ * Test cases for `$in_condition_only` set to true.
+ */
+
+function test_in_condition_only_use_inside_condition() {
+ if ( isset( $_POST['key'] ) ) {
+ echo /* testInConditionOnlyUseInsideCondition */ $_POST['key'];
+ }
+}
+
+function test_in_condition_only_use_outside_condition() {
+ if ( empty( $_POST['key'] ) ) {
+ return;
+ }
+ echo /* testInConditionOnlyUseOutsideCondition */ $_POST['key'];
+}
+
+/*
+ * Test cases for multi-level array key matching across validation paths.
+ */
+
+function test_array_keys_construct() {
+ if ( isset( $_POST['key']['sub'] ) ) {
+ echo /* testArrayKeysConstruct */ $_POST['key']['sub'];
+ }
+}
+
+function test_array_keys_function_call() {
+ if ( array_key_exists( 'sub', $_POST['key'] ) ) {
+ echo /* testArrayKeysFunctionCall */ $_POST['key']['sub'];
+ }
+}
+
+function test_array_keys_function_call_key_param_mismatch() {
+ if ( array_key_exists( 'wrong_key', $_POST['key'] ) ) {
+ echo /* testArrayKeysFunctionCallKeyParamMismatch */ $_POST['key']['sub'];
+ }
+}
+
+function test_array_keys_coalesce() {
+ $_POST['key']['sub'] = $_POST['key']['sub'] ?? 'default';
+ echo /* testArrayKeysCoalesce */ $_POST['key']['sub'];
+}
diff --git a/WordPress/Tests/Helpers/ValidationHelper/IsValidatedUnitTest.php b/WordPress/Tests/Helpers/ValidationHelper/IsValidatedUnitTest.php
new file mode 100644
index 0000000000..1169847f31
--- /dev/null
+++ b/WordPress/Tests/Helpers/ValidationHelper/IsValidatedUnitTest.php
@@ -0,0 +1,439 @@
+assertFalse( ValidationHelper::is_validated( self::$phpcsFile, -1 ) );
+ }
+
+ /**
+ * Test is_validated() handles live coding / parse error situations.
+ *
+ * @dataProvider dataIsValidatedLiveCoding
+ *
+ * @param string $testCaseFile The test case file to parse.
+ *
+ * @return void
+ */
+ public function testIsValidatedLiveCoding( $testCaseFile ) {
+ $this->assertIsValidatedInFile( $testCaseFile, false );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @see testIsValidatedLiveCoding()
+ *
+ * @return array>
+ */
+ public static function dataIsValidatedLiveCoding() {
+ return array(
+ 'construct_not_followed_by_parenthesis' => array(
+ 'testCaseFile' => 'IsValidatedConstructNotFollowedByParenthesisUnitTest.inc',
+ ),
+ 'construct_unclosed_parenthesis' => array(
+ 'testCaseFile' => 'IsValidatedConstructUnclosedParenthesisUnitTest.inc',
+ ),
+ );
+ }
+
+ /**
+ * Test is_validated() correctly respects scope boundaries between file scope and function scope.
+ *
+ * @dataProvider dataIsValidatedScopeBoundaries
+ *
+ * @param string $testCaseFile The test case file to parse.
+ * @param bool $expectedResult The expected return value.
+ *
+ * @return void
+ */
+ public function testIsValidatedScopeBoundaries( $testCaseFile, $expectedResult ) {
+ $this->assertIsValidatedInFile( $testCaseFile, $expectedResult );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @see testIsValidatedScopeBoundaries()
+ *
+ * @return array>
+ */
+ public static function dataIsValidatedScopeBoundaries() {
+ return array(
+ 'function_validation_not_seen_at_file_scope' => array(
+ 'testCaseFile' => 'IsValidatedFunctionValidationNotSeenAtFileScopeUnitTest.inc',
+ 'expectedResult' => false,
+ ),
+ 'outer_validation_not_seen_in_function' => array(
+ 'testCaseFile' => 'IsValidatedOuterValidationNotSeenInFunctionUnitTest.inc',
+ 'expectedResult' => false,
+ ),
+ 'validated_at_file_scope' => array(
+ 'testCaseFile' => 'IsValidatedValidatedAtFileScopeUnitTest.inc',
+ 'expectedResult' => true,
+ ),
+ );
+ }
+
+ /**
+ * Test is_validated() with default parameter for $in_condition_only and a single $array_keys entry.
+ *
+ * @dataProvider dataIsValidated
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param bool $expectedResult The expected return value.
+ *
+ * @return void
+ */
+ public function testIsValidated( $testMarker, $expectedResult ) {
+ $stackPtr = $this->getTargetToken( $testMarker, \T_VARIABLE );
+ $result = ValidationHelper::is_validated( self::$phpcsFile, $stackPtr, array( 'key' ) );
+
+ $this->assertSame( $expectedResult, $result );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @see testIsValidated()
+ *
+ * @return array>
+ */
+ public static function dataIsValidated() {
+ return array(
+ // Cases that should return false.
+ 'not_validated' => array(
+ 'testMarker' => '/* testNotValidated */',
+ 'expectedResult' => false,
+ ),
+ 'outer_function_validation_not_counted' => array(
+ 'testMarker' => '/* testOuterFunctionValidationNotCounted */',
+ 'expectedResult' => false,
+ ),
+ 'closed_scope_validation_not_counted' => array(
+ 'testMarker' => '/* testClosedScopeValidationNotCounted */',
+ 'expectedResult' => false,
+ ),
+ 'arrow_function_validation_not_counted' => array(
+ 'testMarker' => '/* testArrowFunctionValidationNotCounted */',
+ 'expectedResult' => false,
+ ),
+ 'construct_wrong_variable_or_key' => array(
+ 'testMarker' => '/* testConstructWrongVariableOrKey */',
+ 'expectedResult' => false,
+ ),
+ 'function_name_used_as_constant' => array(
+ 'testMarker' => '/* testFunctionNameUsedAsConstant */',
+ 'expectedResult' => false,
+ ),
+ 'function_call_in_attribute' => array(
+ 'testMarker' => '/* testFunctionCallInAttribute */',
+ 'expectedResult' => false,
+ ),
+ 'function_call_non_global' => array(
+ 'testMarker' => '/* testFunctionCallNonGlobal */',
+ 'expectedResult' => false,
+ ),
+ 'function_call_namespaced' => array(
+ 'testMarker' => '/* testFunctionCallNamespaced */',
+ 'expectedResult' => false,
+ ),
+ 'function_call_missing_parameters' => array(
+ 'testMarker' => '/* testFunctionCallMissingParameters */',
+ 'expectedResult' => false,
+ ),
+ 'function_call_wrong_array_param' => array(
+ 'testMarker' => '/* testFunctionCallWrongArrayParam */',
+ 'expectedResult' => false,
+ ),
+ 'function_call_mismatched_key' => array(
+ 'testMarker' => '/* testFunctionCallMismatchedKey */',
+ 'expectedResult' => false,
+ ),
+ 'coalesce_no_match' => array(
+ 'testMarker' => '/* testCoalesceNoMatch */',
+ 'expectedResult' => false,
+ ),
+
+ // Cases that should return true.
+ 'validated_with_isset' => array(
+ 'testMarker' => '/* testValidatedWithIsset */',
+ 'expectedResult' => true,
+ ),
+ 'validated_with_empty' => array(
+ 'testMarker' => '/* testValidatedWithEmpty */',
+ 'expectedResult' => true,
+ ),
+ 'validated_in_closure' => array(
+ 'testMarker' => '/* testValidatedInClosure */',
+ 'expectedResult' => true,
+ ),
+ 'function_call' => array(
+ 'testMarker' => '/* testFunctionCall */',
+ 'expectedResult' => true,
+ ),
+ 'function_call_mixed_case' => array(
+ 'testMarker' => '/* testFunctionCallMixedCase */',
+ 'expectedResult' => true,
+ ),
+ 'function_call_fully_qualified' => array(
+ 'testMarker' => '/* testFunctionCallFullyQualified */',
+ 'expectedResult' => true,
+ ),
+ 'function_call_fully_qualified_uppercase' => array(
+ 'testMarker' => '/* testFunctionCallFullyQualifiedUppercase */',
+ 'expectedResult' => true,
+ ),
+ 'validated_with_null_coalesce' => array(
+ 'testMarker' => '/* testValidatedWithNullCoalesce */',
+ 'expectedResult' => true,
+ ),
+ 'validated_with_coalesce_equal' => array(
+ 'testMarker' => '/* testValidatedWithCoalesceEqual */',
+ 'expectedResult' => true,
+ ),
+ );
+ }
+
+ /**
+ * Test is_validated() with $in_condition_only set to true returns false when the variable
+ * is not inside a condition or the condition has no parentheses.
+ *
+ * @dataProvider dataIsValidatedInConditionOnlyReturnsFalse
+ *
+ * @param string $testCaseFile The test case file to parse.
+ *
+ * @return void
+ */
+ public function testIsValidatedInConditionOnlyReturnsFalse( $testCaseFile ) {
+ $this->assertIsValidatedInFile( $testCaseFile, false, true );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @see testIsValidatedInConditionOnlyReturnsFalse()
+ *
+ * @return array>
+ */
+ public static function dataIsValidatedInConditionOnlyReturnsFalse() {
+ return array(
+ 'no_condition' => array(
+ 'testCaseFile' => 'IsValidatedValidatedAtFileScopeUnitTest.inc',
+ ),
+ 'condition_without_parentheses' => array(
+ 'testCaseFile' => 'IsValidatedInConditionOnlyNoParenthesisUnitTest.inc',
+ ),
+ );
+ }
+
+ /**
+ * Test is_validated() with $in_condition_only set to true.
+ *
+ * @dataProvider dataIsValidatedInConditionOnly
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param bool $expectedResult The expected return value.
+ *
+ * @return void
+ */
+ public function testIsValidatedInConditionOnly( $testMarker, $expectedResult ) {
+ $stackPtr = $this->getTargetToken( $testMarker, \T_VARIABLE );
+ $result = ValidationHelper::is_validated( self::$phpcsFile, $stackPtr, array( 'key' ), true );
+
+ $this->assertSame( $expectedResult, $result );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @see testIsValidatedInConditionOnly()
+ *
+ * @return array>
+ */
+ public static function dataIsValidatedInConditionOnly() {
+ return array(
+ 'use_outside_condition' => array(
+ 'testMarker' => '/* testInConditionOnlyUseOutsideCondition */',
+ 'expectedResult' => false,
+ ),
+ 'use_inside_condition' => array(
+ 'testMarker' => '/* testInConditionOnlyUseInsideCondition */',
+ 'expectedResult' => true,
+ ),
+ );
+ }
+
+ /**
+ * Test is_validated() multi-level array key matching across validation paths.
+ *
+ * @dataProvider dataIsValidatedArrayKeys
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param bool $expectedResult The expected return value.
+ * @param array|string $array_keys The array keys to check for.
+ *
+ * @return void
+ */
+ public function testIsValidatedArrayKeys( $testMarker, $expectedResult, $array_keys ) {
+ $stackPtr = $this->getTargetToken( $testMarker, \T_VARIABLE );
+ $result = ValidationHelper::is_validated( self::$phpcsFile, $stackPtr, $array_keys );
+
+ $this->assertSame( $expectedResult, $result );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @see testIsValidatedArrayKeys()
+ *
+ * @return array|bool|string>>
+ */
+ public static function dataIsValidatedArrayKeys() {
+ return array(
+ 'construct_subset' => array(
+ 'testMarker' => '/* testArrayKeysConstruct */',
+ 'expectedResult' => true,
+ 'array_keys' => array( 'key' ),
+ ),
+ 'construct_exact' => array(
+ 'testMarker' => '/* testArrayKeysConstruct */',
+ 'expectedResult' => true,
+ 'array_keys' => array( 'key', 'sub' ),
+ ),
+ 'construct_superset' => array(
+ 'testMarker' => '/* testArrayKeysConstruct */',
+ 'expectedResult' => false,
+ 'array_keys' => array( 'key', 'sub', 'deeper' ),
+ ),
+ 'construct_wrong_order' => array(
+ 'testMarker' => '/* testArrayKeysConstruct */',
+ 'expectedResult' => false,
+ 'array_keys' => array( 'sub', 'key' ),
+ ),
+ 'construct_string_key_valid' => array(
+ 'testMarker' => '/* testArrayKeysConstruct */',
+ 'expectedResult' => true,
+ 'array_keys' => 'key',
+ ),
+ 'construct_string_key_invalid' => array(
+ 'testMarker' => '/* testArrayKeysConstruct */',
+ 'expectedResult' => false,
+ 'array_keys' => 'other',
+ ),
+ 'construct_key_mismatch' => array(
+ 'testMarker' => '/* testConstructParamKeyMismatch */',
+ 'expectedResult' => false,
+ 'array_keys' => array( 'other' ),
+ ),
+ 'construct_empty_array_keys' => array(
+ 'testMarker' => '/* testValidatedWithIsset */',
+ 'expectedResult' => true,
+ 'array_keys' => array(),
+ ),
+ 'function_call_all_in_array_param' => array(
+ 'testMarker' => '/* testArrayKeysFunctionCall */',
+ 'expectedResult' => true,
+ 'array_keys' => array( 'key' ),
+ ),
+ 'function_call_split_keys' => array(
+ 'testMarker' => '/* testArrayKeysFunctionCall */',
+ 'expectedResult' => true,
+ 'array_keys' => array( 'key', 'sub' ),
+ ),
+ 'function_call_superset' => array(
+ 'testMarker' => '/* testArrayKeysFunctionCall */',
+ 'expectedResult' => false,
+ 'array_keys' => array( 'key', 'sub', 'deeper' ),
+ ),
+ 'function_call_wrong_order' => array(
+ 'testMarker' => '/* testArrayKeysFunctionCall */',
+ 'expectedResult' => false,
+ 'array_keys' => array( 'sub', 'key' ),
+ ),
+ 'function_call_key_param_mismatch' => array(
+ 'testMarker' => '/* testArrayKeysFunctionCallKeyParamMismatch */',
+ 'expectedResult' => false,
+ 'array_keys' => array( 'key', 'sub' ),
+ ),
+ 'function_call_empty_array_keys' => array(
+ 'testMarker' => '/* testFunctionCallMixedCase */',
+ 'expectedResult' => true,
+ 'array_keys' => array(),
+ ),
+ 'coalesce_subset' => array(
+ 'testMarker' => '/* testArrayKeysCoalesce */',
+ 'expectedResult' => true,
+ 'array_keys' => array( 'key' ),
+ ),
+ 'coalesce_exact' => array(
+ 'testMarker' => '/* testArrayKeysCoalesce */',
+ 'expectedResult' => true,
+ 'array_keys' => array( 'key', 'sub' ),
+ ),
+ 'coalesce_superset' => array(
+ 'testMarker' => '/* testArrayKeysCoalesce */',
+ 'expectedResult' => false,
+ 'array_keys' => array( 'key', 'sub', 'deeper' ),
+ ),
+ 'coalesce_wrong_order' => array(
+ 'testMarker' => '/* testArrayKeysCoalesce */',
+ 'expectedResult' => false,
+ 'array_keys' => array( 'sub', 'key' ),
+ ),
+ );
+ }
+
+ /**
+ * Parse a separate .inc file and assert the result of is_validated().
+ *
+ * Temporarily swaps self::$phpcsFile so that getTargetToken() works on the parsed file.
+ *
+ * @param string $testCaseFile The test case file to parse.
+ * @param bool $expectedResult The expected return value.
+ * @param bool $inConditionOnly What to pass as the $in_condition_only parameter.
+ *
+ * @return void
+ */
+ private function assertIsValidatedInFile( $testCaseFile, $expectedResult, $inConditionOnly = false ) {
+ $originalFile = self::$phpcsFile;
+ self::$phpcsFile = self::parseFile(
+ __DIR__ . '/' . $testCaseFile,
+ $originalFile->ruleset,
+ $originalFile->config
+ );
+
+ $stackPtr = $this->getTargetToken( '/* testValidationTarget */', \T_VARIABLE );
+
+ $this->assertSame(
+ $expectedResult,
+ ValidationHelper::is_validated( self::$phpcsFile, $stackPtr, array( 'key' ), $inConditionOnly )
+ );
+
+ self::$phpcsFile = $originalFile;
+ }
+}
diff --git a/WordPress/Tests/Helpers/ValidationHelper/IsValidatedValidatedAtFileScopeUnitTest.inc b/WordPress/Tests/Helpers/ValidationHelper/IsValidatedValidatedAtFileScopeUnitTest.inc
new file mode 100644
index 0000000000..79b2ada2f1
--- /dev/null
+++ b/WordPress/Tests/Helpers/ValidationHelper/IsValidatedValidatedAtFileScopeUnitTest.inc
@@ -0,0 +1,6 @@
+
diff --git a/WordPress/Tests/Helpers/WPDBTrait/IsWpdbMethodCallNoObjectOperatorUnitTest.inc b/WordPress/Tests/Helpers/WPDBTrait/IsWpdbMethodCallNoObjectOperatorUnitTest.inc
new file mode 100644
index 0000000000..5b446081c2
--- /dev/null
+++ b/WordPress/Tests/Helpers/WPDBTrait/IsWpdbMethodCallNoObjectOperatorUnitTest.inc
@@ -0,0 +1,7 @@
+prepare
diff --git a/WordPress/Tests/Helpers/WPDBTrait/IsWpdbMethodCallUnclosedParenthesisUnitTest.inc b/WordPress/Tests/Helpers/WPDBTrait/IsWpdbMethodCallUnclosedParenthesisUnitTest.inc
new file mode 100644
index 0000000000..95898646e3
--- /dev/null
+++ b/WordPress/Tests/Helpers/WPDBTrait/IsWpdbMethodCallUnclosedParenthesisUnitTest.inc
@@ -0,0 +1,7 @@
+prepare(
diff --git a/WordPress/Tests/Helpers/WPDBTrait/IsWpdbMethodCallUnitTest.inc b/WordPress/Tests/Helpers/WPDBTrait/IsWpdbMethodCallUnitTest.inc
new file mode 100644
index 0000000000..de95c66632
--- /dev/null
+++ b/WordPress/Tests/Helpers/WPDBTrait/IsWpdbMethodCallUnitTest.inc
@@ -0,0 +1,51 @@
+prepare( 'SELECT * FROM table' );
+/* testNotWpdbClass */
+DB::esc_like( $find );
+$array[] = /* testWpdbNotMethodCall */ $wpdb;
+
+/* testPropertyAccess */
+echo $wpdb->num_queries /* testPropertyAccessOpenParen */ ;
+
+/* testFunctionCall */
+$result = wpdb( 'SELECT * FROM table' );
+/* testNotTargetMethod */
+$wpdb->insert /* testNotTargetMethodOpenParen */ ( 'table', array( 'name' => 'value' ) );
+/* testUppercaseWpdbVariable */
+$WPDB->prepare( 'SELECT * FROM table' );
+
+/* testVariableMethodCall */
+$wpdb->$prepare /* testVariableMethodCallOpenParen */ ( 'SELECT * FROM table' );
+
+/* testPartiallyQualified */
+MyNamespace\wpdb::esc_like /* testPartiallyQualifiedOpenParen */ ( $find );
+/* testFullyQualifiedNamespaced */
+\MyNamespace\wpdb::esc_like /* testFullyQualifiedNamespacedOpenParen */ ( $find );
+/* testNamespaceRelative */
+namespace\wpdb::esc_like /* testNamespaceRelativeOpenParen */ ( $find ); // The method should start recognizing this as a WPDB method call once it can resolve relative namespaces since this test file is not namespaced.
+/* testNamespaceRelativeSub */
+namespace\Sub\wpdb::esc_like /* testNamespaceRelativeSubOpenParen */ ( $find );
+my_function( /* testNotStringOrVariable */ )->prepare /* testNotStringOrVariableOpenParen */ ( 'SELECT * FROM table' );
+
+/*
+ * The below should be recognized as a $wpdb method call.
+ */
+
+/* testObjectOperator */
+$wpdb->prepare /* testObjectOperatorOpenParen */ ( 'SELECT * FROM table WHERE id = %d', $id );
+/* testNullsafeObjectOperator */
+$wpdb?->PREPARE /* testNullsafeObjectOperatorOpenParen */ ( 'SELECT * FROM table WHERE id = %d', $id );
+/* testUnqualifiedClassUppercase */
+WPDB::esc_like /* testUnqualifiedClassUppercaseOpenParen */ ( $find );
+/* testUnqualifiedClassLowercase */
+wpdb::esc_like /* testUnqualifiedClassLowercaseOpenParen */ ( $find );
+/* testFullyQualifiedGlobalLowercase */
+\wpdb::esc_like /* testFullyQualifiedGlobalLowercaseOpenParen */ ( $find );
+/* testFullyQualifiedGlobalUppercase */
+\WPDB /* comment */ :: /* comment */ esc_like /* testFullyQualifiedGlobalUppercaseOpenParen */ ( $find );
diff --git a/WordPress/Tests/Helpers/WPDBTrait/IsWpdbMethodCallUnitTest.php b/WordPress/Tests/Helpers/WPDBTrait/IsWpdbMethodCallUnitTest.php
new file mode 100644
index 0000000000..0d438f7ce5
--- /dev/null
+++ b/WordPress/Tests/Helpers/WPDBTrait/IsWpdbMethodCallUnitTest.php
@@ -0,0 +1,337 @@
+testClass = new WPDBTraitWrapper();
+ }
+
+ /**
+ * Test is_wpdb_method_call() returns false if the token does not exist.
+ *
+ * @return void
+ */
+ public function testIsWpdbMethodCallReturnsFalseIfTokenDoesNotExist() {
+ $result = $this->testClass->invoke_is_wpdb_method_call(
+ self::$phpcsFile,
+ -1,
+ array( 'prepare' => true )
+ );
+
+ $this->assertFalse( $result );
+ }
+
+ /**
+ * Test is_wpdb_method_call() returns false when $target_methods is empty.
+ *
+ * @return void
+ */
+ public function testIsWpdbMethodCallReturnsFalseWithEmptyTargetMethods() {
+ $stackPtr = $this->getTargetToken( '/* testObjectOperator */', \T_VARIABLE );
+ $result = $this->testClass->invoke_is_wpdb_method_call(
+ self::$phpcsFile,
+ $stackPtr,
+ array()
+ );
+
+ $this->assertFalse( $result );
+ }
+
+ /**
+ * Test is_wpdb_method_call() returns false for live coding / parse error situations.
+ *
+ * @dataProvider dataIsWpdbMethodCallLiveCoding
+ *
+ * @param string $caseFile The test case file to parse.
+ *
+ * @return void
+ */
+ public function testIsWpdbMethodCallLiveCoding( $caseFile ) {
+ $phpcsFile = self::parseFile(
+ __DIR__ . '/' . $caseFile,
+ self::$phpcsFile->ruleset,
+ self::$phpcsFile->config
+ );
+
+ $stackPtr = $phpcsFile->findNext( \T_VARIABLE, 0 );
+ $this->assertNotFalse( $stackPtr );
+
+ $result = $this->testClass->invoke_is_wpdb_method_call(
+ $phpcsFile,
+ $stackPtr,
+ array( 'prepare' => true )
+ );
+
+ $this->assertFalse( $result );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @see testIsWpdbMethodCallLiveCoding()
+ *
+ * @return array>
+ */
+ public static function dataIsWpdbMethodCallLiveCoding() {
+ return array(
+ 'no_object_operator' => array(
+ 'caseFile' => 'IsWpdbMethodCallNoObjectOperatorUnitTest.inc',
+ ),
+ 'no_method_name' => array(
+ 'caseFile' => 'IsWpdbMethodCallNoMethodNameUnitTest.inc',
+ ),
+ 'no_opening_parenthesis' => array(
+ 'caseFile' => 'IsWpdbMethodCallNoOpeningParenthesisUnitTest.inc',
+ ),
+ 'unclosed_parenthesis' => array(
+ 'caseFile' => 'IsWpdbMethodCallUnclosedParenthesisUnitTest.inc',
+ ),
+ );
+ }
+
+ /**
+ * Test is_wpdb_method_call().
+ *
+ * @dataProvider dataIsWpdbMethodCall
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param bool $expectedResult The expected return value.
+ * @param int|string $tokenType The token type to search for.
+ * @param string|null $tokenContent The token content to search for (if applicable).
+ * @param bool $hasMethodPtr Whether the methodPtr property should be set.
+ * @param string|bool $openParenMarker The test marker for the expected i token, or false if not expected to be set.
+ * @param bool $hasEnd Whether the end property should be set.
+ *
+ * @return void
+ */
+ public function testIsWpdbMethodCall(
+ $testMarker,
+ $expectedResult,
+ $tokenType,
+ $tokenContent = null,
+ $hasMethodPtr = false,
+ $openParenMarker = false,
+ $hasEnd = false
+ ) {
+ $stackPtr = $this->getTargetToken( $testMarker, $tokenType, $tokenContent );
+ $result = $this->testClass->invoke_is_wpdb_method_call(
+ self::$phpcsFile,
+ $stackPtr,
+ array(
+ 'prepare' => true,
+ 'esc_like' => true,
+ )
+ );
+
+ $this->assertSame( $expectedResult, $result );
+
+ if ( true === $hasMethodPtr ) {
+ $expectedPtr = self::$phpcsFile->findNext( \T_STRING, ( $stackPtr + 1 ) );
+ $this->assertSame( $expectedPtr, $this->testClass->methodPtr );
+ } else {
+ $this->assertNull( $this->testClass->methodPtr );
+ }
+
+ if ( false !== $openParenMarker ) {
+ $expectedIPtr = $this->getTargetToken( $openParenMarker, array( \T_OPEN_PARENTHESIS, \T_SEMICOLON ) );
+ $this->assertSame( $expectedIPtr, $this->testClass->i );
+ } else {
+ $this->assertNull( $this->testClass->i );
+ }
+
+ // Note: the exact value of end is not verified because it depends on the result of
+ // BCFile::findEndOfStatement() which would require reimplementing the method's logic in the test.
+ if ( true === $hasEnd ) {
+ $this->assertIsInt( $this->testClass->end );
+ } else {
+ $this->assertNull( $this->testClass->end );
+ }
+ }
+
+ /**
+ * Data provider.
+ *
+ * @see testIsWpdbMethodCall()
+ *
+ * @return array>
+ */
+ public static function dataIsWpdbMethodCall() {
+ $isPhpcs3 = version_compare( Helper::getVersion(), '3.99.99', '<=' );
+ $tokenContent = null;
+
+ if ( $isPhpcs3 ) {
+ $tokenContent = 'wpdb';
+ }
+
+ return array(
+ // Cases that should return false.
+ 'not_wpdb_variable' => array(
+ 'testMarker' => '/* testNotWpdbVariable */',
+ 'expectedResult' => false,
+ 'tokenType' => \T_VARIABLE,
+ ),
+ 'not_wpdb_class' => array(
+ 'testMarker' => '/* testNotWpdbClass */',
+ 'expectedResult' => false,
+ 'tokenType' => \T_STRING,
+ ),
+ 'wpdb_not_method_call' => array(
+ 'testMarker' => '/* testWpdbNotMethodCall */',
+ 'expectedResult' => false,
+ 'tokenType' => \T_VARIABLE,
+ ),
+ 'property_access' => array(
+ 'testMarker' => '/* testPropertyAccess */',
+ 'expectedResult' => false,
+ 'tokenType' => \T_VARIABLE,
+ 'tokenContent' => null,
+ 'hasMethodPtr' => true,
+ 'openParenMarker' => '/* testPropertyAccessOpenParen */',
+ ),
+ 'function_call' => array(
+ 'testMarker' => '/* testFunctionCall */',
+ 'expectedResult' => false,
+ 'tokenType' => \T_STRING,
+ ),
+ 'partially_qualified' => array(
+ 'testMarker' => '/* testPartiallyQualified */',
+ 'expectedResult' => false,
+ 'tokenType' => $isPhpcs3 ? \T_STRING : \T_NAME_QUALIFIED,
+ 'tokenContent' => $tokenContent,
+ ),
+ 'fully_qualified_namespaced' => array(
+ 'testMarker' => '/* testFullyQualifiedNamespaced */',
+ 'expectedResult' => false,
+ 'tokenType' => $isPhpcs3 ? \T_STRING : \T_NAME_FULLY_QUALIFIED,
+ 'tokenContent' => $tokenContent,
+ ),
+ 'namespace_relative' => array(
+ 'testMarker' => '/* testNamespaceRelative */',
+ 'expectedResult' => false,
+ 'tokenType' => $isPhpcs3 ? \T_STRING : \T_NAME_RELATIVE,
+ 'tokenContent' => $tokenContent,
+ ),
+ 'namespace_relative_sub' => array(
+ 'testMarker' => '/* testNamespaceRelativeSub */',
+ 'expectedResult' => false,
+ 'tokenType' => $isPhpcs3 ? \T_STRING : \T_NAME_RELATIVE,
+ 'tokenContent' => $tokenContent,
+ ),
+ 'not_target_method' => array(
+ 'testMarker' => '/* testNotTargetMethod */',
+ 'expectedResult' => false,
+ 'tokenType' => \T_VARIABLE,
+ 'tokenContent' => null,
+ 'hasMethodPtr' => true,
+ 'openParenMarker' => '/* testNotTargetMethodOpenParen */',
+ ),
+ 'uppercase_wpdb_variable' => array(
+ 'testMarker' => '/* testUppercaseWpdbVariable */',
+ 'expectedResult' => false,
+ 'tokenType' => \T_VARIABLE,
+ ),
+ 'variable_method_call' => array(
+ 'testMarker' => '/* testVariableMethodCall */',
+ 'expectedResult' => false,
+ 'tokenType' => \T_VARIABLE,
+ 'tokenContent' => '$wpdb',
+ 'hasMethodPtr' => false,
+ 'openParenMarker' => '/* testVariableMethodCallOpenParen */',
+ ),
+ 'not_string_or_variable' => array(
+ 'testMarker' => '/* testNotStringOrVariable */',
+ 'expectedResult' => false,
+ 'tokenType' => \T_CLOSE_PARENTHESIS,
+ ),
+
+ // Cases that should return true.
+ 'object_operator' => array(
+ 'testMarker' => '/* testObjectOperator */',
+ 'expectedResult' => true,
+ 'tokenType' => \T_VARIABLE,
+ 'tokenContent' => null,
+ 'hasMethodPtr' => true,
+ 'openParenMarker' => '/* testObjectOperatorOpenParen */',
+ 'hasEnd' => true,
+ ),
+ 'nullsafe_object_operator' => array(
+ 'testMarker' => '/* testNullsafeObjectOperator */',
+ 'expectedResult' => true,
+ 'tokenType' => \T_VARIABLE,
+ 'tokenContent' => null,
+ 'hasMethodPtr' => true,
+ 'openParenMarker' => '/* testNullsafeObjectOperatorOpenParen */',
+ 'hasEnd' => true,
+ ),
+ 'unqualified_class_uppercase' => array(
+ 'testMarker' => '/* testUnqualifiedClassUppercase */',
+ 'expectedResult' => true,
+ 'tokenType' => \T_STRING,
+ 'tokenContent' => null,
+ 'hasMethodPtr' => true,
+ 'openParenMarker' => '/* testUnqualifiedClassUppercaseOpenParen */',
+ 'hasEnd' => true,
+ ),
+ 'unqualified_class_lowercase' => array(
+ 'testMarker' => '/* testUnqualifiedClassLowercase */',
+ 'expectedResult' => true,
+ 'tokenType' => \T_STRING,
+ 'tokenContent' => null,
+ 'hasMethodPtr' => true,
+ 'openParenMarker' => '/* testUnqualifiedClassLowercaseOpenParen */',
+ 'hasEnd' => true,
+ ),
+ 'fully_qualified_global_lowercase' => array(
+ 'testMarker' => '/* testFullyQualifiedGlobalLowercase */',
+ 'expectedResult' => true,
+ 'tokenType' => $isPhpcs3 ? \T_STRING : \T_NAME_FULLY_QUALIFIED,
+ 'tokenContent' => null,
+ 'hasMethodPtr' => true,
+ 'openParenMarker' => '/* testFullyQualifiedGlobalLowercaseOpenParen */',
+ 'hasEnd' => true,
+ ),
+ 'fully_qualified_global_uppercase' => array(
+ 'testMarker' => '/* testFullyQualifiedGlobalUppercase */',
+ 'expectedResult' => true,
+ 'tokenType' => $isPhpcs3 ? \T_STRING : \T_NAME_FULLY_QUALIFIED,
+ 'tokenContent' => null,
+ 'hasMethodPtr' => true,
+ 'openParenMarker' => '/* testFullyQualifiedGlobalUppercaseOpenParen */',
+ 'hasEnd' => true,
+ ),
+ );
+ }
+}
diff --git a/WordPress/Tests/Helpers/WPDBTrait/WPDBTraitWrapper.php b/WordPress/Tests/Helpers/WPDBTrait/WPDBTraitWrapper.php
new file mode 100644
index 0000000000..2e8823e110
--- /dev/null
+++ b/WordPress/Tests/Helpers/WPDBTrait/WPDBTraitWrapper.php
@@ -0,0 +1,57 @@
+is_wpdb_method_call( $phpcsFile, $stackPtr, $targetMethods );
+ }
+}
diff --git a/WordPress/Tests/Helpers/WPHookHelper/GetHookNameParamUnitTest.inc b/WordPress/Tests/Helpers/WPHookHelper/GetHookNameParamUnitTest.inc
index e1992e34b8..83ee36e7ca 100644
--- a/WordPress/Tests/Helpers/WPHookHelper/GetHookNameParamUnitTest.inc
+++ b/WordPress/Tests/Helpers/WPHookHelper/GetHookNameParamUnitTest.inc
@@ -22,3 +22,6 @@ aPpLy_FiLtErS( 'my_filter', $value );
/* testNamedParameter */
do_action_deprecated( args: $args, hook_name: 'my_action' );
+
+/* testFullyQualifiedName */
+\apply_filters( 'my_action', $value );
diff --git a/WordPress/Tests/Helpers/WPHookHelper/GetHookNameParamUnitTest.php b/WordPress/Tests/Helpers/WPHookHelper/GetHookNameParamUnitTest.php
index e90cef82a3..de839931d7 100644
--- a/WordPress/Tests/Helpers/WPHookHelper/GetHookNameParamUnitTest.php
+++ b/WordPress/Tests/Helpers/WPHookHelper/GetHookNameParamUnitTest.php
@@ -9,6 +9,7 @@
namespace WordPressCS\WordPress\Tests\Helpers\WPHookHelper;
+use PHPCSUtils\BackCompat\Helper;
use PHPCSUtils\TestUtils\UtilityMethodTestCase;
use PHPCSUtils\Utils\PassedParameters;
use WordPressCS\WordPress\Helpers\WPHookHelper;
@@ -30,11 +31,12 @@ final class GetHookNameParamUnitTest extends UtilityMethodTestCase {
* @param string $testMarker The comment which prefaces the target token in the test file.
* @param string|false $expectedResult The raw content of the expected hook name parameter,
* or `false` when no hook name parameter is expected.
+ * @param int|string $tokenType Optional. The token type to search for. Defaults to `T_STRING`.
*
* @return void
*/
- public function testGetHookNameParam( $testMarker, $expectedResult ) {
- $stackPtr = $this->getTargetToken( $testMarker, \T_STRING );
+ public function testGetHookNameParam( $testMarker, $expectedResult, $tokenType = \T_STRING ) {
+ $stackPtr = $this->getTargetToken( $testMarker, $tokenType );
$functionName = self::$phpcsFile->getTokens()[ $stackPtr ]['content'];
$parameters = PassedParameters::getParameters( self::$phpcsFile, $stackPtr );
@@ -54,9 +56,12 @@ public function testGetHookNameParam( $testMarker, $expectedResult ) {
*
* @see testGetHookNameParam()
*
- * @return array>
+ * @return array>
*/
public static function dataGetHookNameParam() {
+ $phpcs_version = Helper::getVersion();
+ $is_phpcs_4 = version_compare( $phpcs_version, '3.99.99', '>' );
+
return array(
'not_a_hook_function' => array(
'testMarker' => '/* testNotAHookFunction */',
@@ -78,6 +83,11 @@ public static function dataGetHookNameParam() {
'testMarker' => '/* testNamedParameter */',
'expectedResult' => "'my_action'",
),
+ 'fully_qualified_name' => array(
+ 'testMarker' => '/* testFullyQualifiedName */',
+ 'expectedResult' => "'my_action'",
+ 'tokenType' => ( true === $is_phpcs_4 ? \T_NAME_FULLY_QUALIFIED : \T_STRING ),
+ ),
);
}
}
diff --git a/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.1.inc b/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.1.inc
index 707468c137..83e20721ad 100644
--- a/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.1.inc
+++ b/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.1.inc
@@ -696,3 +696,13 @@ MyNamespace\do_action( 'plugin_action' ); // Ok.
namespace\do_action_ref_array( 'plugin_action', array( $variable ) ); // Ok. The sniff should start flagging this once it can resolve relative namespaces.
// phpcs:set WordPress.NamingConventions.PrefixAllGlobals prefixes[]
+
+/*
+ * Safeguard handling of empty custom test class values (PHPCS 4.x compatibility).
+ * PHPCS >= 4.0 converts empty string values in array properties to null.
+ */
+// phpcs:set WordPress.NamingConventions.PrefixAllGlobals prefixes[] acronym
+// phpcs:set WordPress.NamingConventions.PrefixAllGlobals custom_test_classes[] ,
+class Test_Empty_Custom_Test_Class extends WP_UnitTestCase {}
+// phpcs:set WordPress.NamingConventions.PrefixAllGlobals custom_test_classes[]
+// phpcs:set WordPress.NamingConventions.PrefixAllGlobals prefixes[]
diff --git a/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.php b/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.php
index b93ad6216d..768b7cb383 100644
--- a/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.php
+++ b/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\NamingConventions;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the PrefixAllGlobals sniff.
@@ -20,7 +20,7 @@
* @covers \WordPressCS\WordPress\Helpers\IsUnitTestTrait
* @covers \WordPressCS\WordPress\Sniffs\NamingConventions\PrefixAllGlobalsSniff
*/
-final class PrefixAllGlobalsUnitTest extends AbstractSniffUnitTest {
+final class PrefixAllGlobalsUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/NamingConventions/ValidFunctionNameUnitTest.php b/WordPress/Tests/NamingConventions/ValidFunctionNameUnitTest.php
index 349091a4d8..8168abde4d 100644
--- a/WordPress/Tests/NamingConventions/ValidFunctionNameUnitTest.php
+++ b/WordPress/Tests/NamingConventions/ValidFunctionNameUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\NamingConventions;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the ValidFunctionName sniff.
@@ -20,7 +20,7 @@
* @covers \WordPressCS\WordPress\Helpers\DeprecationHelper
* @covers \WordPressCS\WordPress\Sniffs\NamingConventions\ValidFunctionNameSniff
*/
-final class ValidFunctionNameUnitTest extends AbstractSniffUnitTest {
+final class ValidFunctionNameUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/NamingConventions/ValidHookNameUnitTest.php b/WordPress/Tests/NamingConventions/ValidHookNameUnitTest.php
index 763760f1c3..9611b68b43 100644
--- a/WordPress/Tests/NamingConventions/ValidHookNameUnitTest.php
+++ b/WordPress/Tests/NamingConventions/ValidHookNameUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\NamingConventions;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the ValidHookName sniff.
@@ -20,7 +20,7 @@
* @covers \WordPressCS\WordPress\Helpers\WPHookHelper::get_functions
* @covers \WordPressCS\WordPress\Sniffs\NamingConventions\ValidHookNameSniff
*/
-final class ValidHookNameUnitTest extends AbstractSniffUnitTest {
+final class ValidHookNameUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/NamingConventions/ValidPostTypeSlugUnitTest.php b/WordPress/Tests/NamingConventions/ValidPostTypeSlugUnitTest.php
index cb2d41c998..fddd4b4bb6 100644
--- a/WordPress/Tests/NamingConventions/ValidPostTypeSlugUnitTest.php
+++ b/WordPress/Tests/NamingConventions/ValidPostTypeSlugUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\NamingConventions;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the PostType sniff.
@@ -18,7 +18,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\NamingConventions\ValidPostTypeSlugSniff
*/
-final class ValidPostTypeSlugUnitTest extends AbstractSniffUnitTest {
+final class ValidPostTypeSlugUnitTest extends AbstractSniffTestCase {
/**
* Set warnings level to 3 to trigger suggestions as warnings.
diff --git a/WordPress/Tests/NamingConventions/ValidVariableNameUnitTest.php b/WordPress/Tests/NamingConventions/ValidVariableNameUnitTest.php
index 0ac884c991..b114521b6e 100644
--- a/WordPress/Tests/NamingConventions/ValidVariableNameUnitTest.php
+++ b/WordPress/Tests/NamingConventions/ValidVariableNameUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\NamingConventions;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the ValidVariableName sniff.
@@ -20,7 +20,7 @@
* @covers \WordPressCS\WordPress\Helpers\SnakeCaseHelper
* @covers \WordPressCS\WordPress\Sniffs\NamingConventions\ValidVariableNameSniff
*/
-final class ValidVariableNameUnitTest extends AbstractSniffUnitTest {
+final class ValidVariableNameUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/PHP/DevelopmentFunctionsUnitTest.php b/WordPress/Tests/PHP/DevelopmentFunctionsUnitTest.php
index f3e953545c..ec151f59ec 100644
--- a/WordPress/Tests/PHP/DevelopmentFunctionsUnitTest.php
+++ b/WordPress/Tests/PHP/DevelopmentFunctionsUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\PHP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the PHP_DevelopmentFunctions sniff.
@@ -19,7 +19,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\PHP\DevelopmentFunctionsSniff
*/
-final class DevelopmentFunctionsUnitTest extends AbstractSniffUnitTest {
+final class DevelopmentFunctionsUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/PHP/DiscouragedPHPFunctionsUnitTest.php b/WordPress/Tests/PHP/DiscouragedPHPFunctionsUnitTest.php
index b9f0e89a1b..5e716650a3 100644
--- a/WordPress/Tests/PHP/DiscouragedPHPFunctionsUnitTest.php
+++ b/WordPress/Tests/PHP/DiscouragedPHPFunctionsUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\PHP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the PHP_DiscouragedPHPFunctions sniff.
@@ -19,7 +19,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\PHP\DiscouragedPHPFunctionsSniff
*/
-final class DiscouragedPHPFunctionsUnitTest extends AbstractSniffUnitTest {
+final class DiscouragedPHPFunctionsUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/PHP/DontExtractUnitTest.php b/WordPress/Tests/PHP/DontExtractUnitTest.php
index 52d6fdeb65..af0dedb48d 100644
--- a/WordPress/Tests/PHP/DontExtractUnitTest.php
+++ b/WordPress/Tests/PHP/DontExtractUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\PHP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the DontExtract sniff.
@@ -20,7 +20,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\PHP\DontExtractSniff
*/
-final class DontExtractUnitTest extends AbstractSniffUnitTest {
+final class DontExtractUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/PHP/IniSetUnitTest.php b/WordPress/Tests/PHP/IniSetUnitTest.php
index 1dd5ca4ab7..e3b78a65a0 100644
--- a/WordPress/Tests/PHP/IniSetUnitTest.php
+++ b/WordPress/Tests/PHP/IniSetUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\PHP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the IniSet sniff.
@@ -18,7 +18,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\PHP\IniSetSniff
*/
-final class IniSetUnitTest extends AbstractSniffUnitTest {
+final class IniSetUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/PHP/NoSilencedErrorsUnitTest.inc b/WordPress/Tests/PHP/NoSilencedErrorsUnitTest.inc
index 27b3598537..88c312744e 100644
--- a/WordPress/Tests/PHP/NoSilencedErrorsUnitTest.inc
+++ b/WordPress/Tests/PHP/NoSilencedErrorsUnitTest.inc
@@ -92,3 +92,11 @@ $file = @MyNS\MyClass::file_get_contents( $file ); // Bad.
$file = @MyNS\MyClass\file_exists( $file ); // Bad.
$file = @namespace\MyNS\MyClass::file( $file ); // Bad.
$file = @namespace\is_dir( $dir ); // The sniff should stop flagging this once it can resolve relative namespaces.
+
+/*
+ * Safeguard handling of empty values in the custom allowed functions list (PHPCS 4.x compatibility).
+ * PHPCS >= 4.0 converts empty string values in array properties to null.
+ */
+// phpcs:set WordPress.PHP.NoSilencedErrors customAllowedFunctionsList[] ,
+$unserialized = @unserialize( $str );
+// phpcs:set WordPress.PHP.NoSilencedErrors customAllowedFunctionsList[]
diff --git a/WordPress/Tests/PHP/NoSilencedErrorsUnitTest.php b/WordPress/Tests/PHP/NoSilencedErrorsUnitTest.php
index 038c2c8d71..f55db69edb 100644
--- a/WordPress/Tests/PHP/NoSilencedErrorsUnitTest.php
+++ b/WordPress/Tests/PHP/NoSilencedErrorsUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\PHP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the PHP.NoSilencedErrors sniff.
@@ -18,7 +18,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\PHP\NoSilencedErrorsSniff
*/
-final class NoSilencedErrorsUnitTest extends AbstractSniffUnitTest {
+final class NoSilencedErrorsUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/PHP/POSIXFunctionsUnitTest.php b/WordPress/Tests/PHP/POSIXFunctionsUnitTest.php
index d4086c3e49..75155bb2bf 100644
--- a/WordPress/Tests/PHP/POSIXFunctionsUnitTest.php
+++ b/WordPress/Tests/PHP/POSIXFunctionsUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\PHP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the POSIXFunctions sniff.
@@ -19,7 +19,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\PHP\POSIXFunctionsSniff
*/
-final class POSIXFunctionsUnitTest extends AbstractSniffUnitTest {
+final class POSIXFunctionsUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/PHP/PregQuoteDelimiterUnitTest.php b/WordPress/Tests/PHP/PregQuoteDelimiterUnitTest.php
index 4c6a254fae..c78b3c1dd0 100644
--- a/WordPress/Tests/PHP/PregQuoteDelimiterUnitTest.php
+++ b/WordPress/Tests/PHP/PregQuoteDelimiterUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\PHP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the PregQuoteDelimiter sniff.
@@ -18,7 +18,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\PHP\PregQuoteDelimiterSniff
*/
-final class PregQuoteDelimiterUnitTest extends AbstractSniffUnitTest {
+final class PregQuoteDelimiterUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/PHP/RestrictedPHPFunctionsUnitTest.php b/WordPress/Tests/PHP/RestrictedPHPFunctionsUnitTest.php
index a596706030..601d711b60 100644
--- a/WordPress/Tests/PHP/RestrictedPHPFunctionsUnitTest.php
+++ b/WordPress/Tests/PHP/RestrictedPHPFunctionsUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\PHP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the PHP_DiscouragedPHPFunctions sniff.
@@ -18,7 +18,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\PHP\RestrictedPHPFunctionsSniff
*/
-final class RestrictedPHPFunctionsUnitTest extends AbstractSniffUnitTest {
+final class RestrictedPHPFunctionsUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/PHP/StrictInArrayUnitTest.php b/WordPress/Tests/PHP/StrictInArrayUnitTest.php
index d7104d7d9b..309f61f32e 100644
--- a/WordPress/Tests/PHP/StrictInArrayUnitTest.php
+++ b/WordPress/Tests/PHP/StrictInArrayUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\PHP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the StrictInArray sniff.
@@ -20,7 +20,7 @@
* @covers \WordPressCS\WordPress\AbstractFunctionParameterSniff
* @covers \WordPressCS\WordPress\Sniffs\PHP\StrictInArraySniff
*/
-final class StrictInArrayUnitTest extends AbstractSniffUnitTest {
+final class StrictInArrayUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/PHP/TypeCastsUnitTest.php b/WordPress/Tests/PHP/TypeCastsUnitTest.php
index 878b089927..9c4ac39198 100644
--- a/WordPress/Tests/PHP/TypeCastsUnitTest.php
+++ b/WordPress/Tests/PHP/TypeCastsUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\PHP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the TypeCasts sniff.
@@ -18,7 +18,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\PHP\TypeCastsSniff
*/
-final class TypeCastsUnitTest extends AbstractSniffUnitTest {
+final class TypeCastsUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/PHP/YodaConditionsUnitTest.php b/WordPress/Tests/PHP/YodaConditionsUnitTest.php
index 3cb8604025..b1cd57c1a4 100644
--- a/WordPress/Tests/PHP/YodaConditionsUnitTest.php
+++ b/WordPress/Tests/PHP/YodaConditionsUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\PHP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the YodaConditions sniff.
@@ -19,7 +19,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\PHP\YodaConditionsSniff
*/
-final class YodaConditionsUnitTest extends AbstractSniffUnitTest {
+final class YodaConditionsUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/Security/EscapeOutputUnitTest.php b/WordPress/Tests/Security/EscapeOutputUnitTest.php
index 7111a1ff13..94dc6623e7 100644
--- a/WordPress/Tests/Security/EscapeOutputUnitTest.php
+++ b/WordPress/Tests/Security/EscapeOutputUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\Security;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the EscapeOutput sniff.
@@ -20,12 +20,11 @@
*
* @covers \WordPressCS\WordPress\Helpers\ArrayWalkingFunctionsHelper
* @covers \WordPressCS\WordPress\Helpers\ContextHelper::get_safe_cast_tokens
- * @covers \WordPressCS\WordPress\Helpers\ConstantsHelper::is_use_of_global_constant
* @covers \WordPressCS\WordPress\Helpers\EscapingFunctionsTrait
* @covers \WordPressCS\WordPress\Helpers\PrintingFunctionsTrait
* @covers \WordPressCS\WordPress\Sniffs\Security\EscapeOutputSniff
*/
-final class EscapeOutputUnitTest extends AbstractSniffUnitTest {
+final class EscapeOutputUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/Security/NonceVerificationUnitTest.php b/WordPress/Tests/Security/NonceVerificationUnitTest.php
index d5ee162048..265248a9ba 100644
--- a/WordPress/Tests/Security/NonceVerificationUnitTest.php
+++ b/WordPress/Tests/Security/NonceVerificationUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\Security;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the NonceVerification sniff.
@@ -20,7 +20,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\Security\NonceVerificationSniff
*/
-final class NonceVerificationUnitTest extends AbstractSniffUnitTest {
+final class NonceVerificationUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/Security/PluginMenuSlugUnitTest.php b/WordPress/Tests/Security/PluginMenuSlugUnitTest.php
index cee8c0e1b6..a18a0fca26 100644
--- a/WordPress/Tests/Security/PluginMenuSlugUnitTest.php
+++ b/WordPress/Tests/Security/PluginMenuSlugUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\Security;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the PluginMenuSlug sniff.
@@ -20,7 +20,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\Security\PluginMenuSlugSniff
*/
-final class PluginMenuSlugUnitTest extends AbstractSniffUnitTest {
+final class PluginMenuSlugUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/Security/SafeRedirectUnitTest.php b/WordPress/Tests/Security/SafeRedirectUnitTest.php
index 8adb348ce6..fb5e2ba52f 100644
--- a/WordPress/Tests/Security/SafeRedirectUnitTest.php
+++ b/WordPress/Tests/Security/SafeRedirectUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\Security;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the Security_SafeRedirect sniff.
@@ -18,7 +18,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\Security\SafeRedirectSniff
*/
-final class SafeRedirectUnitTest extends AbstractSniffUnitTest {
+final class SafeRedirectUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/Security/ValidatedSanitizedInputUnitTest.php b/WordPress/Tests/Security/ValidatedSanitizedInputUnitTest.php
index a5428b1560..192a3f9ca4 100644
--- a/WordPress/Tests/Security/ValidatedSanitizedInputUnitTest.php
+++ b/WordPress/Tests/Security/ValidatedSanitizedInputUnitTest.php
@@ -9,7 +9,8 @@
namespace WordPressCS\WordPress\Tests\Security;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
+use PHPCSUtils\BackCompat\Helper;
/**
* Unit test class for the ValidatedSanitizedInput sniff.
@@ -19,13 +20,11 @@
* @since 1.0.0 This sniff has been moved from the `VIP` category to the `Security` category.
*
* @covers \WordPressCS\WordPress\Helpers\ArrayWalkingFunctionsHelper
- * @covers \WordPressCS\WordPress\Helpers\SanitizationHelperTrait
* @covers \WordPressCS\WordPress\Helpers\UnslashingFunctionsHelper
- * @covers \WordPressCS\WordPress\Helpers\ValidationHelper
* @covers \WordPressCS\WordPress\Helpers\VariableHelper
* @covers \WordPressCS\WordPress\Sniffs\Security\ValidatedSanitizedInputSniff
*/
-final class ValidatedSanitizedInputUnitTest extends AbstractSniffUnitTest {
+final class ValidatedSanitizedInputUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
@@ -35,6 +34,9 @@ final class ValidatedSanitizedInputUnitTest extends AbstractSniffUnitTest {
* @return array Key is the line number, value is the number of expected errors.
*/
public function getErrorList( $testFile = '' ) {
+ $phpcs_version = Helper::getVersion();
+ $is_phpcs_4 = version_compare( $phpcs_version, '3.99.99', '>' );
+
switch ( $testFile ) {
case 'ValidatedSanitizedInputUnitTest.1.inc':
return array(
@@ -127,10 +129,10 @@ public function getErrorList( $testFile = '' ) {
539 => 2,
// The error counts below differ depending on whether running PHPCS 3.x or PHPCS 4.x. See the comment in the test case file.
- 554 => 1,
- 557 => 1,
- 560 => 1,
- 563 => 1,
+ 554 => ( true === $is_phpcs_4 ) ? 2 : 1,
+ 557 => ( true === $is_phpcs_4 ) ? 2 : 1,
+ 560 => ( true === $is_phpcs_4 ) ? 2 : 1,
+ 563 => ( true === $is_phpcs_4 ) ? 2 : 1,
575 => 1,
578 => 1,
diff --git a/WordPress/Tests/Utils/I18nTextDomainFixerStdInTest.xml b/WordPress/Tests/Utils/I18nTextDomainFixerStdInTest.xml
new file mode 100644
index 0000000000..d158d19c59
--- /dev/null
+++ b/WordPress/Tests/Utils/I18nTextDomainFixerStdInTest.xml
@@ -0,0 +1,7 @@
+
+
+ Ruleset for testing I18nTextDomainFixer with STDIN.
+
+
+
+
diff --git a/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.php b/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.php
index 872050aa97..0a4636281f 100644
--- a/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.php
+++ b/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.php
@@ -11,7 +11,7 @@
use PHP_CodeSniffer\Files\DummyFile;
use PHP_CodeSniffer\Ruleset;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
use PHPCSUtils\BackCompat\Helper;
use PHPCSUtils\TestUtils\ConfigDouble;
@@ -23,7 +23,7 @@
* @covers \WordPressCS\WordPress\AbstractFunctionParameterSniff::is_targetted_token
* @covers \WordPressCS\WordPress\Sniffs\Utils\I18nTextDomainFixerSniff
*/
-final class I18nTextDomainFixerUnitTest extends AbstractSniffUnitTest {
+final class I18nTextDomainFixerUnitTest extends AbstractSniffTestCase {
/**
* The tab width to use during testing.
@@ -214,8 +214,7 @@ public function getWarningList( $testFile = '' ) {
public function testStdIn() {
$config = new ConfigDouble();
Helper::setConfigData( 'installed_paths', dirname( dirname( __DIR__ ) ), true, $config );
- $config->standards = array( 'WordPress' );
- $config->sniffs = array( 'WordPress.Utils.I18nTextDomainFixer' );
+ $config->standards = array( __DIR__ . '/I18nTextDomainFixerStdInTest.xml' );
$ruleset = new Ruleset( $config );
diff --git a/WordPress/Tests/WP/AlternativeFunctionsUnitTest.php b/WordPress/Tests/WP/AlternativeFunctionsUnitTest.php
index 58f4083729..f7d85becee 100644
--- a/WordPress/Tests/WP/AlternativeFunctionsUnitTest.php
+++ b/WordPress/Tests/WP/AlternativeFunctionsUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\WP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the WP_AlternativeFunctions sniff.
@@ -20,7 +20,7 @@
* @covers \WordPressCS\WordPress\Helpers\MinimumWPVersionTrait
* @covers \WordPressCS\WordPress\Sniffs\WP\AlternativeFunctionsSniff
*/
-final class AlternativeFunctionsUnitTest extends AbstractSniffUnitTest {
+final class AlternativeFunctionsUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/WP/CapabilitiesUnitTest.php b/WordPress/Tests/WP/CapabilitiesUnitTest.php
index 1b4e34dd05..c2d8768b89 100644
--- a/WordPress/Tests/WP/CapabilitiesUnitTest.php
+++ b/WordPress/Tests/WP/CapabilitiesUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\WP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
use PHPCSUtils\BackCompat\Helper;
/**
@@ -19,7 +19,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\WP\CapabilitiesSniff
*/
-final class CapabilitiesUnitTest extends AbstractSniffUnitTest {
+final class CapabilitiesUnitTest extends AbstractSniffTestCase {
/**
* Adjust the config to allow for testing with specific CLI arguments.
diff --git a/WordPress/Tests/WP/CapitalPDangitUnitTest.php b/WordPress/Tests/WP/CapitalPDangitUnitTest.php
index ff545fcc65..2504230a9b 100644
--- a/WordPress/Tests/WP/CapitalPDangitUnitTest.php
+++ b/WordPress/Tests/WP/CapitalPDangitUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\WP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the CapitalPDangit sniff.
@@ -19,7 +19,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\WP\CapitalPDangitSniff
*/
-final class CapitalPDangitUnitTest extends AbstractSniffUnitTest {
+final class CapitalPDangitUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/WP/ClassNameCaseUnitTest.php b/WordPress/Tests/WP/ClassNameCaseUnitTest.php
index f34d832c2d..4f54be28df 100644
--- a/WordPress/Tests/WP/ClassNameCaseUnitTest.php
+++ b/WordPress/Tests/WP/ClassNameCaseUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\WP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the ClassNameCase sniff.
@@ -18,7 +18,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\WP\ClassNameCaseSniff
*/
-final class ClassNameCaseUnitTest extends AbstractSniffUnitTest {
+final class ClassNameCaseUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/WP/CronIntervalUnitTest.inc b/WordPress/Tests/WP/CronIntervalUnitTest.inc
index b4b81fc714..9e2011c92d 100644
--- a/WordPress/Tests/WP/CronIntervalUnitTest.inc
+++ b/WordPress/Tests/WP/CronIntervalUnitTest.inc
@@ -316,7 +316,7 @@ function first_class_weekly_schedule( $schedules ) {
}
add_filter( 'cron_schedules', 'first_class_weekly_schedule'(...)); // Ok: > 15 min.
add_filter( 'cron_schedules', first_class_weekly_schedule(...)); // Ok: > 15 min.
-add_filter( 'cron_schedules', namespace\first_class_weekly_schedule(...)); // Ok: > 15 min.
+add_filter( 'cron_schedules', namespace\first_class_weekly_schedule(...)); // Ok: > 15 min in PHPCS 3.x and warning in PHPCS 4.x. Should become ok in PHPCS 4.x when the sniff is able to resolve relative namespaces.
function first_class_six_min_schedule( $schedules ) {
$schedules['every_6_mins'] = array(
@@ -332,15 +332,15 @@ add_filter( 'cron_schedules', \FIRST_CLASS_SIX_MIN_SCHEDULE(...)); // Warning: 6
add_filter( 'cron_schedules', namespace\first_class_six_min_schedule(...)); // Warning: 6 min.
/*
- * The tests below document the current behavior of the sniff, even though they are false negatives. The sniff treats
- * the first-class callable examples below as if referencing the global function first_class_six_min_schedule()
- * and not a namespaced function with the same name.
+ * The tests below document the current behavior of the sniff, even though they are false negatives when running with
+ * PHPCS 3.x (with PHPCS 4.x this problem doesn't occur). The sniff treats the first-class callable examples below as if
+ * referencing the global function first_class_six_min_schedule() and not a namespaced function with the same name.
*
* Related to: https://github.com/WordPress/WordPress-Coding-Standards/issues/2644.
*/
-add_filter( 'cron_schedules', MyNamespace\first_class_weekly_schedule(...)); // False negative - Ok: > 15 min, but should be marked `ChangeDetected`.
-add_filter( 'cron_schedules', \MyNamespace\first_class_weekly_schedule(...)); // False negative - Ok: > 15 min, but should be marked `ChangeDetected`.
-add_filter( 'cron_schedules', namespace\Sub\first_class_weekly_schedule(...)); // False negative - Ok: > 15 min, but should be marked `ChangeDetected`.
+add_filter( 'cron_schedules', MyNamespace\first_class_weekly_schedule(...)); // PHPCS 3.x false negative - Ok: > 15 min, but should be marked `ChangeDetected`.
+add_filter( 'cron_schedules', \MyNamespace\first_class_weekly_schedule(...)); // PHPCS 3.x false negative - Ok: > 15 min, but should be marked `ChangeDetected`.
+add_filter( 'cron_schedules', namespace\Sub\first_class_weekly_schedule(...)); // PHPCS 3.x false negative - Ok: > 15 min, but should be marked `ChangeDetected`.
/*
* The tests below document the current behavior of the sniff, even though they are false positives. The sniff treats
diff --git a/WordPress/Tests/WP/CronIntervalUnitTest.php b/WordPress/Tests/WP/CronIntervalUnitTest.php
index 4bd6add5b3..cd01753cca 100644
--- a/WordPress/Tests/WP/CronIntervalUnitTest.php
+++ b/WordPress/Tests/WP/CronIntervalUnitTest.php
@@ -9,7 +9,8 @@
namespace WordPressCS\WordPress\Tests\WP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
+use PHPCSUtils\BackCompat\Helper;
/**
* Unit test class for the CronInterval sniff.
@@ -20,7 +21,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\WP\CronIntervalSniff
*/
-final class CronIntervalUnitTest extends AbstractSniffUnitTest {
+final class CronIntervalUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
@@ -37,6 +38,9 @@ public function getErrorList() {
* @return array Key is the line number, value is the number of expected warnings.
*/
public function getWarningList() {
+ $phpcs_version = Helper::getVersion();
+ $is_phpcs_4 = version_compare( $phpcs_version, '3.99.99', '>' );
+
return array(
12 => 1,
17 => 1,
@@ -65,11 +69,15 @@ public function getWarningList() {
286 => 1,
288 => 1,
290 => 1,
+ 319 => ( true === $is_phpcs_4 ) ? 1 : 0,
328 => 1,
329 => 1,
330 => 1,
331 => 1,
332 => 1,
+ 341 => ( true === $is_phpcs_4 ) ? 1 : 0,
+ 342 => ( true === $is_phpcs_4 ) ? 1 : 0,
+ 343 => ( true === $is_phpcs_4 ) ? 1 : 0,
352 => 1,
353 => 1,
354 => 1,
diff --git a/WordPress/Tests/WP/DeprecatedClassesUnitTest.php b/WordPress/Tests/WP/DeprecatedClassesUnitTest.php
index 3490968b53..7b8d4cf7ee 100644
--- a/WordPress/Tests/WP/DeprecatedClassesUnitTest.php
+++ b/WordPress/Tests/WP/DeprecatedClassesUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\WP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the WP_DeprecatedClasses sniff.
@@ -19,7 +19,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\WP\DeprecatedClassesSniff
*/
-final class DeprecatedClassesUnitTest extends AbstractSniffUnitTest {
+final class DeprecatedClassesUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/WP/DeprecatedFunctionsUnitTest.php b/WordPress/Tests/WP/DeprecatedFunctionsUnitTest.php
index 451d635219..8d745c8fc4 100644
--- a/WordPress/Tests/WP/DeprecatedFunctionsUnitTest.php
+++ b/WordPress/Tests/WP/DeprecatedFunctionsUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\WP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the WP_DeprecatedFunctions sniff.
@@ -19,7 +19,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\WP\DeprecatedFunctionsSniff
*/
-final class DeprecatedFunctionsUnitTest extends AbstractSniffUnitTest {
+final class DeprecatedFunctionsUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/WP/DeprecatedParameterValuesUnitTest.php b/WordPress/Tests/WP/DeprecatedParameterValuesUnitTest.php
index 958738ab87..cd45722ae6 100644
--- a/WordPress/Tests/WP/DeprecatedParameterValuesUnitTest.php
+++ b/WordPress/Tests/WP/DeprecatedParameterValuesUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\WP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the DeprecatedParameterValues sniff.
@@ -18,7 +18,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\WP\DeprecatedParameterValuesSniff
*/
-final class DeprecatedParameterValuesUnitTest extends AbstractSniffUnitTest {
+final class DeprecatedParameterValuesUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/WP/DeprecatedParametersUnitTest.php b/WordPress/Tests/WP/DeprecatedParametersUnitTest.php
index 51c9e11751..45d6c24003 100644
--- a/WordPress/Tests/WP/DeprecatedParametersUnitTest.php
+++ b/WordPress/Tests/WP/DeprecatedParametersUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\WP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the DeprecatedParameters sniff.
@@ -19,7 +19,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\WP\DeprecatedParametersSniff
*/
-final class DeprecatedParametersUnitTest extends AbstractSniffUnitTest {
+final class DeprecatedParametersUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/WP/DiscouragedConstantsUnitTest.php b/WordPress/Tests/WP/DiscouragedConstantsUnitTest.php
index 9e820865ca..2691ee6eaf 100644
--- a/WordPress/Tests/WP/DiscouragedConstantsUnitTest.php
+++ b/WordPress/Tests/WP/DiscouragedConstantsUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\WP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the WP_DiscouragedConstants sniff.
@@ -18,7 +18,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\WP\DiscouragedConstantsSniff
*/
-final class DiscouragedConstantsUnitTest extends AbstractSniffUnitTest {
+final class DiscouragedConstantsUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/WP/DiscouragedFunctionsUnitTest.php b/WordPress/Tests/WP/DiscouragedFunctionsUnitTest.php
index 67031257c3..f231a3046d 100644
--- a/WordPress/Tests/WP/DiscouragedFunctionsUnitTest.php
+++ b/WordPress/Tests/WP/DiscouragedFunctionsUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\WP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the WP_DiscouragedFunctions sniff.
@@ -19,10 +19,9 @@
*
* @covers \WordPressCS\WordPress\AbstractFunctionRestrictionsSniff
* @covers \WordPressCS\WordPress\Helpers\ContextHelper::has_object_operator_before
- * @covers \WordPressCS\WordPress\Helpers\ContextHelper::is_token_namespaced
* @covers \WordPressCS\WordPress\Sniffs\WP\DiscouragedFunctionsSniff
*/
-final class DiscouragedFunctionsUnitTest extends AbstractSniffUnitTest {
+final class DiscouragedFunctionsUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/WP/EnqueuedResourceParametersUnitTest.php b/WordPress/Tests/WP/EnqueuedResourceParametersUnitTest.php
index a1dd90b4f5..d819318808 100644
--- a/WordPress/Tests/WP/EnqueuedResourceParametersUnitTest.php
+++ b/WordPress/Tests/WP/EnqueuedResourceParametersUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\WP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the EnqueuedCheck sniff.
@@ -18,7 +18,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\WP\EnqueuedResourceParametersSniff
*/
-final class EnqueuedResourceParametersUnitTest extends AbstractSniffUnitTest {
+final class EnqueuedResourceParametersUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/WP/EnqueuedResourcesUnitTest.php b/WordPress/Tests/WP/EnqueuedResourcesUnitTest.php
index d329549306..0eb9f9050e 100644
--- a/WordPress/Tests/WP/EnqueuedResourcesUnitTest.php
+++ b/WordPress/Tests/WP/EnqueuedResourcesUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\WP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the EnqueuedResources sniff.
@@ -19,7 +19,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\WP\EnqueuedResourcesSniff
*/
-final class EnqueuedResourcesUnitTest extends AbstractSniffUnitTest {
+final class EnqueuedResourcesUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/WP/GetMetaSingleUnitTest.php b/WordPress/Tests/WP/GetMetaSingleUnitTest.php
index f1b328c39b..174f01edf7 100644
--- a/WordPress/Tests/WP/GetMetaSingleUnitTest.php
+++ b/WordPress/Tests/WP/GetMetaSingleUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\WP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the GetMetaSingle sniff.
@@ -18,7 +18,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\WP\GetMetaSingleSniff
*/
-final class GetMetaSingleUnitTest extends AbstractSniffUnitTest {
+final class GetMetaSingleUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/WP/GlobalVariablesOverrideUnitTest.php b/WordPress/Tests/WP/GlobalVariablesOverrideUnitTest.php
index 954b65ee4e..513c72c0f3 100644
--- a/WordPress/Tests/WP/GlobalVariablesOverrideUnitTest.php
+++ b/WordPress/Tests/WP/GlobalVariablesOverrideUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\WP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the GlobalVariables sniff.
@@ -23,7 +23,7 @@
* @covers \WordPressCS\WordPress\Helpers\WPGlobalVariablesHelper
* @covers \WordPressCS\WordPress\Sniffs\WP\GlobalVariablesOverrideSniff
*/
-final class GlobalVariablesOverrideUnitTest extends AbstractSniffUnitTest {
+final class GlobalVariablesOverrideUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/WP/I18nUnitTest.php b/WordPress/Tests/WP/I18nUnitTest.php
index 20e7accaab..586bdcf75d 100644
--- a/WordPress/Tests/WP/I18nUnitTest.php
+++ b/WordPress/Tests/WP/I18nUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\WP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the I18n sniff.
@@ -19,7 +19,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\WP\I18nSniff
*/
-final class I18nUnitTest extends AbstractSniffUnitTest {
+final class I18nUnitTest extends AbstractSniffTestCase {
/**
* Set CLI values before the file is tested.
diff --git a/WordPress/Tests/WP/PostsPerPageUnitTest.php b/WordPress/Tests/WP/PostsPerPageUnitTest.php
index bf3296ce25..a25d56f5ba 100644
--- a/WordPress/Tests/WP/PostsPerPageUnitTest.php
+++ b/WordPress/Tests/WP/PostsPerPageUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\WP;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the PostsPerPage sniff.
@@ -23,7 +23,7 @@
* @covers \WordPressCS\WordPress\AbstractArrayAssignmentRestrictionsSniff
* @covers \WordPressCS\WordPress\Sniffs\WP\PostsPerPageSniff
*/
-final class PostsPerPageUnitTest extends AbstractSniffUnitTest {
+final class PostsPerPageUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/WhiteSpace/CastStructureSpacingUnitTest.php b/WordPress/Tests/WhiteSpace/CastStructureSpacingUnitTest.php
index 5392cf4852..bae649d133 100644
--- a/WordPress/Tests/WhiteSpace/CastStructureSpacingUnitTest.php
+++ b/WordPress/Tests/WhiteSpace/CastStructureSpacingUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\WhiteSpace;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the CastStructureSpacing sniff.
@@ -19,7 +19,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\WhiteSpace\CastStructureSpacingSniff
*/
-final class CastStructureSpacingUnitTest extends AbstractSniffUnitTest {
+final class CastStructureSpacingUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/WhiteSpace/ControlStructureSpacingUnitTest.php b/WordPress/Tests/WhiteSpace/ControlStructureSpacingUnitTest.php
index e173ae0ec9..9590baf6b8 100644
--- a/WordPress/Tests/WhiteSpace/ControlStructureSpacingUnitTest.php
+++ b/WordPress/Tests/WhiteSpace/ControlStructureSpacingUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\WhiteSpace;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the ControlStructureSpacing sniff.
@@ -19,7 +19,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\WhiteSpace\ControlStructureSpacingSniff
*/
-final class ControlStructureSpacingUnitTest extends AbstractSniffUnitTest {
+final class ControlStructureSpacingUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/WhiteSpace/ObjectOperatorSpacingUnitTest.php b/WordPress/Tests/WhiteSpace/ObjectOperatorSpacingUnitTest.php
index adfbe1ba9c..d86058e99e 100644
--- a/WordPress/Tests/WhiteSpace/ObjectOperatorSpacingUnitTest.php
+++ b/WordPress/Tests/WhiteSpace/ObjectOperatorSpacingUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\WhiteSpace;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the ObjectOperatorSpacing sniff.
@@ -18,7 +18,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\WhiteSpace\ObjectOperatorSpacingSniff
*/
-final class ObjectOperatorSpacingUnitTest extends AbstractSniffUnitTest {
+final class ObjectOperatorSpacingUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/WordPress/Tests/WhiteSpace/OperatorSpacingUnitTest.php b/WordPress/Tests/WhiteSpace/OperatorSpacingUnitTest.php
index d7343f1ec2..ff97a55474 100644
--- a/WordPress/Tests/WhiteSpace/OperatorSpacingUnitTest.php
+++ b/WordPress/Tests/WhiteSpace/OperatorSpacingUnitTest.php
@@ -9,7 +9,7 @@
namespace WordPressCS\WordPress\Tests\WhiteSpace;
-use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
/**
* Unit test class for the OperatorSpacing sniff.
@@ -21,7 +21,7 @@
*
* @covers \WordPressCS\WordPress\Sniffs\WhiteSpace\OperatorSpacingSniff
*/
-final class OperatorSpacingUnitTest extends AbstractSniffUnitTest {
+final class OperatorSpacingUnitTest extends AbstractSniffTestCase {
/**
* Returns the lines where errors should occur.
diff --git a/composer.json b/composer.json
index 4961107d8d..223b21baef 100644
--- a/composer.json
+++ b/composer.json
@@ -21,13 +21,13 @@
"ext-libxml": "*",
"ext-tokenizer": "*",
"ext-xmlreader": "*",
- "squizlabs/php_codesniffer": "^3.13.5",
+ "squizlabs/php_codesniffer": "^3.13.5 || ^4.0.1",
"phpcsstandards/phpcsutils": "^1.2.2",
"phpcsstandards/phpcsextra": "^1.5.0"
},
"require-dev": {
"phpcompatibility/php-compatibility": "^10.0.0@dev",
- "phpunit/phpunit": "^8.0 || ^9.0",
+ "phpunit/phpunit": "^8.0 || ^9.3.4",
"phpcsstandards/phpcsdevtools": "^1.2.0",
"php-parallel-lint/php-parallel-lint": "^1.4.0",
"php-parallel-lint/php-console-highlighter": "^1.0.0"
@@ -54,12 +54,18 @@
"fix-cs": [
"@php ./vendor/squizlabs/php_codesniffer/bin/phpcbf"
],
- "run-tests": [
+ "run-tests-phpcs3": [
"@php ./vendor/phpunit/phpunit/phpunit --filter WordPress ./vendor/squizlabs/php_codesniffer/tests/AllTests.php --no-coverage"
],
- "coverage": [
+ "run-tests-phpcs4": [
+ "@php ./vendor/phpunit/phpunit/phpunit --no-coverage"
+ ],
+ "coverage-phpcs3": [
"@php ./vendor/phpunit/phpunit/phpunit --filter WordPress ./vendor/squizlabs/php_codesniffer/tests/AllTests.php"
],
+ "coverage-phpcs4": [
+ "@php ./vendor/phpunit/phpunit/phpunit"
+ ],
"check-complete": [
"@php ./vendor/phpcsstandards/phpcsdevtools/bin/phpcs-check-feature-completeness -q ./WordPress"
],
@@ -69,7 +75,7 @@
"check-all": [
"@lint",
"@check-cs",
- "@run-tests",
+ "if [ -f ./vendor/squizlabs/php_codesniffer/tests/AllTests.php ]; then composer run-tests-phpcs3; else composer run-tests-phpcs4; fi",
"@check-complete-strict"
]
},
@@ -77,8 +83,10 @@
"lint": "Lint PHP files against parse errors.",
"check-cs": "Run the PHPCS script against the entire codebase.",
"fix-cs": "Run the PHPCBF script to fix all the autofixable violations on the codebase.",
- "run-tests": "Run all the unit tests for the WordPress Coding Standards sniffs without code coverage.",
- "coverage": "Run all the unit tests for the WordPress Coding Standards sniffs with code coverage.",
+ "run-tests-phpcs3": "Run all the unit tests for the WordPress Coding Standards sniffs without code coverage (PHPCS 3.x).",
+ "run-tests-phpcs4": "Run all the unit tests for the WordPress Coding Standards sniffs without code coverage (PHPCS 4.x).",
+ "coverage-phpcs3": "Run all the unit tests for the WordPress Coding Standards sniffs with code coverage (PHPCS 3.x).",
+ "coverage-phpcs4": "Run all the unit tests for the WordPress Coding Standards sniffs with code coverage (PHPCS 4.x).",
"check-complete": "Check if all the sniffs have tests.",
"check-complete-strict": "Check if all the sniffs have unit tests and XML documentation.",
"check-all": "Run all checks (lint, phpcs, feature completeness) and tests."