@flarum/cli 3.0.4 → 3.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +1 -1
  2. package/lib/providers/php-provider.js +6 -1
  3. package/oclif.manifest.json +1 -1
  4. package/package.json +1 -1
  5. package/php-subsystem/composer.json +3 -0
  6. package/php-subsystem/composer.lock +76 -66
  7. package/php-subsystem/index.php +9 -0
  8. package/php-subsystem/vendor/composer/installed.json +63 -56
  9. package/php-subsystem/vendor/composer/installed.php +9 -9
  10. package/php-subsystem/vendor/composer/platform_check.php +2 -2
  11. package/php-subsystem/vendor/nikic/php-parser/README.md +1 -1
  12. package/php-subsystem/vendor/nikic/php-parser/composer.json +41 -41
  13. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/Builder/ClassConst.php +2 -2
  14. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/Builder/EnumCase.php +0 -1
  15. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php +22 -0
  16. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/Builder/Property.php +63 -1
  17. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/BuilderFactory.php +1 -1
  18. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/BuilderHelpers.php +6 -1
  19. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/Comment.php +2 -0
  20. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/Error.php +2 -0
  21. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/Internal/TokenStream.php +12 -5
  22. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/Lexer/Emulative.php +4 -4
  23. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/AsymmetricVisibilityTokenEmulator.php +93 -0
  24. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php +6 -2
  25. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/PropertyTokenEmulator.php +19 -0
  26. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/Modifiers.php +42 -26
  27. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/NameContext.php +1 -1
  28. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/Node/Identifier.php +11 -1
  29. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/Node/Name.php +10 -1
  30. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/Node/Param.php +28 -2
  31. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/Node/PropertyHook.php +78 -0
  32. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Property.php +15 -0
  33. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Property.php +27 -2
  34. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/Node.php +4 -0
  35. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/NodeAbstract.php +3 -0
  36. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/NodeDumper.php +9 -0
  37. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NameResolver.php +6 -0
  38. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/Parser/Php7.php +1210 -1119
  39. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/Parser/Php8.php +1210 -1141
  40. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/ParserAbstract.php +82 -57
  41. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/PhpVersion.php +1 -1
  42. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/PrettyPrinter/Standard.php +17 -2
  43. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/PrettyPrinterAbstract.php +49 -11
  44. package/php-subsystem/vendor/nikic/php-parser/lib/PhpParser/compatibility_tokens.php +5 -0
@@ -7,6 +7,7 @@ namespace PhpParser;
7
7
  * turn is based on work by Masato Bito.
8
8
  */
9
9
 
10
+ use PhpParser\Node\Arg;
10
11
  use PhpParser\Node\Expr;
11
12
  use PhpParser\Node\Expr\Array_;
12
13
  use PhpParser\Node\Expr\Cast\Double;
@@ -14,6 +15,7 @@ use PhpParser\Node\Identifier;
14
15
  use PhpParser\Node\InterpolatedStringPart;
15
16
  use PhpParser\Node\Name;
16
17
  use PhpParser\Node\Param;
18
+ use PhpParser\Node\PropertyHook;
17
19
  use PhpParser\Node\Scalar\InterpolatedString;
18
20
  use PhpParser\Node\Scalar\Int_;
19
21
  use PhpParser\Node\Scalar\String_;
@@ -1137,45 +1139,87 @@ abstract class ParserAbstract implements Parser {
1137
1139
  }
1138
1140
 
1139
1141
  protected function checkClassConst(ClassConst $node, int $modifierPos): void {
1140
- if ($node->flags & Modifiers::STATIC) {
1142
+ foreach ([Modifiers::STATIC, Modifiers::ABSTRACT, Modifiers::READONLY] as $modifier) {
1143
+ if ($node->flags & $modifier) {
1144
+ $this->emitError(new Error(
1145
+ "Cannot use '" . Modifiers::toString($modifier) . "' as constant modifier",
1146
+ $this->getAttributesAt($modifierPos)));
1147
+ }
1148
+ }
1149
+ }
1150
+
1151
+ protected function checkUseUse(UseItem $node, int $namePos): void {
1152
+ if ($node->alias && $node->alias->isSpecialClassName()) {
1141
1153
  $this->emitError(new Error(
1142
- "Cannot use 'static' as constant modifier",
1143
- $this->getAttributesAt($modifierPos)));
1154
+ sprintf(
1155
+ 'Cannot use %s as %s because \'%2$s\' is a special class name',
1156
+ $node->name, $node->alias
1157
+ ),
1158
+ $this->getAttributesAt($namePos)
1159
+ ));
1144
1160
  }
