init
This commit is contained in:
289
backend/vendor/phpstan/phpdoc-parser/src/Parser/ConstExprParser.php
vendored
Normal file
289
backend/vendor/phpstan/phpdoc-parser/src/Parser/ConstExprParser.php
vendored
Normal file
@@ -0,0 +1,289 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace PHPStan\PhpDocParser\Parser;
|
||||
|
||||
use PHPStan\PhpDocParser\Ast;
|
||||
use PHPStan\PhpDocParser\Lexer\Lexer;
|
||||
use PHPStan\PhpDocParser\ParserConfig;
|
||||
use function str_replace;
|
||||
use function strtolower;
|
||||
|
||||
class ConstExprParser
|
||||
{
|
||||
|
||||
private ParserConfig $config;
|
||||
|
||||
private bool $parseDoctrineStrings;
|
||||
|
||||
public function __construct(
|
||||
ParserConfig $config
|
||||
)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->parseDoctrineStrings = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function toDoctrine(): self
|
||||
{
|
||||
$self = new self($this->config);
|
||||
$self->parseDoctrineStrings = true;
|
||||
return $self;
|
||||
}
|
||||
|
||||
public function parse(TokenIterator $tokens): Ast\ConstExpr\ConstExprNode
|
||||
{
|
||||
$startLine = $tokens->currentTokenLine();
|
||||
$startIndex = $tokens->currentTokenIndex();
|
||||
if ($tokens->isCurrentTokenType(Lexer::TOKEN_FLOAT)) {
|
||||
$value = $tokens->currentTokenValue();
|
||||
$tokens->next();
|
||||
|
||||
return $this->enrichWithAttributes(
|
||||
$tokens,
|
||||
new Ast\ConstExpr\ConstExprFloatNode(str_replace('_', '', $value)),
|
||||
$startLine,
|
||||
$startIndex,
|
||||
);
|
||||
}
|
||||
|
||||
if ($tokens->isCurrentTokenType(Lexer::TOKEN_INTEGER)) {
|
||||
$value = $tokens->currentTokenValue();
|
||||
$tokens->next();
|
||||
|
||||
return $this->enrichWithAttributes(
|
||||
$tokens,
|
||||
new Ast\ConstExpr\ConstExprIntegerNode(str_replace('_', '', $value)),
|
||||
$startLine,
|
||||
$startIndex,
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->parseDoctrineStrings && $tokens->isCurrentTokenType(Lexer::TOKEN_DOCTRINE_ANNOTATION_STRING)) {
|
||||
$value = $tokens->currentTokenValue();
|
||||
$tokens->next();
|
||||
|
||||
return $this->enrichWithAttributes(
|
||||
$tokens,
|
||||
new Ast\ConstExpr\DoctrineConstExprStringNode(Ast\ConstExpr\DoctrineConstExprStringNode::unescape($value)),
|
||||
$startLine,
|
||||
$startIndex,
|
||||
);
|
||||
}
|
||||
|
||||
if ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING, Lexer::TOKEN_DOUBLE_QUOTED_STRING)) {
|
||||
if ($this->parseDoctrineStrings) {
|
||||
if ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) {
|
||||
throw new ParserException(
|
||||
$tokens->currentTokenValue(),
|
||||
$tokens->currentTokenType(),
|
||||
$tokens->currentTokenOffset(),
|
||||
Lexer::TOKEN_DOUBLE_QUOTED_STRING,
|
||||
null,
|
||||
$tokens->currentTokenLine(),
|
||||
);
|
||||
}
|
||||
|
||||
$value = $tokens->currentTokenValue();
|
||||
$tokens->next();
|
||||
|
||||
return $this->enrichWithAttributes(
|
||||
$tokens,
|
||||
$this->parseDoctrineString($value, $tokens),
|
||||
$startLine,
|
||||
$startIndex,
|
||||
);
|
||||
}
|
||||
|
||||
$value = StringUnescaper::unescapeString($tokens->currentTokenValue());
|
||||
$type = $tokens->currentTokenType();
|
||||
$tokens->next();
|
||||
|
||||
return $this->enrichWithAttributes(
|
||||
$tokens,
|
||||
new Ast\ConstExpr\ConstExprStringNode(
|
||||
$value,
|
||||
$type === Lexer::TOKEN_SINGLE_QUOTED_STRING
|
||||
? Ast\ConstExpr\ConstExprStringNode::SINGLE_QUOTED
|
||||
: Ast\ConstExpr\ConstExprStringNode::DOUBLE_QUOTED,
|
||||
),
|
||||
$startLine,
|
||||
$startIndex,
|
||||
);
|
||||
|
||||
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) {
|
||||
$identifier = $tokens->currentTokenValue();
|
||||
$tokens->next();
|
||||
|
||||
switch (strtolower($identifier)) {
|
||||
case 'true':
|
||||
return $this->enrichWithAttributes(
|
||||
$tokens,
|
||||
new Ast\ConstExpr\ConstExprTrueNode(),
|
||||
$startLine,
|
||||
$startIndex,
|
||||
);
|
||||
case 'false':
|
||||
return $this->enrichWithAttributes(
|
||||
$tokens,
|
||||
new Ast\ConstExpr\ConstExprFalseNode(),
|
||||
$startLine,
|
||||
$startIndex,
|
||||
);
|
||||
case 'null':
|
||||
return $this->enrichWithAttributes(
|
||||
$tokens,
|
||||
new Ast\ConstExpr\ConstExprNullNode(),
|
||||
$startLine,
|
||||
$startIndex,
|
||||
);
|
||||
case 'array':
|
||||
$tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES);
|
||||
return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_PARENTHESES, $startIndex);
|
||||
}
|
||||
|
||||
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_COLON)) {
|
||||
$classConstantName = '';
|
||||
$lastType = null;
|
||||
while (true) {
|
||||
if ($lastType !== Lexer::TOKEN_IDENTIFIER && $tokens->currentTokenType() === Lexer::TOKEN_IDENTIFIER) {
|
||||
$classConstantName .= $tokens->currentTokenValue();
|
||||
$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
|
||||
$lastType = Lexer::TOKEN_IDENTIFIER;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($lastType !== Lexer::TOKEN_WILDCARD && $tokens->tryConsumeTokenType(Lexer::TOKEN_WILDCARD)) {
|
||||
$classConstantName .= '*';
|
||||
$lastType = Lexer::TOKEN_WILDCARD;
|
||||
|
||||
if ($tokens->getSkippedHorizontalWhiteSpaceIfAny() !== '') {
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($lastType === null) {
|
||||
// trigger parse error if nothing valid was consumed
|
||||
$tokens->consumeTokenType(Lexer::TOKEN_WILDCARD);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->enrichWithAttributes(
|
||||
$tokens,
|
||||
new Ast\ConstExpr\ConstFetchNode($identifier, $classConstantName),
|
||||
$startLine,
|
||||
$startIndex,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
return $this->enrichWithAttributes(
|
||||
$tokens,
|
||||
new Ast\ConstExpr\ConstFetchNode('', $identifier),
|
||||
$startLine,
|
||||
$startIndex,
|
||||
);
|
||||
|
||||
} elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
|
||||
return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_SQUARE_BRACKET, $startIndex);
|
||||
}
|
||||
|
||||
throw new ParserException(
|
||||
$tokens->currentTokenValue(),
|
||||
$tokens->currentTokenType(),
|
||||
$tokens->currentTokenOffset(),
|
||||
Lexer::TOKEN_IDENTIFIER,
|
||||
null,
|
||||
$tokens->currentTokenLine(),
|
||||
);
|
||||
}
|
||||
|
||||
private function parseArray(TokenIterator $tokens, int $endToken, int $startIndex): Ast\ConstExpr\ConstExprArrayNode
|
||||
{
|
||||
$items = [];
|
||||
|
||||
$startLine = $tokens->currentTokenLine();
|
||||
|
||||
if (!$tokens->tryConsumeTokenType($endToken)) {
|
||||
do {
|
||||
$items[] = $this->parseArrayItem($tokens);
|
||||
} while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA) && !$tokens->isCurrentTokenType($endToken));
|
||||
$tokens->consumeTokenType($endToken);
|
||||
}
|
||||
|
||||
return $this->enrichWithAttributes(
|
||||
$tokens,
|
||||
new Ast\ConstExpr\ConstExprArrayNode($items),
|
||||
$startLine,
|
||||
$startIndex,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is supposed to be called with TokenIterator after reading TOKEN_DOUBLE_QUOTED_STRING and shifting
|
||||
* to the next token.
|
||||
*/
|
||||
public function parseDoctrineString(string $text, TokenIterator $tokens): Ast\ConstExpr\DoctrineConstExprStringNode
|
||||
{
|
||||
// Because of how Lexer works, a valid Doctrine string
|
||||
// can consist of a sequence of TOKEN_DOUBLE_QUOTED_STRING and TOKEN_DOCTRINE_ANNOTATION_STRING
|
||||
while ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING, Lexer::TOKEN_DOCTRINE_ANNOTATION_STRING)) {
|
||||
$text .= $tokens->currentTokenValue();
|
||||
$tokens->next();
|
||||
}
|
||||
|
||||
return new Ast\ConstExpr\DoctrineConstExprStringNode(Ast\ConstExpr\DoctrineConstExprStringNode::unescape($text));
|
||||
}
|
||||
|
||||
private function parseArrayItem(TokenIterator $tokens): Ast\ConstExpr\ConstExprArrayItemNode
|
||||
{
|
||||
$startLine = $tokens->currentTokenLine();
|
||||
$startIndex = $tokens->currentTokenIndex();
|
||||
|
||||
$expr = $this->parse($tokens);
|
||||
|
||||
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_ARROW)) {
|
||||
$key = $expr;
|
||||
$value = $this->parse($tokens);
|
||||
|
||||
} else {
|
||||
$key = null;
|
||||
$value = $expr;
|
||||
}
|
||||
|
||||
return $this->enrichWithAttributes(
|
||||
$tokens,
|
||||
new Ast\ConstExpr\ConstExprArrayItemNode($key, $value),
|
||||
$startLine,
|
||||
$startIndex,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T of Ast\ConstExpr\ConstExprNode
|
||||
* @param T $node
|
||||
* @return T
|
||||
*/
|
||||
private function enrichWithAttributes(TokenIterator $tokens, Ast\ConstExpr\ConstExprNode $node, int $startLine, int $startIndex): Ast\ConstExpr\ConstExprNode
|
||||
{
|
||||
if ($this->config->useLinesAttributes) {
|
||||
$node->setAttribute(Ast\Attribute::START_LINE, $startLine);
|
||||
$node->setAttribute(Ast\Attribute::END_LINE, $tokens->currentTokenLine());
|
||||
}
|
||||
|
||||
if ($this->config->useIndexAttributes) {
|
||||
$node->setAttribute(Ast\Attribute::START_INDEX, $startIndex);
|
||||
$node->setAttribute(Ast\Attribute::END_INDEX, $tokens->endIndexOfLastRelevantToken());
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
}
|
||||
93
backend/vendor/phpstan/phpdoc-parser/src/Parser/ParserException.php
vendored
Normal file
93
backend/vendor/phpstan/phpdoc-parser/src/Parser/ParserException.php
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace PHPStan\PhpDocParser\Parser;
|
||||
|
||||
use Exception;
|
||||
use PHPStan\PhpDocParser\Lexer\Lexer;
|
||||
use function assert;
|
||||
use function json_encode;
|
||||
use function sprintf;
|
||||
use const JSON_INVALID_UTF8_SUBSTITUTE;
|
||||
use const JSON_UNESCAPED_SLASHES;
|
||||
use const JSON_UNESCAPED_UNICODE;
|
||||
|
||||
class ParserException extends Exception
|
||||
{
|
||||
|
||||
private string $currentTokenValue;
|
||||
|
||||
private int $currentTokenType;
|
||||
|
||||
private int $currentOffset;
|
||||
|
||||
private int $expectedTokenType;
|
||||
|
||||
private ?string $expectedTokenValue;
|
||||
|
||||
private ?int $currentTokenLine;
|
||||
|
||||
public function __construct(
|
||||
string $currentTokenValue,
|
||||
int $currentTokenType,
|
||||
int $currentOffset,
|
||||
int $expectedTokenType,
|
||||
?string $expectedTokenValue,
|
||||
?int $currentTokenLine
|
||||
)
|
||||
{
|
||||
$this->currentTokenValue = $currentTokenValue;
|
||||
$this->currentTokenType = $currentTokenType;
|
||||
$this->currentOffset = $currentOffset;
|
||||
$this->expectedTokenType = $expectedTokenType;
|
||||
$this->expectedTokenValue = $expectedTokenValue;
|
||||
$this->currentTokenLine = $currentTokenLine;
|
||||
|
||||
parent::__construct(sprintf(
|
||||
'Unexpected token %s, expected %s%s at offset %d%s',
|
||||
$this->formatValue($currentTokenValue),
|
||||
Lexer::TOKEN_LABELS[$expectedTokenType],
|
||||
$expectedTokenValue !== null ? sprintf(' (%s)', $this->formatValue($expectedTokenValue)) : '',
|
||||
$currentOffset,
|
||||
$currentTokenLine === null ? '' : sprintf(' on line %d', $currentTokenLine),
|
||||
));
|
||||
}
|
||||
|
||||
public function getCurrentTokenValue(): string
|
||||
{
|
||||
return $this->currentTokenValue;
|
||||
}
|
||||
|
||||
public function getCurrentTokenType(): int
|
||||
{
|
||||
return $this->currentTokenType;
|
||||
}
|
||||
|
||||
public function getCurrentOffset(): int
|
||||
{
|
||||
return $this->currentOffset;
|
||||
}
|
||||
|
||||
public function getExpectedTokenType(): int
|
||||
{
|
||||
return $this->expectedTokenType;
|
||||
}
|
||||
|
||||
public function getExpectedTokenValue(): ?string
|
||||
{
|
||||
return $this->expectedTokenValue;
|
||||
}
|
||||
|
||||
public function getCurrentTokenLine(): ?int
|
||||
{
|
||||
return $this->currentTokenLine;
|
||||
}
|
||||
|
||||
private function formatValue(string $value): string
|
||||
{
|
||||
$json = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE);
|
||||
assert($json !== false);
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
}
|
||||
1244
backend/vendor/phpstan/phpdoc-parser/src/Parser/PhpDocParser.php
vendored
Normal file
1244
backend/vendor/phpstan/phpdoc-parser/src/Parser/PhpDocParser.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
100
backend/vendor/phpstan/phpdoc-parser/src/Parser/StringUnescaper.php
vendored
Normal file
100
backend/vendor/phpstan/phpdoc-parser/src/Parser/StringUnescaper.php
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace PHPStan\PhpDocParser\Parser;
|
||||
|
||||
use PHPStan\ShouldNotHappenException;
|
||||
use function chr;
|
||||
use function hexdec;
|
||||
use function octdec;
|
||||
use function preg_replace_callback;
|
||||
use function str_replace;
|
||||
use function substr;
|
||||
|
||||
class StringUnescaper
|
||||
{
|
||||
|
||||
private const REPLACEMENTS = [
|
||||
'\\' => '\\',
|
||||
'n' => "\n",
|
||||
'r' => "\r",
|
||||
't' => "\t",
|
||||
'f' => "\f",
|
||||
'v' => "\v",
|
||||
'e' => "\x1B",
|
||||
];
|
||||
|
||||
public static function unescapeString(string $string): string
|
||||
{
|
||||
$quote = $string[0];
|
||||
|
||||
if ($quote === '\'') {
|
||||
return str_replace(
|
||||
['\\\\', '\\\''],
|
||||
['\\', '\''],
|
||||
substr($string, 1, -1),
|
||||
);
|
||||
}
|
||||
|
||||
return self::parseEscapeSequences(substr($string, 1, -1), '"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation based on https://github.com/nikic/PHP-Parser/blob/b0edd4c41111042d43bb45c6c657b2e0db367d9e/lib/PhpParser/Node/Scalar/String_.php#L90-L130
|
||||
*/
|
||||
private static function parseEscapeSequences(string $str, string $quote): string
|
||||
{
|
||||
$str = str_replace('\\' . $quote, $quote, $str);
|
||||
|
||||
return preg_replace_callback(
|
||||
'~\\\\([\\\\nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}|u\{([0-9a-fA-F]+)\})~',
|
||||
static function ($matches) {
|
||||
$str = $matches[1];
|
||||
|
||||
if (isset(self::REPLACEMENTS[$str])) {
|
||||
return self::REPLACEMENTS[$str];
|
||||
}
|
||||
if ($str[0] === 'x' || $str[0] === 'X') {
|
||||
return chr((int) hexdec(substr($str, 1)));
|
||||
}
|
||||
if ($str[0] === 'u') {
|
||||
if (!isset($matches[2])) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
return self::codePointToUtf8((int) hexdec($matches[2]));
|
||||
}
|
||||
|
||||
return chr((int) octdec($str));
|
||||
},
|
||||
$str,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation based on https://github.com/nikic/PHP-Parser/blob/b0edd4c41111042d43bb45c6c657b2e0db367d9e/lib/PhpParser/Node/Scalar/String_.php#L132-L154
|
||||
*/
|
||||
private static function codePointToUtf8(int $num): string
|
||||
{
|
||||
if ($num <= 0x7F) {
|
||||
return chr($num);
|
||||
}
|
||||
if ($num <= 0x7FF) {
|
||||
return chr(($num >> 6) + 0xC0)
|
||||
. chr(($num & 0x3F) + 0x80);
|
||||
}
|
||||
if ($num <= 0xFFFF) {
|
||||
return chr(($num >> 12) + 0xE0)
|
||||
. chr((($num >> 6) & 0x3F) + 0x80)
|
||||
. chr(($num & 0x3F) + 0x80);
|
||||
}
|
||||
if ($num <= 0x1FFFFF) {
|
||||
return chr(($num >> 18) + 0xF0)
|
||||
. chr((($num >> 12) & 0x3F) + 0x80)
|
||||
. chr((($num >> 6) & 0x3F) + 0x80)
|
||||
. chr(($num & 0x3F) + 0x80);
|
||||
}
|
||||
|
||||
// Invalid UTF-8 codepoint escape sequence: Codepoint too large
|
||||
return "\xef\xbf\xbd";
|
||||
}
|
||||
|
||||
}
|
||||
400
backend/vendor/phpstan/phpdoc-parser/src/Parser/TokenIterator.php
vendored
Normal file
400
backend/vendor/phpstan/phpdoc-parser/src/Parser/TokenIterator.php
vendored
Normal file
@@ -0,0 +1,400 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace PHPStan\PhpDocParser\Parser;
|
||||
|
||||
use LogicException;
|
||||
use PHPStan\PhpDocParser\Ast\Comment;
|
||||
use PHPStan\PhpDocParser\Lexer\Lexer;
|
||||
use function array_pop;
|
||||
use function assert;
|
||||
use function count;
|
||||
use function in_array;
|
||||
use function strlen;
|
||||
use function substr;
|
||||
|
||||
class TokenIterator
|
||||
{
|
||||
|
||||
/** @var list<array{string, int, int}> */
|
||||
private array $tokens;
|
||||
|
||||
private int $index;
|
||||
|
||||
/** @var list<Comment> */
|
||||
private array $comments = [];
|
||||
|
||||
/** @var list<array{int, list<Comment>}> */
|
||||
private array $savePoints = [];
|
||||
|
||||
/** @var list<int> */
|
||||
private array $skippedTokenTypes = [Lexer::TOKEN_HORIZONTAL_WS];
|
||||
|
||||
private ?string $newline = null;
|
||||
|
||||
/**
|
||||
* @param list<array{string, int, int}> $tokens
|
||||
*/
|
||||
public function __construct(array $tokens, int $index = 0)
|
||||
{
|
||||
$this->tokens = $tokens;
|
||||
$this->index = $index;
|
||||
|
||||
$this->skipIrrelevantTokens();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<array{string, int, int}>
|
||||
*/
|
||||
public function getTokens(): array
|
||||
{
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
public function getContentBetween(int $startPos, int $endPos): string
|
||||
{
|
||||
if ($startPos < 0 || $endPos > count($this->tokens)) {
|
||||
throw new LogicException();
|
||||
}
|
||||
|
||||
$content = '';
|
||||
for ($i = $startPos; $i < $endPos; $i++) {
|
||||
$content .= $this->tokens[$i][Lexer::VALUE_OFFSET];
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
public function getTokenCount(): int
|
||||
{
|
||||
return count($this->tokens);
|
||||
}
|
||||
|
||||
public function currentTokenValue(): string
|
||||
{
|
||||
return $this->tokens[$this->index][Lexer::VALUE_OFFSET];
|
||||
}
|
||||
|
||||
public function currentTokenType(): int
|
||||
{
|
||||
return $this->tokens[$this->index][Lexer::TYPE_OFFSET];
|
||||
}
|
||||
|
||||
public function currentTokenOffset(): int
|
||||
{
|
||||
$offset = 0;
|
||||
for ($i = 0; $i < $this->index; $i++) {
|
||||
$offset += strlen($this->tokens[$i][Lexer::VALUE_OFFSET]);
|
||||
}
|
||||
|
||||
return $offset;
|
||||
}
|
||||
|
||||
public function currentTokenLine(): int
|
||||
{
|
||||
return $this->tokens[$this->index][Lexer::LINE_OFFSET];
|
||||
}
|
||||
|
||||
public function currentTokenIndex(): int
|
||||
{
|
||||
return $this->index;
|
||||
}
|
||||
|
||||
public function endIndexOfLastRelevantToken(): int
|
||||
{
|
||||
$endIndex = $this->currentTokenIndex();
|
||||
$endIndex--;
|
||||
while (in_array($this->tokens[$endIndex][Lexer::TYPE_OFFSET], $this->skippedTokenTypes, true)) {
|
||||
if (!isset($this->tokens[$endIndex - 1])) {
|
||||
break;
|
||||
}
|
||||
$endIndex--;
|
||||
}
|
||||
|
||||
return $endIndex;
|
||||
}
|
||||
|
||||
public function isCurrentTokenValue(string $tokenValue): bool
|
||||
{
|
||||
return $this->tokens[$this->index][Lexer::VALUE_OFFSET] === $tokenValue;
|
||||
}
|
||||
|
||||
public function isCurrentTokenType(int ...$tokenType): bool
|
||||
{
|
||||
return in_array($this->tokens[$this->index][Lexer::TYPE_OFFSET], $tokenType, true);
|
||||
}
|
||||
|
||||
public function isPrecededByHorizontalWhitespace(): bool
|
||||
{
|
||||
return ($this->tokens[$this->index - 1][Lexer::TYPE_OFFSET] ?? -1) === Lexer::TOKEN_HORIZONTAL_WS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ParserException
|
||||
*/
|
||||
public function consumeTokenType(int $tokenType): void
|
||||
{
|
||||
if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] !== $tokenType) {
|
||||
$this->throwError($tokenType);
|
||||
}
|
||||
|
||||
if ($tokenType === Lexer::TOKEN_PHPDOC_EOL) {
|
||||
if ($this->newline === null) {
|
||||
$this->detectNewline();
|
||||
}
|
||||
}
|
||||
|
||||
$this->next();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ParserException
|
||||
*/
|
||||
public function consumeTokenValue(int $tokenType, string $tokenValue): void
|
||||
{
|
||||
if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] !== $tokenType || $this->tokens[$this->index][Lexer::VALUE_OFFSET] !== $tokenValue) {
|
||||
$this->throwError($tokenType, $tokenValue);
|
||||
}
|
||||
|
||||
$this->next();
|
||||
}
|
||||
|
||||
/** @phpstan-impure */
|
||||
public function tryConsumeTokenValue(string $tokenValue): bool
|
||||
{
|
||||
if ($this->tokens[$this->index][Lexer::VALUE_OFFSET] !== $tokenValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->next();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Comment>
|
||||
*/
|
||||
public function flushComments(): array
|
||||
{
|
||||
$res = $this->comments;
|
||||
$this->comments = [];
|
||||
return $res;
|
||||
}
|
||||
|
||||
/** @phpstan-impure */
|
||||
public function tryConsumeTokenType(int $tokenType): bool
|
||||
{
|
||||
if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] !== $tokenType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($tokenType === Lexer::TOKEN_PHPDOC_EOL) {
|
||||
if ($this->newline === null) {
|
||||
$this->detectNewline();
|
||||
}
|
||||
}
|
||||
|
||||
$this->next();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use skipNewLineTokensAndConsumeComments instead (when parsing a type)
|
||||
*/
|
||||
public function skipNewLineTokens(): void
|
||||
{
|
||||
if (!$this->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
$foundNewLine = $this->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
|
||||
} while ($foundNewLine === true);
|
||||
}
|
||||
|
||||
public function skipNewLineTokensAndConsumeComments(): void
|
||||
{
|
||||
if ($this->currentTokenType() === Lexer::TOKEN_COMMENT) {
|
||||
$this->comments[] = new Comment($this->currentTokenValue(), $this->currentTokenLine(), $this->currentTokenIndex());
|
||||
$this->next();
|
||||
}
|
||||
|
||||
if (!$this->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
$foundNewLine = $this->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
|
||||
if ($this->currentTokenType() !== Lexer::TOKEN_COMMENT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->comments[] = new Comment($this->currentTokenValue(), $this->currentTokenLine(), $this->currentTokenIndex());
|
||||
$this->next();
|
||||
} while ($foundNewLine === true);
|
||||
}
|
||||
|
||||
private function detectNewline(): void
|
||||
{
|
||||
$value = $this->currentTokenValue();
|
||||
if (substr($value, 0, 2) === "\r\n") {
|
||||
$this->newline = "\r\n";
|
||||
} elseif (substr($value, 0, 1) === "\n") {
|
||||
$this->newline = "\n";
|
||||
}
|
||||
}
|
||||
|
||||
public function getSkippedHorizontalWhiteSpaceIfAny(): string
|
||||
{
|
||||
if ($this->index > 0 && $this->tokens[$this->index - 1][Lexer::TYPE_OFFSET] === Lexer::TOKEN_HORIZONTAL_WS) {
|
||||
return $this->tokens[$this->index - 1][Lexer::VALUE_OFFSET];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/** @phpstan-impure */
|
||||
public function joinUntil(int ...$tokenType): string
|
||||
{
|
||||
$s = '';
|
||||
while (!in_array($this->tokens[$this->index][Lexer::TYPE_OFFSET], $tokenType, true)) {
|
||||
$s .= $this->tokens[$this->index++][Lexer::VALUE_OFFSET];
|
||||
}
|
||||
return $s;
|
||||
}
|
||||
|
||||
public function next(): void
|
||||
{
|
||||
$this->index++;
|
||||
$this->skipIrrelevantTokens();
|
||||
}
|
||||
|
||||
private function skipIrrelevantTokens(): void
|
||||
{
|
||||
if (!isset($this->tokens[$this->index])) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (in_array($this->tokens[$this->index][Lexer::TYPE_OFFSET], $this->skippedTokenTypes, true)) {
|
||||
if (!isset($this->tokens[$this->index + 1])) {
|
||||
break;
|
||||
}
|
||||
$this->index++;
|
||||
}
|
||||
}
|
||||
|
||||
public function addEndOfLineToSkippedTokens(): void
|
||||
{
|
||||
$this->skippedTokenTypes = [Lexer::TOKEN_HORIZONTAL_WS, Lexer::TOKEN_PHPDOC_EOL];
|
||||
}
|
||||
|
||||
public function removeEndOfLineFromSkippedTokens(): void
|
||||
{
|
||||
$this->skippedTokenTypes = [Lexer::TOKEN_HORIZONTAL_WS];
|
||||
}
|
||||
|
||||
/** @phpstan-impure */
|
||||
public function forwardToTheEnd(): void
|
||||
{
|
||||
$lastToken = count($this->tokens) - 1;
|
||||
$this->index = $lastToken;
|
||||
}
|
||||
|
||||
public function pushSavePoint(): void
|
||||
{
|
||||
$this->savePoints[] = [$this->index, $this->comments];
|
||||
}
|
||||
|
||||
public function dropSavePoint(): void
|
||||
{
|
||||
array_pop($this->savePoints);
|
||||
}
|
||||
|
||||
public function rollback(): void
|
||||
{
|
||||
$savepoint = array_pop($this->savePoints);
|
||||
assert($savepoint !== null);
|
||||
[$this->index, $this->comments] = $savepoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ParserException
|
||||
*/
|
||||
private function throwError(int $expectedTokenType, ?string $expectedTokenValue = null): void
|
||||
{
|
||||
throw new ParserException(
|
||||
$this->currentTokenValue(),
|
||||
$this->currentTokenType(),
|
||||
$this->currentTokenOffset(),
|
||||
$expectedTokenType,
|
||||
$expectedTokenValue,
|
||||
$this->currentTokenLine(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the position is directly preceded by a certain token type.
|
||||
*
|
||||
* During this check TOKEN_HORIZONTAL_WS and TOKEN_PHPDOC_EOL are skipped
|
||||
*/
|
||||
public function hasTokenImmediatelyBefore(int $pos, int $expectedTokenType): bool
|
||||
{
|
||||
$tokens = $this->tokens;
|
||||
$pos--;
|
||||
for (; $pos >= 0; $pos--) {
|
||||
$token = $tokens[$pos];
|
||||
$type = $token[Lexer::TYPE_OFFSET];
|
||||
if ($type === $expectedTokenType) {
|
||||
return true;
|
||||
}
|
||||
if (!in_array($type, [
|
||||
Lexer::TOKEN_HORIZONTAL_WS,
|
||||
Lexer::TOKEN_PHPDOC_EOL,
|
||||
], true)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the position is directly followed by a certain token type.
|
||||
*
|
||||
* During this check TOKEN_HORIZONTAL_WS and TOKEN_PHPDOC_EOL are skipped
|
||||
*/
|
||||
public function hasTokenImmediatelyAfter(int $pos, int $expectedTokenType): bool
|
||||
{
|
||||
$tokens = $this->tokens;
|
||||
$pos++;
|
||||
for ($c = count($tokens); $pos < $c; $pos++) {
|
||||
$token = $tokens[$pos];
|
||||
$type = $token[Lexer::TYPE_OFFSET];
|
||||
if ($type === $expectedTokenType) {
|
||||
return true;
|
||||
}
|
||||
if (!in_array($type, [
|
||||
Lexer::TOKEN_HORIZONTAL_WS,
|
||||
Lexer::TOKEN_PHPDOC_EOL,
|
||||
], true)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getDetectedNewline(): ?string
|
||||
{
|
||||
return $this->newline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given position is immediately surrounded by parenthesis.
|
||||
*/
|
||||
public function hasParentheses(int $startPos, int $endPos): bool
|
||||
{
|
||||
return $this->hasTokenImmediatelyBefore($startPos, Lexer::TOKEN_OPEN_PARENTHESES)
|
||||
&& $this->hasTokenImmediatelyAfter($endPos, Lexer::TOKEN_CLOSE_PARENTHESES);
|
||||
}
|
||||
|
||||
}
|
||||
1128
backend/vendor/phpstan/phpdoc-parser/src/Parser/TypeParser.php
vendored
Normal file
1128
backend/vendor/phpstan/phpdoc-parser/src/Parser/TypeParser.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user