Overview

Namespaces

  • Pharborist
    • Constants
    • ControlStructures
    • Exceptions
    • Functions
    • Generators
    • Namespaces
    • Objects
    • Operators
    • Types
    • Variables

Classes

  • Pharborist\Variables\CompoundVariableNode
  • Pharborist\Variables\GlobalStatementNode
  • Pharborist\Variables\ReferenceVariableNode
  • Pharborist\Variables\StaticVariableNode
  • Pharborist\Variables\StaticVariableStatementNode
  • Pharborist\Variables\VariableNode
  • Pharborist\Variables\VariableVariableNode

Interfaces

  • Pharborist\Variables\VariableExpressionNode
  • Overview
  • Namespace
  • Class
  1: <?php
  2: namespace Pharborist;
  3: 
  4: use Pharborist\Constants\ConstantNode;
  5: use Pharborist\ControlStructures\CaseNode;
  6: use Pharborist\ControlStructures\DefaultNode;
  7: use Pharborist\ControlStructures\DoWhileNode;
  8: use Pharborist\ControlStructures\ElseIfNode;
  9: use Pharborist\ControlStructures\ForeachNode;
 10: use Pharborist\ControlStructures\ForNode;
 11: use Pharborist\ControlStructures\IfNode;
 12: use Pharborist\ControlStructures\SwitchNode;
 13: use Pharborist\ControlStructures\WhileNode;
 14: use Pharborist\Exceptions\CatchNode;
 15: use Pharborist\Functions\CallNode;
 16: use Pharborist\Functions\FunctionDeclarationNode;
 17: use Pharborist\Functions\ParameterNode;
 18: use Pharborist\Namespaces\NamespaceNode;
 19: use Pharborist\Objects\ClassMethodNode;
 20: use Pharborist\Objects\InterfaceMethodNode;
 21: use Pharborist\Objects\InterfaceNode;
 22: use Pharborist\Objects\NewNode;
 23: use Pharborist\Objects\ObjectMethodCallNode;
 24: use Pharborist\Objects\SingleInheritanceNode;
 25: use Pharborist\Operators\BinaryOperationNode;
 26: use Pharborist\Operators\CastNode;
 27: use Pharborist\Operators\CloneNode;
 28: use Pharborist\Operators\PostDecrementNode;
 29: use Pharborist\Operators\PostIncrementNode;
 30: use Pharborist\Operators\PrintNode;
 31: use Pharborist\Operators\UnaryOperationNode;
 32: use Pharborist\Types\ArrayNode;
 33: use Pharborist\Types\BooleanNode;
 34: use Pharborist\Types\NullNode;
 35: 
 36: class Formatter extends VisitorBase {
 37:   /**
 38:    * Formatter config
 39:    *
 40:    * @var array
 41:    */
 42:   protected $config;
 43: 
 44:   /**
 45:    * Current indentation level.
 46:    *
 47:    * @var int
 48:    */
 49:   protected $indentLevel = 0;
 50: 
 51:   /**
 52:    * Data attached to nodes.
 53:    *
 54:    * @var \SplObjectStorage
 55:    */
 56:   protected $nodeData;
 57: 
 58:   public function __construct($config = []) {
 59:     $this->nodeData = new \SplObjectStorage();
 60:     $this->config = $config + [
 61:       'nl' => "\n",
 62:       'indent' => 2,
 63:       'soft_limit' => 80,
 64:       'boolean_null_upper' => TRUE,
 65:       'force_array_new_style' => TRUE,
 66:       'else_newline' => TRUE,
 67:       'declaration_brace_newline' => FALSE,
 68:       'list_keep_wrap' => FALSE,
 69:       'list_wrap_if_long' => FALSE,
 70:       'blank_lines_around_class_body' => 1,
 71:     ];
 72:   }
 73: 
 74:   /**
 75:    * @param Node $node
 76:    */
 77:   public function format(Node $node) {
 78:     if ($node instanceof ParentNode) {
 79:       $node->acceptVisitor($this);
 80:     }
 81:     else {
 82:       $this->visit($node);
 83:     }
 84:   }
 85: 
 86:   /**
 87:    * Get the config value for the specified key.
 88:    *
 89:    * @param string $key
 90:    *   The config key.
 91:    *
 92:    * @return mixed
 93:    */
 94:   public function getConfig($key) {
 95:     return $this->config[$key];
 96:   }
 97: 
 98:   /**
 99:    * @param bool $close
100:    *
101:    * @return string
102:    */
103:   protected function getIndent($close = FALSE) {
104:     $indent_per_level = str_repeat(' ', $this->config['indent']);
105:     return str_repeat($indent_per_level, $this->indentLevel - ($close ? 1 : 0));
106:   }
107: 
108:   /**
109:    * @param WhitespaceNode|NULL $wsNode
110:    * @param bool $close
111:    *
112:    * @return string
113:    */
114:   protected function getNewlineIndent($wsNode = NULL, $close = FALSE) {
115:     $indent = $this->getIndent($close);
116:     $nl_count = $wsNode ? $wsNode->getNewlineCount() : 1;
117:     $nl_count = max($nl_count, 1);
118:     return str_repeat($this->config['nl'], $nl_count) . $indent;
119:   }
120: 
121:   /**
122:    * Set a single space before a node.
123:    *
124:    * @param Node $node
125:    *   Node to set space before.
126:    */
127:   protected function spaceBefore(Node $node) {
128:     $prev = $node->previousToken();
129:     if ($prev instanceof WhitespaceNode) {
130:       $prev->setText(' ');
131:     }
132:     else {
133:       $node->before(Token::space());
134:     }
135:   }
136: 
137:   /**
138:    * Set a single space after a node.
139:    *
140:    * @param Node $node
141:    *   Node to set space after.
142:    */
143:   protected function spaceAfter(Node $node) {
144:     $next = $node->nextToken();
145:     if ($next instanceof WhitespaceNode) {
146:       $next->setText(' ');
147:     }
148:     else {
149:       $node->after(Token::space());
150:     }
151:   }
152: 
153:   /**
154:    * Remove whitespace before a node.
155:    *
156:    * @param Node $node
157:    *   Node to remove space before.
158:    */
159:   protected function removeSpaceBefore(Node $node) {
160:     $prev = $node->previousToken();
161:     if ($prev instanceof WhitespaceNode) {
162:       $prev->remove();
163:     }
164:   }
165: 
166:   /**
167:    * Remove whitespace after a node.
168:    *
169:    * @param Node $node
170:    *   Node to remove space after.
171:    */
172:   protected function removeSpaceAfter(Node $node) {
173:     $next = $node->nextToken();
174:     if ($next instanceof WhitespaceNode) {
175:       $next->remove();
176:     }
177:   }
178: 
179:   /**
180:    * Set so there a newline before a node.
181:    *
182:    * @param Node $node
183:    *   Node to set newline before.
184:    * @param bool $close
185:    *   If the newline is for before a closing token, eg. ) or }
186:    */
187:   protected function newlineBefore(Node $node, $close = FALSE) {
188:     $prev = $node->previousToken();
189:     if ($prev instanceof WhitespaceNode) {
190:       $prev_ws = $prev->previousToken();
191:       if ($prev_ws instanceof CommentNode && $prev_ws->isLineComment() && $prev->getNewlineCount() === 0) {
192:         $prev->setText($this->getIndent($close));
193:       }
194:       else {
195:         $prev->setText($this->getNewlineIndent($prev, $close));
196:       }
197:     }
198:     else {
199:       if ($prev instanceof CommentNode && $prev->isLineComment()) {
200:         if ($this->indentLevel > 0) {
201:           $node->before(Token::whitespace($this->getIndent($close)));
202:         }
203:       }
204:       else {
205:         $node->before(Token::whitespace($this->getNewlineIndent(NULL, $close)));
206:       }
207:     }
208:   }
209: 
210:   /**
211:    * Set so there a newline after a node.
212:    *
213:    * @param Node $node
214:    *   Node to set newline after.
215:    */
216:   protected function newlineAfter(Node $node) {
217:     $next = $node->nextToken();
218:     if ($next instanceof WhitespaceNode) {
219:       $next->setText($this->getNewlineIndent($next));
220:     }
221:     else {
222:       $node->after(Token::whitespace($this->getNewlineIndent()));
223:     }
224:   }
225: 
226:   public function visitStatementNode(StatementNode $node) {
227:     $this->indentLevel++;
228:   }
229: 
230:   public function endStatementNode(StatementNode $node) {
231:     $this->indentLevel--;
232:   }
233: 
234:   public function visitBinaryOperationNode(BinaryOperationNode $node) {
235:     // Space around operator.
236:     $operator = $node->getOperator();
237:     $this->spaceBefore($operator);
238:     // @todo The following results in expressions are single line.
239:     $this->spaceAfter($operator);
240:   }
241: 
242:   public function visitUnaryOperationNode(UnaryOperationNode $node) {
243:     $operator = $node->getOperator();
244:     if ($node instanceof PostDecrementNode || $node instanceof PostIncrementNode) {
245:       $this->removeSpaceBefore($operator);
246:     }
247:     elseif ($node instanceof CastNode || $node instanceof CloneNode || $node instanceof PrintNode) {
248:       $this->spaceAfter($operator);
249:     }
250:     else {
251:       $this->removeSpaceAfter($operator);
252:     }
253:   }
254: 
255:   public function visitDocCommentNode(DocCommentNode $node) {
256:     $this->removeSpaceAfter($node);
257:     if ($this->indentLevel > 0) {
258:       $this->indentLevel--;
259:       $this->newlineAfter($node);
260:       $this->indentLevel++;
261:     }
262:     else {
263:       $this->newlineAfter($node);
264:     }
265:   }
266: 
267:   public function visitWhitespaceNode(WhitespaceNode $node) {
268:     if ($node->previousToken()->getType() === T_DOC_COMMENT) {
269:       return;
270:     }
271: 
272:     // Normalise whitespace.
273:     $nl_count = $node->getNewlineCount();
274:     if ($nl_count > 0) {
275:       $node->setText($this->getNewlineIndent($node));
276:     }
277:     else {
278:       $prev = $node->previousToken();
279:       if ($prev instanceof CommentNode && $prev->isLineComment()) {
280:         // Whitespace has already been processed.
281:       }
282:       else {
283:         $node->setText(' ');
284:       }
285:     }
286:   }
287: 
288:   public function visitTokenNode(TokenNode $node) {
289:     switch ($node->getType()) {
290:       case T_DOUBLE_ARROW:
291:         $this->spaceBefore($node);
292:         $this->spaceAfter($node);
293:         break;
294:     }
295:   }
296: 
297:   /**
298:    * Handle formatting of constant node.
299:    *
300:    * @param ConstantNode $node
301:    *   true, false, or null node.
302:    */
303:   protected function handleBuiltinConstantNode(ConstantNode $node) {
304:     $to_upper = $this->config['boolean_null_upper'];
305:     if ($to_upper) {
306:       $node->toUpperCase();
307:     }
308:     else {
309:       $node->toLowerCase();
310:     }
311:   }
312: 
313:   public function visitBooleanNode(BooleanNode $node) {
314:     $this->handleBuiltinConstantNode($node);
315:   }
316: 
317:   public function visitNullNode(NullNode $node) {
318:     $this->handleBuiltinConstantNode($node);
319:   }
320: 
321:   /**
322:    * Wrap single line body statements in braces.
323:    *
324:    * @param Node|NULL $node
325:    */
326:   protected function encloseBlock($node) {
327:     if ($node && !($node instanceof StatementBlockNode)) {
328:       $blockNode = new StatementBlockNode();
329:       $blockNode->append([Token::openBrace(), clone $node, Token::closeBrace()]);
330:       $node->replaceWith($blockNode);
331:     }
332:   }
333: 
334:   /**
335:    * Handle whitespace around and inside parens for control structures.
336:    *
337:    * @param IfNode|ElseIfNode|ForNode|ForeachNode|SwitchNode|DoWhileNode|WhileNode $node
338:    */
339:   protected function handleParens($node) {
340:     $open_paren = $node->getOpenParen();
341:     $this->removeSpaceAfter($open_paren);
342:     $this->spaceBefore($open_paren);
343:     $close_paren = $node->getCloseParen();
344:     $this->removeSpaceBefore($close_paren);
345:   }
346: 
347:   /**
348:    * Generic formatting rules for control structures.
349:    *
350:    * @param IfNode|ForNode|ForeachNode|SwitchNode|DoWhileNode|WhileNode $node
351:    */
352:   protected function handleControlStructure($node) {
353:     $this->handleParens($node);
354:     $colons = $node->children(Filter::isTokenType(':'));
355:     foreach ($colons as $colon) {
356:       $this->removeSpaceBefore($colon);
357:     }
358:     if ($colons->isNotEmpty()) {
359:       $this->newlineBefore($node->lastChild()->previous());
360:     }
361:   }
362: 
363:   public function visitIfNode(IfNode $node) {
364:     $this->encloseBlock($node->getThen());
365:     $this->encloseBlock($node->getElse());
366:   }
367: 
368:   public function endIfNode(IfNode $node) {
369:     $this->handleControlStructure($node);
370:     if ($node->getElse()) {
371:       $elseKeyword = $node->getElseKeyword();
372:       $else_newline = $this->config['else_newline'];
373:       if ($node->isAlterativeSyntax() || $else_newline) {
374:         $this->newlineBefore($elseKeyword);
375:       }
376:       else {
377:         $this->spaceBefore($elseKeyword);
378:       }
379:     }
380:   }
381: 
382:   public function visitElseIfNode(ElseIfNode $node) {
383:     $this->handleParens($node);
384:     $this->encloseBlock($node->getThen());
385:     $else_newline = $this->config['else_newline'];
386:     if ($node->getOpenColon() || $else_newline) {
387:       $this->newlineBefore($node, TRUE);
388:     }
389:     else {
390:       $this->spaceBefore($node);
391:     }
392:     if ($colon = $node->getOpenColon()) {
393:       $this->removeSpaceBefore($colon);
394:     }
395:   }
396: 
397:   public function visitWhileNode(WhileNode $node) {
398:     $this->encloseBlock($node->getBody());
399:   }
400: 
401:   public function endWhileNode(WhileNode $node) {
402:     $this->handleControlStructure($node);
403:   }
404: 
405:   public function visitDoWhileNode(DoWhileNode $node) {
406:     $this->handleParens($node);
407:     $this->encloseBlock($node->getBody());
408:     $this->spaceBefore($node->getWhileKeyword());
409:   }
410: 
411:   public function visitForNode(ForNode $node) {
412:     $this->encloseBlock($node->getBody());
413:     foreach ($node->children(Filter::isTokenType(';')) as $semicolon) {
414:       $this->removeSpaceBefore($semicolon);
415:       $this->spaceAfter($semicolon);
416:     }
417:   }
418: 
419:   public function endForNode(ForNode $node) {
420:     $this->handleControlStructure($node);
421:   }
422: 
423:   public function visitForeachNode(ForeachNode $node) {
424:     $this->encloseBlock($node->getBody());
425:   }
426: 
427:   public function endForeachNode(ForeachNode $node) {
428:     $this->handleControlStructure($node);
429:   }
430: 
431:   public function endSwitchNode(SwitchNode $node) {
432:     $this->handleControlStructure($node);
433: 
434:     /** @var TokenNode $token */
435:     $token = $node->getSwitchOn()->nextUntil(Filter::isTokenType(':', '{'), TRUE)->last()->get(0);
436:     if ($token->getType() === ':') {
437:       $this->removeSpaceBefore($token);
438:     }
439:     else {
440:       $this->spaceBefore($token);
441:     }
442: 
443:     $last = $node->lastChild();
444:     if ($last instanceof TokenNode && $last->getType() === '}') {
445:       $this->newlineBefore($last);
446:     }
447:   }
448: 
449:   public function endCaseNode(CaseNode $node) {
450:     $this->newlineBefore($node);
451:   }
452: 
453:   public function endDefaultNode(DefaultNode $node) {
454:     $this->newlineBefore($node);
455:   }
456: 
457:   /**
458:    * Test if declaration_brace_newline setting applies to node.
459:    *
460:    * @param ParentNode $node
461:    *   Node to test.
462:    *
463:    * @return bool
464:    *   TRUE if declaration_brace_newline applies to node.
465:    */
466:   protected function isDeclaration(ParentNode $node) {
467:     return $node instanceof FunctionDeclarationNode ||
468:       $node instanceof SingleInheritanceNode ||
469:       $node instanceof InterfaceNode ||
470:       $node instanceof ClassMethodNode ||
471:       $node instanceof InterfaceMethodNode;
472:   }
473: 
474:   public function visitStatementBlockNode(StatementBlockNode $node) {
475:     $nested = FALSE;
476:     $first = $node->firstChild();
477:     if ($first instanceof TokenNode && $first->getType() === '{') {
478:       if ($node->parent() instanceof StatementBlockNode) {
479:         $this->indentLevel++;
480:         $nested = TRUE;
481:       }
482: 
483:       $brace_newline = $this->config['declaration_brace_newline'];
484:       if ($brace_newline && $this->isDeclaration($node->parent())) {
485:         $this->newlineBefore($node, TRUE);
486:       }
487:       else {
488:         $this->spaceBefore($node);
489:       }
490:       $this->newlineAfter($first);
491:     }
492:     $this->nodeData[$node] = $nested;
493: 
494:     foreach ($node->getStatements() as $statement) {
495:       $this->newlineBefore($statement);
496:     }
497:   }
498: 
499:   public function endStatementBlockNode(StatementBlockNode $node) {
500:     $nested = $this->nodeData[$node];
501:     unset($this->nodeData[$node]);
502:     if ($nested) {
503:       $this->indentLevel--;
504:     }
505:     $last = $node->lastChild();
506:     if ($last instanceof TokenNode && $last->getType() === '}') {
507:       $this->newlineBefore($last, TRUE);
508:     }
509:   }
510: 
511:   public function visitLineCommentBlockNode(LineCommentBlockNode $node) {
512:     if ($this->indentLevel > 0) {
513:       $indent = $this->getIndent();
514:       foreach ($node->children(Filter::isInstanceOf('\Pharborist\CommentNode'))->slice(1) as $line_comment) {
515:         $prev = $line_comment->previous();
516:         if ($prev instanceof WhitespaceNode) {
517:           $prev->setText($indent);
518:         }
519:         else {
520:           $line_comment->before(Token::whitespace($indent));
521:         }
522:       }
523:     }
524:     else {
525:       $node->children(Filter::isInstanceOf('\Pharborist\WhitespaceNode'))->remove();
526:     }
527:   }
528: 
529:   /**
530:    * Calculate the column start position of the node.
531:    *
532:    * @param Node $node
533:    *   Node to calculate column position for.
534:    *
535:    * @return int
536:    *   Column position.
537:    */
538:   protected function calculateColumnPosition(Node $node) {
539:     // Add tokens until have whitespace containing newline.
540:     $column_position = 1;
541:     $start_token = $node instanceof ParentNode ? $node->firstToken() : $node;
542:     $token = $start_token;
543:     while ($token = $token->previousToken()) {
544:       if ($token instanceof WhitespaceNode && $token->getNewlineCount() > 0) {
545:         $lines = explode($this->config['nl'], $token->getText());
546:         $last_line = end($lines);
547:         $column_position += strlen($last_line);
548:         break;
549:       }
550:       $column_position += strlen($token->getText());
551:     }
552:     return $column_position;
553:   }
554: 
555:   public function visitArrayNode(ArrayNode $node) {
556:     $nested = FALSE;
557:     if ($node->parents(Filter::isInstanceOf('\Pharborist\Types\ArrayNode'))->isNotEmpty()) {
558:       $this->indentLevel++;
559:       $nested = TRUE;
560:     }
561: 
562:     if ($this->config['force_array_new_style']) {
563:       $first = $node->firstChild();
564:       /** @var TokenNode $first */
565:       if ($first->getType() === T_ARRAY) {
566:         $open_paren = $first->nextUntil(Filter::isTokenType('('), TRUE)->last()->get(0);
567:         $open_paren->previousAll()->remove();
568:         $open_paren->replaceWith(Token::openBracket());
569:         $close_paren = $node->lastChild();
570:         $close_paren->replaceWith(Token::closeBracket());
571:       }
572:     }
573: 
574:     // Remove space after T_ARRAY.
575:     $first = $node->firstChild();
576:     /** @var TokenNode $first */
577:     if ($first->getType() === T_ARRAY) {
578:       $this->removeSpaceAfter($first);
579:     }
580: 
581:     // Remove whitespace before first element.
582:     $this->removeSpaceBefore($node->getElementList());
583: 
584:     // Remove whitespace after last element.
585:     $this->removeSpaceAfter($node->getElementList());
586: 
587:     // Remove trailing comma.
588:     $last = $node->getElementList()->lastChild();
589:     if ($last instanceof TokenNode && $last->getType() === ',') {
590:       $last->remove();
591:     }
592: 
593:     $this->nodeData[$node] = $nested;
594:   }
595: 
596:   public function endArrayNode(ArrayNode $node) {
597:     $nested = $this->nodeData[$node];
598:     unset($this->nodeData[$node]);
599: 
600:     if ($nested) {
601:       $this->indentLevel--;
602:     }
603: 
604:     $is_wrapped = $node->getElementList()->children(Filter::isNewline())->isNotEmpty();
605:     if ($is_wrapped) {
606:       // Enforce trailing comma after last element.
607:       $node->getElementList()->append(Token::comma());
608: 
609:       // Newline before closing ) or ].
610:       $this->newlineBefore($node->lastChild(), !$nested);
611:     }
612:   }
613: 
614:   public function visitCommaListNode(CommaListNode $node) {
615:     if ($node->isEmpty()) {
616:       return;
617:     }
618:     $keep_wrap = $this->config['list_keep_wrap'];
619:     if (!$keep_wrap) {
620:       $keep_wrap = $node->parent() instanceof ArrayNode;
621:     }
622:     if ($keep_wrap) {
623:       $has_wrap = $node->children(Filter::isNewline())->isNotEmpty();
624:       $this->nodeData[$node] = $has_wrap;
625:     }
626:     foreach ($node->children(Filter::isTokenType(',')) as $comma_node) {
627:       $this->removeSpaceBefore($comma_node);
628:       $this->spaceAfter($comma_node);
629:     }
630:   }
631: 
632:   public function endCommaListNode(CommaListNode $node) {
633:     if ($node->isEmpty()) {
634:       return;
635:     }
636:     $keep_wrap = $this->config['list_keep_wrap'];
637:     $wrap_if_long = $this->config['list_wrap_if_long'];
638:     if ($node->parent() instanceof ArrayNode) {
639:       $keep_wrap = TRUE;
640:       $wrap_if_long = TRUE;
641:     }
642:     $wrap_list = FALSE;
643:     if ($keep_wrap) {
644:       $wrap_list = $this->nodeData[$node];
645:       unset($this->nodeData[$node]);
646:     }
647:     if (!$wrap_list && $wrap_if_long) {
648:       $column_position = $this->calculateColumnPosition($node);
649:       $column_position += strlen($node->getText());
650:       $soft_limit = $this->config['soft_limit'];
651:       $wrap_list = $column_position > $soft_limit;
652:     }
653:     if ($wrap_list) {
654:       $this->newlineBefore($node);
655:       foreach ($node->children(Filter::isTokenType(',')) as $comma_node) {
656:         $this->newlineAfter($comma_node);
657:       }
658:       $this->newlineAfter($node, TRUE);
659:     }
660:   }
661: 
662:   /**
663:    * @param FunctionDeclarationNode|ClassMethodNode|InterfaceMethodNode $node
664:    */
665:   protected function handleParameters($node) {
666:     $parameter_list = $node->getParameterList();
667:     $this->removeSpaceBefore($parameter_list);
668:     $this->removeSpaceAfter($parameter_list);
669:     $this->removeSpaceBefore($node->getOpenParen());
670:   }
671: 
672:   public function visitFunctionDeclarationNode(FunctionDeclarationNode $node) {
673:     $this->handleParameters($node);
674:   }
675: 
676:   /**
677:    * @param FunctionDeclarationNode|ClassMethodNode|InterfaceMethodNode $node
678:    */
679:   protected function handleParameterWrapping($node) {
680:     $parameter_list = $node->getParameterList();
681:     $parameter_wrapped = $parameter_list->children(Filter::isNewline())->isNotEmpty();
682:     if ($parameter_wrapped) {
683:       $this->newlineAfter($parameter_list);
684:       if (!($node instanceof InterfaceMethodNode) && $node->getBody()) {
685:         $this->spaceBefore($node->getBody());
686:       }
687:     }
688:   }
689: 
690:   public function endFunctionDeclarationNode(FunctionDeclarationNode $node) {
691:     $this->handleParameterWrapping($node);
692:   }
693: 
694:   public function visitParameterNode(ParameterNode $node) {
695:     if ($node->getValue()) {
696:       $assign = $node->getValue()->previousUntil(Filter::isTokenType('='), TRUE)->get(0);
697:       $this->spaceBefore($assign);
698:       $this->spaceAfter($assign);
699:     }
700:   }
701: 
702:   public function visitCallNode(CallNode $node) {
703:     $arg_list = $node->getArgumentList();
704:     $this->removeSpaceBefore($arg_list);
705:     $this->removeSpaceAfter($arg_list);
706:     $this->removeSpaceBefore($node->getOpenParen());
707:   }
708: 
709:   /**
710:    * @param SingleInheritanceNode|InterfaceNode $node
711:    */
712:   protected function endClassTraitOrInterface($node) {
713:     $nl = $this->config['nl'];
714:     $indent = str_repeat(' ', $this->config['indent']);
715:     $indent = str_repeat($indent, $this->indentLevel + 1);
716:     /** @var WhitespaceNode $ws_node */
717:     $whitespace = $node->getBody()->children(Filter::isInstanceOf('\Pharborist\WhitespaceNode'));
718:     foreach ($whitespace->slice(1, -1) as $ws_node) {
719:       // Blank line between methods and properties.
720:       $ws_node->setText(str_repeat($nl, 2) . $indent);
721:     }
722: 
723:     if ($whitespace->count() === 1) {
724:       return;
725:     }
726: 
727:     $blank_lines_around_class_body = $this->config['blank_lines_around_class_body'];
728:     $nl_count = $blank_lines_around_class_body + 1;
729: 
730:     /** @var WhitespaceNode $open_whitespace */
731:     $open_whitespace = $whitespace->get(0);
732:     $open_whitespace->setText(str_repeat($nl, $nl_count) . $indent);
733: 
734:     /** @var WhitespaceNode $close_whitespace */
735:     $close_whitespace = $whitespace->last()->get(0);
736:     $indent = str_repeat($indent, $this->indentLevel);
737:     $close_whitespace->setText(str_repeat($nl, $nl_count) . $indent);
738:   }
739: 
740:   public function endSingleInheritanceNode(SingleInheritanceNode $node) {
741:     $this->endClassTraitOrInterface($node);
742:   }
743: 
744:   /**
745:    * @param ClassMethodNode|InterfaceMethodNode $node
746:    */
747:   protected function visitMethod($node) {
748:     $this->handleParameters($node);
749: 
750:     if ($node->getVisibility() === NULL) {
751:       $node->setVisibility('public');
752:     }
753:     if ($node->getStatic()) {
754:       /** @var TokenNode $next */
755:       $next = $node->getStatic()->nextUntil(Filter::isNotHidden(), TRUE)->last()->get(0);
756:       if ($next->getType() !== T_FUNCTION) {
757:         $node->getStatic()->swapWith($node->getVisibility());
758:       }
759:     }
760:   }
761: 
762:   public function visitClassMethodNode(ClassMethodNode $node) {
763:     if ($node->getBody()) {
764:       $close_brace = $node->getBody()->lastChild();
765:       $this->newlineBefore($close_brace);
766:     }
767:     $this->visitMethod($node);
768:     if ($node->getAbstract() && $node->firstChild() !== $node->getAbstract()) {
769:       $node->getAbstract()->swapWith($node->getVisibility());
770:     }
771:     if ($node->getFinal() && $node->firstChild() !== $node->getFinal()) {
772:       $node->getFinal()->swapWith($node->getVisibility());
773:     }
774:   }
775: 
776:   public function endClassMethodNode(ClassMethodNode $node) {
777:     $this->handleParameterWrapping($node);
778:   }
779: 
780:   public function endInterfaceNode(InterfaceNode $node) {
781:     $this->endClassTraitOrInterface($node);
782:   }
783: 
784:   public function visitInterfaceMethodNode(InterfaceMethodNode $node) {
785:     $this->visitMethod($node);
786:   }
787: 
788:   public function endInterfaceMethodNode(InterfaceMethodNode $node) {
789:     $this->handleParameterWrapping($node);
790:   }
791: 
792:   public function endCatchNode(CatchNode $node) {
793:     $this->handleParens($node);
794:     $this->newlineBefore($node, TRUE);
795:   }
796: 
797:   public function visitNewNode(NewNode $node) {
798:     if (!$node->getArgumentList()) {
799:       $node->append(Token::openParen());
800:       $node->addChild(new CommaListNode(), 'arguments');
801:       $node->append(Token::closeParen());
802:     }
803:   }
804: 
805:   public function visitObjectMethodCallNode(ObjectMethodCallNode $node) {
806:     $this->removeSpaceAfter($node->getOperator());
807:   }
808: 
809:   public function endRootNode(RootNode $node) {
810:     /** @var $open_tag TokenNode */
811:     foreach ($node->children(Filter::isTokenType(T_OPEN_TAG)) as $open_tag) {
812:       $this->removeSpaceAfter($open_tag);
813:       if ($open_tag !== "<?php\n") {
814:         $open_tag->setText("<?php\n");
815:       }
816:     }
817:   }
818: 
819:   public function visitNamespaceNode(NamespaceNode $node) {
820:     $first = $node->getBody()->firstToken();
821:     $has_braces = $first->getType() === '{';
822:     $this->indentLevel = $has_braces ? 1 : 0;
823:     if (!$has_braces) {
824:       foreach ($node->children(Filter::isTokenType(';')) as $semicolon) {
825:         $next = $semicolon->next();
826:         $newlines = str_repeat($this->config['nl'], 2);
827:         if ($next instanceof WhitespaceNode) {
828:           $next->setText($newlines);
829:         }
830:         else {
831:           $semicolon->after(Token::whitespace($newlines));
832:         }
833:       }
834:     }
835:   }
836: 
837:   public function endNamespaceNode(NamespaceNode $node) {
838:     $this->indentLevel = 0;
839:   }
840: }
841: 
Pharborist API documentation generated by ApiGen