Skip to content

Commit fd3d8ff

Browse files
committed
New helpers - currently markes as internal
1 parent 77d193c commit fd3d8ff

File tree

8 files changed

+247
-336
lines changed

8 files changed

+247
-336
lines changed

SlevomatCodingStandard/Helpers/FunctionHelper.php

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use const T_ANON_CLASS;
88
use const T_BITWISE_AND;
99
use const T_CLASS;
10-
use const T_CLOSURE;
1110
use const T_COLON;
1211
use const T_COMMA;
1312
use const T_ELLIPSIS;
@@ -165,31 +164,26 @@ public static function returnsValue(File $codeSnifferFile, int $functionPointer)
165164
{
166165
$tokens = $codeSnifferFile->getTokens();
167166

168-
$isInSameLevel = function (int $pointer) use ($functionPointer, $tokens): bool {
169-
foreach (array_reverse($tokens[$pointer]['conditions'], true) as $conditionPointer => $conditionTokenCode) {
170-
if ($conditionPointer === $functionPointer) {
171-
break;
172-
}
173-
174-
if ($conditionTokenCode !== T_CLOSURE && $conditionTokenCode !== T_ANON_CLASS) {
175-
continue;
176-
}
167+
$firstPointerInScope = $tokens[$functionPointer]['scope_opener'] + 1;
177168

178-
return false;
169+
for ($i = $firstPointerInScope; $i < $tokens[$functionPointer]['scope_closer']; $i++) {
170+
if (!in_array($tokens[$i]['code'], [T_YIELD, T_YIELD_FROM], true)) {
171+
continue;
179172
}
180-
return true;
181-
};
182173

183-
for ($i = $tokens[$functionPointer]['scope_opener'] + 1; $i < $tokens[$functionPointer]['scope_closer']; $i++) {
184-
if (!in_array($tokens[$i]['code'], [T_YIELD, T_YIELD_FROM], true) || !$isInSameLevel($i)) {
174+
if (!ScopeHelper::isInSameScope($codeSnifferFile, $i, $firstPointerInScope)) {
185175
continue;
186176
}
187177

188178
return true;
189179
}
190180

191-
for ($i = $tokens[$functionPointer]['scope_opener'] + 1; $i < $tokens[$functionPointer]['scope_closer']; $i++) {
192-
if ($tokens[$i]['code'] !== T_RETURN || !$isInSameLevel($i)) {
181+
for ($i = $firstPointerInScope; $i < $tokens[$functionPointer]['scope_closer']; $i++) {
182+
if ($tokens[$i]['code'] !== T_RETURN) {
183+
continue;
184+
}
185+
186+
if (!ScopeHelper::isInSameScope($codeSnifferFile, $i, $firstPointerInScope)) {
193187
continue;
194188
}
195189

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace SlevomatCodingStandard\Helpers;
4+
5+
use PHP_CodeSniffer\Files\File;
6+
use function array_key_exists;
7+
use function array_keys;
8+
use function array_reverse;
9+
use function in_array;
10+
11+
/**
12+
* @internal
13+
*/
14+
class ParameterHelper
15+
{
16+
17+
public static function isParameter(File $phpcsFile, int $variablePointer): bool
18+
{
19+
$tokens = $phpcsFile->getTokens();
20+
21+
if (!array_key_exists('nested_parenthesis', $tokens[$variablePointer])) {
22+
return false;
23+
}
24+
25+
$parenthesisOpenerPointer = array_reverse(array_keys($tokens[$variablePointer]['nested_parenthesis']))[0];
26+
if (!array_key_exists('parenthesis_owner', $tokens[$parenthesisOpenerPointer])) {
27+
return false;
28+
}
29+
30+
$parenthesisOwnerPointer = $tokens[$parenthesisOpenerPointer]['parenthesis_owner'];
31+
return in_array($tokens[$parenthesisOwnerPointer]['code'], TokenHelper::$functionTokenCodes, true);
32+
}
33+
34+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace SlevomatCodingStandard\Helpers;
4+
5+
use PHP_CodeSniffer\Files\File;
6+
use function array_reverse;
7+
use function in_array;
8+
9+
/**
10+
* @internal
11+
*/
12+
class ScopeHelper
13+
{
14+
15+
public static function isInSameScope(File $phpcsFile, int $firstPointer, int $secondPointer): bool
16+
{
17+
$tokens = $phpcsFile->getTokens();
18+
19+
$getScope = function (int $pointer) use ($tokens): int {
20+
$scope = 0;
21+
22+
foreach (array_reverse($tokens[$pointer]['conditions'], true) as $conditionPointer => $conditionTokenCode) {
23+
if (!in_array($conditionTokenCode, TokenHelper::$functionTokenCodes, true)) {
24+
continue;
25+
}
26+
27+
$scope = $tokens[$conditionPointer]['level'] + 1;
28+
break;
29+
}
30+
31+
return $scope;
32+
};
33+
34+
return $getScope($firstPointer) === $getScope($secondPointer);
35+
}
36+
37+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace SlevomatCodingStandard\Helpers;
4+
5+
use PHP_CodeSniffer\Files\File;
6+
use const T_DOUBLE_COLON;
7+
use const T_DOUBLE_QUOTED_STRING;
8+
use const T_HEREDOC;
9+
use const T_OPEN_PARENTHESIS;
10+
use const T_OPEN_TAG;
11+
use const T_STRING;
12+
use const T_VARIABLE;
13+
use function count;
14+
use function in_array;
15+
use function preg_match;
16+
use function preg_quote;
17+
use function strtolower;
18+
use function substr;
19+
20+
/**
21+
* @internal
22+
*/
23+
class VariableHelper
24+
{
25+
26+
public static function isUsedInScope(File $phpcsFile, int $scopeOwnerPointer, int $variablePointer): bool
27+
{
28+
$tokens = $phpcsFile->getTokens();
29+
30+
$firstPointerInScope = $tokens[$scopeOwnerPointer]['code'] === T_OPEN_TAG ? $scopeOwnerPointer + 1 : $tokens[$scopeOwnerPointer]['scope_opener'] + 1;
31+
return self::isUsedInScopeInternal($phpcsFile, $scopeOwnerPointer, $variablePointer, $firstPointerInScope);
32+
}
33+
34+
public static function isUsedInScopeAfterPointer(File $phpcsFile, int $scopeOwnerPointer, int $variablePointer, int $startCheckPointer): bool
35+
{
36+
return self::isUsedInScopeInternal($phpcsFile, $scopeOwnerPointer, $variablePointer, $startCheckPointer);
37+
}
38+
39+
private static function isUsedInScopeInternal(File $phpcsFile, int $scopeOwnerPointer, int $variablePointer, int $startCheckPointer): bool
40+
{
41+
$tokens = $phpcsFile->getTokens();
42+
43+
$scopeCloserPointer = $tokens[$scopeOwnerPointer]['code'] === T_OPEN_TAG
44+
? count($tokens) - 1
45+
: $tokens[$scopeOwnerPointer]['scope_closer'] - 1;
46+
$firstPointerInScope = $tokens[$scopeOwnerPointer]['code'] === T_OPEN_TAG
47+
? $scopeOwnerPointer + 1
48+
: $tokens[$scopeOwnerPointer]['scope_opener'] + 1;
49+
50+
for ($i = $startCheckPointer; $i <= $scopeCloserPointer; $i++) {
51+
if (!ScopeHelper::isInSameScope($phpcsFile, $i, $firstPointerInScope)) {
52+
continue;
53+
}
54+
55+
if (
56+
$tokens[$i]['code'] === T_VARIABLE
57+
&& self::isUsedAsVariable($phpcsFile, $variablePointer, $i)
58+
) {
59+
return true;
60+
}
61+
62+
if (
63+
$tokens[$i]['code'] === T_STRING
64+
&& self::isUsedInCompactFunction($phpcsFile, $variablePointer, $i)
65+
) {
66+
return true;
67+
}
68+
69+
if (
70+
in_array($tokens[$i]['code'], [T_DOUBLE_QUOTED_STRING, T_HEREDOC], true)
71+
&& self::isUsedInScopeInString($phpcsFile, $variablePointer, $i)
72+
) {
73+
return true;
74+
}
75+
}
76+
77+
return false;
78+
}
79+
80+
public static function isUsedAsVariable(File $phpcsFile, int $variablePointer, int $variableToCheckPointer): bool
81+
{
82+
$tokens = $phpcsFile->getTokens();
83+
84+
if ($tokens[$variablePointer]['content'] !== $tokens[$variableToCheckPointer]['content']) {
85+
return false;
86+
}
87+
88+
if ($tokens[$variableToCheckPointer - 1]['code'] === T_DOUBLE_COLON) {
89+
return false;
90+
}
91+
92+
if (ParameterHelper::isParameter($phpcsFile, $variableToCheckPointer)) {
93+
return false;
94+
}
95+
96+
return true;
97+
}
98+
99+
public static function isUsedInCompactFunction(File $phpcsFile, int $variablePointer, int $stringPointer): bool
100+
{
101+
$tokens = $phpcsFile->getTokens();
102+
103+
$stringContent = $tokens[$stringPointer]['content'];
104+
if (strtolower($stringContent) !== 'compact') {
105+
return false;
106+
}
107+
108+
$parenthesisOpenerPointer = TokenHelper::findNextEffective($phpcsFile, $stringPointer + 1);
109+
if ($tokens[$parenthesisOpenerPointer]['code'] !== T_OPEN_PARENTHESIS) {
110+
return false;
111+
}
112+
113+
$variableNameWithoutDollar = substr($tokens[$variablePointer]['content'], 1);
114+
for ($i = $parenthesisOpenerPointer + 1; $i < $tokens[$parenthesisOpenerPointer]['parenthesis_closer']; $i++) {
115+
if (preg_match('~^([\'"])' . $variableNameWithoutDollar . '\\1$~', $tokens[$i]['content'])) {
116+
return true;
117+
}
118+
}
119+
120+
return false;
121+
}
122+
123+
private static function isUsedInScopeInString(File $phpcsFile, int $variablePointer, int $stringPointer): bool
124+
{
125+
$tokens = $phpcsFile->getTokens();
126+
127+
$stringContent = $tokens[$stringPointer]['content'];
128+
129+
$variableName = $tokens[$variablePointer]['content'];
130+
if (preg_match('~(?<!\\\\)' . preg_quote($variableName, '~') . '\b(?!\()~', $stringContent)) {
131+
return true;
132+
}
133+
134+
$variableNameWithoutDollar = substr($variableName, 1);
135+
if (preg_match('~\$\{' . preg_quote($variableNameWithoutDollar, '~') . '\}~', $stringContent)) {
136+
return true;
137+
}
138+
139+
return false;
140+
}
141+
142+
}

SlevomatCodingStandard/Sniffs/Functions/UnusedInheritedVariablePassedToClosureSniff.php

Lines changed: 8 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@
55
use PHP_CodeSniffer\Files\File;
66
use PHP_CodeSniffer\Sniffs\Sniff;
77
use SlevomatCodingStandard\Helpers\TokenHelper;
8+
use SlevomatCodingStandard\Helpers\VariableHelper;
89
use const T_CLOSE_PARENTHESIS;
910
use const T_CLOSURE;
1011
use const T_COMMA;
1112
use const T_OPEN_PARENTHESIS;
1213
use const T_USE;
1314
use const T_VARIABLE;
14-
use function array_keys;
15-
use function array_reverse;
1615
use function sprintf;
1716

1817
class UnusedInheritedVariablePassedToClosureSniff implements Sniff
@@ -45,6 +44,7 @@ public function process(File $phpcsFile, $usePointer): void
4544
return;
4645
}
4746

47+
/** @var int $closurePointer */
4848
$closurePointer = TokenHelper::findPrevious($phpcsFile, T_CLOSURE, $usePointer - 1);
4949

5050
$currentPointer = $parenthesisOpenerPointer + 1;
@@ -60,8 +60,7 @@ public function process(File $phpcsFile, $usePointer): void
6060
$parenthesisOpenerPointer,
6161
$tokens[$parenthesisOpenerPointer]['parenthesis_closer'],
6262
$variablePointer,
63-
$tokens[$closurePointer]['scope_opener'],
64-
$tokens[$closurePointer]['scope_closer']
63+
$closurePointer
6564
);
6665

6766
$currentPointer = $variablePointer + 1;
@@ -74,56 +73,17 @@ private function checkVariableUsage(
7473
int $useParenthesisOpenerPointer,
7574
int $useParenthesisCloserPointer,
7675
int $variablePointer,
77-
int $scopeOpener,
78-
int $scopeCloser
76+
int $scopeOwnerPointer
7977
): void
8078
{
8179
$tokens = $phpcsFile->getTokens();
8280

83-
$variableName = $tokens[$variablePointer]['content'];
84-
85-
$isInSameLevel = function (int $pointer) use ($variablePointer, $tokens): bool {
86-
foreach (array_reverse($tokens[$pointer]['conditions'], true) as $conditionPointer => $conditionTokenCode) {
87-
if ($tokens[$conditionPointer]['level'] <= $tokens[$variablePointer]['level']) {
88-
break;
89-
}
90-
91-
if ($conditionTokenCode === T_CLOSURE) {
92-
return false;
93-
}
94-
}
95-
96-
return true;
97-
};
98-
99-
$currentPointer = $scopeOpener + 1;
100-
do {
101-
$usedVariablePointer = TokenHelper::findNextContent($phpcsFile, T_VARIABLE, $variableName, $currentPointer, $scopeCloser - 1);
102-
if ($usedVariablePointer === null) {
103-
break;
104-
}
105-
106-
if (isset($tokens[$usedVariablePointer]['nested_parenthesis'])) {
107-
$parenthesisOpenerPointer = array_reverse(array_keys($tokens[$usedVariablePointer]['nested_parenthesis']))[0];
108-
if (isset($tokens[$parenthesisOpenerPointer]['parenthesis_owner'])) {
109-
$parenthesisOwnerPointer = $tokens[$parenthesisOpenerPointer]['parenthesis_owner'];
110-
if ($tokens[$parenthesisOwnerPointer]['code'] === T_CLOSURE) {
111-
// Parameter
112-
$currentPointer = $usedVariablePointer + 1;
113-
continue;
114-
}
115-
}
116-
}
117-
118-
if ($isInSameLevel($usedVariablePointer)) {
119-
return;
120-
}
121-
122-
$currentPointer = $usedVariablePointer + 1;
123-
} while (true);
81+
if (VariableHelper::isUsedInScope($phpcsFile, $scopeOwnerPointer, $variablePointer)) {
82+
return;
83+
}
12484

12585
$fix = $phpcsFile->addFixableError(
126-
sprintf('Unused inherited variable %s passed to closure.', $variableName),
86+
sprintf('Unused inherited variable %s passed to closure.', $tokens[$variablePointer]['content']),
12787
$variablePointer,
12888
self::CODE_UNUSED_INHERITED_VARIABLE
12989
);

0 commit comments

Comments
 (0)