webshell-analyzer 1.0.0__py3-none-any.whl

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 (304) hide show
  1. webshell_analyzer/__init__.py +0 -0
  2. webshell_analyzer/cli/__init__.py +0 -0
  3. webshell_analyzer/cli/cli.py +151 -0
  4. webshell_analyzer/extractor/ast_extraction/composer.json +5 -0
  5. webshell_analyzer/extractor/ast_extraction/composer.lock +77 -0
  6. webshell_analyzer/extractor/ast_extraction/src/NodeVisitor.php +85 -0
  7. webshell_analyzer/extractor/ast_extraction/src/astBuilder.php +50 -0
  8. webshell_analyzer/extractor/ast_extraction/src/astExtractor.php +50 -0
  9. webshell_analyzer/extractor/ast_extraction/src/featuresDeclare.php +36 -0
  10. webshell_analyzer/extractor/ast_extraction/vendor/autoload.php +25 -0
  11. webshell_analyzer/extractor/ast_extraction/vendor/composer/ClassLoader.php +579 -0
  12. webshell_analyzer/extractor/ast_extraction/vendor/composer/InstalledVersions.php +378 -0
  13. webshell_analyzer/extractor/ast_extraction/vendor/composer/autoload_classmap.php +10 -0
  14. webshell_analyzer/extractor/ast_extraction/vendor/composer/autoload_namespaces.php +9 -0
  15. webshell_analyzer/extractor/ast_extraction/vendor/composer/autoload_psr4.php +10 -0
  16. webshell_analyzer/extractor/ast_extraction/vendor/composer/autoload_real.php +38 -0
  17. webshell_analyzer/extractor/ast_extraction/vendor/composer/autoload_static.php +36 -0
  18. webshell_analyzer/extractor/ast_extraction/vendor/composer/installed.json +67 -0
  19. webshell_analyzer/extractor/ast_extraction/vendor/composer/installed.php +32 -0
  20. webshell_analyzer/extractor/ast_extraction/vendor/composer/platform_check.php +26 -0
  21. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Builder/ClassConst.php +150 -0
  22. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Builder/Class_.php +151 -0
  23. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Builder/Declaration.php +50 -0
  24. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Builder/EnumCase.php +86 -0
  25. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Builder/Enum_.php +116 -0
  26. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Builder/FunctionLike.php +73 -0
  27. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Builder/Function_.php +67 -0
  28. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Builder/Interface_.php +94 -0
  29. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Builder/Method.php +147 -0
  30. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Builder/Namespace_.php +45 -0
  31. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php +171 -0
  32. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Builder/Property.php +223 -0
  33. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Builder/TraitUse.php +65 -0
  34. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Builder/TraitUseAdaptation.php +145 -0
  35. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Builder/Trait_.php +83 -0
  36. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Builder/Use_.php +49 -0
  37. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Builder.php +12 -0
  38. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/BuilderFactory.php +375 -0
  39. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/BuilderHelpers.php +338 -0
  40. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Comment/Doc.php +6 -0
  41. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Comment.php +209 -0
  42. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/ConstExprEvaluationException.php +6 -0
  43. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/ConstExprEvaluator.php +237 -0
  44. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Error.php +173 -0
  45. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/ErrorHandler/Collecting.php +43 -0
  46. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/ErrorHandler/Throwing.php +17 -0
  47. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/ErrorHandler.php +12 -0
  48. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Internal/DiffElem.php +31 -0
  49. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Internal/Differ.php +178 -0
  50. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Internal/PrintableNewAnonClassNode.php +71 -0
  51. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Internal/TokenPolyfill.php +237 -0
  52. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Internal/TokenStream.php +282 -0
  53. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/JsonDecoder.php +108 -0
  54. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Lexer/Emulative.php +230 -0
  55. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/AsymmetricVisibilityTokenEmulator.php +93 -0
  56. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php +49 -0
  57. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php +26 -0
  58. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php +45 -0
  59. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php +60 -0
  60. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php +19 -0
  61. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php +60 -0
  62. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/PipeOperatorEmulator.php +45 -0
  63. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/PropertyTokenEmulator.php +19 -0
  64. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyFunctionTokenEmulator.php +31 -0
  65. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php +31 -0
  66. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php +37 -0
  67. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/TokenEmulator.php +30 -0
  68. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/VoidCastEmulator.php +98 -0
  69. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Lexer.php +116 -0
  70. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Modifiers.php +85 -0
  71. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/NameContext.php +284 -0
  72. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Arg.php +44 -0
  73. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/ArrayItem.php +43 -0
  74. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Attribute.php +33 -0
  75. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/AttributeGroup.php +27 -0
  76. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/ClosureUse.php +36 -0
  77. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/ComplexType.php +13 -0
  78. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Const_.php +36 -0
  79. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/DeclareItem.php +37 -0
  80. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrayDimFetch.php +33 -0
  81. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrayItem.php +15 -0
  82. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Array_.php +34 -0
  83. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrowFunction.php +84 -0
  84. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Assign.php +33 -0
  85. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php +11 -0
  86. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseOr.php +11 -0
  87. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseXor.php +11 -0
  88. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Coalesce.php +11 -0
  89. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Concat.php +11 -0
  90. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Div.php +11 -0
  91. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Minus.php +11 -0
  92. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Mod.php +11 -0
  93. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Mul.php +11 -0
  94. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Plus.php +11 -0
  95. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Pow.php +11 -0
  96. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/ShiftLeft.php +11 -0
  97. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/ShiftRight.php +11 -0
  98. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp.php +29 -0
  99. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignRef.php +33 -0
  100. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php +15 -0
  101. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseOr.php +15 -0
  102. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseXor.php +15 -0
  103. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BooleanAnd.php +15 -0
  104. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BooleanOr.php +15 -0
  105. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Coalesce.php +15 -0
  106. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Concat.php +15 -0
  107. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Div.php +15 -0
  108. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Equal.php +15 -0
  109. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Greater.php +15 -0
  110. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.php +15 -0
  111. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Identical.php +15 -0
  112. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalAnd.php +15 -0
  113. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalOr.php +15 -0
  114. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalXor.php +15 -0
  115. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Minus.php +15 -0
  116. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Mod.php +15 -0
  117. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Mul.php +15 -0
  118. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/NotEqual.php +15 -0
  119. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/NotIdentical.php +15 -0
  120. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Pipe.php +15 -0
  121. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Plus.php +15 -0
  122. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Pow.php +15 -0
  123. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/ShiftLeft.php +15 -0
  124. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/ShiftRight.php +15 -0
  125. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Smaller.php +15 -0
  126. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.php +15 -0
  127. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Spaceship.php +15 -0
  128. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp.php +37 -0
  129. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BitwiseNot.php +29 -0
  130. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BooleanNot.php +29 -0
  131. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/CallLike.php +60 -0
  132. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Array_.php +11 -0
  133. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Bool_.php +15 -0
  134. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Double.php +16 -0
  135. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Int_.php +15 -0
  136. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Object_.php +11 -0
  137. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/String_.php +15 -0
  138. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Unset_.php +11 -0
  139. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Void_.php +11 -0
  140. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast.php +25 -0
  141. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ClassConstFetch.php +36 -0
  142. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Clone_.php +29 -0
  143. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Closure.php +86 -0
  144. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ClosureUse.php +15 -0
  145. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ConstFetch.php +30 -0
  146. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Empty_.php +29 -0
  147. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Error.php +30 -0
  148. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ErrorSuppress.php +29 -0
  149. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Eval_.php +29 -0
  150. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Exit_.php +33 -0
  151. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/FuncCall.php +38 -0
  152. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Include_.php +38 -0
  153. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Instanceof_.php +35 -0
  154. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Isset_.php +29 -0
  155. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/List_.php +34 -0
  156. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Match_.php +32 -0
  157. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/MethodCall.php +45 -0
  158. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/New_.php +40 -0
  159. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/NullsafeMethodCall.php +45 -0
  160. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/NullsafePropertyFetch.php +35 -0
  161. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PostDec.php +29 -0
  162. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PostInc.php +29 -0
  163. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PreDec.php +29 -0
  164. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PreInc.php +29 -0
  165. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Print_.php +29 -0
  166. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PropertyFetch.php +35 -0
  167. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ShellExec.php +30 -0
  168. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/StaticCall.php +45 -0
  169. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/StaticPropertyFetch.php +36 -0
  170. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Ternary.php +37 -0
  171. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Throw_.php +29 -0
  172. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryMinus.php +29 -0
  173. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryPlus.php +29 -0
  174. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Variable.php +29 -0
  175. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/YieldFrom.php +29 -0
  176. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Yield_.php +33 -0
  177. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Expr.php +8 -0
  178. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/FunctionLike.php +40 -0
  179. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Identifier.php +85 -0
  180. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/InterpolatedStringPart.php +32 -0
  181. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/IntersectionType.php +27 -0
  182. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/MatchArm.php +29 -0
  183. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Name/FullyQualified.php +49 -0
  184. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Name/Relative.php +49 -0
  185. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Name.php +278 -0
  186. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/NullableType.php +29 -0
  187. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Param.php +123 -0
  188. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/PropertyHook.php +105 -0
  189. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/PropertyItem.php +37 -0
  190. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/DNumber.php +15 -0
  191. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/Encapsed.php +15 -0
  192. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/EncapsedStringPart.php +17 -0
  193. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/Float_.php +78 -0
  194. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/Int_.php +82 -0
  195. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/InterpolatedString.php +34 -0
  196. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/LNumber.php +15 -0
  197. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Class_.php +15 -0
  198. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Dir.php +15 -0
  199. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/File.php +15 -0
  200. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Function_.php +15 -0
  201. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Line.php +15 -0
  202. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Method.php +15 -0
  203. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Namespace_.php +15 -0
  204. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Property.php +15 -0
  205. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Trait_.php +15 -0
  206. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst.php +27 -0
  207. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/String_.php +161 -0
  208. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar.php +6 -0
  209. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/StaticVar.php +39 -0
  210. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Block.php +29 -0
  211. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Break_.php +29 -0
  212. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Case_.php +33 -0
  213. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Catch_.php +40 -0
  214. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassConst.php +77 -0
  215. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassLike.php +109 -0
  216. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassMethod.php +154 -0
  217. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Class_.php +94 -0
  218. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Const_.php +37 -0
  219. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Continue_.php +29 -0
  220. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/DeclareDeclare.php +17 -0
  221. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Declare_.php +34 -0
  222. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Do_.php +33 -0
  223. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Echo_.php +29 -0
  224. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ElseIf_.php +33 -0
  225. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Else_.php +29 -0
  226. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/EnumCase.php +36 -0
  227. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Enum_.php +44 -0
  228. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Expression.php +32 -0
  229. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Finally_.php +29 -0
  230. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/For_.php +47 -0
  231. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Foreach_.php +50 -0
  232. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Function_.php +81 -0
  233. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Global_.php +29 -0
  234. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Goto_.php +30 -0
  235. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/GroupUse.php +41 -0
  236. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/HaltCompiler.php +29 -0
  237. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/If_.php +46 -0
  238. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/InlineHTML.php +29 -0
  239. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Interface_.php +40 -0
  240. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Label.php +30 -0
  241. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Namespace_.php +37 -0
  242. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Nop.php +16 -0
  243. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Property.php +121 -0
  244. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/PropertyProperty.php +17 -0
  245. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Return_.php +29 -0
  246. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/StaticVar.php +15 -0
  247. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Static_.php +30 -0
  248. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Switch_.php +33 -0
  249. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUse.php +33 -0
  250. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.php +37 -0
  251. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php +33 -0
  252. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation.php +12 -0
  253. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Trait_.php +34 -0
  254. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TryCatch.php +37 -0
  255. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Unset_.php +29 -0
  256. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/UseUse.php +17 -0
  257. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Use_.php +47 -0
  258. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/While_.php +33 -0
  259. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt.php +8 -0
  260. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/UnionType.php +27 -0
  261. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/UseItem.php +55 -0
  262. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/VarLikeIdentifier.php +16 -0
  263. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node/VariadicPlaceholder.php +27 -0
  264. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Node.php +150 -0
  265. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/NodeAbstract.php +181 -0
  266. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/NodeDumper.php +299 -0
  267. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/NodeFinder.php +90 -0
  268. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php +287 -0
  269. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/NodeTraverserInterface.php +26 -0
  270. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/CloningVisitor.php +19 -0
  271. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/CommentAnnotatingVisitor.php +82 -0
  272. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/FindingVisitor.php +47 -0
  273. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/FirstFindingVisitor.php +49 -0
  274. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NameResolver.php +269 -0
  275. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NodeConnectingVisitor.php +73 -0
  276. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/ParentConnectingVisitor.php +51 -0
  277. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor.php +124 -0
  278. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/NodeVisitorAbstract.php +24 -0
  279. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Parser/Php7.php +2919 -0
  280. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Parser/Php8.php +2917 -0
  281. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Parser.php +24 -0
  282. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/ParserAbstract.php +1335 -0
  283. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/ParserFactory.php +42 -0
  284. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/PhpVersion.php +175 -0
  285. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/PrettyPrinter/Standard.php +1232 -0
  286. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/PrettyPrinter.php +51 -0
  287. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/PrettyPrinterAbstract.php +1706 -0
  288. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/Token.php +18 -0
  289. webshell_analyzer/extractor/ast_extraction/vendor/nikic/php-parser/lib/PhpParser/compatibility_tokens.php +71 -0
  290. webshell_analyzer/extractor/extractor.py +73 -0
  291. webshell_analyzer/extractor/lexical_extraction/lexicalExtractor.py +31 -0
  292. webshell_analyzer/extractor/statistical_extraction/statisticalExtractor.py +60 -0
  293. webshell_analyzer/model/features_selected.json +1 -0
  294. webshell_analyzer/model/model_loader.py +19 -0
  295. webshell_analyzer/model/rf_webshell_model.joblib +0 -0
  296. webshell_analyzer/modules/__init__.py +0 -0
  297. webshell_analyzer/modules/detector.py +174 -0
  298. webshell_analyzer/modules/scanner.py +68 -0
  299. webshell_analyzer-1.0.0.dist-info/METADATA +105 -0
  300. webshell_analyzer-1.0.0.dist-info/RECORD +304 -0
  301. webshell_analyzer-1.0.0.dist-info/WHEEL +5 -0
  302. webshell_analyzer-1.0.0.dist-info/entry_points.txt +2 -0
  303. webshell_analyzer-1.0.0.dist-info/licenses/LICENSE +21 -0
  304. webshell_analyzer-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1706 @@
