classAnnotationMatcher = $classAnnotationMatcher; $this->currentNodeProvider = $currentNodeProvider; } /** * @required */ public function autowire(StaticDoctrineAnnotationParser $staticDoctrineAnnotationParser, \Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser\ArrayParser $arrayParser) : void { $this->staticDoctrineAnnotationParser = $staticDoctrineAnnotationParser; $this->arrayParser = $arrayParser; } /** * @return string|mixed[]|ConstExprNode|DoctrineAnnotationTagValueNode */ public function parseValue(BetterTokenIterator $tokenIterator) { $currentTokenValue = $tokenIterator->currentTokenValue(); // temporary hackaround multi-line doctrine annotations if ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_END)) { return $currentTokenValue; } // consume the token $isOpenCurlyArray = $tokenIterator->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET); if ($isOpenCurlyArray) { return $this->arrayParser->parseCurlyArray($tokenIterator); } $tokenIterator->next(); // normalize value $constExprNode = $this->matchConstantValue($currentTokenValue); if ($constExprNode instanceof ConstExprNode) { return $constExprNode; } while ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON) || $tokenIterator->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) { $currentTokenValue .= $tokenIterator->currentTokenValue(); $tokenIterator->next(); } // nested entity! if ($tokenIterator->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { return $this->parseNestedDoctrineAnnotationTagValueNode($currentTokenValue, $tokenIterator); } $start = $tokenIterator->currentPosition(); // from "quote to quote" if ($currentTokenValue === '"') { do { $tokenIterator->next(); } while (\strpos($tokenIterator->currentTokenValue(), '"') === \false); } $end = $tokenIterator->currentPosition(); if ($start + 1 < $end) { return $tokenIterator->printFromTo($start, $end); } return $currentTokenValue; } private function parseNestedDoctrineAnnotationTagValueNode(string $currentTokenValue, BetterTokenIterator $tokenIterator) : DoctrineAnnotationTagValueNode { // @todo $annotationShortName = $currentTokenValue; $values = $this->staticDoctrineAnnotationParser->resolveAnnotationMethodCall($tokenIterator); $currentNode = $this->currentNodeProvider->getNode(); if (!$currentNode instanceof Node) { throw new ShouldNotHappenException(); } $fullyQualifiedAnnotationClass = $this->classAnnotationMatcher->resolveTagFullyQualifiedName($annotationShortName, $currentNode); // keep the last ")" $tokenIterator->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); if ($tokenIterator->currentTokenValue() === ')') { $tokenIterator->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); } // keep original name to differentiate between short and FQN class $identifierTypeNode = new IdentifierTypeNode($annotationShortName); $identifierTypeNode->setAttribute(PhpDocAttributeKey::RESOLVED_CLASS, $fullyQualifiedAnnotationClass); return new DoctrineAnnotationTagValueNode($identifierTypeNode, $annotationShortName, $values); } private function matchConstantValue(string $currentTokenValue) : ?\PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode { if (\strtolower($currentTokenValue) === 'false') { return new ConstExprFalseNode(); } if (\strtolower($currentTokenValue) === 'true') { return new ConstExprTrueNode(); } if (!\is_numeric($currentTokenValue)) { return null; } if ((string) (int) $currentTokenValue !== $currentTokenValue) { return null; } return new ConstExprIntegerNode($currentTokenValue); } }