Skip to content

Commit 1701786

Browse files
authored
Merge pull request #3807 from Daimona/master
Make InnerFunctionsSniff detect functions inside closures
2 parents d415500 + 47b9cbb commit 1701786

File tree

3 files changed

+57
-10
lines changed

3 files changed

+57
-10
lines changed

src/Standards/Squiz/Sniffs/PHP/InnerFunctionsSniff.php

+18-10
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
use PHP_CodeSniffer\Files\File;
1313
use PHP_CodeSniffer\Sniffs\Sniff;
14+
use PHP_CodeSniffer\Util\Tokens;
1415

1516
class InnerFunctionsSniff implements Sniff
1617
{
@@ -41,21 +42,28 @@ public function process(File $phpcsFile, $stackPtr)
4142
{
4243
$tokens = $phpcsFile->getTokens();
4344

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

50-
$class = $phpcsFile->getCondition($stackPtr, T_ANON_CLASS, false);
51-
if ($class !== false && $class > $function) {
52-
// Ignore methods in anon classes.
53-
return;
49+
$conditions = $tokens[$stackPtr]['conditions'];
50+
$reversedConditions = array_reverse($conditions, true);
51+
52+
$outerFuncToken = null;
53+
foreach ($reversedConditions as $condToken => $condition) {
54+
if ($condition === T_FUNCTION || $condition === T_CLOSURE) {
55+
$outerFuncToken = $condToken;
56+
break;
57+
}
58+
59+
if (\array_key_exists($condition, Tokens::$ooScopeTokens) === true) {
60+
// Ignore methods in OOP structures defined within functions.
61+
return;
62+
}
5463
}
5564

56-
$prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
57-
if ($tokens[$prev]['code'] === T_EQUAL) {
58-
// Ignore closures.
65+
if ($outerFuncToken === null) {
66+
// Not a nested function.
5967
return;
6068
}
6169

src/Standards/Squiz/Tests/PHP/InnerFunctionsUnitTest.inc

+37
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,40 @@ new class {
4848
}
4949
}
5050
};
51+
52+
$outerClosure = function ()
53+
{
54+
// Functions inside closures are not allowed.
55+
function innerFunction() {
56+
}
57+
};
58+
59+
// Allow methods in classes/traits/interfaces defined inside functions
60+
function foo() {
61+
if (class_exists('MyClass') === false) {
62+
class MyClass {
63+
function foo() {}
64+
}
65+
}
66+
67+
if (trait_exists('MyTrait') === false) {
68+
trait MyTrait {
69+
function foo() {}
70+
}
71+
}
72+
73+
if (interface_exists('MyInterface') === false) {
74+
interface MyInterface {
75+
function foo();
76+
}
77+
}
78+
79+
// But disallow functions nested inside those methods
80+
if (class_exists('NestedFunctionInMethod') === false) {
81+
class NestedFunctionInMethod {
82+
function foo() {
83+
function innerFunction() {}
84+
}
85+
}
86+
}
87+
}

src/Standards/Squiz/Tests/PHP/InnerFunctionsUnitTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ public function getErrorList()
2828
return [
2929
5 => 1,
3030
46 => 1,
31+
55 => 1,
32+
83 => 1,
3133
];
3234

3335
}//end getErrorList()

0 commit comments

Comments
 (0)