1145
- if ($node->flags & Modifiers::ABSTRACT) {
1161
+ }
1162
+
1163
+ /** @param PropertyHook[] $hooks */
1164
+ protected function checkPropertyHookList(array $hooks, int $hookPos): void {
1165
+ if (empty($hooks)) {
1146
1166
  $this->emitError(new Error(
1147
- "Cannot use 'abstract' as constant modifier",
1148
- $this->getAttributesAt($modifierPos)));
1167
+ 'Property hook list cannot be empty', $this->getAttributesAt($hookPos)));
1149
1168
  }
1150
- if ($node->flags & Modifiers::READONLY) {
1169
+ }
1170
+
1171
+ protected function checkPropertyHook(PropertyHook $hook, ?int $paramListPos): void {
1172
+ $name = $hook->name->toLowerString();
1173
+ if ($name !== 'get' && $name !== 'set') {
1151
1174
  $this->emitError(new Error(
1152
- "Cannot use 'readonly' as constant modifier",
1153
- $this->getAttributesAt($modifierPos)));
1175
+ 'Unknown hook "' . $hook->name . '", expected "get" or "set"',
1176
+ $hook->name->getAttributes()));
1177
+ }
1178
+ if ($name === 'get' && $paramListPos !== null) {
1179
+ $this->emitError(new Error(
1180
+ 'get hook must not have a parameter list', $this->getAttributesAt($paramListPos)));
1154
1181
  }
1155
1182
  }
1156
1183
 
