Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
<!-- When submitting a PR for a feature, add the appropriate prerelease heading here
and fill in the contents as you go. This simplifies later release management. -->

## 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()`
Expand Down
62 changes: 62 additions & 0 deletions HM/Sniffs/Files/PatternSlugMatchesFilenameSniff.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php
/**
* Sniff verifying that theme pattern slugs match their filenames.
*/

namespace HM\Sniffs\Files;

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

/**
* Flags theme pattern files whose Slug: header does not end with the file's basename.
*
* A pattern with `Slug: my-theme/hero-banner` must live in `hero-banner.php`.
*/
class PatternSlugMatchesFilenameSniff implements Sniff {

/**
* Register for the open tag so the check runs once per file.
*
* @return array<int|string>
*/
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;
}
}
68 changes: 68 additions & 0 deletions HM/Tests/Files/PatternSlugMatchesFilenameUnitTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace HM\Tests\Files;

use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;

/**
* Class PatternSlugMatchesFilenameUnitTest
*
* @group hm-sniffs
*/
class PatternSlugMatchesFilenameUnitTest extends AbstractSniffUnitTest {
/**
* Get files to test against.
*
* Overridden from base to recurse through the fixture directory, since
* the sniff only applies to files within a theme's patterns directory.
*/
protected function getTestFiles( $test_base_dir ) {
$test_base_dir = rtrim( $test_base_dir, '.' );
$test_files = [];

$di = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator( $test_base_dir )
);

foreach ( $di as $file ) {
if ( ! $file->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 <int line number> => <int number of errors>
*/
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 <int line number> => <int number of warnings>
*/
public function getWarningList() {
return [];
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php
/**
* Title: Not a pattern
* Slug: my-theme/some-other-slug
*
* Files outside a patterns directory are ignored even if they have a Slug header.
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php
/**
* Title: Hero Banner
* Slug: my-theme/hero-banner
* Categories: banner
*/
?>
<!-- wp:paragraph --><p>Hero banner content.</p><!-- /wp:paragraph -->
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php
/**
* A pattern-directory file without a Slug header is not flagged.
*/
?>
<!-- wp:paragraph --><p>No slug here.</p><!-- /wp:paragraph -->
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php
/**
* Title: Hero Banner
* Slug: my-theme/hero-banner
* Categories: banner
*/
?>
<!-- wp:paragraph --><p>The slug above does not match this filename.</p><!-- /wp:paragraph -->