PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 3.5.0
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v3.5.0
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 3.5.0, at includes/Dependencies/PhpParser/PrettyPrinterAbstract.php

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