1157
- protected function checkProperty(Property $node, int $modifierPos): void {
1158
- if ($node->flags & Modifiers::ABSTRACT) {
1159
- $this->emitError(new Error('Properties cannot be declared abstract',
1160
- $this->getAttributesAt($modifierPos)));
1184
+ protected function checkPropertyHookModifiers(int $a, int $b, int $modifierPos): void {
1185
+ try {
1186
+ Modifiers::verifyModifier($a, $b);
1187
+ } catch (Error $error) {
1188
+ $error->setAttributes($this->getAttributesAt($modifierPos));
1189
+ $this->emitError($error);
1161
1190
  }
1162
1191
 
1163
- if ($node->flags & Modifiers::FINAL) {
1164
- $this->emitError(new Error('Properties cannot be declared final',
1192
+ if ($b != Modifiers::FINAL) {
1193
+ $this->emitError(new Error(
1194
+ 'Cannot use the ' . Modifiers::toString($b) . ' modifier on a property hook',
1165
1195
  $this->getAttributesAt($modifierPos)));
1166
1196
  }
1167
1197
  }
1168
1198
 
1169
- protected function checkUseUse(UseItem $node, int $namePos): void {
1170
- if ($node->alias && $node->alias->isSpecialClassName()) {
1171
- $this->emitError(new Error(
1172
- sprintf(
1173
- 'Cannot use %s as %s because \'%2$s\' is a special class name',
1174
- $node->name, $node->alias
1175
- ),
1176
- $this->getAttributesAt($namePos)
1177
- ));
1199
+ /** @param array<Node\Arg|Node\VariadicPlaceholder> $args */
1200
+ private function isSimpleExit(array $args): bool {
1201
+ if (\count($args) === 0) {
1202
+ return true;
1203
+ }
1204
+ if (\count($args) === 1) {
1205
+ $arg = $args[0];
1206
+ return $arg instanceof Arg && $arg->name === null &&
1207
+ $arg->byRef === false && $arg->unpack === false;
1208
+ }
1209
+ return false;
1210
+ }
1211
+
1212
+ /**
1213
+ * @param array<Node\Arg|Node\VariadicPlaceholder> $args
1214
+ * @param array<string, mixed> $attrs
1215
+ */
1216
+ protected function createExitExpr(string $name, int $namePos, array $args, array $attrs): Expr {
1217
+ if ($this->isSimpleExit($args)) {
1218
+ // Create Exit node for backwards compatibility.
1219
+ $attrs['kind'] = strtolower($name) === 'exit' ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE;
1220
+ return new Expr\Exit_(\count($args) === 1 ? $args[0]->value : null, $attrs);
1178
1221
  }
1222
+ return new Expr\FuncCall(new Name($name, $this->getAttributesAt($namePos)), $args, $attrs);
1179
1223
  }
1180
1224
 
1181
1225
  /**
@@ -1190,42 +1234,23 @@ abstract class ParserAbstract implements Parser {
1190
1234
  protected function createTokenMap(): array {
1191
1235
  $tokenMap = [];
1192
1236
 
1193
- for ($i = 0; $i < 1000; ++$i) {
1194
- if ($i < 256) {
1195
- // Single-char tokens use an identity mapping.
1196
- $tokenMap[$i] = $i;
1197
- } elseif (\T_DOUBLE_COLON === $i) {
1198
- // T_DOUBLE_COLON is equivalent to T_PAAMAYIM_NEKUDOTAYIM
1199
- $tokenMap[$i] = static::T_PAAMAYIM_NEKUDOTAYIM;
1200
- } elseif (\T_OPEN_TAG_WITH_ECHO === $i) {
1201
- // T_OPEN_TAG_WITH_ECHO with dropped T_OPEN_TAG results in T_ECHO
1202
- $tokenMap[$i] = static::T_ECHO;
1203
- } elseif (\T_CLOSE_TAG === $i) {
1204
- // T_CLOSE_TAG is equivalent to ';'
1205
- $tokenMap[$i] = ord(';');
1206
- } elseif ('UNKNOWN' !== $name = token_name($i)) {
1207
- if (defined($name = static::class . '::' . $name)) {
1208
- // Other tokens can be mapped directly
1209
- $tokenMap[$i] = constant($name);
1210
- }
1237
+ // Single-char tokens use an identity mapping.
1238
+ for ($i = 0; $i < 256; ++$i) {
1239
+ $tokenMap[$i] = $i;
1240
+ }
1241
+
1242
+ foreach ($this->symbolToName as $name) {
1243
+ if ($name[0] === 'T') {
1244
+ $tokenMap[\constant($name)] = constant(static::class . '::' . $name);
1211
1245
  }
1212
1246
  }
1213
1247
 
1214
- // Assign tokens for which we define compatibility constants, as token_name() does not know them.
1215
- $tokenMap[\T_FN] = static::T_FN;
1216
- $tokenMap[\T_COALESCE_EQUAL] = static::T_COALESCE_EQUAL;
1217
- $tokenMap[\T_NAME_QUALIFIED] = static::T_NAME_QUALIFIED;
1218
- $tokenMap[\T_NAME_FULLY_QUALIFIED] = static::T_NAME_FULLY_QUALIFIED;
1219
- $tokenMap[\T_NAME_RELATIVE] = static::T_NAME_RELATIVE;
1220
- $tokenMap[\T_MATCH] = static::T_MATCH;
1221
- $tokenMap[\T_NULLSAFE_OBJECT_OPERATOR] = static::T_NULLSAFE_OBJECT_OPERATOR;
1222
- $tokenMap[\T_ATTRIBUTE] = static::T_ATTRIBUTE;
1223
- $tokenMap[\T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG] = static::T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG;
1224
- $tokenMap[\T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG] = static::T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG;
1225
- $tokenMap[\T_ENUM] = static::T_ENUM;
1226
- $tokenMap[\T_READONLY] = static::T_READONLY;
1227
-
1228
- // We have create a map from PHP token IDs to external symbol IDs.
1248
+ // T_OPEN_TAG_WITH_ECHO with dropped T_OPEN_TAG results in T_ECHO
1249
+ $tokenMap[\T_OPEN_TAG_WITH_ECHO] = static::T_ECHO;
1250
+ // T_CLOSE_TAG is equivalent to ';'
1251
+ $tokenMap[\T_CLOSE_TAG] = ord(';');
1252
+
1253
+ // We have created a map from PHP token IDs to external symbol IDs.
1229
1254
  // Now map them to the internal symbol ID.
1230
1255
  $fullTokenMap = [];
1231
1256
  foreach ($tokenMap as $phpToken => $extSymbol) {
@@ -43,7 +43,7 @@ class PhpVersion {
43
43
  * if it is still under development.
44
44
  */
45
45
  public static function getNewestSupported(): self {
46
- return self::fromComponents(8, 3);
46
+ return self::fromComponents(8, 4);
47
47
  }
48
48
 
49
49
  /**
@@ -23,7 +23,8 @@ class Standard extends PrettyPrinterAbstract {
23
23
  . ($node->byRef ? '&' : '')
24
24
  . ($node->variadic ? '...' : '')
25
25
  . $this->p($node->var)
26
- . ($node->default ? ' = ' . $this->p($node->default) : '');
26
+ . ($node->default ? ' = ' . $this->p($node->default) : '')
27
+ . ($node->hooks ? ' {' . $this->pStmts($node->hooks) . $this->nl . '}' : '');
27
28
  }
28
29
 
29
30
  protected function pArg(Node\Arg $node): string {
@@ -125,6 +126,10 @@ class Standard extends PrettyPrinterAbstract {
125
126
  return '__TRAIT__';
126
127
  }
127
128
 
129
+ protected function pScalar_MagicConst_Property(MagicConst\Property $node): string {
130
+ return '__PROPERTY__';
131
+ }
132
+
128
133
  // Scalars
129
134
 
130
135
  private function indentString(string $str): string {
@@ -827,7 +832,8 @@ class Standard extends PrettyPrinterAbstract {
827
832
  return $this->pAttrGroups($node->attrGroups)
828
833
  . (0 === $node->flags ? 'var ' : $this->pModifiers($node->flags))
829
834
  . ($node->type ? $this->p($node->type) . ' ' : '')
830
- . $this->pCommaSeparated($node->props) . ';';
835
+ . $this->pCommaSeparated($node->props)
836
+ . ($node->hooks ? ' {' . $this->pStmts($node->hooks) . $this->nl . '}' : ';');
831
837
  }
832
838
 
833
839
  protected function pPropertyItem(Node\PropertyItem $node): string {
@@ -835,6 +841,15 @@ class Standard extends PrettyPrinterAbstract {
835
841
  . (null !== $node->default ? ' = ' . $this->p($node->default) : '');
836
842
  }
837
843
 
844
+ protected function pPropertyHook(Node\PropertyHook $node): string {
845
+ return $this->pAttrGroups($node->attrGroups)
846
+ . $this->pModifiers($node->flags)
847
+ . ($node->byRef ? '&' : '') . $node->name
848
+ . ($node->params ? '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')' : '')
849
+ . (\is_array($node->body) ? ' {' . $this->pStmts($node->body) . $this->nl . '}'
850
+ : ($node->body !== null ? ' => ' . $this->p($node->body) : '') . ';');
851
+ }
852
+
838
853
  protected function pStmt_ClassMethod(Stmt\ClassMethod $node): string {
839
854
  return $this->pAttrGroups($node->attrGroups)
840
855
  . $this->pModifiers($node->flags)
@@ -14,6 +14,7 @@ use PhpParser\Node\Expr\Cast;
14
14
  use PhpParser\Node\IntersectionType;
15
15
  use PhpParser\Node\MatchArm;
16
16
  use PhpParser\Node\Param;
17
+ use PhpParser\Node\PropertyHook;
17
18
  use PhpParser\Node\Scalar;
18
19
  use PhpParser\Node\Stmt;
19
20
  use PhpParser\Node\UnionType;
@@ -76,7 +77,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
76
77
  BinaryOp\BooleanAnd::class => [120, 121, 120],
77
78
  BinaryOp\BooleanOr::class => [130, 131, 130],
78
79
  BinaryOp\Coalesce::class => [140, 140, 141],
79
- Expr\Ternary::class => [150, -1, -1],
80
+ Expr\Ternary::class => [150, 150, 150],
80
81
  Expr\Assign::class => [160, -1, -1],
81
82
  Expr\AssignRef::class => [160, -1, -1],
82
83
  AssignOp\Plus::class => [160, -1, -1],
@@ -105,6 +106,15 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
105
106
 
106
107
  /** @var int Current indentation level. */
107
108
  protected int $indentLevel;
109
+ /** @var string String for single level of indentation */
110
+ private string $indent;
111
+ /** @var int Width in spaces to indent by. */
112
+ private int $indentWidth;
113
+ /** @var bool Whether to use tab indentation. */
114
+ private bool $useTabs;
115
+ /** @var int Width in spaces of one tab. */
116
+ private int $tabWidth = 4;
117
+
108
118
  /** @var string Newline style. Does not include current indentation. */
109
119
  protected string $newline;
110
120
  /** @var string Newline including current indentation. */
@@ -169,12 +179,14 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
169
179
  * PHP version while specifying an older target (but the result will
170
180
  * of course not be compatible with the older version in that case).
171
181
  * * string $newline: The newline style to use. Should be "\n" (default) or "\r\n".
182
+ * * string $indent: The indentation to use. Should either be all spaces or a single
183
+ * tab. Defaults to four spaces (" ").
172
184
  * * bool $shortArraySyntax: Whether to use [] instead of array() as the default array
173
185
  * syntax, if the node does not specify a format. Defaults to whether
174
186
  * the phpVersion support short array syntax.
175
187
  *
176
188
  * @param array{
177
- * phpVersion?: PhpVersion, newline?: string, shortArraySyntax?: bool
189
+ * phpVersion?: PhpVersion, newline?: string, indent?: string, shortArraySyntax?: bool
178
190
  * } $options Dictionary of formatting options
179
191
  */
180
192
  public function __construct(array $options = []) {
@@ -189,6 +201,17 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
189
201
  $options['shortArraySyntax'] ?? $this->phpVersion->supportsShortArraySyntax();
190
202
  $this->docStringEndToken =
191
203
  $this->phpVersion->supportsFlexibleHeredoc() ? null : '_DOC_STRING_END_' . mt_rand();
204
+
205
+ $this->indent = $indent = $options['indent'] ?? ' ';
206
+ if ($indent === "\t") {
207
+ $this->useTabs = true;
208
+ $this->indentWidth = $this->tabWidth;
209
+ } elseif ($indent === \str_repeat(' ', \strlen($indent))) {
210
+ $this->useTabs = false;
211
+ $this->indentWidth = \strlen($indent);
212
+ } else {
213
+ throw new \LogicException('Option "indent" must either be all spaces or a single tab');
214
+ }
192
215
  }
193
216
 
194
217
  /**
@@ -207,24 +230,29 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
207
230
  */
208
231
  protected function setIndentLevel(int $level): void {
209
232
  $this->indentLevel = $level;
210
- $this->nl = $this->newline . \str_repeat(' ', $level);
233
+ if ($this->useTabs) {
234
+ $tabs = \intdiv($level, $this->tabWidth);
235
+ $spaces = $level % $this->tabWidth;
236
+ $this->nl = $this->newline . \str_repeat("\t", $tabs) . \str_repeat(' ', $spaces);
237
+ } else {
238
+ $this->nl = $this->newline . \str_repeat(' ', $level);
239
+ }
211
240
  }
212
241
 
213
242
  /**
214
243
  * Increase indentation level.
215
244
  */
216
245
  protected function indent(): void {
217
- $this->indentLevel += 4;
218
- $this->nl .= ' ';
246
+ $this->indentLevel += $this->indentWidth;
247
+ $this->nl .= $this->indent;
219
248
  }
220
249
 
221
250
  /**
222
251
  * Decrease indentation level.
223
252
  */
224
253
  protected function outdent(): void {
225
- assert($this->indentLevel >= 4);
226
- $this->indentLevel -= 4;
227
- $this->nl = $this->newline . str_repeat(' ', $this->indentLevel);
254
+ assert($this->indentLevel >= $this->indentWidth);
255
+ $this->setIndentLevel($this->indentLevel - $this->indentWidth);
228
256
  }
229
257
 
230
258
  /**
@@ -536,7 +564,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
536
564
  $this->initializeModifierChangeMap();
537
565
 
538
566
  $this->resetState();
539
- $this->origTokens = new TokenStream($origTokens);
567
+ $this->origTokens = new TokenStream($origTokens, $this->tabWidth);
540
568
 
541
569
  $this->preprocessNodes($stmts);
542
570
 
@@ -708,7 +736,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
708
736
  $result .= $extraLeft;
709
737
 
710
738
  $origIndentLevel = $this->indentLevel;
711
- $this->setIndentLevel($this->origTokens->getIndentationBefore($subStartPos) + $indentAdjustment);
739
+ $this->setIndentLevel(max($this->origTokens->getIndentationBefore($subStartPos) + $indentAdjustment, 0));
712
740
 
713
741
  // If it's the same node that was previously in this position, it certainly doesn't
714
742
  // need fixup. It's important to check this here, because our fixup checks are more
@@ -811,7 +839,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
811
839
  \assert($itemStartPos >= 0 && $itemEndPos >= 0 && $itemStartPos >= $pos);
812
840
 
813
841
  $origIndentLevel = $this->indentLevel;
814
- $lastElemIndentLevel = $this->origTokens->getIndentationBefore($itemStartPos) + $indentAdjustment;
842
+ $lastElemIndentLevel = max($this->origTokens->getIndentationBefore($itemStartPos) + $indentAdjustment, 0);
815
843
  $this->setIndentLevel($lastElemIndentLevel);
816
844
 
817
845
  $comments = $arrItem->getComments();
@@ -1195,6 +1223,9 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
1195
1223
  . ($modifiers & Modifiers::PUBLIC ? 'public ' : '')
1196
1224
  . ($modifiers & Modifiers::PROTECTED ? 'protected ' : '')
1197
1225
  . ($modifiers & Modifiers::PRIVATE ? 'private ' : '')
1226
+ . ($modifiers & Modifiers::PUBLIC_SET ? 'public(set) ' : '')
1227
+ . ($modifiers & Modifiers::PROTECTED_SET ? 'protected(set) ' : '')
1228
+ . ($modifiers & Modifiers::PRIVATE_SET ? 'private(set) ' : '')
1198
1229
  . ($modifiers & Modifiers::STATIC ? 'static ' : '')
1199
1230
  . ($modifiers & Modifiers::READONLY ? 'readonly ' : '');
1200
1231
  }
@@ -1517,6 +1548,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
1517
1548
  Stmt\UseUse::class . '->uses' => ', ',
1518
1549
  MatchArm::class . '->conds' => ', ',
1519
1550
  AttributeGroup::class . '->attrs' => ', ',
1551
+ PropertyHook::class . '->params' => ', ',
1520
1552
 
1521
1553
  // statement lists
1522
1554
  Expr\Closure::class . '->stmts' => "\n",
@@ -1554,10 +1586,15 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
1554
1586
  Expr\Closure::class . '->attrGroups' => ' ',
1555
1587
  Expr\ArrowFunction::class . '->attrGroups' => ' ',
1556
1588
  Param::class . '->attrGroups' => ' ',
1589
+ PropertyHook::class . '->attrGroups' => ' ',
1590
+
1557
1591
  Stmt\Switch_::class . '->cases' => "\n",
1558
1592
  Stmt\TraitUse::class . '->adaptations' => "\n",
1559
1593
  Stmt\TryCatch::class . '->stmts' => "\n",
1560
1594
  Stmt\While_::class . '->stmts' => "\n",
1595
+ PropertyHook::class . '->body' => "\n",
1596
+ Stmt\Property::class . '->hooks' => "\n",
1597
+ Param::class . '->hooks' => "\n",
1561
1598
 
1562
1599
  // dummy for top-level context
1563
1600
  'File->stmts' => "\n",
@@ -1641,6 +1678,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
1641
1678
  Stmt\Property::class . '->flags' => ['pModifiers', \T_VARIABLE],
1642
1679
  PrintableNewAnonClassNode::class . '->flags' => ['pModifiers', \T_CLASS],
1643
1680
  Param::class . '->flags' => ['pModifiers', \T_VARIABLE],
1681
+ PropertyHook::class . '->flags' => ['pModifiers', \T_STRING],
1644
1682
  Expr\Closure::class . '->static' => ['pStatic', \T_FUNCTION],
1645
1683
  Expr\ArrowFunction::class . '->static' => ['pStatic', \T_FN],
1646
1684
  //Stmt\TraitUseAdaptation\Alias::class . '->newModifier' => 0, // TODO
@@ -17,6 +17,11 @@ if (!\function_exists('PhpParser\defineCompatibilityTokens')) {
17
17
  'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG',
18
18
  'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG',
19
19
  'T_READONLY',
20
+ // PHP 8.4
21
+ 'T_PROPERTY_C',
22
+ 'T_PUBLIC_SET',
23
+ 'T_PROTECTED_SET',
24
+ 'T_PRIVATE_SET',
20
25
  ];
21
26
 
22
27
  // PHP-Parser might be used together with another library that also emulates some or all