@@ -825,7 +825,12 @@ 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 ) {
@@ -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 )) {
@@ -3283,6 +3322,9 @@ private function isInterfaceMemberDeclarationStart(Token $token) {
32833322 // static-modifier
32843323 case TokenKind::StaticKeyword:
32853324
3325+ // readonly-modifier
3326+ case TokenKind::ReadonlyKeyword:
3327+
32863328 // class-modifier
32873329 case TokenKind::AbstractKeyword:
32883330 case TokenKind::FinalKeyword:
@@ -3457,6 +3499,7 @@ private function isTraitMemberDeclarationStart($token) {
34573499 case TokenKind::StaticKeyword:
34583500 case TokenKind::AbstractKeyword:
34593501 case TokenKind::FinalKeyword:
3502+ case TokenKind::ReadonlyKeyword:
34603503
34613504 // method-declaration
34623505 case TokenKind::FunctionKeyword:
@@ -3538,6 +3581,7 @@ private function isEnumMemberDeclarationStart($token) {
35383581 case TokenKind::ProtectedKeyword:
35393582 case TokenKind::PrivateKeyword:
35403583 // case TokenKind::VarKeyword:
3584+ // case TokenKind::ReadonlyKeyword:
35413585 case TokenKind::StaticKeyword:
35423586 case TokenKind::AbstractKeyword:
35433587 case TokenKind::FinalKeyword:
0 commit comments