From 36157228a471f8085584726ead8e3e59503648ab Mon Sep 17 00:00:00 2001 From: Anton Vlasenko <43744263+anton-vlasenko@users.noreply.github.com> Date: Thu, 20 Jul 2023 16:48:07 +0200 Subject: [PATCH] Enforce checks against redeclaration for functions and classes (#52696) Enforce checks against redeclaration for functions and classes. See https://github.com/WordPress/gutenberg/blob/trunk/lib/README.md#wrap-functions-and-classes-with--function_exists-and--class_exists Props: @tellthemachines @azaozz @ramonjd @anton-vlasenko --- .github/CODEOWNERS | 1 + composer.json | 12 +- lib/class-wp-duotone-gutenberg.php | 4 + lib/class-wp-theme-json-data-gutenberg.php | 4 + lib/class-wp-theme-json-gutenberg.php | 4 + ...class-wp-theme-json-resolver-gutenberg.php | 4 + .../class-wp-html-attribute-token.php | 4 + .../html-api/class-wp-html-span.php | 4 + .../html-api/class-wp-html-tag-processor.php | 4 + .../class-wp-html-text-replacement.php | 4 + lib/compat/wordpress-6.2/rest-api.php | 69 +++--- lib/compat/wordpress-6.3/kses.php | 32 +-- lib/compat/wordpress-6.3/rest-api.php | 14 +- lib/compat/wordpress-6.3/theme-previews.php | 37 +-- lib/experimental/class--wp-editors.php | 4 + ...-rest-block-editor-settings-controller.php | 4 + .../class-wp-rest-customizer-nonces.php | 4 + .../class-wp-directive-context.php | 4 + .../class-wp-directive-processor.php | 4 + .../class-wp-interactivity-store.php | 4 + lib/experimental/interactivity-api/store.php | 24 +- lib/experimental/kses.php | 37 +-- lib/experimental/rest-api.php | 35 +-- lib/experiments-page.php | 42 ++-- phpcs.xml.dist | 16 ++ .../php/gutenberg-coding-standards/.gitignore | 5 + .../.phpcs.xml.dist | 88 +++++++ .../GuardedFunctionAndClassNamesSniff.php | 221 ++++++++++++++++++ .../GuardedFunctionAndClassNamesUnitTest.inc | 26 +++ .../GuardedFunctionAndClassNamesUnitTest.php | 39 ++++ .../Gutenberg/ruleset.xml | 8 + test/php/gutenberg-coding-standards/README.md | 3 + .../Tests/bootstrap.php | 86 +++++++ .../gutenberg-coding-standards/composer.json | 65 ++++++ .../phpunit.xml.dist | 16 ++ 35 files changed, 794 insertions(+), 138 deletions(-) create mode 100644 test/php/gutenberg-coding-standards/.gitignore create mode 100644 test/php/gutenberg-coding-standards/.phpcs.xml.dist create mode 100644 test/php/gutenberg-coding-standards/Gutenberg/Sniffs/CodeAnalysis/GuardedFunctionAndClassNamesSniff.php create mode 100644 test/php/gutenberg-coding-standards/Gutenberg/Tests/CodeAnalysis/GuardedFunctionAndClassNamesUnitTest.inc create mode 100644 test/php/gutenberg-coding-standards/Gutenberg/Tests/CodeAnalysis/GuardedFunctionAndClassNamesUnitTest.php create mode 100644 test/php/gutenberg-coding-standards/Gutenberg/ruleset.xml create mode 100644 test/php/gutenberg-coding-standards/README.md create mode 100644 test/php/gutenberg-coding-standards/Tests/bootstrap.php create mode 100644 test/php/gutenberg-coding-standards/composer.json create mode 100644 test/php/gutenberg-coding-standards/phpunit.xml.dist diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4008efa1b4d4e1..a324657fad6f7d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -82,6 +82,7 @@ /packages/scripts @gziolo @ntwb @nerrad @ajitbohra @ryanwelcher /packages/stylelint-config @ntwb /test/e2e @kevin940726 @Mamaduka +/test/php/gutenberg-coding-standards @anton-vlasenko # UI Components /packages/components @ajitbohra diff --git a/composer.json b/composer.json index 3f16ba495a94ca..134e366befdb94 100644 --- a/composer.json +++ b/composer.json @@ -32,8 +32,18 @@ "wp-coding-standards/wpcs": "^2.2", "sirbrillig/phpcs-variable-analysis": "^2.8", "spatie/phpunit-watcher": "^1.23", - "yoast/phpunit-polyfills": "^1.0" + "yoast/phpunit-polyfills": "^1.0", + "gutenberg/gutenberg-coding-standards": "@dev" }, + "repositories": [ + { + "type": "path", + "url": "./test/php/gutenberg-coding-standards", + "options": { + "symlink": false + } + } + ], "require": { "composer/installers": "~1.0" }, diff --git a/lib/class-wp-duotone-gutenberg.php b/lib/class-wp-duotone-gutenberg.php index 816f7e414ad793..41120a882ed235 100644 --- a/lib/class-wp-duotone-gutenberg.php +++ b/lib/class-wp-duotone-gutenberg.php @@ -32,6 +32,10 @@ * @since 6.3.0 */ +if ( class_exists( 'WP_Duotone_Gutenberg' ) ) { + return; +} + /** * Manages duotone block supports and global styles. * diff --git a/lib/class-wp-theme-json-data-gutenberg.php b/lib/class-wp-theme-json-data-gutenberg.php index 2e5ea474346a92..db0737ebea08b6 100644 --- a/lib/class-wp-theme-json-data-gutenberg.php +++ b/lib/class-wp-theme-json-data-gutenberg.php @@ -6,6 +6,10 @@ * @since 6.1.0 */ +if ( class_exists( 'WP_Theme_JSON_Data_Gutenberg' ) ) { + return; +} + /** * Class to provide access to update a theme.json structure. */ diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 0745ee06b84a23..60e69632eedb62 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -6,6 +6,10 @@ * @since 5.8.0 */ +if ( class_exists( 'WP_Theme_JSON_Gutenberg' ) ) { + return; +} + /** * Class that encapsulates the processing of structures that adhere to the theme.json spec. * diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index 1e825e3c6bbe4f..39721742946cd1 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -6,6 +6,10 @@ * @since 5.8.0 */ +if ( class_exists( 'WP_Theme_JSON_Resolver_Gutenberg' ) ) { + return; +} + /** * Class that abstracts the processing of the different data sources * for site-level config and offers an API to work with them. diff --git a/lib/compat/wordpress-6.2/html-api/class-wp-html-attribute-token.php b/lib/compat/wordpress-6.2/html-api/class-wp-html-attribute-token.php index 2c52164a979f02..cc03c1441ee042 100644 --- a/lib/compat/wordpress-6.2/html-api/class-wp-html-attribute-token.php +++ b/lib/compat/wordpress-6.2/html-api/class-wp-html-attribute-token.php @@ -7,6 +7,10 @@ * @since 6.2.0 */ +if ( class_exists( 'WP_HTML_Attribute_Token' ) ) { + return; +} + /** * Data structure for the attribute token that allows to drastically improve performance. * diff --git a/lib/compat/wordpress-6.2/html-api/class-wp-html-span.php b/lib/compat/wordpress-6.2/html-api/class-wp-html-span.php index d92778cd3a2223..e38bc551923170 100644 --- a/lib/compat/wordpress-6.2/html-api/class-wp-html-span.php +++ b/lib/compat/wordpress-6.2/html-api/class-wp-html-span.php @@ -7,6 +7,10 @@ * @since 6.2.0 */ +if ( class_exists( 'WP_HTML_Span' ) ) { + return; +} + /** * Represents a textual span inside an HTML document. * diff --git a/lib/compat/wordpress-6.2/html-api/class-wp-html-tag-processor.php b/lib/compat/wordpress-6.2/html-api/class-wp-html-tag-processor.php index 7edb67f9f0423e..d61180074f608d 100644 --- a/lib/compat/wordpress-6.2/html-api/class-wp-html-tag-processor.php +++ b/lib/compat/wordpress-6.2/html-api/class-wp-html-tag-processor.php @@ -26,6 +26,10 @@ * @since 6.2.0 */ +if ( class_exists( 'WP_HTML_Tag_Processor' ) ) { + return; +} + /** * Modifies attributes in an HTML document for tags matching a query. * diff --git a/lib/compat/wordpress-6.2/html-api/class-wp-html-text-replacement.php b/lib/compat/wordpress-6.2/html-api/class-wp-html-text-replacement.php index 912b4a56a5eb42..b3f70c8e7c57f4 100644 --- a/lib/compat/wordpress-6.2/html-api/class-wp-html-text-replacement.php +++ b/lib/compat/wordpress-6.2/html-api/class-wp-html-text-replacement.php @@ -7,6 +7,10 @@ * @since 6.2.0 */ +if ( class_exists( 'WP_HTML_Text_Replacement' ) ) { + return; +} + /** * Data structure used to replace existing content from start to end that allows to drastically improve performance. * diff --git a/lib/compat/wordpress-6.2/rest-api.php b/lib/compat/wordpress-6.2/rest-api.php index c0f098fa6893ac..97f7daecdff2f7 100644 --- a/lib/compat/wordpress-6.2/rest-api.php +++ b/lib/compat/wordpress-6.2/rest-api.php @@ -91,43 +91,46 @@ function gutenberg_modify_rest_sidebars_response( $response ) { } add_filter( 'rest_prepare_sidebar', 'gutenberg_modify_rest_sidebars_response' ); - -/** - * Add the `block_types` value to the `pattern-directory-item` schema. - * - * @since 6.2.0 Added 'block_types' property. - */ -function add_block_pattern_block_types_schema() { - register_rest_field( - 'pattern-directory-item', - 'block_types', - array( - 'schema' => array( - 'description' => __( 'The block types which can use this pattern.', 'gutenberg' ), - 'type' => 'array', - 'uniqueItems' => true, - 'items' => array( 'type' => 'string' ), - 'context' => array( 'view', 'embed' ), - ), - ) - ); +if ( ! function_exists( 'add_block_pattern_block_types_schema' ) ) { + /** + * Add the `block_types` value to the `pattern-directory-item` schema. + * + * @since 6.2.0 Added 'block_types' property. + */ + function add_block_pattern_block_types_schema() { + register_rest_field( + 'pattern-directory-item', + 'block_types', + array( + 'schema' => array( + 'description' => __( 'The block types which can use this pattern.', 'gutenberg' ), + 'type' => 'array', + 'uniqueItems' => true, + 'items' => array( 'type' => 'string' ), + 'context' => array( 'view', 'embed' ), + ), + ) + ); + } } add_filter( 'rest_api_init', 'add_block_pattern_block_types_schema' ); -/** - * Add the `block_types` value into the API response. - * - * @since 6.2.0 Added 'block_types' property. - * - * @param WP_REST_Response $response The response object. - * @param object $raw_pattern The unprepared pattern. - */ -function filter_block_pattern_response( $response, $raw_pattern ) { - $data = $response->get_data(); - $data['block_types'] = array_map( 'sanitize_text_field', $raw_pattern->meta->wpop_block_types ); - $response->set_data( $data ); - return $response; +if ( ! function_exists( 'filter_block_pattern_response' ) ) { + /** + * Add the `block_types` value into the API response. + * + * @since 6.2.0 Added 'block_types' property. + * + * @param WP_REST_Response $response The response object. + * @param object $raw_pattern The unprepared pattern. + */ + function filter_block_pattern_response( $response, $raw_pattern ) { + $data = $response->get_data(); + $data['block_types'] = array_map( 'sanitize_text_field', $raw_pattern->meta->wpop_block_types ); + $response->set_data( $data ); + return $response; + } } add_filter( 'rest_prepare_block_pattern', 'filter_block_pattern_response', 10, 2 ); diff --git a/lib/compat/wordpress-6.3/kses.php b/lib/compat/wordpress-6.3/kses.php index 23eee580f831ab..b0b7356d2dac1c 100644 --- a/lib/compat/wordpress-6.3/kses.php +++ b/lib/compat/wordpress-6.3/kses.php @@ -7,21 +7,23 @@ * @package gutenberg */ -/** - * Mark CSS safe if it contains grid functions - * - * This function should not be backported to core. - * - * @param bool $allow_css Whether the CSS is allowed. - * @param string $css_test_string The CSS to test. - */ -function allow_grid_functions_in_styles( $allow_css, $css_test_string ) { - if ( preg_match( - '/^grid-template-columns:\s*repeat\([0-9,a-z-\s\(\)]*\)$/', - $css_test_string - ) ) { - return true; +if ( ! function_exists( 'allow_grid_functions_in_styles' ) ) { + /** + * Mark CSS safe if it contains grid functions + * + * This function should not be backported to core. + * + * @param bool $allow_css Whether the CSS is allowed. + * @param string $css_test_string The CSS to test. + */ + function allow_grid_functions_in_styles( $allow_css, $css_test_string ) { + if ( preg_match( + '/^grid-template-columns:\s*repeat\([0-9,a-z-\s\(\)]*\)$/', + $css_test_string + ) ) { + return true; + } + return $allow_css; } - return $allow_css; } add_filter( 'safecss_filter_attr_allow_css', 'allow_grid_functions_in_styles', 10, 2 ); diff --git a/lib/compat/wordpress-6.3/rest-api.php b/lib/compat/wordpress-6.3/rest-api.php index 90898c0b71e246..ecb8f52392fef9 100644 --- a/lib/compat/wordpress-6.3/rest-api.php +++ b/lib/compat/wordpress-6.3/rest-api.php @@ -52,12 +52,13 @@ function gutenberg_update_templates_template_parts_rest_controller( $args, $post } add_filter( 'register_post_type_args', 'gutenberg_update_templates_template_parts_rest_controller', 10, 2 ); -/** - * Add the `modified` value to the `wp_template` schema. - * - * @since 6.3.0 Added 'modified' property and response value. - */ -function add_modified_wp_template_schema() { +if ( ! function_exists( 'add_modified_wp_template_schema' ) ) { + /** + * Add the `modified` value to the `wp_template` schema. + * + * @since 6.3.0 Added 'modified' property and response value. + */ + function add_modified_wp_template_schema() { register_rest_field( array( 'wp_template', 'wp_template_part' ), 'modified', @@ -80,6 +81,7 @@ function add_modified_wp_template_schema() { }, ) ); + } } add_filter( 'rest_api_init', 'add_modified_wp_template_schema' ); diff --git a/lib/compat/wordpress-6.3/theme-previews.php b/lib/compat/wordpress-6.3/theme-previews.php index 26153d74878b58..e885d389b94fe0 100644 --- a/lib/compat/wordpress-6.3/theme-previews.php +++ b/lib/compat/wordpress-6.3/theme-previews.php @@ -51,14 +51,15 @@ function gutenberg_attach_theme_preview_middleware() { ); } -/** - * Temporary function to add a live preview button to block themes. - * Remove when https://core.trac.wordpress.org/ticket/58190 lands. - */ -function add_live_preview_button() { - global $pagenow; - if ( 'themes.php' === $pagenow ) { - ?> +if ( ! function_exists( 'add_live_preview_button' ) ) { + /** + * Temporary function to add a live preview button to block themes. + * Remove when https://core.trac.wordpress.org/ticket/58190 lands. + */ + function add_live_preview_button() { + global $pagenow; + if ( 'themes.php' === $pagenow ) { + ?> - +if ( ! function_exists( 'block_theme_activate_nonce' ) ) { + /** + * Adds a nonce for the theme activation link. + */ + function block_theme_activate_nonce() { + $nonce_handle = 'switch-theme_' . gutenberg_get_theme_preview_path(); + ?> - -