| @@ -33,13 +33,14 @@ | ||
| 33 | 33 | 'message' => '', |
| 34 | 34 | ]; |
| 35 | 35 | } |
| 36 | 36 | |
| 37 | - $redeclaration_error = self::get_redeclaration_error( $snippet_code, $snippet_type ); | |
| 38 | - if ( null !== $redeclaration_error ) { | |
| 37 | + $redeclaration = self::find_function_redeclaration( $snippet_code, $snippet_type ); | |
| 38 | + if ( null !== $redeclaration ) { | |
| 39 | 39 | return [ |
| 40 | 40 | 'valid' => false, |
| 41 | - 'message' => $redeclaration_error->getMessage(), | |
| 41 | + // translators: %1$d is the line number, %2$s is the fully qualified function name. | |
| 42 | + 'message' => sprintf( __( 'Line %1$d: Cannot redeclare function %2$s(). Rename the function or guard its declaration with function_exists().', 'insert-php' ), $redeclaration['line'], $redeclaration['name'] ), | |
| 42 | 43 | ]; |
| 43 | 44 | } |
| 44 | 45 | |
| 45 | 46 | $validation_errors = []; |
| @@ -117,47 +118,8 @@ | ||
| 117 | 118 | ]; |
| 118 | 119 | } |
| 119 | 120 | |
| 120 | 121 | /** |
| 121 | - * Check executable snippet code for syntax errors without running it. | |
| 122 | - * | |
| 123 | - * The tokenizer parses the code in TOKEN_PARSE mode and never executes it. | |
| 124 | - * Errors PHP raises only when the code runs, such as a call to an undefined | |
| 125 | - * function, are left to the check that runs when the snippet is activated. | |
| 126 | - * Without the tokenizer extension nothing is checked and the result says so. | |
| 127 | - * | |
| 128 | - * @param string $snippet_code Snippet code, without the opening tag for PHP snippets. | |
| 129 | - * @param string $snippet_type Snippet type. | |
| 130 | - * @return array{valid:bool,checked:bool,message:string} Check result. | |
| 131 | - */ | |
| 132 | - public static function check_syntax( string $snippet_code, $snippet_type ): array { | |
| 133 | - if ( ! self::can_tokenize() ) { | |
| 134 | - return [ | |
| 135 | - 'valid' => true, | |
| 136 | - 'checked' => false, | |
| 137 | - 'message' => '', | |
| 138 | - ]; | |
| 139 | - } | |
| 140 | - | |
| 141 | - try { | |
| 142 | - token_get_all( self::get_source( $snippet_code, $snippet_type ), TOKEN_PARSE ); | |
| 143 | - } catch ( CompileError $e ) { | |
| 144 | - return [ | |
| 145 | - 'valid' => false, | |
| 146 | - 'checked' => true, | |
| 147 | - // translators: %1$d is the line number, %2$s is the error message. | |
| 148 | - 'message' => sprintf( __( 'Syntax error on line %1$d: %2$s', 'insert-php' ), $e->getLine(), $e->getMessage() ), | |
| 149 | - ]; | |
| 150 | - } | |
| 151 | - | |
| 152 | - return [ | |
| 153 | - 'valid' => true, | |
| 154 | - 'checked' => true, | |
| 155 | - 'message' => '', | |
| 156 | - ]; | |
| 157 | - } | |
| 158 | - | |
| 159 | - /** | |
| 160 | 122 | * Find a top-level function declaration that would be redeclared by eval(). |
| 161 | 123 | * |
| 162 | 124 | * PHP terminates the request when eval() declares an already-loaded function, |
| 163 | 125 | * so this conflict has to be detected before evaluating the snippet. |
| @@ -166,110 +128,14 @@ | ||
| 166 | 128 | * @param string $snippet_type Snippet type. |
| 167 | 129 | * @return array{name:string,line:int}|null Conflict details, or null when none is found. |
| 168 | 130 | */ |
| 169 | 131 | public static function find_function_redeclaration( $snippet_code, $snippet_type ) { |
| 170 | - $redeclaration = self::scan_redeclarations( (string) $snippet_code, $snippet_type, false ); | |
| 171 | - if ( null === $redeclaration ) { | |
| 172 | - return null; | |
| 173 | - } | |
| 132 | + $source = WINP_SNIPPET_TYPE_UNIVERSAL === $snippet_type | |
| 133 | + ? $snippet_code | |
| 134 | + : '<?php ' . $snippet_code; | |
| 135 | + $tokens = token_get_all( $source ); | |
| 174 | 136 | |
| 175 | - return [ | |
| 176 | - 'name' => $redeclaration['name'], | |
| 177 | - 'line' => $redeclaration['line'], | |
| 178 | - ]; | |
| 179 | - } | |
| 180 | - | |
| 181 | - /** | |
| 182 | - * Find a top-level function, class, interface, trait or enum that would be redeclared by eval(). | |
| 183 | - * | |
| 184 | - * PHP terminates the request when eval() declares a name that is already in | |
| 185 | - * use, so this conflict has to be detected before evaluating the snippet. | |
| 186 | - * | |
| 187 | - * @param string $snippet_code Snippet code. | |
| 188 | - * @param string $snippet_type Snippet type. | |
| 189 | - * @return array{kind:string,name:string,line:int}|null Conflict details, or null when none is found. | |
| 190 | - */ | |
| 191 | - public static function find_redeclaration( string $snippet_code, $snippet_type ): ?array { | |
| 192 | - return self::scan_redeclarations( $snippet_code, $snippet_type, true ); | |
| 193 | - } | |
| 194 | - | |
| 195 | - /** | |
| 196 | - * Describe a redeclaration found by find_redeclaration(). | |
| 197 | - * | |
| 198 | - * @param array{kind:string,name:string,line:int} $redeclaration Conflict details. | |
| 199 | - * @return string | |
| 200 | - */ | |
| 201 | - public static function get_redeclaration_message( array $redeclaration ): string { | |
| 202 | - switch ( $redeclaration['kind'] ) { | |
| 203 | - case 'class': | |
| 204 | - // translators: %s is the fully qualified class name. | |
| 205 | - return sprintf( __( 'Cannot declare class %s, because the name is already in use. Rename the class or guard its declaration with class_exists().', 'insert-php' ), $redeclaration['name'] ); | |
| 206 | - case 'interface': | |
| 207 | - // translators: %s is the fully qualified interface name. | |
| 208 | - return sprintf( __( 'Cannot declare interface %s, because the name is already in use. Rename the interface or guard its declaration with interface_exists().', 'insert-php' ), $redeclaration['name'] ); | |
| 209 | - case 'trait': | |
| 210 | - // translators: %s is the fully qualified trait name. | |
| 211 | - return sprintf( __( 'Cannot declare trait %s, because the name is already in use. Rename the trait or guard its declaration with trait_exists().', 'insert-php' ), $redeclaration['name'] ); | |
| 212 | - case 'enum': | |
| 213 | - // translators: %s is the fully qualified enum name. | |
| 214 | - return sprintf( __( 'Cannot declare enum %s, because the name is already in use. Rename the enum or guard its declaration with enum_exists().', 'insert-php' ), $redeclaration['name'] ); | |
| 215 | - default: | |
| 216 | - // translators: %s is the fully qualified function name. | |
| 217 | - return sprintf( __( 'Cannot redeclare function %s(). Rename the function or guard its declaration with function_exists().', 'insert-php' ), $redeclaration['name'] ); | |
| 218 | - } | |
| 219 | - } | |
| 220 | - | |
| 221 | - /** | |
| 222 | - * Build the error for a top-level declaration the snippet would redeclare. | |
| 223 | - * | |
| 224 | - * @param string $snippet_code Snippet code. | |
| 225 | - * @param string $snippet_type Snippet type. | |
| 226 | - * @return ErrorException|null Error carrying the snippet line, or null when none is found. | |
| 227 | - */ | |
| 228 | - public static function get_redeclaration_error( string $snippet_code, $snippet_type ): ?ErrorException { | |
| 229 | - $redeclaration = self::find_redeclaration( $snippet_code, $snippet_type ); | |
| 230 | - if ( null === $redeclaration ) { | |
| 231 | - return null; | |
| 232 | - } | |
| 233 | - | |
| 234 | - return new ErrorException( | |
| 235 | - // translators: %1$d is the line number, %2$s is the error message. | |
| 236 | - sprintf( __( 'Line %1$d: %2$s', 'insert-php' ), $redeclaration['line'], self::get_redeclaration_message( $redeclaration ) ), | |
| 237 | - 0, | |
| 238 | - E_COMPILE_ERROR, | |
| 239 | - __FILE__, | |
| 240 | - $redeclaration['line'] | |
| 241 | - ); | |
| 242 | - } | |
| 243 | - | |
| 244 | - /** | |
| 245 | - * Find the first top-level declaration whose name is already in use. | |
| 246 | - * | |
| 247 | - * Top-level functions are bound when eval() compiles the code, so a conflict | |
| 248 | - * always ends the request. Classes, interfaces, traits and enums are declared | |
| 249 | - * when execution reaches them, so they are not reported after a return, exit, | |
| 250 | - * throw or goto outside any function or class body, which may guard them. | |
| 251 | - * Declarations inside braces are conditional and are skipped, even when the | |
| 252 | - * condition is always true. Class-like declarations inside alternative-syntax | |
| 253 | - * blocks, as in if ( ... ): ... endif;, are skipped too, while functions there | |
| 254 | - * are still reported. Nothing is reported without the tokenizer extension. | |
| 255 | - * | |
| 256 | - * @param string $snippet_code Snippet code. | |
| 257 | - * @param string $snippet_type Snippet type. | |
| 258 | - * @param bool $include_classes Whether class-like declarations are checked. | |
| 259 | - * @return array{kind:string,name:string,line:int}|null Conflict details, or null when none is found. | |
| 260 | - */ | |
| 261 | - private static function scan_redeclarations( string $snippet_code, $snippet_type, bool $include_classes ): ?array { | |
| 262 | - if ( ! self::can_tokenize() ) { | |
| 263 | - return null; | |
| 264 | - } | |
| 265 | - | |
| 266 | - $tokens = token_get_all( self::get_source( $snippet_code, $snippet_type ) ); | |
| 267 | - $declaration_kinds = self::get_declaration_kinds(); | |
| 268 | - $blocks = []; // One entry per open brace, true when it opens a function or class body. | |
| 269 | - $opens_body = false; | |
| 270 | - $alternative_depth = 0; | |
| 271 | - $may_stop = false; | |
| 137 | + $brace_depth = 0; | |
| 272 | 138 | $namespace = ''; |
| 273 | 139 | $namespace_depth = 0; |
| 274 | 140 | $reading_namespace = false; |
| 275 | 141 | $namespace_name = ''; |
| @@ -275,116 +141,79 @@ | ||
| 275 | 141 | $namespace_name = ''; |
| 276 | 142 | $declared = []; |
| 277 | 143 | |
| 278 | 144 | foreach ( $tokens as $index => $token ) { |
| 279 | - if ( ! is_array( $token ) ) { | |
| 280 | - if ( $reading_namespace ) { | |
| 281 | - $reading_namespace = false; | |
| 145 | + if ( is_array( $token ) ) { | |
| 146 | + if ( in_array( $token[0], [ T_CURLY_OPEN, T_DOLLAR_OPEN_CURLY_BRACES ], true ) ) { | |
| 147 | + ++$brace_depth; | |
| 148 | + continue; | |
| 149 | + } | |
| 282 | 150 | |
| 283 | - if ( ';' === $token ) { | |
| 284 | - $namespace = trim( $namespace_name, '\\' ); | |
| 285 | - $namespace_depth = 0; | |
| 286 | - continue; | |
| 287 | - } | |
| 151 | + if ( T_NAMESPACE === $token[0] ) { | |
| 152 | + $reading_namespace = true; | |
| 153 | + $namespace_name = ''; | |
| 154 | + continue; | |
| 155 | + } | |
| 288 | 156 | |
| 289 | - if ( '{' !== $token ) { | |
| 290 | - $namespace_name = ''; | |
| 291 | - continue; | |
| 157 | + if ( $reading_namespace ) { | |
| 158 | + if ( self::is_namespace_name_token( $token[0] ) ) { | |
| 159 | + $namespace_name .= $token[1]; | |
| 160 | + } elseif ( ! in_array( $token[0], [ T_WHITESPACE, T_COMMENT, T_DOC_COMMENT ], true ) ) { | |
| 161 | + $reading_namespace = false; | |
| 162 | + $namespace_name = ''; | |
| 292 | 163 | } |
| 164 | + continue; | |
| 165 | + } | |
| 293 | 166 | |
| 294 | - $blocks[] = false; | |
| 295 | - $namespace = trim( $namespace_name, '\\' ); | |
| 296 | - $namespace_depth = count( $blocks ); | |
| 167 | + if ( T_FUNCTION !== $token[0] || $brace_depth !== $namespace_depth ) { | |
| 297 | 168 | continue; |
| 298 | 169 | } |
| 299 | 170 | |
| 300 | - if ( '{' === $token ) { | |
| 301 | - $blocks[] = $opens_body; | |
| 302 | - $opens_body = false; | |
| 303 | - } elseif ( '}' === $token ) { | |
| 304 | - array_pop( $blocks ); | |
| 305 | - if ( $namespace_depth > count( $blocks ) ) { | |
| 306 | - $namespace = ''; | |
| 307 | - $namespace_depth = 0; | |
| 308 | - } | |
| 309 | - } elseif ( ';' === $token ) { | |
| 310 | - // Abstract and interface methods have no body. | |
| 311 | - $opens_body = false; | |
| 171 | + $function_name = self::get_function_name( $tokens, $index ); | |
| 172 | + if ( null === $function_name ) { | |
| 173 | + continue; | |
| 312 | 174 | } |
| 313 | - continue; | |
| 314 | - } | |
| 315 | 175 | |
| 316 | - $token_id = $token[0]; | |
| 176 | + $qualified_name = $namespace ? $namespace . '\\' . $function_name : $function_name; | |
| 177 | + $normalized_name = strtolower( $qualified_name ); | |
| 317 | 178 | |
| 318 | - if ( in_array( $token_id, [ T_CURLY_OPEN, T_DOLLAR_OPEN_CURLY_BRACES ], true ) ) { | |
| 319 | - $blocks[] = false; | |
| 320 | - continue; | |
| 321 | - } | |
| 179 | + if ( isset( $declared[ $normalized_name ] ) || function_exists( $qualified_name ) ) { | |
| 180 | + return [ | |
| 181 | + 'name' => $qualified_name, | |
| 182 | + 'line' => (int) $token[2], | |
| 183 | + ]; | |
| 184 | + } | |
| 322 | 185 | |
| 323 | - if ( T_NAMESPACE === $token_id ) { | |
| 324 | - $reading_namespace = true; | |
| 325 | - $namespace_name = ''; | |
| 186 | + $declared[ $normalized_name ] = true; | |
| 326 | 187 | continue; |
| 327 | 188 | } |
| 328 | 189 | |
| 329 | 190 | if ( $reading_namespace ) { |
| 330 | - if ( self::is_namespace_name_token( $token_id ) ) { | |
| 331 | - $namespace_name .= $token[1]; | |
| 332 | - } elseif ( ! self::is_insignificant( $token ) ) { | |
| 191 | + if ( ';' === $token ) { | |
| 192 | + $namespace = trim( $namespace_name, '\\' ); | |
| 193 | + $namespace_depth = 0; | |
| 333 | 194 | $reading_namespace = false; |
| 195 | + } elseif ( '{' === $token ) { | |
| 196 | + ++$brace_depth; | |
| 197 | + $namespace = trim( $namespace_name, '\\' ); | |
| 198 | + $namespace_depth = $brace_depth; | |
| 199 | + $reading_namespace = false; | |
| 200 | + } else { | |
| 201 | + $reading_namespace = false; | |
| 334 | 202 | $namespace_name = ''; |
| 335 | 203 | } |
| 336 | 204 | continue; |
| 337 | 205 | } |
| 338 | 206 | |
| 339 | - if ( in_array( $token_id, [ T_RETURN, T_EXIT, T_THROW, T_GOTO ], true ) ) { | |
| 340 | - $may_stop = $may_stop || ! in_array( true, $blocks, true ); | |
| 341 | - continue; | |
| 342 | - } | |
| 343 | - | |
| 344 | - if ( in_array( $token_id, [ T_IF, T_WHILE, T_FOR, T_FOREACH, T_SWITCH ], true ) ) { | |
| 345 | - if ( self::opens_alternative_block( $tokens, $index ) ) { | |
| 346 | - ++$alternative_depth; | |
| 207 | + if ( '{' === $token ) { | |
| 208 | + ++$brace_depth; | |
| 209 | + } elseif ( '}' === $token ) { | |
| 210 | + $brace_depth = max( 0, $brace_depth - 1 ); | |
| 211 | + if ( $namespace_depth > $brace_depth ) { | |
| 212 | + $namespace = ''; | |
| 213 | + $namespace_depth = 0; | |
| 347 | 214 | } |
| 348 | - continue; | |
| 349 | 215 | } |
| 350 | - | |
| 351 | - if ( in_array( $token_id, [ T_ENDIF, T_ENDWHILE, T_ENDFOR, T_ENDFOREACH, T_ENDSWITCH ], true ) ) { | |
| 352 | - $alternative_depth = max( 0, $alternative_depth - 1 ); | |
| 353 | - continue; | |
| 354 | - } | |
| 355 | - | |
| 356 | - if ( ! isset( $declaration_kinds[ $token_id ] ) || self::is_name_reference( $tokens, $index ) ) { | |
| 357 | - continue; | |
| 358 | - } | |
| 359 | - | |
| 360 | - // Named and anonymous functions and classes open their body at the next brace. | |
| 361 | - $opens_body = true; | |
| 362 | - $kind = $declaration_kinds[ $token_id ]; | |
| 363 | - $is_function = 'function' === $kind; | |
| 364 | - | |
| 365 | - if ( count( $blocks ) !== $namespace_depth || ( ! $is_function && ( ! $include_classes || $may_stop || $alternative_depth > 0 ) ) ) { | |
| 366 | - continue; | |
| 367 | - } | |
| 368 | - | |
| 369 | - $name = self::get_declared_name( $tokens, $index ); | |
| 370 | - if ( null === $name ) { | |
| 371 | - continue; | |
| 372 | - } | |
| 373 | - | |
| 374 | - $qualified_name = $namespace ? $namespace . '\\' . $name : $name; | |
| 375 | - $symbol_key = ( $is_function ? 'function:' : 'class:' ) . strtolower( $qualified_name ); | |
| 376 | - $in_use = $is_function ? function_exists( $qualified_name ) : self::is_class_name_in_use( $qualified_name ); | |
| 377 | - | |
| 378 | - if ( isset( $declared[ $symbol_key ] ) || $in_use ) { | |
| 379 | - return [ | |
| 380 | - 'kind' => $kind, | |
| 381 | - 'name' => $qualified_name, | |
| 382 | - 'line' => (int) $token[2], | |
| 383 | - ]; | |
| 384 | - } | |
| 385 | - | |
| 386 | - $declared[ $symbol_key ] = true; | |
| 387 | 216 | } |
| 388 | 217 | |
| 389 | 218 | return null; |
| 390 | 219 | } |
| @@ -389,134 +218,8 @@ | ||
| 389 | 218 | return null; |
| 390 | 219 | } |
| 391 | 220 | |
| 392 | 221 | /** |
| 393 | - * Check whether the tokenizer extension is available. | |
| 394 | - * | |
| 395 | - * @return bool | |
| 396 | - */ | |
| 397 | - private static function can_tokenize(): bool { | |
| 398 | - return function_exists( 'token_get_all' ); | |
| 399 | - } | |
| 400 | - | |
| 401 | - /** | |
| 402 | - * Build the source the tokenizer reads: PHP snippets are stored without their opening tag. | |
| 403 | - * | |
| 404 | - * @param string $snippet_code Snippet code. | |
| 405 | - * @param string $snippet_type Snippet type. | |
| 406 | - * @return string | |
| 407 | - */ | |
| 408 | - private static function get_source( string $snippet_code, $snippet_type ): string { | |
| 409 | - return WINP_SNIPPET_TYPE_UNIVERSAL === $snippet_type ? $snippet_code : '<?php ' . $snippet_code; | |
| 410 | - } | |
| 411 | - | |
| 412 | - /** | |
| 413 | - * Map the declaration tokens to the kind of name they declare. | |
| 414 | - * | |
| 415 | - * @return array<int, string> | |
| 416 | - */ | |
| 417 | - private static function get_declaration_kinds(): array { | |
| 418 | - $kinds = [ | |
| 419 | - T_FUNCTION => 'function', | |
| 420 | - T_CLASS => 'class', | |
| 421 | - T_INTERFACE => 'interface', | |
| 422 | - T_TRAIT => 'trait', | |
| 423 | - ]; | |
| 424 | - | |
| 425 | - if ( defined( 'T_ENUM' ) ) { | |
| 426 | - $kinds[ constant( 'T_ENUM' ) ] = 'enum'; | |
| 427 | - } | |
| 428 | - | |
| 429 | - return $kinds; | |
| 430 | - } | |
| 431 | - | |
| 432 | - /** | |
| 433 | - * Check whether a class, interface, trait or enum with this name is already loaded. | |
| 434 | - * | |
| 435 | - * Autoloading is skipped: eval() only fails on names that are already declared. | |
| 436 | - * | |
| 437 | - * @param string $name Fully qualified name. | |
| 438 | - * @return bool | |
| 439 | - */ | |
| 440 | - private static function is_class_name_in_use( string $name ): bool { | |
| 441 | - return class_exists( $name, false ) || interface_exists( $name, false ) || trait_exists( $name, false ); | |
| 442 | - } | |
| 443 | - | |
| 444 | - /** | |
| 445 | - * Check whether a token is whitespace or a comment. | |
| 446 | - * | |
| 447 | - * @param array<int, int|string>|string $token Token. | |
| 448 | - * @return bool | |
| 449 | - */ | |
| 450 | - private static function is_insignificant( $token ): bool { | |
| 451 | - return is_array( $token ) && in_array( $token[0], [ T_WHITESPACE, T_COMMENT, T_DOC_COMMENT ], true ); | |
| 452 | - } | |
| 453 | - | |
| 454 | - /** | |
| 455 | - * Check whether a declaration keyword only refers to a name, as in Example::class or use function example;. | |
| 456 | - * | |
| 457 | - * @param array<int, array<int, int|string>|string> $tokens Token stream. | |
| 458 | - * @param int $index Keyword token index. | |
| 459 | - * @return bool | |
| 460 | - */ | |
| 461 | - private static function is_name_reference( array $tokens, int $index ): bool { | |
| 462 | - $preceding = [ T_DOUBLE_COLON, T_OBJECT_OPERATOR, T_USE ]; | |
| 463 | - | |
| 464 | - if ( defined( 'T_NULLSAFE_OBJECT_OPERATOR' ) ) { | |
| 465 | - $preceding[] = constant( 'T_NULLSAFE_OBJECT_OPERATOR' ); | |
| 466 | - } | |
| 467 | - | |
| 468 | - for ( $previous = $index - 1; $previous >= 0; --$previous ) { | |
| 469 | - if ( ! self::is_insignificant( $tokens[ $previous ] ) ) { | |
| 470 | - return is_array( $tokens[ $previous ] ) && in_array( $tokens[ $previous ][0], $preceding, true ); | |
| 471 | - } | |
| 472 | - } | |
| 473 | - | |
| 474 | - return false; | |
| 475 | - } | |
| 476 | - | |
| 477 | - /** | |
| 478 | - * Check whether a control structure uses the alternative syntax, as in if ( ... ): ... endif;. | |
| 479 | - * | |
| 480 | - * @param array<int, array<int, int|string>|string> $tokens Token stream. | |
| 481 | - * @param int $index Control structure token index. | |
| 482 | - * @return bool | |
| 483 | - */ | |
| 484 | - private static function opens_alternative_block( array $tokens, int $index ): bool { | |
| 485 | - $token_count = count( $tokens ); | |
| 486 | - $depth = 0; | |
| 487 | - | |
| 488 | - for ( $current = $index + 1; $current < $token_count; ++$current ) { | |
| 489 | - $token = $tokens[ $current ]; | |
| 490 | - | |
| 491 | - if ( self::is_insignificant( $token ) ) { | |
| 492 | - continue; | |
| 493 | - } | |
| 494 | - | |
| 495 | - if ( '(' === $token ) { | |
| 496 | - ++$depth; | |
| 497 | - continue; | |
| 498 | - } | |
| 499 | - | |
| 500 | - if ( 0 === $depth ) { | |
| 501 | - return false; | |
| 502 | - } | |
| 503 | - | |
| 504 | - if ( ')' === $token && 0 === --$depth ) { | |
| 505 | - for ( $next = $current + 1; $next < $token_count; ++$next ) { | |
| 506 | - if ( ! self::is_insignificant( $tokens[ $next ] ) ) { | |
| 507 | - return ':' === $tokens[ $next ]; | |
| 508 | - } | |
| 509 | - } | |
| 510 | - | |
| 511 | - return false; | |
| 512 | - } | |
| 513 | - } | |
| 514 | - | |
| 515 | - return false; | |
| 516 | - } | |
| 517 | - | |
| 518 | - /** | |
| 519 | 222 | * Check whether a token can be part of a namespace name. |
| 520 | 223 | * |
| 521 | 224 | * @param int $token_id Token identifier. |
| 522 | 225 | * @return bool |
| @@ -533,22 +236,22 @@ | ||
| 533 | 236 | return in_array( $token_id, $name_tokens, true ); |
| 534 | 237 | } |
| 535 | 238 | |
| 536 | 239 | /** |
| 537 | - * Get the name following a declaration token, ignoring whitespace and references. | |
| 240 | + * Get the name following a function token, ignoring whitespace and references. | |
| 538 | 241 | * |
| 539 | 242 | * @param array<int, array<int, int|string>|string> $tokens Token stream. |
| 540 | - * @param int $declaration_index Declaration token index. | |
| 541 | - * @return string|null Null for closures, anonymous classes and other unnamed uses. | |
| 243 | + * @param int $function_index Function token index. | |
| 244 | + * @return string|null | |
| 542 | 245 | */ |
| 543 | - private static function get_declared_name( $tokens, $declaration_index ) { | |
| 246 | + private static function get_function_name( $tokens, $function_index ) { | |
| 544 | 247 | $token_count = count( $tokens ); |
| 545 | 248 | |
| 546 | - for ( $index = $declaration_index + 1; $index < $token_count; ++$index ) { | |
| 249 | + for ( $index = $function_index + 1; $index < $token_count; ++$index ) { | |
| 547 | 250 | $token = $tokens[ $index ]; |
| 548 | 251 | |
| 549 | 252 | if ( is_array( $token ) ) { |
| 550 | - if ( self::is_insignificant( $token ) || '&' === $token[1] ) { | |
| 253 | + if ( in_array( $token[0], [ T_WHITESPACE, T_COMMENT, T_DOC_COMMENT ], true ) || '&' === $token[1] ) { | |
| 551 | 254 | continue; |
| 552 | 255 | } |
| 553 | 256 | |
| 554 | 257 | return T_STRING === $token[0] ? (string) $token[1] : null; |