PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.6.2
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.6.2
4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.5.2 All 199 releases
betterdocs / includes / Dependencies / PhpParser / PrettyPrinterAbstract.php

PrettyPrinterAbstract.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.6.2, at includes/Dependencies/PhpParser/PrettyPrinterAbstract.php

350 lines 12.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // phpcs:ignoreFile -- Bundled third-party (Mozart) dependency; exempt from plugin coding standards.
3
4 namespace WPDeveloper\BetterDocs\Dependencies\PhpParser;
5
6 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Expr;
7 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Stmt;
8
9 abstract class PrettyPrinterAbstract
10 {
11 protected $precedenceMap = array(
12 // [precedence, associativity] where for the latter -1 is %left, 0 is %nonassoc and 1 is %right
13 'Expr_BinaryOp_Pow' => array( 0, 1),
14 'Expr_BitwiseNot' => array( 10, 1),
15 'Expr_PreInc' => array( 10, 1),
16 'Expr_PreDec' => array( 10, 1),
17 'Expr_PostInc' => array( 10, -1),
18 'Expr_PostDec' => array( 10, -1),
19 'Expr_UnaryPlus' => array( 10, 1),
20 'Expr_UnaryMinus' => array( 10, 1),
21 'Expr_Cast_Int' => array( 10, 1),
22 'Expr_Cast_Double' => array( 10, 1),
23 'Expr_Cast_String' => array( 10, 1),
24 'Expr_Cast_Array' => array( 10, 1),
25 'Expr_Cast_Object' => array( 10, 1),
26 'Expr_Cast_Bool' => array( 10, 1),
27 'Expr_Cast_Unset' => array( 10, 1),
28 'Expr_ErrorSuppress' => array( 10, 1),
29 'Expr_Instanceof' => array( 20, 0),
30 'Expr_BooleanNot' => array( 30, 1),
31 'Expr_BinaryOp_Mul' => array( 40, -1),
32 'Expr_BinaryOp_Div' => array( 40, -1),
33 'Expr_BinaryOp_Mod' => array( 40, -1),
34 'Expr_BinaryOp_Plus' => array( 50, -1),
35 'Expr_BinaryOp_Minus' => array( 50, -1),
36 'Expr_BinaryOp_Concat' => array( 50, -1),
37 'Expr_BinaryOp_ShiftLeft' => array( 60, -1),
38 'Expr_BinaryOp_ShiftRight' => array( 60, -1),
39 'Expr_BinaryOp_Smaller' => array( 70, 0),
40 'Expr_BinaryOp_SmallerOrEqual' => array( 70, 0),
41 'Expr_BinaryOp_Greater' => array( 70, 0),
42 'Expr_BinaryOp_GreaterOrEqual' => array( 70, 0),
43 'Expr_BinaryOp_Equal' => array( 80, 0),
44 'Expr_BinaryOp_NotEqual' => array( 80, 0),
45 'Expr_BinaryOp_Identical' => array( 80, 0),
46 'Expr_BinaryOp_NotIdentical' => array( 80, 0),
47 'Expr_BinaryOp_Spaceship' => array( 80, 0),
48 'Expr_BinaryOp_BitwiseAnd' => array( 90, -1),
49 'Expr_BinaryOp_BitwiseXor' => array(100, -1),
50 'Expr_BinaryOp_BitwiseOr' => array(110, -1),
51 'Expr_BinaryOp_BooleanAnd' => array(120, -1),
52 'Expr_BinaryOp_BooleanOr' => array(130, -1),
53 'Expr_BinaryOp_Coalesce' => array(140, 1),
54 'Expr_Ternary' => array(150, -1),
55 // parser uses %left for assignments, but they really behave as %right
56 'Expr_Assign' => array(160, 1),
57 'Expr_AssignRef' => array(160, 1),
58 'Expr_AssignOp_Plus' => array(160, 1),
59 'Expr_AssignOp_Minus' => array(160, 1),
60 'Expr_AssignOp_Mul' => array(160, 1),
61 'Expr_AssignOp_Div' => array(160, 1),
62 'Expr_AssignOp_Concat' => array(160, 1),
63 'Expr_AssignOp_Mod' => array(160, 1),
64 'Expr_AssignOp_BitwiseAnd' => array(160, 1),
65 'Expr_AssignOp_BitwiseOr' => array(160, 1),
66 'Expr_AssignOp_BitwiseXor' => array(160, 1),
67 'Expr_AssignOp_ShiftLeft' => array(160, 1),
68 'Expr_AssignOp_ShiftRight' => array(160, 1),
69 'Expr_AssignOp_Pow' => array(160, 1),
70 'Expr_YieldFrom' => array(165, 1),
71 'Expr_Print' => array(168, 1),
72 'Expr_BinaryOp_LogicalAnd' => array(170, -1),
73 'Expr_BinaryOp_LogicalXor' => array(180, -1),
74 'Expr_BinaryOp_LogicalOr' => array(190, -1),
75 'Expr_Include' => array(200, -1),
76 );
77
78 protected $noIndentToken;
79 protected $docStringEndToken;
80 protected $canUseSemicolonNamespaces;
81 protected $options;
82
83 /**
84 * Creates a pretty printer instance using the given options.
85 *
86 * Supported options:
87 * * bool $shortArraySyntax = false: Whether to use [] instead of array() as the default array
88 * syntax, if the node does not specify a format.
89 *
90 * @param array $options Dictionary of formatting options
91 */
92 public function __construct(array $options = []) {
93 $this->noIndentToken = '_NO_INDENT_' . mt_rand();
94 $this->docStringEndToken = '_DOC_STRING_END_' . mt_rand();
95
96 $defaultOptions = ['shortArraySyntax' => false];
97 $this->options = $options + $defaultOptions;
98 }
99
100 /**
101 * Pretty prints an array of statements.
102 *
103 * @param Node[] $stmts Array of statements
104 *
105 * @return string Pretty printed statements
106 */
107 public function prettyPrint(array $stmts) {
108 $this->preprocessNodes($stmts);
109
110 return ltrim($this->handleMagicTokens($this->pStmts($stmts, false)));
111 }
112
113 /**
114 * Pretty prints an expression.
115 *
116 * @param Expr $node Expression node
117 *
118 * @return string Pretty printed node
119 */
120 public function prettyPrintExpr(Expr $node) {
121 return $this->handleMagicTokens($this->p($node));
122 }
123
124 /**
125 * Pretty prints a file of statements (includes the opening <?php tag if it is required).
126 *
127 * @param Node[] $stmts Array of statements
128 *
129 * @return string Pretty printed statements
130 */
131 public function prettyPrintFile(array $stmts) {
132 if (!$stmts) {
133 return "<?php\n\n";
134 }
135
136 $p = "<?php\n\n" . $this->prettyPrint($stmts);
137
138 if ($stmts[0] instanceof Stmt\InlineHTML) {
139 $p = preg_replace('/^<\?php\s+\?>\n?/', '', $p);
140 }
141 if ($stmts[count($stmts) - 1] instanceof Stmt\InlineHTML) {
142 $p = preg_replace('/<\?php$/', '', rtrim($p));
143 }
144
145 return $p;
146 }
147
148 /**
149 * Preprocesses the top-level nodes to initialize pretty printer state.
150 *
151 * @param Node[] $nodes Array of nodes
152 */
153 protected function preprocessNodes(array $nodes) {
154 /* We can use semicolon-namespaces unless there is a global namespace declaration */
155 $this->canUseSemicolonNamespaces = true;
156 foreach ($nodes as $node) {
157 if ($node instanceof Stmt\Namespace_ && null === $node->name) {
158 $this->canUseSemicolonNamespaces = false;
159 }
160 }
161 }
162
163 protected function handleMagicTokens($str) {
164 // Drop no-indent tokens
165 $str = str_replace($this->noIndentToken, '', $str);
166
167 // Replace doc-string-end tokens with nothing or a newline
168 $str = str_replace($this->docStringEndToken . ";\n", ";\n", $str);
169 $str = str_replace($this->docStringEndToken, "\n", $str);
170
171 return $str;
172 }
173
174 /**
175 * Pretty prints an array of nodes (statements) and indents them optionally.
176 *
177 * @param Node[] $nodes Array of nodes
178 * @param bool $indent Whether to indent the printed nodes
179 *
180 * @return string Pretty printed statements
181 */
182 protected function pStmts(array $nodes, $indent = true) {
183 $result = '';
184 foreach ($nodes as $node) {
185 $comments = $node->getAttribute('comments', array());
186 if ($comments) {
187 $result .= "\n" . $this->pComments($comments);
188 if ($node instanceof Stmt\Nop) {
189 continue;
190 }
191 }
192
193 $result .= "\n" . $this->p($node) . ($node instanceof Expr ? ';' : '');
194 }
195
196 if ($indent) {
197 return preg_replace('~\n(?!$|' . $this->noIndentToken . ')~', "\n ", $result);
198 } else {
199 return $result;
200 }
201 }
202
203 /**
204 * Pretty prints a node.
205 *
206 * @param Node $node Node to be pretty printed
207 *
208 * @return string Pretty printed node
209 */
210 protected function p(Node $node) {
211 return $this->{'p' . $node->getType()}($node);
212 }
213
214 protected function pInfixOp($type, Node $leftNode, $operatorString, Node $rightNode) {
215 list($precedence, $associativity) = $this->precedenceMap[$type];
216
217 return $this->pPrec($leftNode, $precedence, $associativity, -1)
218 . $operatorString
219 . $this->pPrec($rightNode, $precedence, $associativity, 1);
220 }
221
222 protected function pPrefixOp($type, $operatorString, Node $node) {
223 list($precedence, $associativity) = $this->precedenceMap[$type];
224 return $operatorString . $this->pPrec($node, $precedence, $associativity, 1);
225 }
226
227 protected function pPostfixOp($type, Node $node, $operatorString) {
228 list($precedence, $associativity) = $this->precedenceMap[$type];
229 return $this->pPrec($node, $precedence, $associativity, -1) . $operatorString;
230 }
231
232 /**
233 * Prints an expression node with the least amount of parentheses necessary to preserve the meaning.
234 *
235 * @param Node $node Node to pretty print
236 * @param int $parentPrecedence Precedence of the parent operator
237 * @param int $parentAssociativity Associativity of parent operator
238 * (-1 is left, 0 is nonassoc, 1 is right)
239 * @param int $childPosition Position of the node relative to the operator
240 * (-1 is left, 1 is right)
241 *
242 * @return string The pretty printed node
243 */
244 protected function pPrec(Node $node, $parentPrecedence, $parentAssociativity, $childPosition) {
245 $type = $node->getType();
246 if (isset($this->precedenceMap[$type])) {
247 $childPrecedence = $this->precedenceMap[$type][0];
248 if ($childPrecedence > $parentPrecedence
249 || ($parentPrecedence == $childPrecedence && $parentAssociativity != $childPosition)
250 ) {
251 return '(' . $this->p($node) . ')';
252 }
253 }
254
255 return $this->p($node);
256 }
257
258 /**
259 * Pretty prints an array of nodes and implodes the printed values.
260 *
261 * @param Node[] $nodes Array of Nodes to be printed
262 * @param string $glue Character to implode with
263 *
264 * @return string Imploded pretty printed nodes
265 */
266 protected function pImplode(array $nodes, $glue = '') {
267 $pNodes = array();
268 foreach ($nodes as $node) {
269 if (null === $node) {
270 $pNodes[] = '';
271 } else {
272 $pNodes[] = $this->p($node);
273 }
274 }
275
276 return implode($glue, $pNodes);
277 }
278
279 /**
280 * Pretty prints an array of nodes and implodes the printed values with commas.
281 *
282 * @param Node[] $nodes Array of Nodes to be printed
283 *
284 * @return string Comma separated pretty printed nodes
285 */
286 protected function pCommaSeparated(array $nodes) {
287 return $this->pImplode($nodes, ', ');
288 }
289
290 /**
291 * Pretty prints a comma-separated list of nodes in multiline style, including comments.
292 *
293 * The result includes a leading newline and one level of indentation (same as pStmts).
294 *
295 * @param Node[] $nodes Array of Nodes to be printed
296 * @param bool $trailingComma Whether to use a trailing comma
297 *
298 * @return string Comma separated pretty printed nodes in multiline style
299 */
300 protected function pCommaSeparatedMultiline(array $nodes, $trailingComma) {
301 $result = '';
302 $lastIdx = count($nodes) - 1;
303 foreach ($nodes as $idx => $node) {
304 if ($node !== null) {
305 $comments = $node->getAttribute('comments', array());
306 if ($comments) {
307 $result .= "\n" . $this->pComments($comments);
308 }
309
310 $result .= "\n" . $this->p($node);
311 } else {
312 $result .= "\n";
313 }
314 if ($trailingComma || $idx !== $lastIdx) {
315 $result .= ',';
316 }
317 }
318
319 return preg_replace('~\n(?!$|' . $this->noIndentToken . ')~', "\n ", $result);
320 }
321
322 /**
323 * Signals the pretty printer that a string shall not be indented.
324 *
325 * @param string $string Not to be indented string
326 *
327 * @return string String marked with $this->noIndentToken's.
328 */
329 protected function pNoIndent($string) {
330 return str_replace("\n", "\n" . $this->noIndentToken, $string);
331 }
332
333 /**
334 * Prints reformatted text of the passed comments.
335 *
336 * @param Comment[] $comments List of comments
337 *
338 * @return string Reformatted text of comments
339 */
340 protected function pComments(array $comments) {
341 $formattedComments = [];
342
343 foreach ($comments as $comment) {
344 $formattedComments[] = $comment->getReformattedText();
345 }
346
347 return implode("\n", $formattedComments);
348 }
349 }
350