Chris@17
|
1 <?php
|
Chris@17
|
2 /**
|
Chris@18
|
3 * Processes single and multi-line arrays.
|
Chris@17
|
4 *
|
Chris@17
|
5 * @author Greg Sherwood <gsherwood@squiz.net>
|
Chris@17
|
6 * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
|
Chris@17
|
7 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
|
Chris@17
|
8 */
|
Chris@17
|
9
|
Chris@17
|
10 namespace PHP_CodeSniffer\Sniffs;
|
Chris@17
|
11
|
Chris@17
|
12 use PHP_CodeSniffer\Files\File;
|
Chris@17
|
13 use PHP_CodeSniffer\Util\Tokens;
|
Chris@17
|
14
|
Chris@17
|
15 abstract class AbstractArraySniff implements Sniff
|
Chris@17
|
16 {
|
Chris@17
|
17
|
Chris@17
|
18
|
Chris@17
|
19 /**
|
Chris@17
|
20 * Returns an array of tokens this test wants to listen for.
|
Chris@17
|
21 *
|
Chris@17
|
22 * @return array
|
Chris@17
|
23 */
|
Chris@17
|
24 final public function register()
|
Chris@17
|
25 {
|
Chris@17
|
26 return [
|
Chris@17
|
27 T_ARRAY,
|
Chris@17
|
28 T_OPEN_SHORT_ARRAY,
|
Chris@17
|
29 ];
|
Chris@17
|
30
|
Chris@17
|
31 }//end register()
|
Chris@17
|
32
|
Chris@17
|
33
|
Chris@17
|
34 /**
|
Chris@17
|
35 * Processes this sniff, when one of its tokens is encountered.
|
Chris@17
|
36 *
|
Chris@17
|
37 * @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being checked.
|
Chris@17
|
38 * @param int $stackPtr The position of the current token in
|
Chris@17
|
39 * the stack passed in $tokens.
|
Chris@17
|
40 *
|
Chris@17
|
41 * @return void
|
Chris@17
|
42 */
|
Chris@17
|
43 public function process(File $phpcsFile, $stackPtr)
|
Chris@17
|
44 {
|
Chris@17
|
45 $tokens = $phpcsFile->getTokens();
|
Chris@17
|
46
|
Chris@17
|
47 if ($tokens[$stackPtr]['code'] === T_ARRAY) {
|
Chris@17
|
48 $phpcsFile->recordMetric($stackPtr, 'Short array syntax used', 'no');
|
Chris@17
|
49
|
Chris@17
|
50 $arrayStart = $tokens[$stackPtr]['parenthesis_opener'];
|
Chris@17
|
51 if (isset($tokens[$arrayStart]['parenthesis_closer']) === false) {
|
Chris@17
|
52 // Incomplete array.
|
Chris@17
|
53 return;
|
Chris@17
|
54 }
|
Chris@17
|
55
|
Chris@17
|
56 $arrayEnd = $tokens[$arrayStart]['parenthesis_closer'];
|
Chris@17
|
57 } else {
|
Chris@17
|
58 $phpcsFile->recordMetric($stackPtr, 'Short array syntax used', 'yes');
|
Chris@17
|
59 $arrayStart = $stackPtr;
|
Chris@17
|
60 $arrayEnd = $tokens[$stackPtr]['bracket_closer'];
|
Chris@17
|
61 }
|
Chris@17
|
62
|
Chris@17
|
63 $lastContent = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($arrayEnd - 1), null, true);
|
Chris@17
|
64 if ($tokens[$lastContent]['code'] === T_COMMA) {
|
Chris@17
|
65 // Last array item ends with a comma.
|
Chris@17
|
66 $phpcsFile->recordMetric($stackPtr, 'Array end comma', 'yes');
|
Chris@17
|
67 $lastArrayToken = $lastContent;
|
Chris@17
|
68 } else {
|
Chris@17
|
69 $phpcsFile->recordMetric($stackPtr, 'Array end comma', 'no');
|
Chris@17
|
70 $lastArrayToken = $arrayEnd;
|
Chris@17
|
71 }
|
Chris@17
|
72
|
Chris@17
|
73 if ($tokens[$stackPtr]['code'] === T_ARRAY) {
|
Chris@17
|
74 $lastToken = $tokens[$stackPtr]['parenthesis_opener'];
|
Chris@17
|
75 } else {
|
Chris@17
|
76 $lastToken = $stackPtr;
|
Chris@17
|
77 }
|
Chris@17
|
78
|
Chris@17
|
79 $keyUsed = false;
|
Chris@17
|
80 $indices = [];
|
Chris@17
|
81
|
Chris@17
|
82 for ($checkToken = ($stackPtr + 1); $checkToken <= $lastArrayToken; $checkToken++) {
|
Chris@17
|
83 // Skip bracketed statements, like function calls.
|
Chris@17
|
84 if ($tokens[$checkToken]['code'] === T_OPEN_PARENTHESIS
|
Chris@17
|
85 && (isset($tokens[$checkToken]['parenthesis_owner']) === false
|
Chris@17
|
86 || $tokens[$checkToken]['parenthesis_owner'] !== $stackPtr)
|
Chris@17
|
87 ) {
|
Chris@17
|
88 $checkToken = $tokens[$checkToken]['parenthesis_closer'];
|
Chris@17
|
89 continue;
|
Chris@17
|
90 }
|
Chris@17
|
91
|
Chris@17
|
92 if ($tokens[$checkToken]['code'] === T_ARRAY
|
Chris@17
|
93 || $tokens[$checkToken]['code'] === T_OPEN_SHORT_ARRAY
|
Chris@17
|
94 || $tokens[$checkToken]['code'] === T_CLOSURE
|
Chris@17
|
95 ) {
|
Chris@17
|
96 // Let subsequent calls of this test handle nested arrays.
|
Chris@17
|
97 if ($tokens[$lastToken]['code'] !== T_DOUBLE_ARROW) {
|
Chris@17
|
98 $indices[] = ['value_start' => $checkToken];
|
Chris@17
|
99 $lastToken = $checkToken;
|
Chris@17
|
100 }
|
Chris@17
|
101
|
Chris@17
|
102 if ($tokens[$checkToken]['code'] === T_ARRAY) {
|
Chris@17
|
103 $checkToken = $tokens[$tokens[$checkToken]['parenthesis_opener']]['parenthesis_closer'];
|
Chris@17
|
104 } else if ($tokens[$checkToken]['code'] === T_OPEN_SHORT_ARRAY) {
|
Chris@17
|
105 $checkToken = $tokens[$checkToken]['bracket_closer'];
|
Chris@17
|
106 } else {
|
Chris@17
|
107 // T_CLOSURE.
|
Chris@17
|
108 $checkToken = $tokens[$checkToken]['scope_closer'];
|
Chris@17
|
109 }
|
Chris@17
|
110
|
Chris@17
|
111 $checkToken = $phpcsFile->findNext(T_WHITESPACE, ($checkToken + 1), null, true);
|
Chris@17
|
112 $lastToken = $checkToken;
|
Chris@17
|
113 if ($tokens[$checkToken]['code'] !== T_COMMA) {
|
Chris@17
|
114 $checkToken--;
|
Chris@17
|
115 }
|
Chris@17
|
116
|
Chris@17
|
117 continue;
|
Chris@17
|
118 }//end if
|
Chris@17
|
119
|
Chris@17
|
120 if ($tokens[$checkToken]['code'] !== T_DOUBLE_ARROW
|
Chris@17
|
121 && $tokens[$checkToken]['code'] !== T_COMMA
|
Chris@17
|
122 && $checkToken !== $arrayEnd
|
Chris@17
|
123 ) {
|
Chris@17
|
124 continue;
|
Chris@17
|
125 }
|
Chris@17
|
126
|
Chris@17
|
127 if ($tokens[$checkToken]['code'] === T_COMMA
|
Chris@17
|
128 || $checkToken === $arrayEnd
|
Chris@17
|
129 ) {
|
Chris@17
|
130 $stackPtrCount = 0;
|
Chris@17
|
131 if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
|
Chris@17
|
132 $stackPtrCount = count($tokens[$stackPtr]['nested_parenthesis']);
|
Chris@17
|
133 }
|
Chris@17
|
134
|
Chris@17
|
135 $commaCount = 0;
|
Chris@17
|
136 if (isset($tokens[$checkToken]['nested_parenthesis']) === true) {
|
Chris@17
|
137 $commaCount = count($tokens[$checkToken]['nested_parenthesis']);
|
Chris@17
|
138 if ($tokens[$stackPtr]['code'] === T_ARRAY) {
|
Chris@17
|
139 // Remove parenthesis that are used to define the array.
|
Chris@17
|
140 $commaCount--;
|
Chris@17
|
141 }
|
Chris@17
|
142 }
|
Chris@17
|
143
|
Chris@17
|
144 if ($commaCount > $stackPtrCount) {
|
Chris@17
|
145 // This comma is inside more parenthesis than the ARRAY keyword,
|
Chris@17
|
146 // so it is actually a comma used to do things like
|
Chris@17
|
147 // separate arguments in a function call.
|
Chris@17
|
148 continue;
|
Chris@17
|
149 }
|
Chris@17
|
150
|
Chris@17
|
151 if ($keyUsed === false) {
|
Chris@17
|
152 $valueContent = $phpcsFile->findNext(
|
Chris@17
|
153 Tokens::$emptyTokens,
|
Chris@17
|
154 ($lastToken + 1),
|
Chris@17
|
155 $checkToken,
|
Chris@17
|
156 true
|
Chris@17
|
157 );
|
Chris@17
|
158
|
Chris@17
|
159 $indices[] = ['value_start' => $valueContent];
|
Chris@17
|
160 }
|
Chris@17
|
161
|
Chris@17
|
162 $lastToken = $checkToken;
|
Chris@17
|
163 $keyUsed = false;
|
Chris@17
|
164 continue;
|
Chris@17
|
165 }//end if
|
Chris@17
|
166
|
Chris@17
|
167 if ($tokens[$checkToken]['code'] === T_DOUBLE_ARROW) {
|
Chris@17
|
168 $keyUsed = true;
|
Chris@17
|
169
|
Chris@17
|
170 // Find the start of index that uses this double arrow.
|
Chris@17
|
171 $indexEnd = $phpcsFile->findPrevious(T_WHITESPACE, ($checkToken - 1), $arrayStart, true);
|
Chris@17
|
172 $indexStart = $phpcsFile->findStartOfStatement($indexEnd);
|
Chris@17
|
173
|
Chris@17
|
174 // Find the value of this index.
|
Chris@17
|
175 $nextContent = $phpcsFile->findNext(
|
Chris@17
|
176 Tokens::$emptyTokens,
|
Chris@17
|
177 ($checkToken + 1),
|
Chris@17
|
178 $arrayEnd,
|
Chris@17
|
179 true
|
Chris@17
|
180 );
|
Chris@17
|
181
|
Chris@17
|
182 $indices[] = [
|
Chris@17
|
183 'index_start' => $indexStart,
|
Chris@17
|
184 'index_end' => $indexEnd,
|
Chris@17
|
185 'arrow' => $checkToken,
|
Chris@17
|
186 'value_start' => $nextContent,
|
Chris@17
|
187 ];
|
Chris@17
|
188
|
Chris@17
|
189 $lastToken = $checkToken;
|
Chris@17
|
190 }//end if
|
Chris@17
|
191 }//end for
|
Chris@17
|
192
|
Chris@17
|
193 if ($tokens[$arrayStart]['line'] === $tokens[$arrayEnd]['line']) {
|
Chris@17
|
194 $this->processSingleLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices);
|
Chris@17
|
195 } else {
|
Chris@17
|
196 $this->processMultiLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices);
|
Chris@17
|
197 }
|
Chris@17
|
198
|
Chris@17
|
199 }//end process()
|
Chris@17
|
200
|
Chris@17
|
201
|
Chris@17
|
202 /**
|
Chris@17
|
203 * Processes a single-line array definition.
|
Chris@17
|
204 *
|
Chris@17
|
205 * @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being checked.
|
Chris@17
|
206 * @param int $stackPtr The position of the current token
|
Chris@17
|
207 * in the stack passed in $tokens.
|
Chris@17
|
208 * @param int $arrayStart The token that starts the array definition.
|
Chris@17
|
209 * @param int $arrayEnd The token that ends the array definition.
|
Chris@17
|
210 * @param array $indices An array of token positions for the array keys,
|
Chris@17
|
211 * double arrows, and values.
|
Chris@17
|
212 *
|
Chris@17
|
213 * @return void
|
Chris@17
|
214 */
|
Chris@17
|
215 abstract protected function processSingleLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices);
|
Chris@17
|
216
|
Chris@17
|
217
|
Chris@17
|
218 /**
|
Chris@17
|
219 * Processes a multi-line array definition.
|
Chris@17
|
220 *
|
Chris@17
|
221 * @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being checked.
|
Chris@17
|
222 * @param int $stackPtr The position of the current token
|
Chris@17
|
223 * in the stack passed in $tokens.
|
Chris@17
|
224 * @param int $arrayStart The token that starts the array definition.
|
Chris@17
|
225 * @param int $arrayEnd The token that ends the array definition.
|
Chris@17
|
226 * @param array $indices An array of token positions for the array keys,
|
Chris@17
|
227 * double arrows, and values.
|
Chris@17
|
228 *
|
Chris@17
|
229 * @return void
|
Chris@17
|
230 */
|
Chris@17
|
231 abstract protected function processMultiLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices);
|
Chris@17
|
232
|
Chris@17
|
233
|
Chris@17
|
234 }//end class
|