Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
2707cc4
Security/EscapeOutput: add tests for namespaced names
rodrigoprimo Aug 21, 2025
54816d5
DELME: temporally allow PHPCS 4.x tests to fail while they are not fixed
rodrigoprimo Aug 5, 2025
4845e3b
Tests: update to allow for running the tests on PHPCS 4.x
rodrigoprimo Aug 4, 2025
522264e
PR 2619 DB/DirectDatabaseQuery: fix handling of namespaced calls to c…
rodrigoprimo Aug 7, 2025
0c00842
PR 2579 ConstantsHelper::is_use_of_global_constant(): fix false posit…
rodrigoprimo Aug 15, 2025
8ff3e3b
PR 2617 WP/AlternativeFunctions: add tests for namespaced names
rodrigoprimo Aug 21, 2025
9d1a1de
PR 2620 Utils/I18nTextDomainFixer: add tests for namespaced names
rodrigoprimo Aug 19, 2025
0a6510f
PR 2620 Security/NonceVerification: add tests for namespaced names
rodrigoprimo Aug 21, 2025
a2fff27
PR 2620 NamingConventions/ValidHookName: add tests for namespaced names
rodrigoprimo Aug 21, 2025
22676cc
PR 2620 NamingConventions/PrefixAllGlobals: add tests for namespaced …
rodrigoprimo Aug 21, 2025
e241175
PR 2620 PHP/NoSilencedErrors: add tests for namespaced names
rodrigoprimo Aug 21, 2025
8dc4fa2
PR 2620 WP/CronInterval: add tests for namespaced names
rodrigoprimo Aug 21, 2025
2eeb734
PR 2620 WP/DiscouragedConstants: add tests for namespaced function calls
rodrigoprimo Aug 18, 2025
d009a42
PR 2633 CodeAnalysis/EscapedNotTranslated: add tests for namespaced n…
rodrigoprimo Oct 16, 2025
876a109
PR 2633 DateTime/CurrentTimeTimestamp: add tests for namespaced names
rodrigoprimo Oct 16, 2025
ebe557f
PR 2633 NamingConventions/ValidPostTypeSlug: add tests for namespaced…
rodrigoprimo Oct 16, 2025
c3db331
PR 2633 PHP/IniSet: add tests for namespaced names
rodrigoprimo Oct 16, 2025
732f8d1
PR 2633 Security/PluginMenuSlug: add tests for namespaced names
rodrigoprimo Oct 16, 2025
d28fa7a
PR 2633 PHP/PregQuoteDelimiter: add tests for namespaced names
rodrigoprimo Oct 16, 2025
08ed8ab
PR 2633 PHP/StrictInArray: add tests for namespaced names
rodrigoprimo Oct 16, 2025
41a9c33
PR 2633 WP/Capabilities: move syntax error test to its own file
rodrigoprimo Oct 16, 2025
5356e81
PR 2633 WP/Capabilities: add tests for namespaced names
rodrigoprimo Oct 16, 2025
a38552a
PR 2633 WP/DeprecatedParameters: add tests for namespaced names
rodrigoprimo Oct 16, 2025
0523893
PR 2633 WP/DeprecatedParameterValues: rename test case file
rodrigoprimo Oct 16, 2025
36ab22d
PR 2633 WP/DeprecatedParameterValues: move syntax error test to its o…
rodrigoprimo Oct 16, 2025
841ed48
PR 2633 WP/DeprecatedParameterValues: add tests for namespaced names
rodrigoprimo Oct 16, 2025
65c24d4
PR 2633 WP/GetMetaSingle: add tests for namespaced names
rodrigoprimo Oct 16, 2025
525c7aa
PR 2633 WP/I18n: add tests for namespaced names
rodrigoprimo Oct 16, 2025
fd53b86
DB/DirectDatabaseQuery: update for PHPCS 4.0
rodrigoprimo Aug 7, 2025
2d10010
CHECK WPDBTrait::is_wpdb_method_call(): update for PHPCS 4.0
rodrigoprimo Aug 7, 2025
3f8f121
WP/GlobalVariablesOverride: update for PHPCS 4.0
rodrigoprimo Aug 12, 2025
45cd93b
Security/NonceVerification: update for PHPCS 4.0
rodrigoprimo Aug 12, 2025
fc01362
NamingConventions/ValidHookName: update for PHPCS 4.0
rodrigoprimo Aug 13, 2025
30e7dc3
AbstractFunctionRestrictions: update for PHPCS 4.0
rodrigoprimo Aug 13, 2025
60f2eb9
NamingConventions/PrefixAllGlobals: update for PHPCS 4.0
rodrigoprimo Aug 13, 2025
88ee1b7
AbstractClassRestrictions: update for PHPCS 4.0
rodrigoprimo Aug 13, 2025
fc3fab7
PHP/NoSilencedErrors: update for PHPCS 4.0
rodrigoprimo Aug 13, 2025
e6e1c27
ContextHelper::is_in_function_call(): update for PHPCS 4.0
rodrigoprimo Aug 14, 2025
d36c3ea
WP/CronInterval: update for PHPCS 4.0
rodrigoprimo Aug 14, 2025
724aa4d
ValidationHelper::is_validated(): update for PHPCS 4.0
rodrigoprimo Aug 14, 2025
30983d9
ConstantsHelper::is_use_of_global_constant(): update for PHPCS 4.0
rodrigoprimo Aug 15, 2025
a261a24
WP/DiscouragedConstants: update for PHPCS 4.0
rodrigoprimo Aug 18, 2025
e263c42
Security/EscapeOutput: update for PHPCS 4.0
rodrigoprimo Aug 19, 2025
253d25c
WIP NamingConventions/PrefixAllGlobals: improve prefix validation
rodrigoprimo Aug 6, 2025
077c71a
WIP ContextHelper::is_in_function_call(): add basic tests
rodrigoprimo Aug 14, 2025
bd8a4f0
WIP ConstantHelper::is_use_of_global_constant(): add basic tests
rodrigoprimo Aug 15, 2025
cece698
WIP ContextHelper::is_in_isset_or_empty(): add basic tests
rodrigoprimo Sep 24, 2025
080027e
WIP ContextHelper::is_in_array_comparison(): add basic tests
rodrigoprimo Sep 30, 2025
e99c7be
WIP ContextHelper::is_in_type_test(): add basic tests
rodrigoprimo Sep 30, 2025
f352544
WIP ContextHelper::is_token_namespaced(): add basic tests
rodrigoprimo Aug 18, 2025
096c87b
WIP ContextHelper::is_token_namespaced(): update for PHPCS 4.0
rodrigoprimo Aug 18, 2025
b4901b6
WIP WPDBTrait::is_wpdb_method_call(): add basic tests
rodrigoprimo Oct 2, 2025
bb57474
PR 2631 WP/EnqueuedResourceParameters: fix some inaccuracies and typo…
rodrigoprimo Oct 15, 2025
69372b1
PR 2631 WP/EnqueuedResourceParameters: fix handling of non-lowercased…
rodrigoprimo Oct 15, 2025
bd7382e
WP/EnqueuedResourceParameters: handle fully qualified `\false` and `\…
rodrigoprimo Aug 14, 2025
09c4f6f
WP/EnqueuedResourceParameters: add tests for namespaced names
rodrigoprimo Oct 15, 2025
d48edcb
WIP DB/PreparedSQLPlaceholders: add namespaced tests
rodrigoprimo Aug 21, 2025
a05889c
DB/PreparedSQLPlaceholders: update for PHPCS 4.0
rodrigoprimo Aug 7, 2025
8cd96e9
DB/PreparedSQL: move intentional syntax error test to its own file
rodrigoprimo Aug 7, 2025
966f4d9
WIP DB/PreparedSQL: add tests for namespaced names
rodrigoprimo Aug 21, 2025
64f1bfd
WIP DB/PreparedSQL: the sniff currently doesn't handle well the added…
rodrigoprimo Aug 11, 2025
3e1ea76
DB/PreparedSQL: update for PHPCS 4.0
rodrigoprimo Aug 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions .github/workflows/quicktest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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' }}
Expand Down
58 changes: 44 additions & 14 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -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
Expand All @@ -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"
Expand All @@ -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 }}"

Expand All @@ -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' }}
Expand Down
25 changes: 25 additions & 0 deletions Tests/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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.
*/
Expand Down
14 changes: 11 additions & 3 deletions WordPress/AbstractClassRestrictionsSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 ) );
}

Expand Down
3 changes: 2 additions & 1 deletion WordPress/AbstractFunctionParameterSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
22 changes: 18 additions & 4 deletions WordPress/AbstractFunctionRestrictionsSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ public function register() {

return array(
\T_STRING,
\T_NAME_FULLY_QUALIFIED,
);
}

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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 ) {
Expand Down Expand Up @@ -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.
Expand Down
18 changes: 15 additions & 3 deletions WordPress/Helpers/ConstantsHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,16 @@
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.
* - The method was changed to be `static`.
* - 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
*/
Expand All @@ -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;
}

Expand All @@ -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();
Expand All @@ -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;
Expand Down
Loading
Loading