Skip to content

Commit c41a317

Browse files
authored
Merge pull request #2620 from rodrigoprimo/more-namespace-tests
Sniffs: add tests for namespaced names
2 parents 262b65a + b86f078 commit c41a317

16 files changed

+368
-42
lines changed

WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.1.inc

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,10 +260,10 @@ $GLOBALS[ $something ] = 'value'; // Warning.
260260
$GLOBALS[ "{$something}_something" ] = 'value'; // Warning.
261261
$GLOBALS[ ${$something} ] = 'value'; // Warning.
262262

263-
define( ${$something}, 'value' ); // Warning.
263+
DEFINE( ${$something}, 'value' ); // Warning.
264264
define( $something, 'value' ); // Warning.
265265
define( $something . '_CONSTANT', 'value' ); // Warning.
266-
define( "{$something}_CONSTANT", 'value' ); // Warning.
266+
\Define( "{$something}_CONSTANT", 'value' ); // Warning.
267267
define( $something . '_CONSTANT', 'value' ); // Warning.
268268

269269
do_action( "{$acronym_filter_var}_hook_name" ); // Warning.
@@ -683,4 +683,16 @@ class Acronym_AsymmetricVisibilityProperties {
683683
public function __construct(public protected(set) int $foo = 0) {} // Ok.
684684
}
685685

686+
/*
687+
* Safeguard correct handling of all types of namespaced function calls.
688+
*/
689+
\define('SOME_GLOBAL', [ 1, 2, 3 ]); // Bad.
690+
MyNamespace\define('SOME_GLOBAL', [ 1, 2, 3 ]); // Ok.
691+
\MyNamespace\define('SOME_GLOBAL', [ 1, 2, 3 ]); // Ok.
692+
namespace\define('SOME_GLOBAL', [ 1, 2, 3 ]); // Ok. The sniff should start flagging this once it can resolve relative namespaces.
693+
\do_action( 'plugin_action' ); // Bad.
694+
MyNamespace\do_action( 'plugin_action' ); // Ok.
695+
\MyNamespace\apply_filters( 'plugin_filter', $variable ); // Ok.
696+
namespace\do_action_ref_array( 'plugin_action', array( $variable ) ); // Ok. The sniff should start flagging this once it can resolve relative namespaces.
697+
686698
// phpcs:set WordPress.NamingConventions.PrefixAllGlobals prefixes[]

WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.3.inc

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class Some_Test extends \PHPUnit_Framework_TestCase {
1212
}
1313
}
1414

