getTokens(); if (isset($tokens[$stackPtr]) === false || ($tokens[$stackPtr]['code'] === \T_ANON_CLASS || $tokens[$stackPtr]['code'] === \T_CLOSURE) ) { return null; } $tokenCode = $tokens[$stackPtr]['code']; if ($tokenCode !== \T_FUNCTION && $tokenCode !== \T_CLASS && $tokenCode !== \T_INTERFACE && $tokenCode !== \T_TRAIT && $tokenCode !== \T_ENUM ) { throw new RuntimeException( 'Token type "' . $tokens[$stackPtr]['type'] . '" is not T_FUNCTION, T_CLASS, T_INTERFACE, T_TRAIT or T_ENUM' ); } if ($tokenCode === \T_FUNCTION && \strtolower($tokens[$stackPtr]['content']) !== 'function' ) { // This is a function declared without the "function" keyword. // So this token is the function name. return $tokens[$stackPtr]['content']; } /* * Determine the name. Note that we cannot simply look for the first T_STRING * because an (invalid) class name starting with a number will be multiple tokens. * Whitespace or comment are however not allowed within a name. */ $stopPoint = $phpcsFile->numTokens; if ($tokenCode === \T_FUNCTION && isset($tokens[$stackPtr]['parenthesis_opener']) === true) { $stopPoint = $tokens[$stackPtr]['parenthesis_opener']; } elseif (isset($tokens[$stackPtr]['scope_opener']) === true) { $stopPoint = $tokens[$stackPtr]['scope_opener']; } $exclude = Tokens::$emptyTokens; $exclude[] = \T_OPEN_PARENTHESIS; $exclude[] = \T_OPEN_CURLY_BRACKET; $exclude[] = \T_BITWISE_AND; $exclude[] = \T_COLON; // Backed enums. $nameStart = $phpcsFile->findNext($exclude, ($stackPtr + 1), $stopPoint, true); if ($nameStart === false) { // Live coding or parse error. return null; } $tokenAfterNameEnd = $phpcsFile->findNext($exclude, $nameStart, $stopPoint); if ($tokenAfterNameEnd === false) { return $tokens[$nameStart]['content']; } // Name starts with number, so is composed of multiple tokens. return GetTokensAsString::noEmpties($phpcsFile, $nameStart, ($tokenAfterNameEnd - 1)); } /** * Retrieves the implementation properties of a class. * * Main differences with the PHPCS version: * - Bugs fixed: * - Handling of PHPCS annotations. * - Handling of unorthodox docblock placement. * - Defensive coding against incorrect calls to this method. * - Additional `'abstract_token'`, `'final_token'`, and `'readonly_token'` indexes in the return array. * * @see \PHP_CodeSniffer\Files\File::getClassProperties() Original source. * @see \PHPCSUtils\BackCompat\BCFile::getClassProperties() 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 `T_CLASS` * token to acquire the properties for. * * @return array Array with implementation properties of a class. * The format of the return value is: * ```php * array( * 'is_abstract' => bool, // TRUE if the abstract keyword was found. * 'abstract_token' => int|false, // The stack pointer to the `abstract` keyword or * // FALSE if the abstract keyword was not found. * 'is_final' => bool, // TRUE if the final keyword was found. * 'final_token' => int|false, // The stack pointer to the `final` keyword or * // FALSE if the abstract keyword was not found. * 'is_readonly' => bool, // TRUE if the readonly keyword was found. * 'readonly_token' => int|false, // The stack pointer to the `readonly` keyword or * // FALSE if the abstract keyword was not found. * ); * ``` * * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a * `T_CLASS` token. */ public static function getClassProperties(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); if (isset($tokens[$stackPtr]) === false || $tokens[$stackPtr]['code'] !== \T_CLASS) { throw new RuntimeException('$stackPtr must be of type T_CLASS'); } $valid = Collections::classModifierKeywords() + Tokens::$emptyTokens; $properties = [ 'is_abstract' => false, 'abstract_token' => false, 'is_final' => false, 'final_token' => false, 'is_readonly' => false, 'readonly_token' => false, ]; for ($i = ($stackPtr - 1); $i > 0; $i--) { if (isset($valid[$tokens[$i]['code']]) === false) { break; } switch ($tokens[$i]['code']) { case \T_ABSTRACT: $properties['is_abstract'] = true; $properties['abstract_token'] = $i; break; case \T_FINAL: $properties['is_final'] = true; $properties['final_token'] = $i; break; case \T_READONLY: $properties['is_readonly'] = true; $properties['readonly_token'] = $i; break; } } return $properties; } /** * Retrieves the name of the class that the specified class extends. * * Works for classes, anonymous classes and interfaces, though it is strongly recommended * to use the {@see \PHPCSUtils\Utils\ObjectDeclarations::findExtendedInterfaceNames()} * method to examine interfaces instead. Interfaces can extend multiple parent interfaces, * and that use-case is not handled by this method. * * Main differences with the PHPCS version: * - Bugs fixed: * - Handling of PHPCS annotations. * - Handling of comments. * - Handling of the namespace keyword used as operator. * - Improved handling of parse errors. * - The returned name will be clean of superfluous whitespace and/or comments. * - Support for PHP 8.0 tokenization of identifier/namespaced names, cross-version PHP & PHPCS. * * @see \PHP_CodeSniffer\Files\File::findExtendedClassName() Original source. * @see \PHPCSUtils\BackCompat\BCFile::findExtendedClassName() Cross-version compatible version of * the original. * @see \PHPCSUtils\Utils\ObjectDeclarations::findExtendedInterfaceNames() Similar method for extended interfaces. * * @since 1.0.0 * * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. * @param int $stackPtr The stack position of the class or interface. * * @return string|false The extended class name or `FALSE` on error or if there * is no extended class name. */ public static function findExtendedClassName(File $phpcsFile, $stackPtr) { $names = self::findNames($phpcsFile, $stackPtr, \T_EXTENDS, Collections::ooCanExtend()); if ($names === false) { return false; } // Classes can only extend one parent class. return \array_shift($names); } /** * Retrieves the names of the interfaces that the specified class or enum implements. * * Main differences with the PHPCS version: * - Bugs fixed: * - Handling of PHPCS annotations. * - Handling of comments. * - Handling of the namespace keyword used as operator. * - Improved handling of parse errors. * - The returned name(s) will be clean of superfluous whitespace and/or comments. * - Support for PHP 8.0 tokenization of identifier/namespaced names, cross-version PHP & PHPCS. * * @see \PHP_CodeSniffer\Files\File::findImplementedInterfaceNames() Original source. * @see \PHPCSUtils\BackCompat\BCFile::findImplementedInterfaceNames() 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 stack position of the class or enum token. * * @return array|false Array with names of the implemented interfaces or `FALSE` on * error or if there are no implemented interface names. */ public static function findImplementedInterfaceNames(File $phpcsFile, $stackPtr) { return self::findNames($phpcsFile, $stackPtr, \T_IMPLEMENTS, Collections::ooCanImplement()); } /** * Retrieves the names of the interfaces that the specified interface extends. * * @see \PHPCSUtils\Utils\ObjectDeclarations::findExtendedClassName() Similar method for extended classes. * * @since 1.0.0 * * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found. * @param int $stackPtr The stack position of the interface keyword. * * @return array|false Array with names of the extended interfaces or `FALSE` on * error or if there are no extended interface names. */ public static function findExtendedInterfaceNames(File $phpcsFile, $stackPtr) { return self::findNames( $phpcsFile, $stackPtr, \T_EXTENDS, [\T_INTERFACE => \T_INTERFACE] ); } /** * Retrieves the names of the extended classes or interfaces or the implemented * interfaces that the specific class/interface declaration extends/implements. * * @since 1.0.0 * * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found. * @param int $stackPtr The stack position of the * class/interface declaration keyword. * @param int $keyword The token constant for the keyword to examine. * Either `T_EXTENDS` or `T_IMPLEMENTS`. * @param array $allowedFor Array of OO types for which use of the keyword * is allowed. * * @return array|false Returns an array of names or `FALSE` on error or when the object * being declared does not extend/implement another object. */ private static function findNames(File $phpcsFile, $stackPtr, $keyword, array $allowedFor) { $tokens = $phpcsFile->getTokens(); if (isset($tokens[$stackPtr]) === false || isset($allowedFor[$tokens[$stackPtr]['code']]) === false || isset($tokens[$stackPtr]['scope_opener']) === false ) { return false; } $scopeOpener = $tokens[$stackPtr]['scope_opener']; $keywordPtr = $phpcsFile->findNext($keyword, ($stackPtr + 1), $scopeOpener); if ($keywordPtr === false) { return false; } $find = Collections::namespacedNameTokens() + Tokens::$emptyTokens; $names = []; $end = $keywordPtr; do { $start = ($end + 1); $end = $phpcsFile->findNext($find, $start, ($scopeOpener + 1), true); $name = GetTokensAsString::noEmpties($phpcsFile, $start, ($end - 1)); if (\trim($name) !== '') { $names[] = $name; } } while ($tokens[$end]['code'] === \T_COMMA); if (empty($names)) { return false; } return $names; } }