Skip to content

Commit

Permalink
Merge pull request #16 from bigbite/feature/traits-interfaces
Browse files Browse the repository at this point in the history
Add Sniffing support for interfaces and traits
  • Loading branch information
jaymcp authored Apr 14, 2022
2 parents 25365af + 45fb487 commit c18b0f4
Show file tree
Hide file tree
Showing 15 changed files with 172 additions and 36 deletions.
8 changes: 5 additions & 3 deletions BigBite/Docs/Files/FileNameStandard.xml
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
<documentation title="File Names">
<standard>
<![CDATA[
Files containing classes should be prefixed with "class-" or "abstract-class", and be named
as the class name in lower-kebab-case. For example:
the file name for abstract class My_Class_Name {} would be "abstract-class-my-class-name.php".
Files containing classes, interfaces, or traits should be prefixed appropriately - prefixed with the type, and then lower-kebab-case matching the name of the type. Examples:
- trait My_Trait {} => trait-my-trait.php
- interface My_Interface {} => interface-my-interface.php
- class My_Class {} => class-my-class.php
- abstract class My_Abstract_Class {} => abstract-class-my-abstract-class.php
]]>
</standard>
</documentation>
128 changes: 99 additions & 29 deletions BigBite/Sniffs/Files/FileNameSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,37 +59,14 @@ public function process_token( $stackPtr ) {

unset( $expected );

/*
* Check files containing a class for the "class-" prefix and that the rest of
* the file name reflects the class name. Accounts for abstract classes.
*/
if ( true === $this->strict_class_file_names ) {
$has_class = $this->phpcsFile->findNext( \T_CLASS, $stackPtr );

if ( false !== $has_class && false === $this->is_test_class( $has_class ) ) {
$is_abstract = $this->phpcsFile->findPrevious( \T_ABSTRACT, $has_class );
$class_name = $this->phpcsFile->getDeclarationName( $has_class );

if ( is_null( $class_name ) ) {
$class_name = 'anonymous';
}

$expected = 'class-' . $this->kebab( $class_name );
$err_message = 'Class file names should be based on the class name with "class-" prepended. Expected %s, but found %s.';

if ( $is_abstract ) {
$expected = 'abstract-' . $expected;
$err_message = 'Abstract class file names should be based on the class name with "abstract-class-" prepended. Expected %s, but found %s.';
}

if ( substr( $fileName, 0, -4 ) !== $expected ) {
$this->phpcsFile->addError( $err_message, 0, 'InvalidClassFileName', [ $expected . '.php', $fileName ] );
}

unset( $expected );
}
if ( true !== $this->strict_class_file_names ) {
return ( $this->phpcsFile->numTokens + 1 );
}

$this->check_maybe_class( $stackPtr, $fileName );
$this->check_maybe_trait( $stackPtr, $fileName );
$this->check_maybe_interface( $stackPtr, $fileName );

// Only run this sniff once per file, no need to run it again.
return ( $this->phpcsFile->numTokens + 1 );
}
Expand Down Expand Up @@ -129,6 +106,99 @@ protected function is_disabled_by_comments() {
return false;
}

/**
* Check files containing a class for the "class-" prefix,
* and that the rest of the file name reflects the class name.
* Accounts for abstract classes.
*
* @param mixed $stackPtr the token stack
* @param string $fileName the name of the file
*
* @return void
*/
protected function check_maybe_class( $stackPtr, $fileName ) {
$has_class = $this->phpcsFile->findNext( \T_CLASS, $stackPtr );

if ( false === $has_class || false !== $this->is_test_class( $has_class ) ) {
return;
}

$is_abstract = $this->phpcsFile->findPrevious( \T_ABSTRACT, $has_class );
$class_name = $this->phpcsFile->getDeclarationName( $has_class );

if ( is_null( $class_name ) ) {
$class_name = 'anonymous';
}

$expected = 'class-' . $this->kebab( $class_name );
$err_message = 'Class file names should be based on the class name with "class-" prepended. Expected %s, but found %s.';

if ( $is_abstract ) {
$expected = 'abstract-' . $expected;
$err_message = 'Abstract class file names should be based on the class name with "abstract-class-" prepended. Expected %s, but found %s.';
}

if ( substr( $fileName, 0, -4 ) === $expected ) {
return;
}

$this->phpcsFile->addError( $err_message, 0, 'InvalidClassFileName', [ $expected . '.php', $fileName ] );
}

/**
* Check files containing a trait for the "trait-" prefix,
* and that the rest of the file name reflects the trait name.
*
* @param mixed $stackPtr the token stack
* @param string $fileName the name of the file
*
* @return void
*/
protected function check_maybe_trait( $stackPtr, $fileName ) {
$has_trait = $this->phpcsFile->findNext( \T_TRAIT, $stackPtr );

if ( false === $has_trait || false !== $this->is_test_class( $has_trait ) ) {
return;
}

$trait_name = $this->phpcsFile->getDeclarationName( $has_trait );
$expected = 'trait-' . $this->kebab( $trait_name );
$err_message = 'Trait file names should be based on the class name with "trait-" prepended. Expected %s, but found %s.';

if ( substr( $fileName, 0, -4 ) === $expected ) {
return;
}

$this->phpcsFile->addError( $err_message, 0, 'InvalidTraitFileName', [ $expected . '.php', $fileName ] );
}

/**
* Check files containing an interface for the "interface-" prefix,
* and that the rest of the file name reflects the interface name.
*
* @param mixed $stackPtr the token stack
* @param string $fileName the name of the file
*
* @return void
*/
protected function check_maybe_interface( $stackPtr, $fileName ) {
$has_interface = $this->phpcsFile->findNext( \T_INTERFACE, $stackPtr );

if ( false === $has_interface || false !== $this->is_test_class( $has_interface ) ) {
return;
}

$interface_name = $this->phpcsFile->getDeclarationName( $has_interface );
$expected = 'interface-' . $this->kebab( $interface_name );
$err_message = 'Interface file names should be based on the interface name with "interface-" prepended. Expected %s, but found %s.';

if ( substr( $fileName, 0, -4 ) === $expected ) {
return;
}

$this->phpcsFile->addError( $err_message, 0, 'InvalidInterfaceFileName', [ $expected . '.php', $fileName ] );
}

/**
* Convert a string to kebab-case
*
Expand Down
16 changes: 12 additions & 4 deletions BigBite/Tests/Files/FileNameUnitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,18 @@ class FileNameUnitTest extends AbstractSniffUnitTest {
'SomeView.inc' => 1,

// Class file names.
'my-class.inc' => 1,
'my-abstract-class.inc' => 1,
'class-different-class.inc' => 1,
'my-class.inc' => 1,
'my-interface.inc' => 1,
'my-trait.inc' => 1,
'abstract-class-different-class.inc' => 1,
'ClassMyClass.inc' => 2,
'class-different-class.inc' => 1,
'interface-different-interface.inc' => 1,
'trait-different-trait.inc' => 1,
'AbstractClassMyClass.inc' => 2,
'ClassMyClass.inc' => 2,
'InterfaceMyInterface.inc' => 2,
'TraitMyTrait.inc' => 2,

// Theme specific exceptions in a non-theme context.
'single-my_post_type.inc' => 1,
Expand All @@ -52,8 +58,10 @@ class FileNameUnitTest extends AbstractSniffUnitTest {
*/

// Non-strict class names still have to comply with lowercase hyphenated.
'ClassNonStrictClass.inc' => 1,
'AbstractClassNonStrictClass.inc' => 1,
'ClassNonStrictClass.inc' => 1,
'InterfaceNonStrictClass.inc' => 1,
'TraitNonStrictClass.inc' => 1,

/*
* In /FileNameUnitTests/PHPCSAnnotations.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?php

interface My_Interface {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!-- Annotation has to be on the second line as errors are thrown on line 1 and errors on annotation lines are ignored. -->
phpcs:set BigBite.Files.FileName strict_class_file_names false

<?php

interface Non_Strict_Interface {}

// phpcs:set BigBite.Files.FileName strict_class_file_names true
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!-- Annotation has to be on the second line as errors are thrown on line 1 and errors on annotation lines are ignored. -->
phpcs:set BigBite.Files.FileName strict_class_file_names false

<?php

trait Non_Strict_Trait {}

// phpcs:set BigBite.Files.FileName strict_class_file_names true
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!-- Annotation has to be on the second line as errors are thrown on line 1 and errors on annotation lines are ignored. -->
phpcs:set BigBite.Files.FileName strict_class_file_names false

<?php

interface Non_Strict_Interface {}

// phpcs:set BigBite.Files.FileName strict_class_file_names true
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!-- Annotation has to be on the second line as errors are thrown on line 1 and errors on annotation lines are ignored. -->
phpcs:set BigBite.Files.FileName strict_class_file_names false

<?php

trait Non_Strict_Trait {}

// phpcs:set BigBite.Files.FileName strict_class_file_names true
3 changes: 3 additions & 0 deletions BigBite/Tests/Files/FileNameUnitTests/TraitMyTrait.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?php

trait My_Trait {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?php

interface Not_My_Interface {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?php

interface My_Interface {}
3 changes: 3 additions & 0 deletions BigBite/Tests/Files/FileNameUnitTests/my-interface.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?php

interface My_Interface {}
3 changes: 3 additions & 0 deletions BigBite/Tests/Files/FileNameUnitTests/my-trait.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?php

trait My_Trait {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?php

trait Not_My_Trait {}
3 changes: 3 additions & 0 deletions BigBite/Tests/Files/FileNameUnitTests/trait-my-trait.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?php

trait My_Trait {}

0 comments on commit c18b0f4

Please sign in to comment.