Skip to content

Commit 0411d16

Browse files
committed
ArrayHelper refactored
1 parent bb1f6cb commit 0411d16

File tree

3 files changed

+89
-118
lines changed

3 files changed

+89
-118
lines changed

SlevomatCodingStandard/Helpers/ArrayHelper.php

Lines changed: 74 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -3,108 +3,89 @@
33
namespace SlevomatCodingStandard\Helpers;
44

55
use PHP_CodeSniffer\Files\File;
6+
use function array_key_exists;
67
use function arsort;
7-
use function end;
88
use function in_array;
99
use function key;
10-
use function min;
11-
use function strlen;
1210
use function strnatcasecmp;
13-
use function strpos;
1411
use const T_COMMA;
1512
use const T_OPEN_SHORT_ARRAY;
16-
use const T_WHITESPACE;
1713

1814
/**
1915
* @internal
2016
*/
2117
class ArrayHelper
2218
{
2319

24-
/** @var array<int, array<string, array<int, int|string>|int|string>> */
25-
protected static $tokens;
26-
2720
/**
2821
* @return list<ArrayKeyValue>
2922
*/
30-
public static function parse(File $phpcsFile, int $pointer): array
23+
public static function parse(File $phpcsFile, int $arrayPointer): array
3124
{
32-
self::$tokens = $phpcsFile->getTokens();
33-
$token = self::$tokens[$pointer];
34-
[$pointerOpener, $pointerCloser] = self::openClosePointers($token);
35-
$tokenOpener = self::$tokens[$pointerOpener];
36-
$lineIndents = [''];
25+
$tokens = $phpcsFile->getTokens();
26+
27+
$arrayToken = $tokens[$arrayPointer];
28+
[$arrayOpenerPointer, $arrayCloserPointer] = self::openClosePointers($arrayToken);
29+
3730
$keyValues = [];
38-
$skipUntilPointer = null;
39-
$tokenEnd = null;
40-
$pointer = $pointerOpener + 1;
41-
for (; $pointer < $pointerCloser; $pointer++) {
42-
$token = self::$tokens[$pointer];
43-
if ($token['line'] > $tokenOpener['line'] || in_array($token['code'], TokenHelper::$ineffectiveTokenCodes, true) === false) {
44-
break;
45-
}
46-
}
47-
$pointerStart = $pointer;
4831

49-
for (; $pointer < $pointerCloser; $pointer++) {
50-
if ($pointer < $skipUntilPointer) {
51-
continue;
52-
}
32+
$firstPointerOnNextLine = TokenHelper::findFirstTokenOnNextLine($phpcsFile, $arrayOpenerPointer + 1);
33+
$firstEffectivePointer = TokenHelper::findNextEffective($phpcsFile, $arrayOpenerPointer + 1);
5334

54-
$token = self::$tokens[$pointer];
35+
$arrayKeyValueStartPointer = $firstPointerOnNextLine !== null && $firstPointerOnNextLine < $firstEffectivePointer
36+
? $firstPointerOnNextLine
37+
: $firstEffectivePointer;
38+
$arrayKeyValueEndPointer = $arrayKeyValueStartPointer;
39+
40+
$indentation = $tokens[$arrayOpenerPointer]['line'] < $tokens[$firstEffectivePointer]['line']
41+
? IndentationHelper::getIndentation($phpcsFile, $firstEffectivePointer)
42+
: '';
43+
44+
for ($i = $arrayKeyValueStartPointer; $i < $arrayCloserPointer; $i++) {
45+
$token = $tokens[$i];
5546

5647
if (in_array($token['code'], TokenHelper::$arrayTokenCodes, true)) {
57-
$pointerCloserWalk = self::openClosePointers($token)[1];
58-
$skipUntilPointer = $pointerCloserWalk;
48+
$i = self::openClosePointers($token)[1];
5949
continue;
6050
}
61-
if (isset($token['scope_closer']) && $token['scope_closer'] > $pointer) {
62-
$skipUntilPointer = $token['scope_closer'];
51+
52+
if (array_key_exists('scope_closer', $token) && $token['scope_closer'] > $i) {
53+
$i = $token['scope_closer'] - 1;
6354
continue;
6455
}
65-
if (isset($token['parenthesis_closer'])) {
66-
$skipUntilPointer = $token['parenthesis_closer'];
56+
57+
if (array_key_exists('parenthesis_closer', $token) && $token['parenthesis_closer'] > $i) {
58+
$i = $token['parenthesis_closer'] - 1;
6759
continue;
6860
}
69-
$nextEffective = in_array($token['code'], TokenHelper::$ineffectiveTokenCodes, true)
70-
? TokenHelper::findNextEffective($phpcsFile, $pointer)
71-
: TokenHelper::findNextEffective($phpcsFile, $pointer + 1);
72-
if (
73-
isset($lineIndents[$token['line']]) === false
74-
&& in_array($token['code'], TokenHelper::$ineffectiveTokenCodes, true) === false
75-
) {
76-
$firstPointerOnLine = TokenHelper::findFirstTokenOnLine($phpcsFile, $pointer);
77-
$firstEffective = TokenHelper::findNextEffective($phpcsFile, $firstPointerOnLine);
78-
$lineIndents[$token['line']] = TokenHelper::getContent($phpcsFile, $firstPointerOnLine, $firstEffective - 1);
79-
}
8061

81-
$startNewKeyValue = $tokenEnd !== null
82-
? self::parseTestStartKeyVal($pointer, $tokenEnd, end($lineIndents))
83-
: false;
84-
if ($startNewKeyValue) {
85-
if ($nextEffective === $pointerCloser && in_array($token['code'], TokenHelper::$ineffectiveTokenCodes, true)) {
86-
// there are no more key/values
87-
$firstPointerOnLine = TokenHelper::findFirstTokenOnLine($phpcsFile, $pointer);
88-
if ($pointer === $firstPointerOnLine) {
89-
// end last key/value on the prev token
90-
$pointer--;
91-
}
92-
break;
93-
}
94-
$startNewKeyValue = false;
95-
$tokenEnd = null;
96-
$keyValues[] = new ArrayKeyValue($phpcsFile, $pointerStart, $pointer - 1);
97-
$pointerStart = $pointer;
62+
$nextEffectivePointer = TokenHelper::findNextEffective($phpcsFile, $i + 1);
63+
64+
if ($nextEffectivePointer === $arrayCloserPointer) {
65+
$arrayKeyValueEndPointer = $i;
66+
break;
9867
}
99-
if ($token['code'] === T_COMMA || $tokenEnd !== null) {
100-
$tokenEnd = $token;
68+
69+
if ($token['code'] !== T_COMMA || !ScopeHelper::isInSameScope($phpcsFile, $arrayOpenerPointer, $i)) {
70+
continue;
10171
}
72+
73+
$arrayKeyValueEndPointer = $tokens[$nextEffectivePointer]['line'] === $tokens[$i]['line']
74+
? $nextEffectivePointer - 1
75+
: self::getValueEndPointer($phpcsFile, $i, $indentation);
76+
77+
$keyValues[] = new ArrayKeyValue($phpcsFile, $arrayKeyValueStartPointer, $arrayKeyValueEndPointer);
78+
79+
$arrayKeyValueStartPointer = $arrayKeyValueEndPointer + 1;
80+
$i = $arrayKeyValueEndPointer;
10281
}
10382

104-
$pointer = min($pointer, $pointerCloser - 1);
105-
$keyValues[] = new ArrayKeyValue($phpcsFile, $pointerStart, $pointer);
83+
$keyValues[] = new ArrayKeyValue(
84+
$phpcsFile,
85+
$arrayKeyValueStartPointer,
86+
self::getValueEndPointer($phpcsFile, $arrayKeyValueEndPointer, $indentation)
87+
);
10688

107-
self::$tokens = [];
10889
return $keyValues;
10990
}
11091

@@ -219,45 +200,34 @@ public static function openClosePointers(array $token): array
219200
return [(int) $pointerOpener, (int) $pointerCloser];
220201
}
221202

222-
/**
223-
* Test whether we should begin collecting the next key/value tokens
224-
*
225-
* @param array<string, array<int, int|string>|int|string> $tokenPrev
226-
*/
227-
private static function parseTestStartKeyVal(int $pointer, array $tokenPrev, string $lastIndent): bool
203+
private static function getValueEndPointer(File $phpcsFile, int $endPointer, string $indentation): int
228204
{
229-
$token = self::$tokens[$pointer];
230-
231-
// token['column'] cannot be relied on if tabs are being used
232-
// this simply checks if indent contains tab and is the same as the prev indent plus additional
233-
$testTabIndent = static function ($indent, $indentPrev) {
234-
return strpos($indent, "\t") !== false
235-
&& strpos($indent, $indentPrev) === 0
236-
&& strlen($indent) > strlen($indentPrev);
237-
};
238-
239-
$startNew = true;
240-
241-
if (
242-
in_array($token['code'], TokenHelper::$ineffectiveTokenCodes, true)
243-
&& $token['line'] === $tokenPrev['line']
244-
) {
245-
// we're whitespace or comment after the value...
246-
$startNew = false;
247-
} elseif (
248-
$token['code'] === T_WHITESPACE
249-
&& in_array($tokenPrev['code'], TokenHelper::$inlineCommentTokenCodes, true)
250-
&& in_array(self::$tokens[$pointer + 1]['code'], TokenHelper::$inlineCommentTokenCodes, true)
251-
&& (self::$tokens[$pointer + 1]['column'] >= $tokenPrev['column']
252-
|| $testTabIndent($token['content'], $lastIndent)
253-
)
254-
) {
255-
// 'key' => 'value' // tokenPrev is this comment
256-
// // we're in the preceding whitespace
257-
$startNew = false;
205+
$tokens = $phpcsFile->getTokens();
206+
207+
$nextEffectivePointer = TokenHelper::findNextEffective($phpcsFile, $endPointer + 1);
208+
209+
for ($i = $endPointer + 1; $i < $nextEffectivePointer; $i++) {
210+
if ($tokens[$i]['line'] === $tokens[$endPointer]['line']) {
211+
$endPointer = $i;
212+
continue;
213+
}
214+
215+
$nextNonWhitespacePointer = TokenHelper::findNextNonWhitespace($phpcsFile, $i);
216+
217+
if (!in_array($tokens[$nextNonWhitespacePointer]['code'], TokenHelper::$inlineCommentTokenCodes, true)) {
218+
break;
219+
}
220+
221+
if ($indentation === IndentationHelper::getIndentation($phpcsFile, $nextNonWhitespacePointer)) {
222+
$endPointer = $i - 1;
223+
break;
224+
}
225+
226+
$i = TokenHelper::findLastTokenOnLine($phpcsFile, $i);
227+
$endPointer = $i;
258228
}
259229

260-
return $startNew;
230+
return $endPointer;
261231
}
262232

263233
}

SlevomatCodingStandard/Helpers/ArrayKeyValue.php

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -116,23 +116,24 @@ private function addValues(File $phpcsFile): void
116116
$key = '';
117117
$tokens = $phpcsFile->getTokens();
118118
$firstNonWhitespace = null;
119-
$pointerCloser = null;
120-
for ($pointer = $this->pointerStart; $pointer <= $this->pointerEnd; $pointer++) {
121-
if ($pointer < $pointerCloser) {
119+
120+
for ($i = $this->pointerStart; $i <= $this->pointerEnd; $i++) {
121+
$token = $tokens[$i];
122+
123+
if (in_array($token['code'], TokenHelper::$arrayTokenCodes, true)) {
124+
$i = ArrayHelper::openClosePointers($token)[1];
122125
continue;
123126
}
124-
$token = $tokens[$pointer];
125-
if (in_array($token['code'], TokenHelper::$arrayTokenCodes, true)) {
126-
$pointerCloser = ArrayHelper::openClosePointers($token)[1];
127-
} elseif ($token['code'] === T_DOUBLE_ARROW) {
128-
$this->pointerArrow = $pointer;
127+
128+
if ($token['code'] === T_DOUBLE_ARROW) {
129+
$this->pointerArrow = $i;
129130
} elseif ($token['code'] === T_COMMA) {
130-
$this->pointerComma = $pointer;
131+
$this->pointerComma = $i;
131132
} elseif ($token['code'] === T_ELLIPSIS) {
132133
$this->unpacking = true;
133134
} elseif ($this->pointerArrow === null) {
134135
if ($firstNonWhitespace === null && $token['code'] !== T_WHITESPACE) {
135-
$firstNonWhitespace = $pointer;
136+
$firstNonWhitespace = $i;
136137
}
137138
if (in_array($token['code'], TokenHelper::$inlineCommentTokenCodes, true) === false) {
138139
$key .= $token['content'];

tests/Sniffs/Arrays/data/alphabeticallySortedByKeysErrors.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ public function log($msg)
1818
];
1919
}
2020
},
21-
'arrow' => fn($x) => array(
22-
'y' => 'y val',
23-
'x' => 'x val',
24-
),
2521
'nested' => array(
2622
'b2' => 'b2 val',
2723
'a2' => 'a2 val',
24+
),
25+
'arrow' => fn($x) => array(
26+
'y' => 'y val',
27+
'x' => 'x val',
2828
)];
2929

3030
[

0 commit comments

Comments
 (0)