Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
74ecca0
Utils/I18nTextDomainFixer: add tests for namespaced names
rodrigoprimo Aug 19, 2025
49ebb37
Security/NonceVerification: add tests for namespaced names
rodrigoprimo Aug 21, 2025
7d41431
NamingConventions/ValidHookName: add tests for namespaced names
rodrigoprimo Aug 21, 2025
005003f
NamingConventions/PrefixAllGlobals: add tests for namespaced names
rodrigoprimo Aug 21, 2025
fae6704
PHP/NoSilencedErrors: add tests for namespaced names
rodrigoprimo Aug 21, 2025
c16b933
WP/CronInterval: add tests for namespaced names
rodrigoprimo Aug 21, 2025
58afa65
WP/DiscouragedConstants: add tests for namespaced function calls
rodrigoprimo Aug 18, 2025
521f28c
Update WordPress/Tests/PHP/NoSilencedErrorsUnitTest.inc
rodrigoprimo Sep 24, 2025
c72f635
Security/NonceVerification: address PR feedback and add more namespac…
rodrigoprimo Sep 30, 2025
9bb7bd5
NamingConventions/ValidHookName: address PR feedback and add tests of…
rodrigoprimo Sep 30, 2025
bec8ac0
NamingConventions/PrefixAllGlobals: address PR feedback and add more …
rodrigoprimo Sep 30, 2025
4dbc12a
WP/CronInterval: address PR feedback and add test of fully qualified …
rodrigoprimo Sep 30, 2025
0c34a60
Utils/I18nTextDomainFixer: use different function names and case for …
rodrigoprimo Nov 10, 2025
b367f99
Security/NonceVerification: use different function names and case for…
rodrigoprimo Nov 10, 2025
7f3cda4
NamingConventions/ValidHookName: use different function names and cas…
rodrigoprimo Nov 10, 2025
1e8def1
NamingConventions/PrefixAllGlobals: use different function names and …
rodrigoprimo Nov 10, 2025
1fb4689
PHP/NoSilencedErrors: use different function names and case for the n…
rodrigoprimo Nov 10, 2025
451a530
WP/CronInterval: ensure there are tests with different cases for the …
rodrigoprimo Nov 10, 2025
8e89ea4
WP/DiscouragedConstants: use different constant names and different c…
rodrigoprimo Nov 10, 2025
d4f1d12
WP/CronInterval: add tests for namespaced names when calling first cl…
rodrigoprimo Nov 11, 2025
f3d00da
WP/CronInterval: expand first class callables namespaced tests by doc…
rodrigoprimo Nov 11, 2025
0f2d04b
WP/CronInterval: update test comments
rodrigoprimo Nov 11, 2025
6d8f58a
NamingConventions/PrefixAllGlobals: address PR feedback
rodrigoprimo Nov 11, 2025
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.1.inc
Original file line number Diff line number Diff line change
Expand Up @@ -260,10 +260,10 @@ $GLOBALS[ $something ] = 'value'; // Warning.
$GLOBALS[ "{$something}_something" ] = 'value'; // Warning.
$GLOBALS[ ${$something} ] = 'value'; // Warning.

define( ${$something}, 'value' ); // Warning.
DEFINE( ${$something}, 'value' ); // Warning.
define( $something, 'value' ); // Warning.
define( $something . '_CONSTANT', 'value' ); // Warning.
define( "{$something}_CONSTANT", 'value' ); // Warning.
\Define( "{$something}_CONSTANT", 'value' ); // Warning.
define( $something . '_CONSTANT', 'value' ); // Warning.

do_action( "{$acronym_filter_var}_hook_name" ); // Warning.
Expand Down Expand Up @@ -683,4 +683,16 @@ 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. The sniff should start flagging this once it can resolve relative namespaces.
\do_action( 'plugin_action' ); // Bad.
MyNamespace\do_action( 'plugin_action' ); // Ok.
\MyNamespace\apply_filters( 'plugin_filter', $variable ); // Ok.
namespace\do_action_ref_array( 'plugin_action', array( $variable ) ); // Ok. The sniff should start flagging this once it can resolve relative namespaces.

// phpcs:set WordPress.NamingConventions.PrefixAllGlobals prefixes[]
34 changes: 33 additions & 1 deletion WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.3.inc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Some_Test extends \PHPUnit_Framework_TestCase {
}
}