15-
$acronym_test = new class extends \PHPUnit_Framework_TestCase {
15+
$acronym_test = new class extends \phpunit_framework_testcase {
1616

1717
public function testPass() {
1818
define( 'SOME_GLOBAL', '4.0.0' );
@@ -21,4 +21,36 @@ $acronym_test = new class extends \PHPUnit_Framework_TestCase {
2121
}
2222
};
2323

24+
// Test namespace resolution when the sniff checks the extending class.
25+
class Extends_Namespaced_Class_Not_WP_Test extends WP_Font_Face_UnitTestCase {
26+
27+
public function testPass() {
28+
do_action( 'some-action', $something );
29+
}
30+
}
31+
32+
/*
33+
* Safeguard correct handling of namespaced extending classes.
34+
*/
35+
class Extends_Partially_Qualified_Not_WP_Test extends MyNamespace\WP_UnitTestCase_Base {
36+
37+
public function testPass() {
38+
do_action( 'some-action', $something );
39+
}
40+
}
41+
42+
class Extends_Fully_Qualified_Not_WP_Test extends \MyNamespace\WP_Ajax_UnitTestCase {
43+
44+
public function testPass() {
45+
do_action( 'some-action', $something );
46+
}
47+
}
48+
49+
class Extends_Relative_Namespace_Not_WP_Test extends namespace\WP_Canonical_UnitTestCase {
50+
51+
public function testPass() {
52+
do_action( 'some-action', $something );
53+
}
54+
}
55+
2456
// phpcs:set WordPress.NamingConventions.PrefixAllGlobals prefixes[]

WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,16 @@ public function getErrorList( $testFile = 'PrefixAllGlobalsUnitTest.1.inc' ) {
9696
616 => 1,
9797
617 => 1,
9898
633 => 1,
99+
689 => 1,
100+
693 => 1,
101+
);
102+
103+
case 'PrefixAllGlobalsUnitTest.3.inc':
104+
return array(
105+
28 => 1,
106+
38 => 1,
107+
45 => 1,
108+
52 => 1,
99109
);
100110

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

107117
case 'PrefixAllGlobalsUnitTest.2.inc':
108118
// Namespaced - all OK, fall through to the default case.
109-
case 'PrefixAllGlobalsUnitTest.3.inc':
110-
// Test class - non-prefixed constant is fine, fall through to the default case.
111119
default:
112120
return array();
113121
}

WordPress/Tests/NamingConventions/ValidHookNameUnitTest.1.inc

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ prefix_do_action( 'someAction' ); // Ok - not WP do_action.
66

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

1313
// Simple strings.
1414
do_action( "adminHead" ); // Error - use lowercase.
@@ -121,3 +121,14 @@ do_action( 'admin_head_' . $fn( 'UPPERCASE', 'wrong-delimiter' ) . '_action' );
121121
do_action_ref_array( hook: 'My-Hook', args: $args ); // OK. Well, not really, but using the wrong parameter name, so not our concern.
122122
do_action_ref_array( args: $args, hook_name: 'my_hook', ); // OK.
123123
do_action_ref_array( args: $args, hook_name: 'My-Hook', ); // Error - use lowercase + warning about dash.
124+
125+
/*
126+
* Safeguard correct handling of all types of namespaced function calls.
127+
*/
128+
\apply_filters( 'adminHead', $variable ); // Error.
129+
MyNamespace\do_action( 'adminHead' ); // Ok.
130+
\MyNamespace\do_action_ref_array( 'adminHead', array( $variable ) ); // Ok.
131+
namespace\apply_filters_ref_array( 'adminHead', array( $variable ) ); // Ok. The sniff should start flagging this once it can resolve relative namespaces.
132+
apply_filters( 'admin_head_' . MyNamespace\my_function('UPPERCASE') . '_action', $variable ); // Ok.
133+
do_action( 'admin_head_' . \MyNamespace\my_function('UPPERCASE') . '_action' ); // Ok.
134+
do_action_ref_array( 'admin_head_' . namespace\my_function('UPPERCASE') . '_action', array( $variable ) ); // Ok.

WordPress/Tests/NamingConventions/ValidHookNameUnitTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ public function getErrorList( $testFile = 'ValidHookNameUnitTest.1.inc' ) {
7575
114 => 1,
7676
115 => 1,
7777
123 => 1,
78+
128 => 1,
7879
);
7980

8081
case 'ValidHookNameUnitTest.2.inc':

WordPress/Tests/PHP/NoSilencedErrorsUnitTest.inc

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ if (@in_array($array, $needle)) { // Bad.
1212
}
1313

1414
// File extension.
15-
if ( @&file_exists( $filename ) && @ /*comment*/ is_readable( $filename ) ) {
15+
if ( @&file_exists( $filename ) && @ /*comment*/ IS_READABLE( $filename ) ) {
1616
$file = @ \file( $filename );
1717
}
1818

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

2323
// Directory extension.
2424
if (@is_dir($dir)) {
25-
if ($dh = @\opendir($dir)) {
25+
if ($dh = @\OPENDIR($dir)) {
2626
while (($file = @readdir($dh)) !== false) { // Bad.
2727
echo "filename: $file : filetype: " . @\filetype($dir . $file) . "\n";
2828
}
@@ -84,3 +84,11 @@ $decoded = @hex2bin( $data );
8484
// phpcs:set WordPress.PHP.NoSilencedErrors context_length 0
8585
echo @some_userland_function( $param ); // Bad.
8686
// phpcs:set WordPress.PHP.NoSilencedErrors context_length 6
87+
88+
/*
89+
* Safeguard correct handling of namespaced function calls (fully qualified is already tested above).
90+
*/
91+
$file = @MyNS\MyClass::file_get_contents( $file ); // Bad.
92+
$file = @MyNS\MyClass\file_exists( $file ); // Bad.
93+
$file = @namespace\MyNS\MyClass::file( $file ); // Bad.
94+
$file = @namespace\is_dir( $dir ); // The sniff should stop flagging this once it can resolve relative namespaces.

WordPress/Tests/PHP/NoSilencedErrorsUnitTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ public function getWarningList() {
6161
71 => 1,
6262
78 => 1,
6363
85 => 1,
64+
91 => 1,
65+
92 => 1,
66+
93 => 1,
67+
94 => 1,
6468
);
6569
}
6670
}

WordPress/Tests/Security/NonceVerificationUnitTest.1.inc

Lines changed: 168 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ function allow_for_unslash_before_noncecheck() {
250250
}
251251

252252
function allow_for_unslash_in_sanitization() {
253-
$var = sanitize_text_field( wp_unslash( $_POST['foo'] ) ); // OK.
253+
$var = sanitize_text_field( WP_UNSLASH( $_POST['foo'] ) ); // OK.
254254
wp_verify_nonce( $var );
255255
echo $var;
256256
}
@@ -314,7 +314,7 @@ function allow_in_array_key_exists_before_noncecheck() {
314314
}
315315

316316
function allow_in_key_exists_before_noncecheck() {
317-
if (key_exists('foo', $_POST['subset']) === false) { // OK.
317+
if (Key_Exists('foo', $_POST['subset']) === false) { // OK.
318318
return;
319319
}
320320

@@ -369,7 +369,7 @@ function disallow_for_non_array_comparison_in_condition() {
369369
}
370370

371371
function allow_for_array_comparison_in_condition_with_named_params() {
372-
if ( array_keys( filter_value: 'my_action', array: $_GET['actions'], strict: true, ) ) { // OK.
372+
if ( \array_KEYS( filter_value: 'my_action', array: $_GET['actions'], strict: true, ) ) { // OK.
373373
check_admin_referer( 'foo' );
374374
foo();
375375
}
@@ -524,3 +524,168 @@ function different_function_name() {
524524
update_post_meta( (int) $_POST['id'], 'a_key', $_POST['a_value'] );
525525
}
526526
// phpcs:set WordPress.Security.NonceVerification customNonceVerificationFunctions[]
527+
528+
function test_fully_qualified_call_to_global_nonce_verification_function() {
529+
if ( ! IS_NUMERIC( $_POST['foo'] ) ) { // OK.
530+
return;
531+
}
532+
533+
\wp_verify_nonce( 'some_action' );
534+
}
535+
536+
function test_namespace_relative_call_to_global_nonce_verification_function() {
537+
if ( ! IS_NUMERIC( $_POST['foo'] ) ) { // Bad, but should become ok once the sniff is able to resolve relative namespaces.
538+
return;
539+
}
540+
541+
namespace\wp_verify_nonce( 'some_action' );
542+
}
543+
544+
function test_namespaced_calls_to_incorrect_nonce_verification_functions() {
545+
if ( ! is_numeric( $_POST['foo'] ) ) { // Bad - none of the below are the WP global functions, so no nonce verification.
546+
return;
547+
}
548+
549+
MyNamespace\wp_verify_nonce( 'some_action' );
550+
\MyNamespace\check_admin_referer( 'some_action' );
551+
namespace\Sub\check_ajax_referer( 'some_action' );
552+
}
553+
554+
// phpcs:set WordPress.Security.NonceVerification customNonceVerificationFunctions[] my_nonce_check
555+
556+
function test_namespace_relative_call_to_custom_nonce_verification_function() {
557+
if ( ! is_int( $_POST['foo'] ) ) { // Bad, but should become ok once the sniff is able to resolve relative namespaces.
558+
return;
559+
}
560+
561+
namespace\my_nonce_check( 'some_action' );
562+
}
563+
564+
function test_namespaced_calls_to_incorrect_custom_nonce_verification_functions() {
565+
if ( ! is_string( $_POST['foo'] ) ) { // Bad - none of the below are a custom nonce verification function.
566+
return;
567+
}
568+
569+
MyNamespace\my_nonce_check( 'some_action' );
570+
\MyNamespace\my_nonce_check( 'some_action' );
571+
namespace\Sub\my_nonce_check( 'some_action' );
572+
}
573+
574+
// phpcs:set WordPress.Security.NonceVerification customNonceVerificationFunctions[]
575+
576+
function allow_fully_qualified_key_exists_functions() {
577+
if ( \array_key_exists('foo', $_POST) === false ) { // OK.
578+
return;
579+
}
580+
581+
\WP_VERIFY_NONCE( 'some_action' );
582+
}
583+
584+
function allow_fully_qualified_key_exists_functions_with_mixed_case() {
585+
if ( \Key_Exists('foo', $_POST) === false ) { // OK.
586+
return;
587+
}
588+
589+
wp_verify_nonce( 'some_action' );
590+
}
591+
592+
function allow_namespace_relative_call_to_global_key_exists_functions() {
593+
if ( namespace\array_key_exists('foo', $_POST) === false ) { // Bad, but should become ok once the sniff is able to resolve relative namespaces.
594+
return;
595+
}
596+
597+
wp_verify_nonce( 'some_action' );
598+
}
599+
600+
function disallow_namespaced_key_exists_functions() {
601+
if ( MyNamespace\array_key_exists( 'foo', $_POST ) === false // Bad.
602+
|| \MyNamespace\key_exists( 'foo', $_POST ) === false // Bad.
603+
|| namespace\Sub\key_exists( 'foo', $_POST ) === false // Bad.
604+
) {
605+
return;
606+
}
607+
608+
wp_verify_nonce( 'some_action' );
609+
}
610+
611+
function allow_fully_qualified_type_test_functions() {
612+
if ( ! \is_numeric( $_POST['foo'] ) ) { // OK.
613+
return;
614+
}
615+
616+
\wp_verify_nonce( 'some_action' );
617+
}
618+
619+
function allow_fully_qualified_type_test_functions_uppercase() {
620+
if ( ! \IS_int( $_POST['foo'] ) ) { // OK.
621+
return;
622+
}
623+
624+
\wp_verify_nonce( 'some_action' );
625+
}
626+
627+
function allow_namespace_relative_call_to_global_type_test_functions() {
628+
if ( ! namespace\is_numeric( $_POST['foo'] ) ) { // Bad, but should become ok once the sniff is able to resolve relative namespaces.
629+
return;
630+
}
631+
632+
\wp_verify_nonce( 'some_action' );
633+
}
634+
635+
function disallow_namespaced_type_test_functions() {
636+
if ( ! MyNamespace\is_bool( $_POST['foo'] ) // Bad.
637+
|| ! \MyNamespace\is_object( $_POST['foo'] ) // Bad.
638+
|| ! namespace\Sub\is_string( $_POST['foo'] ) ) // Bad.
639+
{
640+
return;
641+
}
642+
643+
\wp_verify_nonce( 'some_action' );
644+
}
645+
646+
function allow_fully_qualified_array_comparison_functions() {
647+
if ( \in_array( $_GET['action'], $valid_actions, true ) ) { // OK.
648+
check_admin_referer( 'foo' );
649+
}
650+
}
651+
652+
function allow_namespace_relative_call_to_global_array_comparison_functions() {
653+
if ( namespace\in_array( $_GET['action'], $valid_actions, true ) ) { // Bad, but should become ok once the sniff is able to resolve relative namespaces.
654+
check_admin_referer( 'foo' );
655+
}
656+
}
657+
658+
function disallow_namespaced_array_comparison_functions() {
659+
if ( MyNamespace\in_array( $_GET['action'], $valid_actions, true ) // Bad.
660+
|| \MyNamespace\array_search( array( 'subscribe', 'unsubscribe' ), $_GET['action'], true ) // Bad.
661+
|| namespace\Sub\array_keys( $_GET['actions'], 'my_action', true ) // Bad.
662+
) {
663+
check_admin_referer( 'foo' );
664+
}
665+
}
666+
667+
function allow_fully_qualified_unslashing_functions() {
668+
$var = \stripslashes_from_strings_only( $_POST['foo'] ); // OK.
669+
wp_verify_nonce( $var );
670+
echo $var;
671+
}
672+
673+
function allow_fully_qualified_unslashing_functions_mixed_case() {
674+
$var = \stripslashes_FROM_strings_ONLY( $_POST['foo'] ); // OK.
675+
wp_verify_nonce( $var );
676+
echo $var;
677+
}
678+
679+
function allow_namespace_relative_call_to_global_unslashing_functions() {
680+
$var = namespace\stripslashes_from_strings_only( $_POST['foo'] ); // Bad, but should become ok once the sniff is able to resolve relative namespaces.
681+
wp_verify_nonce( $var );
682+
echo $var;
683+
}
684+
685+
function disallow_namespaced_unslashing_functions() {
686+
$var = MyNamespace\stripslashes_from_strings_only( $_POST['foo'] ); // Bad.
687+
$var = \MyNamespace\stripslashes_deep( $_POST['foo'] ); // Bad.
688+
$var = namespace\Sub\wp_unslash( $_POST['foo'] ); // Bad.
689+
wp_verify_nonce( $var );
690+
echo $var;
691+
}

0 commit comments

Comments
 (0)