Skip to content

Commit

Permalink
Enforce valid function names in the ./packages/block-library/src/*/*.…
Browse files Browse the repository at this point in the history
…php files (#53438)

Enforce valid function names in the ./packages/block-library/src/*/*.php files.

---------

Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
  • Loading branch information
anton-vlasenko and gziolo authored Aug 16, 2023
1 parent 618652f commit c991bfc
Show file tree
Hide file tree
Showing 3 changed files with 227 additions and 0 deletions.
55 changes: 55 additions & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,59 @@
</property>
</properties>
</rule>

<rule ref="Gutenberg.NamingConventions.ValidBlockLibraryFunctionName">
<include-pattern>./packages/block-library/src/*/*.php</include-pattern>
<properties>
<property name="prefixes" type="array">
<element value="block_core_"/>
<element value="render_block_core_"/>
<element value="register_block_core_"/>
</property>
<!--
The following list of functions is final and must not be extended.
It includes functions that cannot be renamed due to backward compatibility concerns.
-->
<property name="allowed_functions" type="array">
<element value="_delete_custom_logo_on_remove_site_logo"/>
<element value="_delete_site_logo_on_remove_custom_logo"/>
<element value="_delete_site_logo_on_remove_custom_logo_on_setup_theme"/>
<element value="_delete_site_logo_on_remove_theme_mods"/>
<element value="_override_custom_logo_theme_mod"/>
<element value="_sync_custom_logo_to_site_logo"/>
<element value="_wp_rest_api_autosave_meta"/>
<element value="_wp_rest_api_force_autosave_difference"/>
<element value="apply_block_core_search_border_style"/>
<element value="apply_block_core_search_border_styles"/>
<element value="build_dropdown_script_block_core_categories"/>
<element value="build_template_part_block_area_variations"/>
<element value="build_template_part_block_instance_variations"/>
<element value="build_template_part_block_variations"/>
<element value="build_variation_for_navigation_link"/>
<element value="classnames_for_block_core_search"/>
<element value="comments_block_form_defaults"/>
<element value="enqueue_legacy_post_comments_block_styles"/>
<element value="get_block_core_avatar_border_attributes"/>
<element value="get_block_core_post_featured_image_border_attributes"/>
<element value="get_block_core_post_featured_image_overlay_element_markup"/>
<element value="get_border_color_classes_for_block_core_search"/>
<element value="get_color_classes_for_block_core_search"/>
<element value="get_typography_classes_for_block_core_search"/>
<element value="get_typography_styles_for_block_core_search"/>
<element value="gutenberg_block_core_file_update_interactive_view_script"/>
<element value="gutenberg_block_core_navigation_update_interactive_view_script"/>
<element value="post_comments_form_block_form_defaults"/>
<element value="register_block_core_site_icon_setting"/>
<element value="register_legacy_post_comments_block"/>
<element value="styles_for_block_core_search"/>
<element value="wp_add_footnotes_revisions_to_post_meta"/>
<element value="wp_add_footnotes_to_revision"/>
<element value="wp_get_footnotes_from_revision"/>
<element value="wp_keep_footnotes_revision_id"/>
<element value="wp_latest_comments_draft_or_post_title"/>
<element value="wp_restore_footnotes_from_revision"/>
<element value="wp_save_footnotes_meta"/>
</property>
</properties>
</rule>
</ruleset>
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
<?php
/**
* Gutenberg Coding Standards.
*
* @package gutenberg/gutenberg-coding-standards
* @link https://github.com/WordPress/gutenberg
* @license https://opensource.org/licenses/MIT MIT
*/

namespace GutenbergCS\Gutenberg\Sniffs\NamingConventions;

use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;

