true, 'class.wp-scripts.php' => true, 'class.wp-styles.php' => true, 'functions.wp-scripts.php' => true, 'functions.wp-styles.php' => true, ); /** * Unit test version of the historical exceptions in WP core. * * @since 0.11.0 * @since 3.0.0 Property has been renamed from `$unittest_class_exceptions` to `$unittest_hyphenation_exceptions`, * * @var array */ private $unittest_hyphenation_exceptions = array( 'class.wp-dependencies.inc' => true, 'class.wp-scripts.inc' => true, 'class.wp-styles.inc' => true, 'functions.wp-scripts.inc' => true, 'functions.wp-styles.inc' => true, ); /** * Returns an array of tokens this test wants to listen for. * * @return array */ public function register() { if ( \defined( '\PHP_CODESNIFFER_IN_TESTS' ) ) { $this->hyphenation_exceptions += $this->unittest_hyphenation_exceptions; } return Collections::phpOpenTags(); } /** * Processes this test, when one of its tokens is encountered. * * @param int $stackPtr The position of the current token in the stack. * * @return int|void Integer stack pointer to skip forward or void to continue * normal file processing. */ public function process_token( $stackPtr ) { // Usage of `stripQuotes` is to ensure `stdin_path` passed by IDEs does not include quotes. $file = TextStrings::stripQuotes( $this->phpcsFile->getFileName() ); if ( 'STDIN' === $file ) { return; } $class_ptr = $this->phpcsFile->findNext( \T_CLASS, $stackPtr ); if ( false !== $class_ptr && $this->is_test_class( $this->phpcsFile, $class_ptr ) ) { /* * This rule should not be applied to test classes (at all). * @link https://github.com/WordPress/WordPress-Coding-Standards/issues/1995 */ return; } // Respect phpcs:disable comments as long as they are not accompanied by an enable. $i = -1; while ( $i = $this->phpcsFile->findNext( \T_PHPCS_DISABLE, ( $i + 1 ) ) ) { if ( empty( $this->tokens[ $i ]['sniffCodes'] ) || isset( $this->tokens[ $i ]['sniffCodes']['WordPress'] ) || isset( $this->tokens[ $i ]['sniffCodes']['WordPress.Files'] ) || isset( $this->tokens[ $i ]['sniffCodes']['WordPress.Files.FileName'] ) ) { do { $i = $this->phpcsFile->findNext( \T_PHPCS_ENABLE, ( $i + 1 ) ); } while ( false !== $i && ! empty( $this->tokens[ $i ]['sniffCodes'] ) && ! isset( $this->tokens[ $i ]['sniffCodes']['WordPress'] ) && ! isset( $this->tokens[ $i ]['sniffCodes']['WordPress.Files'] ) && ! isset( $this->tokens[ $i ]['sniffCodes']['WordPress.Files.FileName'] ) ); if ( false === $i ) { // The entire (rest of the) file is disabled. return; } } } $file_name = basename( $file ); $this->check_filename_is_hyphenated( $file_name ); if ( true === $this->strict_class_file_names && false !== $class_ptr ) { $this->check_filename_has_class_prefix( $class_ptr, $file_name ); } if ( false !== strpos( $file, \DIRECTORY_SEPARATOR . 'wp-includes' . \DIRECTORY_SEPARATOR ) && false === $class_ptr ) { $this->check_filename_for_template_suffix( $stackPtr, $file_name ); } // Only run this sniff once per file, no need to run it again. return ( $this->phpcsFile->numTokens + 1 ); } /** * Generic check for lowercase hyphenated file names. * * @since 3.0.0 * * @param string $file_name The name of the current file. * * @return void */ protected function check_filename_is_hyphenated( $file_name ) { $extension = strrchr( $file_name, '.' ); $name = substr( $file_name, 0, ( strlen( $file_name ) - strlen( $extension ) ) ); $expected = strtolower( preg_replace( '`[[:punct:]]`', '-', $name ) ) . $extension; if ( $file_name === $expected || isset( $this->hyphenation_exceptions[ $file_name ] ) ) { return; } if ( true === $this->is_theme && 1 === preg_match( self::THEME_EXCEPTIONS_REGEX, $file_name ) ) { return; } $this->phpcsFile->addError( 'Filenames should be all lowercase with hyphens as word separators. Expected %s, but found %s.', 0, 'NotHyphenatedLowercase', array( $expected, $file_name ) ); } /** * Check files containing a class for the "class-" prefix and that the rest of * the file name reflects the class name. * * @since 3.0.0 * * @param int $class_ptr Stack pointer to the first T_CLASS in the file. * @param string $file_name The name of the current file. * * @return void */ protected function check_filename_has_class_prefix( $class_ptr, $file_name ) { $extension = strrchr( $file_name, '.' ); $class_name = ObjectDeclarations::getName( $this->phpcsFile, $class_ptr ); $expected = 'class-' . strtolower( str_replace( '_', '-', $class_name ) ) . $extension; if ( $file_name === $expected ) { return; } $this->phpcsFile->addError( 'Class file names should be based on the class name with "class-" prepended. Expected %s, but found %s.', 0, 'InvalidClassFileName', array( $expected, $file_name, ) ); } /** * Check non-class files in "wp-includes" with a "@subpackage Template" tag for a "-template" suffix. * * @since 3.0.0 * * @param int $stackPtr Stack pointer to the first PHP open tag in the file. * @param string $file_name The name of the current file. * * @return void */ protected function check_filename_for_template_suffix( $stackPtr, $file_name ) { $subpackage_tag = $this->phpcsFile->findNext( \T_DOC_COMMENT_TAG, $stackPtr, null, false, '@subpackage' ); if ( false === $subpackage_tag ) { return; } $subpackage = $this->phpcsFile->findNext( \T_DOC_COMMENT_STRING, $subpackage_tag ); if ( false === $subpackage ) { return; } $fileName_end = substr( $file_name, -13 ); if ( ( 'Template' === trim( $this->tokens[ $subpackage ]['content'] ) && $this->tokens[ $subpackage_tag ]['line'] === $this->tokens[ $subpackage ]['line'] ) && ( ( ! \defined( '\PHP_CODESNIFFER_IN_TESTS' ) && '-template.php' !== $fileName_end ) || ( \defined( '\PHP_CODESNIFFER_IN_TESTS' ) && '-template.inc' !== $fileName_end ) ) ) { $this->phpcsFile->addError( 'Files containing template tags should have "-template" appended to the end of the file name. Expected %s, but found %s.', 0, 'InvalidTemplateTagFileName', array( substr( $file_name, 0, -4 ) . '-template.php', $file_name, ) ); } } }