PluginProbe
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings / 5.8.4
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings v5.8.4
7.2.2 7.2.1 7.2 7.1.2 7.1.1 7.1 7.0.4 7.0.6 7.0.7 6.3.8 6.3.7 6.3.6 6.3.5 6.3.4 6.3.3 6.3.1 trunk 5.7.3 5.7.5 5.8.1 5.8.2 5.8.3 5.8.4 5.8.6 6.0.4 All 37 releases
mlsimport / vendor / phpcsstandards / phpcsutils / PHPCSUtils / Utils / FunctionDeclarations.php

FunctionDeclarations.php in MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings 5.8.4, at vendor/phpcsstandards/phpcsutils/PHPCSUtils/Utils/FunctionDeclarations.php

810 lines 33.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * PHPCSUtils, utility functions and classes for PHP_CodeSniffer sniff developers.
4 *
5 * @package PHPCSUtils
6 * @copyright 2019-2020 PHPCSUtils Contributors
7 * @license https://opensource.org/licenses/LGPL-3.0 LGPL3
8 * @link https://github.com/PHPCSStandards/PHPCSUtils
9 */
10
11 namespace PHPCSUtils\Utils;
12
13 use PHP_CodeSniffer\Exceptions\RuntimeException;
14 use PHP_CodeSniffer\Files\File;
15 use PHP_CodeSniffer\Util\Tokens;
16 use PHPCSUtils\Internal\Cache;
17 use PHPCSUtils\Tokens\Collections;
18 use PHPCSUtils\Utils\GetTokensAsString;
19 use PHPCSUtils\Utils\ObjectDeclarations;
20 use PHPCSUtils\Utils\Scopes;
21 use PHPCSUtils\Utils\UseStatements;
22
23 /**
24 * Utility functions for use when examining function declaration statements.
25 *
26 * @since 1.0.0 The `FunctionDeclarations::getProperties()` and the
27 * `FunctionDeclarations::getParameters()` methods are based on and
28 * inspired by respectively the `getMethodProperties()`
29 * and `getMethodParameters()` methods in the PHPCS native
30 * `PHP_CodeSniffer\Files\File` class.
31 * Also see {@see \PHPCSUtils\BackCompat\BCFile}.
32 */
33 final class FunctionDeclarations
34 {
35
36 /**
37 * A list of all PHP magic functions.
38 *
39 * The array keys contain the function names. The values contain the name without the double underscore.
40 *
41 * The function names are listed in lowercase as these function names in PHP are case-insensitive
42 * and comparisons against this list should therefore always be done in a case-insensitive manner.
43 *
44 * @since 1.0.0
45 *
46 * @var array<string, string>
47 */
48 public static $magicFunctions = [
49 '__autoload' => 'autoload',
50 ];
51
52 /**
53 * A list of all PHP magic methods.
54 *
55 * The array keys contain the method names. The values contain the name without the double underscore.
56 *
57 * The method names are listed in lowercase as these method names in PHP are case-insensitive
58 * and comparisons against this list should therefore always be done in a case-insensitive manner.
59 *
60 * @since 1.0.0
61 *
62 * @var array<string, string>
63 */
64 public static $magicMethods = [
65 '__construct' => 'construct',
66 '__destruct' => 'destruct',
67 '__call' => 'call',
68 '__callstatic' => 'callstatic',
69 '__get' => 'get',
70 '__set' => 'set',
71 '__isset' => 'isset',
72 '__unset' => 'unset',
73 '__sleep' => 'sleep',
74 '__wakeup' => 'wakeup',
75 '__tostring' => 'tostring',
76 '__set_state' => 'set_state',
77 '__clone' => 'clone',
78 '__invoke' => 'invoke',
79 '__debuginfo' => 'debuginfo', // PHP >= 5.6.
80 '__serialize' => 'serialize', // PHP >= 7.4.
81 '__unserialize' => 'unserialize', // PHP >= 7.4.
82 ];
83
84 /**
85 * A list of all PHP native non-magic methods starting with a double underscore.
86 *
87 * These come from PHP modules such as SOAPClient.
88 *
89 * The array keys are the method names, the values the name of the PHP class containing
90 * the function.
91 *
92 * The method names are listed in lowercase as function names in PHP are case-insensitive
93 * and comparisons against this list should therefore always be done in a case-insensitive manner.
94 *
95 * @since 1.0.0
96 *
97 * @var array<string, string>
98 */
99 public static $methodsDoubleUnderscore = [
100 '__dorequest' => 'SOAPClient',
101 '__getcookies' => 'SOAPClient',
102 '__getfunctions' => 'SOAPClient',
103 '__getlastrequest' => 'SOAPClient',
104 '__getlastrequestheaders' => 'SOAPClient',
105 '__getlastresponse' => 'SOAPClient',
106 '__getlastresponseheaders' => 'SOAPClient',
107 '__gettypes' => 'SOAPClient',
108 '__setcookie' => 'SOAPClient',
109 '__setlocation' => 'SOAPClient',
110 '__setsoapheaders' => 'SOAPClient',
111 '__soapcall' => 'SOAPClient',
112 ];
113
114 /**
115 * Returns the declaration name for a function.
116 *
117 * Alias for the {@see \PHPCSUtils\Utils\ObjectDeclarations::getName()} method.
118 *
119 * @see \PHPCSUtils\BackCompat\BCFile::getDeclarationName() Original function.
120 * @see \PHPCSUtils\Utils\ObjectDeclarations::getName() PHPCSUtils native improved version.
121 *
122 * @since 1.0.0
123 *
124 * @codeCoverageIgnore
125 *
126 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
127 * @param int $stackPtr The position of the function keyword token.
128 *
129 * @return string|null The name of the function; or `NULL` if the passed token doesn't exist,
130 * the function is anonymous or in case of a parse error/live coding.
131 *
132 * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified token is not of type
133 * `T_FUNCTION`.
134 */
135 public static function getName(File $phpcsFile, $stackPtr)
136 {
137 return ObjectDeclarations::getName($phpcsFile, $stackPtr);
138 }
139
140 /**
141 * Retrieves the visibility and implementation properties of a method.
142 *
143 * Main differences with the PHPCS version:
144 * - Bugs fixed:
145 * - Handling of PHPCS annotations.
146 * - `"has_body"` index could be set to `true` for functions without body in the case of
147 * parse errors or live coding.
148 * - Defensive coding against incorrect calls to this method.
149 * - More efficient checking whether a function has a body.
150 * - Support for PHP 8.0 identifier name tokens in return types, cross-version PHP & PHPCS.
151 * - The results of this function call are cached during a PHPCS run for faster response times.
152 *
153 * @see \PHP_CodeSniffer\Files\File::getMethodProperties() Original source.
154 * @see \PHPCSUtils\BackCompat\BCFile::getMethodProperties() Cross-version compatible version of the original.
155 *
156 * @since 1.0.0
157 *
158 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
159 * @param int $stackPtr The position in the stack of the function token to
160 * acquire the properties for.
161 *
162 * @return array<string, mixed> Array with information about a function declaration.
163 * The format of the return value is:
164 * ```php
165 * array(
166 * 'scope' => string, // Public, private, or protected
167 * 'scope_specified' => bool, // TRUE if the scope keyword was found.
168 * 'return_type' => string, // The return type of the method.
169 * 'return_type_token' => int|false, // The stack pointer to the start of the return type
170 * // or FALSE if there is no return type.
171 * 'return_type_end_token' => int|false, // The stack pointer to the end of the return type
172 * // or FALSE if there is no return type.
173 * 'nullable_return_type' => bool, // TRUE if the return type is preceded
174 * // by the nullability operator.
175 * 'is_abstract' => bool, // TRUE if the abstract keyword was found.
176 * 'is_final' => bool, // TRUE if the final keyword was found.
177 * 'is_static' => bool, // TRUE if the static keyword was found.
178 * 'has_body' => bool, // TRUE if the method has a body
179 * );
180 * ```
181 *
182 * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a T_FUNCTION
183 * or T_CLOSURE token, nor an arrow function.
184 */
185 public static function getProperties(File $phpcsFile, $stackPtr)
186 {
187 $tokens = $phpcsFile->getTokens();
188
189 if (isset($tokens[$stackPtr]) === false
190 || isset(Collections::functionDeclarationTokens()[$tokens[$stackPtr]['code']]) === false
191 ) {
192 throw new RuntimeException('$stackPtr must be of type T_FUNCTION or T_CLOSURE or an arrow function');
193 }
194
195 if (Cache::isCached($phpcsFile, __METHOD__, $stackPtr) === true) {
196 return Cache::get($phpcsFile, __METHOD__, $stackPtr);
197 }
198
199 if ($tokens[$stackPtr]['code'] === \T_FUNCTION) {
200 $valid = Tokens::$methodPrefixes;
201 } else {
202 $valid = [\T_STATIC => \T_STATIC];
203 }
204
205 $valid += Tokens::$emptyTokens;
206
207 $scope = 'public';
208 $scopeSpecified = false;
209 $isAbstract = false;
210 $isFinal = false;
211 $isStatic = false;
212
213 for ($i = ($stackPtr - 1); $i > 0; $i--) {
214 if (isset($valid[$tokens[$i]['code']]) === false) {
215 break;
216 }
217
218 switch ($tokens[$i]['code']) {
219 case \T_PUBLIC:
220 $scope = 'public';
221 $scopeSpecified = true;
222 break;
223 case \T_PRIVATE:
224 $scope = 'private';
225 $scopeSpecified = true;
226 break;
227 case \T_PROTECTED:
228 $scope = 'protected';
229 $scopeSpecified = true;
230 break;
231 case \T_ABSTRACT:
232 $isAbstract = true;
233 break;
234 case \T_FINAL:
235 $isFinal = true;
236 break;
237 case \T_STATIC:
238 $isStatic = true;
239 break;
240 }
241 }
242
243 $returnType = '';
244 $returnTypeToken = false;
245 $returnTypeEndToken = false;
246 $nullableReturnType = false;
247 $hasBody = false;
248 $returnTypeTokens = Collections::returnTypeTokens();
249
250 $parenthesisCloser = null;
251 if (isset($tokens[$stackPtr]['parenthesis_closer']) === true) {
252 $parenthesisCloser = $tokens[$stackPtr]['parenthesis_closer'];
253 }
254
255 if (isset($parenthesisCloser) === true) {
256 $scopeOpener = null;
257 if (isset($tokens[$stackPtr]['scope_opener']) === true) {
258 $scopeOpener = $tokens[$stackPtr]['scope_opener'];
259 }
260
261 for ($i = $parenthesisCloser; $i < $phpcsFile->numTokens; $i++) {
262 if ($i === $scopeOpener) {
263 // End of function definition.
264 $hasBody = true;
265 break;
266 }
267
268 if ($scopeOpener === null && $tokens[$i]['code'] === \T_SEMICOLON) {
269 // End of abstract/interface function definition.
270 break;
271 }
272
273 if ($tokens[$i]['code'] === \T_NULLABLE) {
274 $nullableReturnType = true;
275 }
276
277 if (isset($returnTypeTokens[$tokens[$i]['code']]) === true) {
278 if ($returnTypeToken === false) {
279 $returnTypeToken = $i;
280 }
281
282 $returnType .= $tokens[$i]['content'];
283 $returnTypeEndToken = $i;
284 }
285 }
286 }
287
288 if ($returnType !== '' && $nullableReturnType === true) {
289 $returnType = '?' . $returnType;
290 }
291
292 $returnValue = [
293 'scope' => $scope,
294 'scope_specified' => $scopeSpecified,
295 'return_type' => $returnType,
296 'return_type_token' => $returnTypeToken,
297 'return_type_end_token' => $returnTypeEndToken,
298 'nullable_return_type' => $nullableReturnType,
299 'is_abstract' => $isAbstract,
300 'is_final' => $isFinal,
301 'is_static' => $isStatic,
302 'has_body' => $hasBody,
303 ];
304
305 Cache::set($phpcsFile, __METHOD__, $stackPtr, $returnValue);
306 return $returnValue;
307 }
308
309 /**
310 * Retrieves the method parameters for the specified function token.
311 *
312 * Also supports passing in a `T_USE` token for a closure use group.
313 *
314 * The returned array will contain the following information for each parameter:
315 *
316 * ```php
317 * 0 => array(
318 * 'name' => string, // The variable name.
319 * 'token' => int, // The stack pointer to the variable name.
320 * 'content' => string, // The full content of the variable definition.
321 * 'has_attributes' => bool, // Does the parameter have one or more attributes attached ?
322 * 'pass_by_reference' => bool, // Is the variable passed by reference?
323 * 'reference_token' => int|false, // The stack pointer to the reference operator
324 * // or FALSE if the param is not passed by reference.
325 * 'variable_length' => bool, // Is the param of variable length through use of `...` ?
326 * 'variadic_token' => int|false, // The stack pointer to the ... operator
327 * // or FALSE if the param is not variable length.
328 * 'type_hint' => string, // The type hint for the variable.
329 * 'type_hint_token' => int|false, // The stack pointer to the start of the type hint
330 * // or FALSE if there is no type hint.
331 * 'type_hint_end_token' => int|false, // The stack pointer to the end of the type hint
332 * // or FALSE if there is no type hint.
333 * 'nullable_type' => bool, // TRUE if the var type is preceded by the nullability
334 * // operator.
335 * 'comma_token' => int|false, // The stack pointer to the comma after the param
336 * // or FALSE if this is the last param.
337 * )
338 * ```
339 *
340 * Parameters with default values have the following additional array indexes:
341 * ```php
342 * 'default' => string, // The full content of the default value.
343 * 'default_token' => int, // The stack pointer to the start of the default value.
344 * 'default_equal_token' => int, // The stack pointer to the equals sign.
345 * ```
346 *
347 * Parameters declared using PHP 8 constructor property promotion, have these additional array indexes:
348 * ```php
349 * 'property_visibility' => string, // The property visibility as declared.
350 * 'visibility_token' => int|false, // The stack pointer to the visibility modifier token.
351 * // or FALSE if the visibility is not explicitly declared.
352 * 'property_readonly' => bool, // TRUE if the readonly keyword was found.
353 * 'readonly_token' => int, // The stack pointer to the readonly modifier token.
354 * // This index will only be set if the property is readonly.
355 * ```
356 *
357 * Main differences with the PHPCS version:
358 * - Defensive coding against incorrect calls to this method.
359 * - More efficient and more stable checking whether a `T_USE` token is a closure use.
360 * - More efficient and more stable looping of the default value.
361 * - Clearer exception message when a non-closure use token was passed to the function.
362 * - Support for PHP 8.0 identifier name tokens in parameter types, cross-version PHP & PHPCS.
363 * - The results of this function call are cached during a PHPCS run for faster response times.
364 *
365 * @see \PHP_CodeSniffer\Files\File::getMethodParameters() Original source.
366 * @see \PHPCSUtils\BackCompat\BCFile::getMethodParameters() Cross-version compatible version of the original.
367 *
368 * @since 1.0.0
369 *
370 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
371 * @param int $stackPtr The position in the stack of the function token
372 * to acquire the parameters for.
373 *
374 * @return array<int, array<string, mixed>>
375 *
376 * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified $stackPtr is not of
377 * type `T_FUNCTION`, `T_CLOSURE` or `T_USE`,
378 * nor an arrow function.
379 * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If a passed `T_USE` token is not a closure
380 * use token.
381 */
382 public static function getParameters(File $phpcsFile, $stackPtr)
383 {
384 $tokens = $phpcsFile->getTokens();
385
386 if (isset($tokens[$stackPtr]) === false
387 || (isset(Collections::functionDeclarationTokens()[$tokens[$stackPtr]['code']]) === false
388 && $tokens[$stackPtr]['code'] !== \T_USE)
389 ) {
390 throw new RuntimeException('$stackPtr must be of type T_FUNCTION, T_CLOSURE or T_USE or an arrow function');
391 }
392
393 if ($tokens[$stackPtr]['code'] === \T_USE) {
394 // This will work PHPCS 3.x/4.x cross-version without much overhead.
395 $opener = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
396 if ($opener === false
397 || $tokens[$opener]['code'] !== \T_OPEN_PARENTHESIS
398 || UseStatements::isClosureUse($phpcsFile, $stackPtr) === false
399 ) {
400 throw new RuntimeException('$stackPtr was not a valid closure T_USE');
401 }
402 } else {
403 if (isset($tokens[$stackPtr]['parenthesis_opener']) === false) {
404 // Live coding or syntax error, so no params to find.
405 return [];
406 }
407
408 $opener = $tokens[$stackPtr]['parenthesis_opener'];
409 }
410
411 if (isset($tokens[$opener]['parenthesis_closer']) === false) {
412 // Live coding or syntax error, so no params to find.
413 return [];
414 }
415
416 if (Cache::isCached($phpcsFile, __METHOD__, $stackPtr) === true) {
417 return Cache::get($phpcsFile, __METHOD__, $stackPtr);
418 }
419
420 $closer = $tokens[$opener]['parenthesis_closer'];
421
422 $vars = [];
423 $currVar = null;
424 $paramStart = ($opener + 1);
425 $defaultStart = null;
426 $equalToken = null;
427 $paramCount = 0;
428 $hasAttributes = false;
429 $passByReference = false;
430 $referenceToken = false;
431 $variableLength = false;
432 $variadicToken = false;
433 $typeHint = '';
434 $typeHintToken = false;
435 $typeHintEndToken = false;
436 $nullableType = false;
437 $visibilityToken = null;
438 $readonlyToken = null;
439
440 $parameterTypeTokens = Collections::parameterTypeTokens();
441
442 for ($i = $paramStart; $i <= $closer; $i++) {
443 if (isset($parameterTypeTokens[$tokens[$i]['code']]) === true
444 /*
445 * Self and parent are valid, static invalid, but was probably intended as type declaration.
446 * Note: constructor property promotion does not support static properties, so this should
447 * still be a valid assumption.
448 */
449 || $tokens[$i]['code'] === \T_STATIC
450 ) {
451 if ($typeHintToken === false) {
452 $typeHintToken = $i;
453 }
454
455 $typeHint .= $tokens[$i]['content'];
456 $typeHintEndToken = $i;
457 continue;
458 }
459
460 switch ($tokens[$i]['code']) {
461 case \T_ATTRIBUTE:
462 $hasAttributes = true;
463
464 // Skip to the end of the attribute.
465 $i = $tokens[$i]['attribute_closer'];
466 break;
467
468 case \T_BITWISE_AND:
469 $passByReference = true;
470 $referenceToken = $i;
471 break;
472
473 case \T_VARIABLE:
474 $currVar = $i;
475 break;
476
477 case \T_ELLIPSIS:
478 $variableLength = true;
479 $variadicToken = $i;
480 break;
481
482 case \T_NULLABLE:
483 $nullableType = true;
484 $typeHint .= $tokens[$i]['content'];
485 $typeHintEndToken = $i;
486 break;
487
488 case \T_PUBLIC:
489 case \T_PROTECTED:
490 case \T_PRIVATE:
491 $visibilityToken = $i;
492 break;
493
494 case \T_READONLY:
495 $readonlyToken = $i;
496 break;
497
498 case \T_CLOSE_PARENTHESIS:
499 case \T_COMMA:
500 // If it's null, then there must be no parameters for this
501 // method.
502 if ($currVar === null) {
503 continue 2;
504 }
505
506 $vars[$paramCount] = [];
507 $vars[$paramCount]['token'] = $currVar;
508 $vars[$paramCount]['name'] = $tokens[$currVar]['content'];
509 $vars[$paramCount]['content'] = \trim(
510 GetTokensAsString::normal($phpcsFile, $paramStart, ($i - 1))
511 );
512
513 if ($defaultStart !== null) {
514 $vars[$paramCount]['default'] = \trim(
515 GetTokensAsString::normal($phpcsFile, $defaultStart, ($i - 1))
516 );
517 $vars[$paramCount]['default_token'] = $defaultStart;
518 $vars[$paramCount]['default_equal_token'] = $equalToken;
519 }
520
521 $vars[$paramCount]['has_attributes'] = $hasAttributes;
522 $vars[$paramCount]['pass_by_reference'] = $passByReference;
523 $vars[$paramCount]['reference_token'] = $referenceToken;
524 $vars[$paramCount]['variable_length'] = $variableLength;
525 $vars[$paramCount]['variadic_token'] = $variadicToken;
526 $vars[$paramCount]['type_hint'] = $typeHint;
527 $vars[$paramCount]['type_hint_token'] = $typeHintToken;
528 $vars[$paramCount]['type_hint_end_token'] = $typeHintEndToken;
529 $vars[$paramCount]['nullable_type'] = $nullableType;
530
531 if ($visibilityToken !== null || $readonlyToken !== null) {
532 $vars[$paramCount]['property_visibility'] = 'public';
533 $vars[$paramCount]['visibility_token'] = false;
534 $vars[$paramCount]['property_readonly'] = false;
535
536 if ($visibilityToken !== null) {
537 $vars[$paramCount]['property_visibility'] = $tokens[$visibilityToken]['content'];
538 $vars[$paramCount]['visibility_token'] = $visibilityToken;
539 }
540
541 if ($readonlyToken !== null) {
542 $vars[$paramCount]['property_readonly'] = true;
543 $vars[$paramCount]['readonly_token'] = $readonlyToken;
544 }
545 }
546
547 if ($tokens[$i]['code'] === \T_COMMA) {
548 $vars[$paramCount]['comma_token'] = $i;
549 } else {
550 $vars[$paramCount]['comma_token'] = false;
551 }
552
553 // Reset the vars, as we are about to process the next parameter.
554 $currVar = null;
555 $paramStart = ($i + 1);
556 $defaultStart = null;
557 $equalToken = null;
558 $hasAttributes = false;
559 $passByReference = false;
560 $referenceToken = false;
561 $variableLength = false;
562 $variadicToken = false;
563 $typeHint = '';
564 $typeHintToken = false;
565 $typeHintEndToken = false;
566 $nullableType = false;
567 $visibilityToken = null;
568 $readonlyToken = null;
569
570 ++$paramCount;
571 break;
572
573 case \T_EQUAL:
574 $defaultStart = $phpcsFile->findNext(Tokens::$emptyTokens, ($i + 1), null, true);
575 $equalToken = $i;
576
577 // Skip past everything in the default value before going into the next switch loop.
578 for ($j = ($i + 1); $j <= $closer; $j++) {
579 // Skip past array()'s et al as default values.
580 if (isset($tokens[$j]['parenthesis_opener'], $tokens[$j]['parenthesis_closer'])) {
581 $j = $tokens[$j]['parenthesis_closer'];
582
583 if ($j === $closer) {
584 // Found the end of the parameter.
585 break;
586 }
587
588 continue;
589 }
590
591 // Skip past short arrays et al as default values.
592 if (isset($tokens[$j]['bracket_opener'])) {
593 $j = $tokens[$j]['bracket_closer'];
594 continue;
595 }
596
597 if ($tokens[$j]['code'] === \T_COMMA) {
598 break;
599 }
600 }
601
602 $i = ($j - 1);
603 break;
604 }
605 }
606
607 Cache::set($phpcsFile, __METHOD__, $stackPtr, $vars);
608 return $vars;
609 }
610
611 /**
612 * Checks if a given function is a PHP magic function.
613 *
614 * @todo Add check for the function declaration being namespaced!
615 *
616 * @see \PHPCSUtils\Utils\FunctionDeclaration::$magicFunctions List of names of magic functions.
617 * @see \PHPCSUtils\Utils\FunctionDeclaration::isMagicFunctionName() For when you already know the name of the
618 * function and scope checking is done in the
619 * sniff.
620 *
621 * @since 1.0.0
622 *
623 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
624 * @param int $stackPtr The `T_FUNCTION` token to check.
625 *
626 * @return bool
627 */
628 public static function isMagicFunction(File $phpcsFile, $stackPtr)
629 {
630 $tokens = $phpcsFile->getTokens();
631 if (isset($tokens[$stackPtr]) === false || $tokens[$stackPtr]['code'] !== \T_FUNCTION) {
632 return false;
633 }
634
635 if (Scopes::isOOMethod($phpcsFile, $stackPtr) === true) {
636 return false;
637 }
638
639 $name = self::getName($phpcsFile, $stackPtr);
640 return self::isMagicFunctionName($name);
641 }
642
643 /**
644 * Verify if a given function name is the name of a PHP magic function.
645 *
646 * @see \PHPCSUtils\Utils\FunctionDeclaration::$magicFunctions List of names of magic functions.
647 *
648 * @since 1.0.0
649 *
650 * @param string $name The full function name.
651 *
652 * @return bool
653 */
654 public static function isMagicFunctionName($name)
655 {
656 $name = \strtolower($name);
657 return (isset(self::$magicFunctions[$name]) === true);
658 }
659
660 /**
661 * Checks if a given function is a PHP magic method.
662 *
663 * @see \PHPCSUtils\Utils\FunctionDeclaration::$magicMethods List of names of magic methods.
664 * @see \PHPCSUtils\Utils\FunctionDeclaration::isMagicMethodName() For when you already know the name of the
665 * method and scope checking is done in the
666 * sniff.
667 *
668 * @since 1.0.0
669 *
670 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
671 * @param int $stackPtr The `T_FUNCTION` token to check.
672 *
673 * @return bool
674 */
675 public static function isMagicMethod(File $phpcsFile, $stackPtr)
676 {
677 if (Scopes::isOOMethod($phpcsFile, $stackPtr) === false) {
678 return false;
679 }
680
681 $name = self::getName($phpcsFile, $stackPtr);
682 return self::isMagicMethodName($name);
683 }
684
685 /**
686 * Verify if a given function name is the name of a PHP magic method.
687 *
688 * @see \PHPCSUtils\Utils\FunctionDeclaration::$magicMethods List of names of magic methods.
689 *
690 * @since 1.0.0
691 *
692 * @param string $name The full function name.
693 *
694 * @return bool
695 */
696 public static function isMagicMethodName($name)
697 {
698 $name = \strtolower($name);
699 return (isset(self::$magicMethods[$name]) === true);
700 }
701
702 /**
703 * Checks if a given function is a non-magic PHP native double underscore method.
704 *
705 * @see \PHPCSUtils\Utils\FunctionDeclaration::$methodsDoubleUnderscore List of the PHP native non-magic
706 * double underscore method names.
707 * @see \PHPCSUtils\Utils\FunctionDeclaration::isPHPDoubleUnderscoreMethodName() For when you already know the
708 * name of the method and scope
709 * checking is done in the sniff.
710 *
711 * @since 1.0.0
712 *
713 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
714 * @param int $stackPtr The `T_FUNCTION` token to check.
715 *
716 * @return bool
717 */
718 public static function isPHPDoubleUnderscoreMethod(File $phpcsFile, $stackPtr)
719 {
720 $tokens = $phpcsFile->getTokens();
721 if (isset($tokens[$stackPtr]) === false || $tokens[$stackPtr]['code'] !== \T_FUNCTION) {
722 return false;
723 }
724
725 $scopePtr = Scopes::validDirectScope($phpcsFile, $stackPtr, Tokens::$ooScopeTokens);
726 if ($scopePtr === false) {
727 return false;
728 }
729
730 /*
731 * If this is a class, make sure it extends something, as otherwise, the methods
732 * still can't be overloads for the SOAPClient methods.
733 * For a trait/interface we don't know the concrete implementation context, so skip
734 * this check.
735 */
736 if ($tokens[$scopePtr]['code'] === \T_CLASS || $tokens[$scopePtr]['code'] === \T_ANON_CLASS) {
737 $extends = ObjectDeclarations::findExtendedClassName($phpcsFile, $scopePtr);
738 if ($extends === false) {
739 return false;
740 }
741 }
742
743 $name = self::getName($phpcsFile, $stackPtr);
744 return self::isPHPDoubleUnderscoreMethodName($name);
745 }
746
747 /**
748 * Verify if a given function name is the name of a non-magic PHP native double underscore method.
749 *
750 * @see \PHPCSUtils\Utils\FunctionDeclaration::$methodsDoubleUnderscore List of the PHP native non-magic
751 * double underscore method names.
752 *
753 * @since 1.0.0
754 *
755 * @param string $name The full function name.
756 *
757 * @return bool
758 */
759 public static function isPHPDoubleUnderscoreMethodName($name)
760 {
761 $name = \strtolower($name);
762 return (isset(self::$methodsDoubleUnderscore[$name]) === true);
763 }
764
765 /**
766 * Checks if a given function is a magic method or a PHP native double underscore method.
767 *
768 * {@internal Not the most efficient way of checking this, but less efficient ways will get
769 * less reliable results or introduce a lot of code duplication.}
770 *
771 * @see \PHPCSUtils\Utils\FunctionDeclaration::isSpecialMethodName() For when you already know the name of the
772 * method and scope checking is done in the
773 * sniff.
774 *
775 * @since 1.0.0
776 *
777 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
778 * @param int $stackPtr The `T_FUNCTION` token to check.
779 *
780 * @return bool
781 */
782 public static function isSpecialMethod(File $phpcsFile, $stackPtr)
783 {
784 if (self::isMagicMethod($phpcsFile, $stackPtr) === true) {
785 return true;
786 }
787
788 if (self::isPHPDoubleUnderscoreMethod($phpcsFile, $stackPtr) === true) {
789 return true;
790 }
791
792 return false;
793 }
794
795 /**
796 * Verify if a given function name is the name of a magic method or a PHP native double underscore method.
797 *
798 * @since 1.0.0
799 *
800 * @param string $name The full function name.
801 *
802 * @return bool
803 */
804 public static function isSpecialMethodName($name)
805 {
806 $name = \strtolower($name);
807 return (isset(self::$magicMethods[$name]) === true || isset(self::$methodsDoubleUnderscore[$name]) === true);
808 }
809 }
810