/**
*
* This sniff checks function names to ensure they adhere to specified prefixes
* determined by the parent directory name. It enforces that function names start
* with one of the allowed prefixes defined in the sniffer configuration.
*
* @package gutenberg/gutenberg-coding-standards
*
* @since 1.0.0
*/
final class ValidBlockLibraryFunctionNameSniff implements Sniff {
/**
* Target prefixes.
*
* @var array
*/
public $prefixes = array();

/**
* These functions are considered permissible and will be ignored by the sniffer.
*
* @var array
*/
public $allowed_functions = array();

/**
* Registers the tokens that this sniff wants to listen for.
*
* @return array
*/
public function register() {
$this->onRegisterEvent();

return array( T_FUNCTION );
}

/**
* Processes function tokens.
*
* @param File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process( File $phpcsFile, $stackPtr ) {
$tokens = $phpcsFile->getTokens();
$token = $tokens[ $stackPtr ];

if ( 'T_FUNCTION' !== $token['type'] ) {
return;
}

$this->processFunctionToken( $phpcsFile, $stackPtr );
}

/**
* This method analyzes the function token and its name within the provided file.
* It checks if the function name adheres to allowed prefixes based on the parent directory name.
* If the function name is not valid, an error message is added to the code sniffer report.
*
* @param File $phpcsFile The file being scanned.
* @param int $stackPointer The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
private function processFunctionToken( File $phpcsFile, $stackPointer ) {

if ( empty( $this->prefixes ) ) {
// Nothing to process.
return;
}

$tokens = $phpcsFile->getTokens();
$function_token = $phpcsFile->findNext( T_STRING, $stackPointer );

$wrapping_tokens_to_check = array(
T_CLASS,
T_INTERFACE,
T_TRAIT,
);

foreach ( $wrapping_tokens_to_check as $wrapping_token_to_check ) {
if ( false !== $phpcsFile->getCondition( $function_token, $wrapping_token_to_check, false ) ) {
// This sniff only processes functions, not class methods.
return;
}
}

$function_name = $tokens[ $function_token ]['content'];

if ( in_array( $function_name, $this->allowed_functions, true ) ) {
// The function name is included in the list of allowed functions; bypassing further checks.
return;
}

$parent_directory_name = basename( dirname( $phpcsFile->getFilename() ) );

$allowed_function_prefixes = array();
$is_function_name_valid = false;
foreach ( $this->prefixes as $prefix ) {
$prefix = rtrim( $prefix, '_' );
$allowed_function_prefix = $prefix . '_' . self::sanitize_directory_name( $parent_directory_name );
$allowed_function_prefixes[] = $allowed_function_prefix;
// Validate the name's correctness and ensure it does not end with an underscore.
$regexp = sprintf( '/^%s(|_.+)$/', preg_quote( $allowed_function_prefix, '/' ) );
$is_function_name_valid |= ( 1 === preg_match( $regexp, $function_name ) );
}

if ( $is_function_name_valid ) {
return;
}

$error_message = "The function name `{$function_name}()` is invalid because PHP function names in this file should start with one of the following prefixes: `"
. implode( '`, `', $allowed_function_prefixes ) . '`.';
$phpcsFile->addError( $error_message, $function_token, 'FunctionNameInvalid' );
}

/**
* The purpose of this method is to run callbacks
* after the class properties have been set.
*/
private function onRegisterEvent() {
$this->prefixes = self::sanitize( $this->prefixes );
$this->allowed_functions = self::sanitize( $this->allowed_functions );
}

/**
* Sanitize a directory name by converting it to lowercase and replacing non-letter
* and non-digit characters with underscores.
*
* @param string $directory_name
*
* @return string
*/
private static function sanitize_directory_name( $directory_name ) {
// Convert to lowercase.
$directory_name = strtolower( $directory_name );

// Replace non-letter and non-digit characters with underscores.
return preg_replace( '/[^a-z0-9]/', '_', $directory_name );
}

/**
* Sanitize an array of values by trimming each element and removing empty elements.
*
* @param array $values The values being sanitized.
*
* @return array
*/
private static function sanitize( $values ) {
$values = array_map( 'trim', $values );

return array_filter( $values );
}
}
1 change: 1 addition & 0 deletions test/php/gutenberg-coding-standards/Gutenberg/ruleset.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
<description>Gutenberg Coding Standards</description>

<rule ref="Gutenberg.CodeAnalysis.GuardedFunctionAndClassNames"/>
<rule ref="Gutenberg.NamingConventions.ValidBlockLibraryFunctionName"/>

</ruleset>

0 comments on commit c991bfc

Please sign in to comment.