1
+ <?php declare(strict_types=1);
2
+
3
+ namespace PhpParser;
4
+
5
+ use PhpParser\Internal\DiffElem;
6
+ use PhpParser\Internal\Differ;
7
+ use PhpParser\Internal\PrintableNewAnonClassNode;
8
+ use PhpParser\Internal\TokenStream;
9
+ use PhpParser\Node\AttributeGroup;
10
+ use PhpParser\Node\Expr;
11
+ use PhpParser\Node\Expr\AssignOp;
12
+ use PhpParser\Node\Expr\BinaryOp;
13
+ use PhpParser\Node\Expr\Cast;
14
+ use PhpParser\Node\IntersectionType;
15
+ use PhpParser\Node\MatchArm;
16
+ use PhpParser\Node\Param;
17
+ use PhpParser\Node\PropertyHook;
18
+ use PhpParser\Node\Scalar;
19
+ use PhpParser\Node\Stmt;
20
+ use PhpParser\Node\UnionType;
21
+
22
+ abstract class PrettyPrinterAbstract implements PrettyPrinter {
23
+ protected const FIXUP_PREC_LEFT = 0; // LHS operand affected by precedence
24
+ protected const FIXUP_PREC_RIGHT = 1; // RHS operand affected by precedence
25
+ protected const FIXUP_PREC_UNARY = 2; // Only operand affected by precedence
26
+ protected const FIXUP_CALL_LHS = 3; // LHS of call
27
+ protected const FIXUP_DEREF_LHS = 4; // LHS of dereferencing operation
28
+ protected const FIXUP_STATIC_DEREF_LHS = 5; // LHS of static dereferencing operation
29
+ protected const FIXUP_BRACED_NAME = 6; // Name operand that may require bracing
30
+ protected const FIXUP_VAR_BRACED_NAME = 7; // Name operand that may require ${} bracing
31
+ protected const FIXUP_ENCAPSED = 8; // Encapsed string part
32
+ protected const FIXUP_NEW = 9; // New/instanceof operand
33
+
34
+ protected const MAX_PRECEDENCE = 1000;
35
+
36
+ /** @var array<class-string, array{int, int, int}> */
37
+ protected array $precedenceMap = [
38
+ // [precedence, precedenceLHS, precedenceRHS]
39
+ // Where the latter two are the precedences to use for the LHS and RHS of a binary operator,
40
+ // where 1 is added to one of the sides depending on associativity. This information is not
41
+ // used for unary operators and set to -1.
42
+ Expr\Clone_::class => [-10, 0, 1],
43
+ BinaryOp\Pow::class => [ 0, 0, 1],
44
+ Expr\BitwiseNot::class => [ 10, -1, -1],
45
+ Expr\UnaryPlus::class => [ 10, -1, -1],
46
+ Expr\UnaryMinus::class => [ 10, -1, -1],
47
+ Cast\Int_::class => [ 10, -1, -1],
48
+ Cast\Double::class => [ 10, -1, -1],
49
+ Cast\String_::class => [ 10, -1, -1],
50
+ Cast\Array_::class => [ 10, -1, -1],
51
+ Cast\Object_::class => [ 10, -1, -1],
52
+ Cast\Bool_::class => [ 10, -1, -1],
53
+ Cast\Unset_::class => [ 10, -1, -1],
54
+ Expr\ErrorSuppress::class => [ 10, -1, -1],
55
+ Expr\Instanceof_::class => [ 20, -1, -1],
56
+ Expr\BooleanNot::class => [ 30, -1, -1],
57
+ BinaryOp\Mul::class => [ 40, 41, 40],
58
+ BinaryOp\Div::class => [ 40, 41, 40],
59
+ BinaryOp\Mod::class => [ 40, 41, 40],
60
+ BinaryOp\Plus::class => [ 50, 51, 50],
61
+ BinaryOp\Minus::class => [ 50, 51, 50],
62
+ // FIXME: This precedence is incorrect for PHP 8.
63
+ BinaryOp\Concat::class => [ 50, 51, 50],
64
+ BinaryOp\ShiftLeft::class => [ 60, 61, 60],
65
+ BinaryOp\ShiftRight::class => [ 60, 61, 60],
66
+ BinaryOp\Pipe::class => [ 65, 66, 65],
67
+ BinaryOp\Smaller::class => [ 70, 70, 70],
68
+ BinaryOp\SmallerOrEqual::class => [ 70, 70, 70],
69
+ BinaryOp\Greater::class => [ 70, 70, 70],
70
+ BinaryOp\GreaterOrEqual::class => [ 70, 70, 70],
71
+ BinaryOp\Equal::class => [ 80, 80, 80],
72
+ BinaryOp\NotEqual::class => [ 80, 80, 80],
73
+ BinaryOp\Identical::class => [ 80, 80, 80],
74
+ BinaryOp\NotIdentical::class => [ 80, 80, 80],
75
+ BinaryOp\Spaceship::class => [ 80, 80, 80],
76
+ BinaryOp\BitwiseAnd::class => [ 90, 91, 90],
77
+ BinaryOp\BitwiseXor::class => [100, 101, 100],
78
+ BinaryOp\BitwiseOr::class => [110, 111, 110],
79
+ BinaryOp\BooleanAnd::class => [120, 121, 120],
80
+ BinaryOp\BooleanOr::class => [130, 131, 130],
81
+ BinaryOp\Coalesce::class => [140, 140, 141],
82
+ Expr\Ternary::class => [150, 150, 150],
83
+ Expr\Assign::class => [160, -1, -1],
84
+ Expr\AssignRef::class => [160, -1, -1],
85
+ AssignOp\Plus::class => [160, -1, -1],
86
+ AssignOp\Minus::class => [160, -1, -1],
87
+ AssignOp\Mul::class => [160, -1, -1],
88
+ AssignOp\Div::class => [160, -1, -1],
89
+ AssignOp\Concat::class => [160, -1, -1],
90
+ AssignOp\Mod::class => [160, -1, -1],
91
+ AssignOp\BitwiseAnd::class => [160, -1, -1],
92
+ AssignOp\BitwiseOr::class => [160, -1, -1],
93
+ AssignOp\BitwiseXor::class => [160, -1, -1],
94
+ AssignOp\ShiftLeft::class => [160, -1, -1],
95
+ AssignOp\ShiftRight::class => [160, -1, -1],
96
+ AssignOp\Pow::class => [160, -1, -1],
97
+ AssignOp\Coalesce::class => [160, -1, -1],
98
+ Expr\YieldFrom::class => [170, -1, -1],
99
+ Expr\Yield_::class => [175, -1, -1],
100
+ Expr\Print_::class => [180, -1, -1],
101
+ BinaryOp\LogicalAnd::class => [190, 191, 190],
102
+ BinaryOp\LogicalXor::class => [200, 201, 200],
103
+ BinaryOp\LogicalOr::class => [210, 211, 210],
104
+ Expr\Include_::class => [220, -1, -1],
105
+ Expr\ArrowFunction::class => [230, -1, -1],
106
+ Expr\Throw_::class => [240, -1, -1],
107
+ Expr\Cast\Void_::class => [250, -1, -1],
108
+ ];
109
+
110
+ /** @var int Current indentation level. */
111
+ protected int $indentLevel;
112
+ /** @var string String for single level of indentation */
113
+ private string $indent;
114
+ /** @var int Width in spaces to indent by. */
115
+ private int $indentWidth;
116
+ /** @var bool Whether to use tab indentation. */
117
+ private bool $useTabs;
118
+ /** @var int Width in spaces of one tab. */
119
+ private int $tabWidth = 4;
120
+
121
+ /** @var string Newline style. Does not include current indentation. */
122
+ protected string $newline;
123
+ /** @var string Newline including current indentation. */
124
+ protected string $nl;
125
+ /** @var string|null Token placed at end of doc string to ensure it is followed by a newline.
126
+ * Null if flexible doc strings are used. */
127
+ protected ?string $docStringEndToken;
128
+ /** @var bool Whether semicolon namespaces can be used (i.e. no global namespace is used) */
129
+ protected bool $canUseSemicolonNamespaces;
130
+ /** @var bool Whether to use short array syntax if the node specifies no preference */
131
+ protected bool $shortArraySyntax;
132
+ /** @var PhpVersion PHP version to target */
133
+ protected PhpVersion $phpVersion;
134
+
135
+ /** @var TokenStream|null Original tokens for use in format-preserving pretty print */
136
+ protected ?TokenStream $origTokens;
137
+ /** @var Internal\Differ<Node> Differ for node lists */
138
+ protected Differ $nodeListDiffer;
139
+ /** @var array<string, bool> Map determining whether a certain character is a label character */
140
+ protected array $labelCharMap;
141
+ /**
142
+ * @var array<string, array<string, int>> Map from token classes and subnode names to FIXUP_* constants.
143
+ * This is used during format-preserving prints to place additional parens/braces if necessary.
144
+ */
145
+ protected array $fixupMap;
146
+ /**
147
+ * @var array<string, array{left?: int|string, right?: int|string}> Map from "{$node->getType()}->{$subNode}"
148
+ * to ['left' => $l, 'right' => $r], where $l and $r specify the token type that needs to be stripped
149
+ * when removing this node.
150
+ */
151
+ protected array $removalMap;
152
+ /**
153
+ * @var array<string, array{int|string|null, bool, string|null, string|null}> Map from
154
+ * "{$node->getType()}->{$subNode}" to [$find, $beforeToken, $extraLeft, $extraRight].
155
+ * $find is an optional token after which the insertion occurs. $extraLeft/Right
156
+ * are optionally added before/after the main insertions.
157
+ */
158
+ protected array $insertionMap;
159
+ /**
160
+ * @var array<string, string> Map From "{$class}->{$subNode}" to string that should be inserted
161
+ * between elements of this list subnode.
162
+ */
163
+ protected array $listInsertionMap;
164
+
165
+ /**
166
+ * @var array<string, array{int|string|null, string, string}>
167
+ */
168
+ protected array $emptyListInsertionMap;
169
+ /** @var array<string, array{string, int, int}>
170
+ * Map from "{$class}->{$subNode}" to [$printFn, $skipToken, $findToken] where $printFn is the function to
171
+ * print the modifiers, $skipToken is the token to skip at the start and $findToken is the token before which
172
+ * the modifiers should be reprinted. */
173
+ protected array $modifierChangeMap;
174
+
175
+ /**
176
+ * Creates a pretty printer instance using the given options.
177
+ *
178
+ * Supported options:
179
+ * * PhpVersion $phpVersion: The PHP version to target (default to PHP 7.4). This option
180
+ * controls compatibility of the generated code with older PHP
181
+ * versions in cases where a simple stylistic choice exists (e.g.
182
+ * array() vs []). It is safe to pretty-print an AST for a newer
183
+ * PHP version while specifying an older target (but the result will
184
+ * of course not be compatible with the older version in that case).
185
+ * * string $newline: The newline style to use. Should be "\n" (default) or "\r\n".
186
+ * * string $indent: The indentation to use. Should either be all spaces or a single
187
+ * tab. Defaults to four spaces (" ").
188
+ * * bool $shortArraySyntax: Whether to use [] instead of array() as the default array
189
+ * syntax, if the node does not specify a format. Defaults to whether
190
+ * the phpVersion support short array syntax.
191
+ *
192
+ * @param array{
193
+ * phpVersion?: PhpVersion, newline?: string, indent?: string, shortArraySyntax?: bool
194
+ * } $options Dictionary of formatting options
195
+ */
196
+ public function __construct(array $options = []) {
197
+ $this->phpVersion = $options['phpVersion'] ?? PhpVersion::fromComponents(7, 4);
198
+
199
+ $this->newline = $options['newline'] ?? "\n";
200
+ if ($this->newline !== "\n" && $this->newline != "\r\n") {
201
+ throw new \LogicException('Option "newline" must be one of "\n" or "\r\n"');
202
+ }
203
+
204
+ $this->shortArraySyntax =
205
+ $options['shortArraySyntax'] ?? $this->phpVersion->supportsShortArraySyntax();
206
+ $this->docStringEndToken =
207
+ $this->phpVersion->supportsFlexibleHeredoc() ? null : '_DOC_STRING_END_' . mt_rand();
208
+
209
+ $this->indent = $indent = $options['indent'] ?? ' ';
210
+ if ($indent === "\t") {
211
+ $this->useTabs = true;
212
+ $this->indentWidth = $this->tabWidth;
213
+ } elseif ($indent === \str_repeat(' ', \strlen($indent))) {
214
+ $this->useTabs = false;
215
+ $this->indentWidth = \strlen($indent);
216
+ } else {
217
+ throw new \LogicException('Option "indent" must either be all spaces or a single tab');
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Reset pretty printing state.
223
+ */
224
+ protected function resetState(): void {
225
+ $this->indentLevel = 0;
226
+ $this->nl = $this->newline;
227
+ $this->origTokens = null;
228
+ }
229
+
230
+ /**
231
+ * Set indentation level
232
+ *
233
+ * @param int $level Level in number of spaces
234
+ */
235
+ protected function setIndentLevel(int $level): void {
236
+ $this->indentLevel = $level;
237
+ if ($this->useTabs) {
238
+ $tabs = \intdiv($level, $this->tabWidth);
239
+ $spaces = $level % $this->tabWidth;
240
+ $this->nl = $this->newline . \str_repeat("\t", $tabs) . \str_repeat(' ', $spaces);
241
+ } else {
242
+ $this->nl = $this->newline . \str_repeat(' ', $level);
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Increase indentation level.
248
+ */
249
+ protected function indent(): void {
250
+ $this->indentLevel += $this->indentWidth;
251
+ $this->nl .= $this->indent;
252
+ }
253
+
254
+ /**
255
+ * Decrease indentation level.
256
+ */
257
+ protected function outdent(): void {
258
+ assert($this->indentLevel >= $this->indentWidth);
259
+ $this->setIndentLevel($this->indentLevel - $this->indentWidth);
260
+ }
261
+
262
+ /**
263
+ * Pretty prints an array of statements.
264
+ *
265
+ * @param Node[] $stmts Array of statements
266
+ *
267
+ * @return string Pretty printed statements
268
+ */
269
+ public function prettyPrint(array $stmts): string {
270
+ $this->resetState();
271
+ $this->preprocessNodes($stmts);
272
+
273
+ return ltrim($this->handleMagicTokens($this->pStmts($stmts, false)));
274
+ }
275
+
276
+ /**
277
+ * Pretty prints an expression.
278
+ *
279
+ * @param Expr $node Expression node
280
+ *
281
+ * @return string Pretty printed node
282
+ */
283
+ public function prettyPrintExpr(Expr $node): string {
284
+ $this->resetState();
285
+ return $this->handleMagicTokens($this->p($node));
286
+ }
287
+
288
+ /**
289
+ * Pretty prints a file of statements (includes the opening <?php tag if it is required).
290
+ *
291
+ * @param Node[] $stmts Array of statements
292
+ *
293
+ * @return string Pretty printed statements
294
+ */
295
+ public function prettyPrintFile(array $stmts): string {
296
+ if (!$stmts) {
297
+ return "<?php" . $this->newline . $this->newline;
298
+ }
299
+
300
+ $p = "<?php" . $this->newline . $this->newline . $this->prettyPrint($stmts);
301
+
302
+ if ($stmts[0] instanceof Stmt\InlineHTML) {
303
+ $p = preg_replace('/^<\?php\s+\?>\r?\n?/', '', $p);
304
+ }
305
+ if ($stmts[count($stmts) - 1] instanceof Stmt\InlineHTML) {
306
+ $p = preg_replace('/<\?php$/', '', rtrim($p));
307
+ }
308
+
309
+ return $p;
310
+ }
311
+
312
+ /**
313
+ * Preprocesses the top-level nodes to initialize pretty printer state.
314
+ *
315
+ * @param Node[] $nodes Array of nodes
316
+ */
317
+ protected function preprocessNodes(array $nodes): void {
318
+ /* We can use semicolon-namespaces unless there is a global namespace declaration */
319
+ $this->canUseSemicolonNamespaces = true;
320
+ foreach ($nodes as $node) {
321
+ if ($node instanceof Stmt\Namespace_ && null === $node->name) {
322
+ $this->canUseSemicolonNamespaces = false;
323
+ break;
324
+ }
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Handles (and removes) doc-string-end tokens.
330
+ */
331
+ protected function handleMagicTokens(string $str): string {
332
+ if ($this->docStringEndToken !== null) {
333
+ // Replace doc-string-end tokens with nothing or a newline
334
+ $str = str_replace(
335
+ $this->docStringEndToken . ';' . $this->newline,
336
+ ';' . $this->newline,
337
+ $str);
338
+ $str = str_replace($this->docStringEndToken, $this->newline, $str);
339
+ }
340
+
341
+ return $str;
342
+ }
343
+
344
+ /**
345
+ * Pretty prints an array of nodes (statements) and indents them optionally.
346
+ *
347
+ * @param Node[] $nodes Array of nodes
348
+ * @param bool $indent Whether to indent the printed nodes
349
+ *
350
+ * @return string Pretty printed statements
351
+ */
352
+ protected function pStmts(array $nodes, bool $indent = true): string {
353
+ if ($indent) {
354
+ $this->indent();
355
+ }
356
+
357
+ $result = '';
358
+ foreach ($nodes as $node) {
359
+ $comments = $node->getComments();
360
+ if ($comments) {
361
+ $result .= $this->nl . $this->pComments($comments);
362
+ if ($node instanceof Stmt\Nop) {
363
+ continue;
364
+ }
365
+ }
366
+
367
+ $result .= $this->nl . $this->p($node);
368
+ }
369
+
370
+ if ($indent) {
371
+ $this->outdent();
372
+ }
373
+
374
+ return $result;
375
+ }
376
+
377
+ /**
378
+ * Pretty-print an infix operation while taking precedence into account.
379
+ *
380
+ * @param string $class Node class of operator
381
+ * @param Node $leftNode Left-hand side node
382
+ * @param string $operatorString String representation of the operator
383
+ * @param Node $rightNode Right-hand side node
384
+ * @param int $precedence Precedence of parent operator
385
+ * @param int $lhsPrecedence Precedence for unary operator on LHS of binary operator
386
+ *
387
+ * @return string Pretty printed infix operation
388
+ */
389
+ protected function pInfixOp(
390
+ string $class, Node $leftNode, string $operatorString, Node $rightNode,
391
+ int $precedence, int $lhsPrecedence
392
+ ): string {
393
+ list($opPrecedence, $newPrecedenceLHS, $newPrecedenceRHS) = $this->precedenceMap[$class];
394
+ $prefix = '';
395
+ $suffix = '';
396
+ if ($opPrecedence >= $precedence) {
397
+ $prefix = '(';
398
+ $suffix = ')';
399
+ $lhsPrecedence = self::MAX_PRECEDENCE;
400
+ }
401
+ return $prefix . $this->p($leftNode, $newPrecedenceLHS, $newPrecedenceLHS)
402
+ . $operatorString . $this->p($rightNode, $newPrecedenceRHS, $lhsPrecedence) . $suffix;
403
+ }
404
+
405
+ /**
406
+ * Pretty-print a prefix operation while taking precedence into account.
407
+ *
408
+ * @param string $class Node class of operator
409
+ * @param string $operatorString String representation of the operator
410
+ * @param Node $node Node
411
+ * @param int $precedence Precedence of parent operator
412
+ * @param int $lhsPrecedence Precedence for unary operator on LHS of binary operator
413
+ *
414
+ * @return string Pretty printed prefix operation
415
+ */
416
+ protected function pPrefixOp(string $class, string $operatorString, Node $node, int $precedence, int $lhsPrecedence): string {
417
+ $opPrecedence = $this->precedenceMap[$class][0];
418
+ $prefix = '';
419
+ $suffix = '';
420
+ if ($opPrecedence >= $lhsPrecedence) {
421
+ $prefix = '(';
422
+ $suffix = ')';
423
+ $lhsPrecedence = self::MAX_PRECEDENCE;
424
+ }
425
+ $printedArg = $this->p($node, $opPrecedence, $lhsPrecedence);
426
+ if (($operatorString === '+' && $printedArg[0] === '+') ||
427
+ ($operatorString === '-' && $printedArg[0] === '-')
428
+ ) {
429
+ // Avoid printing +(+$a) as ++$a and similar.
430
+ $printedArg = '(' . $printedArg . ')';
431
+ }
432
+ return $prefix . $operatorString . $printedArg . $suffix;
433
+ }
434
+
435
+ /**
436
+ * Pretty-print a postfix operation while taking precedence into account.
437
+ *
438
+ * @param string $class Node class of operator
439
+ * @param string $operatorString String representation of the operator
440
+ * @param Node $node Node
441
+ * @param int $precedence Precedence of parent operator
442
+ * @param int $lhsPrecedence Precedence for unary operator on LHS of binary operator
443
+ *
444
+ * @return string Pretty printed postfix operation
445
+ */
446
+ protected function pPostfixOp(string $class, Node $node, string $operatorString, int $precedence, int $lhsPrecedence): string {
447
+ $opPrecedence = $this->precedenceMap[$class][0];
448
+ $prefix = '';
449
+ $suffix = '';
450
+ if ($opPrecedence >= $precedence) {
451
+ $prefix = '(';
452
+ $suffix = ')';
453
+ $lhsPrecedence = self::MAX_PRECEDENCE;
454
+ }
455
+ if ($opPrecedence < $lhsPrecedence) {
456
+ $lhsPrecedence = $opPrecedence;
457
+ }
458
+ return $prefix . $this->p($node, $opPrecedence, $lhsPrecedence) . $operatorString . $suffix;
459
+ }
460
+
461
+ /**
462
+ * Pretty prints an array of nodes and implodes the printed values.
463
+ *
464
+ * @param Node[] $nodes Array of Nodes to be printed
465
+ * @param string $glue Character to implode with
466
+ *
467
+ * @return string Imploded pretty printed nodes> $pre
468
+ */
469
+ protected function pImplode(array $nodes, string $glue = ''): string {
470
+ $pNodes = [];
471
+ foreach ($nodes as $node) {
472
+ if (null === $node) {
473
+ $pNodes[] = '';
474
+ } else {
475
+ $pNodes[] = $this->p($node);
476
+ }
477
+ }
478
+
479
+ return implode($glue, $pNodes);
480
+ }
481
+
482
+ /**
483
+ * Pretty prints an array of nodes and implodes the printed values with commas.
484
+ *
485
+ * @param Node[] $nodes Array of Nodes to be printed
486
+ *
487
+ * @return string Comma separated pretty printed nodes
488
+ */
489
+ protected function pCommaSeparated(array $nodes): string {
490
+ return $this->pImplode($nodes, ', ');
491
+ }
492
+
493
+ /**
494
+ * Pretty prints a comma-separated list of nodes in multiline style, including comments.
495
+ *
496
+ * The result includes a leading newline and one level of indentation (same as pStmts).
497
+ *
498
+ * @param Node[] $nodes Array of Nodes to be printed
499
+ * @param bool $trailingComma Whether to use a trailing comma
500
+ *
501
+ * @return string Comma separated pretty printed nodes in multiline style
502
+ */
503
+ protected function pCommaSeparatedMultiline(array $nodes, bool $trailingComma): string {
504
+ $this->indent();
505
+
506
+ $result = '';
507
+ $lastIdx = count($nodes) - 1;
508
+ foreach ($nodes as $idx => $node) {
509
+ if ($node !== null) {
510
+ $comments = $node->getComments();
511
+ if ($comments) {
512
+ $result .= $this->nl . $this->pComments($comments);
513
+ }
514
+
515
+ $result .= $this->nl . $this->p($node);
516
+ } else {
517
+ $result .= $this->nl;
518
+ }
519
+ if ($trailingComma || $idx !== $lastIdx) {
520
+ $result .= ',';
521
+ }
522
+ }
523
+
524
+ $this->outdent();
525
+ return $result;
526
+ }
527
+
528
+ /**
529
+ * Prints reformatted text of the passed comments.
530
+ *
531
+ * @param Comment[] $comments List of comments
532
+ *
533
+ * @return string Reformatted text of comments
534
+ */
535
+ protected function pComments(array $comments): string {
536
+ $formattedComments = [];
537
+
538
+ foreach ($comments as $comment) {
539
+ $formattedComments[] = str_replace("\n", $this->nl, $comment->getReformattedText());
540
+ }
541
+
542
+ return implode($this->nl, $formattedComments);
543
+ }
544
+
545
+ /**
546
+ * Perform a format-preserving pretty print of an AST.
547
+ *
548
+ * The format preservation is best effort. For some changes to the AST the formatting will not
549
+ * be preserved (at least not locally).
550
+ *
551
+ * In order to use this method a number of prerequisites must be satisfied:
552
+ * * The startTokenPos and endTokenPos attributes in the lexer must be enabled.
553
+ * * The CloningVisitor must be run on the AST prior to modification.
554
+ * * The original tokens must be provided, using the getTokens() method on the lexer.
555
+ *
556
+ * @param Node[] $stmts Modified AST with links to original AST
557
+ * @param Node[] $origStmts Original AST with token offset information
558
+ * @param Token[] $origTokens Tokens of the original code
559
+ */
560
+ public function printFormatPreserving(array $stmts, array $origStmts, array $origTokens): string {
561
+ $this->initializeNodeListDiffer();
562
+ $this->initializeLabelCharMap();
563
+ $this->initializeFixupMap();
564
+ $this->initializeRemovalMap();
565
+ $this->initializeInsertionMap();
566
+ $this->initializeListInsertionMap();
567
+ $this->initializeEmptyListInsertionMap();
568
+ $this->initializeModifierChangeMap();
569
+
570
+ $this->resetState();
571
+ $this->origTokens = new TokenStream($origTokens, $this->tabWidth);
572
+
573
+ $this->preprocessNodes($stmts);
574
+
575
+ $pos = 0;
576
+ $result = $this->pArray($stmts, $origStmts, $pos, 0, 'File', 'stmts', null);
577
+ if (null !== $result) {
578
+ $result .= $this->origTokens->getTokenCode($pos, count($origTokens) - 1, 0);
579
+ } else {
580
+ // Fallback
581
+ // TODO Add <?php properly
582
+ $result = "<?php" . $this->newline . $this->pStmts($stmts, false);
583
+ }
584
+
585
+ return $this->handleMagicTokens($result);
586
+ }
587
+
588
+ protected function pFallback(Node $node, int $precedence, int $lhsPrecedence): string {
589
+ return $this->{'p' . $node->getType()}($node, $precedence, $lhsPrecedence);
590
+ }
591
+
592
+ /**
593
+ * Pretty prints a node.
594
+ *
595
+ * This method also handles formatting preservation for nodes.
596
+ *
597
+ * @param Node $node Node to be pretty printed
598
+ * @param int $precedence Precedence of parent operator
599
+ * @param int $lhsPrecedence Precedence for unary operator on LHS of binary operator
600
+ * @param bool $parentFormatPreserved Whether parent node has preserved formatting
601
+ *
602
+ * @return string Pretty printed node
603
+ */
604
+ protected function p(
605
+ Node $node, int $precedence = self::MAX_PRECEDENCE, int $lhsPrecedence = self::MAX_PRECEDENCE,
606
+ bool $parentFormatPreserved = false
607
+ ): string {
608
+ // No orig tokens means this is a normal pretty print without preservation of formatting
609
+ if (!$this->origTokens) {
610
+ return $this->{'p' . $node->getType()}($node, $precedence, $lhsPrecedence);
611
+ }
612
+
613
+ /** @var Node|null $origNode */
614
+ $origNode = $node->getAttribute('origNode');
615
+ if (null === $origNode) {
616
+ return $this->pFallback($node, $precedence, $lhsPrecedence);
617
+ }
618
+
619
+ $class = \get_class($node);
620
+ \assert($class === \get_class($origNode));
621
+
622
+ $startPos = $origNode->getStartTokenPos();
623
+ $endPos = $origNode->getEndTokenPos();
624
+ \assert($startPos >= 0 && $endPos >= 0);
625
+
626
+ $fallbackNode = $node;
627
+ if ($node instanceof Expr\New_ && $node->class instanceof Stmt\Class_) {
628
+ // Normalize node structure of anonymous classes
629
+ assert($origNode instanceof Expr\New_);
630
+ $node = PrintableNewAnonClassNode::fromNewNode($node);
631
+ $origNode = PrintableNewAnonClassNode::fromNewNode($origNode);
632
+ $class = PrintableNewAnonClassNode::class;
633
+ }
634
+
635
+ // InlineHTML node does not contain closing and opening PHP tags. If the parent formatting
636
+ // is not preserved, then we need to use the fallback code to make sure the tags are
637
+ // printed.
638
+ if ($node instanceof Stmt\InlineHTML && !$parentFormatPreserved) {
639
+ return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence);
640
+ }
641
+
642
+ $indentAdjustment = $this->indentLevel - $this->origTokens->getIndentationBefore($startPos);
643
+
644
+ $type = $node->getType();
645
+ $fixupInfo = $this->fixupMap[$class] ?? null;
646
+
647
+ $result = '';
648
+ $pos = $startPos;
649
+ foreach ($node->getSubNodeNames() as $subNodeName) {
650
+ $subNode = $node->$subNodeName;
651
+ $origSubNode = $origNode->$subNodeName;
652
+
653
+ if ((!$subNode instanceof Node && $subNode !== null)
654
+ || (!$origSubNode instanceof Node && $origSubNode !== null)
655
+ ) {
656
+ if ($subNode === $origSubNode) {
657
+ // Unchanged, can reuse old code
658
+ continue;
659
+ }
660
+
661
+ if (is_array($subNode) && is_array($origSubNode)) {
662
+ // Array subnode changed, we might be able to reconstruct it
663
+ $listResult = $this->pArray(
664
+ $subNode, $origSubNode, $pos, $indentAdjustment, $class, $subNodeName,
665
+ $fixupInfo[$subNodeName] ?? null
666
+ );
667
+ if (null === $listResult) {
668
+ return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence);
669
+ }
670
+
671
+ $result .= $listResult;
672
+ continue;
673
+ }
674
+
675
+ // Check if this is a modifier change
676
+ $key = $class . '->' . $subNodeName;
677
+ if (!isset($this->modifierChangeMap[$key])) {
678
+ return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence);
679
+ }
680
+
681
+ [$printFn, $skipToken, $findToken] = $this->modifierChangeMap[$key];
682
+ $skipWSPos = $this->origTokens->skipRight($pos, $skipToken);
683
+ $result .= $this->origTokens->getTokenCode($pos, $skipWSPos, $indentAdjustment);
684
+ $result .= $this->$printFn($subNode);
685
+ $pos = $this->origTokens->findRight($skipWSPos, $findToken);
686
+ continue;
687
+ }
688
+
689
+ $extraLeft = '';
690
+ $extraRight = '';
691
+ if ($origSubNode !== null) {
692
+ $subStartPos = $origSubNode->getStartTokenPos();
693
+ $subEndPos = $origSubNode->getEndTokenPos();
694
+ \assert($subStartPos >= 0 && $subEndPos >= 0);
695
+ } else {
696
+ if ($subNode === null) {
697
+ // Both null, nothing to do
698
+ continue;
699
+ }
700
+
701
+ // A node has been inserted, check if we have insertion information for it
702
+ $key = $type . '->' . $subNodeName;
703
+ if (!isset($this->insertionMap[$key])) {
704
+ return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence);
705
+ }
706
+
707
+ list($findToken, $beforeToken, $extraLeft, $extraRight) = $this->insertionMap[$key];
708
+ if (null !== $findToken) {
709
+ $subStartPos = $this->origTokens->findRight($pos, $findToken)
710
+ + (int) !$beforeToken;
711
+ } else {
712
+ $subStartPos = $pos;
713
+ }
714
+
715
+ if (null === $extraLeft && null !== $extraRight) {
716
+ // If inserting on the right only, skipping whitespace looks better
717
+ $subStartPos = $this->origTokens->skipRightWhitespace($subStartPos);
718
+ }
719
+ $subEndPos = $subStartPos - 1;
720
+ }
721
+
722
+ if (null === $subNode) {
723
+ // A node has been removed, check if we have removal information for it
724
+ $key = $type . '->' . $subNodeName;
725
+ if (!isset($this->removalMap[$key])) {
726
+ return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence);
727
+ }
728
+
729
+ // Adjust positions to account for additional tokens that must be skipped
730
+ $removalInfo = $this->removalMap[$key];
731
+ if (isset($removalInfo['left'])) {
732
+ $subStartPos = $this->origTokens->skipLeft($subStartPos - 1, $removalInfo['left']) + 1;
733
+ }
734
+ if (isset($removalInfo['right'])) {
735
+ $subEndPos = $this->origTokens->skipRight($subEndPos + 1, $removalInfo['right']) - 1;
736
+ }
737
+ }
738
+
739
+ $result .= $this->origTokens->getTokenCode($pos, $subStartPos, $indentAdjustment);
740
+
741
+ if (null !== $subNode) {
742
+ $result .= $extraLeft;
743
+
744
+ $origIndentLevel = $this->indentLevel;
745
+ $this->setIndentLevel(max($this->origTokens->getIndentationBefore($subStartPos) + $indentAdjustment, 0));
746
+
747
+ // If it's the same node that was previously in this position, it certainly doesn't
748
+ // need fixup. It's important to check this here, because our fixup checks are more
749
+ // conservative than strictly necessary.
750
+ if (isset($fixupInfo[$subNodeName])
751
+ && $subNode->getAttribute('origNode') !== $origSubNode
752
+ ) {
753
+ $fixup = $fixupInfo[$subNodeName];
754
+ $res = $this->pFixup($fixup, $subNode, $class, $subStartPos, $subEndPos);
755
+ } else {
756
+ $res = $this->p($subNode, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, true);
757
+ }
758
+
759
+ $this->safeAppend($result, $res);
760
+ $this->setIndentLevel($origIndentLevel);
761
+
762
+ $result .= $extraRight;
763
+ }
764
+
765
+ $pos = $subEndPos + 1;
766
+ }
767
+
768
+ $result .= $this->origTokens->getTokenCode($pos, $endPos + 1, $indentAdjustment);
769
+ return $result;
770
+ }
771
+
772
+ /**
773
+ * Perform a format-preserving pretty print of an array.
774
+ *
775
+ * @param Node[] $nodes New nodes
776
+ * @param Node[] $origNodes Original nodes
777
+ * @param int $pos Current token position (updated by reference)
778
+ * @param int $indentAdjustment Adjustment for indentation
779
+ * @param string $parentNodeClass Class of the containing node.
780
+ * @param string $subNodeName Name of array subnode.
781
+ * @param null|int $fixup Fixup information for array item nodes
782
+ *
783
+ * @return null|string Result of pretty print or null if cannot preserve formatting
784
+ */
785
+ protected function pArray(
786
+ array $nodes, array $origNodes, int &$pos, int $indentAdjustment,
787
+ string $parentNodeClass, string $subNodeName, ?int $fixup
788
+ ): ?string {
789
+ $diff = $this->nodeListDiffer->diffWithReplacements($origNodes, $nodes);
790
+
791
+ $mapKey = $parentNodeClass . '->' . $subNodeName;
792
+ $insertStr = $this->listInsertionMap[$mapKey] ?? null;
793
+ $isStmtList = $subNodeName === 'stmts';
794
+
795
+ $beforeFirstKeepOrReplace = true;
796
+ $skipRemovedNode = false;
797
+ $delayedAdd = [];
798
+ $lastElemIndentLevel = $this->indentLevel;
799
+
800
+ $insertNewline = false;
801
+ if ($insertStr === "\n") {
802
+ $insertStr = '';
803
+ $insertNewline = true;
804
+ }
805
+
806
+ if ($isStmtList && \count($origNodes) === 1 && \count($nodes) !== 1) {
807
+ $startPos = $origNodes[0]->getStartTokenPos();
808
+ $endPos = $origNodes[0]->getEndTokenPos();
809
+ \assert($startPos >= 0 && $endPos >= 0);
810
+ if (!$this->origTokens->haveBraces($startPos, $endPos)) {
811
+ // This was a single statement without braces, but either additional statements
812
+ // have been added, or the single statement has been removed. This requires the
813
+ // addition of braces. For now fall back.
814
+ // TODO: Try to preserve formatting
815
+ return null;
816
+ }
817
+ }
818
+
819
+ $result = '';
820
+ foreach ($diff as $i => $diffElem) {
821
+ $diffType = $diffElem->type;
822
+ /** @var Node|string|null $arrItem */
823
+ $arrItem = $diffElem->new;
824
+ /** @var Node|string|null $origArrItem */
825
+ $origArrItem = $diffElem->old;
826
+
827
+ if ($diffType === DiffElem::TYPE_KEEP || $diffType === DiffElem::TYPE_REPLACE) {
828
+ $beforeFirstKeepOrReplace = false;
829
+
830
+ if ($origArrItem === null || $arrItem === null) {
831
+ // We can only handle the case where both are null
832
+ if ($origArrItem === $arrItem) {
833
+ continue;
834
+ }
835
+ return null;
836
+ }
837
+
838
+ if (!$arrItem instanceof Node || !$origArrItem instanceof Node) {
839
+ // We can only deal with nodes. This can occur for Names, which use string arrays.
840
+ return null;
841
+ }
842
+
843
+ $itemStartPos = $origArrItem->getStartTokenPos();
844
+ $itemEndPos = $origArrItem->getEndTokenPos();
845
+ \assert($itemStartPos >= 0 && $itemEndPos >= 0 && $itemStartPos >= $pos);
846
+
847
+ $origIndentLevel = $this->indentLevel;
848
+ $lastElemIndentLevel = max($this->origTokens->getIndentationBefore($itemStartPos) + $indentAdjustment, 0);
849
+ $this->setIndentLevel($lastElemIndentLevel);
850
+
851
+ $comments = $arrItem->getComments();
852
+ $origComments = $origArrItem->getComments();
853
+ $commentStartPos = $origComments ? $origComments[0]->getStartTokenPos() : $itemStartPos;
854
+ \assert($commentStartPos >= 0);
855
+
856
+ if ($commentStartPos < $pos) {
857
+ // Comments may be assigned to multiple nodes if they start at the same position.
858
+ // Make sure we don't try to print them multiple times.
859
+ $commentStartPos = $itemStartPos;
860
+ }
861
+
862
+ if ($skipRemovedNode) {
863
+ if ($isStmtList && $this->origTokens->haveTagInRange($pos, $itemStartPos)) {
864
+ // We'd remove an opening/closing PHP tag.
865
+ // TODO: Preserve formatting.
866
+ $this->setIndentLevel($origIndentLevel);
867
+ return null;
868
+ }
869
+ } else {
870
+ $result .= $this->origTokens->getTokenCode(
871
+ $pos, $commentStartPos, $indentAdjustment);
872
+ }
873
+
874
+ if (!empty($delayedAdd)) {
875
+ /** @var Node $delayedAddNode */
876
+ foreach ($delayedAdd as $delayedAddNode) {
877
+ if ($insertNewline) {
878
+ $delayedAddComments = $delayedAddNode->getComments();
879
+ if ($delayedAddComments) {
880
+ $result .= $this->pComments($delayedAddComments) . $this->nl;
881
+ }
882
+ }
883
+
884
+ $this->safeAppend($result, $this->p($delayedAddNode, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, true));
885
+
886
+ if ($insertNewline) {
887
+ $result .= $insertStr . $this->nl;
888
+ } else {
889
+ $result .= $insertStr;
890
+ }
891
+ }
892
+
893
+ $delayedAdd = [];
894
+ }
895
+
896
+ if ($comments !== $origComments) {
897
+ if ($comments) {
898
+ $result .= $this->pComments($comments) . $this->nl;
899
+ }
900
+ } else {
901
+ $result .= $this->origTokens->getTokenCode(
902
+ $commentStartPos, $itemStartPos, $indentAdjustment);
903
+ }
904
+
905
+ // If we had to remove anything, we have done so now.
906
+ $skipRemovedNode = false;
907
+ } elseif ($diffType === DiffElem::TYPE_ADD) {
908
+ if (null === $insertStr) {
909
+ // We don't have insertion information for this list type
910
+ return null;
911
+ }
912
+
913
+ if (!$arrItem instanceof Node) {
914
+ // We only support list insertion of nodes.
915
+ return null;
916
+ }
917
+
918
+ // We go multiline if the original code was multiline,
919
+ // or if it's an array item with a comment above it.
920
+ // Match always uses multiline formatting.
921
+ if ($insertStr === ', ' &&
922
+ ($this->isMultiline($origNodes) || $arrItem->getComments() ||
923
+ $parentNodeClass === Expr\Match_::class)
924
+ ) {
925
+ $insertStr = ',';
926
+ $insertNewline = true;
927
+ }
928
+
929
+ if ($beforeFirstKeepOrReplace) {
930
+ // Will be inserted at the next "replace" or "keep" element
931
+ $delayedAdd[] = $arrItem;
932
+ continue;
933
+ }
934
+
935
+ $itemStartPos = $pos;
936
+ $itemEndPos = $pos - 1;
937
+
938
+ $origIndentLevel = $this->indentLevel;
939
+ $this->setIndentLevel($lastElemIndentLevel);
940
+
941
+ if ($insertNewline) {
942
+ $result .= $insertStr . $this->nl;
943
+ $comments = $arrItem->getComments();
944
+ if ($comments) {
945
+ $result .= $this->pComments($comments) . $this->nl;
946
+ }
947
+ } else {
948
+ $result .= $insertStr;
949
+ }
950
+ } elseif ($diffType === DiffElem::TYPE_REMOVE) {
951
+ if (!$origArrItem instanceof Node) {
952
+ // We only support removal for nodes
953
+ return null;
954
+ }
955
+
956
+ $itemStartPos = $origArrItem->getStartTokenPos();
957
+ $itemEndPos = $origArrItem->getEndTokenPos();
958
+ \assert($itemStartPos >= 0 && $itemEndPos >= 0);
959
+
960
+ // Consider comments part of the node.
961
+ $origComments = $origArrItem->getComments();
962
+ if ($origComments) {
963
+ $itemStartPos = $origComments[0]->getStartTokenPos();
964
+ }
965
+
966
+ if ($i === 0) {
967
+ // If we're removing from the start, keep the tokens before the node and drop those after it,
968
+ // instead of the other way around.
969
+ $result .= $this->origTokens->getTokenCode(
970
+ $pos, $itemStartPos, $indentAdjustment);
971
+ $skipRemovedNode = true;
972
+ } else {
973
+ if ($isStmtList && $this->origTokens->haveTagInRange($pos, $itemStartPos)) {
974
+ // We'd remove an opening/closing PHP tag.
975
+ // TODO: Preserve formatting.
976
+ return null;
977
+ }
978
+ }
979
+
980
+ $pos = $itemEndPos + 1;
981
+ continue;
982
+ } else {
983
+ throw new \Exception("Shouldn't happen");
984
+ }
985
+
986
+ if (null !== $fixup && $arrItem->getAttribute('origNode') !== $origArrItem) {
987
+ $res = $this->pFixup($fixup, $arrItem, null, $itemStartPos, $itemEndPos);
988
+ } else {
989
+ $res = $this->p($arrItem, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, true);
990
+ }
991
+ $this->safeAppend($result, $res);
992
+
993
+ $this->setIndentLevel($origIndentLevel);
994
+ $pos = $itemEndPos + 1;
995
+ }
996
+
997
+ if ($skipRemovedNode) {
998
+ // TODO: Support removing single node.
999
+ return null;
1000
+ }
1001
+
1002
+ if (!empty($delayedAdd)) {
1003
+ if (!isset($this->emptyListInsertionMap[$mapKey])) {
1004
+ return null;
1005
+ }
1006
+
1007
+ list($findToken, $extraLeft, $extraRight) = $this->emptyListInsertionMap[$mapKey];
1008
+ if (null !== $findToken) {
1009
+ $insertPos = $this->origTokens->findRight($pos, $findToken) + 1;
1010
+ $result .= $this->origTokens->getTokenCode($pos, $insertPos, $indentAdjustment);
1011
+ $pos = $insertPos;
1012
+ }
1013
+
1014
+ $first = true;
1015
+ $result .= $extraLeft;
1016
+ foreach ($delayedAdd as $delayedAddNode) {
1017
+ if (!$first) {
1018
+ $result .= $insertStr;
1019
+ if ($insertNewline) {
1020
+ $result .= $this->nl;
1021
+ }
1022
+ }
1023
+ $result .= $this->p($delayedAddNode, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, true);
1024
+ $first = false;
1025
+ }
1026
+ $result .= $extraRight === "\n" ? $this->nl : $extraRight;
1027
+ }
1028
+
1029
+ return $result;
1030
+ }
1031
+
1032
+ /**
1033
+ * Print node with fixups.
1034
+ *
1035
+ * Fixups here refer to the addition of extra parentheses, braces or other characters, that
1036
+ * are required to preserve program semantics in a certain context (e.g. to maintain precedence
1037
+ * or because only certain expressions are allowed in certain places).
1038
+ *
1039
+ * @param int $fixup Fixup type
1040
+ * @param Node $subNode Subnode to print
1041
+ * @param string|null $parentClass Class of parent node
1042
+ * @param int $subStartPos Original start pos of subnode
1043
+ * @param int $subEndPos Original end pos of subnode
1044
+ *
1045
+ * @return string Result of fixed-up print of subnode
1046
+ */
1047
+ protected function pFixup(int $fixup, Node $subNode, ?string $parentClass, int $subStartPos, int $subEndPos): string {
1048
+ switch ($fixup) {
1049
+ case self::FIXUP_PREC_LEFT:
1050
+ // We use a conservative approximation where lhsPrecedence == precedence.
1051
+ if (!$this->origTokens->haveParens($subStartPos, $subEndPos)) {
1052
+ $precedence = $this->precedenceMap[$parentClass][1];
1053
+ return $this->p($subNode, $precedence, $precedence);
1054
+ }
1055
+ break;
1056
+ case self::FIXUP_PREC_RIGHT:
1057
+ if (!$this->origTokens->haveParens($subStartPos, $subEndPos)) {
1058
+ $precedence = $this->precedenceMap[$parentClass][2];
1059
+ return $this->p($subNode, $precedence, $precedence);
1060
+ }
1061
+ break;
1062
+ case self::FIXUP_PREC_UNARY:
1063
+ if (!$this->origTokens->haveParens($subStartPos, $subEndPos)) {
1064
+ $precedence = $this->precedenceMap[$parentClass][0];
1065
+ return $this->p($subNode, $precedence, $precedence);
1066
+ }
1067
+ break;
1068
+ case self::FIXUP_CALL_LHS:
1069
+ if ($this->callLhsRequiresParens($subNode)
1070
+ && !$this->origTokens->haveParens($subStartPos, $subEndPos)
1071
+ ) {
1072
+ return '(' . $this->p($subNode) . ')';
1073
+ }
1074
+ break;
1075
+ case self::FIXUP_DEREF_LHS:
1076
+ if ($this->dereferenceLhsRequiresParens($subNode)
1077
+ && !$this->origTokens->haveParens($subStartPos, $subEndPos)
1078
+ ) {
1079
+ return '(' . $this->p($subNode) . ')';
1080
+ }
1081
+ break;
1082
+ case self::FIXUP_STATIC_DEREF_LHS:
1083
+ if ($this->staticDereferenceLhsRequiresParens($subNode)
1084
+ && !$this->origTokens->haveParens($subStartPos, $subEndPos)
1085
+ ) {
1086
+ return '(' . $this->p($subNode) . ')';
1087
+ }
1088
+ break;
1089
+ case self::FIXUP_NEW:
1090
+ if ($this->newOperandRequiresParens($subNode)
1091
+ && !$this->origTokens->haveParens($subStartPos, $subEndPos)) {
1092
+ return '(' . $this->p($subNode) . ')';
1093
+ }
1094
+ break;
1095
+ case self::FIXUP_BRACED_NAME:
1096
+ case self::FIXUP_VAR_BRACED_NAME:
1097
+ if ($subNode instanceof Expr
1098
+ && !$this->origTokens->haveBraces($subStartPos, $subEndPos)
1099
+ ) {
1100
+ return ($fixup === self::FIXUP_VAR_BRACED_NAME ? '$' : '')
1101
+ . '{' . $this->p($subNode) . '}';
1102
+ }
1103
+ break;
1104
+ case self::FIXUP_ENCAPSED:
1105
+ if (!$subNode instanceof Node\InterpolatedStringPart
1106
+ && !$this->origTokens->haveBraces($subStartPos, $subEndPos)
1107
+ ) {
1108
+ return '{' . $this->p($subNode) . '}';
1109
+ }
1110
+ break;
1111
+ default:
1112
+ throw new \Exception('Cannot happen');
1113
+ }
1114
+
1115
+ // Nothing special to do
1116
+ return $this->p($subNode);
1117
+ }
1118
+
1119
+ /**
1120
+ * Appends to a string, ensuring whitespace between label characters.
1121
+ *
1122
+ * Example: "echo" and "$x" result in "echo$x", but "echo" and "x" result in "echo x".
1123
+ * Without safeAppend the result would be "echox", which does not preserve semantics.
1124
+ */
1125
+ protected function safeAppend(string &$str, string $append): void {
1126
+ if ($str === "") {
1127
+ $str = $append;
1128
+ return;
1129
+ }
1130
+
1131
+ if ($append === "") {
1132
+ return;
1133
+ }
1134
+
1135
+ if (!$this->labelCharMap[$append[0]]
1136
+ || !$this->labelCharMap[$str[\strlen($str) - 1]]) {
1137
+ $str .= $append;
1138
+ } else {
1139
+ $str .= " " . $append;
1140
+ }
1141
+ }
1142
+
1143
+ /**
1144
+ * Determines whether the LHS of a call must be wrapped in parenthesis.
1145
+ *
1146
+ * @param Node $node LHS of a call
1147
+ *
1148
+ * @return bool Whether parentheses are required
1149
+ */
1150
+ protected function callLhsRequiresParens(Node $node): bool {
1151
+ if ($node instanceof Expr\New_) {
1152
+ return !$this->phpVersion->supportsNewDereferenceWithoutParentheses();
1153
+ }
1154
+ return !($node instanceof Node\Name
1155
+ || $node instanceof Expr\Variable
1156
+ || $node instanceof Expr\ArrayDimFetch
1157
+ || $node instanceof Expr\FuncCall
1158
+ || $node instanceof Expr\MethodCall
1159
+ || $node instanceof Expr\NullsafeMethodCall
1160
+ || $node instanceof Expr\StaticCall
1161
+ || $node instanceof Expr\Array_);
1162
+ }
1163
+
1164
+ /**
1165
+ * Determines whether the LHS of an array/object operation must be wrapped in parentheses.
1166
+ *
1167
+ * @param Node $node LHS of dereferencing operation
1168
+ *
1169
+ * @return bool Whether parentheses are required
1170
+ */
1171
+ protected function dereferenceLhsRequiresParens(Node $node): bool {
1172
+ // A constant can occur on the LHS of an array/object deref, but not a static deref.
1173
+ return $this->staticDereferenceLhsRequiresParens($node)
1174
+ && !$node instanceof Expr\ConstFetch;
1175
+ }
1176
+
1177
+ /**
1178
+ * Determines whether the LHS of a static operation must be wrapped in parentheses.
1179
+ *
1180
+ * @param Node $node LHS of dereferencing operation
1181
+ *
1182
+ * @return bool Whether parentheses are required
1183
+ */
1184
+ protected function staticDereferenceLhsRequiresParens(Node $node): bool {
1185
+ if ($node instanceof Expr\New_) {
1186
+ return !$this->phpVersion->supportsNewDereferenceWithoutParentheses();
1187
+ }
1188
+ return !($node instanceof Expr\Variable
1189
+ || $node instanceof Node\Name
1190
+ || $node instanceof Expr\ArrayDimFetch
1191
+ || $node instanceof Expr\PropertyFetch
1192
+ || $node instanceof Expr\NullsafePropertyFetch
1193
+ || $node instanceof Expr\StaticPropertyFetch
1194
+ || $node instanceof Expr\FuncCall
1195
+ || $node instanceof Expr\MethodCall
1196
+ || $node instanceof Expr\NullsafeMethodCall
1197
+ || $node instanceof Expr\StaticCall
1198
+ || $node instanceof Expr\Array_
1199
+ || $node instanceof Scalar\String_
1200
+ || $node instanceof Expr\ClassConstFetch);
1201
+ }
1202
+
1203
+ /**
1204
+ * Determines whether an expression used in "new" or "instanceof" requires parentheses.
1205
+ *
1206
+ * @param Node $node New or instanceof operand
1207
+ *
1208
+ * @return bool Whether parentheses are required
1209
+ */
1210
+ protected function newOperandRequiresParens(Node $node): bool {
1211
+ if ($node instanceof Node\Name || $node instanceof Expr\Variable) {
1212
+ return false;
1213
+ }
1214
+ if ($node instanceof Expr\ArrayDimFetch || $node instanceof Expr\PropertyFetch ||
1215
+ $node instanceof Expr\NullsafePropertyFetch
1216
+ ) {
1217
+ return $this->newOperandRequiresParens($node->var);
1218
+ }
1219
+ if ($node instanceof Expr\StaticPropertyFetch) {
1220
+ return $this->newOperandRequiresParens($node->class);
1221
+ }
1222
+ return true;
1223
+ }
1224
+
1225
+ /**
1226
+ * Print modifiers, including trailing whitespace.
1227
+ *
1228
+ * @param int $modifiers Modifier mask to print
1229
+ *
1230
+ * @return string Printed modifiers
1231
+ */
1232
+ protected function pModifiers(int $modifiers): string {
1233
+ return ($modifiers & Modifiers::FINAL ? 'final ' : '')
1234
+ . ($modifiers & Modifiers::ABSTRACT ? 'abstract ' : '')
1235
+ . ($modifiers & Modifiers::PUBLIC ? 'public ' : '')
1236
+ . ($modifiers & Modifiers::PROTECTED ? 'protected ' : '')
1237
+ . ($modifiers & Modifiers::PRIVATE ? 'private ' : '')
1238
+ . ($modifiers & Modifiers::PUBLIC_SET ? 'public(set) ' : '')
1239
+ . ($modifiers & Modifiers::PROTECTED_SET ? 'protected(set) ' : '')
1240
+ . ($modifiers & Modifiers::PRIVATE_SET ? 'private(set) ' : '')
1241
+ . ($modifiers & Modifiers::STATIC ? 'static ' : '')
1242
+ . ($modifiers & Modifiers::READONLY ? 'readonly ' : '');
1243
+ }
1244
+
1245
+ protected function pStatic(bool $static): string {
1246
+ return $static ? 'static ' : '';
1247
+ }
1248
+
1249
+ /**
1250
+ * Determine whether a list of nodes uses multiline formatting.
1251
+ *
1252
+ * @param (Node|null)[] $nodes Node list
1253
+ *
1254
+ * @return bool Whether multiline formatting is used
1255
+ */
1256
+ protected function isMultiline(array $nodes): bool {
1257
+ if (\count($nodes) < 2) {
1258
+ return false;
1259
+ }
1260
+
1261
+ $pos = -1;
1262
+ foreach ($nodes as $node) {
1263
+ if (null === $node) {
1264
+ continue;
1265
+ }
1266
+
1267
+ $endPos = $node->getEndTokenPos() + 1;
1268
+ if ($pos >= 0) {
1269
+ $text = $this->origTokens->getTokenCode($pos, $endPos, 0);
1270
+ if (false === strpos($text, "\n")) {
1271
+ // We require that a newline is present between *every* item. If the formatting
1272
+ // is inconsistent, with only some items having newlines, we don't consider it
1273
+ // as multiline
1274
+ return false;
1275
+ }
1276
+ }
1277
+ $pos = $endPos;
1278
+ }
1279
+
1280
+ return true;
1281
+ }
1282
+
1283
+ /**
1284
+ * Lazily initializes label char map.
1285
+ *
1286
+ * The label char map determines whether a certain character may occur in a label.
1287
+ */
1288
+ protected function initializeLabelCharMap(): void {
1289
+ if (isset($this->labelCharMap)) {
1290
+ return;
1291
+ }
1292
+
1293
+ $this->labelCharMap = [];
1294
+ for ($i = 0; $i < 256; $i++) {
1295
+ $chr = chr($i);
1296
+ $this->labelCharMap[$chr] = $i >= 0x80 || ctype_alnum($chr);
1297
+ }
1298
+
1299
+ if ($this->phpVersion->allowsDelInIdentifiers()) {
1300
+ $this->labelCharMap["\x7f"] = true;
1301
+ }
1302
+ }
1303
+
1304
+ /**
1305
+ * Lazily initializes node list differ.
1306
+ *
1307
+ * The node list differ is used to determine differences between two array subnodes.
1308
+ */
1309
+ protected function initializeNodeListDiffer(): void {
1310
+ if (isset($this->nodeListDiffer)) {
1311
+ return;
1312
+ }
1313
+
1314
+ $this->nodeListDiffer = new Internal\Differ(function ($a, $b) {
1315
+ if ($a instanceof Node && $b instanceof Node) {
1316
+ return $a === $b->getAttribute('origNode');
1317
+ }
1318
+ // Can happen for array destructuring
1319
+ return $a === null && $b === null;
1320
+ });
1321
+ }
1322
+
1323
+ /**
1324
+ * Lazily initializes fixup map.
1325
+ *
1326
+ * The fixup map is used to determine whether a certain subnode of a certain node may require
1327
+ * some kind of "fixup" operation, e.g. the addition of parenthesis or braces.
1328
+ */
1329
+ protected function initializeFixupMap(): void {
1330
+ if (isset($this->fixupMap)) {
1331
+ return;
1332
+ }
1333
+
1334
+ $this->fixupMap = [
1335
+ Expr\Instanceof_::class => [
1336
+ 'expr' => self::FIXUP_PREC_UNARY,
1337
+ 'class' => self::FIXUP_NEW,
1338
+ ],
1339
+ Expr\Ternary::class => [
1340
+ 'cond' => self::FIXUP_PREC_LEFT,
1341
+ 'else' => self::FIXUP_PREC_RIGHT,
1342
+ ],
1343
+ Expr\Yield_::class => ['value' => self::FIXUP_PREC_UNARY],
1344
+
1345
+ Expr\FuncCall::class => ['name' => self::FIXUP_CALL_LHS],
1346
+ Expr\StaticCall::class => ['class' => self::FIXUP_STATIC_DEREF_LHS],
1347
+ Expr\ArrayDimFetch::class => ['var' => self::FIXUP_DEREF_LHS],
1348
+ Expr\ClassConstFetch::class => [
1349
+ 'class' => self::FIXUP_STATIC_DEREF_LHS,
1350
+ 'name' => self::FIXUP_BRACED_NAME,
1351
+ ],
1352
+ Expr\New_::class => ['class' => self::FIXUP_NEW],
1353
+ Expr\MethodCall::class => [
1354
+ 'var' => self::FIXUP_DEREF_LHS,
1355
+ 'name' => self::FIXUP_BRACED_NAME,
1356
+ ],
1357
+ Expr\NullsafeMethodCall::class => [
1358
+ 'var' => self::FIXUP_DEREF_LHS,
1359
+ 'name' => self::FIXUP_BRACED_NAME,
1360
+ ],
1361
+ Expr\StaticPropertyFetch::class => [
1362
+ 'class' => self::FIXUP_STATIC_DEREF_LHS,
1363
+ 'name' => self::FIXUP_VAR_BRACED_NAME,
1364
+ ],
1365
+ Expr\PropertyFetch::class => [
1366
+ 'var' => self::FIXUP_DEREF_LHS,
1367
+ 'name' => self::FIXUP_BRACED_NAME,
1368
+ ],
1369
+ Expr\NullsafePropertyFetch::class => [
1370
+ 'var' => self::FIXUP_DEREF_LHS,
1371
+ 'name' => self::FIXUP_BRACED_NAME,
1372
+ ],
1373
+ Scalar\InterpolatedString::class => [
1374
+ 'parts' => self::FIXUP_ENCAPSED,
1375
+ ],
1376
+ ];
1377
+
1378
+ $binaryOps = [
1379
+ BinaryOp\Pow::class, BinaryOp\Mul::class, BinaryOp\Div::class, BinaryOp\Mod::class,
1380
+ BinaryOp\Plus::class, BinaryOp\Minus::class, BinaryOp\Concat::class,
1381
+ BinaryOp\ShiftLeft::class, BinaryOp\ShiftRight::class, BinaryOp\Smaller::class,
1382
+ BinaryOp\SmallerOrEqual::class, BinaryOp\Greater::class, BinaryOp\GreaterOrEqual::class,
1383
+ BinaryOp\Equal::class, BinaryOp\NotEqual::class, BinaryOp\Identical::class,
1384
+ BinaryOp\NotIdentical::class, BinaryOp\Spaceship::class, BinaryOp\BitwiseAnd::class,
1385
+ BinaryOp\BitwiseXor::class, BinaryOp\BitwiseOr::class, BinaryOp\BooleanAnd::class,
1386
+ BinaryOp\BooleanOr::class, BinaryOp\Coalesce::class, BinaryOp\LogicalAnd::class,
1387
+ BinaryOp\LogicalXor::class, BinaryOp\LogicalOr::class, BinaryOp\Pipe::class,
1388
+ ];
1389
+ foreach ($binaryOps as $binaryOp) {
1390
+ $this->fixupMap[$binaryOp] = [
1391
+ 'left' => self::FIXUP_PREC_LEFT,
1392
+ 'right' => self::FIXUP_PREC_RIGHT
1393
+ ];
1394
+ }
1395
+
1396
+ $prefixOps = [
1397
+ Expr\Clone_::class, Expr\BitwiseNot::class, Expr\BooleanNot::class, Expr\UnaryPlus::class, Expr\UnaryMinus::class,
1398
+ Cast\Int_::class, Cast\Double::class, Cast\String_::class, Cast\Array_::class,
1399
+ Cast\Object_::class, Cast\Bool_::class, Cast\Unset_::class, Expr\ErrorSuppress::class,
1400
+ Expr\YieldFrom::class, Expr\Print_::class, Expr\Include_::class,
1401
+ Expr\Assign::class, Expr\AssignRef::class, AssignOp\Plus::class, AssignOp\Minus::class,
1402
+ AssignOp\Mul::class, AssignOp\Div::class, AssignOp\Concat::class, AssignOp\Mod::class,
1403
+ AssignOp\BitwiseAnd::class, AssignOp\BitwiseOr::class, AssignOp\BitwiseXor::class,
1404
+ AssignOp\ShiftLeft::class, AssignOp\ShiftRight::class, AssignOp\Pow::class, AssignOp\Coalesce::class,
1405
+ Expr\ArrowFunction::class, Expr\Throw_::class,
1406
+ ];
1407
+ foreach ($prefixOps as $prefixOp) {
1408
+ $this->fixupMap[$prefixOp] = ['expr' => self::FIXUP_PREC_UNARY];
1409
+ }
1410
+ }
1411
+
1412
+ /**
1413
+ * Lazily initializes the removal map.
1414
+ *
1415
+ * The removal map is used to determine which additional tokens should be removed when a
1416
+ * certain node is replaced by null.
1417
+ */
1418
+ protected function initializeRemovalMap(): void {
1419
+ if (isset($this->removalMap)) {
1420
+ return;
1421
+ }
1422
+
1423
+ $stripBoth = ['left' => \T_WHITESPACE, 'right' => \T_WHITESPACE];
1424
+ $stripLeft = ['left' => \T_WHITESPACE];
1425
+ $stripRight = ['right' => \T_WHITESPACE];
1426
+ $stripDoubleArrow = ['right' => \T_DOUBLE_ARROW];
1427
+ $stripColon = ['left' => ':'];
1428
+ $stripEquals = ['left' => '='];
1429
+ $this->removalMap = [
1430
+ 'Expr_ArrayDimFetch->dim' => $stripBoth,
1431
+ 'ArrayItem->key' => $stripDoubleArrow,
1432
+ 'Expr_ArrowFunction->returnType' => $stripColon,
1433
+ 'Expr_Closure->returnType' => $stripColon,
1434
+ 'Expr_Exit->expr' => $stripBoth,
1435
+ 'Expr_Ternary->if' => $stripBoth,
1436
+ 'Expr_Yield->key' => $stripDoubleArrow,
1437
+ 'Expr_Yield->value' => $stripBoth,
1438
+ 'Param->type' => $stripRight,
1439
+ 'Param->default' => $stripEquals,
1440
+ 'Stmt_Break->num' => $stripBoth,
1441
+ 'Stmt_Catch->var' => $stripLeft,
1442
+ 'Stmt_ClassConst->type' => $stripRight,
1443
+ 'Stmt_ClassMethod->returnType' => $stripColon,
1444
+ 'Stmt_Class->extends' => ['left' => \T_EXTENDS],
1445
+ 'Stmt_Enum->scalarType' => $stripColon,
1446
+ 'Stmt_EnumCase->expr' => $stripEquals,
1447
+ 'Expr_PrintableNewAnonClass->extends' => ['left' => \T_EXTENDS],
1448
+ 'Stmt_Continue->num' => $stripBoth,
1449
+ 'Stmt_Foreach->keyVar' => $stripDoubleArrow,
1450
+ 'Stmt_Function->returnType' => $stripColon,
1451
+ 'Stmt_If->else' => $stripLeft,
1452
+ 'Stmt_Namespace->name' => $stripLeft,
1453
+ 'Stmt_Property->type' => $stripRight,
1454
+ 'PropertyItem->default' => $stripEquals,
1455
+ 'Stmt_Return->expr' => $stripBoth,
1456
+ 'Stmt_StaticVar->default' => $stripEquals,
1457
+ 'Stmt_TraitUseAdaptation_Alias->newName' => $stripLeft,
1458
+ 'Stmt_TryCatch->finally' => $stripLeft,
1459
+ // 'Stmt_Case->cond': Replace with "default"
1460
+ // 'Stmt_Class->name': Unclear what to do
1461
+ // 'Stmt_Declare->stmts': Not a plain node
1462
+ // 'Stmt_TraitUseAdaptation_Alias->newModifier': Not a plain node
1463
+ ];
1464
+ }
1465
+
1466
+ protected function initializeInsertionMap(): void {
1467
+ if (isset($this->insertionMap)) {
1468
+ return;
1469
+ }
1470
+
1471
+ // TODO: "yield" where both key and value are inserted doesn't work
1472
+ // [$find, $beforeToken, $extraLeft, $extraRight]
1473
+ $this->insertionMap = [
1474
+ 'Expr_ArrayDimFetch->dim' => ['[', false, null, null],
1475
+ 'ArrayItem->key' => [null, false, null, ' => '],
1476
+ 'Expr_ArrowFunction->returnType' => [')', false, ': ', null],
1477
+ 'Expr_Closure->returnType' => [')', false, ': ', null],
1478
+ 'Expr_Ternary->if' => ['?', false, ' ', ' '],
1479
+ 'Expr_Yield->key' => [\T_YIELD, false, null, ' => '],
1480
+ 'Expr_Yield->value' => [\T_YIELD, false, ' ', null],
1481
+ 'Param->type' => [null, false, null, ' '],
1482
+ 'Param->default' => [null, false, ' = ', null],
1483
+ 'Stmt_Break->num' => [\T_BREAK, false, ' ', null],
1484
+ 'Stmt_Catch->var' => [null, false, ' ', null],
1485
+ 'Stmt_ClassMethod->returnType' => [')', false, ': ', null],
1486
+ 'Stmt_ClassConst->type' => [\T_CONST, false, ' ', null],
1487
+ 'Stmt_Class->extends' => [null, false, ' extends ', null],
1488
+ 'Stmt_Enum->scalarType' => [null, false, ' : ', null],
1489
+ 'Stmt_EnumCase->expr' => [null, false, ' = ', null],
1490
+ 'Expr_PrintableNewAnonClass->extends' => [null, false, ' extends ', null],
1491
+ 'Stmt_Continue->num' => [\T_CONTINUE, false, ' ', null],
1492
+ 'Stmt_Foreach->keyVar' => [\T_AS, false, null, ' => '],
1493
+ 'Stmt_Function->returnType' => [')', false, ': ', null],
1494
+ 'Stmt_If->else' => [null, false, ' ', null],
1495
+ 'Stmt_Namespace->name' => [\T_NAMESPACE, false, ' ', null],
1496
+ 'Stmt_Property->type' => [\T_VARIABLE, true, null, ' '],
1497
+ 'PropertyItem->default' => [null, false, ' = ', null],
1498
+ 'Stmt_Return->expr' => [\T_RETURN, false, ' ', null],
1499
+ 'Stmt_StaticVar->default' => [null, false, ' = ', null],
1500
+ //'Stmt_TraitUseAdaptation_Alias->newName' => [T_AS, false, ' ', null], // TODO
1501
+ 'Stmt_TryCatch->finally' => [null, false, ' ', null],
1502
+
1503
+ // 'Expr_Exit->expr': Complicated due to optional ()
1504
+ // 'Stmt_Case->cond': Conversion from default to case
1505
+ // 'Stmt_Class->name': Unclear
1506
+ // 'Stmt_Declare->stmts': Not a proper node
1507
+ // 'Stmt_TraitUseAdaptation_Alias->newModifier': Not a proper node
1508
+ ];
1509
+ }
1510
+
1511
+ protected function initializeListInsertionMap(): void {
1512
+ if (isset($this->listInsertionMap)) {
1513
+ return;
1514
+ }
1515
+
1516
+ $this->listInsertionMap = [
1517
+ // special
1518
+ //'Expr_ShellExec->parts' => '', // TODO These need to be treated more carefully
1519
+ //'Scalar_InterpolatedString->parts' => '',
1520
+ Stmt\Catch_::class . '->types' => '|',
1521
+ UnionType::class . '->types' => '|',
1522
+ IntersectionType::class . '->types' => '&',
1523
+ Stmt\If_::class . '->elseifs' => ' ',
1524
+ Stmt\TryCatch::class . '->catches' => ' ',
1525
+
1526
+ // comma-separated lists
1527
+ Expr\Array_::class . '->items' => ', ',
1528
+ Expr\ArrowFunction::class . '->params' => ', ',
1529
+ Expr\Closure::class . '->params' => ', ',
1530
+ Expr\Closure::class . '->uses' => ', ',
1531
+ Expr\FuncCall::class . '->args' => ', ',
1532
+ Expr\Isset_::class . '->vars' => ', ',
1533
+ Expr\List_::class . '->items' => ', ',
1534
+ Expr\MethodCall::class . '->args' => ', ',
1535
+ Expr\NullsafeMethodCall::class . '->args' => ', ',
1536
+ Expr\New_::class . '->args' => ', ',
1537
+ PrintableNewAnonClassNode::class . '->args' => ', ',
1538
+ Expr\StaticCall::class . '->args' => ', ',
1539
+ Stmt\ClassConst::class . '->consts' => ', ',
1540
+ Stmt\ClassMethod::class . '->params' => ', ',
1541
+ Stmt\Class_::class . '->implements' => ', ',
1542
+ Stmt\Enum_::class . '->implements' => ', ',
1543
+ PrintableNewAnonClassNode::class . '->implements' => ', ',
1544
+ Stmt\Const_::class . '->consts' => ', ',
1545
+ Stmt\Declare_::class . '->declares' => ', ',
1546
+ Stmt\Echo_::class . '->exprs' => ', ',
1547
+ Stmt\For_::class . '->init' => ', ',
1548
+ Stmt\For_::class . '->cond' => ', ',
1549
+ Stmt\For_::class . '->loop' => ', ',
1550
+ Stmt\Function_::class . '->params' => ', ',
1551
+ Stmt\Global_::class . '->vars' => ', ',
1552
+ Stmt\GroupUse::class . '->uses' => ', ',
1553
+ Stmt\Interface_::class . '->extends' => ', ',
1554
+ Expr\Match_::class . '->arms' => ', ',
1555
+ Stmt\Property::class . '->props' => ', ',
1556
+ Stmt\StaticVar::class . '->vars' => ', ',
1557
+ Stmt\TraitUse::class . '->traits' => ', ',
1558
+ Stmt\TraitUseAdaptation\Precedence::class . '->insteadof' => ', ',
1559
+ Stmt\Unset_::class . '->vars' => ', ',
1560
+ Stmt\UseUse::class . '->uses' => ', ',
1561
+ MatchArm::class . '->conds' => ', ',
1562
+ AttributeGroup::class . '->attrs' => ', ',
1563
+ PropertyHook::class . '->params' => ', ',
1564
+
1565
+ // statement lists
1566
+ Expr\Closure::class . '->stmts' => "\n",
1567
+ Stmt\Case_::class . '->stmts' => "\n",
1568
+ Stmt\Catch_::class . '->stmts' => "\n",
1569
+ Stmt\Class_::class . '->stmts' => "\n",
1570
+ Stmt\Enum_::class . '->stmts' => "\n",
1571
+ PrintableNewAnonClassNode::class . '->stmts' => "\n",
1572
+ Stmt\Interface_::class . '->stmts' => "\n",
1573
+ Stmt\Trait_::class . '->stmts' => "\n",
1574
+ Stmt\ClassMethod::class . '->stmts' => "\n",
1575
+ Stmt\Declare_::class . '->stmts' => "\n",
1576
+ Stmt\Do_::class . '->stmts' => "\n",
1577
+ Stmt\ElseIf_::class . '->stmts' => "\n",
1578
+ Stmt\Else_::class . '->stmts' => "\n",
1579
+ Stmt\Finally_::class . '->stmts' => "\n",
1580
+ Stmt\Foreach_::class . '->stmts' => "\n",
1581
+ Stmt\For_::class . '->stmts' => "\n",
1582
+ Stmt\Function_::class . '->stmts' => "\n",
1583
+ Stmt\If_::class . '->stmts' => "\n",
1584
+ Stmt\Namespace_::class . '->stmts' => "\n",
1585
+ Stmt\Block::class . '->stmts' => "\n",
1586
+
1587
+ // Attribute groups
1588
+ Stmt\Class_::class . '->attrGroups' => "\n",
1589
+ Stmt\Enum_::class . '->attrGroups' => "\n",
1590
+ Stmt\EnumCase::class . '->attrGroups' => "\n",
1591
+ Stmt\Interface_::class . '->attrGroups' => "\n",
1592
+ Stmt\Trait_::class . '->attrGroups' => "\n",
1593
+ Stmt\Function_::class . '->attrGroups' => "\n",
1594
+ Stmt\ClassMethod::class . '->attrGroups' => "\n",
1595
+ Stmt\ClassConst::class . '->attrGroups' => "\n",
1596
+ Stmt\Property::class . '->attrGroups' => "\n",
1597
+ PrintableNewAnonClassNode::class . '->attrGroups' => ' ',
1598
+ Expr\Closure::class . '->attrGroups' => ' ',
1599
+ Expr\ArrowFunction::class . '->attrGroups' => ' ',
1600
+ Param::class . '->attrGroups' => ' ',
1601
+ PropertyHook::class . '->attrGroups' => ' ',
1602
+
1603
+ Stmt\Switch_::class . '->cases' => "\n",
1604
+ Stmt\TraitUse::class . '->adaptations' => "\n",
1605
+ Stmt\TryCatch::class . '->stmts' => "\n",
1606
+ Stmt\While_::class . '->stmts' => "\n",
1607
+ PropertyHook::class . '->body' => "\n",
1608
+ Stmt\Property::class . '->hooks' => "\n",
1609
+ Param::class . '->hooks' => "\n",
1610
+
1611
+ // dummy for top-level context
1612
+ 'File->stmts' => "\n",
1613
+ ];
1614
+ }
1615
+
1616
+ protected function initializeEmptyListInsertionMap(): void {
1617
+ if (isset($this->emptyListInsertionMap)) {
1618
+ return;
1619
+ }
1620
+
1621
+ // TODO Insertion into empty statement lists.
1622
+
1623
+ // [$find, $extraLeft, $extraRight]
1624
+ $this->emptyListInsertionMap = [
1625
+ Expr\ArrowFunction::class . '->params' => ['(', '', ''],
1626
+ Expr\Closure::class . '->uses' => [')', ' use (', ')'],
1627
+ Expr\Closure::class . '->params' => ['(', '', ''],
1628
+ Expr\FuncCall::class . '->args' => ['(', '', ''],
1629
+ Expr\MethodCall::class . '->args' => ['(', '', ''],
1630
+ Expr\NullsafeMethodCall::class . '->args' => ['(', '', ''],
1631
+ Expr\New_::class . '->args' => ['(', '', ''],
1632
+ PrintableNewAnonClassNode::class . '->args' => ['(', '', ''],
1633
+ PrintableNewAnonClassNode::class . '->implements' => [null, ' implements ', ''],
1634
+ Expr\StaticCall::class . '->args' => ['(', '', ''],
1635
+ Stmt\Class_::class . '->implements' => [null, ' implements ', ''],
1636
+ Stmt\Enum_::class . '->implements' => [null, ' implements ', ''],
1637
+ Stmt\ClassMethod::class . '->params' => ['(', '', ''],
1638
+ Stmt\Interface_::class . '->extends' => [null, ' extends ', ''],
1639
+ Stmt\Function_::class . '->params' => ['(', '', ''],
1640
+ Stmt\Interface_::class . '->attrGroups' => [null, '', "\n"],
1641
+ Stmt\Class_::class . '->attrGroups' => [null, '', "\n"],
1642
+ Stmt\ClassConst::class . '->attrGroups' => [null, '', "\n"],
1643
+ Stmt\ClassMethod::class . '->attrGroups' => [null, '', "\n"],
1644
+ Stmt\Function_::class . '->attrGroups' => [null, '', "\n"],
1645
+ Stmt\Property::class . '->attrGroups' => [null, '', "\n"],
1646
+ Stmt\Trait_::class . '->attrGroups' => [null, '', "\n"],
1647
+ Expr\ArrowFunction::class . '->attrGroups' => [null, '', ' '],
1648
+ Expr\Closure::class . '->attrGroups' => [null, '', ' '],
1649
+ Stmt\Const_::class . '->attrGroups' => [null, '', "\n"],
1650
+ PrintableNewAnonClassNode::class . '->attrGroups' => [\T_NEW, ' ', ''],
1651
+
1652
+ /* These cannot be empty to start with:
1653
+ * Expr_Isset->vars
1654
+ * Stmt_Catch->types
1655
+ * Stmt_Const->consts
1656
+ * Stmt_ClassConst->consts
1657
+ * Stmt_Declare->declares
1658
+ * Stmt_Echo->exprs
1659
+ * Stmt_Global->vars
1660
+ * Stmt_GroupUse->uses
1661
+ * Stmt_Property->props
1662
+ * Stmt_StaticVar->vars
1663
+ * Stmt_TraitUse->traits
1664
+ * Stmt_TraitUseAdaptation_Precedence->insteadof
1665
+ * Stmt_Unset->vars
1666
+ * Stmt_Use->uses
1667
+ * UnionType->types
1668
+ */
1669
+
1670
+ /* TODO
1671
+ * Stmt_If->elseifs
1672
+ * Stmt_TryCatch->catches
1673
+ * Expr_Array->items
1674
+ * Expr_List->items
1675
+ * Stmt_For->init
1676
+ * Stmt_For->cond
1677
+ * Stmt_For->loop
1678
+ */
1679
+ ];
1680
+ }
1681
+
1682
+ protected function initializeModifierChangeMap(): void {
1683
+ if (isset($this->modifierChangeMap)) {
1684
+ return;
1685
+ }
1686
+
1687
+ $this->modifierChangeMap = [
1688
+ Stmt\ClassConst::class . '->flags' => ['pModifiers', \T_WHITESPACE, \T_CONST],
1689
+ Stmt\ClassMethod::class . '->flags' => ['pModifiers', \T_WHITESPACE, \T_FUNCTION],
1690
+ Stmt\Class_::class . '->flags' => ['pModifiers', \T_WHITESPACE, \T_CLASS],
1691
+ Stmt\Property::class . '->flags' => ['pModifiers', \T_WHITESPACE, \T_VARIABLE],
1692
+ PrintableNewAnonClassNode::class . '->flags' => ['pModifiers', \T_NEW, \T_CLASS],
1693
+ Param::class . '->flags' => ['pModifiers', \T_WHITESPACE, \T_VARIABLE],
1694
+ PropertyHook::class . '->flags' => ['pModifiers', \T_WHITESPACE, \T_STRING],
1695
+ Expr\Closure::class . '->static' => ['pStatic', \T_WHITESPACE, \T_FUNCTION],
1696
+ Expr\ArrowFunction::class . '->static' => ['pStatic', \T_WHITESPACE, \T_FN],
1697
+ //Stmt\TraitUseAdaptation\Alias::class . '->newModifier' => 0, // TODO
1698
+ ];
1699
+
1700
+ // List of integer subnodes that are not modifiers:
1701
+ // Expr_Include->type
1702
+ // Stmt_GroupUse->type
1703
+ // Stmt_Use->type
1704
+ // UseItem->type
1705
+ }
1706
+ }