1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
class Kint_SourceParser |
|
|
|
|
4
|
|
|
{ |
5
|
|
|
private static $ignore = array( |
6
|
|
|
T_CLOSE_TAG => true, |
7
|
|
|
T_COMMENT => true, |
8
|
|
|
T_DOC_COMMENT => true, |
9
|
|
|
T_INLINE_HTML => true, |
10
|
|
|
T_OPEN_TAG => true, |
11
|
|
|
T_OPEN_TAG_WITH_ECHO => true, |
12
|
|
|
T_WHITESPACE => true, |
13
|
|
|
); |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* Things we need to do specially for operator tokens: |
17
|
|
|
* - Refuse to strip spaces around them |
18
|
|
|
* - Wrap the access path in parentheses if there |
19
|
|
|
* are any of these in the final short parameter. |
20
|
|
|
*/ |
21
|
|
|
private static $operator = array( |
22
|
|
|
T_AND_EQUAL => true, |
23
|
|
|
T_BOOLEAN_AND => true, |
24
|
|
|
T_BOOLEAN_OR => true, |
25
|
|
|
T_ARRAY_CAST => true, |
26
|
|
|
T_BOOL_CAST => true, |
27
|
|
|
T_CLONE => true, |
28
|
|
|
T_CONCAT_EQUAL => true, |
29
|
|
|
T_DEC => true, |
30
|
|
|
T_DIV_EQUAL => true, |
31
|
|
|
T_DOUBLE_CAST => true, |
32
|
|
|
T_INC => true, |
33
|
|
|
T_INCLUDE => true, |
34
|
|
|
T_INCLUDE_ONCE => true, |
35
|
|
|
T_INSTANCEOF => true, |
36
|
|
|
T_INT_CAST => true, |
37
|
|
|
T_IS_EQUAL => true, |
38
|
|
|
T_IS_GREATER_OR_EQUAL => true, |
39
|
|
|
T_IS_IDENTICAL => true, |
40
|
|
|
T_IS_NOT_EQUAL => true, |
41
|
|
|
T_IS_NOT_IDENTICAL => true, |
42
|
|
|
T_IS_SMALLER_OR_EQUAL => true, |
43
|
|
|
T_LOGICAL_AND => true, |
44
|
|
|
T_LOGICAL_OR => true, |
45
|
|
|
T_LOGICAL_XOR => true, |
46
|
|
|
T_MINUS_EQUAL => true, |
47
|
|
|
T_MOD_EQUAL => true, |
48
|
|
|
T_MUL_EQUAL => true, |
49
|
|
|
T_NEW => true, |
50
|
|
|
T_OBJECT_CAST => true, |
51
|
|
|
T_OR_EQUAL => true, |
52
|
|
|
T_PLUS_EQUAL => true, |
53
|
|
|
T_REQUIRE => true, |
54
|
|
|
T_REQUIRE_ONCE => true, |
55
|
|
|
T_SL => true, |
56
|
|
|
T_SL_EQUAL => true, |
57
|
|
|
T_SR => true, |
58
|
|
|
T_SR_EQUAL => true, |
59
|
|
|
T_STRING_CAST => true, |
60
|
|
|
T_UNSET_CAST => true, |
61
|
|
|
T_XOR_EQUAL => true, |
62
|
|
|
'!' => true, |
63
|
|
|
'%' => true, |
64
|
|
|
'&' => true, |
65
|
|
|
'*' => true, |
66
|
|
|
'+' => true, |
67
|
|
|
'-' => true, |
68
|
|
|
'.' => true, |
69
|
|
|
'/' => true, |
70
|
|
|
':' => true, |
71
|
|
|
'<' => true, |
72
|
|
|
'=' => true, |
73
|
|
|
'>' => true, |
74
|
|
|
'?' => true, |
75
|
|
|
'^' => true, |
76
|
|
|
'|' => true, |
77
|
|
|
'~' => true, |
78
|
|
|
); |
79
|
|
|
|
80
|
|
|
private static $strip = array( |
81
|
|
|
'(' => true, |
82
|
|
|
')' => true, |
83
|
|
|
'[' => true, |
84
|
|
|
']' => true, |
85
|
|
|
'{' => true, |
86
|
|
|
'}' => true, |
87
|
|
|
T_OBJECT_OPERATOR => true, |
88
|
|
|
T_DOUBLE_COLON => true, |
89
|
|
|
); |
90
|
|
|
|
91
|
|
|
public static function getFunctionCalls($source, $line, $function) |
92
|
|
|
{ |
93
|
|
|
static $up = array( |
|
|
|
|
94
|
|
|
'(' => true, |
95
|
|
|
'[' => true, |
96
|
|
|
'{' => true, |
97
|
|
|
T_CURLY_OPEN => true, |
98
|
|
|
T_DOLLAR_OPEN_CURLY_BRACES => true, |
99
|
|
|
); |
100
|
|
|
static $down = array( |
101
|
|
|
')' => true, |
102
|
|
|
']' => true, |
103
|
|
|
'}' => true, |
104
|
|
|
); |
105
|
|
|
static $modifiers = array( |
106
|
|
|
'!' => true, |
107
|
|
|
'@' => true, |
108
|
|
|
'~' => true, |
109
|
|
|
'+' => true, |
110
|
|
|
'-' => true, |
111
|
|
|
); |
112
|
|
|
|
113
|
|
|
if (KINT_PHP53) { |
114
|
|
|
self::$strip[T_NS_SEPARATOR] = true; |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
if (KINT_PHP56) { |
118
|
|
|
self::$operator[T_POW] = true; |
119
|
|
|
self::$operator[T_POW_EQUAL] = true; |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
if (KINT_PHP70) { |
123
|
|
|
self::$operator[T_SPACESHIP] = true; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
$tokens = token_get_all($source); |
127
|
|
|
$cursor = 1; |
128
|
|
|
$function_calls = array(); |
|
|
|
|
129
|
|
|
$prev_tokens = array(null, null, null); |
|
|
|
|
130
|
|
|
|
131
|
|
|
if (is_array($function)) { |
132
|
|
|
$class = explode('\\', $function[0]); |
133
|
|
|
$class = strtolower(end($class)); |
134
|
|
|
$function = strtolower($function[1]); |
|
|
|
|
135
|
|
|
} else { |
136
|
|
|
$class = null; |
137
|
|
|
$function = strtolower($function); |
|
|
|
|
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
// Loop through tokens |
141
|
|
|
foreach ($tokens as $index => $token) { |
142
|
|
|
if (!is_array($token)) { |
143
|
|
|
continue; |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
// Count newlines for line number instead of using |
147
|
|
|
// $token[2] since it's not available until 5.2.2 |
148
|
|
|
// Also note that certain situations (String tokens after whitespace) |
149
|
|
|
// may not have the correct line number unless you do this manually |
150
|
|
|
$cursor += substr_count($token[1], "\n"); |
151
|
|
|
if ($cursor > $line) { |
152
|
|
|
break; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
// Store the last real tokens for later |
156
|
|
|
if (isset(self::$ignore[$token[0]])) { |
157
|
|
|
continue; |
158
|
|
|
} else { |
159
|
|
|
$prev_tokens = array($prev_tokens[1], $prev_tokens[2], $token); |
|
|
|
|
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
// Check if it's the right type to be the function we're looking for |
163
|
|
|
if ($token[0] !== T_STRING || strtolower($token[1]) !== $function) { |
164
|
|
|
continue; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
// Check if it's a function call |
168
|
|
|
if ($tokens[self::realTokenIndex($tokens, $index, 1)] !== '(') { |
169
|
|
|
continue; |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
// Check if it matches the signature |
173
|
|
|
if ($class === null) { |
174
|
|
|
if ($prev_tokens[1] && in_array($prev_tokens[1][0], array(T_DOUBLE_COLON, T_OBJECT_OPERATOR))) { |
|
|
|
|
175
|
|
|
continue; |
176
|
|
|
} |
177
|
|
|
} else { |
178
|
|
|
if (!$prev_tokens[1] || $prev_tokens[1][0] !== T_DOUBLE_COLON) { |
|
|
|
|
179
|
|
|
continue; |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
if (!$prev_tokens[0] || $prev_tokens[0][0] !== T_STRING || strtolower($prev_tokens[0][1]) !== $class) { |
|
|
|
|
183
|
|
|
continue; |
184
|
|
|
} |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
$inner_cursor = $cursor; |
|
|
|
|
188
|
|
|
$depth = 0; // The depth respective to the function call |
189
|
|
|
$offset = 1; // The offset from the function call |
190
|
|
|
$instring = false; // Whether we're in a string or not |
191
|
|
|
$realtokens = false; // Whether the string contains anything meaningful or not |
192
|
|
|
$params = array(); // All our collected parameters |
193
|
|
|
$shortparam = array(); // The short version of the parameter |
194
|
|
|
$param_start = 1; // The distance to the start of the parameter |
|
|
|
|
195
|
|
|
|
196
|
|
|
// Loop through the following tokens until the function call ends |
197
|
|
|
while (isset($tokens[$index + $offset])) { |
198
|
|
|
$token = $tokens[$index + $offset]; |
199
|
|
|
|
200
|
|
|
// Ensure that the $inner_cursor is correct and |
201
|
|
|
// that $token is either a T_ constant or a string |
202
|
|
|
if (is_array($token)) { |
203
|
|
|
$inner_cursor += substr_count($token[1], "\n"); |
|
|
|
|
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
if (!isset(self::$ignore[$token[0]]) && !isset($down[$token[0]])) { |
207
|
|
|
$realtokens = true; |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
// If it's a token that makes us to up a level, increase the depth |
211
|
|
|
if (isset($up[$token[0]])) { |
212
|
|
|
// If this is the first paren set the start of the param to just after it |
213
|
|
|
if ($depth === 0) { |
214
|
|
|
$param_start = $offset + 1; |
|
|
|
|
215
|
|
|
} elseif ($depth === 1) { |
216
|
|
|
$shortparam[] = $token; |
217
|
|
|
$realtokens = false; |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
++$depth; |
221
|
|
|
} elseif (isset($down[$token[0]])) { |
222
|
|
|
--$depth; |
223
|
|
|
|
224
|
|
|
// If this brings us down to the parameter level, and we've had |
225
|
|
|
// real tokens since going up, fill the $shortparam with an ellipsis |
226
|
|
|
if ($depth === 1) { |
227
|
|
|
if ($realtokens) { |
228
|
|
|
$shortparam[] = '...'; |
229
|
|
|
} |
230
|
|
|
$shortparam[] = $token; |
231
|
|
|
} |
232
|
|
|
} elseif ($token[0] === '"') { |
233
|
|
|
// Strings use the same symbol for up and down, but we can |
234
|
|
|
// only ever be inside one string, so just use a bool for that |
235
|
|
|
if ($instring) { |
236
|
|
|
--$depth; |
237
|
|
|
if ($depth === 1) { |
238
|
|
|
$shortparam[] = '...'; |
239
|
|
|
} |
240
|
|
|
} else { |
241
|
|
|
++$depth; |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
$instring = !$instring; |
245
|
|
|
|
246
|
|
|
$shortparam[] = '"'; |
247
|
|
|
} elseif ($depth === 1) { |
248
|
|
|
if ($token[0] === ',') { |
249
|
|
|
$params[] = array( |
250
|
|
|
'full' => array_slice($tokens, $index + $param_start, $offset - $param_start), |
|
|
|
|
251
|
|
|
'short' => $shortparam, |
252
|
|
|
); |
253
|
|
|
$shortparam = array(); |
254
|
|
|
$param_start = $offset + 1; |
|
|
|
|
255
|
|
|
} elseif ($token[0] === T_CONSTANT_ENCAPSED_STRING && strlen($token[1]) > 2) { |
256
|
|
|
$shortparam[] = $token[1][0].'...'.$token[1][0]; |
257
|
|
|
} else { |
258
|
|
|
$shortparam[] = $token; |
259
|
|
|
} |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
// Depth has dropped to 0 (So we've hit the closing paren) |
263
|
|
|
if ($depth <= 0) { |
264
|
|
|
$params[] = array( |
265
|
|
|
'full' => array_slice($tokens, $index + $param_start, $offset - $param_start), |
|
|
|
|
266
|
|
|
'short' => $shortparam, |
267
|
|
|
); |
268
|
|
|
|
269
|
|
|
break; |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
++$offset; |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
// If we're not passed (or at) the line at the end |
276
|
|
|
// of the function call, we're too early so skip it |
277
|
|
|
if ($inner_cursor < $line) { |
|
|
|
|
278
|
|
|
continue; |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
// Format the final output parameters |
282
|
|
|
foreach ($params as &$param) { |
283
|
|
|
$name = self::tokensFormatted($param['short']); |
284
|
|
|
$expression = false; |
285
|
|
|
foreach ($name as $token) { |
286
|
|
|
if (self::tokenIsOperator($token)) { |
287
|
|
|
$expression = true; |
288
|
|
|
break; |
289
|
|
|
} |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
$param = array( |
293
|
|
|
'name' => self::tokensToString($name), |
294
|
|
|
'path' => self::tokensToString(self::tokensTrim($param['full'])), |
295
|
|
|
'expression' => $expression, |
296
|
|
|
); |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
// Get the modifiers |
300
|
|
|
$mods = array(); |
301
|
|
|
--$index; |
302
|
|
|
|
303
|
|
|
while (isset($tokens[$index])) { |
304
|
|
|
if (isset(self::$ignore[$tokens[$index][0]])) { |
305
|
|
|
--$index; |
306
|
|
|
continue; |
307
|
|
|
} elseif (is_array($tokens[$index]) && empty($mods)) { |
308
|
|
|
if ($tokens[$index][0] === T_DOUBLE_COLON || $tokens[$index][0] === T_STRING || (KINT_PHP53 && $tokens[$index][0] === T_NS_SEPARATOR)) { |
309
|
|
|
--$index; |
310
|
|
|
continue; |
311
|
|
|
} else { |
312
|
|
|
break; |
313
|
|
|
} |
314
|
|
|
} elseif (isset($modifiers[$tokens[$index][0]])) { |
315
|
|
|
$mods[] = $tokens[$index]; |
316
|
|
|
--$index; |
317
|
|
|
continue; |
318
|
|
|
} else { |
319
|
|
|
break; |
320
|
|
|
} |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
$function_calls[] = array( |
|
|
|
|
324
|
|
|
'parameters' => $params, |
325
|
|
|
'modifiers' => $mods, |
326
|
|
|
); |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
return $function_calls; |
|
|
|
|
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
private static function realTokenIndex(array $tokens, $index, $direction) |
|
|
|
|
333
|
|
|
{ |
334
|
|
|
$index += $direction; |
335
|
|
|
|
336
|
|
|
while (isset($tokens[$index])) { |
337
|
|
|
if (!isset(self::$ignore[$tokens[$index][0]])) { |
338
|
|
|
return $index; |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
$index += $direction; |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
return null; |
345
|
|
|
} |
346
|
|
|
|
347
|
|
|
/** |
348
|
|
|
* We need a separate method to check if tokens are operators because we |
349
|
|
|
* occasionally add "..." to short parameter versions. If we simply check |
350
|
|
|
* for `$token[0]` then "..." will incorrectly match the "." operator. |
351
|
|
|
* |
352
|
|
|
* @param array|string $token The token to check |
353
|
|
|
* |
354
|
|
|
* @return bool |
355
|
|
|
*/ |
356
|
|
|
private static function tokenIsOperator($token) |
|
|
|
|
357
|
|
|
{ |
358
|
|
|
return $token !== '...' && isset(self::$operator[$token[0]]); |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
private static function tokensToString(array $tokens) |
362
|
|
|
{ |
363
|
|
|
$out = ''; |
364
|
|
|
|
365
|
|
|
foreach ($tokens as $token) { |
366
|
|
|
if (is_string($token)) { |
367
|
|
|
$out .= $token; |
368
|
|
|
} elseif (is_array($token)) { |
369
|
|
|
$out .= $token[1]; |
370
|
|
|
} |
371
|
|
|
} |
372
|
|
|
|
373
|
|
|
return $out; |
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
private static function tokensTrim(array $tokens) |
377
|
|
|
{ |
378
|
|
View Code Duplication |
foreach ($tokens as $index => $token) { |
|
|
|
|
379
|
|
|
if (isset(self::$ignore[$token[0]])) { |
380
|
|
|
unset($tokens[$index]); |
381
|
|
|
} else { |
382
|
|
|
break; |
383
|
|
|
} |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
$tokens = array_reverse($tokens); |
|
|
|
|
387
|
|
|
|
388
|
|
View Code Duplication |
foreach ($tokens as $index => $token) { |
|
|
|
|
389
|
|
|
if (isset(self::$ignore[$token[0]])) { |
390
|
|
|
unset($tokens[$index]); |
391
|
|
|
} else { |
392
|
|
|
break; |
393
|
|
|
} |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
return array_reverse($tokens); |
397
|
|
|
} |
398
|
|
|
|
399
|
|
|
private static function tokensFormatted(array $tokens) |
400
|
|
|
{ |
401
|
|
|
$space = false; |
402
|
|
|
|
403
|
|
|
$tokens = self::tokensTrim($tokens); |
|
|
|
|
404
|
|
|
|
405
|
|
|
$output = array(); |
406
|
|
|
$last = null; |
407
|
|
|
|
408
|
|
|
foreach ($tokens as $index => $token) { |
409
|
|
|
if (isset(self::$ignore[$token[0]])) { |
410
|
|
|
if ($space) { |
411
|
|
|
continue; |
412
|
|
|
} |
413
|
|
|
|
414
|
|
|
$next = $tokens[self::realTokenIndex($tokens, $index, 1)]; |
415
|
|
|
|
416
|
|
|
if (isset(self::$strip[$last[0]]) && !self::tokenIsOperator($next)) { |
417
|
|
|
continue; |
418
|
|
|
} elseif (isset(self::$strip[$next[0]]) && $last && !self::tokenIsOperator($last)) { |
419
|
|
|
continue; |
420
|
|
|
} |
421
|
|
|
|
422
|
|
|
$token = ' '; |
423
|
|
|
$space = true; |
424
|
|
|
} else { |
425
|
|
|
$space = false; |
426
|
|
|
$last = $token; |
427
|
|
|
} |
428
|
|
|
|
429
|
|
|
$output[] = $token; |
430
|
|
|
} |
431
|
|
|
|
432
|
|
|
return $output; |
433
|
|
|
} |
434
|
|
|
} |
435
|
|
|
|
This check examines a number of code elements and verifies that they conform to the given naming conventions.
You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.