2021-04-04 09:01:11 +00:00
|
|
|
<?php
|
|
|
|
|
2021-05-09 20:15:43 +00:00
|
|
|
declare (strict_types=1);
|
2022-06-06 17:12:56 +00:00
|
|
|
namespace Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser;
|
2021-04-04 09:01:11 +00:00
|
|
|
|
2022-06-06 17:12:56 +00:00
|
|
|
use PhpParser\Node;
|
|
|
|
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFalseNode;
|
|
|
|
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
|
|
|
|
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode;
|
|
|
|
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprTrueNode;
|
|
|
|
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
|
|
|
use PHPStan\PhpDocParser\Lexer\Lexer;
|
|
|
|
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
|
|
|
|
use Rector\BetterPhpDocParser\PhpDocParser\ClassAnnotationMatcher;
|
|
|
|
use Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser;
|
|
|
|
use Rector\BetterPhpDocParser\ValueObject\Parser\BetterTokenIterator;
|
|
|
|
use Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey;
|
|
|
|
use Rector\Core\Configuration\CurrentNodeProvider;
|
|
|
|
use Rector\Core\Exception\ShouldNotHappenException;
|
2023-02-02 09:24:12 +00:00
|
|
|
use RectorPrefix202302\Symfony\Contracts\Service\Attribute\Required;
|
2021-04-04 09:01:11 +00:00
|
|
|
final class PlainValueParser
|
|
|
|
{
|
|
|
|
/**
|
2021-05-12 13:15:45 +00:00
|
|
|
* @var \Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser
|
2021-04-04 09:01:11 +00:00
|
|
|
*/
|
|
|
|
private $staticDoctrineAnnotationParser;
|
|
|
|
/**
|
2021-05-12 13:15:45 +00:00
|
|
|
* @var \Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser\ArrayParser
|
2021-04-04 09:01:11 +00:00
|
|
|
*/
|
2021-05-10 23:39:21 +00:00
|
|
|
private $arrayParser;
|
2021-04-04 09:01:11 +00:00
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-05-10 23:39:21 +00:00
|
|
|
* @var \Rector\BetterPhpDocParser\PhpDocParser\ClassAnnotationMatcher
|
2021-04-04 09:01:11 +00:00
|
|
|
*/
|
2021-05-10 23:39:21 +00:00
|
|
|
private $classAnnotationMatcher;
|
2021-04-13 18:51:41 +00:00
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-05-10 23:39:21 +00:00
|
|
|
* @var \Rector\Core\Configuration\CurrentNodeProvider
|
2021-04-13 18:51:41 +00:00
|
|
|
*/
|
2021-05-10 23:39:21 +00:00
|
|
|
private $currentNodeProvider;
|
2022-06-07 08:22:29 +00:00
|
|
|
public function __construct(ClassAnnotationMatcher $classAnnotationMatcher, CurrentNodeProvider $currentNodeProvider)
|
2021-05-09 20:15:43 +00:00
|
|
|
{
|
2021-04-04 09:01:11 +00:00
|
|
|
$this->classAnnotationMatcher = $classAnnotationMatcher;
|
|
|
|
$this->currentNodeProvider = $currentNodeProvider;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* @required
|
|
|
|
*/
|
2022-06-07 08:22:29 +00:00
|
|
|
public function autowire(StaticDoctrineAnnotationParser $staticDoctrineAnnotationParser, \Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser\ArrayParser $arrayParser) : void
|
2021-05-09 20:15:43 +00:00
|
|
|
{
|
2021-04-04 09:01:11 +00:00
|
|
|
$this->staticDoctrineAnnotationParser = $staticDoctrineAnnotationParser;
|
2021-04-13 18:51:41 +00:00
|
|
|
$this->arrayParser = $arrayParser;
|
2021-04-04 09:01:11 +00:00
|
|
|
}
|
|
|
|
/**
|
2022-06-24 05:46:03 +00:00
|
|
|
* @return string|mixed[]|ConstExprNode|DoctrineAnnotationTagValueNode
|
2021-04-04 09:01:11 +00:00
|
|
|
*/
|
2022-06-07 08:22:29 +00:00
|
|
|
public function parseValue(BetterTokenIterator $tokenIterator)
|
2021-04-04 09:01:11 +00:00
|
|
|
{
|
|
|
|
$currentTokenValue = $tokenIterator->currentTokenValue();
|
|
|
|
// temporary hackaround multi-line doctrine annotations
|
2022-06-07 08:22:29 +00:00
|
|
|
if ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_END)) {
|
2021-04-04 09:01:11 +00:00
|
|
|
return $currentTokenValue;
|
|
|
|
}
|
|
|
|
// consume the token
|
2022-06-07 08:22:29 +00:00
|
|
|
$isOpenCurlyArray = $tokenIterator->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET);
|
2021-04-13 18:51:41 +00:00
|
|
|
if ($isOpenCurlyArray) {
|
|
|
|
return $this->arrayParser->parseCurlyArray($tokenIterator);
|
|
|
|
}
|
2021-04-04 09:01:11 +00:00
|
|
|
$tokenIterator->next();
|
|
|
|
// normalize value
|
2022-07-18 15:25:27 +00:00
|
|
|
$constExprNode = $this->matchConstantValue($currentTokenValue);
|
|
|
|
if ($constExprNode !== null) {
|
|
|
|
return $constExprNode;
|
2021-04-04 09:01:11 +00:00
|
|
|
}
|
2022-06-07 08:22:29 +00:00
|
|
|
while ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON) || $tokenIterator->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) {
|
2021-04-04 09:01:11 +00:00
|
|
|
$currentTokenValue .= $tokenIterator->currentTokenValue();
|
|
|
|
$tokenIterator->next();
|
|
|
|
}
|
|
|
|
// nested entity!
|
2022-06-07 08:22:29 +00:00
|
|
|
if ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
|
2021-04-13 18:51:41 +00:00
|
|
|
return $this->parseNestedDoctrineAnnotationTagValueNode($currentTokenValue, $tokenIterator);
|
|
|
|
}
|
2021-06-30 01:01:00 +00:00
|
|
|
$start = $tokenIterator->currentPosition();
|
2021-07-10 13:57:04 +00:00
|
|
|
// from "quote to quote"
|
|
|
|
if ($currentTokenValue === '"') {
|
|
|
|
do {
|
2021-06-30 01:01:00 +00:00
|
|
|
$tokenIterator->next();
|
2021-07-10 13:57:04 +00:00
|
|
|
} while (\strpos($tokenIterator->currentTokenValue(), '"') === \false);
|
2021-06-30 01:01:00 +00:00
|
|
|
}
|
|
|
|
$end = $tokenIterator->currentPosition();
|
|
|
|
if ($start + 1 < $end) {
|
|
|
|
return $tokenIterator->printFromTo($start, $end);
|
|
|
|
}
|
2021-04-13 18:51:41 +00:00
|
|
|
return $currentTokenValue;
|
|
|
|
}
|
2022-06-07 08:22:29 +00:00
|
|
|
private function parseNestedDoctrineAnnotationTagValueNode(string $currentTokenValue, BetterTokenIterator $tokenIterator) : DoctrineAnnotationTagValueNode
|
2021-05-09 20:15:43 +00:00
|
|
|
{
|
2021-04-13 18:51:41 +00:00
|
|
|
// @todo
|
|
|
|
$annotationShortName = $currentTokenValue;
|
|
|
|
$values = $this->staticDoctrineAnnotationParser->resolveAnnotationMethodCall($tokenIterator);
|
|
|
|
$currentNode = $this->currentNodeProvider->getNode();
|
2022-06-07 08:22:29 +00:00
|
|
|
if (!$currentNode instanceof Node) {
|
|
|
|
throw new ShouldNotHappenException();
|
2021-04-13 18:51:41 +00:00
|
|
|
}
|
2021-05-09 20:15:43 +00:00
|
|
|
$fullyQualifiedAnnotationClass = $this->classAnnotationMatcher->resolveTagFullyQualifiedName($annotationShortName, $currentNode);
|
2021-04-13 18:51:41 +00:00
|
|
|
// keep the last ")"
|
2022-06-07 08:22:29 +00:00
|
|
|
$tokenIterator->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
|
2021-12-18 10:25:56 +00:00
|
|
|
if ($tokenIterator->currentTokenValue() === ')') {
|
2022-06-07 08:22:29 +00:00
|
|
|
$tokenIterator->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
|
2021-12-18 10:25:56 +00:00
|
|
|
}
|
2021-07-01 13:22:55 +00:00
|
|
|
// keep original name to differentiate between short and FQN class
|
2022-06-07 08:22:29 +00:00
|
|
|
$identifierTypeNode = new IdentifierTypeNode($annotationShortName);
|
|
|
|
$identifierTypeNode->setAttribute(PhpDocAttributeKey::RESOLVED_CLASS, $fullyQualifiedAnnotationClass);
|
|
|
|
return new DoctrineAnnotationTagValueNode($identifierTypeNode, $annotationShortName, $values);
|
2021-04-04 09:01:11 +00:00
|
|
|
}
|
2022-08-11 14:29:35 +00:00
|
|
|
private function matchConstantValue(string $currentTokenValue) : ?\PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode
|
2021-06-30 01:01:00 +00:00
|
|
|
{
|
|
|
|
if (\strtolower($currentTokenValue) === 'false') {
|
2022-06-07 08:22:29 +00:00
|
|
|
return new ConstExprFalseNode();
|
2021-06-30 01:01:00 +00:00
|
|
|
}
|
|
|
|
if (\strtolower($currentTokenValue) === 'true') {
|
2022-06-07 08:22:29 +00:00
|
|
|
return new ConstExprTrueNode();
|
2021-06-30 01:01:00 +00:00
|
|
|
}
|
|
|
|
if (!\is_numeric($currentTokenValue)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
if ((string) (int) $currentTokenValue !== $currentTokenValue) {
|
|
|
|
return null;
|
|
|
|
}
|
2022-06-07 08:22:29 +00:00
|
|
|
return new ConstExprIntegerNode($currentTokenValue);
|
2021-06-30 01:01:00 +00:00
|
|
|
}
|
2021-04-04 09:01:11 +00:00
|
|
|
}
|