@@ -7168,22 +7168,37 @@ public function format($source)
71687168 break;
71697169 case T_FINAL:
71707170 case T_ABSTRACT:
7171- if (! $this->rightTokenIs([T_CLASS]) && ! $this->leftTokenIs(T_DOUBLE_COLON)) {
7172- $finalOrAbstract = $text;
7173- $skipWhitespaces = true;
7171+ if ($this->leftTokenIs(T_DOUBLE_COLON)) {
7172+ $this->appendCode($text);
71747173 break;
71757174 }
7176- $this->appendCode($text);
7175+
7176+ $nextIdx = $this->rightUsefulTokenIdx();
7177+ if (isset($this->tkns[$nextIdx]) && T_READONLY === $this->tkns[$nextIdx][0]) {
7178+ $nextIdx = $this->rightTokenSubsetAtIdx($this->tkns, $nextIdx, $this->ignoreFutileTokens);
7179+ }
7180+
7181+ if (isset($this->tkns[$nextIdx]) && in_array($this->tkns[$nextIdx][0], [T_CLASS, T_INTERFACE, T_TRAIT, T_ENUM], true)) {
7182+ $this->appendCode($text);
7183+ break;
7184+ }
7185+
7186+ $finalOrAbstract = $text;
7187+ $skipWhitespaces = true;
71777188 break;
71787189 case T_READONLY:
71797190 if (! $this->leftTokenIs(T_DOUBLE_COLON)) {
7180- if (! is_null($visibility)) {
7191+ // `readonly` can be a class modifier (PHP 8.2): `readonly class Foo {}`
7192+ // Handle it here so combinations like `final readonly class` and
7193+ // `abstract readonly class` keep the modifier on the class.
7194+ if ($this->rightUsefulTokenIs([T_CLASS, T_INTERFACE, T_TRAIT, T_ENUM])) {
71817195 $readonly = $text;
71827196 $skipWhitespaces = true;
71837197 break;
7184- } elseif ($this->leftTokenIs([T_FINAL])) { // ??
7185- $readonly = $text;
7186- $visibility = 'public';
7198+ }
7199+ if (! is_null($visibility)) {
7200+ $readonly = $text;
7201+ $skipWhitespaces = true;
71877202 break;
71887203 } elseif (! $this->rightTokenIs([T_VARIABLE, T_DOUBLE_COLON]) && ! $this->leftTokenIs([T_NEW, ST_COMMA])) {
71897204 $readonly = $text;
0 commit comments