diff --git a/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.1.inc b/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.1.inc index 011e250b7f..707468c137 100644 --- a/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.1.inc +++ b/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.1.inc @@ -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. @@ -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[] diff --git a/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.3.inc b/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.3.inc index be1eb3627f..e910701dba 100644 --- a/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.3.inc +++ b/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.3.inc @@ -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' ); @@ -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[] diff --git a/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.php b/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.php index b9beb09ff6..b93ad6216d 100644 --- a/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.php +++ b/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.php @@ -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': @@ -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(); } diff --git a/WordPress/Tests/NamingConventions/ValidHookNameUnitTest.1.inc b/WordPress/Tests/NamingConventions/ValidHookNameUnitTest.1.inc index 830c3552d6..466c23e522 100644 --- a/WordPress/Tests/NamingConventions/ValidHookNameUnitTest.1.inc +++ b/WordPress/Tests/NamingConventions/ValidHookNameUnitTest.1.inc @@ -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. @@ -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. diff --git a/WordPress/Tests/NamingConventions/ValidHookNameUnitTest.php b/WordPress/Tests/NamingConventions/ValidHookNameUnitTest.php index aac97da195..98649fb620 100644 --- a/WordPress/Tests/NamingConventions/ValidHookNameUnitTest.php +++ b/WordPress/Tests/NamingConventions/ValidHookNameUnitTest.php @@ -75,6 +75,7 @@ public function getErrorList( $testFile = 'ValidHookNameUnitTest.1.inc' ) { 114 => 1, 115 => 1, 123 => 1, + 128 => 1, ); case 'ValidHookNameUnitTest.2.inc': diff --git a/WordPress/Tests/PHP/NoSilencedErrorsUnitTest.inc b/WordPress/Tests/PHP/NoSilencedErrorsUnitTest.inc index f007d150bd..27b3598537 100644 --- a/WordPress/Tests/PHP/NoSilencedErrorsUnitTest.inc +++ b/WordPress/Tests/PHP/NoSilencedErrorsUnitTest.inc @@ -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 ); } @@ -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"; } @@ -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. diff --git a/WordPress/Tests/PHP/NoSilencedErrorsUnitTest.php b/WordPress/Tests/PHP/NoSilencedErrorsUnitTest.php index 7a576b992c..038c2c8d71 100644 --- a/WordPress/Tests/PHP/NoSilencedErrorsUnitTest.php +++ b/WordPress/Tests/PHP/NoSilencedErrorsUnitTest.php @@ -61,6 +61,10 @@ public function getWarningList() { 71 => 1, 78 => 1, 85 => 1, + 91 => 1, + 92 => 1, + 93 => 1, + 94 => 1, ); } } diff --git a/WordPress/Tests/Security/NonceVerificationUnitTest.1.inc b/WordPress/Tests/Security/NonceVerificationUnitTest.1.inc index aee51ae39a..fc688e09de 100644 --- a/WordPress/Tests/Security/NonceVerificationUnitTest.1.inc +++ b/WordPress/Tests/Security/NonceVerificationUnitTest.1.inc @@ -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; } @@ -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; } @@ -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(); } @@ -524,3 +524,168 @@ 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'] ) ) { // OK. + return; + } + + \wp_verify_nonce( 'some_action' ); +} + +function test_namespace_relative_call_to_global_nonce_verification_function() { + if ( ! IS_NUMERIC( $_POST['foo'] ) ) { // Bad, but should become ok once the sniff is able to resolve relative namespaces. + return; + } + + namespace\wp_verify_nonce( 'some_action' ); +} + +function test_namespaced_calls_to_incorrect_nonce_verification_functions() { + if ( ! is_numeric( $_POST['foo'] ) ) { // Bad - none of the below are the WP global functions, so no nonce verification. + return; + } + + MyNamespace\wp_verify_nonce( 'some_action' ); + \MyNamespace\check_admin_referer( 'some_action' ); + namespace\Sub\check_ajax_referer( 'some_action' ); +} + +// phpcs:set WordPress.Security.NonceVerification customNonceVerificationFunctions[] my_nonce_check + +function test_namespace_relative_call_to_custom_nonce_verification_function() { + if ( ! is_int( $_POST['foo'] ) ) { // Bad, but should become ok once the sniff is able to resolve relative namespaces. + return; + } + + namespace\my_nonce_check( 'some_action' ); +} + +function test_namespaced_calls_to_incorrect_custom_nonce_verification_functions() { + if ( ! is_string( $_POST['foo'] ) ) { // Bad - none of the below are a custom nonce verification function. + return; + } + + MyNamespace\my_nonce_check( 'some_action' ); + \MyNamespace\my_nonce_check( 'some_action' ); + namespace\Sub\my_nonce_check( 'some_action' ); +} + +// phpcs:set WordPress.Security.NonceVerification customNonceVerificationFunctions[] + +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 allow_namespace_relative_call_to_global_key_exists_functions() { + if ( namespace\array_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 disallow_namespaced_key_exists_functions() { + if ( MyNamespace\array_key_exists( 'foo', $_POST ) === false // Bad. + || \MyNamespace\key_exists( 'foo', $_POST ) === false // Bad. + || namespace\Sub\key_exists( 'foo', $_POST ) === false // Bad. + ) { + 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' ); +} + +function allow_fully_qualified_type_test_functions_uppercase() { + if ( ! \IS_int( $_POST['foo'] ) ) { // OK. + return; + } + + \wp_verify_nonce( 'some_action' ); +} + +function allow_namespace_relative_call_to_global_type_test_functions() { + if ( ! namespace\is_numeric( $_POST['foo'] ) ) { // Bad, but should become ok once the sniff is able to resolve relative namespaces. + return; + } + + \wp_verify_nonce( 'some_action' ); +} + +function disallow_namespaced_type_test_functions() { + if ( ! MyNamespace\is_bool( $_POST['foo'] ) // Bad. + || ! \MyNamespace\is_object( $_POST['foo'] ) // Bad. + || ! namespace\Sub\is_string( $_POST['foo'] ) ) // Bad. + { + return; + } + + \wp_verify_nonce( 'some_action' ); +} + +function allow_fully_qualified_array_comparison_functions() { + if ( \in_array( $_GET['action'], $valid_actions, true ) ) { // OK. + check_admin_referer( 'foo' ); + } +} + +function allow_namespace_relative_call_to_global_array_comparison_functions() { + if ( namespace\in_array( $_GET['action'], $valid_actions, true ) ) { // Bad, but should become ok once the sniff is able to resolve relative namespaces. + 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\Sub\array_keys( $_GET['actions'], 'my_action', true ) // Bad. + ) { + 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 allow_namespace_relative_call_to_global_unslashing_functions() { + $var = namespace\stripslashes_from_strings_only( $_POST['foo'] ); // Bad, but should become ok once the sniff is able to resolve relative namespaces. + 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\Sub\wp_unslash( $_POST['foo'] ); // Bad. + wp_verify_nonce( $var ); + echo $var; +} diff --git a/WordPress/Tests/Security/NonceVerificationUnitTest.php b/WordPress/Tests/Security/NonceVerificationUnitTest.php index aa5e832e83..ea76b5a4b0 100644 --- a/WordPress/Tests/Security/NonceVerificationUnitTest.php +++ b/WordPress/Tests/Security/NonceVerificationUnitTest.php @@ -75,6 +75,22 @@ public function getErrorList( $testFile = '' ) { 470 => 1, 478 => 1, 524 => 2, + 537 => 1, + 545 => 1, + 557 => 1, + 565 => 1, + 593 => 1, + 601 => 1, + 602 => 1, + 603 => 1, + 628 => 1, + 636 => 1, + 637 => 1, + 638 => 1, + 680 => 1, + 686 => 1, + 687 => 1, + 688 => 1, ); case 'NonceVerificationUnitTest.2.inc': @@ -107,6 +123,10 @@ public function getWarningList( $testFile = '' ) { return array( 365 => 1, 379 => 1, + 653 => 1, + 659 => 1, + 660 => 1, + 661 => 1, ); case 'NonceVerificationUnitTest.4.inc': diff --git a/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.4.inc b/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.4.inc index d33d0a18e0..26f6cdd71f 100644 --- a/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.4.inc +++ b/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.4.inc @@ -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' ); @@ -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 diff --git a/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.4.inc.fixed b/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.4.inc.fixed index f7fd2c6f4e..98f6adabfd 100644 --- a/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.4.inc.fixed +++ b/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.4.inc.fixed @@ -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' ); @@ -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 diff --git a/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.php b/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.php index a8c7c95e84..872050aa97 100644 --- a/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.php +++ b/WordPress/Tests/Utils/I18nTextDomainFixerUnitTest.php @@ -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/CronIntervalUnitTest.inc b/WordPress/Tests/WP/CronIntervalUnitTest.inc index e9541f49d2..5ebc161d4c 100644 --- a/WordPress/Tests/WP/CronIntervalUnitTest.inc +++ b/WordPress/Tests/WP/CronIntervalUnitTest.inc @@ -165,9 +165,9 @@ 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_hundred_minute_schedule' ) ); // Warning: time undetermined. - add_filter( 'cron_schedules', array( $this, 'sneaky_fake_wp_constant_schedule' ) ); // Warning: time undetermined. + \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. } public function add_weekly_schedule( $schedules ) { @@ -316,6 +316,7 @@ function first_class_weekly_schedule( $schedules ) { } add_filter( 'cron_schedules', 'first_class_weekly_schedule'(...)); // Ok: > 15 min. add_filter( 'cron_schedules', first_class_weekly_schedule(...)); // Ok: > 15 min. +add_filter( 'cron_schedules', namespace\first_class_weekly_schedule(...)); // Ok: > 15 min. function first_class_six_min_schedule( $schedules ) { $schedules['every_6_mins'] = array( @@ -326,3 +327,35 @@ 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. +add_filter( 'cron_schedules', \first_class_six_min_schedule(...)); // Warning: 6 min. +add_filter( 'cron_schedules', namespace\first_class_six_min_schedule(...)); // Warning: 6 min. + +/* + * The tests below document the current behavior of the sniff, even though they are false negatives. The sniff treats + * the first-class callable examples below as if referencing the global function first_class_six_min_schedule() + * and not a namespaced function with the same name. + * + * Related to: https://github.com/WordPress/WordPress-Coding-Standards/issues/2644. + */ +add_filter( 'cron_schedules', MyNamespace\first_class_weekly_schedule(...)); // False negative - Ok: > 15 min, but should be marked `ChangeDetected`. +add_filter( 'cron_schedules', \MyNamespace\first_class_weekly_schedule(...)); // False negative - Ok: > 15 min, but should be marked `ChangeDetected`. +add_filter( 'cron_schedules', namespace\Sub\first_class_weekly_schedule(...)); // False negative - Ok: > 15 min, but should be marked `ChangeDetected`. + +/* + * The tests below document the current behavior of the sniff, even though they are false positives. The sniff treats + * the first-class callable examples below as if referencing the global function first_class_six_min_schedule() + * and not a namespaced function with the same name. Fixing this incorrect behavior is not trivial. + * + * Related to: https://github.com/WordPress/WordPress-Coding-Standards/issues/2644. + */ +add_filter( 'cron_schedules', MyNamespace\first_class_six_min_schedule(...)); // False positive - `CronSchedulesInterval` warning (6 min), but should be `ChangeDetected`. +add_filter( 'cron_schedules', \MyNamespace\first_class_six_min_schedule(...)); // False positive - `CronSchedulesInterval` warning (6 min), but should be `ChangeDetected`. +add_filter( 'cron_schedules', namespace\Sub\first_class_six_min_schedule(...)); // False positive - `CronSchedulesInterval` warning (6 min), but should be `ChangeDetected`. + +/* + * 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..2417abaf41 100644 --- a/WordPress/Tests/WP/CronIntervalUnitTest.php +++ b/WordPress/Tests/WP/CronIntervalUnitTest.php @@ -65,8 +65,13 @@ public function getWarningList() { 286 => 1, 288 => 1, 290 => 1, - 327 => 1, 328 => 1, + 329 => 1, + 330 => 1, + 331 => 1, + 351 => 1, + 352 => 1, + 353 => 1, ); } } diff --git a/WordPress/Tests/WP/DiscouragedConstantsUnitTest.inc b/WordPress/Tests/WP/DiscouragedConstantsUnitTest.inc index ea8ae72524..50302d54fc 100644 --- a/WordPress/Tests/WP/DiscouragedConstantsUnitTest.inc +++ b/WordPress/Tests/WP/DiscouragedConstantsUnitTest.inc @@ -80,10 +80,10 @@ enum BACKGROUND_COLOR: string implements Colorful {} // Safeguard support for PHP 8.0+ named parameters. define( case_insensitive: false, value: 'something' ); // OK. Well, not really as missing a required param, but that's not the concern of this sniff. -define( case_insensitive: false, constant_name: 'STYLESHEETPATH', value: 'something' ); // Bad. +\DEFINE( case_insensitive: false, constant_name: 'STYLESHEETPATH', value: 'something' ); // Bad. // Safeguard that comments in the parameters are ignored. -define( +Define( // Name. 'STYLESHEETPATH', // Value. @@ -102,3 +102,11 @@ enum ContainsConst { } echo HEADER_TEXTCOLOR::$var; // OK. + +/* + * Safeguard correct handling of all types of namespaced function calls. + */ +\define( 'HEADER_IMAGE', 'something' ); // Bad. +\MyNamespace\define( 'TEMPLATEPATH', 'something' ); // Ok. +MyNamespace\define( 'PLUGINDIR', 'something' ); // Ok. +namespace\define( 'MUPLUGINDIR', 'something' ); // Ok. diff --git a/WordPress/Tests/WP/DiscouragedConstantsUnitTest.php b/WordPress/Tests/WP/DiscouragedConstantsUnitTest.php index e27fd838c2..138d0eb4ef 100644 --- a/WordPress/Tests/WP/DiscouragedConstantsUnitTest.php +++ b/WordPress/Tests/WP/DiscouragedConstantsUnitTest.php @@ -36,26 +36,27 @@ 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, + 64 => 1, + 66 => 1, + 67 => 1, + 71 => 1, + 72 => 1, + 83 => 1, + 88 => 1, + 109 => 1, ); } }