google_forms/vendor/slevomat/coding-standard/SlevomatCodingStandard/Helpers/UseStatementHelper.php

271 lines
7.9 KiB
PHP

<?php declare(strict_types = 1);
namespace SlevomatCodingStandard\Helpers;
use PHP_CodeSniffer\Files\File;
use function array_key_exists;
use function array_merge;
use function array_reverse;
use function count;
use function current;
use function in_array;
use function strtolower;
use const T_AS;
use const T_CLOSE_CURLY_BRACKET;
use const T_COMMA;
use const T_DECLARE;
use const T_FUNCTION;
use const T_NAMESPACE;
use const T_OPEN_CURLY_BRACKET;
use const T_OPEN_PARENTHESIS;
use const T_OPEN_TAG;
use const T_OPEN_USE_GROUP;
use const T_SEMICOLON;
use const T_STRING;
use const T_USE;
/**
* @internal
*/
class UseStatementHelper
{
public static function isImportUse(File $phpcsFile, int $usePointer): bool
{
$tokens = $phpcsFile->getTokens();
$nextPointer = TokenHelper::findNextEffective($phpcsFile, $usePointer + 1);
// Anonymous function use
if ($tokens[$nextPointer]['code'] === T_OPEN_PARENTHESIS) {
return false;
}
if (
$tokens[$nextPointer]['code'] === T_STRING
&& in_array(strtolower($tokens[$nextPointer]['content']), ['function', 'const'], true)
) {
return true;
}
$previousPointer = TokenHelper::findPrevious(
$phpcsFile,
[T_OPEN_TAG, T_DECLARE, T_NAMESPACE, T_OPEN_CURLY_BRACKET, T_CLOSE_CURLY_BRACKET],
$usePointer
);
if (in_array($tokens[$previousPointer]['code'], [T_OPEN_TAG, T_DECLARE, T_NAMESPACE], true)) {
return true;
}
if (array_key_exists('scope_condition', $tokens[$previousPointer])) {
$scopeConditionPointer = $tokens[$previousPointer]['scope_condition'];
if (
$tokens[$previousPointer]['code'] === T_OPEN_CURLY_BRACKET
&& in_array($tokens[$scopeConditionPointer]['code'], TokenHelper::$typeWithAnonymousClassKeywordTokenCodes, true)
) {
return false;
}
// Trait use after another trait use
if ($tokens[$scopeConditionPointer]['code'] === T_USE) {
return false;
}
// Trait use after method or import use after function
if ($tokens[$scopeConditionPointer]['code'] === T_FUNCTION) {
return ClassHelper::getClassPointer($phpcsFile, $usePointer) === null;
}
}
return true;
}
public static function isTraitUse(File $phpcsFile, int $usePointer): bool
{
$tokens = $phpcsFile->getTokens();
$nextPointer = TokenHelper::findNextEffective($phpcsFile, $usePointer + 1);
// Anonymous function use
if ($tokens[$nextPointer]['code'] === T_OPEN_PARENTHESIS) {
return false;
}
return !self::isImportUse($phpcsFile, $usePointer);
}
public static function getAlias(File $phpcsFile, int $usePointer): ?string
{
$endPointer = TokenHelper::findNext($phpcsFile, [T_SEMICOLON, T_COMMA], $usePointer + 1);
$asPointer = TokenHelper::findNext($phpcsFile, T_AS, $usePointer + 1, $endPointer);
if ($asPointer === null) {
return null;
}
$tokens = $phpcsFile->getTokens();
return $tokens[TokenHelper::findNext($phpcsFile, T_STRING, $asPointer + 1)]['content'];
}
public static function getNameAsReferencedInClassFromUse(File $phpcsFile, int $usePointer): string
{
$alias = self::getAlias($phpcsFile, $usePointer);
if ($alias !== null) {
return $alias;
}
$name = self::getFullyQualifiedTypeNameFromUse($phpcsFile, $usePointer);
return NamespaceHelper::getUnqualifiedNameFromFullyQualifiedName($name);
}
public static function getFullyQualifiedTypeNameFromUse(File $phpcsFile, int $usePointer): string
{
$tokens = $phpcsFile->getTokens();
$nameEndPointer = TokenHelper::findNext($phpcsFile, [T_SEMICOLON, T_AS, T_COMMA], $usePointer + 1) - 1;
if (in_array($tokens[$nameEndPointer]['code'], TokenHelper::$ineffectiveTokenCodes, true)) {
$nameEndPointer = TokenHelper::findPreviousEffective($phpcsFile, $nameEndPointer);
}
$nameStartPointer = TokenHelper::findPreviousExcluding($phpcsFile, TokenHelper::getNameTokenCodes(), $nameEndPointer - 1) + 1;
$name = TokenHelper::getContent($phpcsFile, $nameStartPointer, $nameEndPointer);
return NamespaceHelper::normalizeToCanonicalName($name);
}
/**
* @return array<string, UseStatement>
*/
public static function getUseStatementsForPointer(File $phpcsFile, int $pointer): array
{
$allUseStatements = self::getFileUseStatements($phpcsFile);
if (count($allUseStatements) === 1) {
return current($allUseStatements);
}
foreach (array_reverse($allUseStatements, true) as $pointerBeforeUseStatements => $useStatements) {
if ($pointerBeforeUseStatements < $pointer) {
return $useStatements;
}
}
return [];
}
/**
* @return array<int, array<string, UseStatement>>
*/
public static function getFileUseStatements(File $phpcsFile): array
{
$lazyValue = static function () use ($phpcsFile): array {
$useStatements = [];
$tokens = $phpcsFile->getTokens();
$namespaceAndOpenTagPointers = TokenHelper::findNextAll($phpcsFile, [T_OPEN_TAG, T_NAMESPACE], 0);
$openTagPointer = $namespaceAndOpenTagPointers[0];
foreach (self::getUseStatementPointers($phpcsFile, $openTagPointer) as $usePointer) {
$pointerBeforeUseStatements = $openTagPointer;
if (count($namespaceAndOpenTagPointers) > 1) {
foreach (array_reverse($namespaceAndOpenTagPointers) as $namespaceAndOpenTagPointer) {
if ($namespaceAndOpenTagPointer < $usePointer) {
$pointerBeforeUseStatements = $namespaceAndOpenTagPointer;
break;
}
}
}
$nextTokenFromUsePointer = TokenHelper::findNextEffective($phpcsFile, $usePointer + 1);
$type = UseStatement::TYPE_CLASS;
if ($tokens[$nextTokenFromUsePointer]['code'] === T_STRING) {
if ($tokens[$nextTokenFromUsePointer]['content'] === 'const') {
$type = UseStatement::TYPE_CONSTANT;
} elseif ($tokens[$nextTokenFromUsePointer]['content'] === 'function') {
$type = UseStatement::TYPE_FUNCTION;
}
}
$name = self::getNameAsReferencedInClassFromUse($phpcsFile, $usePointer);
$useStatement = new UseStatement(
$name,
self::getFullyQualifiedTypeNameFromUse($phpcsFile, $usePointer),
$usePointer,
$type,
self::getAlias($phpcsFile, $usePointer)
);
$useStatements[$pointerBeforeUseStatements][UseStatement::getUniqueId($type, $name)] = $useStatement;
}
return $useStatements;
};
return SniffLocalCache::getAndSetIfNotCached($phpcsFile, 'useStatements', $lazyValue);
}
public static function getUseStatementPointer(File $phpcsFile, int $pointer): ?int
{
$pointers = self::getUseStatementPointers($phpcsFile, 0);
foreach (array_reverse($pointers) as $pointerBeforeUseStatements) {
if ($pointerBeforeUseStatements < $pointer) {
return $pointerBeforeUseStatements;
}
}
return null;
}
/**
* Searches for all use statements in a file, skips bodies of classes and traits.
*
* @return list<int>
*/
private static function getUseStatementPointers(File $phpcsFile, int $openTagPointer): array
{
$lazy = static function () use ($phpcsFile, $openTagPointer): array {
$tokens = $phpcsFile->getTokens();
$pointer = $openTagPointer + 1;
$pointers = [];
while (true) {
$typesToFind = array_merge([T_USE], TokenHelper::$typeKeywordTokenCodes);
$pointer = TokenHelper::findNext($phpcsFile, $typesToFind, $pointer);
if ($pointer === null) {
break;
}
$token = $tokens[$pointer];
if (in_array($token['code'], TokenHelper::$typeKeywordTokenCodes, true)) {
$pointer = $token['scope_closer'] + 1;
continue;
}
if (self::isGroupUse($phpcsFile, $pointer)) {
$pointer++;
continue;
}
if (!self::isImportUse($phpcsFile, $pointer)) {
$pointer++;
continue;
}
$pointers[] = $pointer;
$pointer++;
}
return $pointers;
};
return SniffLocalCache::getAndSetIfNotCached($phpcsFile, 'useStatementPointers', $lazy);
}
private static function isGroupUse(File $phpcsFile, int $usePointer): bool
{
$tokens = $phpcsFile->getTokens();
$semicolonOrGroupUsePointer = TokenHelper::findNext($phpcsFile, [T_SEMICOLON, T_OPEN_USE_GROUP], $usePointer + 1);
return $tokens[$semicolonOrGroupUsePointer]['code'] === T_OPEN_USE_GROUP;
}
}