$acronym_test = new class extends \PHPUnit_Framework_TestCase {
$acronym_test = new class extends \phpunit_framework_testcase {

public function testPass() {
define( 'SOME_GLOBAL', '4.0.0' );
Expand All @@ -21,4 +21,36 @@ $acronym_test = new class extends \PHPUnit_Framework_TestCase {
}
};

// Test namespace resolution when the sniff checks the extending class.
class Extends_Namespaced_Class_Not_WP_Test extends WP_Font_Face_UnitTestCase {

public function testPass() {
do_action( 'some-action', $something );
}
}

/*
* Safeguard correct handling of namespaced extending classes.
*/
class Extends_Partially_Qualified_Not_WP_Test extends MyNamespace\WP_UnitTestCase_Base {

public function testPass() {
do_action( 'some-action', $something );
}
}

class Extends_Fully_Qualified_Not_WP_Test extends \MyNamespace\WP_Ajax_UnitTestCase {

public function testPass() {
do_action( 'some-action', $something );
}
}

class Extends_Relative_Namespace_Not_WP_Test extends namespace\WP_Canonical_UnitTestCase {

public function testPass() {
do_action( 'some-action', $something );
}
}

// phpcs:set WordPress.NamingConventions.PrefixAllGlobals prefixes[]
12 changes: 10 additions & 2 deletions WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,16 @@ public function getErrorList( $testFile = 'PrefixAllGlobalsUnitTest.1.inc' ) {
616 => 1,
617 => 1,
633 => 1,
689 => 1,
693 => 1,
);

case 'PrefixAllGlobalsUnitTest.3.inc':
return array(
28 => 1,
38 => 1,
45 => 1,
52 => 1,
);

case 'PrefixAllGlobalsUnitTest.4.inc':
Expand All @@ -106,8 +116,6 @@ public function getErrorList( $testFile = 'PrefixAllGlobalsUnitTest.1.inc' ) {

case 'PrefixAllGlobalsUnitTest.2.inc':
// Namespaced - all OK, fall through to the default case.
case 'PrefixAllGlobalsUnitTest.3.inc':
// Test class - non-prefixed constant is fine, fall through to the default case.
default:
return array();
}
Expand Down
15 changes: 13 additions & 2 deletions WordPress/Tests/NamingConventions/ValidHookNameUnitTest.1.inc
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ prefix_do_action( 'someAction' ); // Ok - not WP do_action.

// Check for incorrect word separators.
do_action( "admin_head-$hook_suffix" ); // Warning - use underscore.
do_action( 'admin_head.media.upload_popup' ); // Warning - use underscore.
DO_ACTION( 'admin_head.media.upload_popup' ); // Warning - use underscore.
apply_filters( "bulk_actions {$this->screen->id}", $this->_actions ); // Warning - use underscore.
apply_filters( "current_theme/supports-{$feature}", true, $args, $_wp_theme_features[$feature] ); // Warning - use underscore.
\Apply_Filters( "current_theme/supports-{$feature}", true, $args, $_wp_theme_features[$feature] ); // Warning - use underscore.

// Simple strings.
do_action( "adminHead" ); // Error - use lowercase.
Expand Down Expand Up @@ -121,3 +121,14 @@ 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.
*/
\apply_filters( 'adminHead', $variable ); // Error.
MyNamespace\do_action( 'adminHead' ); // Ok.
\MyNamespace\do_action_ref_array( 'adminHead', array( $variable ) ); // Ok.
namespace\apply_filters_ref_array( 'adminHead', array( $variable ) ); // Ok. The sniff should start flagging this once it can resolve relative namespaces.
apply_filters( 'admin_head_' . MyNamespace\my_function('UPPERCASE') . '_action', $variable ); // Ok.
do_action( 'admin_head_' . \MyNamespace\my_function('UPPERCASE') . '_action' ); // Ok.
do_action_ref_array( 'admin_head_' . namespace\my_function('UPPERCASE') . '_action', array( $variable ) ); // Ok.
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public function getErrorList( $testFile = 'ValidHookNameUnitTest.1.inc' ) {
114 => 1,
115 => 1,
123 => 1,
128 => 1,
);

case 'ValidHookNameUnitTest.2.inc':
Expand Down
12 changes: 10 additions & 2 deletions WordPress/Tests/PHP/NoSilencedErrorsUnitTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ if (@in_array($array, $needle)) { // Bad.
}

// File extension.
if ( @&file_exists( $filename ) && @ /*comment*/ is_readable( $filename ) ) {
if ( @&file_exists( $filename ) && @ /*comment*/ IS_READABLE( $filename ) ) {
$file = @ \file( $filename );
}

Expand All @@ -22,7 +22,7 @@ $fp = @fopen('https://www.example.com', 'r', false);

// Directory extension.
if (@is_dir($dir)) {
if ($dh = @\opendir($dir)) {
if ($dh = @\OPENDIR($dir)) {
while (($file = @readdir($dh)) !== false) { // Bad.
echo "filename: $file : filetype: " . @\filetype($dir . $file) . "\n";
}
Expand Down Expand Up @@ -84,3 +84,11 @@ $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 = @MyNS\MyClass\file_exists( $file ); // Bad.
$file = @namespace\MyNS\MyClass::file( $file ); // Bad.
$file = @namespace\is_dir( $dir ); // The sniff should stop flagging this once it can resolve relative namespaces.
4 changes: 4 additions & 0 deletions WordPress/Tests/PHP/NoSilencedErrorsUnitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ public function getWarningList() {
71 => 1,
78 => 1,
85 => 1,
91 => 1,
92 => 1,
93 => 1,
94 => 1,
);
}
}
113 changes: 110 additions & 3 deletions WordPress/Tests/Security/NonceVerificationUnitTest.1.inc
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ function allow_for_unslash_before_noncecheck() {
}

function allow_for_unslash_in_sanitization() {
$var = sanitize_text_field( wp_unslash( $_POST['foo'] ) ); // OK.
$var = sanitize_text_field( WP_UNSLASH( $_POST['foo'] ) ); // OK.
wp_verify_nonce( $var );
echo $var;
}
Expand Down Expand Up @@ -314,7 +314,7 @@ function allow_in_array_key_exists_before_noncecheck() {
}

function allow_in_key_exists_before_noncecheck() {
if (key_exists('foo', $_POST['subset']) === false) { // OK.
if (Key_Exists('foo', $_POST['subset']) === false) { // OK.
return;
}

Expand Down Expand Up @@ -369,7 +369,7 @@ function disallow_for_non_array_comparison_in_condition() {
}

function allow_for_array_comparison_in_condition_with_named_params() {
if ( array_keys( filter_value: 'my_action', array: $_GET['actions'], strict: true, ) ) { // OK.
if ( \array_KEYS( filter_value: 'my_action', array: $_GET['actions'], strict: true, ) ) { // OK.
check_admin_referer( 'foo' );
foo();
}
Expand Down Expand Up @@ -524,3 +524,110 @@ 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\check_admin_referer( 'some_action' ); // Bad.
namespace\check_ajax_referer( 'some_action' ); // Bad, but should become ok once the sniff is able to resolve relative namespaces.
}

function allow_fully_qualified_key_exists_functions() {
if ( \array_key_exists('foo', $_POST) === false ) { // OK.
return;
}

\WP_VERIFY_NONCE( 'some_action' );
}

function allow_fully_qualified_key_exists_functions_with_mixed_case() {
if ( \Key_Exists('foo', $_POST) === false ) { // OK.
return;
}

\WP_VERIFY_NONCE( 'some_action' );
}

function disallow_namespaced_key_exists_functions() {
if ( MyNamespace\array_key_exists( 'foo', $_POST ) === false // Bad.
|| \MyNamespace\key_exists( 'foo', $_POST ) === false // Bad.
|| namespace\key_exists( 'foo', $_POST ) === false // Bad, but should become ok once the sniff is able to resolve relative namespaces.
) {
return;
}

wp_verify_nonce( 'some_action' );
}

function allow_fully_qualified_type_test_functions() {
if ( ! \is_numeric( $_POST['foo'] ) ) { // OK.
return;
}

\wp_verify_nonce( 'some_action' ); // OK.
}

function allow_fully_qualified_type_test_functions_uppercase() {
if ( ! \IS_NUMERIC( $_POST['foo'] ) ) { // OK.
return;
}

\wp_verify_nonce( 'some_action' ); // OK.
}

function disallow_namespaced_type_test_functions() {
if ( ! MyNamespace\is_bool( $_POST['foo'] ) // Bad.
|| ! MyNamespace\is_object( $_POST['foo'] ) // Bad.
|| ! namespace\is_string( $_POST['foo'] ) ) // Bad, but should become ok once the sniff is able to resolve relative namespaces.
{
return;
}

\wp_verify_nonce( 'some_action' ); // OK.
}

function allow_fully_qualified_array_comparison_functions() {
if ( \in_array( $_GET['action'], $valid_actions, true ) ) { // OK.
check_admin_referer( 'foo' );
}
}

function disallow_namespaced_array_comparison_functions() {
if ( MyNamespace\in_array( $_GET['action'], $valid_actions, true ) // Bad.
|| \MyNamespace\array_search( array( 'subscribe', 'unsubscribe' ), $_GET['action'], true ) // Bad.
|| namespace\array_keys( $_GET['actions'], 'my_action', true ) // Bad, but should become ok once the sniff is able to resolve relative namespaces.
) {
check_admin_referer( 'foo' );
}
}

function allow_fully_qualified_unslashing_functions() {
$var = \stripslashes_from_strings_only( $_POST['foo'] ); // OK.
wp_verify_nonce( $var );
echo $var;
}

function allow_fully_qualified_unslashing_functions_mixed_case() {
$var = \stripslashes_FROM_strings_ONLY( $_POST['foo'] ); // OK.
wp_verify_nonce( $var );
echo $var;
}

function disallow_namespaced_unslashing_functions() {
$var = MyNamespace\stripslashes_from_strings_only( $_POST['foo'] ); // Bad.
$var = \MyNamespace\stripslashes_deep( $_POST['foo'] ); // Bad.
$var = namespace\wp_unslash( $_POST['foo'] ); // Bad, but should become ok once the sniff is able to resolve relative namespaces.
wp_verify_nonce( $var );
echo $var;
}
13 changes: 13 additions & 0 deletions WordPress/Tests/Security/NonceVerificationUnitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@ public function getErrorList( $testFile = '' ) {
470 => 1,
478 => 1,
524 => 2,
537 => 1,
563 => 1,
564 => 1,
565 => 1,
590 => 1,
591 => 1,
592 => 1,
628 => 1,
629 => 1,
630 => 1,
);

case 'NonceVerificationUnitTest.2.inc':
Expand Down Expand Up @@ -107,6 +117,9 @@ public function getWarningList( $testFile = '' ) {
return array(
365 => 1,
379 => 1,
607 => 1,
608 => 1,
609 => 1,
);

case 'NonceVerificationUnitTest.4.inc':
Expand Down
12 changes: 10 additions & 2 deletions WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.4.inc
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,11 @@ is_textdomain_loaded( 'some-other-plugin' );
* Incorrect text domain, should be replaced.
*/
load_textdomain( 'text-domain', '/path/to/file.mo' );
load_plugin_textdomain( 'text-domain', false, '/languages/' );
\load_plugin_textdomain( 'text-domain', false, '/languages/' );
load_muplugin_textdomain( 'other-text-domain', '/languages/' );
load_theme_textdomain( 'third-text-domain', '/path/to/languages/' );
load_child_theme_textdomain( 'text-domain', '/path/to/languages/' );
unload_textdomain( 'text-domain' );
unload_TEXTDOMAIN( 'text-domain' );

__( $text, 'text-domain' );
_e( $text, 'text-domain' );
Expand Down Expand Up @@ -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\__( $text, 'text-domain' );
\ESC_HTML__( $text, 'text-domain' );
\get_translations_for_domain();

// phpcs:set WordPress.Utils.I18nTextDomainFixer old_text_domain[]
// phpcs:set WordPress.Utils.I18nTextDomainFixer new_text_domain false
12 changes: 10 additions & 2 deletions WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.4.inc.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,11 @@ is_textdomain_loaded( 'some-other-plugin' );
* Incorrect text domain, should be replaced.
*/
load_textdomain( 'something-else', '/path/to/file.mo' );
load_plugin_textdomain( 'something-else', false, '/languages/' );
\load_plugin_textdomain( 'something-else', false, '/languages/' );
load_muplugin_textdomain( 'something-else', '/languages/' );
load_theme_textdomain( 'something-else', '/path/to/languages/' );
load_child_theme_textdomain( 'something-else', '/path/to/languages/' );
unload_textdomain( 'something-else' );
unload_TEXTDOMAIN( 'something-else' );

__( $text, 'something-else' );
_e( $text, 'something-else' );
Expand Down Expand Up @@ -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\__( $text, 'text-domain' );
\ESC_HTML__( $text, 'something-else' );
\get_translations_for_domain( 'something-else' );

// phpcs:set WordPress.Utils.I18nTextDomainFixer old_text_domain[]
// phpcs:set WordPress.Utils.I18nTextDomainFixer new_text_domain false
Loading
Loading