@@ -825,12 +825,17 @@ private function parseParameterFn() {
825825 if ($ this ->token ->kind === TokenKind::AttributeToken) {
826826 $ parameter ->attributes = $ this ->parseAttributeGroups ($ parameter );
827827 }
828+ // Note that parameter modifiers are allowed to be repeated by the parser in php 8.1 (it is a compiler error)
829+ //
830+ // TODO: Remove the visibilityToken in a future backwards incompatible release
828831 $ parameter ->visibilityToken = $ this ->eatOptional ([TokenKind::PublicKeyword, TokenKind::ProtectedKeyword, TokenKind::PrivateKeyword]);
832+ $ parameter ->modifiers = $ this ->parseParameterModifiers () ?: null ;
833+
829834 $ parameter ->questionToken = $ this ->eatOptional1 (TokenKind::QuestionToken);
830835 $ parameter ->typeDeclarationList = $ this ->tryParseParameterTypeDeclarationList ($ parameter );
831836 if ($ parameter ->typeDeclarationList ) {
832837 $ children = $ parameter ->typeDeclarationList ->children ;
833- if (end ($ children ) instanceof MissingToken && ($ children [count ($ children ) - 2 ]->kind ?? null ) === TokenKind::AmpersandToken) {
838+ if (end ($ children ) instanceof MissingToken && ($ children [\ count ($ children ) - 2 ]->kind ?? null ) === TokenKind::AmpersandToken) {
834839 array_pop ($ parameter ->typeDeclarationList ->children );
835840 $ parameter ->byRefToken = array_pop ($ parameter ->typeDeclarationList ->children );
836841 if (!$ parameter ->typeDeclarationList ->children ) {
@@ -964,6 +969,9 @@ private function isClassMemberDeclarationStart(Token $token) {
964969 // static-modifier
965970 case TokenKind::StaticKeyword:
966971
972+ // readonly-modifier
973+ case TokenKind::ReadonlyKeyword:
974+
967975 // class-modifier
968976 case TokenKind::AbstractKeyword:
969977 case TokenKind::FinalKeyword:
@@ -1443,7 +1451,7 @@ private function parseReservedWordExpression($parentNode) {
14431451 return $ reservedWord ;
14441452 }
14451453
1446- private function isModifier ($ token ) {
1454+ private function isModifier ($ token ): bool {
14471455 switch ($ token ->kind ) {
14481456 // class-modifier
14491457 case TokenKind::AbstractKeyword:
@@ -1457,14 +1465,45 @@ private function isModifier($token) {
14571465 // static-modifier
14581466 case TokenKind::StaticKeyword:
14591467
1468+ // readonly-modifier
1469+ case TokenKind::ReadonlyKeyword:
1470+
14601471 // var
14611472 case TokenKind::VarKeyword:
14621473 return true ;
14631474 }
14641475 return false ;
14651476 }
14661477
1467- private function parseModifiers () {
1478+ private function isParameterModifier ($ token ): bool {
1479+ switch ($ token ->kind ) {
1480+ // visibility-modifier
1481+ case TokenKind::PublicKeyword:
1482+ case TokenKind::ProtectedKeyword:
1483+ case TokenKind::PrivateKeyword:
1484+
1485+ // readonly-modifier
1486+ case TokenKind::ReadonlyKeyword:
1487+
1488+ return true ;
1489+ }
1490+ return false ;
1491+ }
1492+
1493+ /** @return Token[] */
1494+ private function parseParameterModifiers (): array {
1495+ $ modifiers = [];
1496+ $ token = $ this ->getCurrentToken ();
1497+ while ($ this ->isParameterModifier ($ token )) {
1498+ $ modifiers [] = $ token ;
1499+ $ this ->advanceToken ();
1500+ $ token = $ this ->getCurrentToken ();
1501+ }
1502+ return $ modifiers ;
1503+ }
1504+
1505+ /** @return Token[] */
1506+ private function parseModifiers (): array {
14681507 $ modifiers = [];
14691508 $ token = $ this ->getCurrentToken ();
14701509 while ($ this ->isModifier ($ token )) {
@@ -3101,14 +3140,27 @@ private function parseObjectCreationExpression($parentNode) {
31013140 return $ objectCreationExpression ;
31023141 }
31033142
3143+ /**
3144+ * @return DelimitedList\ArgumentExpressionList|null
3145+ */
31043146 private function parseArgumentExpressionList ($ parentNode ) {
3105- return $ this ->parseDelimitedList (
3147+ $ list = $ this ->parseDelimitedList (
31063148 DelimitedList \ArgumentExpressionList::class,
31073149 TokenKind::CommaToken,
31083150 $ this ->isArgumentExpressionStartFn (),
31093151 $ this ->parseArgumentExpressionFn (),
31103152 $ parentNode
31113153 );
3154+ $ children = $ list ->children ?? null ;
3155+ if (is_array ($ children ) && \count ($ children ) === 1 ) {
3156+ $ arg = $ children [0 ];
3157+ if ($ arg instanceof ArgumentExpression) {
3158+ if ($ arg ->dotDotDotToken && $ arg ->expression instanceof MissingToken && !$ arg ->colonToken && !$ arg ->name ) {
3159+ $ arg ->expression = null ;
3160+ }
3161+ }
3162+ }
3163+ return $ list ;
31123164 }
31133165
31143166 /**
@@ -3283,6 +3335,9 @@ private function isInterfaceMemberDeclarationStart(Token $token) {
32833335 // static-modifier
32843336 case TokenKind::StaticKeyword:
32853337
3338+ // readonly-modifier
3339+ case TokenKind::ReadonlyKeyword:
3340+
32863341 // class-modifier
32873342 case TokenKind::AbstractKeyword:
32883343 case TokenKind::FinalKeyword:
@@ -3457,6 +3512,7 @@ private function isTraitMemberDeclarationStart($token) {
34573512 case TokenKind::StaticKeyword:
34583513 case TokenKind::AbstractKeyword:
34593514 case TokenKind::FinalKeyword:
3515+ case TokenKind::ReadonlyKeyword:
34603516
34613517 // method-declaration
34623518 case TokenKind::FunctionKeyword:
@@ -3537,7 +3593,6 @@ private function isEnumMemberDeclarationStart($token) {
35373593 case TokenKind::PublicKeyword:
35383594 case TokenKind::ProtectedKeyword:
35393595 case TokenKind::PrivateKeyword:
3540- // case TokenKind::VarKeyword:
35413596 case TokenKind::StaticKeyword:
35423597 case TokenKind::AbstractKeyword:
35433598 case TokenKind::FinalKeyword:
0 commit comments