Skip to content

Commit

Permalink
Make InnerFunctionsSniff detect functions inside closures and ignore …
Browse files Browse the repository at this point in the history
…all OO methods (#3807)
  • Loading branch information
Daimona authored and jrfnl committed Jul 11, 2023
1 parent 5859ed6 commit cfb95cb
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 10 deletions.
28 changes: 18 additions & 10 deletions src/Standards/Squiz/Sniffs/PHP/InnerFunctionsSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

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

class InnerFunctionsSniff implements Sniff
{
Expand Down Expand Up @@ -41,21 +42,28 @@ public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();

$function = $phpcsFile->getCondition($stackPtr, T_FUNCTION);
if ($function === false) {
// Not a nested function.
if (isset($tokens[$stackPtr]['conditions']) === false) {
return;
}

$class = $phpcsFile->getCondition($stackPtr, T_ANON_CLASS, false);
if ($class !== false && $class > $function) {
// Ignore methods in anon classes.
return;
$conditions = $tokens[$stackPtr]['conditions'];
$reversedConditions = array_reverse($conditions, true);

$outerFuncToken = null;
foreach ($reversedConditions as $condToken => $condition) {
if ($condition === T_FUNCTION || $condition === T_CLOSURE) {
$outerFuncToken = $condToken;
break;
}

if (\array_key_exists($condition, Tokens::$ooScopeTokens) === true) {
// Ignore methods in OOP structures defined within functions.
return;
}
}

$prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
if ($tokens[$prev]['code'] === T_EQUAL) {
// Ignore closures.
if ($outerFuncToken === null) {
// Not a nested function.
return;
}

Expand Down
37 changes: 37 additions & 0 deletions src/Standards/Squiz/Tests/PHP/InnerFunctionsUnitTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,40 @@ new class {
}
}
};

$outerClosure = function ()
{
// Functions inside closures are not allowed.
function innerFunction() {
}
};

// Allow methods in classes/traits/interfaces defined inside functions
function foo() {
if (class_exists('MyClass') === false) {
class MyClass {
function foo() {}
}
}

if (trait_exists('MyTrait') === false) {
trait MyTrait {
function foo() {}
}
}

if (interface_exists('MyInterface') === false) {
interface MyInterface {
function foo();
}
}

// But disallow functions nested inside those methods
if (class_exists('NestedFunctionInMethod') === false) {
class NestedFunctionInMethod {
function foo() {
function innerFunction() {}
}
}
}
}
2 changes: 2 additions & 0 deletions src/Standards/Squiz/Tests/PHP/InnerFunctionsUnitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public function getErrorList()
return [
5 => 1,
46 => 1,
55 => 1,
83 => 1,
];

}//end getErrorList()
Expand Down

0 comments on commit cfb95cb

Please sign in to comment.