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
41 changes: 29 additions & 12 deletions WordPress/Sniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -1210,10 +1210,11 @@ protected function has_whitelist_comment( $comment, $stackPtr ) {
* Check if a token is used within a unit test.
*
* Unit test methods are identified as such:
* - Method name starts with `test_`.
* - Method is within a unit test class.
* - Method is within a known unit test class;
* - or Method is within a class/trait which extends a known unit test class.
*
* @since 0.11.0
* @since 1.1.0 Supports anonymous test classes and improved handling of nested scopes.
*
* @param int $stackPtr The position of the token to be examined.
*
Expand All @@ -1223,22 +1224,38 @@ protected function is_token_in_test_method( $stackPtr ) {
// Is the token inside of a function definition ?
$functionToken = $this->phpcsFile->getCondition( $stackPtr, \T_FUNCTION );
if ( false === $functionToken ) {
// No conditions or no function condition.
return false;
}

// Is this a method inside of a class or a trait ?
$classToken = $this->phpcsFile->getCondition( $functionToken, \T_CLASS );
$traitToken = $this->phpcsFile->getCondition( $functionToken, \T_TRAIT );
if ( false === $classToken && false === $traitToken ) {
return false;
}
/*
* Is this a method inside of a class or a trait ? If so, it is a test class/trait ?
*
* {@internal Once the minimum supported PHPCS version has gone up to 3.1.0, the
* local array here can be replace with Tokens::$ooScopeTokens.}}
*/
$oo_tokens = array(
\T_CLASS => true,
\T_TRAIT => true,
\T_ANON_CLASS => true,
);
$conditions = $this->tokens[ $stackPtr ]['conditions'];

$structureToken = $classToken;
if ( false !== $traitToken ) {
$structureToken = $traitToken;
foreach ( $conditions as $token => $condition ) {
if ( $token === $functionToken ) {
// Only examine the conditions the function is nested in, not those nested within the function.
break;
}

if ( isset( $oo_tokens[ $condition ] ) ) {
$is_test_class = $this->is_test_class( $token );
if ( true === $is_test_class ) {
return true;
}
}
}

return $this->is_test_class( $structureToken );
return false;
}

/**
Expand Down
26 changes: 18 additions & 8 deletions WordPress/Sniffs/NamingConventions/PrefixAllGlobalsSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -166,13 +166,14 @@ class PrefixAllGlobalsSniff extends AbstractFunctionParameterSniff {
*/
public function register() {
$targets = array(
\T_FUNCTION => \T_FUNCTION,
\T_CLASS => \T_CLASS,
\T_INTERFACE => \T_INTERFACE,
\T_TRAIT => \T_TRAIT,
\T_CONST => \T_CONST,
\T_VARIABLE => \T_VARIABLE,
\T_DOLLAR => \T_DOLLAR, // Variable variables.
\T_FUNCTION => \T_FUNCTION,
\T_CLASS => \T_CLASS,
\T_INTERFACE => \T_INTERFACE,
\T_TRAIT => \T_TRAIT,
\T_CONST => \T_CONST,
\T_VARIABLE => \T_VARIABLE,
\T_DOLLAR => \T_DOLLAR, // Variable variables.
\T_ANON_CLASS => \T_ANON_CLASS, // Only used for skipping over test classes.
);

// Add function call target for hook names and constants defined using define().
Expand Down Expand Up @@ -241,14 +242,23 @@ public function process_token( $stackPtr ) {
}

// Ignore test classes.
if ( \T_CLASS === $this->tokens[ $stackPtr ]['code'] && true === $this->is_test_class( $stackPtr ) ) {
if ( ( \T_CLASS === $this->tokens[ $stackPtr ]['code']
|| \T_TRAIT === $this->tokens[ $stackPtr ]['code']
|| \T_ANON_CLASS === $this->tokens[ $stackPtr ]['code'] )
&& true === $this->is_test_class( $stackPtr )
) {
if ( $this->tokens[ $stackPtr ]['scope_condition'] === $stackPtr && isset( $this->tokens[ $stackPtr ]['scope_closer'] ) ) {
// Skip forward to end of test class.
return $this->tokens[ $stackPtr ]['scope_closer'];
}
return;
}

if ( \T_ANON_CLASS === $this->tokens[ $stackPtr ]['code'] ) {
// Token was only registered to allow skipping over test classes.
return;
}

if ( \T_STRING === $this->tokens[ $stackPtr ]['code'] ) {
// Disallow excluding function groups for this sniff.
$this->exclude = array();
Expand Down
15 changes: 15 additions & 0 deletions WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.1.inc
Original file line number Diff line number Diff line change
Expand Up @@ -383,4 +383,19 @@ const SCRIPT_DEBUG = $develop_src;
do_action( 'test-this-hookname' ); // OK.
apply_filters( 'myplugin\filtername', $var ); // OK.

// Non-prefixed constant and action within a (nested) anonymous test class is fine.
class Some_Test_Class extends NonTestClass { // Bad.
public function some_test_method() {
define( 'SOME_GLOBAL', '4.0.0' ); // Bad.

return new class extends \PHPUnit_Framework_TestCase {
public function testPass() {
define( 'SOME_GLOBAL', '4.0.0' ); // OK.

do_action( 'some-action', $something ); // OK.
}
};
}
}

// @codingStandardsChangeSetting WordPress.NamingConventions.PrefixAllGlobals prefixes false
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,13 @@ class Some_Test extends \PHPUnit_Framework_TestCase {
}
}

$acronym_test = new class extends \PHPUnit_Framework_TestCase {

public function testPass() {
define( 'SOME_GLOBAL', '4.0.0' );

do_action( 'some-action', $something );
}
};

// @codingStandardsChangeSetting WordPress.NamingConventions.PrefixAllGlobals prefixes false
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ public function getErrorList( $testFile = 'PrefixAllGlobalsUnitTest.1.inc' ) {
349 => 1,
352 => 1,
357 => 1,
387 => 1,
389 => 1,
);

case 'PrefixAllGlobalsUnitTest.2.inc':
Expand Down