diff --git a/CHANGELOG.md b/CHANGELOG.md index f1a014ef..e32dd763 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ +## 2.5.0 + +- Add `HM.Files.PatternSlugMatchesFilename` sniff verifying that a theme pattern file's `Slug:` header matches its filename + ## 2.4.0 - Add `HM.Functions.RegisterBlockTypePath` sniff recommending `register_block_type_from_metadata()` when a file path is passed to `register_block_type()` diff --git a/HM/Sniffs/Files/PatternSlugMatchesFilenameSniff.php b/HM/Sniffs/Files/PatternSlugMatchesFilenameSniff.php new file mode 100644 index 00000000..76d72e15 --- /dev/null +++ b/HM/Sniffs/Files/PatternSlugMatchesFilenameSniff.php @@ -0,0 +1,62 @@ + + */ + public function register() : array { + return [ T_OPEN_TAG ]; + } + + /** + * Compare the trailing segment of the pattern's Slug header to the filename. + * + * @param File $phpcs_file The file being scanned. + * @param int $stack_ptr Position of the open tag. + * @return int Pointer past the end of the file, so the sniff runs only once. + */ + public function process( File $phpcs_file, $stack_ptr ) { + if ( ! preg_match( '#/themes/[^/]+/patterns/([^/]+)\.php$#', $phpcs_file->getFilename(), $matches ) ) { + return $phpcs_file->numTokens; + } + $filename = $matches[1]; + + foreach ( $phpcs_file->getTokens() as $ptr => $token ) { + if ( T_DOC_COMMENT_STRING !== $token['code'] ) { + continue; + } + if ( ! preg_match( '#^Slug:\s*(\S+)#', trim( $token['content'] ), $slug_match ) ) { + continue; + } + + $slug = $slug_match[1]; + if ( basename( $slug ) !== $filename ) { + $phpcs_file->addError( + 'Pattern slug "%s" does not match the filename; expected the file to be named "%s.php".', + $ptr, + 'SlugMismatch', + [ $slug, basename( $slug ) ] + ); + } + break; + } + + return $phpcs_file->numTokens; + } +} diff --git a/HM/Tests/Files/PatternSlugMatchesFilenameUnitTest.php b/HM/Tests/Files/PatternSlugMatchesFilenameUnitTest.php new file mode 100644 index 00000000..849f0b15 --- /dev/null +++ b/HM/Tests/Files/PatternSlugMatchesFilenameUnitTest.php @@ -0,0 +1,68 @@ +isFile() ) { + continue; + } + + $test_files[] = $file->getPathname(); + } + + // Put them in order. + sort( $test_files ); + + return $test_files; + } + + /** + * Returns the lines where errors should occur. + * + * @return array => + */ + public function getErrorList() { + $file = func_get_arg( 0 ); + $fail = [ + 'renamed-pattern.php' => [ + 4 => 1, + ], + ]; + + return $fail[ $file ] ?? []; + } + + /** + * Returns the lines where warnings should occur. + * + * @return array => + */ + public function getWarningList() { + return []; + } + +} diff --git a/HM/Tests/Files/PatternSlugMatchesFilenameUnitTest/themes/my-theme/functions.php b/HM/Tests/Files/PatternSlugMatchesFilenameUnitTest/themes/my-theme/functions.php new file mode 100644 index 00000000..1d01a7b5 --- /dev/null +++ b/HM/Tests/Files/PatternSlugMatchesFilenameUnitTest/themes/my-theme/functions.php @@ -0,0 +1,7 @@ + +

Hero banner content.

diff --git a/HM/Tests/Files/PatternSlugMatchesFilenameUnitTest/themes/my-theme/patterns/no-slug.php b/HM/Tests/Files/PatternSlugMatchesFilenameUnitTest/themes/my-theme/patterns/no-slug.php new file mode 100644 index 00000000..751c938e --- /dev/null +++ b/HM/Tests/Files/PatternSlugMatchesFilenameUnitTest/themes/my-theme/patterns/no-slug.php @@ -0,0 +1,6 @@ + +

No slug here.

diff --git a/HM/Tests/Files/PatternSlugMatchesFilenameUnitTest/themes/my-theme/patterns/renamed-pattern.php b/HM/Tests/Files/PatternSlugMatchesFilenameUnitTest/themes/my-theme/patterns/renamed-pattern.php new file mode 100644 index 00000000..fc138385 --- /dev/null +++ b/HM/Tests/Files/PatternSlugMatchesFilenameUnitTest/themes/my-theme/patterns/renamed-pattern.php @@ -0,0 +1,8 @@ + +

The slug above does not match this filename.