1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | namespace GraphQL\Language; |
||||
6 | |||||
7 | use GraphQL\Language\AST\ArgumentNode; |
||||
8 | use GraphQL\Language\AST\BooleanValueNode; |
||||
9 | use GraphQL\Language\AST\DirectiveDefinitionNode; |
||||
10 | use GraphQL\Language\AST\DirectiveNode; |
||||
11 | use GraphQL\Language\AST\DocumentNode; |
||||
12 | use GraphQL\Language\AST\EnumTypeDefinitionNode; |
||||
13 | use GraphQL\Language\AST\EnumTypeExtensionNode; |
||||
14 | use GraphQL\Language\AST\EnumValueDefinitionNode; |
||||
15 | use GraphQL\Language\AST\EnumValueNode; |
||||
16 | use GraphQL\Language\AST\FieldDefinitionNode; |
||||
17 | use GraphQL\Language\AST\FieldNode; |
||||
18 | use GraphQL\Language\AST\FloatValueNode; |
||||
19 | use GraphQL\Language\AST\FragmentDefinitionNode; |
||||
20 | use GraphQL\Language\AST\FragmentSpreadNode; |
||||
21 | use GraphQL\Language\AST\InlineFragmentNode; |
||||
22 | use GraphQL\Language\AST\InputObjectTypeDefinitionNode; |
||||
23 | use GraphQL\Language\AST\InputObjectTypeExtensionNode; |
||||
24 | use GraphQL\Language\AST\InputValueDefinitionNode; |
||||
25 | use GraphQL\Language\AST\InterfaceTypeDefinitionNode; |
||||
26 | use GraphQL\Language\AST\InterfaceTypeExtensionNode; |
||||
27 | use GraphQL\Language\AST\IntValueNode; |
||||
28 | use GraphQL\Language\AST\ListTypeNode; |
||||
29 | use GraphQL\Language\AST\ListValueNode; |
||||
30 | use GraphQL\Language\AST\NamedTypeNode; |
||||
31 | use GraphQL\Language\AST\NameNode; |
||||
32 | use GraphQL\Language\AST\Node; |
||||
33 | use GraphQL\Language\AST\NodeKind; |
||||
34 | use GraphQL\Language\AST\NonNullTypeNode; |
||||
35 | use GraphQL\Language\AST\NullValueNode; |
||||
36 | use GraphQL\Language\AST\ObjectFieldNode; |
||||
37 | use GraphQL\Language\AST\ObjectTypeDefinitionNode; |
||||
38 | use GraphQL\Language\AST\ObjectTypeExtensionNode; |
||||
39 | use GraphQL\Language\AST\ObjectValueNode; |
||||
40 | use GraphQL\Language\AST\OperationDefinitionNode; |
||||
41 | use GraphQL\Language\AST\OperationTypeDefinitionNode; |
||||
42 | use GraphQL\Language\AST\ScalarTypeDefinitionNode; |
||||
43 | use GraphQL\Language\AST\ScalarTypeExtensionNode; |
||||
44 | use GraphQL\Language\AST\SchemaDefinitionNode; |
||||
45 | use GraphQL\Language\AST\SchemaTypeExtensionNode; |
||||
46 | use GraphQL\Language\AST\SelectionSetNode; |
||||
47 | use GraphQL\Language\AST\StringValueNode; |
||||
48 | use GraphQL\Language\AST\UnionTypeDefinitionNode; |
||||
49 | use GraphQL\Language\AST\UnionTypeExtensionNode; |
||||
50 | use GraphQL\Language\AST\VariableDefinitionNode; |
||||
51 | use GraphQL\Language\AST\VariableNode; |
||||
52 | use GraphQL\Utils\Utils; |
||||
53 | use function count; |
||||
54 | use function implode; |
||||
55 | use function json_encode; |
||||
56 | use function preg_replace; |
||||
57 | use function sprintf; |
||||
58 | use function str_replace; |
||||
59 | use function strpos; |
||||
60 | |||||
61 | /** |
||||
62 | * Prints AST to string. Capable of printing GraphQL queries and Type definition language. |
||||
63 | * Useful for pretty-printing queries or printing back AST for logging, documentation, etc. |
||||
64 | * |
||||
65 | * Usage example: |
||||
66 | * |
||||
67 | * ```php |
||||
68 | * $query = 'query myQuery {someField}'; |
||||
69 | * $ast = GraphQL\Language\Parser::parse($query); |
||||
70 | * $printed = GraphQL\Language\Printer::doPrint($ast); |
||||
71 | * ``` |
||||
72 | */ |
||||
73 | class Printer |
||||
74 | { |
||||
75 | /** |
||||
76 | * Prints AST to string. Capable of printing GraphQL queries and Type definition language. |
||||
77 | * |
||||
78 | * @param Node $ast |
||||
79 | * |
||||
80 | * @return string |
||||
81 | * |
||||
82 | * @api |
||||
83 | */ |
||||
84 | 144 | public static function doPrint($ast) |
|||
85 | { |
||||
86 | 144 | static $instance; |
|||
87 | 144 | $instance = $instance ?: new static(); |
|||
88 | |||||
89 | 144 | return $instance->printAST($ast); |
|||
90 | } |
||||
91 | |||||
92 | 1 | protected function __construct() |
|||
93 | { |
||||
94 | 1 | } |
|||
95 | |||||
96 | 144 | public function printAST($ast) |
|||
97 | { |
||||
98 | 144 | return Visitor::visit( |
|||
99 | 144 | $ast, |
|||
100 | [ |
||||
101 | 'leave' => [ |
||||
102 | NodeKind::NAME => static function (NameNode $node) { |
||||
103 | 77 | return '' . $node->value; |
|||
104 | 144 | }, |
|||
105 | |||||
106 | NodeKind::VARIABLE => static function (VariableNode $node) { |
||||
107 | 6 | return '$' . $node->name; |
|||
108 | 144 | }, |
|||
109 | |||||
110 | NodeKind::DOCUMENT => function (DocumentNode $node) { |
||||
111 | 31 | return $this->join($node->definitions, "\n\n") . "\n"; |
|||
112 | 144 | }, |
|||
113 | |||||
114 | NodeKind::OPERATION_DEFINITION => function (OperationDefinitionNode $node) { |
||||
115 | 9 | $op = $node->operation; |
|||
116 | 9 | $name = $node->name; |
|||
117 | 9 | $varDefs = $this->wrap('(', $this->join($node->variableDefinitions, ', '), ')'); |
|||
118 | 9 | $directives = $this->join($node->directives, ' '); |
|||
119 | 9 | $selectionSet = $node->selectionSet; |
|||
120 | |||||
121 | // Anonymous queries with no directives or variable definitions can use |
||||
122 | // the query short form. |
||||
123 | 9 | return ! $name && ! $directives && ! $varDefs && $op === 'query' |
|||
0 ignored issues
–
show
introduced
by
![]() |
|||||
124 | 8 | ? $selectionSet |
|||
125 | 9 | : $this->join([$op, $this->join([$name, $varDefs]), $directives, $selectionSet], ' '); |
|||
126 | 144 | }, |
|||
127 | |||||
128 | NodeKind::VARIABLE_DEFINITION => function (VariableDefinitionNode $node) { |
||||
129 | 6 | return $node->variable |
|||
130 | 6 | . ': ' |
|||
131 | 6 | . $node->type |
|||
132 | 6 | . $this->wrap(' = ', $node->defaultValue) |
|||
133 | 6 | . $this->wrap(' ', $this->join($node->directives, ' ')); |
|||
134 | 144 | }, |
|||
135 | |||||
136 | NodeKind::SELECTION_SET => function (SelectionSetNode $node) { |
||||
137 | 11 | return $this->block($node->selections); |
|||
138 | 144 | }, |
|||
139 | |||||
140 | NodeKind::FIELD => function (FieldNode $node) { |
||||
141 | 12 | return $this->join( |
|||
142 | [ |
||||
143 | 12 | $this->wrap('', $node->alias, ': ') . $node->name . $this->wrap( |
|||
144 | 12 | '(', |
|||
145 | 12 | $this->join($node->arguments, ', '), |
|||
146 | 12 | ')' |
|||
147 | ), |
||||
148 | 12 | $this->join($node->directives, ' '), |
|||
149 | 12 | $node->selectionSet, |
|||
150 | ], |
||||
151 | 12 | ' ' |
|||
152 | ); |
||||
153 | 144 | }, |
|||
154 | |||||
155 | NodeKind::ARGUMENT => static function (ArgumentNode $node) { |
||||
156 | 10 | return $node->name . ': ' . $node->value; |
|||
157 | 144 | }, |
|||
158 | |||||
159 | NodeKind::FRAGMENT_SPREAD => function (FragmentSpreadNode $node) { |
||||
160 | 2 | return '...' . $node->name . $this->wrap(' ', $this->join($node->directives, ' ')); |
|||
161 | 144 | }, |
|||
162 | |||||
163 | NodeKind::INLINE_FRAGMENT => function (InlineFragmentNode $node) { |
||||
164 | 2 | return $this->join( |
|||
165 | [ |
||||
166 | 2 | '...', |
|||
167 | 2 | $this->wrap('on ', $node->typeCondition), |
|||
168 | 2 | $this->join($node->directives, ' '), |
|||
169 | 2 | $node->selectionSet, |
|||
170 | ], |
||||
171 | 2 | ' ' |
|||
172 | ); |
||||
173 | 144 | }, |
|||
174 | |||||
175 | NodeKind::FRAGMENT_DEFINITION => function (FragmentDefinitionNode $node) { |
||||
176 | // Note: fragment variable definitions are experimental and may be changed or removed in the future. |
||||
177 | 4 | return sprintf('fragment %s', $node->name) |
|||
178 | 4 | . $this->wrap('(', $this->join($node->variableDefinitions, ', '), ')') |
|||
179 | 4 | . sprintf(' on %s ', $node->typeCondition) |
|||
180 | 4 | . $this->wrap('', $this->join($node->directives, ' '), ' ') |
|||
181 | 4 | . $node->selectionSet; |
|||
182 | 144 | }, |
|||
183 | |||||
184 | NodeKind::INT => static function (IntValueNode $node) { |
||||
185 | 22 | return $node->value; |
|||
186 | 144 | }, |
|||
187 | |||||
188 | NodeKind::FLOAT => static function (FloatValueNode $node) { |
||||
189 | 8 | return $node->value; |
|||
190 | 144 | }, |
|||
191 | |||||
192 | NodeKind::STRING => function (StringValueNode $node, $key) { |
||||
193 | 30 | if ($node->block) { |
|||
194 | 8 | return $this->printBlockString($node->value, $key === 'description'); |
|||
195 | } |
||||
196 | |||||
197 | 26 | return json_encode($node->value); |
|||
198 | 144 | }, |
|||
199 | |||||
200 | NodeKind::BOOLEAN => static function (BooleanValueNode $node) { |
||||
201 | 14 | return $node->value ? 'true' : 'false'; |
|||
202 | 144 | }, |
|||
203 | |||||
204 | NodeKind::NULL => static function (NullValueNode $node) { |
||||
0 ignored issues
–
show
The parameter
$node is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||
205 | 9 | return 'null'; |
|||
206 | 144 | }, |
|||
207 | |||||
208 | NodeKind::ENUM => static function (EnumValueNode $node) { |
||||
209 | 18 | return $node->value; |
|||
210 | 144 | }, |
|||
211 | |||||
212 | NodeKind::LST => function (ListValueNode $node) { |
||||
213 | 5 | return '[' . $this->join($node->values, ', ') . ']'; |
|||
214 | 144 | }, |
|||
215 | |||||
216 | NodeKind::OBJECT => function (ObjectValueNode $node) { |
||||
217 | 5 | return '{' . $this->join($node->fields, ', ') . '}'; |
|||
218 | 144 | }, |
|||
219 | |||||
220 | NodeKind::OBJECT_FIELD => static function (ObjectFieldNode $node) { |
||||
221 | 5 | return $node->name . ': ' . $node->value; |
|||
222 | 144 | }, |
|||
223 | |||||
224 | NodeKind::DIRECTIVE => function (DirectiveNode $node) { |
||||
225 | 9 | return '@' . $node->name . $this->wrap('(', $this->join($node->arguments, ', '), ')'); |
|||
226 | 144 | }, |
|||
227 | |||||
228 | NodeKind::NAMED_TYPE => static function (NamedTypeNode $node) { |
||||
229 | 70 | return $node->name; |
|||
230 | 144 | }, |
|||
231 | |||||
232 | NodeKind::LIST_TYPE => static function (ListTypeNode $node) { |
||||
233 | 58 | return '[' . $node->type . ']'; |
|||
234 | 144 | }, |
|||
235 | |||||
236 | NodeKind::NON_NULL_TYPE => static function (NonNullTypeNode $node) { |
||||
237 | 60 | return $node->type . '!'; |
|||
238 | 144 | }, |
|||
239 | |||||
240 | NodeKind::SCHEMA_DEFINITION => function (SchemaDefinitionNode $def) { |
||||
241 | 3 | return $this->join( |
|||
242 | [ |
||||
243 | 3 | 'schema', |
|||
244 | 3 | $this->join($def->directives, ' '), |
|||
245 | 3 | $this->block($def->operationTypes), |
|||
246 | ], |
||||
247 | 3 | ' ' |
|||
248 | ); |
||||
249 | 144 | }, |
|||
250 | |||||
251 | NodeKind::OPERATION_TYPE_DEFINITION => static function (OperationTypeDefinitionNode $def) { |
||||
252 | 4 | return $def->operation . ': ' . $def->type; |
|||
253 | 144 | }, |
|||
254 | |||||
255 | NodeKind::SCALAR_TYPE_DEFINITION => $this->addDescription(function (ScalarTypeDefinitionNode $def) { |
||||
256 | 59 | return $this->join(['scalar', $def->name, $this->join($def->directives, ' ')], ' '); |
|||
257 | 144 | }), |
|||
258 | |||||
259 | NodeKind::OBJECT_TYPE_DEFINITION => $this->addDescription(function (ObjectTypeDefinitionNode $def) { |
||||
260 | 58 | return $this->join( |
|||
261 | [ |
||||
262 | 58 | 'type', |
|||
263 | 58 | $def->name, |
|||
264 | 58 | $this->wrap('implements ', $this->join($def->interfaces, ' & ')), |
|||
265 | 58 | $this->join($def->directives, ' '), |
|||
266 | 58 | $this->block($def->fields), |
|||
267 | ], |
||||
268 | 58 | ' ' |
|||
269 | ); |
||||
270 | 144 | }), |
|||
271 | |||||
272 | NodeKind::FIELD_DEFINITION => $this->addDescription(function (FieldDefinitionNode $def) { |
||||
273 | $noIndent = Utils::every($def->arguments, static function (string $arg) { |
||||
0 ignored issues
–
show
$def->arguments of type GraphQL\Language\AST\NodeList is incompatible with the type array<mixed,mixed> expected by parameter $traversable of GraphQL\Utils\Utils::every() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
274 | 58 | return strpos($arg, "\n") === false; |
|||
275 | 58 | }); |
|||
276 | |||||
277 | 58 | return $def->name |
|||
278 | 58 | . ($noIndent |
|||
279 | 58 | ? $this->wrap('(', $this->join($def->arguments, ', '), ')') |
|||
280 | 58 | : $this->wrap("(\n", $this->indent($this->join($def->arguments, "\n")), "\n)")) |
|||
281 | 58 | . ': ' . $def->type |
|||
282 | 58 | . $this->wrap(' ', $this->join($def->directives, ' ')); |
|||
283 | 144 | }), |
|||
284 | |||||
285 | NodeKind::INPUT_VALUE_DEFINITION => $this->addDescription(function (InputValueDefinitionNode $def) { |
||||
286 | 58 | return $this->join( |
|||
287 | [ |
||||
288 | 58 | $def->name . ': ' . $def->type, |
|||
289 | 58 | $this->wrap('= ', $def->defaultValue), |
|||
290 | 58 | $this->join($def->directives, ' '), |
|||
291 | ], |
||||
292 | 58 | ' ' |
|||
293 | ); |
||||
294 | 144 | }), |
|||
295 | |||||
296 | 144 | NodeKind::INTERFACE_TYPE_DEFINITION => $this->addDescription( |
|||
297 | function (InterfaceTypeDefinitionNode $def) { |
||||
298 | 58 | return $this->join( |
|||
299 | [ |
||||
300 | 58 | 'interface', |
|||
301 | 58 | $def->name, |
|||
302 | 58 | $this->join($def->directives, ' '), |
|||
303 | 58 | $this->block($def->fields), |
|||
304 | ], |
||||
305 | 58 | ' ' |
|||
306 | ); |
||||
307 | 144 | } |
|||
308 | ), |
||||
309 | |||||
310 | NodeKind::UNION_TYPE_DEFINITION => $this->addDescription(function (UnionTypeDefinitionNode $def) { |
||||
311 | 58 | return $this->join( |
|||
312 | [ |
||||
313 | 58 | 'union', |
|||
314 | 58 | $def->name, |
|||
315 | 58 | $this->join($def->directives, ' '), |
|||
316 | 58 | $def->types |
|||
317 | 58 | ? '= ' . $this->join($def->types, ' | ') |
|||
318 | 58 | : '', |
|||
319 | ], |
||||
320 | 58 | ' ' |
|||
321 | ); |
||||
322 | 144 | }), |
|||
323 | |||||
324 | NodeKind::ENUM_TYPE_DEFINITION => $this->addDescription(function (EnumTypeDefinitionNode $def) { |
||||
325 | 58 | return $this->join( |
|||
326 | [ |
||||
327 | 58 | 'enum', |
|||
328 | 58 | $def->name, |
|||
329 | 58 | $this->join($def->directives, ' '), |
|||
330 | 58 | $this->block($def->values), |
|||
331 | ], |
||||
332 | 58 | ' ' |
|||
333 | ); |
||||
334 | 144 | }), |
|||
335 | |||||
336 | NodeKind::ENUM_VALUE_DEFINITION => $this->addDescription(function (EnumValueDefinitionNode $def) { |
||||
337 | 58 | return $this->join([$def->name, $this->join($def->directives, ' ')], ' '); |
|||
338 | 144 | }), |
|||
339 | |||||
340 | NodeKind::INPUT_OBJECT_TYPE_DEFINITION => $this->addDescription(function ( |
||||
341 | InputObjectTypeDefinitionNode $def |
||||
342 | ) { |
||||
343 | 58 | return $this->join( |
|||
344 | [ |
||||
345 | 58 | 'input', |
|||
346 | 58 | $def->name, |
|||
347 | 58 | $this->join($def->directives, ' '), |
|||
348 | 58 | $this->block($def->fields), |
|||
349 | ], |
||||
350 | 58 | ' ' |
|||
351 | ); |
||||
352 | 144 | }), |
|||
353 | |||||
354 | NodeKind::SCHEMA_EXTENSION => function (SchemaTypeExtensionNode $def) { |
||||
355 | 1 | return $this->join( |
|||
356 | [ |
||||
357 | 1 | 'extend schema', |
|||
358 | 1 | $this->join($def->directives, ' '), |
|||
359 | 1 | $this->block($def->operationTypes), |
|||
360 | ], |
||||
361 | 1 | ' ' |
|||
362 | ); |
||||
363 | 144 | }, |
|||
364 | |||||
365 | NodeKind::SCALAR_TYPE_EXTENSION => function (ScalarTypeExtensionNode $def) { |
||||
366 | 3 | return $this->join( |
|||
367 | [ |
||||
368 | 3 | 'extend scalar', |
|||
369 | 3 | $def->name, |
|||
370 | 3 | $this->join($def->directives, ' '), |
|||
371 | ], |
||||
372 | 3 | ' ' |
|||
373 | ); |
||||
374 | 144 | }, |
|||
375 | |||||
376 | NodeKind::OBJECT_TYPE_EXTENSION => function (ObjectTypeExtensionNode $def) { |
||||
377 | 2 | return $this->join( |
|||
378 | [ |
||||
379 | 2 | 'extend type', |
|||
380 | 2 | $def->name, |
|||
381 | 2 | $this->wrap('implements ', $this->join($def->interfaces, ' & ')), |
|||
382 | 2 | $this->join($def->directives, ' '), |
|||
383 | 2 | $this->block($def->fields), |
|||
384 | ], |
||||
385 | 2 | ' ' |
|||
386 | ); |
||||
387 | 144 | }, |
|||
388 | |||||
389 | NodeKind::INTERFACE_TYPE_EXTENSION => function (InterfaceTypeExtensionNode $def) { |
||||
390 | 2 | return $this->join( |
|||
391 | [ |
||||
392 | 2 | 'extend interface', |
|||
393 | 2 | $def->name, |
|||
394 | 2 | $this->join($def->directives, ' '), |
|||
395 | 2 | $this->block($def->fields), |
|||
396 | ], |
||||
397 | 2 | ' ' |
|||
398 | ); |
||||
399 | 144 | }, |
|||
400 | |||||
401 | NodeKind::UNION_TYPE_EXTENSION => function (UnionTypeExtensionNode $def) { |
||||
402 | 2 | return $this->join( |
|||
403 | [ |
||||
404 | 2 | 'extend union', |
|||
405 | 2 | $def->name, |
|||
406 | 2 | $this->join($def->directives, ' '), |
|||
407 | 2 | $def->types |
|||
408 | 2 | ? '= ' . $this->join($def->types, ' | ') |
|||
409 | 2 | : '', |
|||
410 | ], |
||||
411 | 2 | ' ' |
|||
412 | ); |
||||
413 | 144 | }, |
|||
414 | |||||
415 | NodeKind::ENUM_TYPE_EXTENSION => function (EnumTypeExtensionNode $def) { |
||||
416 | 2 | return $this->join( |
|||
417 | [ |
||||
418 | 2 | 'extend enum', |
|||
419 | 2 | $def->name, |
|||
420 | 2 | $this->join($def->directives, ' '), |
|||
421 | 2 | $this->block($def->values), |
|||
422 | ], |
||||
423 | 2 | ' ' |
|||
424 | ); |
||||
425 | 144 | }, |
|||
426 | |||||
427 | NodeKind::INPUT_OBJECT_TYPE_EXTENSION => function (InputObjectTypeExtensionNode $def) { |
||||
428 | 2 | return $this->join( |
|||
429 | [ |
||||
430 | 2 | 'extend input', |
|||
431 | 2 | $def->name, |
|||
432 | 2 | $this->join($def->directives, ' '), |
|||
433 | 2 | $this->block($def->fields), |
|||
434 | ], |
||||
435 | 2 | ' ' |
|||
436 | ); |
||||
437 | 144 | }, |
|||
438 | |||||
439 | NodeKind::DIRECTIVE_DEFINITION => $this->addDescription(function (DirectiveDefinitionNode $def) { |
||||
440 | $noIndent = Utils::every($def->arguments, static function (string $arg) { |
||||
441 | 58 | return strpos($arg, "\n") === false; |
|||
442 | 58 | }); |
|||
443 | |||||
444 | return 'directive @' |
||||
445 | 58 | . $def->name |
|||
446 | 58 | . ($noIndent |
|||
447 | 58 | ? $this->wrap('(', $this->join($def->arguments, ', '), ')') |
|||
448 | 58 | : $this->wrap("(\n", $this->indent($this->join($def->arguments, "\n")), "\n")) |
|||
449 | 58 | . ' on ' . $this->join($def->locations, ' | '); |
|||
450 | 144 | }), |
|||
451 | ], |
||||
452 | ] |
||||
453 | ); |
||||
454 | } |
||||
455 | |||||
456 | 144 | public function addDescription(callable $cb) |
|||
457 | { |
||||
458 | return function ($node) use ($cb) { |
||||
459 | 59 | return $this->join([$node->description, $cb($node)], "\n"); |
|||
460 | 144 | }; |
|||
461 | } |
||||
462 | |||||
463 | /** |
||||
464 | * If maybeString is not null or empty, then wrap with start and end, otherwise |
||||
465 | * print an empty string. |
||||
466 | */ |
||||
467 | 70 | public function wrap($start, $maybeString, $end = '') |
|||
468 | { |
||||
469 | 70 | return $maybeString ? ($start . $maybeString . $end) : ''; |
|||
470 | } |
||||
471 | |||||
472 | /** |
||||
473 | * Given array, print each item on its own line, wrapped in an |
||||
474 | * indented "{ }" block. |
||||
475 | */ |
||||
476 | 69 | public function block($array) |
|||
477 | { |
||||
478 | 69 | return $array && $this->length($array) |
|||
479 | 69 | ? "{\n" . $this->indent($this->join($array, "\n")) . "\n}" |
|||
480 | 69 | : ''; |
|||
481 | } |
||||
482 | |||||
483 | 69 | public function indent($maybeString) |
|||
484 | { |
||||
485 | 69 | return $maybeString ? ' ' . str_replace("\n", "\n ", $maybeString) : ''; |
|||
486 | } |
||||
487 | |||||
488 | public function manyList($start, $list, $separator, $end) |
||||
489 | { |
||||
490 | return $this->length($list) === 0 ? null : ($start . $this->join($list, $separator) . $end); |
||||
491 | } |
||||
492 | |||||
493 | 69 | public function length($maybeArray) |
|||
494 | { |
||||
495 | 69 | return $maybeArray ? count($maybeArray) : 0; |
|||
496 | } |
||||
497 | |||||
498 | 72 | public function join($maybeArray, $separator = '') |
|||
499 | { |
||||
500 | 72 | return $maybeArray |
|||
501 | 72 | ? implode( |
|||
502 | 72 | $separator, |
|||
503 | 72 | Utils::filter( |
|||
504 | 72 | $maybeArray, |
|||
505 | static function ($x) { |
||||
506 | 72 | return (bool) $x; |
|||
507 | 72 | } |
|||
508 | ) |
||||
509 | ) |
||||
510 | 72 | : ''; |
|||
511 | } |
||||
512 | |||||
513 | /** |
||||
514 | * Print a block string in the indented block form by adding a leading and |
||||
515 | * trailing blank line. However, if a block string starts with whitespace and is |
||||
516 | * a single-line, adding a leading blank line would strip that whitespace. |
||||
517 | */ |
||||
518 | 8 | private function printBlockString($value, $isDescription) |
|||
519 | { |
||||
520 | 8 | $escaped = str_replace('"""', '\\"""', $value); |
|||
521 | |||||
522 | 8 | return ($value[0] === ' ' || $value[0] === "\t") && strpos($value, "\n") === false |
|||
523 | 3 | ? ('"""' . preg_replace('/"$/', "\"\n", $escaped) . '"""') |
|||
524 | 8 | : ('"""' . "\n" . ($isDescription ? $escaped : $this->indent($escaped)) . "\n" . '"""'); |
|||
525 | } |
||||
526 | } |
||||
527 |