1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\PhpDocParser\Parser;
4:
5: use PHPStan\PhpDocParser\Ast;
6: use PHPStan\PhpDocParser\Lexer\Lexer;
7: use function strtolower;
8: use function trim;
9:
10: class ConstExprParser
11: {
12:
13: public function parse(TokenIterator $tokens, bool $trimStrings = false): Ast\ConstExpr\ConstExprNode
14: {
15: if ($tokens->isCurrentTokenType(Lexer::TOKEN_FLOAT)) {
16: $value = $tokens->currentTokenValue();
17: $tokens->next();
18: return new Ast\ConstExpr\ConstExprFloatNode($value);
19: }
20:
21: if ($tokens->isCurrentTokenType(Lexer::TOKEN_INTEGER)) {
22: $value = $tokens->currentTokenValue();
23: $tokens->next();
24: return new Ast\ConstExpr\ConstExprIntegerNode($value);
25: }
26:
27: if ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) {
28: $value = $tokens->currentTokenValue();
29: if ($trimStrings) {
30: $value = trim($tokens->currentTokenValue(), "'");
31: }
32: $tokens->next();
33: return new Ast\ConstExpr\ConstExprStringNode($value);
34:
35: } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING)) {
36: $value = $tokens->currentTokenValue();
37: if ($trimStrings) {
38: $value = trim($tokens->currentTokenValue(), '"');
39: }
40: $tokens->next();
41: return new Ast\ConstExpr\ConstExprStringNode($value);
42:
43: } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) {
44: $identifier = $tokens->currentTokenValue();
45: $tokens->next();
46:
47: switch (strtolower($identifier)) {
48: case 'true':
49: return new Ast\ConstExpr\ConstExprTrueNode();
50: case 'false':
51: return new Ast\ConstExpr\ConstExprFalseNode();
52: case 'null':
53: return new Ast\ConstExpr\ConstExprNullNode();
54: case 'array':
55: $tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES);
56: return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_PARENTHESES);
57: }
58:
59: if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_COLON)) {
60: $classConstantName = '';
61: $lastType = null;
62: while (true) {
63: if ($lastType !== Lexer::TOKEN_IDENTIFIER && $tokens->currentTokenType() === Lexer::TOKEN_IDENTIFIER) {
64: $classConstantName .= $tokens->currentTokenValue();
65: $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
66: $lastType = Lexer::TOKEN_IDENTIFIER;
67:
68: continue;
69: }
70:
71: if ($lastType !== Lexer::TOKEN_WILDCARD && $tokens->tryConsumeTokenType(Lexer::TOKEN_WILDCARD)) {
72: $classConstantName .= '*';
73: $lastType = Lexer::TOKEN_WILDCARD;
74:
75: if ($tokens->getSkippedHorizontalWhiteSpaceIfAny() !== '') {
76: break;
77: }
78:
79: continue;
80: }
81:
82: if ($lastType === null) {
83: // trigger parse error if nothing valid was consumed
84: $tokens->consumeTokenType(Lexer::TOKEN_WILDCARD);
85: }
86:
87: break;
88: }
89:
90: return new Ast\ConstExpr\ConstFetchNode($identifier, $classConstantName);
91:
92: }
93:
94: return new Ast\ConstExpr\ConstFetchNode('', $identifier);
95:
96: } elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
97: return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_SQUARE_BRACKET);
98: }
99:
100: throw new ParserException(
101: $tokens->currentTokenValue(),
102: $tokens->currentTokenType(),
103: $tokens->currentTokenOffset(),
104: Lexer::TOKEN_IDENTIFIER
105: );
106: }
107:
108:
109: private function parseArray(TokenIterator $tokens, int $endToken): Ast\ConstExpr\ConstExprArrayNode
110: {
111: $items = [];
112:
113: if (!$tokens->tryConsumeTokenType($endToken)) {
114: do {
115: $items[] = $this->parseArrayItem($tokens);
116: } while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA) && !$tokens->isCurrentTokenType($endToken));
117: $tokens->consumeTokenType($endToken);
118: }
119:
120: return new Ast\ConstExpr\ConstExprArrayNode($items);
121: }
122:
123:
124: private function parseArrayItem(TokenIterator $tokens): Ast\ConstExpr\ConstExprArrayItemNode
125: {
126: $expr = $this->parse($tokens);
127:
128: if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_ARROW)) {
129: $key = $expr;
130: $value = $this->parse($tokens);
131:
132: } else {
133: $key = null;
134: $value = $expr;
135: }
136:
137: return new Ast\ConstExpr\ConstExprArrayItemNode($key, $value);
138: }
139:
140: }
141: