@@ -124,8 +124,12 @@ private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode
124124 } elseif ($ tokens ->isCurrentTokenType (Lexer::TOKEN_OPEN_SQUARE_BRACKET )) {
125125 $ type = $ this ->tryParseArrayOrOffsetAccess ($ tokens , $ type );
126126
127- } elseif (in_array ($ type ->name , ['array ' , 'list ' ], true ) && $ tokens ->isCurrentTokenType (Lexer::TOKEN_OPEN_CURLY_BRACKET ) && !$ tokens ->isPrecededByHorizontalWhitespace ()) {
128- $ type = $ this ->parseArrayShape ($ tokens , $ type , $ type ->name );
127+ } elseif (in_array ($ type ->name , ['array ' , 'list ' , 'object ' ], true ) && $ tokens ->isCurrentTokenType (Lexer::TOKEN_OPEN_CURLY_BRACKET ) && !$ tokens ->isPrecededByHorizontalWhitespace ()) {
128+ if ($ type ->name === 'object ' ) {
129+ $ type = $ this ->parseObjectShape ($ tokens );
130+ } else {
131+ $ type = $ this ->parseArrayShape ($ tokens , $ type , $ type ->name );
132+ }
129133
130134 if ($ tokens ->isCurrentTokenType (Lexer::TOKEN_OPEN_SQUARE_BRACKET )) {
131135 $ type = $ this ->tryParseArrayOrOffsetAccess ($ tokens , $ type );
@@ -582,4 +586,64 @@ private function parseArrayShapeKey(TokenIterator $tokens)
582586 return $ key ;
583587 }
584588
589+ /**
590+ * @phpstan-impure
591+ */
592+ private function parseObjectShape (TokenIterator $ tokens ): Ast \Type \ObjectShapeNode
593+ {
594+ $ tokens ->consumeTokenType (Lexer::TOKEN_OPEN_CURLY_BRACKET );
595+
596+ $ items = [];
597+
598+ do {
599+ $ tokens ->tryConsumeTokenType (Lexer::TOKEN_PHPDOC_EOL );
600+
601+ if ($ tokens ->tryConsumeTokenType (Lexer::TOKEN_CLOSE_CURLY_BRACKET )) {
602+ return new Ast \Type \ObjectShapeNode ($ items );
603+ }
604+
605+ $ items [] = $ this ->parseObjectShapeItem ($ tokens );
606+
607+ $ tokens ->tryConsumeTokenType (Lexer::TOKEN_PHPDOC_EOL );
608+ } while ($ tokens ->tryConsumeTokenType (Lexer::TOKEN_COMMA ));
609+
610+ $ tokens ->tryConsumeTokenType (Lexer::TOKEN_PHPDOC_EOL );
611+ $ tokens ->consumeTokenType (Lexer::TOKEN_CLOSE_CURLY_BRACKET );
612+
613+ return new Ast \Type \ObjectShapeNode ($ items );
614+ }
615+
616+ /** @phpstan-impure */
617+ private function parseObjectShapeItem (TokenIterator $ tokens ): Ast \Type \ObjectShapeItemNode
618+ {
619+ $ key = $ this ->parseObjectShapeKey ($ tokens );
620+ $ optional = $ tokens ->tryConsumeTokenType (Lexer::TOKEN_NULLABLE );
621+ $ tokens ->consumeTokenType (Lexer::TOKEN_COLON );
622+ $ value = $ this ->parse ($ tokens );
623+
624+ return new Ast \Type \ObjectShapeItemNode ($ key , $ optional , $ value );
625+ }
626+
627+ /**
628+ * @phpstan-impure
629+ * @return Ast\ConstExpr\ConstExprStringNode|Ast\Type\IdentifierTypeNode
630+ */
631+ private function parseObjectShapeKey (TokenIterator $ tokens )
632+ {
633+ if ($ tokens ->isCurrentTokenType (Lexer::TOKEN_SINGLE_QUOTED_STRING )) {
634+ $ key = new Ast \ConstExpr \ConstExprStringNode (trim ($ tokens ->currentTokenValue (), "' " ));
635+ $ tokens ->next ();
636+
637+ } elseif ($ tokens ->isCurrentTokenType (Lexer::TOKEN_DOUBLE_QUOTED_STRING )) {
638+ $ key = new Ast \ConstExpr \ConstExprStringNode (trim ($ tokens ->currentTokenValue (), '" ' ));
639+ $ tokens ->next ();
640+
641+ } else {
642+ $ key = new Ast \Type \IdentifierTypeNode ($ tokens ->currentTokenValue ());
643+ $ tokens ->consumeTokenType (Lexer::TOKEN_IDENTIFIER );
644+ }
645+
646+ return $ key ;
647+ }
648+
585649}
0 commit comments