菜谱项目

Standard.php 36KB

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