Standard.php 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947
  1. <?php
  2. namespace PhpParser\PrettyPrinter;
  3. use PhpParser\Node;
  4. use PhpParser\Node\Expr;
  5. use PhpParser\Node\Expr\AssignOp;
  6. use PhpParser\Node\Expr\BinaryOp;
  7. use PhpParser\Node\Expr\Cast;
  8. use PhpParser\Node\Name;
  9. use PhpParser\Node\Scalar;
  10. use PhpParser\Node\Scalar\MagicConst;
  11. use PhpParser\Node\Stmt;
  12. use PhpParser\PrettyPrinterAbstract;
  13. class Standard extends PrettyPrinterAbstract
  14. {
  15. // Special nodes
  16. protected function pParam(Node\Param $node) {
  17. return ($node->type ? $this->pType($node->type) . ' ' : '')
  18. . ($node->byRef ? '&' : '')
  19. . ($node->variadic ? '...' : '')
  20. . '$' . $node->name
  21. . ($node->default ? ' = ' . $this->p($node->default) : '');
  22. }
  23. protected function pArg(Node\Arg $node) {
  24. return ($node->byRef ? '&' : '') . ($node->unpack ? '...' : '') . $this->p($node->value);
  25. }
  26. protected function pConst(Node\Const_ $node) {
  27. return $node->name . ' = ' . $this->p($node->value);
  28. }
  29. protected function pNullableType(Node\NullableType $node) {
  30. return '?' . $this->pType($node->type);
  31. }
  32. // Names
  33. protected function pName(Name $node) {
  34. return implode('\\', $node->parts);
  35. }
  36. protected function pName_FullyQualified(Name\FullyQualified $node) {
  37. return '\\' . implode('\\', $node->parts);
  38. }
  39. protected function pName_Relative(Name\Relative $node) {
  40. return 'namespace\\' . implode('\\', $node->parts);
  41. }
  42. // Magic Constants
  43. protected function pScalar_MagicConst_Class(MagicConst\Class_ $node) {
  44. return '__CLASS__';
  45. }
  46. protected function pScalar_MagicConst_Dir(MagicConst\Dir $node) {
  47. return '__DIR__';
  48. }
  49. protected function pScalar_MagicConst_File(MagicConst\File $node) {
  50. return '__FILE__';
  51. }
  52. protected function pScalar_MagicConst_Function(MagicConst\Function_ $node) {
  53. return '__FUNCTION__';
  54. }
  55. protected function pScalar_MagicConst_Line(MagicConst\Line $node) {
  56. return '__LINE__';
  57. }
  58. protected function pScalar_MagicConst_Method(MagicConst\Method $node) {
  59. return '__METHOD__';
  60. }
  61. protected function pScalar_MagicConst_Namespace(MagicConst\Namespace_ $node) {
  62. return '__NAMESPACE__';
  63. }
  64. protected function pScalar_MagicConst_Trait(MagicConst\Trait_ $node) {
  65. return '__TRAIT__';
  66. }
  67. // Scalars
  68. protected function pScalar_String(Scalar\String_ $node) {
  69. $kind = $node->getAttribute('kind', Scalar\String_::KIND_SINGLE_QUOTED);
  70. switch ($kind) {
  71. case Scalar\String_::KIND_NOWDOC:
  72. $label = $node->getAttribute('docLabel');
  73. if ($label && !$this->containsEndLabel($node->value, $label)) {
  74. if ($node->value === '') {
  75. return $this->pNoIndent("<<<'$label'\n$label") . $this->docStringEndToken;
  76. }
  77. return $this->pNoIndent("<<<'$label'\n$node->value\n$label")
  78. . $this->docStringEndToken;
  79. }
  80. /* break missing intentionally */
  81. case Scalar\String_::KIND_SINGLE_QUOTED:
  82. return '\'' . $this->pNoIndent(addcslashes($node->value, '\'\\')) . '\'';
  83. case Scalar\String_::KIND_HEREDOC:
  84. $label = $node->getAttribute('docLabel');
  85. if ($label && !$this->containsEndLabel($node->value, $label)) {
  86. if ($node->value === '') {
  87. return $this->pNoIndent("<<<$label\n$label") . $this->docStringEndToken;
  88. }
  89. $escaped = $this->escapeString($node->value, null);
  90. return $this->pNoIndent("<<<$label\n" . $escaped ."\n$label")
  91. . $this->docStringEndToken;
  92. }
  93. /* break missing intentionally */
  94. case Scalar\String_::KIND_DOUBLE_QUOTED:
  95. return '"' . $this->escapeString($node->value, '"') . '"';
  96. }
  97. throw new \Exception('Invalid string kind');
  98. }
  99. protected function pScalar_Encapsed(Scalar\Encapsed $node) {
  100. if ($node->getAttribute('kind') === Scalar\String_::KIND_HEREDOC) {
  101. $label = $node->getAttribute('docLabel');
  102. if ($label && !$this->encapsedContainsEndLabel($node->parts, $label)) {
  103. if (count($node->parts) === 1
  104. && $node->parts[0] instanceof Scalar\EncapsedStringPart
  105. && $node->parts[0]->value === ''
  106. ) {
  107. return $this->pNoIndent("<<<$label\n$label") . $this->docStringEndToken;
  108. }
  109. return $this->pNoIndent(
  110. "<<<$label\n" . $this->pEncapsList($node->parts, null) . "\n$label"
  111. ) . $this->docStringEndToken;
  112. }
  113. }
  114. return '"' . $this->pEncapsList($node->parts, '"') . '"';
  115. }
  116. protected function pScalar_LNumber(Scalar\LNumber $node) {
  117. if ($node->value === -\PHP_INT_MAX-1) {
  118. // PHP_INT_MIN cannot be represented as a literal,
  119. // because the sign is not part of the literal
  120. return '(-' . \PHP_INT_MAX . '-1)';
  121. }
  122. $kind = $node->getAttribute('kind', Scalar\LNumber::KIND_DEC);
  123. if (Scalar\LNumber::KIND_DEC === $kind) {
  124. return (string) $node->value;
  125. }
  126. $sign = $node->value < 0 ? '-' : '';
  127. $str = (string) $node->value;
  128. switch ($kind) {
  129. case Scalar\LNumber::KIND_BIN:
  130. return $sign . '0b' . base_convert($str, 10, 2);
  131. case Scalar\LNumber::KIND_OCT:
  132. return $sign . '0' . base_convert($str, 10, 8);
  133. case Scalar\LNumber::KIND_HEX:
  134. return $sign . '0x' . base_convert($str, 10, 16);
  135. }
  136. throw new \Exception('Invalid number kind');
  137. }
  138. protected function pScalar_DNumber(Scalar\DNumber $node) {
  139. if (!is_finite($node->value)) {
  140. if ($node->value === \INF) {
  141. return '\INF';
  142. } elseif ($node->value === -\INF) {
  143. return '-\INF';
  144. } else {
  145. return '\NAN';
  146. }
  147. }
  148. // Try to find a short full-precision representation
  149. $stringValue = sprintf('%.16G', $node->value);
  150. if ($node->value !== (double) $stringValue) {
  151. $stringValue = sprintf('%.17G', $node->value);
  152. }
  153. // ensure that number is really printed as float
  154. return preg_match('/^-?[0-9]+$/', $stringValue) ? $stringValue . '.0' : $stringValue;
  155. }
  156. // Assignments
  157. protected function pExpr_Assign(Expr\Assign $node) {
  158. return $this->pInfixOp('Expr_Assign', $node->var, ' = ', $node->expr);
  159. }
  160. protected function pExpr_AssignRef(Expr\AssignRef $node) {
  161. return $this->pInfixOp('Expr_AssignRef', $node->var, ' =& ', $node->expr);
  162. }
  163. protected function pExpr_AssignOp_Plus(AssignOp\Plus $node) {
  164. return $this->pInfixOp('Expr_AssignOp_Plus', $node->var, ' += ', $node->expr);
  165. }
  166. protected function pExpr_AssignOp_Minus(AssignOp\Minus $node) {
  167. return $this->pInfixOp('Expr_AssignOp_Minus', $node->var, ' -= ', $node->expr);
  168. }
  169. protected function pExpr_AssignOp_Mul(AssignOp\Mul $node) {
  170. return $this->pInfixOp('Expr_AssignOp_Mul', $node->var, ' *= ', $node->expr);
  171. }
  172. protected function pExpr_AssignOp_Div(AssignOp\Div $node) {
  173. return $this->pInfixOp('Expr_AssignOp_Div', $node->var, ' /= ', $node->expr);
  174. }
  175. protected function pExpr_AssignOp_Concat(AssignOp\Concat $node) {
  176. return $this->pInfixOp('Expr_AssignOp_Concat', $node->var, ' .= ', $node->expr);
  177. }
  178. protected function pExpr_AssignOp_Mod(AssignOp\Mod $node) {
  179. return $this->pInfixOp('Expr_AssignOp_Mod', $node->var, ' %= ', $node->expr);
  180. }
  181. protected function pExpr_AssignOp_BitwiseAnd(AssignOp\BitwiseAnd $node) {
  182. return $this->pInfixOp('Expr_AssignOp_BitwiseAnd', $node->var, ' &= ', $node->expr);
  183. }
  184. protected function pExpr_AssignOp_BitwiseOr(AssignOp\BitwiseOr $node) {
  185. return $this->pInfixOp('Expr_AssignOp_BitwiseOr', $node->var, ' |= ', $node->expr);
  186. }
  187. protected function pExpr_AssignOp_BitwiseXor(AssignOp\BitwiseXor $node) {
  188. return $this->pInfixOp('Expr_AssignOp_BitwiseXor', $node->var, ' ^= ', $node->expr);
  189. }
  190. protected function pExpr_AssignOp_ShiftLeft(AssignOp\ShiftLeft $node) {
  191. return $this->pInfixOp('Expr_AssignOp_ShiftLeft', $node->var, ' <<= ', $node->expr);
  192. }
  193. protected function pExpr_AssignOp_ShiftRight(AssignOp\ShiftRight $node) {
  194. return $this->pInfixOp('Expr_AssignOp_ShiftRight', $node->var, ' >>= ', $node->expr);
  195. }
  196. protected function pExpr_AssignOp_Pow(AssignOp\Pow $node) {
  197. return $this->pInfixOp('Expr_AssignOp_Pow', $node->var, ' **= ', $node->expr);
  198. }
  199. // Binary expressions
  200. protected function pExpr_BinaryOp_Plus(BinaryOp\Plus $node) {
  201. return $this->pInfixOp('Expr_BinaryOp_Plus', $node->left, ' + ', $node->right);
  202. }
  203. protected function pExpr_BinaryOp_Minus(BinaryOp\Minus $node) {
  204. return $this->pInfixOp('Expr_BinaryOp_Minus', $node->left, ' - ', $node->right);
  205. }
  206. protected function pExpr_BinaryOp_Mul(BinaryOp\Mul $node) {
  207. return $this->pInfixOp('Expr_BinaryOp_Mul', $node->left, ' * ', $node->right);
  208. }
  209. protected function pExpr_BinaryOp_Div(BinaryOp\Div $node) {
  210. return $this->pInfixOp('Expr_BinaryOp_Div', $node->left, ' / ', $node->right);
  211. }
  212. protected function pExpr_BinaryOp_Concat(BinaryOp\Concat $node) {
  213. return $this->pInfixOp('Expr_BinaryOp_Concat', $node->left, ' . ', $node->right);
  214. }
  215. protected function pExpr_BinaryOp_Mod(BinaryOp\Mod $node) {
  216. return $this->pInfixOp('Expr_BinaryOp_Mod', $node->left, ' % ', $node->right);
  217. }
  218. protected function pExpr_BinaryOp_BooleanAnd(BinaryOp\BooleanAnd $node) {
  219. return $this->pInfixOp('Expr_BinaryOp_BooleanAnd', $node->left, ' && ', $node->right);
  220. }
  221. protected function pExpr_BinaryOp_BooleanOr(BinaryOp\BooleanOr $node) {
  222. return $this->pInfixOp('Expr_BinaryOp_BooleanOr', $node->left, ' || ', $node->right);
  223. }
  224. protected function pExpr_BinaryOp_BitwiseAnd(BinaryOp\BitwiseAnd $node) {
  225. return $this->pInfixOp('Expr_BinaryOp_BitwiseAnd', $node->left, ' & ', $node->right);
  226. }
  227. protected function pExpr_BinaryOp_BitwiseOr(BinaryOp\BitwiseOr $node) {
  228. return $this->pInfixOp('Expr_BinaryOp_BitwiseOr', $node->left, ' | ', $node->right);
  229. }
  230. protected function pExpr_BinaryOp_BitwiseXor(BinaryOp\BitwiseXor $node) {
  231. return $this->pInfixOp('Expr_BinaryOp_BitwiseXor', $node->left, ' ^ ', $node->right);
  232. }
  233. protected function pExpr_BinaryOp_ShiftLeft(BinaryOp\ShiftLeft $node) {
  234. return $this->pInfixOp('Expr_BinaryOp_ShiftLeft', $node->left, ' << ', $node->right);
  235. }
  236. protected function pExpr_BinaryOp_ShiftRight(BinaryOp\ShiftRight $node) {
  237. return $this->pInfixOp('Expr_BinaryOp_ShiftRight', $node->left, ' >> ', $node->right);
  238. }
  239. protected function pExpr_BinaryOp_Pow(BinaryOp\Pow $node) {
  240. return $this->pInfixOp('Expr_BinaryOp_Pow', $node->left, ' ** ', $node->right);
  241. }
  242. protected function pExpr_BinaryOp_LogicalAnd(BinaryOp\LogicalAnd $node) {
  243. return $this->pInfixOp('Expr_BinaryOp_LogicalAnd', $node->left, ' and ', $node->right);
  244. }
  245. protected function pExpr_BinaryOp_LogicalOr(BinaryOp\LogicalOr $node) {
  246. return $this->pInfixOp('Expr_BinaryOp_LogicalOr', $node->left, ' or ', $node->right);
  247. }
  248. protected function pExpr_BinaryOp_LogicalXor(BinaryOp\LogicalXor $node) {
  249. return $this->pInfixOp('Expr_BinaryOp_LogicalXor', $node->left, ' xor ', $node->right);
  250. }
  251. protected function pExpr_BinaryOp_Equal(BinaryOp\Equal $node) {
  252. return $this->pInfixOp('Expr_BinaryOp_Equal', $node->left, ' == ', $node->right);
  253. }
  254. protected function pExpr_BinaryOp_NotEqual(BinaryOp\NotEqual $node) {
  255. return $this->pInfixOp('Expr_BinaryOp_NotEqual', $node->left, ' != ', $node->right);
  256. }
  257. protected function pExpr_BinaryOp_Identical(BinaryOp\Identical $node) {
  258. return $this->pInfixOp('Expr_BinaryOp_Identical', $node->left, ' === ', $node->right);
  259. }
  260. protected function pExpr_BinaryOp_NotIdentical(BinaryOp\NotIdentical $node) {
  261. return $this->pInfixOp('Expr_BinaryOp_NotIdentical', $node->left, ' !== ', $node->right);
  262. }
  263. protected function pExpr_BinaryOp_Spaceship(BinaryOp\Spaceship $node) {
  264. return $this->pInfixOp('Expr_BinaryOp_Spaceship', $node->left, ' <=> ', $node->right);
  265. }
  266. protected function pExpr_BinaryOp_Greater(BinaryOp\Greater $node) {
  267. return $this->pInfixOp('Expr_BinaryOp_Greater', $node->left, ' > ', $node->right);
  268. }
  269. protected function pExpr_BinaryOp_GreaterOrEqual(BinaryOp\GreaterOrEqual $node) {
  270. return $this->pInfixOp('Expr_BinaryOp_GreaterOrEqual', $node->left, ' >= ', $node->right);
  271. }
  272. protected function pExpr_BinaryOp_Smaller(BinaryOp\Smaller $node) {
  273. return $this->pInfixOp('Expr_BinaryOp_Smaller', $node->left, ' < ', $node->right);
  274. }
  275. protected function pExpr_BinaryOp_SmallerOrEqual(BinaryOp\SmallerOrEqual $node) {
  276. return $this->pInfixOp('Expr_BinaryOp_SmallerOrEqual', $node->left, ' <= ', $node->right);
  277. }
  278. protected function pExpr_BinaryOp_Coalesce(BinaryOp\Coalesce $node) {
  279. return $this->pInfixOp('Expr_BinaryOp_Coalesce', $node->left, ' ?? ', $node->right);
  280. }
  281. protected function pExpr_Instanceof(Expr\Instanceof_ $node) {
  282. return $this->pInfixOp('Expr_Instanceof', $node->expr, ' instanceof ', $node->class);
  283. }
  284. // Unary expressions
  285. protected function pExpr_BooleanNot(Expr\BooleanNot $node) {
  286. return $this->pPrefixOp('Expr_BooleanNot', '!', $node->expr);
  287. }
  288. protected function pExpr_BitwiseNot(Expr\BitwiseNot $node) {
  289. return $this->pPrefixOp('Expr_BitwiseNot', '~', $node->expr);
  290. }
  291. protected function pExpr_UnaryMinus(Expr\UnaryMinus $node) {
  292. return $this->pPrefixOp('Expr_UnaryMinus', '-', $node->expr);
  293. }
  294. protected function pExpr_UnaryPlus(Expr\UnaryPlus $node) {
  295. return $this->pPrefixOp('Expr_UnaryPlus', '+', $node->expr);
  296. }
  297. protected function pExpr_PreInc(Expr\PreInc $node) {
  298. return $this->pPrefixOp('Expr_PreInc', '++', $node->var);
  299. }
  300. protected function pExpr_PreDec(Expr\PreDec $node) {
  301. return $this->pPrefixOp('Expr_PreDec', '--', $node->var);
  302. }
  303. protected function pExpr_PostInc(Expr\PostInc $node) {
  304. return $this->pPostfixOp('Expr_PostInc', $node->var, '++');
  305. }
  306. protected function pExpr_PostDec(Expr\PostDec $node) {
  307. return $this->pPostfixOp('Expr_PostDec', $node->var, '--');
  308. }
  309. protected function pExpr_ErrorSuppress(Expr\ErrorSuppress $node) {
  310. return $this->pPrefixOp('Expr_ErrorSuppress', '@', $node->expr);
  311. }
  312. protected function pExpr_YieldFrom(Expr\YieldFrom $node) {
  313. return $this->pPrefixOp('Expr_YieldFrom', 'yield from ', $node->expr);
  314. }
  315. protected function pExpr_Print(Expr\Print_ $node) {
  316. return $this->pPrefixOp('Expr_Print', 'print ', $node->expr);
  317. }
  318. // Casts
  319. protected function pExpr_Cast_Int(Cast\Int_ $node) {
  320. return $this->pPrefixOp('Expr_Cast_Int', '(int) ', $node->expr);
  321. }
  322. protected function pExpr_Cast_Double(Cast\Double $node) {
  323. return $this->pPrefixOp('Expr_Cast_Double', '(double) ', $node->expr);
  324. }
  325. protected function pExpr_Cast_String(Cast\String_ $node) {
  326. return $this->pPrefixOp('Expr_Cast_String', '(string) ', $node->expr);
  327. }
  328. protected function pExpr_Cast_Array(Cast\Array_ $node) {
  329. return $this->pPrefixOp('Expr_Cast_Array', '(array) ', $node->expr);
  330. }
  331. protected function pExpr_Cast_Object(Cast\Object_ $node) {
  332. return $this->pPrefixOp('Expr_Cast_Object', '(object) ', $node->expr);
  333. }
  334. protected function pExpr_Cast_Bool(Cast\Bool_ $node) {
  335. return $this->pPrefixOp('Expr_Cast_Bool', '(bool) ', $node->expr);
  336. }
  337. protected function pExpr_Cast_Unset(Cast\Unset_ $node) {
  338. return $this->pPrefixOp('Expr_Cast_Unset', '(unset) ', $node->expr);
  339. }
  340. // Function calls and similar constructs
  341. protected function pExpr_FuncCall(Expr\FuncCall $node) {
  342. return $this->pCallLhs($node->name)
  343. . '(' . $this->pCommaSeparated($node->args) . ')';
  344. }
  345. protected function pExpr_MethodCall(Expr\MethodCall $node) {
  346. return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name)
  347. . '(' . $this->pCommaSeparated($node->args) . ')';
  348. }
  349. protected function pExpr_StaticCall(Expr\StaticCall $node) {
  350. return $this->pDereferenceLhs($node->class) . '::'
  351. . ($node->name instanceof Expr
  352. ? ($node->name instanceof Expr\Variable
  353. ? $this->p($node->name)
  354. : '{' . $this->p($node->name) . '}')
  355. : $node->name)
  356. . '(' . $this->pCommaSeparated($node->args) . ')';
  357. }
  358. protected function pExpr_Empty(Expr\Empty_ $node) {
  359. return 'empty(' . $this->p($node->expr) . ')';
  360. }
  361. protected function pExpr_Isset(Expr\Isset_ $node) {
  362. return 'isset(' . $this->pCommaSeparated($node->vars) . ')';
  363. }
  364. protected function pExpr_Eval(Expr\Eval_ $node) {
  365. return 'eval(' . $this->p($node->expr) . ')';
  366. }
  367. protected function pExpr_Include(Expr\Include_ $node) {
  368. static $map = array(
  369. Expr\Include_::TYPE_INCLUDE => 'include',
  370. Expr\Include_::TYPE_INCLUDE_ONCE => 'include_once',
  371. Expr\Include_::TYPE_REQUIRE => 'require',
  372. Expr\Include_::TYPE_REQUIRE_ONCE => 'require_once',
  373. );
  374. return $map[$node->type] . ' ' . $this->p($node->expr);
  375. }
  376. protected function pExpr_List(Expr\List_ $node) {
  377. return 'list(' . $this->pCommaSeparated($node->items) . ')';
  378. }
  379. // Other
  380. protected function pExpr_Error(Expr\Error $node) {
  381. throw new \LogicException('Cannot pretty-print AST with Error nodes');
  382. }
  383. protected function pExpr_Variable(Expr\Variable $node) {
  384. if ($node->name instanceof Expr) {
  385. return '${' . $this->p($node->name) . '}';
  386. } else {
  387. return '$' . $node->name;
  388. }
  389. }
  390. protected function pExpr_Array(Expr\Array_ $node) {
  391. $syntax = $node->getAttribute('kind',
  392. $this->options['shortArraySyntax'] ? Expr\Array_::KIND_SHORT : Expr\Array_::KIND_LONG);
  393. if ($syntax === Expr\Array_::KIND_SHORT) {
  394. return '[' . $this->pCommaSeparated($node->items) . ']';
  395. } else {
  396. return 'array(' . $this->pCommaSeparated($node->items) . ')';
  397. }
  398. }
  399. protected function pExpr_ArrayItem(Expr\ArrayItem $node) {
  400. return (null !== $node->key ? $this->p($node->key) . ' => ' : '')
  401. . ($node->byRef ? '&' : '') . $this->p($node->value);
  402. }
  403. protected function pExpr_ArrayDimFetch(Expr\ArrayDimFetch $node) {
  404. return $this->pDereferenceLhs($node->var)
  405. . '[' . (null !== $node->dim ? $this->p($node->dim) : '') . ']';
  406. }
  407. protected function pExpr_ConstFetch(Expr\ConstFetch $node) {
  408. return $this->p($node->name);
  409. }
  410. protected function pExpr_ClassConstFetch(Expr\ClassConstFetch $node) {
  411. return $this->p($node->class) . '::'
  412. . (is_string($node->name) ? $node->name : $this->p($node->name));
  413. }
  414. protected function pExpr_PropertyFetch(Expr\PropertyFetch $node) {
  415. return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name);
  416. }
  417. protected function pExpr_StaticPropertyFetch(Expr\StaticPropertyFetch $node) {
  418. return $this->pDereferenceLhs($node->class) . '::$' . $this->pObjectProperty($node->name);
  419. }
  420. protected function pExpr_ShellExec(Expr\ShellExec $node) {
  421. return '`' . $this->pEncapsList($node->parts, '`') . '`';
  422. }
  423. protected function pExpr_Closure(Expr\Closure $node) {
  424. return ($node->static ? 'static ' : '')
  425. . 'function ' . ($node->byRef ? '&' : '')
  426. . '(' . $this->pCommaSeparated($node->params) . ')'
  427. . (!empty($node->uses) ? ' use(' . $this->pCommaSeparated($node->uses) . ')': '')
  428. . (null !== $node->returnType ? ' : ' . $this->pType($node->returnType) : '')
  429. . ' {' . $this->pStmts($node->stmts) . "\n" . '}';
  430. }
  431. protected function pExpr_ClosureUse(Expr\ClosureUse $node) {
  432. return ($node->byRef ? '&' : '') . '$' . $node->var;
  433. }
  434. protected function pExpr_New(Expr\New_ $node) {
  435. if ($node->class instanceof Stmt\Class_) {
  436. $args = $node->args ? '(' . $this->pCommaSeparated($node->args) . ')' : '';
  437. return 'new ' . $this->pClassCommon($node->class, $args);
  438. }
  439. return 'new ' . $this->p($node->class) . '(' . $this->pCommaSeparated($node->args) . ')';
  440. }
  441. protected function pExpr_Clone(Expr\Clone_ $node) {
  442. return 'clone ' . $this->p($node->expr);
  443. }
  444. protected function pExpr_Ternary(Expr\Ternary $node) {
  445. // a bit of cheating: we treat the ternary as a binary op where the ?...: part is the operator.
  446. // this is okay because the part between ? and : never needs parentheses.
  447. return $this->pInfixOp('Expr_Ternary',
  448. $node->cond, ' ?' . (null !== $node->if ? ' ' . $this->p($node->if) . ' ' : '') . ': ', $node->else
  449. );
  450. }
  451. protected function pExpr_Exit(Expr\Exit_ $node) {
  452. $kind = $node->getAttribute('kind', Expr\Exit_::KIND_DIE);
  453. return ($kind === Expr\Exit_::KIND_EXIT ? 'exit' : 'die')
  454. . (null !== $node->expr ? '(' . $this->p($node->expr) . ')' : '');
  455. }
  456. protected function pExpr_Yield(Expr\Yield_ $node) {
  457. if ($node->value === null) {
  458. return 'yield';
  459. } else {
  460. // this is a bit ugly, but currently there is no way to detect whether the parentheses are necessary
  461. return '(yield '
  462. . ($node->key !== null ? $this->p($node->key) . ' => ' : '')
  463. . $this->p($node->value)
  464. . ')';
  465. }
  466. }
  467. // Declarations
  468. protected function pStmt_Namespace(Stmt\Namespace_ $node) {
  469. if ($this->canUseSemicolonNamespaces) {
  470. return 'namespace ' . $this->p($node->name) . ';' . "\n" . $this->pStmts($node->stmts, false);
  471. } else {
  472. return 'namespace' . (null !== $node->name ? ' ' . $this->p($node->name) : '')
  473. . ' {' . $this->pStmts($node->stmts) . "\n" . '}';
  474. }
  475. }
  476. protected function pStmt_Use(Stmt\Use_ $node) {
  477. return 'use ' . $this->pUseType($node->type)
  478. . $this->pCommaSeparated($node->uses) . ';';
  479. }
  480. protected function pStmt_GroupUse(Stmt\GroupUse $node) {
  481. return 'use ' . $this->pUseType($node->type) . $this->pName($node->prefix)
  482. . '\{' . $this->pCommaSeparated($node->uses) . '};';
  483. }
  484. protected function pStmt_UseUse(Stmt\UseUse $node) {
  485. return $this->pUseType($node->type) . $this->p($node->name)
  486. . ($node->name->getLast() !== $node->alias ? ' as ' . $node->alias : '');
  487. }
  488. protected function pUseType($type) {
  489. return $type === Stmt\Use_::TYPE_FUNCTION ? 'function '
  490. : ($type === Stmt\Use_::TYPE_CONSTANT ? 'const ' : '');
  491. }
  492. protected function pStmt_Interface(Stmt\Interface_ $node) {
  493. return 'interface ' . $node->name
  494. . (!empty($node->extends) ? ' extends ' . $this->pCommaSeparated($node->extends) : '')
  495. . "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}';
  496. }
  497. protected function pStmt_Class(Stmt\Class_ $node) {
  498. return $this->pClassCommon($node, ' ' . $node->name);
  499. }
  500. protected function pStmt_Trait(Stmt\Trait_ $node) {
  501. return 'trait ' . $node->name
  502. . "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}';
  503. }
  504. protected function pStmt_TraitUse(Stmt\TraitUse $node) {
  505. return 'use ' . $this->pCommaSeparated($node->traits)
  506. . (empty($node->adaptations)
  507. ? ';'
  508. : ' {' . $this->pStmts($node->adaptations) . "\n" . '}');
  509. }
  510. protected function pStmt_TraitUseAdaptation_Precedence(Stmt\TraitUseAdaptation\Precedence $node) {
  511. return $this->p($node->trait) . '::' . $node->method
  512. . ' insteadof ' . $this->pCommaSeparated($node->insteadof) . ';';
  513. }
  514. protected function pStmt_TraitUseAdaptation_Alias(Stmt\TraitUseAdaptation\Alias $node) {
  515. return (null !== $node->trait ? $this->p($node->trait) . '::' : '')
  516. . $node->method . ' as'
  517. . (null !== $node->newModifier ? ' ' . rtrim($this->pModifiers($node->newModifier), ' ') : '')
  518. . (null !== $node->newName ? ' ' . $node->newName : '')
  519. . ';';
  520. }
  521. protected function pStmt_Property(Stmt\Property $node) {
  522. return (0 === $node->flags ? 'var ' : $this->pModifiers($node->flags)) . $this->pCommaSeparated($node->props) . ';';
  523. }
  524. protected function pStmt_PropertyProperty(Stmt\PropertyProperty $node) {
  525. return '$' . $node->name
  526. . (null !== $node->default ? ' = ' . $this->p($node->default) : '');
  527. }
  528. protected function pStmt_ClassMethod(Stmt\ClassMethod $node) {
  529. return $this->pModifiers($node->flags)
  530. . 'function ' . ($node->byRef ? '&' : '') . $node->name
  531. . '(' . $this->pCommaSeparated($node->params) . ')'
  532. . (null !== $node->returnType ? ' : ' . $this->pType($node->returnType) : '')
  533. . (null !== $node->stmts
  534. ? "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}'
  535. : ';');
  536. }
  537. protected function pStmt_ClassConst(Stmt\ClassConst $node) {
  538. return $this->pModifiers($node->flags)
  539. . 'const ' . $this->pCommaSeparated($node->consts) . ';';
  540. }
  541. protected function pStmt_Function(Stmt\Function_ $node) {
  542. return 'function ' . ($node->byRef ? '&' : '') . $node->name
  543. . '(' . $this->pCommaSeparated($node->params) . ')'
  544. . (null !== $node->returnType ? ' : ' . $this->pType($node->returnType) : '')
  545. . "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}';
  546. }
  547. protected function pStmt_Const(Stmt\Const_ $node) {
  548. return 'const ' . $this->pCommaSeparated($node->consts) . ';';
  549. }
  550. protected function pStmt_Declare(Stmt\Declare_ $node) {
  551. return 'declare (' . $this->pCommaSeparated($node->declares) . ')'
  552. . (null !== $node->stmts ? ' {' . $this->pStmts($node->stmts) . "\n" . '}' : ';');
  553. }
  554. protected function pStmt_DeclareDeclare(Stmt\DeclareDeclare $node) {
  555. return $node->key . '=' . $this->p($node->value);
  556. }
  557. // Control flow
  558. protected function pStmt_If(Stmt\If_ $node) {
  559. return 'if (' . $this->p($node->cond) . ') {'
  560. . $this->pStmts($node->stmts) . "\n" . '}'
  561. . $this->pImplode($node->elseifs)
  562. . (null !== $node->else ? $this->p($node->else) : '');
  563. }
  564. protected function pStmt_ElseIf(Stmt\ElseIf_ $node) {
  565. return ' elseif (' . $this->p($node->cond) . ') {'
  566. . $this->pStmts($node->stmts) . "\n" . '}';
  567. }
  568. protected function pStmt_Else(Stmt\Else_ $node) {
  569. return ' else {' . $this->pStmts($node->stmts) . "\n" . '}';
  570. }
  571. protected function pStmt_For(Stmt\For_ $node) {
  572. return 'for ('
  573. . $this->pCommaSeparated($node->init) . ';' . (!empty($node->cond) ? ' ' : '')
  574. . $this->pCommaSeparated($node->cond) . ';' . (!empty($node->loop) ? ' ' : '')
  575. . $this->pCommaSeparated($node->loop)
  576. . ') {' . $this->pStmts($node->stmts) . "\n" . '}';
  577. }
  578. protected function pStmt_Foreach(Stmt\Foreach_ $node) {
  579. return 'foreach (' . $this->p($node->expr) . ' as '
  580. . (null !== $node->keyVar ? $this->p($node->keyVar) . ' => ' : '')
  581. . ($node->byRef ? '&' : '') . $this->p($node->valueVar) . ') {'
  582. . $this->pStmts($node->stmts) . "\n" . '}';
  583. }
  584. protected function pStmt_While(Stmt\While_ $node) {
  585. return 'while (' . $this->p($node->cond) . ') {'
  586. . $this->pStmts($node->stmts) . "\n" . '}';
  587. }
  588. protected function pStmt_Do(Stmt\Do_ $node) {
  589. return 'do {' . $this->pStmts($node->stmts) . "\n"
  590. . '} while (' . $this->p($node->cond) . ');';
  591. }
  592. protected function pStmt_Switch(Stmt\Switch_ $node) {
  593. return 'switch (' . $this->p($node->cond) . ') {'
  594. . $this->pStmts($node->cases) . "\n" . '}';
  595. }
  596. protected function pStmt_Case(Stmt\Case_ $node) {
  597. return (null !== $node->cond ? 'case ' . $this->p($node->cond) : 'default') . ':'
  598. . $this->pStmts($node->stmts);
  599. }
  600. protected function pStmt_TryCatch(Stmt\TryCatch $node) {
  601. return 'try {' . $this->pStmts($node->stmts) . "\n" . '}'
  602. . $this->pImplode($node->catches)
  603. . ($node->finally !== null ? $this->p($node->finally) : '');
  604. }
  605. protected function pStmt_Catch(Stmt\Catch_ $node) {
  606. return ' catch (' . $this->pImplode($node->types, '|') . ' $' . $node->var . ') {'
  607. . $this->pStmts($node->stmts) . "\n" . '}';
  608. }
  609. protected function pStmt_Finally(Stmt\Finally_ $node) {
  610. return ' finally {' . $this->pStmts($node->stmts) . "\n" . '}';
  611. }
  612. protected function pStmt_Break(Stmt\Break_ $node) {
  613. return 'break' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';';
  614. }
  615. protected function pStmt_Continue(Stmt\Continue_ $node) {
  616. return 'continue' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';';
  617. }
  618. protected function pStmt_Return(Stmt\Return_ $node) {
  619. return 'return' . (null !== $node->expr ? ' ' . $this->p($node->expr) : '') . ';';
  620. }
  621. protected function pStmt_Throw(Stmt\Throw_ $node) {
  622. return 'throw ' . $this->p($node->expr) . ';';
  623. }
  624. protected function pStmt_Label(Stmt\Label $node) {
  625. return $node->name . ':';
  626. }
  627. protected function pStmt_Goto(Stmt\Goto_ $node) {
  628. return 'goto ' . $node->name . ';';
  629. }
  630. // Other
  631. protected function pStmt_Echo(Stmt\Echo_ $node) {
  632. return 'echo ' . $this->pCommaSeparated($node->exprs) . ';';
  633. }
  634. protected function pStmt_Static(Stmt\Static_ $node) {
  635. return 'static ' . $this->pCommaSeparated($node->vars) . ';';
  636. }
  637. protected function pStmt_Global(Stmt\Global_ $node) {
  638. return 'global ' . $this->pCommaSeparated($node->vars) . ';';
  639. }
  640. protected function pStmt_StaticVar(Stmt\StaticVar $node) {
  641. return '$' . $node->name
  642. . (null !== $node->default ? ' = ' . $this->p($node->default) : '');
  643. }
  644. protected function pStmt_Unset(Stmt\Unset_ $node) {
  645. return 'unset(' . $this->pCommaSeparated($node->vars) . ');';
  646. }
  647. protected function pStmt_InlineHTML(Stmt\InlineHTML $node) {
  648. $newline = $node->getAttribute('hasLeadingNewline', true) ? "\n" : '';
  649. return '?>' . $this->pNoIndent($newline . $node->value) . '<?php ';
  650. }
  651. protected function pStmt_HaltCompiler(Stmt\HaltCompiler $node) {
  652. return '__halt_compiler();' . $node->remaining;
  653. }
  654. protected function pStmt_Nop(Stmt\Nop $node) {
  655. return '';
  656. }
  657. // Helpers
  658. protected function pType($node) {
  659. return is_string($node) ? $node : $this->p($node);
  660. }
  661. protected function pClassCommon(Stmt\Class_ $node, $afterClassToken) {
  662. return $this->pModifiers($node->flags)
  663. . 'class' . $afterClassToken
  664. . (null !== $node->extends ? ' extends ' . $this->p($node->extends) : '')
  665. . (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '')
  666. . "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}';
  667. }
  668. protected function pObjectProperty($node) {
  669. if ($node instanceof Expr) {
  670. return '{' . $this->p($node) . '}';
  671. } else {
  672. return $node;
  673. }
  674. }
  675. protected function pModifiers($modifiers) {
  676. return ($modifiers & Stmt\Class_::MODIFIER_PUBLIC ? 'public ' : '')
  677. . ($modifiers & Stmt\Class_::MODIFIER_PROTECTED ? 'protected ' : '')
  678. . ($modifiers & Stmt\Class_::MODIFIER_PRIVATE ? 'private ' : '')
  679. . ($modifiers & Stmt\Class_::MODIFIER_STATIC ? 'static ' : '')
  680. . ($modifiers & Stmt\Class_::MODIFIER_ABSTRACT ? 'abstract ' : '')
  681. . ($modifiers & Stmt\Class_::MODIFIER_FINAL ? 'final ' : '');
  682. }
  683. protected function pEncapsList(array $encapsList, $quote) {
  684. $return = '';
  685. foreach ($encapsList as $element) {
  686. if ($element instanceof Scalar\EncapsedStringPart) {
  687. $return .= $this->escapeString($element->value, $quote);
  688. } else {
  689. $return .= '{' . $this->p($element) . '}';
  690. }
  691. }
  692. return $return;
  693. }
  694. protected function escapeString($string, $quote) {
  695. if (null === $quote) {
  696. // For doc strings, don't escape newlines
  697. $escaped = addcslashes($string, "\t\f\v$\\");
  698. } else {
  699. $escaped = addcslashes($string, "\n\r\t\f\v$" . $quote . "\\");
  700. }
  701. // Escape other control characters
  702. return preg_replace_callback('/([\0-\10\16-\37])(?=([0-7]?))/', function ($matches) {
  703. $oct = decoct(ord($matches[1]));
  704. if ($matches[2] !== '') {
  705. // If there is a trailing digit, use the full three character form
  706. return '\\' . str_pad($oct, 3, '0', STR_PAD_LEFT);
  707. }
  708. return '\\' . $oct;
  709. }, $escaped);
  710. }
  711. protected function containsEndLabel($string, $label, $atStart = true, $atEnd = true) {
  712. $start = $atStart ? '(?:^|[\r\n])' : '[\r\n]';
  713. $end = $atEnd ? '(?:$|[;\r\n])' : '[;\r\n]';
  714. return false !== strpos($string, $label)
  715. && preg_match('/' . $start . $label . $end . '/', $string);
  716. }
  717. protected function encapsedContainsEndLabel(array $parts, $label) {
  718. foreach ($parts as $i => $part) {
  719. $atStart = $i === 0;
  720. $atEnd = $i === count($parts) - 1;
  721. if ($part instanceof Scalar\EncapsedStringPart
  722. && $this->containsEndLabel($part->value, $label, $atStart, $atEnd)
  723. ) {
  724. return true;
  725. }
  726. }
  727. return false;
  728. }
  729. protected function pDereferenceLhs(Node $node) {
  730. if ($node instanceof Expr\Variable
  731. || $node instanceof Name
  732. || $node instanceof Expr\ArrayDimFetch
  733. || $node instanceof Expr\PropertyFetch
  734. || $node instanceof Expr\StaticPropertyFetch
  735. || $node instanceof Expr\FuncCall
  736. || $node instanceof Expr\MethodCall
  737. || $node instanceof Expr\StaticCall
  738. || $node instanceof Expr\Array_
  739. || $node instanceof Scalar\String_
  740. || $node instanceof Expr\ConstFetch
  741. || $node instanceof Expr\ClassConstFetch
  742. ) {
  743. return $this->p($node);
  744. } else {
  745. return '(' . $this->p($node) . ')';
  746. }
  747. }
  748. protected function pCallLhs(Node $node) {
  749. if ($node instanceof Name
  750. || $node instanceof Expr\Variable
  751. || $node instanceof Expr\ArrayDimFetch
  752. || $node instanceof Expr\FuncCall
  753. || $node instanceof Expr\MethodCall
  754. || $node instanceof Expr\StaticCall
  755. || $node instanceof Expr\Array_
  756. ) {
  757. return $this->p($node);
  758. } else {
  759. return '(' . $this->p($node) . ')';
  760. }
  761. }
  762. }