kint-php /
kint
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 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) |
||
|
0 ignored issues
–
show
|
|||
| 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 |
Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a
@returnannotation as described here.