wishthis/vendor/phpcsstandards/phpcsutils/PHPCSUtils/Utils/FunctionDeclarations.php
2023-09-20 13:52:46 +02:00

823 lines
34 KiB
PHP

<?php
/**
* PHPCSUtils, utility functions and classes for PHP_CodeSniffer sniff developers.
*
* @package PHPCSUtils
* @copyright 2019-2020 PHPCSUtils Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSUtils
*/
namespace PHPCSUtils\Utils;
use PHP_CodeSniffer\Exceptions\RuntimeException;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Tokens;
use PHPCSUtils\Internal\Cache;
use PHPCSUtils\Tokens\Collections;
use PHPCSUtils\Utils\GetTokensAsString;
use PHPCSUtils\Utils\ObjectDeclarations;
use PHPCSUtils\Utils\Scopes;
use PHPCSUtils\Utils\UseStatements;
/**
* Utility functions for use when examining function declaration statements.
*
* @since 1.0.0 The `FunctionDeclarations::getProperties()` and the
* `FunctionDeclarations::getParameters()` methods are based on and
* inspired by respectively the `getMethodProperties()`
* and `getMethodParameters()` methods in the PHPCS native
* `PHP_CodeSniffer\Files\File` class.
* Also see {@see \PHPCSUtils\BackCompat\BCFile}.
*/
final class FunctionDeclarations
{
/**
* A list of all PHP magic functions.
*
* The array keys contain the function names. The values contain the name without the double underscore.
*
* The function names are listed in lowercase as these function names in PHP are case-insensitive
* and comparisons against this list should therefore always be done in a case-insensitive manner.
*
* @since 1.0.0
*
* @var array <string> => <string>
*/
public static $magicFunctions = [
'__autoload' => 'autoload',
];
/**
* A list of all PHP magic methods.
*
* The array keys contain the method names. The values contain the name without the double underscore.
*
* The method names are listed in lowercase as these method names in PHP are case-insensitive
* and comparisons against this list should therefore always be done in a case-insensitive manner.
*
* @since 1.0.0
*
* @var array <string> => <string>
*/
public static $magicMethods = [
'__construct' => 'construct',
'__destruct' => 'destruct',
'__call' => 'call',
'__callstatic' => 'callstatic',
'__get' => 'get',
'__set' => 'set',
'__isset' => 'isset',
'__unset' => 'unset',
'__sleep' => 'sleep',
'__wakeup' => 'wakeup',
'__tostring' => 'tostring',
'__set_state' => 'set_state',
'__clone' => 'clone',
'__invoke' => 'invoke',
'__debuginfo' => 'debuginfo', // PHP >= 5.6.
'__serialize' => 'serialize', // PHP >= 7.4.
'__unserialize' => 'unserialize', // PHP >= 7.4.
];
/**
* A list of all PHP native non-magic methods starting with a double underscore.
*
* These come from PHP modules such as SOAPClient.
*
* The array keys are the method names, the values the name of the PHP class containing
* the function.
*
* The method names are listed in lowercase as function names in PHP are case-insensitive
* and comparisons against this list should therefore always be done in a case-insensitive manner.
*
* @since 1.0.0
*
* @var array <string> => <string>
*/
public static $methodsDoubleUnderscore = [
'__dorequest' => 'SOAPClient',
'__getcookies' => 'SOAPClient',
'__getfunctions' => 'SOAPClient',
'__getlastrequest' => 'SOAPClient',
'__getlastrequestheaders' => 'SOAPClient',
'__getlastresponse' => 'SOAPClient',
'__getlastresponseheaders' => 'SOAPClient',
'__gettypes' => 'SOAPClient',
'__setcookie' => 'SOAPClient',
'__setlocation' => 'SOAPClient',
'__setsoapheaders' => 'SOAPClient',
'__soapcall' => 'SOAPClient',
];
/**
* Returns the declaration name for a function.
*
* Alias for the {@see \PHPCSUtils\Utils\ObjectDeclarations::getName()} method.
*
* @see \PHPCSUtils\BackCompat\BCFile::getDeclarationName() Original function.
* @see \PHPCSUtils\Utils\ObjectDeclarations::getName() PHPCSUtils native improved version.
*
* @since 1.0.0
*
* @codeCoverageIgnore
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the function keyword token.
*
* @return string|null The name of the function; or `NULL` if the passed token doesn't exist,
* the function is anonymous or in case of a parse error/live coding.
*
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified token is not of type
* `T_FUNCTION`.
*/
public static function getName(File $phpcsFile, $stackPtr)
{
return ObjectDeclarations::getName($phpcsFile, $stackPtr);
}
/**
* Retrieves the visibility and implementation properties of a method.
*
* Main differences with the PHPCS version:
* - Bugs fixed:
* - Handling of PHPCS annotations.
* - `"has_body"` index could be set to `true` for functions without body in the case of
* parse errors or live coding.
* - Defensive coding against incorrect calls to this method.
* - More efficient checking whether a function has a body.
* - Support for PHP 8.0 identifier name tokens in return types, cross-version PHP & PHPCS.
* - Support for the PHP 8.2 `true` type.
* - The results of this function call are cached during a PHPCS run for faster response times.
*
* @see \PHP_CodeSniffer\Files\File::getMethodProperties() Original source.
* @see \PHPCSUtils\BackCompat\BCFile::getMethodProperties() Cross-version compatible version of the original.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position in the stack of the function token to
* acquire the properties for.
*
* @return array Array with information about a function declaration.
* The format of the return value is:
* ```php
* array(
* 'scope' => string, // Public, private, or protected
* 'scope_specified' => bool, // TRUE if the scope keyword was found.
* 'return_type' => string, // The return type of the method.
* 'return_type_token' => int|false, // The stack pointer to the start of the return type
* // or FALSE if there is no return type.
* 'return_type_end_token' => int|false, // The stack pointer to the end of the return type
* // or FALSE if there is no return type.
* 'nullable_return_type' => bool, // TRUE if the return type is preceded
* // by the nullability operator.
* 'is_abstract' => bool, // TRUE if the abstract keyword was found.
* 'is_final' => bool, // TRUE if the final keyword was found.
* 'is_static' => bool, // TRUE if the static keyword was found.
* 'has_body' => bool, // TRUE if the method has a body
* );
* ```
*
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a T_FUNCTION
* or T_CLOSURE token, nor an arrow function.
*/
public static function getProperties(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
if (isset($tokens[$stackPtr]) === false
|| isset(Collections::functionDeclarationTokens()[$tokens[$stackPtr]['code']]) === false
) {
throw new RuntimeException('$stackPtr must be of type T_FUNCTION or T_CLOSURE or an arrow function');
}
if (Cache::isCached($phpcsFile, __METHOD__, $stackPtr) === true) {
return Cache::get($phpcsFile, __METHOD__, $stackPtr);
}
if ($tokens[$stackPtr]['code'] === \T_FUNCTION) {
$valid = Tokens::$methodPrefixes;
} else {
$valid = [\T_STATIC => \T_STATIC];
}
$valid += Tokens::$emptyTokens;
$scope = 'public';
$scopeSpecified = false;
$isAbstract = false;
$isFinal = false;
$isStatic = false;
for ($i = ($stackPtr - 1); $i > 0; $i--) {
if (isset($valid[$tokens[$i]['code']]) === false) {
break;
}
switch ($tokens[$i]['code']) {
case \T_PUBLIC:
$scope = 'public';
$scopeSpecified = true;
break;
case \T_PRIVATE:
$scope = 'private';
$scopeSpecified = true;
break;
case \T_PROTECTED:
$scope = 'protected';
$scopeSpecified = true;
break;
case \T_ABSTRACT:
$isAbstract = true;
break;
case \T_FINAL:
$isFinal = true;
break;
case \T_STATIC:
$isStatic = true;
break;
}
}
$returnType = '';
$returnTypeToken = false;
$returnTypeEndToken = false;
$nullableReturnType = false;
$hasBody = false;
$returnTypeTokens = Collections::returnTypeTokens();
/*
* BC PHPCS < 3.x.x: The union type separator is not (yet) retokenized correctly
* for union types containing the `true` type.
*/
$returnTypeTokens[\T_BITWISE_OR] = \T_BITWISE_OR;
$parenthesisCloser = null;
if (isset($tokens[$stackPtr]['parenthesis_closer']) === true) {
$parenthesisCloser = $tokens[$stackPtr]['parenthesis_closer'];
}
if (isset($parenthesisCloser) === true) {
$scopeOpener = null;
if (isset($tokens[$stackPtr]['scope_opener']) === true) {
$scopeOpener = $tokens[$stackPtr]['scope_opener'];
}
for ($i = $parenthesisCloser; $i < $phpcsFile->numTokens; $i++) {
if ($i === $scopeOpener) {
// End of function definition.
$hasBody = true;
break;
}
if ($scopeOpener === null && $tokens[$i]['code'] === \T_SEMICOLON) {
// End of abstract/interface function definition.
break;
}
if ($tokens[$i]['code'] === \T_NULLABLE) {
$nullableReturnType = true;
}
if (isset($returnTypeTokens[$tokens[$i]['code']]) === true) {
if ($returnTypeToken === false) {
$returnTypeToken = $i;
}
$returnType .= $tokens[$i]['content'];
$returnTypeEndToken = $i;
}
}
}
if ($returnType !== '' && $nullableReturnType === true) {
$returnType = '?' . $returnType;
}
$returnValue = [
'scope' => $scope,
'scope_specified' => $scopeSpecified,
'return_type' => $returnType,
'return_type_token' => $returnTypeToken,
'return_type_end_token' => $returnTypeEndToken,
'nullable_return_type' => $nullableReturnType,
'is_abstract' => $isAbstract,
'is_final' => $isFinal,
'is_static' => $isStatic,
'has_body' => $hasBody,
];
Cache::set($phpcsFile, __METHOD__, $stackPtr, $returnValue);
return $returnValue;
}
/**
* Retrieves the method parameters for the specified function token.
*
* Also supports passing in a `T_USE` token for a closure use group.
*
* The returned array will contain the following information for each parameter:
*
* ```php
* 0 => array(
* 'name' => string, // The variable name.
* 'token' => int, // The stack pointer to the variable name.
* 'content' => string, // The full content of the variable definition.
* 'has_attributes' => bool, // Does the parameter have one or more attributes attached ?
* 'pass_by_reference' => bool, // Is the variable passed by reference?
* 'reference_token' => int|false, // The stack pointer to the reference operator
* // or FALSE if the param is not passed by reference.
* 'variable_length' => bool, // Is the param of variable length through use of `...` ?
* 'variadic_token' => int|false, // The stack pointer to the ... operator
* // or FALSE if the param is not variable length.
* 'type_hint' => string, // The type hint for the variable.
* 'type_hint_token' => int|false, // The stack pointer to the start of the type hint
* // or FALSE if there is no type hint.
* 'type_hint_end_token' => int|false, // The stack pointer to the end of the type hint
* // or FALSE if there is no type hint.
* 'nullable_type' => bool, // TRUE if the var type is preceded by the nullability
* // operator.
* 'comma_token' => int|false, // The stack pointer to the comma after the param
* // or FALSE if this is the last param.
* )
* ```
*
* Parameters with default values have the following additional array indexes:
* ```php
* 'default' => string, // The full content of the default value.
* 'default_token' => int, // The stack pointer to the start of the default value.
* 'default_equal_token' => int, // The stack pointer to the equals sign.
* ```
*
* Parameters declared using PHP 8 constructor property promotion, have these additional array indexes:
* ```php
* 'property_visibility' => string, // The property visibility as declared.
* 'visibility_token' => int|false, // The stack pointer to the visibility modifier token.
* // or FALSE if the visibility is not explicitly declared.
* 'property_readonly' => bool, // TRUE if the readonly keyword was found.
* 'readonly_token' => int, // The stack pointer to the readonly modifier token.
* // This index will only be set if the property is readonly.
* ```
*
* Main differences with the PHPCS version:
* - Defensive coding against incorrect calls to this method.
* - More efficient and more stable checking whether a `T_USE` token is a closure use.
* - More efficient and more stable looping of the default value.
* - Clearer exception message when a non-closure use token was passed to the function.
* - Support for PHP 8.0 identifier name tokens in parameter types, cross-version PHP & PHPCS.
* - Support for the PHP 8.2 `true` type.
* - The results of this function call are cached during a PHPCS run for faster response times.
*
* @see \PHP_CodeSniffer\Files\File::getMethodParameters() Original source.
* @see \PHPCSUtils\BackCompat\BCFile::getMethodParameters() Cross-version compatible version of the original.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position in the stack of the function token
* to acquire the parameters for.
*
* @return array
*
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified $stackPtr is not of
* type `T_FUNCTION`, `T_CLOSURE` or `T_USE`,
* nor an arrow function.
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException If a passed `T_USE` token is not a closure
* use token.
*/
public static function getParameters(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
if (isset($tokens[$stackPtr]) === false
|| (isset(Collections::functionDeclarationTokens()[$tokens[$stackPtr]['code']]) === false
&& $tokens[$stackPtr]['code'] !== \T_USE)
) {
throw new RuntimeException('$stackPtr must be of type T_FUNCTION, T_CLOSURE or T_USE or an arrow function');
}
if ($tokens[$stackPtr]['code'] === \T_USE) {
// This will work PHPCS 3.x/4.x cross-version without much overhead.
$opener = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($opener === false
|| $tokens[$opener]['code'] !== \T_OPEN_PARENTHESIS
|| UseStatements::isClosureUse($phpcsFile, $stackPtr) === false
) {
throw new RuntimeException('$stackPtr was not a valid closure T_USE');
}
} else {
if (isset($tokens[$stackPtr]['parenthesis_opener']) === false) {
// Live coding or syntax error, so no params to find.
return [];
}
$opener = $tokens[$stackPtr]['parenthesis_opener'];
}
if (isset($tokens[$opener]['parenthesis_closer']) === false) {
// Live coding or syntax error, so no params to find.
return [];
}
if (Cache::isCached($phpcsFile, __METHOD__, $stackPtr) === true) {
return Cache::get($phpcsFile, __METHOD__, $stackPtr);
}
$closer = $tokens[$opener]['parenthesis_closer'];
$vars = [];
$currVar = null;
$paramStart = ($opener + 1);
$defaultStart = null;
$equalToken = null;
$paramCount = 0;
$hasAttributes = false;
$passByReference = false;
$referenceToken = false;
$variableLength = false;
$variadicToken = false;
$typeHint = '';
$typeHintToken = false;
$typeHintEndToken = false;
$nullableType = false;
$visibilityToken = null;
$readonlyToken = null;
$parameterTypeTokens = Collections::parameterTypeTokens();
/*
* BC PHPCS < 3.x.x: The union type separator is not (yet) retokenized correctly
* for union types containing the `true` type.
*/
$parameterTypeTokens[\T_BITWISE_OR] = \T_BITWISE_OR;
for ($i = $paramStart; $i <= $closer; $i++) {
if (isset($parameterTypeTokens[$tokens[$i]['code']]) === true
/*
* Self and parent are valid, static invalid, but was probably intended as type declaration.
* Note: constructor property promotion does not support static properties, so this should
* still be a valid assumption.
*/
|| $tokens[$i]['code'] === \T_STATIC
) {
if ($typeHintToken === false) {
$typeHintToken = $i;
}
$typeHint .= $tokens[$i]['content'];
$typeHintEndToken = $i;
continue;
}
switch ($tokens[$i]['code']) {
case \T_ATTRIBUTE:
$hasAttributes = true;
// Skip to the end of the attribute.
$i = $tokens[$i]['attribute_closer'];
break;
case \T_BITWISE_AND:
$passByReference = true;
$referenceToken = $i;
break;
case \T_VARIABLE:
$currVar = $i;
break;
case \T_ELLIPSIS:
$variableLength = true;
$variadicToken = $i;
break;
case \T_NULLABLE:
$nullableType = true;
$typeHint .= $tokens[$i]['content'];
$typeHintEndToken = $i;
break;
case \T_PUBLIC:
case \T_PROTECTED:
case \T_PRIVATE:
$visibilityToken = $i;
break;
case \T_READONLY:
$readonlyToken = $i;
break;
case \T_CLOSE_PARENTHESIS:
case \T_COMMA:
// If it's null, then there must be no parameters for this
// method.
if ($currVar === null) {
continue 2;
}
$vars[$paramCount] = [];
$vars[$paramCount]['token'] = $currVar;
$vars[$paramCount]['name'] = $tokens[$currVar]['content'];
$vars[$paramCount]['content'] = \trim(
GetTokensAsString::normal($phpcsFile, $paramStart, ($i - 1))
);
if ($defaultStart !== null) {
$vars[$paramCount]['default'] = \trim(
GetTokensAsString::normal($phpcsFile, $defaultStart, ($i - 1))
);
$vars[$paramCount]['default_token'] = $defaultStart;
$vars[$paramCount]['default_equal_token'] = $equalToken;
}
$vars[$paramCount]['has_attributes'] = $hasAttributes;
$vars[$paramCount]['pass_by_reference'] = $passByReference;
$vars[$paramCount]['reference_token'] = $referenceToken;
$vars[$paramCount]['variable_length'] = $variableLength;
$vars[$paramCount]['variadic_token'] = $variadicToken;
$vars[$paramCount]['type_hint'] = $typeHint;
$vars[$paramCount]['type_hint_token'] = $typeHintToken;
$vars[$paramCount]['type_hint_end_token'] = $typeHintEndToken;
$vars[$paramCount]['nullable_type'] = $nullableType;
if ($visibilityToken !== null || $readonlyToken !== null) {
$vars[$paramCount]['property_visibility'] = 'public';
$vars[$paramCount]['visibility_token'] = false;
$vars[$paramCount]['property_readonly'] = false;
if ($visibilityToken !== null) {
$vars[$paramCount]['property_visibility'] = $tokens[$visibilityToken]['content'];
$vars[$paramCount]['visibility_token'] = $visibilityToken;
}
if ($readonlyToken !== null) {
$vars[$paramCount]['property_readonly'] = true;
$vars[$paramCount]['readonly_token'] = $readonlyToken;
}
}
if ($tokens[$i]['code'] === \T_COMMA) {
$vars[$paramCount]['comma_token'] = $i;
} else {
$vars[$paramCount]['comma_token'] = false;
}
// Reset the vars, as we are about to process the next parameter.
$currVar = null;
$paramStart = ($i + 1);
$defaultStart = null;
$equalToken = null;
$hasAttributes = false;
$passByReference = false;
$referenceToken = false;
$variableLength = false;
$variadicToken = false;
$typeHint = '';
$typeHintToken = false;
$typeHintEndToken = false;
$nullableType = false;
$visibilityToken = null;
$readonlyToken = null;
++$paramCount;
break;
case \T_EQUAL:
$defaultStart = $phpcsFile->findNext(Tokens::$emptyTokens, ($i + 1), null, true);
$equalToken = $i;
// Skip past everything in the default value before going into the next switch loop.
for ($j = ($i + 1); $j <= $closer; $j++) {
// Skip past array()'s et al as default values.
if (isset($tokens[$j]['parenthesis_opener'], $tokens[$j]['parenthesis_closer'])) {
$j = $tokens[$j]['parenthesis_closer'];
if ($j === $closer) {
// Found the end of the parameter.
break;
}
continue;
}
// Skip past short arrays et al as default values.
if (isset($tokens[$j]['bracket_opener'])) {
$j = $tokens[$j]['bracket_closer'];
continue;
}
if ($tokens[$j]['code'] === \T_COMMA) {
break;
}
}
$i = ($j - 1);
break;
}
}
Cache::set($phpcsFile, __METHOD__, $stackPtr, $vars);
return $vars;
}
/**
* Checks if a given function is a PHP magic function.
*
* @todo Add check for the function declaration being namespaced!
*
* @see \PHPCSUtils\Utils\FunctionDeclaration::$magicFunctions List of names of magic functions.
* @see \PHPCSUtils\Utils\FunctionDeclaration::isMagicFunctionName() For when you already know the name of the
* function and scope checking is done in the
* sniff.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
* @param int $stackPtr The `T_FUNCTION` token to check.
*
* @return bool
*/
public static function isMagicFunction(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
if (isset($tokens[$stackPtr]) === false || $tokens[$stackPtr]['code'] !== \T_FUNCTION) {
return false;
}
if (Scopes::isOOMethod($phpcsFile, $stackPtr) === true) {
return false;
}
$name = self::getName($phpcsFile, $stackPtr);
return self::isMagicFunctionName($name);
}
/**
* Verify if a given function name is the name of a PHP magic function.
*
* @see \PHPCSUtils\Utils\FunctionDeclaration::$magicFunctions List of names of magic functions.
*
* @since 1.0.0
*
* @param string $name The full function name.
*
* @return bool
*/
public static function isMagicFunctionName($name)
{
$name = \strtolower($name);
return (isset(self::$magicFunctions[$name]) === true);
}
/**
* Checks if a given function is a PHP magic method.
*
* @see \PHPCSUtils\Utils\FunctionDeclaration::$magicMethods List of names of magic methods.
* @see \PHPCSUtils\Utils\FunctionDeclaration::isMagicMethodName() For when you already know the name of the
* method and scope checking is done in the
* sniff.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
* @param int $stackPtr The `T_FUNCTION` token to check.
*
* @return bool
*/
public static function isMagicMethod(File $phpcsFile, $stackPtr)
{
if (Scopes::isOOMethod($phpcsFile, $stackPtr) === false) {
return false;
}
$name = self::getName($phpcsFile, $stackPtr);
return self::isMagicMethodName($name);
}
/**
* Verify if a given function name is the name of a PHP magic method.
*
* @see \PHPCSUtils\Utils\FunctionDeclaration::$magicMethods List of names of magic methods.
*
* @since 1.0.0
*
* @param string $name The full function name.
*
* @return bool
*/
public static function isMagicMethodName($name)
{
$name = \strtolower($name);
return (isset(self::$magicMethods[$name]) === true);
}
/**
* Checks if a given function is a non-magic PHP native double underscore method.
*
* @see \PHPCSUtils\Utils\FunctionDeclaration::$methodsDoubleUnderscore List of the PHP native non-magic
* double underscore method names.
* @see \PHPCSUtils\Utils\FunctionDeclaration::isPHPDoubleUnderscoreMethodName() For when you already know the
* name of the method and scope
* checking is done in the sniff.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
* @param int $stackPtr The `T_FUNCTION` token to check.
*
* @return bool
*/
public static function isPHPDoubleUnderscoreMethod(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
if (isset($tokens[$stackPtr]) === false || $tokens[$stackPtr]['code'] !== \T_FUNCTION) {
return false;
}
$scopePtr = Scopes::validDirectScope($phpcsFile, $stackPtr, Tokens::$ooScopeTokens);
if ($scopePtr === false) {
return false;
}
/*
* If this is a class, make sure it extends something, as otherwise, the methods
* still can't be overloads for the SOAPClient methods.
* For a trait/interface we don't know the concrete implementation context, so skip
* this check.
*/
if ($tokens[$scopePtr]['code'] === \T_CLASS || $tokens[$scopePtr]['code'] === \T_ANON_CLASS) {
$extends = ObjectDeclarations::findExtendedClassName($phpcsFile, $scopePtr);
if ($extends === false) {
return false;
}
}
$name = self::getName($phpcsFile, $stackPtr);
return self::isPHPDoubleUnderscoreMethodName($name);
}
/**
* Verify if a given function name is the name of a non-magic PHP native double underscore method.
*
* @see \PHPCSUtils\Utils\FunctionDeclaration::$methodsDoubleUnderscore List of the PHP native non-magic
* double underscore method names.
*
* @since 1.0.0
*
* @param string $name The full function name.
*
* @return bool
*/
public static function isPHPDoubleUnderscoreMethodName($name)
{
$name = \strtolower($name);
return (isset(self::$methodsDoubleUnderscore[$name]) === true);
}
/**
* Checks if a given function is a magic method or a PHP native double underscore method.
*
* {@internal Not the most efficient way of checking this, but less efficient ways will get
* less reliable results or introduce a lot of code duplication.}
*
* @see \PHPCSUtils\Utils\FunctionDeclaration::isSpecialMethodName() For when you already know the name of the
* method and scope checking is done in the
* sniff.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
* @param int $stackPtr The `T_FUNCTION` token to check.
*
* @return bool
*/
public static function isSpecialMethod(File $phpcsFile, $stackPtr)
{
if (self::isMagicMethod($phpcsFile, $stackPtr) === true) {
return true;
}
if (self::isPHPDoubleUnderscoreMethod($phpcsFile, $stackPtr) === true) {
return true;
}
return false;
}
/**
* Verify if a given function name is the name of a magic method or a PHP native double underscore method.
*
* @since 1.0.0
*
* @param string $name The full function name.
*
* @return bool
*/
public static function isSpecialMethodName($name)
{
$name = \strtolower($name);
return (isset(self::$magicMethods[$name]) === true || isset(self::$methodsDoubleUnderscore[$name]) === true);
}
}