diff --git a/.github/workflows/quicktest.yml b/.github/workflows/quicktest.yml index 5cf6b0fa03..8081fafc79 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.repository_owner != 'WordPress' || 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.repository_owner == 'WordPress' && github.ref_name == 'develop' }} - run: composer coverage + - name: Run the unit tests without code coverage (PHPCS 3.x) + if: ${{ (github.repository_owner != 'WordPress' || 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.repository_owner != 'WordPress' || 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.repository_owner == 'WordPress' && 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.repository_owner == 'WordPress' && github.ref_name == 'develop' && startsWith( steps.phpcs_version.outputs.VERSION, '4.' ) }} + run: composer coverage-phpcs4 - name: Send coverage report to Codecov if: ${{ success() && github.repository_owner == 'WordPress' && github.ref_name == 'develop' }} diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index cda909983b..49ebbd12e4 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,25 +53,38 @@ 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: 'dev' + dependencies: 'phpcs-3-dev' + extensions: '' + coverage: false + - php: '8.1' + dependencies: 'phpcs-4-dev' + extensions: '' + coverage: false + - php: '8.4' + dependencies: 'phpcs-3-dev' extensions: '' coverage: false - php: '8.4' - 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' name: PHP ${{ matrix.php }} on PHPCS ${{ matrix.dependencies }} - continue-on-error: ${{ matrix.php == '8.5' }} + continue-on-error: ${{ matrix.php == '8.5' || matrix.dependencies == 'phpcs-4-dev' }} steps: - name: Checkout repository @@ -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" @@ -97,11 +111,15 @@ jobs: coverage: ${{ matrix.coverage && 'xdebug' || 'none' }} tools: cs2pr + # Remove PHPCompatibility as it would (for now) prevent the tests from being able to run against PHPCS 4.x. + - name: 'Composer: remove PHPCompatibility' + run: composer remove --dev phpcompatibility/php-compatibility --no-update --no-interaction + - 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 +145,25 @@ jobs: if: ${{ matrix.dependencies == 'stable' }} run: composer lint -- --checkstyle | cs2pr - - name: Run the unit tests without code coverage - if: ${{ matrix.coverage == false || github.repository_owner != 'WordPress' }} - 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.repository_owner != 'WordPress') }} + 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.repository_owner != 'WordPress') }} + 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.repository_owner == 'WordPress' }} + run: composer coverage-phpcs3 - - name: Run the unit tests with code coverage - if: ${{ matrix.coverage == true && github.repository_owner == 'WordPress' }} - 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.repository_owner == 'WordPress' }} + run: composer coverage-phpcs4 - name: Send coverage report to Codecov if: ${{ success() && matrix.coverage == true && github.repository_owner == 'WordPress' }} diff --git a/Tests/bootstrap.php b/Tests/bootstrap.php index fd4c952893..9cc9feaf84 100644 --- a/Tests/bootstrap.php +++ b/Tests/bootstrap.php @@ -45,6 +45,21 @@ ) { require_once $phpcsDir . $ds . 'autoload.php'; require_once $phpcsDir . $ds . 'tests' . $ds . 'bootstrap.php'; // PHPUnit 6.x+ support. + + spl_autoload_register( + function ( $className ) { + // Only try & load our own classes. + if ( stripos( $className, 'WordPressCS' ) !== 0 ) { + return; + } + + $file = realpath( dirname( __DIR__ ) ) . DIRECTORY_SEPARATOR . strtr( str_replace( 'WordPressCS\\', '', $className ), '\\', DIRECTORY_SEPARATOR ) . '.php'; + + if ( file_exists( $file ) ) { + include_once $file; + } + } + ); } else { echo 'Uh oh... can\'t find PHPCS. @@ -57,6 +72,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/AbstractClassRestrictionsSniff.php b/WordPress/AbstractClassRestrictionsSniff.php index e0f1c85802..8b7360cc15 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/ConstantsHelper.php b/WordPress/Helpers/ConstantsHelper.php index 6decbb4010..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; } @@ -79,6 +82,7 @@ public static function is_use_of_global_constant( File $phpcsFile, $stackPtr ) { \T_INSTANCEOF => true, \T_INSTEADOF => true, \T_GOTO => true, + \T_AS => true, ); $tokens_to_ignore += Tokens::$ooScopeTokens; $tokens_to_ignore += Collections::objectOperators(); @@ -90,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 b90006fde0..3518f513d5 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 ); + } else { + 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, this methods 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' )`. * @@ -214,7 +279,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 ) { $tokens = $phpcsFile->getTokens(); @@ -229,11 +294,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; 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 afa6ffc54f..722de66fec 100644 --- a/WordPress/Helpers/WPDBTrait.php +++ b/WordPress/Helpers/WPDBTrait.php @@ -53,9 +53,12 @@ final protected function is_wpdb_method_call( File $phpcsFile, $stackPtr, array return false; } + $contentLC = strtolower( $tokens[ $stackPtr ]['content'] ); + // 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/Sniffs/DB/DirectDatabaseQuerySniff.php b/WordPress/Sniffs/DB/DirectDatabaseQuerySniff.php index a600e6ae83..b57ba909fe 100644 --- a/WordPress/Sniffs/DB/DirectDatabaseQuerySniff.php +++ b/WordPress/Sniffs/DB/DirectDatabaseQuerySniff.php @@ -228,15 +228,28 @@ 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 ( \T_STRING === $this->tokens[ $i ]['code'] || \T_NAME_FULLY_QUALIFIED === $this->tokens[ $i ]['code'] ) { $nextNonEmpty = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $i + 1 ), null, true ); if ( \T_OPEN_PARENTHESIS !== $this->tokens[ $nextNonEmpty ]['code'] ) { continue; } + // Skip if this is a non-global namespaced function call. + $prevNonEmpty = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $i - 1 ), null, true ); + if ( \T_NS_SEPARATOR === $this->tokens[ $prevNonEmpty ]['code'] ) { + $prevPrevNonEmpty = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $prevNonEmpty - 1 ), null, true ); + if ( \T_STRING === $this->tokens[ $prevPrevNonEmpty ]['code'] || \T_NAMESPACE === $this->tokens[ $prevPrevNonEmpty ]['code'] ) { + continue; + } + } + $content = strtolower( $this->tokens[ $i ]['content'] ); + if ( \T_NAME_FULLY_QUALIFIED === $this->tokens[ $i ]['code'] ) { + $content = ltrim( $content, '\\' ); + } + 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..ecc24e3a34 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,9 +225,16 @@ public function process_token( $stackPtr ) { } // Detect a specific pattern for variable replacements in combination with `IN`. - if ( \T_STRING === $this->tokens[ $i ]['code'] ) { + if ( \T_STRING === $this->tokens[ $i ]['code'] + || \T_NAME_FULLY_QUALIFIED === $this->tokens[ $i ]['code'] + ) { + $content_lowercase = \strtolower( $this->tokens[ $i ]['content'] ); - if ( 'sprintf' === strtolower( $this->tokens[ $i ]['content'] ) ) { + if ( \T_NAME_FULLY_QUALIFIED === $this->tokens[ $i ]['code'] ) { + $content_lowercase = \ltrim( $content_lowercase, '\\' ); + } + + if ( 'sprintf' === $content_lowercase ) { $sprintf_parameters = PassedParameters::getParameters( $this->phpcsFile, $i ); if ( ! empty( $sprintf_parameters ) ) { @@ -265,7 +273,7 @@ public function process_token( $stackPtr ) { } unset( $sprintf_parameters, $valid_sprintf, $last_param ); - } elseif ( 'implode' === strtolower( $this->tokens[ $i ]['content'] ) ) { + } elseif ( 'implode' === $content_lowercase ) { $ignore_tokens = Tokens::$emptyTokens + array( \T_STRING_CONCAT => \T_STRING_CONCAT, \T_NS_SEPARATOR => \T_NS_SEPARATOR, @@ -679,10 +687,17 @@ protected function analyse_sprintf( $sprintf_params ) { $sprintf_param['end'], true ); + if ( \T_STRING === $this->tokens[ $implode ]['code'] - && 'implode' === strtolower( $this->tokens[ $implode ]['content'] ) + || \T_NAME_FULLY_QUALIFIED === $this->tokens[ $implode ]['code'] ) { - if ( $this->analyse_implode( $implode ) === true ) { + $content_lowercase = \strtolower( $this->tokens[ $implode ]['content'] ); + + if ( \T_NAME_FULLY_QUALIFIED === $this->tokens[ $implode ]['code'] ) { + $content_lowercase = \ltrim( $content_lowercase, '\\' ); + } + + if ( 'implode' === $content_lowercase && $this->analyse_implode( $implode ) === true ) { ++$found; } } @@ -738,11 +753,21 @@ protected function analyse_implode( $implode_token ) { ); if ( \T_STRING !== $this->tokens[ $array_fill ]['code'] - || 'array_fill' !== strtolower( $this->tokens[ $array_fill ]['content'] ) + && \T_NAME_FULLY_QUALIFIED !== $this->tokens[ $array_fill ]['code'] ) { return false; } + $content_lowercase = strtolower( $this->tokens[ $array_fill ]['content'] ); + + if ( \T_NAME_FULLY_QUALIFIED === $this->tokens[ $array_fill ]['code'] ) { + $content_lowercase = \ltrim( $content_lowercase, '\\' ); + } + + if ( 'array_fill' !== $content_lowercase ) { + return false; + } + $array_fill_value_param = PassedParameters::getParameter( $this->phpcsFile, $array_fill, 3, 'value' ); if ( false === $array_fill_value_param ) { return false; 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/NamingConventions/PrefixAllGlobalsSniff.php b/WordPress/Sniffs/NamingConventions/PrefixAllGlobalsSniff.php index 686075042c..d4ac5a519c 100644 --- a/WordPress/Sniffs/NamingConventions/PrefixAllGlobalsSniff.php +++ b/WordPress/Sniffs/NamingConventions/PrefixAllGlobalsSniff.php @@ -456,6 +456,7 @@ public function register() { $parent = parent::register(); if ( ! empty( $parent ) ) { $targets[] = \T_STRING; + $targets[] = \T_NAME_FULLY_QUALIFIED; } return $targets; @@ -548,7 +549,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(); @@ -1221,6 +1224,16 @@ private function validate_prefixes() { $prefixes = array(); $ns_prefixes = array(); foreach ( $this->prefixes as $key => $prefix ) { + if ( is_string( $prefix ) === false || trim( $prefix ) === '' ) { + $this->phpcsFile->addError( + 'The prefix must be a non-empty string. Found: "%s".', + 0, + 'InvalidPrefixPassed', + array( $prefix ) + ); + continue; + } + $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 400da4a6dd..0df5ab6a37 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; @@ -194,10 +195,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 e0714e36c4..0b8a602a53 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 40fb0c61c2..5e4f2cb14c 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 fd48e1a2f2..ce6e5d2a00 100644 --- a/WordPress/Sniffs/WP/CronIntervalSniff.php +++ b/WordPress/Sniffs/WP/CronIntervalSniff.php @@ -233,7 +233,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 745b9ea206..464ac39f18 100644 --- a/WordPress/Sniffs/WP/DeprecatedFunctionsSniff.php +++ b/WordPress/Sniffs/WP/DeprecatedFunctionsSniff.php @@ -1733,7 +1733,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 7db5a37854..9788f3578f 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/EnqueuedResourceParametersSniff.php b/WordPress/Sniffs/WP/EnqueuedResourceParametersSniff.php index 578517238a..3b9ba77bfe 100644 --- a/WordPress/Sniffs/WP/EnqueuedResourceParametersSniff.php +++ b/WordPress/Sniffs/WP/EnqueuedResourceParametersSniff.php @@ -18,7 +18,7 @@ * This checks the enqueued 4th and 5th parameters to make sure the version and in_footer are set. * * If a source ($src) value is passed, then version ($ver) needs to have non-falsy value. - * If a source ($src) value is passed a check for in footer ($in_footer), warn the user if the value is falsy. + * If a source ($src) value is passed, then it is recommended to explicitly set the $in_footer parameter. * * @link https://developer.wordpress.org/reference/functions/wp_register_script/ * @link https://developer.wordpress.org/reference/functions/wp_enqueue_script/ @@ -53,20 +53,21 @@ final class EnqueuedResourceParametersSniff extends AbstractFunctionParameterSni ); /** - * False + the empty tokens array. + * False + T_NS_SEPARATOR + the empty tokens array. * * This array is enriched with the $emptyTokens array in the register() method. * * @var array */ private $false_tokens = array( - \T_FALSE => \T_FALSE, + \T_FALSE => \T_FALSE, + \T_NS_SEPARATOR => \T_NS_SEPARATOR, // Needed to handle fully qualified \false (PHPCS 3.x). ); /** * Token codes which are "safe" to accept to determine whether a version would evaluate to `false`. * - * This array is enriched with the several of the PHPCS token arrays in the register() method. + * This array is enriched with several of the PHPCS token arrays in the register() method. * * @var array */ @@ -88,7 +89,8 @@ final class EnqueuedResourceParametersSniff extends AbstractFunctionParameterSni /** * Returns an array of tokens this test wants to listen for. * - * Overloads and calls the parent method to allow for adding additional tokens to the $safe_tokens property. + * Overloads and calls the parent method to allow for adding additional tokens to the + * $false_tokens and $safe_tokens properties. * * @return array */ @@ -139,7 +141,9 @@ public function process_parameters( $stackPtr, $group_name, $matched_content, $p } } - if ( false === $version_param || 'null' === $version_param['clean'] ) { + $version_clean_lc = ( false !== $version_param ) ? strtolower( $version_param['clean'] ) : ''; + + if ( false === $version_param || 'null' === $version_clean_lc || '\null' === $version_clean_lc ) { $type = 'script'; if ( strpos( $matched_content, '_style' ) !== false ) { $type = 'style'; @@ -165,8 +169,8 @@ public function process_parameters( $stackPtr, $group_name, $matched_content, $p /* * In footer Check * - * Check to make sure that $in_footer is set to true. - * It will warn the user to make sure it is intended. + * Check to make sure that $in_footer is explicitly set. + * Warn the user if it is not set. * * Only wp_register_script and wp_enqueue_script need this check, * as this parameter is not available to wp_register_style and wp_enqueue_style. 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 9a289959e6..2f273d568a 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.inc b/WordPress/Tests/CodeAnalysis/EscapedNotTranslatedUnitTest.inc index c451ccc91b..d9831fe886 100644 --- a/WordPress/Tests/CodeAnalysis/EscapedNotTranslatedUnitTest.inc +++ b/WordPress/Tests/CodeAnalysis/EscapedNotTranslatedUnitTest.inc @@ -9,3 +9,11 @@ esc_attr( 'text', // Some comment. MY_DOMAIN // More comment. ); // Warning. + +/* + * Safeguard correct handling of all types of namespaced function calls. + */ +\esc_attr( 'text', 'domain' ); +MyNamespace\esc_attr( 'text', 'domain' ); +\MyNamespace\esc_attr( 'text', 'domain' ); +namespace\esc_attr( 'text', 'domain' ); // The sniff should start flagging this once it can resolve relative namespaces. diff --git a/WordPress/Tests/CodeAnalysis/EscapedNotTranslatedUnitTest.php b/WordPress/Tests/CodeAnalysis/EscapedNotTranslatedUnitTest.php index 4218e90dc9..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. @@ -36,9 +36,10 @@ public function getErrorList() { */ public function getWarningList() { return array( - 6 => 1, - 7 => 1, - 8 => 1, + 6 => 1, + 7 => 1, + 8 => 1, + 16 => 1, ); } } diff --git a/WordPress/Tests/DB/DirectDatabaseQueryUnitTest.1.inc b/WordPress/Tests/DB/DirectDatabaseQueryUnitTest.1.inc index 1a6be76a5a..a56d37a5e7 100644 --- a/WordPress/Tests/DB/DirectDatabaseQueryUnitTest.1.inc +++ b/WordPress/Tests/DB/DirectDatabaseQueryUnitTest.1.inc @@ -379,3 +379,47 @@ function methodNamesSameAsCacheFunctions() { return $listofthings; } + +function fullyQualifiedCallsToCacheFunctions1() { + global $wpdb; + + if ( ! ( $listofthings = \wp_cache_get( $foo ) ) ) { + $listofthings = $wpdb->get_col( 'SELECT something FROM somewhere WHERE someotherthing = 1' ); // Warning direct DB call. + \wp_cache_set( 'foo', $listofthings ); + } + + return $listofthings; +} + +function fullyQualifiedCallsToCacheFunctions2() { + global $wpdb; + + $wpdb->query( 'SELECT X FROM Y' ); // Warning direct DB call. + \wp_cache_delete( 'key', 'group' ); +} + +function callToNamespacedNonGlobalFunctions1() { + global $wpdb; + + $listofthings = MyNamespace\wp_cache_get( $foo ); + $listofthings = \MyNamespace\wp_cache_get( $foo ); + $listofthings = namespace\wp_cache_get( $foo ); // The sniff should start considering this a valid WP cache function once it can resolve relative namespaces. + + if ( ! $listofthings ) { + $listofthings = $wpdb->get_col( 'SELECT something FROM somewhere WHERE someotherthing = 1' ); // Warning x 2. + MyNamespace\wp_cache_set( 'foo', $listofthings ); + \MyNamespace\wp_cache_set( 'foo', $listofthings ); + namespace\wp_cache_set( 'foo', $listofthings ); // The sniff should start considering this a valid WP cache function once it can resolve relative namespaces. + } + + return $listofthings; +} + +function callToNamespacedNonGlobalFunctions2() { + global $wpdb; + + $wpdb->query( 'SELECT X FROM Y' ); // Warning x 2. + MyNamespace\wp_cache_delete( 'key', 'group' ); + \MyNamespace\wp_cache_delete( 'key', 'group' ); + namespace\wp_cache_delete( 'key', 'group' ); // The sniff should start considering this a valid WP cache function once it can resolve relative namespaces. +} diff --git a/WordPress/Tests/DB/DirectDatabaseQueryUnitTest.php b/WordPress/Tests/DB/DirectDatabaseQueryUnitTest.php index e43fbb126b..8fbd71c42c 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. @@ -99,6 +99,10 @@ public function getWarningList( $testFile = '' ) { 350 => 1, 364 => 2, 376 => 1, + 387 => 1, + 397 => 1, + 409 => 2, + 421 => 2, ); default: return array(); diff --git a/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.inc b/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.inc index 601877bfa4..53673af6d6 100644 --- a/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.inc +++ b/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.inc @@ -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' ), ), @@ -518,3 +518,16 @@ $where = $wpdb->prepare( */ $callback = $wpdb->prepare(...); // OK. +/* + * Safeguard correct handling of namespaced function calls. + */ +//$sql = MyNamespace\WPDB::prepare( "SELECT * FROM $wpdb->users WHERE id = %d AND user_login = %s" ); +//$sql = \MyNamespace\WPDB::prepare( "SELECT * FROM $wpdb->users WHERE id = %d AND user_login = %s" ); +//$sql = namespace\WPDB::prepare( "SELECT * FROM $wpdb->users WHERE id = %d AND user_login = %s" ); // The sniff should start flagging this once it can resolve relative namespaces. +//$where = $wpdb->prepare( +// MyNamespace\sprintf( +// "{$wpdb->posts}.post_type IN (%s)", +// MyNamespace\implode( ',', MyNamespace\array_fill( 0, count($post_types), '%s' ) ) +// ), +// $post_types +//); diff --git a/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.php b/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.php index df0bc753bf..6937395a09 100644 --- a/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.php +++ b/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.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 PreparedSQLPlaceholders sniff. @@ -17,8 +17,9 @@ * @since 0.14.0 * * @covers \WordPressCS\WordPress\Sniffs\DB\PreparedSQLPlaceholdersSniff + * @covers \WordPressCS\WordPress\Helpers\WPDBTrait::is_wpdb_method_call */ -final class PreparedSQLPlaceholdersUnitTest extends AbstractSniffUnitTest { +final class PreparedSQLPlaceholdersUnitTest extends AbstractSniffTestCase { /** * Returns the lines where errors should occur. diff --git a/WordPress/Tests/DB/PreparedSQLUnitTest.1.inc b/WordPress/Tests/DB/PreparedSQLUnitTest.1.inc index 1f1a49076c..5b08c46d61 100644 --- a/WordPress/Tests/DB/PreparedSQLUnitTest.1.inc +++ b/WordPress/Tests/DB/PreparedSQLUnitTest.1.inc @@ -117,7 +117,7 @@ $wpdb $wpdb?->query( "SELECT * FROM $wpdb->posts WHERE post_title LIKE '" . (int) $foo . "';" ); // OK. $wpdb?->query( "SELECT * FROM $wpdb->posts WHERE post_title LIKE '" . foo() . "';" ); // Bad. -WPDB::prepare( "SELECT * FROM $wpdb->posts WHERE post_title LIKE '" . foo() . "';" ); // Bad. +\WPDB::prepare( "SELECT * FROM $wpdb->posts WHERE post_title LIKE '" . foo() . "';" ); // Bad. $wpdb->Query( "SELECT * FROM $wpdb->posts WHERE post_title LIKE '" . foo() . "';" ); // Bad. $wpdb->query( "SELECT * FROM $wpdb->posts WHERE value = " . {$foo} . ";" ); // Bad - on $foo, not on the {}. @@ -143,5 +143,31 @@ echo $wpdb::CONSTANT_NAME; // Not an identifiable method call. $wpdb->{$methodName}('query'); -// Don't throw an error during live coding. -wpdb::prepare( "SELECT * FROM $wpdb->posts +// TODO: the below currently is flagged but it shouldn't be. +/* +MyNamespace\WPDB::prepare( "SELECT * FROM $wpdb->posts WHERE post_title LIKE '" . foo() . "';" ); +\MyNamespace\WPDB::prepare( "SELECT * FROM $wpdb->posts WHERE post_title LIKE '" . foo() . "';" ); +namespace\WPDB::prepare( "SELECT * FROM $wpdb->posts WHERE post_title LIKE '" . foo() . "';" ); // This should be flagged in the future once the sniff is able to resolve relative namespaces. +*/ + +$wpdb->query( "SELECT * FROM $wpdb->posts WHERE ID = " . MyNamespace\absint( $foo ) ); +$wpdb->query( "SELECT * FROM $wpdb->posts WHERE ID = " . \MyNamespace\absint( $foo ) ); +$wpdb->query( "SELECT * FROM $wpdb->posts WHERE ID = " . namespace\absint( $foo ) ); // This should NOT be flagged in the future once the sniff is able to resolve relative namespaces. + +// TODO: add similar namespace tests as the ones above for PreparedSQLSniff::$SQLAutoEscapedFunctions and FormattingFunctionsHelper::$formattingFunctions. + +/** +$wpdb->query( "SELECT * FROM $wpdb->posts WHERE post_title LIKE '" . namespace\esc_sql( $foo ) . "';" ); // TODO: should the sniff code be updated to handle this case? Currently it is an error, but it shouldn't as this is a call to the global esc_sql() function. +$wpdb->query( "SELECT * FROM $wpdb->posts WHERE post_title LIKE '" . \MyNamespace\esc_sql( $foo ) . "';" ); // Bad. + +$sql = $wpdb->prepare( namespace\sprintf( + 'SELECT `post_id`, `meta_value` FROM `%s` WHERE `meta_key` = "sort_order" AND `post_id` IN (%s)', + $wpdb->postmeta, + implode( ',', array_fill( 0, count( $post_ids ), '%d' ) ) +), $post_ids ); // Bad, but maybe it shouldn't be (check TODO above). +$sql = $wpdb->prepare( \MyNamespace\sprintf( + 'SELECT `post_id`, `meta_value` FROM `%s` WHERE `meta_key` = "sort_order" AND `post_id` IN (%s)', + $wpdb->postmeta, + implode( ',', array_fill( 0, count( $post_ids ), '%d' ) ) +), $post_ids ); // Bad. +*/ diff --git a/WordPress/Tests/DB/PreparedSQLUnitTest.3.inc b/WordPress/Tests/DB/PreparedSQLUnitTest.3.inc new file mode 100644 index 0000000000..4047216b82 --- /dev/null +++ b/WordPress/Tests/DB/PreparedSQLUnitTest.3.inc @@ -0,0 +1,8 @@ +posts diff --git a/WordPress/Tests/DB/PreparedSQLUnitTest.php b/WordPress/Tests/DB/PreparedSQLUnitTest.php index 0a296ff984..ab4a494a3d 100644 --- a/WordPress/Tests/DB/PreparedSQLUnitTest.php +++ b/WordPress/Tests/DB/PreparedSQLUnitTest.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 PreparedSQL sniff. @@ -23,7 +23,7 @@ * @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. @@ -66,6 +66,19 @@ public function getErrorList( $testFile = '' ) { 124 => 1, 128 => 1, 132 => 2, + 153 => 1, + 154 => 1, + 155 => 1, + + // phpcs:disable Squiz.PHP.CommentedOutCode.Found + + /* + 147 => 1, + 148 => 1, + 155 => 1, + 160 => 1, + */ + // phpcs:enable Squiz.PHP.CommentedOutCode.Found ); case 'PreparedSQLUnitTest.2.inc': diff --git a/WordPress/Tests/DB/RestrictedClassesUnitTest.1.inc b/WordPress/Tests/DB/RestrictedClassesUnitTest.1.inc index 4449a0395b..bd0c41470b 100644 --- a/WordPress/Tests/DB/RestrictedClassesUnitTest.1.inc +++ b/WordPress/Tests/DB/RestrictedClassesUnitTest.1.inc @@ -115,3 +115,6 @@ $anon = new readonly class { $anon = new readonly class() extends PDOStatement {}; // Error. $anon = new #[MyAttribute] readonly class {}; + +echo namespace\DBlayer::VERSION; // Ok - resolves to \DBlayer. +echo namespace\PDO::getAvailableDrivers(); // Bad - resolves to \PDO::getAvailableDrivers(). diff --git a/WordPress/Tests/DB/RestrictedClassesUnitTest.php b/WordPress/Tests/DB/RestrictedClassesUnitTest.php index a07ace04d8..f97bee7f13 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 @@ -102,6 +102,7 @@ public function getErrorList( $testFile = '' ) { 103 => 1, 106 => 1, 115 => 1, + 120 => 1, ); case 'RestrictedClassesUnitTest.2.inc': 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.inc b/WordPress/Tests/DateTime/CurrentTimeTimestampUnitTest.inc index 07634e769a..665c7e88bd 100644 --- a/WordPress/Tests/DateTime/CurrentTimeTimestampUnitTest.inc +++ b/WordPress/Tests/DateTime/CurrentTimeTimestampUnitTest.inc @@ -30,3 +30,11 @@ current_time( gmt: true, type: 'mysql', ); // OK. current_time( type: 'Y-m-d' ); // OK. current_time( gmt: true, type: 'timestamp' ); // Error. current_time( gmt: 0, type : 'U' ); // Warning. + +/* + * Safeguard correct handling of all types of namespaced function calls. + */ +\current_time( 'timestamp', true ); +MyNamespace\current_time( 'timestamp', true ); +\MyNamespace\current_time( 'timestamp', true ); +namespace\current_time( 'timestamp', true ); // The sniff should start flagging this once it can resolve relative namespaces. diff --git a/WordPress/Tests/DateTime/CurrentTimeTimestampUnitTest.inc.fixed b/WordPress/Tests/DateTime/CurrentTimeTimestampUnitTest.inc.fixed index 5b2fa7e28a..3c01c1ff52 100644 --- a/WordPress/Tests/DateTime/CurrentTimeTimestampUnitTest.inc.fixed +++ b/WordPress/Tests/DateTime/CurrentTimeTimestampUnitTest.inc.fixed @@ -27,3 +27,11 @@ current_time( gmt: true, type: 'mysql', ); // OK. current_time( type: 'Y-m-d' ); // OK. time(); // Error. current_time( gmt: 0, type : 'U' ); // Warning. + +/* + * Safeguard correct handling of all types of namespaced function calls. + */ +\time(); +MyNamespace\current_time( 'timestamp', true ); +\MyNamespace\current_time( 'timestamp', true ); +namespace\current_time( 'timestamp', true ); // The sniff should start flagging this once it can resolve relative namespaces. diff --git a/WordPress/Tests/DateTime/CurrentTimeTimestampUnitTest.php b/WordPress/Tests/DateTime/CurrentTimeTimestampUnitTest.php index c667af8234..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. @@ -31,6 +31,7 @@ public function getErrorList() { 11 => 1, 17 => 1, 31 => 1, + 37 => 1, ); } diff --git a/WordPress/Tests/DateTime/RestrictedFunctionsUnitTest.php b/WordPress/Tests/DateTime/RestrictedFunctionsUnitTest.php index a2f29c2364..f4ec719739 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/FileNameUnitTest.php b/WordPress/Tests/Files/FileNameUnitTest.php index c56d3661f2..c671695de3 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. diff --git a/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.1.inc b/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.1.inc index 011e250b7f..7db871a296 100644 --- a/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.1.inc +++ b/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.1.inc @@ -683,4 +683,12 @@ class Acronym_AsymmetricVisibilityProperties { public function __construct(public protected(set) int $foo = 0) {} // Ok. } +/* + * Safeguard correct handling of all types of namespaced function calls. + */ +\define('SOME_GLOBAL', [ 1, 2, 3 ]); // Bad. +MyNamespace\define('SOME_GLOBAL', [ 1, 2, 3 ]); // Ok. +\MyNamespace\define('SOME_GLOBAL', [ 1, 2, 3 ]); // Ok. +namespace\define('SOME_GLOBAL', [ 1, 2, 3 ]); // Ok. + // phpcs:set WordPress.NamingConventions.PrefixAllGlobals prefixes[] diff --git a/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.php b/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.php index b9beb09ff6..cb33983839 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. @@ -96,6 +96,7 @@ public function getErrorList( $testFile = 'PrefixAllGlobalsUnitTest.1.inc' ) { 616 => 1, 617 => 1, 633 => 1, + 689 => 1, ); case 'PrefixAllGlobalsUnitTest.4.inc': 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.1.inc b/WordPress/Tests/NamingConventions/ValidHookNameUnitTest.1.inc index 830c3552d6..ecc8940d3d 100644 --- a/WordPress/Tests/NamingConventions/ValidHookNameUnitTest.1.inc +++ b/WordPress/Tests/NamingConventions/ValidHookNameUnitTest.1.inc @@ -121,3 +121,10 @@ do_action( 'admin_head_' . $fn( 'UPPERCASE', 'wrong-delimiter' ) . '_action' ); do_action_ref_array( hook: 'My-Hook', args: $args ); // OK. Well, not really, but using the wrong parameter name, so not our concern. do_action_ref_array( args: $args, hook_name: 'my_hook', ); // OK. do_action_ref_array( args: $args, hook_name: 'My-Hook', ); // Error - use lowercase + warning about dash. + +/* + * Safeguard correct handling of all types of namespaced function calls. + */ +do_action( 'admin_head_' . MyNamespace\my_function('UPPERCASE') . '_action' ); // Ok. +do_action( 'admin_head_' . \MyNamespace\my_function('UPPERCASE') . '_action' ); // Ok. +do_action( 'admin_head_' . namespace\my_function('UPPERCASE') . '_action' ); // Ok. diff --git a/WordPress/Tests/NamingConventions/ValidHookNameUnitTest.php b/WordPress/Tests/NamingConventions/ValidHookNameUnitTest.php index aac97da195..b4b3019977 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 * @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.1.inc b/WordPress/Tests/NamingConventions/ValidPostTypeSlugUnitTest.1.inc index 5c0ed5729a..724533d85d 100644 --- a/WordPress/Tests/NamingConventions/ValidPostTypeSlugUnitTest.1.inc +++ b/WordPress/Tests/NamingConventions/ValidPostTypeSlugUnitTest.1.inc @@ -68,3 +68,11 @@ register_post_type( // Post type name. $name,// Non string literal. Warning with severity: 3 ); + +/* + * Safeguard correct handling of all types of namespaced function calls. + */ +\register_post_type( 'my-own-post-type-too-long', array() ); // Bad. +MyNamespace\register_post_type( 'my-own-post-type-too-long', array() ); // Ok. +\MyNamespace\register_post_type( 'my-own-post-type-too-long', array() ); // Ok. +namespace\register_post_type( 'my-own-post-type-too-long', array() ); // The sniff should start flagging this once it can resolve relative namespaces. diff --git a/WordPress/Tests/NamingConventions/ValidPostTypeSlugUnitTest.php b/WordPress/Tests/NamingConventions/ValidPostTypeSlugUnitTest.php index 821b73ef24..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. @@ -57,6 +57,7 @@ public function getErrorList( $testFile = '' ) { 52 => 1, 62 => 1, 64 => 1, + 75 => 1, ); case 'ValidPostTypeSlugUnitTest.2.inc': 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 f9a1df7494..038152228c 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.inc b/WordPress/Tests/PHP/IniSetUnitTest.inc index c5ef7295d1..652c61c6d2 100644 --- a/WordPress/Tests/PHP/IniSetUnitTest.inc +++ b/WordPress/Tests/PHP/IniSetUnitTest.inc @@ -58,3 +58,11 @@ ini_set( // Set the number of decimals. 0 ); // Error. + +/* + * Safeguard correct handling of all types of namespaced function calls. + */ +\ini_set( 'bcmath.scale', 0 ); // Error. +MyNamespace\ini_set( 'bcmath.scale', 0 ); // Ok. +\MyNamespace\ini_set( 'bcmath.scale', 0 ); // Ok. +namespace\ini_set( 'bcmath.scale', 0 ); // The sniff should start flagging this once it can resolve relative namespaces. diff --git a/WordPress/Tests/PHP/IniSetUnitTest.php b/WordPress/Tests/PHP/IniSetUnitTest.php index fede4955b2..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. @@ -49,6 +49,7 @@ public function getErrorList() { 42 => 1, 51 => 1, 55 => 1, + 65 => 1, ); } diff --git a/WordPress/Tests/PHP/NoSilencedErrorsUnitTest.inc b/WordPress/Tests/PHP/NoSilencedErrorsUnitTest.inc index f007d150bd..3fc75bc24c 100644 --- a/WordPress/Tests/PHP/NoSilencedErrorsUnitTest.inc +++ b/WordPress/Tests/PHP/NoSilencedErrorsUnitTest.inc @@ -84,3 +84,10 @@ $decoded = @hex2bin( $data ); // phpcs:set WordPress.PHP.NoSilencedErrors context_length 0 echo @some_userland_function( $param ); // Bad. // phpcs:set WordPress.PHP.NoSilencedErrors context_length 6 + +/* + * Safeguard correct handling of namespaced function calls (fully qualified is already tested above). + */ +$file = @MyNS\MyClass::file_get_contents( $file ); // Bad. +$file = @namespace\MyNS\MyClass::file_get_contents( $file ); // Bad. +$file = @namespace\file_get_contents( $file ); // The sniff should stop flagging this once it can resolve relative namespaces. diff --git a/WordPress/Tests/PHP/NoSilencedErrorsUnitTest.php b/WordPress/Tests/PHP/NoSilencedErrorsUnitTest.php index 7a576b992c..a0a9acb2d7 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. @@ -61,6 +61,9 @@ public function getWarningList() { 71 => 1, 78 => 1, 85 => 1, + 91 => 1, + 92 => 1, + 93 => 1, ); } } 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.inc b/WordPress/Tests/PHP/PregQuoteDelimiterUnitTest.inc index 1942b2b07f..3cc54cbeb1 100644 --- a/WordPress/Tests/PHP/PregQuoteDelimiterUnitTest.inc +++ b/WordPress/Tests/PHP/PregQuoteDelimiterUnitTest.inc @@ -13,3 +13,11 @@ preg_quote(delimiter: '#', str: $keywords); // OK. preg_quote(str: $keywords); // Warning. preg_quote(str: $keywords, delimitter: '#'); // Warning (typo in param name). preg_quote(delimiter: '#'); // OK. Invalid function call, but that's not the concern of this sniff. + +/* + * Safeguard correct handling of all types of namespaced function calls. + */ +\preg_quote($keywords); // Warning. +MyNamespace\preg_quote($keywords); // Ok. +\MyNamespace\preg_quote($keywords); // Ok. +namespace\preg_quote($keywords); // The sniff should start flagging this once it can resolve relative namespaces. diff --git a/WordPress/Tests/PHP/PregQuoteDelimiterUnitTest.php b/WordPress/Tests/PHP/PregQuoteDelimiterUnitTest.php index 63d4e0eb9f..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. @@ -40,6 +40,7 @@ public function getWarningList() { 7 => 1, 13 => 1, 14 => 1, + 20 => 1, ); } } diff --git a/WordPress/Tests/PHP/RestrictedPHPFunctionsUnitTest.php b/WordPress/Tests/PHP/RestrictedPHPFunctionsUnitTest.php index 5cfa75ae51..1bc164ee25 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.inc b/WordPress/Tests/PHP/StrictInArrayUnitTest.inc index fd1e194225..1c18b4163c 100644 --- a/WordPress/Tests/PHP/StrictInArrayUnitTest.inc +++ b/WordPress/Tests/PHP/StrictInArrayUnitTest.inc @@ -55,3 +55,11 @@ array_search( $haystack, true // Use strict typing. ); // Ok. + +/* + * Safeguard correct handling of all types of namespaced function calls. + */ +\in_array( 1, array( '1', 2 ) ); // Bad. +MyNamespace\in_array( 1, array( '1', 2 ) ); // Ok. +\MyNamespace\in_array( 1, array( '1', 2 ) ); // Ok. +namespace\in_array( 1, array( '1', 2 ) ); // The sniff should start flagging this once it can resolve relative namespaces. diff --git a/WordPress/Tests/PHP/StrictInArrayUnitTest.php b/WordPress/Tests/PHP/StrictInArrayUnitTest.php index 41aa9d37c4..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. @@ -52,6 +52,7 @@ public function getWarningList() { 44 => 1, 48 => 1, 49 => 1, + 62 => 1, ); } } 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.1.inc b/WordPress/Tests/Security/EscapeOutputUnitTest.1.inc index 52c3a59976..379b11a211 100644 --- a/WordPress/Tests/Security/EscapeOutputUnitTest.1.inc +++ b/WordPress/Tests/Security/EscapeOutputUnitTest.1.inc @@ -258,7 +258,7 @@ echo esc_html_x( $some_nasty_var, 'context' ); // Ok. 1, 19 => 1, @@ -160,10 +163,29 @@ public function getErrorList( $testFile = '' ) { 655 => 1, 657 => 1, 663 => 1, - 664 => 1, + // PHPCS 3.13.3 changed the tokenization of FQN exit/die it impacts directly how this test case + // behaves (see https://github.com/PHPCSStandards/PHP_CodeSniffer/issues/1201). + 664 => version_compare( $phpcs_version, '3.13.3', '>=' ) ? 1 : 0, 672 => 1, 673 => 1, 678 => 1, + 694 => 1, + 699 => 1, + 700 => 1, + 701 => 1, + 707 => 1, + 708 => 1, + 709 => 1, + 714 => 1, + 722 => 1, + 724 => 1, + 725 => 1, + 726 => 1, + 732 => 1, + 733 => 1, + 734 => 1, + 735 => 1, + 744 => 1, ); case 'EscapeOutputUnitTest.6.inc': diff --git a/WordPress/Tests/Security/NonceVerificationUnitTest.1.inc b/WordPress/Tests/Security/NonceVerificationUnitTest.1.inc index aee51ae39a..792ccf58d8 100644 --- a/WordPress/Tests/Security/NonceVerificationUnitTest.1.inc +++ b/WordPress/Tests/Security/NonceVerificationUnitTest.1.inc @@ -524,3 +524,21 @@ function different_function_name() { update_post_meta( (int) $_POST['id'], 'a_key', $_POST['a_value'] ); } // phpcs:set WordPress.Security.NonceVerification customNonceVerificationFunctions[] + +function test_fully_qualified_call_to_global_nonce_verification_function() { + if ( ! is_numeric ( $_POST['foo'] ) ) { + return; + } + + \wp_verify_nonce( 'some_action' ); // OK. +} + +function test_namespaced_calls_to_incorrect_functions() { + if ( ! is_numeric ( $_POST['foo'] ) ) { + return; + } + + MyNamespace\wp_verify_nonce( 'some_action' ); // Bad. + \MyNamespace\wp_verify_nonce( 'some_action' ); // Bad. + namespace\wp_verify_nonce( 'some_action' ); // Bad, but should become ok once the sniff is able to resolve relative namespaces. +} diff --git a/WordPress/Tests/Security/NonceVerificationUnitTest.php b/WordPress/Tests/Security/NonceVerificationUnitTest.php index aa5e832e83..2d5cc16f48 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. @@ -24,7 +24,7 @@ * @covers \WordPressCS\WordPress\Helpers\ContextHelper::is_in_array_comparison * @covers \WordPressCS\WordPress\Sniffs\Security\NonceVerificationSniff */ -final class NonceVerificationUnitTest extends AbstractSniffUnitTest { +final class NonceVerificationUnitTest extends AbstractSniffTestCase { /** * Returns the lines where errors should occur. @@ -75,6 +75,7 @@ public function getErrorList( $testFile = '' ) { 470 => 1, 478 => 1, 524 => 2, + 537 => 1, ); case 'NonceVerificationUnitTest.2.inc': diff --git a/WordPress/Tests/Security/PluginMenuSlugUnitTest.inc b/WordPress/Tests/Security/PluginMenuSlugUnitTest.inc index 2c77ee621e..8d941a207f 100644 --- a/WordPress/Tests/Security/PluginMenuSlugUnitTest.inc +++ b/WordPress/Tests/Security/PluginMenuSlugUnitTest.inc @@ -20,3 +20,11 @@ add_submenu_page( parent_slug: __FILE__, // Bad. capability: $capability, ); + +/* + * Safeguard correct handling of all types of namespaced function calls. + */ +\add_menu_page( $page_title, $menu_title, $capability, __FILE__, $function, $icon_url, $position ); // Bad. +MyNamespace\add_menu_page( $page_title, $menu_title, $capability, __FILE__, $function, $icon_url, $position ); // Ok. +\MyNamespace\add_menu_page( $page_title, $menu_title, $capability, __FILE__, $function, $icon_url, $position ); // Ok. +namespace\add_menu_page( $page_title, $menu_title, $capability, __FILE__, $function, $icon_url, $position ); // The sniff should start flagging this once it can resolve relative namespaces. diff --git a/WordPress/Tests/Security/PluginMenuSlugUnitTest.php b/WordPress/Tests/Security/PluginMenuSlugUnitTest.php index 6741d048a1..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. @@ -42,6 +42,7 @@ public function getWarningList() { 5 => 1, 9 => 2, 20 => 1, + 27 => 1, ); } } diff --git a/WordPress/Tests/Security/SafeRedirectUnitTest.php b/WordPress/Tests/Security/SafeRedirectUnitTest.php index 358cdcfdd1..42d5eccd1d 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 065162c2a8..c670659ae2 100644 --- a/WordPress/Tests/Security/ValidatedSanitizedInputUnitTest.php +++ b/WordPress/Tests/Security/ValidatedSanitizedInputUnitTest.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 ValidatedSanitizedInput sniff. @@ -25,7 +25,7 @@ * @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. diff --git a/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.4.inc b/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.4.inc index d33d0a18e0..fb5e1b8526 100644 --- a/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.4.inc +++ b/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.4.inc @@ -281,5 +281,13 @@ __ ( $args ); +/* + * Safeguard correct handling of namespaced function calls (partially qualified is already tested above). + */ +\MyNamespace\load_textdomain( 'text-domain', '/path/to/file.mo' ); +namespace\load_textdomain( 'text-domain', '/path/to/file.mo' ); +\load_textdomain( 'text-domain', '/path/to/file.mo' ); +\load_textdomain(); + // phpcs:set WordPress.Utils.I18nTextDomainFixer old_text_domain[] // phpcs:set WordPress.Utils.I18nTextDomainFixer new_text_domain false diff --git a/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.4.inc.fixed b/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.4.inc.fixed index f7fd2c6f4e..f47da4e173 100644 --- a/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.4.inc.fixed +++ b/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.4.inc.fixed @@ -287,5 +287,13 @@ __ ( 'something-else' ); +/* + * Safeguard correct handling of namespaced function calls (partially qualified is already tested above). + */ +\MyNamespace\load_textdomain( 'text-domain', '/path/to/file.mo' ); +namespace\load_textdomain( 'text-domain', '/path/to/file.mo' ); +\load_textdomain( 'something-else', '/path/to/file.mo' ); +\load_textdomain( 'something-else' ); + // phpcs:set WordPress.Utils.I18nTextDomainFixer old_text_domain[] // phpcs:set WordPress.Utils.I18nTextDomainFixer new_text_domain false diff --git a/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.php b/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.php index a8c7c95e84..5674720986 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. @@ -154,6 +154,8 @@ public function getErrorList( $testFile = '' ) { 245 => 1, 277 => 1, 278 => 1, + 289 => 1, + 290 => 1, ); default: diff --git a/WordPress/Tests/WP/AlternativeFunctionsUnitTest.inc b/WordPress/Tests/WP/AlternativeFunctionsUnitTest.inc index a88e5638a9..3336dd0b18 100644 --- a/WordPress/Tests/WP/AlternativeFunctionsUnitTest.inc +++ b/WordPress/Tests/WP/AlternativeFunctionsUnitTest.inc @@ -147,3 +147,11 @@ file_get_contents( // Not using plugin_dir_path() for reasons. $url ); // Warning. + +/* + * Safeguard support for PHP 8.0+ tokenization of namespaced "names". + */ +\curl_init(); +MyNamespace\curl_init(); +\MyNamespace\curl_init(); +namespace\curl_init(); // The sniff should start flagging this once it can resolve relative namespaces. diff --git a/WordPress/Tests/WP/AlternativeFunctionsUnitTest.php b/WordPress/Tests/WP/AlternativeFunctionsUnitTest.php index b0f1bf96aa..e509e1805b 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. @@ -89,6 +89,7 @@ public function getWarningList() { 131 => 1, 142 => 1, 146 => 1, + 154 => 1, ); } } diff --git a/WordPress/Tests/WP/CapabilitiesUnitTest.1.inc b/WordPress/Tests/WP/CapabilitiesUnitTest.1.inc index ab1fdb9c41..698f57228d 100644 --- a/WordPress/Tests/WP/CapabilitiesUnitTest.1.inc +++ b/WordPress/Tests/WP/CapabilitiesUnitTest.1.inc @@ -111,8 +111,11 @@ add_menu_page( $p, $t, /* deliberately empty */, $slug, ); add_menu_page( [] ); // Should bow out because the parameter is not found. $obj->current_user_can( 'foo_bar' ); // Ok. We're not checking for method calls. -My\NamespaceS\add_posts_page( 'page_title', 'menu_title', 'administrator', 'menu_slug', 'function' ); // Ok. We're not checking namespaced functions. -// Parse error, should be handled correctly by bowing out. -// This must be the last test in the file! -add_posts_page( 'page_title', +/* + * Safeguard correct handling of all types of namespaced function calls. + */ +\add_posts_page( 'page_title', 'menu_title', 'administrator', 'menu_slug', 'function' ); // Bad. +My\NamespaceS\add_posts_page( 'page_title', 'menu_title', 'administrator', 'menu_slug', 'function' ); // Ok. We're not checking namespaced functions. +\MyNamespace\add_posts_page( 'page_title', 'menu_title', 'administrator', 'menu_slug', 'function' ); // Ok. +namespace\add_posts_page( 'page_title', 'menu_title', 'administrator', 'menu_slug', 'function' ); // The sniff should start flagging this once it can resolve relative namespaces. diff --git a/WordPress/Tests/WP/CapabilitiesUnitTest.5.inc b/WordPress/Tests/WP/CapabilitiesUnitTest.5.inc new file mode 100644 index 0000000000..d5f8d0d393 --- /dev/null +++ b/WordPress/Tests/WP/CapabilitiesUnitTest.5.inc @@ -0,0 +1,8 @@ + 1, 85 => 1, 106 => 1, + 118 => 1, ); case 'CapabilitiesUnitTest.3.inc': diff --git a/WordPress/Tests/WP/CapitalPDangitUnitTest.php b/WordPress/Tests/WP/CapitalPDangitUnitTest.php index ea1cfcf65e..d5a9dc2791 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 7869ea7f6f..01ef4cb6d1 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 e9541f49d2..2dc9379fad 100644 --- a/WordPress/Tests/WP/CronIntervalUnitTest.inc +++ b/WordPress/Tests/WP/CronIntervalUnitTest.inc @@ -165,7 +165,7 @@ add_filter( 'cron_schedules', function ( $schedules ) { class FQNConstants { public function add_schedules() { add_filter( 'cron_schedules', array( $this, 'add_weekly_schedule' ) ); // Ok: > 15 min. - add_filter( 'cron_schedules', array( $this, 'add_eight_minute_schedule' ) ); // Warning: 8 min. + \add_filter( 'cron_schedules', array( $this, 'add_eight_minute_schedule' ) ); // Warning: 8 min. add_filter( 'cron_schedules', array( $this, 'add_hundred_minute_schedule' ) ); // Warning: time undetermined. add_filter( 'cron_schedules', array( $this, 'sneaky_fake_wp_constant_schedule' ) ); // Warning: time undetermined. } @@ -326,3 +326,11 @@ function first_class_six_min_schedule( $schedules ) { } add_filter( 'cron_schedules', first_class_six_min_schedule(...)); // Warning: 6 min. add_filter( 'cron_schedules', 'first_class_six_min_schedule'(...)); // Warning: 6 min. + +/* + * Safeguard correct handling of all types of namespaced function calls (except FQN global function call which is + * handled above). + */ +MyNamespace\add_filter( 'cron_schedules', 'unknown_callback' ); // Ok. +\MyNamespace\add_filter( 'cron_schedules', 'unknown_callback' ); // Ok. +namespace\add_filter( 'cron_schedules', 'unknown_callback' ); // Ok. The sniff should start flagging this once it can resolve relative namespaces. diff --git a/WordPress/Tests/WP/CronIntervalUnitTest.php b/WordPress/Tests/WP/CronIntervalUnitTest.php index 81f02c2768..66f5d527c3 100644 --- a/WordPress/Tests/WP/CronIntervalUnitTest.php +++ b/WordPress/Tests/WP/CronIntervalUnitTest.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 CronInterval sniff. @@ -20,7 +20,7 @@ * * @covers \WordPressCS\WordPress\Sniffs\WP\CronIntervalSniff */ -final class CronIntervalUnitTest extends AbstractSniffUnitTest { +final class CronIntervalUnitTest extends AbstractSniffTestCase { /** * Returns the lines where errors should occur. diff --git a/WordPress/Tests/WP/DeprecatedClassesUnitTest.php b/WordPress/Tests/WP/DeprecatedClassesUnitTest.php index 682f144ebe..cca29723cb 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 fb53e37231..7fda5f94a7 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.inc b/WordPress/Tests/WP/DeprecatedParameterValuesUnitTest.1.inc similarity index 88% rename from WordPress/Tests/WP/DeprecatedParameterValuesUnitTest.inc rename to WordPress/Tests/WP/DeprecatedParameterValuesUnitTest.1.inc index c2f75c8747..73345f6d7e 100644 --- a/WordPress/Tests/WP/DeprecatedParameterValuesUnitTest.inc +++ b/WordPress/Tests/WP/DeprecatedParameterValuesUnitTest.1.inc @@ -55,5 +55,10 @@ wp_get_typography_font_size_value( $preset, array() ); // OK. wp_get_typography_font_size_value( $preset, true ); // Error. wp_get_typography_font_size_value( $preset, false ); // Error. -// Live coding/parse error. -get_bloginfo( show: /*to do*/, ); +/* + * Safeguard correct handling of all types of namespaced function calls. + */ +\get_bloginfo( 'home' ); // Bad. +MyNamespace\get_bloginfo( 'home' ); // Ok. +\MyNamespace\get_bloginfo( 'home' ); // Ok. +namespace\get_bloginfo( 'home' ); // The sniff should start flagging this once it can resolve relative namespaces. diff --git a/WordPress/Tests/WP/DeprecatedParameterValuesUnitTest.2.inc b/WordPress/Tests/WP/DeprecatedParameterValuesUnitTest.2.inc new file mode 100644 index 0000000000..b5923d40e4 --- /dev/null +++ b/WordPress/Tests/WP/DeprecatedParameterValuesUnitTest.2.inc @@ -0,0 +1,8 @@ + Key is the line number, value is the number of expected errors. */ - public function getErrorList() { - return array( - 5 => 1, - 6 => 1, - 7 => 1, - 8 => 1, - 9 => 1, - 10 => 1, - 11 => 1, - 12 => 1, - 13 => 1, - 14 => 1, - 15 => 1, - 16 => 1, - 17 => 1, - 18 => 1, - 35 => 1, - 40 => 1, - 43 => 1, - 44 => 1, - 45 => 1, - 46 => 1, - 47 => 1, - 48 => 1, - 49 => 1, - 50 => 1, - 51 => 1, - ); + public function getErrorList( $testFile = '' ) { + switch ( $testFile ) { + case 'DeprecatedParameterValuesUnitTest.1.inc': + return array( + 5 => 1, + 6 => 1, + 7 => 1, + 8 => 1, + 9 => 1, + 10 => 1, + 11 => 1, + 12 => 1, + 13 => 1, + 14 => 1, + 15 => 1, + 16 => 1, + 17 => 1, + 18 => 1, + 35 => 1, + 40 => 1, + 43 => 1, + 44 => 1, + 45 => 1, + 46 => 1, + 47 => 1, + 48 => 1, + 49 => 1, + 50 => 1, + 51 => 1, + 61 => 1, + ); + + default: + return array(); + } } /** * Returns the lines where warnings should occur. * + * @param string $testFile The name of the file being tested. + * * @return array Key is the line number, value is the number of expected warnings. */ - public function getWarningList() { - return array( - 55 => 1, - 56 => 1, - ); + public function getWarningList( $testFile = '' ) { + switch ( $testFile ) { + case 'DeprecatedParameterValuesUnitTest.1.inc': + return array( + 55 => 1, + 56 => 1, + ); + + default: + return array(); + } } } diff --git a/WordPress/Tests/WP/DeprecatedParametersUnitTest.inc b/WordPress/Tests/WP/DeprecatedParametersUnitTest.inc index 1fc0c27b04..2c0f32cdc8 100644 --- a/WordPress/Tests/WP/DeprecatedParametersUnitTest.inc +++ b/WordPress/Tests/WP/DeprecatedParametersUnitTest.inc @@ -99,3 +99,11 @@ global_terms( $foo, 'deprecated' ); // All will give an WARNING as they have been deprecated after WP 6.5. inject_ignored_hooked_blocks_metadata_attributes('', 'deprecated'); wp_render_elements_support_styles('deprecated'); + +/* + * Safeguard correct handling of all types of namespaced function calls. + */ +\add_option( '', '', [] ); // Bad. +MyNamespace\add_option( '', '', [] ); // Ok. +\MyNamespace\add_option( '', '', [] ); // Ok. +namespace\add_option( '', '', [] ); // The sniff should start flagging this once it can resolve relative namespaces. diff --git a/WordPress/Tests/WP/DeprecatedParametersUnitTest.php b/WordPress/Tests/WP/DeprecatedParametersUnitTest.php index 3d516ec983..d922bd41d8 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. @@ -42,6 +42,9 @@ public function getErrorList() { $errors[50] = 2; $errors[76] = 2; + // Fully qualified function call. + $errors[106] = 1; + return $errors; } diff --git a/WordPress/Tests/WP/DiscouragedConstantsUnitTest.inc b/WordPress/Tests/WP/DiscouragedConstantsUnitTest.inc index ea8ae72524..bed0914d53 100644 --- a/WordPress/Tests/WP/DiscouragedConstantsUnitTest.inc +++ b/WordPress/Tests/WP/DiscouragedConstantsUnitTest.inc @@ -102,3 +102,11 @@ enum ContainsConst { } echo HEADER_TEXTCOLOR::$var; // OK. + +/* + * Safeguard correct handling of all types of namespaced function calls. + */ +\define( 'STYLESHEETPATH', 'something' ); // Bad. +\MyNamespace\define( 'STYLESHEETPATH', 'something' ); // Ok. +MyNamespace\define( 'STYLESHEETPATH', 'something' ); // Ok. +namespace\define( 'STYLESHEETPATH', 'something' ); // Ok. diff --git a/WordPress/Tests/WP/DiscouragedConstantsUnitTest.php b/WordPress/Tests/WP/DiscouragedConstantsUnitTest.php index e27fd838c2..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. @@ -36,26 +36,26 @@ public function getErrorList() { */ public function getWarningList() { return array( - 50 => 1, - 51 => 1, - 52 => 1, - 53 => 1, - 54 => 1, - 55 => 1, - 56 => 1, - 57 => 1, - 58 => 1, - 59 => 1, - 60 => 1, - 61 => 1, - 63 => 1, - 64 => 1, - 66 => 1, - 67 => 1, - 71 => 1, - 72 => 1, - 83 => 1, - 88 => 1, + 50 => 1, + 51 => 1, + 52 => 1, + 53 => 1, + 54 => 1, + 55 => 1, + 56 => 1, + 57 => 1, + 58 => 1, + 59 => 1, + 60 => 1, + 61 => 1, + 63 => 1, + 66 => 1, + 67 => 1, + 71 => 1, + 72 => 1, + 83 => 1, + 88 => 1, + 109 => 1, ); } } diff --git a/WordPress/Tests/WP/DiscouragedFunctionsUnitTest.php b/WordPress/Tests/WP/DiscouragedFunctionsUnitTest.php index 825ff39702..92ce2acf43 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. @@ -22,7 +22,7 @@ * @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.1.inc b/WordPress/Tests/WP/EnqueuedResourceParametersUnitTest.1.inc index 4a73b458d0..3e15b71623 100644 --- a/WordPress/Tests/WP/EnqueuedResourceParametersUnitTest.1.inc +++ b/WordPress/Tests/WP/EnqueuedResourceParametersUnitTest.1.inc @@ -29,7 +29,7 @@ wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), function() { }, true ); // OK. wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), $version, true ); // OK. -wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), '1.1.0' ); // Warning - In Footer is set to a falsy (default) value. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), '1.1.0' ); // Warning - $in_footer is not explicitly set. wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), '1.1.0', false ); // OK. wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), '1.1.0', null ); // OK. wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), '1.1.0', 0 ); // OK. @@ -95,3 +95,25 @@ wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), (double) 0, true ); // Error - 0, false or NULL are not allowed. wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), (binary) 0, true ); // Error - 0, false or NULL are not allowed. + +// Safeguard handling of non-lowercase `null`. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), NULL, true ); // Error - 0, false or NULL are not allowed. + +/* + * Safeguard handling of fully qualified \true, \false and \null. + * Also safeguard that adding T_NS_SEPARATOR to $false_tokens doesn't cause false positives due to problems in is_falsy(). + */ +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), \FALSE, true ); // Error - 0, false or NULL are not allowed. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), \null, true ); // Error - 0, false or NULL are not allowed. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), \Null, true ); // Error - 0, false or NULL are not allowed. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), \true, true ); // Ok. +wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), \get_version(), true ); // OK. + +/* + * Safeguard correct handling of all types of namespaced function calls. + */ +\wp_enqueue_script( 'script-name', 'https://example.com/someScript.js', false, '1.1.0' ); // Error - missing $in_footer. +\wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), '1.1.0' ); // Error - missing $in_footer. +MyNamespace\wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), '1.1.0' ); // Ok. +\MyNamespace\wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), '1.1.0' ); // Ok. +namespace\wp_register_script( 'someScript-js', 'https://example.com/someScript.js' , array( 'jquery' ), '1.1.0' ); // The sniff should start flagging this once it can resolve relative namespaces. diff --git a/WordPress/Tests/WP/EnqueuedResourceParametersUnitTest.php b/WordPress/Tests/WP/EnqueuedResourceParametersUnitTest.php index 4273b3f016..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. @@ -31,22 +31,23 @@ public function getErrorList( $testFile = '' ) { switch ( $testFile ) { case 'EnqueuedResourceParametersUnitTest.1.inc': return array( - 6 => 1, - 9 => 1, - 10 => 1, - 12 => 1, - 13 => 1, - 14 => 1, - 22 => 1, - 54 => 1, - 57 => 1, - 61 => 1, - 82 => 1, - 85 => 1, - 89 => 1, - 92 => 1, - 95 => 1, - 97 => 1, + 6 => 1, + 9 => 1, + 10 => 1, + 12 => 1, + 13 => 1, + 14 => 1, + 22 => 1, + 54 => 1, + 57 => 1, + 61 => 1, + 82 => 1, + 85 => 1, + 89 => 1, + 92 => 1, + 95 => 1, + 97 => 1, + 106 => 1, ); case 'EnqueuedResourceParametersUnitTest.2.inc': @@ -70,14 +71,19 @@ public function getWarningList( $testFile = '' ) { switch ( $testFile ) { case 'EnqueuedResourceParametersUnitTest.1.inc': return array( - 3 => 2, - 11 => 1, - 32 => 1, - 39 => 2, - 42 => 1, - 45 => 1, - 66 => 2, - 77 => 1, + 3 => 2, + 11 => 1, + 32 => 1, + 39 => 2, + 42 => 1, + 45 => 1, + 66 => 2, + 77 => 1, + 100 => 1, + 107 => 1, + 108 => 1, + 115 => 1, + 116 => 1, ); default: 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.inc b/WordPress/Tests/WP/GetMetaSingleUnitTest.inc index 54ea2fecc0..02001afef3 100644 --- a/WordPress/Tests/WP/GetMetaSingleUnitTest.inc +++ b/WordPress/Tests/WP/GetMetaSingleUnitTest.inc @@ -46,3 +46,10 @@ $warning = get_metadata( ); $warning = get_metadata_raw( 'post', $post_id, $meta_key ); $warning = get_metadata_default( 'post', $post_id, $meta_key ); + +/* + * Safeguard correct handling of fully qualified and relative namespaced function calls (fully qualified global function + * call and partially qualified namespaced function call are already handled above). + */ +\MyNamespace\get_post_meta( $post_id, $meta_key ); // Ok. +namespace\get_post_meta( $post_id, $meta_key ); // The sniff should start flagging this once it can resolve relative namespaces. 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.1.inc b/WordPress/Tests/WP/I18nUnitTest.1.inc index 6256e6d84a..a54094ad5c 100644 --- a/WordPress/Tests/WP/I18nUnitTest.1.inc +++ b/WordPress/Tests/WP/I18nUnitTest.1.inc @@ -317,4 +317,14 @@ esc_html_e( 'foo', '' ); // Bad: text-domain can not be empty. // PHP 8.0+: safeguard handling of newly introduced placeholders. __( 'There are %1$h monkeys in the %H', 'my-slug' ); // Bad: multiple arguments should be numbered. +/* + * Safeguard correct handling of all types of namespaced function calls. + */ +\_( 'foo', 'my-slug' ); // Bad. +\translate( 'foo', 'my-slug' ); // Bad. +\translate_with_gettext_context( 'foo', 'bar', 'my-slug' ); // Bad. +MyNamespace\translate_with_gettext_context( 'foo', 'bar', 'my-slug' ); // Ok. +\MyNamespace\translate_with_gettext_context( 'foo', 'bar', 'my-slug' ); // Ok. +namespace\translate_with_gettext_context( 'foo', 'bar', 'my-slug' ); // The sniff should start flagging this once it can resolve relative namespaces. + // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment diff --git a/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed b/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed index b23e9b6192..7c595dde5c 100644 --- a/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed +++ b/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed @@ -317,4 +317,14 @@ esc_html_e( 'foo', '' ); // Bad: text-domain can not be empty. // PHP 8.0+: safeguard handling of newly introduced placeholders. __( 'There are %1$h monkeys in the %H', 'my-slug' ); // Bad: multiple arguments should be numbered. +/* + * Safeguard correct handling of all types of namespaced function calls. + */ +\_( 'foo', 'my-slug' ); // Bad. +\translate( 'foo', 'my-slug' ); // Bad. +\translate_with_gettext_context( 'foo', 'bar', 'my-slug' ); // Bad. +MyNamespace\translate_with_gettext_context( 'foo', 'bar', 'my-slug' ); // Ok. +\MyNamespace\translate_with_gettext_context( 'foo', 'bar', 'my-slug' ); // Ok. +namespace\translate_with_gettext_context( 'foo', 'bar', 'my-slug' ); // The sniff should start flagging this once it can resolve relative namespaces. + // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment diff --git a/WordPress/Tests/WP/I18nUnitTest.php b/WordPress/Tests/WP/I18nUnitTest.php index 3834eb189f..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. @@ -148,6 +148,7 @@ public function getErrorList( $testFile = '' ) { 311 => 1, 315 => 1, 318 => 1, + 323 => 1, ); case 'I18nUnitTest.2.inc': @@ -217,6 +218,8 @@ public function getWarningList( $testFile = '' ) { 300 => 1, 301 => 1, 302 => 1, + 324 => 1, + 325 => 1, ); case 'I18nUnitTest.2.inc': 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/WordPress/Util/Tests/Helpers/ConstantsHelper/IsUseOfGlobalConstantUnitTest.inc b/WordPress/Util/Tests/Helpers/ConstantsHelper/IsUseOfGlobalConstantUnitTest.inc new file mode 100644 index 0000000000..0534dd86fb --- /dev/null +++ b/WordPress/Util/Tests/Helpers/ConstantsHelper/IsUseOfGlobalConstantUnitTest.inc @@ -0,0 +1,86 @@ +PHP_OS; +/* test return false 19 */ $obj?->PHP_OS; +class YetAnotherClass { + use MyTrait { + /* test return false 20 */ + foo as private PHP_OS; + /* test return false 21 */ + bar as protected PHP_OS; + /* test return false 22 */ + baz as public PHP_OS; + } +} +/* test return false 23 */ \My\Ns\PHP_OS; +/* test return false 24 */ My\Ns\PHP_OS; +/* test return false 25 */ namespace\Ns\PHP_OS; +class Foo { + /* test return false 26 */ const PHP_OS = 1; +} +/* test return false 27 */use const SomeNamespace\PHP_OS as EOL; +/* test return false 28 */use const ABC as PHP_OS; +/* test return false 29 */use const SomeNamespace\{PHP_OS, PHP_VERSION_ID}; + +/* + * The below should be recognized as use of a global constant. + */ + +/* test return true 1 */ +echo PHP_OS; + +/* test return true 2 */ +echo \PHP_OS; + +$folder = basename( /* test return true 3 */ PHP_OS ); + +/* test return true 4 */ +include PHP_OS . '/js/myfile.js'; + +/* test return true 5 */ +use const PHP_OS as SSP; + +/* test return true 6 */ +switch( PHP_OS ) { + /* test return true 7 */ + case PHP_OS: + break; +} + +/* test return true 8 */ +const PHP_OS = 'something'; + +/* test return true 9 */ +$array[PHP_OS] = 'something'; + +/* test return true 10 */ +const ABC = '123', + DEF = '456', + PHP_OS = 'something', + GHI = 789; diff --git a/WordPress/Util/Tests/Helpers/ConstantsHelper/IsUseOfGlobalConstantUnitTest.php b/WordPress/Util/Tests/Helpers/ConstantsHelper/IsUseOfGlobalConstantUnitTest.php new file mode 100644 index 0000000000..73537d4b21 --- /dev/null +++ b/WordPress/Util/Tests/Helpers/ConstantsHelper/IsUseOfGlobalConstantUnitTest.php @@ -0,0 +1,152 @@ +assertFalse( + ConstantsHelper::is_use_of_global_constant( + self::$phpcsFile, + -1 + ) + ); + } + + /** + * Test is_use_of_global_constant() returns false for non-constant contexts. + * + * @dataProvider dataIsUseOfGlobalConstantShouldReturnFalse + * + * @param string $commentString The comment which prefaces the target token in the test file. + * @param int $tokenType The token type to use for the target token. + * @param string|null $tokenContent The token content to use for the target token. + * + * @return void + */ + public function testIsUseOfGlobalConstantShouldReturnFalse( $commentString, $tokenType = null, $tokenContent = null ) { + if ( null === $tokenType ) { + $tokenType = Collections::nameTokens(); + + $isPhpcs3 = version_compare( Helper::getVersion(), '3.99.99', '<=' ); + + if ( null === $tokenContent || $isPhpcs3 ) { + $tokenContent = 'PHP_OS'; + } + } + + $stackPtr = $this->getTargetToken( $commentString, $tokenType, $tokenContent ); + $this->assertFalse( ConstantsHelper::is_use_of_global_constant( self::$phpcsFile, $stackPtr ) ); + } + + /** + * Data provider. + * + * @return array + * @see testIsUseOfGlobalConstantShouldReturnFalse() + */ + public static function dataIsUseOfGlobalConstantShouldReturnFalse() { + return array( + array( '/* test return false 1 */', \T_VARIABLE ), + array( '/* test return false 2 */' ), + array( '/* test return false 3 */', null, 'PHP_OS\Ns\SomeClass' ), + array( '/* test return false 4 */' ), + array( '/* test return false 5 */' ), + array( '/* test return false 6 */' ), + array( '/* test return false 7 */' ), + array( '/* test return false 8 */' ), + array( '/* test return false 9 */' ), + array( '/* test return false 10 */' ), + array( '/* test return false 11 */' ), + array( '/* test return false 12 */' ), + array( '/* test return false 13 */' ), + array( '/* test return false 14 */' ), + array( '/* test return false 15 */' ), + array( '/* test return false 16 */' ), + array( '/* test return false 17 */' ), + array( '/* test return false 18 */' ), + array( '/* test return false 19 */' ), + array( '/* test return false 20 */' ), + array( '/* test return false 21 */' ), + array( '/* test return false 22 */' ), + array( '/* test return false 23 */', null, '\My\Ns\PHP_OS' ), + array( '/* test return false 24 */', null, 'My\Ns\PHP_OS' ), + array( '/* test return false 25 */', null, 'namespace\Ns\PHP_OS' ), + array( '/* test return false 26 */' ), + array( '/* test return false 27 */', null, 'SomeNamespace\PHP_OS' ), + array( '/* test return false 28 */' ), + array( '/* test return false 29 */' ), + ); + } + + /** + * Test is_use_of_global_constant() returns true for use of global constants. + * + * @dataProvider dataIsUseOfGlobalConstantShouldReturnTrue + * + * @param string $commentString The comment which prefaces the target token in the test file. + * @param string|null $tokenContent The token content to use for the target token. + * + * @return void + */ + public function testIsUseOfGlobalConstantShouldReturnTrue( $commentString, $tokenContent = null ) { + $isPhpcs3 = version_compare( Helper::getVersion(), '3.99.99', '<=' ); + + if ( null === $tokenContent || $isPhpcs3 ) { + $tokenContent = 'PHP_OS'; + } + + $tokenTypes = array( + \T_STRING => \T_STRING, + \T_NAME_FULLY_QUALIFIED => \T_NAME_FULLY_QUALIFIED, + ); + + $stackPtr = $this->getTargetToken( $commentString, $tokenTypes, $tokenContent ); + $this->assertTrue( ConstantsHelper::is_use_of_global_constant( self::$phpcsFile, $stackPtr ) ); + } + + /** + * Data provider. + * + * @return array + * @see testIsUseOfGlobalConstantShouldReturnTrue() + */ + public static function dataIsUseOfGlobalConstantShouldReturnTrue() { + return array( + array( '/* test return true 1 */' ), + array( '/* test return true 2 */', '\PHP_OS' ), + array( '/* test return true 3 */' ), + array( '/* test return true 4 */' ), + array( '/* test return true 5 */' ), + array( '/* test return true 6 */' ), + array( '/* test return true 7 */' ), + array( '/* test return true 8 */' ), + array( '/* test return true 9 */' ), + array( '/* test return true 10 */' ), + ); + } +} diff --git a/WordPress/Util/Tests/Helpers/ContextHelper/IsInArrayComparisonUnitTest.inc b/WordPress/Util/Tests/Helpers/ContextHelper/IsInArrayComparisonUnitTest.inc new file mode 100644 index 0000000000..0380c3535f --- /dev/null +++ b/WordPress/Util/Tests/Helpers/ContextHelper/IsInArrayComparisonUnitTest.inc @@ -0,0 +1,64 @@ +in_array( /* test return false 6 */ $needle, $haystack ); +// Static method call +ArrayHelper::in_array( /* test return false 7 */ $needle, $haystack ); +// array_keys without filter_value parameter +array_keys( /* test return false 8 */ $array ); +// array_keys with wrong parameter name (should be filter_value) +array_keys( array: /* test return false 9 */ $array, search_value: 'value', strict: true ); +// Variable not inside an array comparison function +some_other_function( /* test return false 10 */ $variable ); +// Relative namespace function call +namespace\in_array( /* test return false 11 */ $needle, $haystack ); + +// Test cases that should return true + +// Basic in_array usage +in_array( /* test return true 1 */ $needle, $haystack ); +in_array( /* test return true 2 */ $needle, $haystack, true ); + +// Basic array_search usage +array_search( /* test return true 3 */ $needle, $haystack ); +array_search( /* test return true 4 */ $needle, $haystack, true ); + +// array_keys with filter_value parameter (second parameter) +array_keys( $array, /* test return true 5 */ $filter_value ); +array_keys( $array, /* test return true 6 */ $filter_value, true ); + +// array_keys with named parameters +array_keys( array: $array, filter_value: /* test return true 7 */ $search_value ); +array_keys( array: $array, filter_value: /* test return true 8 */ $search_value, strict: true ); + +// Case insensitive function names +IN_ARRAY( /* test return true 9 */ $needle, $haystack ); +Array_Search( /* test return true 10 */ $needle, $haystack ); +ARRAY_KEYS( $array, /* test return true 11 */ $filter_value ); + +// Fully qualified global function calls +\in_array( /* test return true 12 */ $needle, $haystack ); +\array_search( /* test return true 13 */ $needle, $haystack ); +\array_keys( $array, /* test return true 14 */ $filter_value ); + +// Complex expressions as parameters +in_array( /* test return true 15 */ $obj->property, $haystack ); +array_search( /* test return true 16 */ $array['key'], $haystack ); +array_keys( $data, /* test return true 17 */ get_value() ); + +// Multiple variables in the same function call +in_array( /* test return true 18 */ $needle1, $haystack ); +in_array( $needle2, /* test return true 19 */ $haystack ); + +// Nested array access +array_keys( /* test return true 20 */ $nested['array']['data'], $filter ); \ No newline at end of file diff --git a/WordPress/Util/Tests/Helpers/ContextHelper/IsInArrayComparisonUnitTest.php b/WordPress/Util/Tests/Helpers/ContextHelper/IsInArrayComparisonUnitTest.php new file mode 100644 index 0000000000..1bc64245a9 --- /dev/null +++ b/WordPress/Util/Tests/Helpers/ContextHelper/IsInArrayComparisonUnitTest.php @@ -0,0 +1,107 @@ +getTargetToken( $commentString, $tokenType ); + $result = ContextHelper::is_in_array_comparison( self::$phpcsFile, $stackPtr ); + $this->assertFalse( $result ); + } + + /** + * Data provider. + * + * @return array + * @see testIsInArrayComparisonShouldReturnFalse() + */ + public static function dataIsInArrayComparisonShouldReturnFalse() { + return array( + array( '/* test return false 1 */', \T_VARIABLE ), + array( '/* test return false 2 */', \T_VARIABLE ), + array( '/* test return false 3 */', \T_VARIABLE ), + array( '/* test return false 4 */', \T_VARIABLE ), + array( '/* test return false 5 */', \T_VARIABLE ), + array( '/* test return false 6 */', \T_VARIABLE ), + array( '/* test return false 7 */', \T_VARIABLE ), + array( '/* test return false 8 */', \T_VARIABLE ), + array( '/* test return false 9 */', \T_VARIABLE ), + array( '/* test return false 11 */', \T_VARIABLE ), + ); + } + + /** + * Test is_in_array_comparison() returns true if given token is inside an array comparison function. + * + * @dataProvider dataIsInArrayComparisonShouldReturnTrue + * + * @param string $commentString The comment which prefaces the target token in the test file. + * @param int|string $tokenType The token type to search for. + * + * @return void + */ + public function testIsInArrayComparisonShouldReturnTrue( $commentString, $tokenType ) { + $stackPtr = $this->getTargetToken( $commentString, $tokenType ); + $result = ContextHelper::is_in_array_comparison( self::$phpcsFile, $stackPtr ); + $this->assertTrue( $result ); + } + + /** + * Data provider. + * + * @return array + * @see testIsInArrayComparisonShouldReturnTrue() + */ + public static function dataIsInArrayComparisonShouldReturnTrue() { + return array( + array( '/* test return true 1 */', \T_VARIABLE ), + array( '/* test return true 2 */', \T_VARIABLE ), + array( '/* test return true 3 */', \T_VARIABLE ), + array( '/* test return true 4 */', \T_VARIABLE ), + array( '/* test return true 5 */', \T_VARIABLE ), + array( '/* test return true 6 */', \T_VARIABLE ), + array( '/* test return true 7 */', \T_VARIABLE ), + array( '/* test return true 8 */', \T_VARIABLE ), + array( '/* test return true 9 */', \T_VARIABLE ), + array( '/* test return true 10 */', \T_VARIABLE ), + array( '/* test return true 11 */', \T_VARIABLE ), + array( '/* test return true 12 */', \T_VARIABLE ), + array( '/* test return true 13 */', \T_VARIABLE ), + array( '/* test return true 14 */', \T_VARIABLE ), + array( '/* test return true 15 */', \T_VARIABLE ), + array( '/* test return true 16 */', \T_VARIABLE ), + array( '/* test return true 17 */', \T_STRING ), + array( '/* test return true 18 */', \T_VARIABLE ), + array( '/* test return true 19 */', \T_VARIABLE ), + array( '/* test return true 20 */', \T_VARIABLE ), + ); + } +} diff --git a/WordPress/Util/Tests/Helpers/ContextHelper/IsInFunctionCallUnitTest.inc b/WordPress/Util/Tests/Helpers/ContextHelper/IsInFunctionCallUnitTest.inc new file mode 100644 index 0000000000..36f11ca226 --- /dev/null +++ b/WordPress/Util/Tests/Helpers/ContextHelper/IsInFunctionCallUnitTest.inc @@ -0,0 +1,57 @@ +my_function( /* test inside function pointer 11 */ $a ); +/* test function call 12 */ $obj?->my_function( /* test inside function pointer 12 */ $a ); + +/* + * Make sure that tokens inside a function call are correctly identified when `$allow_nested` is + * set to true. + * + * The below should be recognized as inside a function call to one of the valid functions. + */ +/* test function call 13 */ my_function( another_function( /* test inside function pointer 13 */ $a ) ); +another_function( /* test function call 14 */ my_function( /* test inside function pointer 14 */ $a ) ); +/* test function call 15 */ my_function( middle_function( inner_function( /* test inside function pointer 15 */ $a ) ) ); diff --git a/WordPress/Util/Tests/Helpers/ContextHelper/IsInFunctionCallUnitTest.php b/WordPress/Util/Tests/Helpers/ContextHelper/IsInFunctionCallUnitTest.php new file mode 100644 index 0000000000..0389cfd67b --- /dev/null +++ b/WordPress/Util/Tests/Helpers/ContextHelper/IsInFunctionCallUnitTest.php @@ -0,0 +1,165 @@ +getTargetToken( $commentString, $tokenType ); + $result = ContextHelper::is_in_function_call( self::$phpcsFile, $stackPtr, array( 'my_function' => true ) ); + $this->assertFalse( $result ); + } + + /** + * Data provider. + * + * @return array + * @see testIsInFunctionCallShouldReturnFalse() + */ + public static function dataIsInFunctionCallShouldReturnFalse() { + return array( + array( '/* test return false 1 */', \T_CONSTANT_ENCAPSED_STRING ), + array( '/* test return false 2 */', \T_VARIABLE ), + array( '/* test return false 3 */', \T_VARIABLE ), + array( '/* test return false 4 */', \T_VARIABLE ), + array( '/* test return false 5 */', \T_VARIABLE ), + array( '/* test return false 6 */', \T_VARIABLE ), + array( '/* test return false 7 */', \T_VARIABLE ), + array( '/* test return false 8 */', \T_VARIABLE ), + ); + } + + /** + * Test is_in_function_call() returns pointer to function name if given token is inside a function call. + * + * @dataProvider dataIsInFunctionCallShouldReturnFunctionPointer + * + * @param string $insideFunctionCommentString The comment which prefaces the target token inside a function in the test file. + * @param int|string $insideFunctionTokenType The token type to search for. + * @param string $functionCallCommentString The comment which prefaces the function call token in the test file. + * + * @return void + */ + public function testIsInFunctionCallShouldReturnFunctionPointer( $insideFunctionCommentString, $insideFunctionTokenType, $functionCallCommentString ) { + $insideFunctionPtr = $this->getTargetToken( $insideFunctionCommentString, $insideFunctionTokenType ); + $functionNamePtr = $this->getTargetToken( $functionCallCommentString, Collections::nameTokens() ); + $result = ContextHelper::is_in_function_call( self::$phpcsFile, $insideFunctionPtr, array( 'my_function' => true ) ); + $this->assertSame( $result, $functionNamePtr ); + } + + /** + * Data provider. + * + * @return array + * @see testIsInFunctionCallShouldReturnFunctionPointer() + */ + public static function dataIsInFunctionCallShouldReturnFunctionPointer() { + return array( + array( '/* test inside function pointer 1 */', \T_VARIABLE, '/* test function call 1 */' ), + array( '/* test inside function pointer 2 */', \T_VARIABLE, '/* test function call 2 */' ), + array( '/* test inside function pointer 3 */', \T_VARIABLE, '/* test function call 3 */' ), + ); + } + + /** + * Test is_in_function_call() returns pointer to function name if given token is inside a + * function call when `$global_functions` is set to false. + * + * @dataProvider dataIsInFunctionCallShouldReturnFunctionPointerWhenGlobalIsFalse + * + * @param string $insideFunctionCommentString The comment which prefaces the target token inside a function in the test file. + * @param int|string $insideFunctionTokenType The token type to search for. + * @param string $functionCallCommentString The comment which prefaces the function call token in the test file. + * + * @return void + */ + public function testIsInFunctionCallShouldReturnFunctionPointerWhenGlobalIsFalse( $insideFunctionCommentString, $insideFunctionTokenType, $functionCallCommentString ) { + $insideFunctionPtr = $this->getTargetToken( $insideFunctionCommentString, $insideFunctionTokenType ); + $functionNamePtr = $this->getTargetToken( $functionCallCommentString, Collections::nameTokens() ); + $result = ContextHelper::is_in_function_call( self::$phpcsFile, $insideFunctionPtr, array( 'my_function' => true ), false ); + $this->assertSame( $result, $functionNamePtr ); + } + + /** + * Data provider. + * + * @return array + * @see testIsInFunctionCallShouldReturnFunctionPointerWhenGlobalIsFalse() + */ + public static function dataIsInFunctionCallShouldReturnFunctionPointerWhenGlobalIsFalse() { + return array( + array( '/* test inside function pointer 4 */', \T_VARIABLE, '/* test function call 4 */' ), + array( '/* test inside function pointer 5 */', \T_VARIABLE, '/* test function call 5 */' ), + array( '/* test inside function pointer 6 */', \T_VARIABLE, '/* test function call 6 */' ), + array( '/* test inside function pointer 7 */', \T_VARIABLE, '/* test function call 7 */' ), + array( '/* test inside function pointer 8 */', \T_VARIABLE, '/* test function call 8 */' ), + array( '/* test inside function pointer 9 */', \T_VARIABLE, '/* test function call 9 */' ), + array( '/* test inside function pointer 10 */', \T_VARIABLE, '/* test function call 10 */' ), + array( '/* test inside function pointer 11 */', \T_VARIABLE, '/* test function call 11 */' ), + array( '/* test inside function pointer 12 */', \T_VARIABLE, '/* test function call 12 */' ), + ); + } + + /** + * Test is_in_function_call() returns pointer to function name if given token is inside a + * function call when `$allow_nested` is set to true. + * + * @dataProvider dataIsInFunctionCallShouldReturnFunctionPointerWhenAllowNestedIsTrue + * + * @param string $insideFunctionCommentString The comment which prefaces the target token inside a function in the test file. + * @param int|string $insideFunctionTokenType The token type to search for. + * @param string $functionCallCommentString The comment which prefaces the function call token in the test file. + * + * @return void + */ + public function testIsInFunctionCallShouldReturnFunctionPointerWhenAllowNestedIsTrue( $insideFunctionCommentString, $insideFunctionTokenType, $functionCallCommentString ) { + $insideFunctionPtr = $this->getTargetToken( $insideFunctionCommentString, $insideFunctionTokenType ); + $functionNamePtr = $this->getTargetToken( $functionCallCommentString, Collections::nameTokens() ); + $result = ContextHelper::is_in_function_call( self::$phpcsFile, $insideFunctionPtr, array( 'my_function' => true ), true, true ); + $this->assertSame( $result, $functionNamePtr ); + } + + /** + * Data provider. + * + * @return array + * @see testIsInFunctionCallShouldReturnFunctionPointerWhenAllowNestedIsTrue() + */ + public static function dataIsInFunctionCallShouldReturnFunctionPointerWhenAllowNestedIsTrue() { + return array( + array( '/* test inside function pointer 13 */', \T_VARIABLE, '/* test function call 13 */' ), + array( '/* test inside function pointer 14 */', \T_VARIABLE, '/* test function call 14 */' ), + array( '/* test inside function pointer 15 */', \T_VARIABLE, '/* test function call 15 */' ), + ); + } +} diff --git a/WordPress/Util/Tests/Helpers/ContextHelper/IsInIssetOrEmptyUnitTest.inc b/WordPress/Util/Tests/Helpers/ContextHelper/IsInIssetOrEmptyUnitTest.inc new file mode 100644 index 0000000000..6c819227a6 --- /dev/null +++ b/WordPress/Util/Tests/Helpers/ContextHelper/IsInIssetOrEmptyUnitTest.inc @@ -0,0 +1,54 @@ +getTargetToken( $commentString, $tokenType ); + $result = ContextHelper::is_in_isset_or_empty( self::$phpcsFile, $stackPtr ); + $this->assertFalse( $result ); + } + + /** + * Data provider. + * + * @return array + * @see testIsInIssetOrEmptyShouldReturnFalse() + */ + public static function dataIsInIssetOrEmptyShouldReturnFalse() { + return array( + array( '/* test return false 1 */', \T_VARIABLE ), + array( '/* test return false 2 */', \T_VARIABLE ), + array( '/* test return false 3 */', \T_VARIABLE ), + array( '/* test return false 4 */', \T_VARIABLE ), + array( '/* test return false 5 */', \T_VARIABLE ), + array( '/* test return false 6 */', \T_VARIABLE ), + array( '/* test return false 7 */', \T_VARIABLE ), + array( '/* test return false 8 */', \T_VARIABLE ), + array( '/* test return false 9 */', \T_VARIABLE ), + array( '/* test return false 10 */', \T_VARIABLE ), + array( '/* test return false 11 */', \T_VARIABLE ), + array( '/* test return false 12 */', \T_VARIABLE ), + array( '/* test return false 13 */', \T_VARIABLE ), + ); + } + + /** + * Test is_in_isset_or_empty() returns true if given token is inside isset() or empty(). + * + * @dataProvider dataIsInIssetOrEmptyShouldReturnTrue + * + * @param string $commentString The comment which prefaces the target token in the test file. + * @param int|string $tokenType The token type to search for. + * + * @return void + */ + public function testIsInIssetOrEmptyShouldReturnTrue( $commentString, $tokenType ) { + $stackPtr = $this->getTargetToken( $commentString, $tokenType ); + $result = ContextHelper::is_in_isset_or_empty( self::$phpcsFile, $stackPtr ); + $this->assertTrue( $result ); + } + + /** + * Data provider. + * + * @return array + * @see testIsInIssetOrEmptyShouldReturnTrue() + */ + public static function dataIsInIssetOrEmptyShouldReturnTrue() { + return array( + array( '/* test return true 1 */', \T_VARIABLE ), + array( '/* test return true 2 */', \T_VARIABLE ), + array( '/* test return true 3 */', \T_VARIABLE ), + array( '/* test return true 4 */', \T_VARIABLE ), + array( '/* test return true 5 */', \T_VARIABLE ), + array( '/* test return true 6 */', \T_VARIABLE ), + array( '/* test return true 7 */', \T_VARIABLE ), + array( '/* test return true 8 */', \T_VARIABLE ), + array( '/* test return true 9 */', \T_VARIABLE ), + array( '/* test return true 10 */', \T_VARIABLE ), + array( '/* test return true 11 */', \T_VARIABLE ), + array( '/* test return true 12 */', \T_VARIABLE ), + array( '/* test return true 13 */', \T_VARIABLE ), + array( '/* test return true 14 */', \T_VARIABLE ), + array( '/* test return true 15 */', \T_VARIABLE ), + array( '/* test return true 16 */', \T_VARIABLE ), + ); + } +} \ No newline at end of file diff --git a/WordPress/Util/Tests/Helpers/ContextHelper/IsInTypeTestUnitTest.inc b/WordPress/Util/Tests/Helpers/ContextHelper/IsInTypeTestUnitTest.inc new file mode 100644 index 0000000000..3b2cf8479c --- /dev/null +++ b/WordPress/Util/Tests/Helpers/ContextHelper/IsInTypeTestUnitTest.inc @@ -0,0 +1,65 @@ +is_string( /* test return false 6 */ $value ); +// Static method call +TypeChecker::is_int( /* test return false 7 */ $value ); +// Function that is not a type test +strlen( /* test return false 8 */ $string ); +// Relative namespace function call +namespace\is_bool( /* test return false 9 */ $value ); +// Custom function with similar name +my_is_numeric( /* test return false 10 */ $value ); + +// Test cases that should return true + +// Basic type test functions +is_array( /* test return true 1 */ $data ); +is_bool( /* test return true 2 */ $flag ); +is_callable( /* test return true 3 */ $callback ); +is_countable( /* test return true 4 */ $items ); +is_double( /* test return true 5 */ $number ); +is_float( /* test return true 6 */ $decimal ); +is_int( /* test return true 7 */ $count ); +is_integer( /* test return true 8 */ $id ); +is_iterable( /* test return true 9 */ $collection ); +is_long( /* test return true 10 */ $timestamp ); +is_null( /* test return true 11 */ $optional ); +is_numeric( /* test return true 12 */ $value ); +is_object( /* test return true 13 */ $instance ); +is_real( /* test return true 14 */ $coefficient ); +is_resource( /* test return true 15 */ $handle ); +is_scalar( /* test return true 16 */ $primitive ); +is_string( /* test return true 17 */ $text ); + +// Case insensitive function names +IS_ARRAY( /* test return true 18 */ $data ); +Is_Bool( /* test return true 19 */ $flag ); +IS_NUMERIC( /* test return true 20 */ $value ); + +// Fully qualified global function calls +\is_array( /* test return true 21 */ $data ); +\is_string( /* test return true 22 */ $text ); +\is_numeric( /* test return true 23 */ $value ); + +// Complex expressions as parameters +is_array( /* test return true 24 */ $obj->property ); +is_string( /* test return true 25 */ $array['key'] ); +is_numeric( /* test return true 26 */ get_value() ); + +// Multiple variables in the same function call (though type test functions typically have one param) +is_callable( /* test return true 27 */ $callback, false ); + +// Nested array/object access +is_array( /* test return true 28 */ $nested['data']['items'] ); +is_object( /* test return true 29 */ $config->settings ); \ No newline at end of file diff --git a/WordPress/Util/Tests/Helpers/ContextHelper/IsInTypeTestUnitTest.php b/WordPress/Util/Tests/Helpers/ContextHelper/IsInTypeTestUnitTest.php new file mode 100644 index 0000000000..f4fd0c4ff0 --- /dev/null +++ b/WordPress/Util/Tests/Helpers/ContextHelper/IsInTypeTestUnitTest.php @@ -0,0 +1,116 @@ +getTargetToken( $commentString, $tokenType ); + $result = ContextHelper::is_in_type_test( self::$phpcsFile, $stackPtr ); + $this->assertFalse( $result ); + } + + /** + * Data provider. + * + * @return array + * @see testIsInTypeTestShouldReturnFalse() + */ + public static function dataIsInTypeTestShouldReturnFalse() { + return array( + array( '/* test return false 1 */', \T_VARIABLE ), + array( '/* test return false 2 */', \T_VARIABLE ), + array( '/* test return false 3 */', \T_VARIABLE ), + array( '/* test return false 4 */', \T_VARIABLE ), + array( '/* test return false 5 */', \T_VARIABLE ), + array( '/* test return false 6 */', \T_VARIABLE ), + array( '/* test return false 7 */', \T_VARIABLE ), + array( '/* test return false 8 */', \T_VARIABLE ), + array( '/* test return false 9 */', \T_VARIABLE ), + array( '/* test return false 10 */', \T_VARIABLE ), + ); + } + + /** + * Test is_in_type_test() returns true if given token is inside a type test function. + * + * @dataProvider dataIsInTypeTestShouldReturnTrue + * + * @param string $commentString The comment which prefaces the target token in the test file. + * @param int|string $tokenType The token type to search for. + * + * @return void + */ + public function testIsInTypeTestShouldReturnTrue( $commentString, $tokenType ) { + $stackPtr = $this->getTargetToken( $commentString, $tokenType ); + $result = ContextHelper::is_in_type_test( self::$phpcsFile, $stackPtr ); + $this->assertTrue( $result ); + } + + /** + * Data provider. + * + * @return array + * @see testIsInTypeTestShouldReturnTrue() + */ + public static function dataIsInTypeTestShouldReturnTrue() { + return array( + array( '/* test return true 1 */', \T_VARIABLE ), + array( '/* test return true 2 */', \T_VARIABLE ), + array( '/* test return true 3 */', \T_VARIABLE ), + array( '/* test return true 4 */', \T_VARIABLE ), + array( '/* test return true 5 */', \T_VARIABLE ), + array( '/* test return true 6 */', \T_VARIABLE ), + array( '/* test return true 7 */', \T_VARIABLE ), + array( '/* test return true 8 */', \T_VARIABLE ), + array( '/* test return true 9 */', \T_VARIABLE ), + array( '/* test return true 10 */', \T_VARIABLE ), + array( '/* test return true 11 */', \T_VARIABLE ), + array( '/* test return true 12 */', \T_VARIABLE ), + array( '/* test return true 13 */', \T_VARIABLE ), + array( '/* test return true 14 */', \T_VARIABLE ), + array( '/* test return true 15 */', \T_VARIABLE ), + array( '/* test return true 16 */', \T_VARIABLE ), + array( '/* test return true 17 */', \T_VARIABLE ), + array( '/* test return true 18 */', \T_VARIABLE ), + array( '/* test return true 19 */', \T_VARIABLE ), + array( '/* test return true 20 */', \T_VARIABLE ), + array( '/* test return true 21 */', \T_VARIABLE ), + array( '/* test return true 22 */', \T_VARIABLE ), + array( '/* test return true 23 */', \T_VARIABLE ), + array( '/* test return true 24 */', \T_VARIABLE ), + array( '/* test return true 25 */', \T_VARIABLE ), + array( '/* test return true 26 */', \T_STRING ), + array( '/* test return true 27 */', \T_VARIABLE ), + array( '/* test return true 28 */', \T_VARIABLE ), + array( '/* test return true 29 */', \T_VARIABLE ), + ); + } +} diff --git a/WordPress/Util/Tests/Helpers/ContextHelper/IsTokenNamespacedUnitTest.inc b/WordPress/Util/Tests/Helpers/ContextHelper/IsTokenNamespacedUnitTest.inc new file mode 100644 index 0000000000..57af0b8fb3 --- /dev/null +++ b/WordPress/Util/Tests/Helpers/ContextHelper/IsTokenNamespacedUnitTest.inc @@ -0,0 +1,22 @@ +assertFalse( ContextHelper::is_token_namespaced( self::$phpcsFile, -1 ) ); + } + + /** + * Test is_token_namespaced() returns false for non-namespaced contexts. + * + * @dataProvider dataIsTokenNamespacedShouldReturnFalse + * + * @param string $commentString The comment which prefaces the target token in the test file. + * @param string $tokenContent The token content to use for the target token. + * + * @return void + */ + public function testIsTokenNamespacedShouldReturnFalse( $commentString, $tokenContent ) { + $stackPtr = $this->getTargetToken( $commentString, Collections::nameTokens(), $tokenContent ); + $this->assertFalse( ContextHelper::is_token_namespaced( self::$phpcsFile, $stackPtr ) ); + } + + /** + * Data provider. + * + * @return array + * @see testIsTokenNamespacedShouldReturnFalse() + */ + public static function dataIsTokenNamespacedShouldReturnFalse() { + $isPhpcs3 = version_compare( Helper::getVersion(), '3.99.99', '<=' ); + + return array( + array( '/* test return false 1 */', 'my_function' ), + array( '/* test return false 2 */', ( $isPhpcs3 ? 'my_function' : '\my_function' ) ), + array( '/* test return false 3 */', 'MyClass' ), + array( '/* test return false 4 */', ( $isPhpcs3 ? 'MyClass' : '\MyClass' ) ), + array( '/* test return false 5 */', 'MY_CONSTANT' ), + array( '/* test return false 6 */', ( $isPhpcs3 ? 'MY_CONSTANT' : '\MY_CONSTANT' ) ), + ); + } + + /** + * Test is_token_namespaced() returns true for namespaced contexts. + * + * @dataProvider dataIsTokenNamespacedShouldReturnTrue + * + * @param string $commentString The comment which prefaces the target token in the test file. + * @param string $tokenContent The token content to use for the target token. + * + * @return void + */ + public function testIsTokenNamespacedShouldReturnTrue( $commentString, $tokenContent ) { + $stackPtr = $this->getTargetToken( $commentString, Collections::nameTokens(), $tokenContent ); + $this->assertTrue( ContextHelper::is_token_namespaced( self::$phpcsFile, $stackPtr ) ); + } + + /** + * Data provider. + * + * @return array + * @see testIsTokenNamespacedShouldReturnTrue() + */ + public static function dataIsTokenNamespacedShouldReturnTrue() { + $isPhpcs3 = version_compare( Helper::getVersion(), '3.99.99', '<=' ); + + return array( + array( '/* test return true 1 */', ( $isPhpcs3 ? 'my_function' : 'MyNamespace\my_function' ) ), + array( '/* test return true 2 */', ( $isPhpcs3 ? 'my_function' : '\MyNamespace\my_function' ) ), + array( '/* test return true 3 */', ( $isPhpcs3 ? 'my_function' : 'namespace\my_function' ) ), + array( '/* test return true 4 */', ( $isPhpcs3 ? 'MyClass' : 'MyNamespace\MyClass' ) ), + array( '/* test return true 5 */', ( $isPhpcs3 ? 'MY_CONSTANT' : 'MyNamespace\MY_CONSTANT' ) ), + ); + } +} diff --git a/WordPress/Util/Tests/Helpers/WPDBTrait/IsWpdbMethodCallUnitTest.inc b/WordPress/Util/Tests/Helpers/WPDBTrait/IsWpdbMethodCallUnitTest.inc new file mode 100644 index 0000000000..260e6959b8 --- /dev/null +++ b/WordPress/Util/Tests/Helpers/WPDBTrait/IsWpdbMethodCallUnitTest.inc @@ -0,0 +1,74 @@ +prepare( 'SELECT * FROM table' ); +/* test return false 2 */ $custom_db->query( 'SELECT * FROM table' ); + +// WPDB variable but not a method call. +/* test return false 3 */ $wpdb; +/* test return false 4 */ $wpdb['key']; +/* test return false 5 */ $wpdb = new wpdb(); + +// Property access (not method calls) - definitely no parentheses. +/* test return false 6 */ echo $wpdb->table_name; +/* test return false 7 */ echo $wpdb->insert_id; + +// Function calls, not object method calls. +/* test return false 8 */ $result = wpdb( 'SELECT * FROM table' ); + +// Not WPDB class references. +/* test return false 9 */ wpdb_custom(); +/* test return false 10 */ \wpdb_custom(); + +// Method calls on different variables. +/* test return false 11 */ $database->prepare( 'SELECT * FROM table' ); +/* test return false 12 */ $custom->query( 'SELECT * FROM table' ); + +// WPDB variable but no parentheses (property access). +/* test return false 13 */ echo $wpdb->last_error; +/* test return false 14 */ echo $wpdb->num_rows; +/* test return false 15 */ echo $wpdb->prefix; + +/* + * Test cases that should be recognized as WPDB method calls. + */ + +// Standard WPDB method calls. +/* test return true 1 */ $wpdb->prepare( 'SELECT * FROM table WHERE id = %d', $id ); +/* test return true 2 */ $wpdb->query( 'DELETE FROM table WHERE id = 1' ); +/* test return true 3 */ $wpdb->prepare( 'INSERT INTO table (name) VALUES (%s)', $name ); +/* test return true 4 */ $wpdb->query( $sql ); + +// WPDB method calls with whitespace. +/* test return true 5 */ $wpdb -> prepare( 'SELECT * FROM table' ); + +// Static WPDB method calls. +/* test return true 6 */ $wpdb::prepare( 'SELECT * FROM table WHERE id = %d', $id ); + +// Global WPDB class references. +/* test return true 7 */ \wpdb::prepare( 'SELECT * FROM table WHERE id = %d', $id ); + +/* + * Test cases for methods that are not in the target methods list. + */ + +// WPDB method calls but not target methods. +/* test non-target method 1 */ $wpdb->insert( 'table', array( 'name' => 'value' ) ); +/* test non-target method 2 */ $wpdb->update( 'table', array( 'name' => 'value' ), array( 'id' => 1 ) ); + +/* + * Test cases for namespaced calls (should handle correctly). + */ + +// Global namespace (should work). +/* test namespaced 1 */ wpdb::prepare( 'SELECT * FROM table' ); +/* test namespaced 2 */ \wpdb::prepare( 'SELECT * FROM table' ); + +// Custom namespaces (should not work). +/* test namespaced 3 */ MyNamespace\wpdb::prepare( 'SELECT * FROM table' ); +/* test namespaced 4 */ \MyNamespace\wpdb::prepare( 'SELECT * FROM table' ); +/* test namespaced 5 */ SomeNamespace\wpdb::prepare( 'SELECT * FROM table' ); \ No newline at end of file diff --git a/WordPress/Util/Tests/Helpers/WPDBTrait/IsWpdbMethodCallUnitTest.php b/WordPress/Util/Tests/Helpers/WPDBTrait/IsWpdbMethodCallUnitTest.php new file mode 100644 index 0000000000..06ef6750f3 --- /dev/null +++ b/WordPress/Util/Tests/Helpers/WPDBTrait/IsWpdbMethodCallUnitTest.php @@ -0,0 +1,195 @@ +is_wpdb_method_call( $phpcsFile, $stackPtr, $target_methods ); + } + }; + } + + /** + * Test is_wpdb_method_call() returns false if given token is not a WPDB method call. + * + * @dataProvider dataIsWpdbMethodCallShouldReturnFalse + * + * @param string $commentString The comment which prefaces the target token in the test file. + * @param int|string $tokenType The token type to search for. + * + * @return void + */ + public function testIsWpdbMethodCallShouldReturnFalse( $commentString, $tokenType ) { + $stackPtr = $this->getTargetToken( $commentString, $tokenType ); + $result = self::$testClass->testIsWpdbMethodCall( self::$phpcsFile, $stackPtr, array( 'prepare' => true, 'query' => true ) ); + $this->assertFalse( $result ); + } + + /** + * Data provider. + * + * @return array + * @see testIsWpdbMethodCallShouldReturnFalse() + */ + public static function dataIsWpdbMethodCallShouldReturnFalse() { + return array( + array( '/* test return false 1 */', \T_VARIABLE ), + array( '/* test return false 2 */', \T_VARIABLE ), + array( '/* test return false 3 */', \T_VARIABLE ), + array( '/* test return false 4 */', \T_VARIABLE ), + array( '/* test return false 5 */', \T_VARIABLE ), + array( '/* test return false 6 */', \T_VARIABLE ), + array( '/* test return false 7 */', \T_VARIABLE ), + array( '/* test return false 8 */', \T_VARIABLE ), + array( '/* test return false 9 */', \T_STRING ), + array( '/* test return false 10 */', \T_STRING ), + array( '/* test return false 11 */', \T_VARIABLE ), + array( '/* test return false 12 */', \T_VARIABLE ), + array( '/* test return false 13 */', \T_VARIABLE ), + array( '/* test return false 14 */', \T_VARIABLE ), + array( '/* test return false 15 */', \T_VARIABLE ), + ); + } + + /** + * Test is_wpdb_method_call() returns true for valid WPDB method calls. + * + * @dataProvider dataIsWpdbMethodCallShouldReturnTrue + * + * @param string $commentString The comment which prefaces the target token in the test file. + * @param int|string $tokenType The token type to search for. + * + * @return void + */ + public function testIsWpdbMethodCallShouldReturnTrue( $commentString, $tokenType ) { + $stackPtr = $this->getTargetToken( $commentString, $tokenType ); + $result = self::$testClass->testIsWpdbMethodCall( self::$phpcsFile, $stackPtr, array( 'prepare' => true, 'query' => true ) ); + $this->assertTrue( $result ); + } + + /** + * Data provider. + * + * @return array + * @see testIsWpdbMethodCallShouldReturnTrue() + */ + public static function dataIsWpdbMethodCallShouldReturnTrue() { + return array( + array( '/* test return true 1 */', \T_VARIABLE ), + array( '/* test return true 2 */', \T_VARIABLE ), + array( '/* test return true 3 */', \T_VARIABLE ), + array( '/* test return true 4 */', \T_VARIABLE ), + array( '/* test return true 5 */', \T_VARIABLE ), + array( '/* test return true 6 */', \T_VARIABLE ), + array( '/* test return true 7 */', \T_STRING ), + ); + } + + /** + * Test is_wpdb_method_call() returns false for WPDB method calls with non-target methods. + * + * @dataProvider dataIsWpdbMethodCallShouldReturnFalseForNonTargetMethods + * + * @param string $commentString The comment which prefaces the target token in the test file. + * @param int|string $tokenType The token type to search for. + * + * @return void + */ + public function testIsWpdbMethodCallShouldReturnFalseForNonTargetMethods( $commentString, $tokenType ) { + $stackPtr = $this->getTargetToken( $commentString, $tokenType ); + $result = self::$testClass->testIsWpdbMethodCall( self::$phpcsFile, $stackPtr, array( 'prepare' => true ) ); + $this->assertFalse( $result ); + } + + /** + * Data provider. + * + * @return array + * @see testIsWpdbMethodCallShouldReturnFalseForNonTargetMethods() + */ + public static function dataIsWpdbMethodCallShouldReturnFalseForNonTargetMethods() { + return array( + array( '/* test non-target method 1 */', \T_VARIABLE ), + array( '/* test non-target method 2 */', \T_VARIABLE ), + ); + } + + /** + * Test is_wpdb_method_call() correctly handles namespaced calls. + * + * @dataProvider dataIsWpdbMethodCallShouldHandleNamespacedCalls + * + * @param string $commentString The comment which prefaces the target token in the test file. + * @param int|string $tokenType The token type to search for. + * @param bool $expected Whether the result should be true or false. + * + * @return void + */ + public function testIsWpdbMethodCallShouldHandleNamespacedCalls( $commentString, $tokenType, $expected ) { + $stackPtr = $this->getTargetToken( $commentString, $tokenType ); + $result = self::$testClass->testIsWpdbMethodCall( self::$phpcsFile, $stackPtr, array( 'prepare' => true, 'query' => true ) ); + + if ( $expected ) { + $this->assertTrue( $result ); + } else { + $this->assertFalse( $result ); + } + } + + /** + * Data provider. + * + * @return array + * @see testIsWpdbMethodCallShouldHandleNamespacedCalls() + */ + public static function dataIsWpdbMethodCallShouldHandleNamespacedCalls() { + return array( + array( '/* test namespaced 1 */', \T_STRING, true ), + array( '/* test namespaced 2 */', \T_STRING, true ), + array( '/* test namespaced 3 */', \T_STRING, false ), + array( '/* test namespaced 4 */', \T_STRING, false ), + array( '/* test namespaced 5 */', \T_STRING, false ), + ); + } +} \ No newline at end of file diff --git a/composer.json b/composer.json index a5afb55f6b..d448cec01e 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,7 @@ }, "require-dev": { "phpcompatibility/php-compatibility": "^9.0", - "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" @@ -52,11 +52,19 @@ "fix-cs": [ "@php ./vendor/squizlabs/php_codesniffer/bin/phpcbf" ], - "run-tests": [ - "@php ./vendor/phpunit/phpunit/phpunit --filter WordPress ./vendor/squizlabs/php_codesniffer/tests/AllTests.php --no-coverage" + "run-tests-phpcs3": [ + "@php ./vendor/phpunit/phpunit/phpunit --filter WordPress ./vendor/squizlabs/php_codesniffer/tests/AllTests.php --no-coverage", + "@php ./vendor/phpunit/phpunit/phpunit WordPress/Util/Tests/ --no-coverage" ], - "coverage": [ - "@php ./vendor/phpunit/phpunit/phpunit --filter WordPress ./vendor/squizlabs/php_codesniffer/tests/AllTests.php" + "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", + "@php ./vendor/phpunit/phpunit/phpunit WordPress/Util/Tests/" + ], + "coverage-phpcs4": [ + "@php ./vendor/phpunit/phpunit/phpunit" ], "check-complete": [ "@php ./vendor/phpcsstandards/phpcsdevtools/bin/phpcs-check-feature-completeness -q ./WordPress" @@ -67,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" ] }, @@ -75,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." diff --git a/phpunit.xml.dist b/phpunit.xml.dist index a7a2b1401f..757c96fec1 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -13,7 +13,10 @@ forceCoversAnnotation="true"> - + + ./WordPress/Util/Tests/ + + ./WordPress/Tests/