1<?php 2 3declare(strict_types=1); 4 5/* 6 * This file is part of the league/commonmark package. 7 * 8 * (c) Colin O'Dell <colinodell@gmail.com> 9 * 10 * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 * - (c) John MacFarlane 12 * 13 * For the full copyright and license information, please view the LICENSE 14 * file that was distributed with this source code. 15 */ 16 17namespace League\CommonMark\Node; 18 19use League\CommonMark\Node\Block\AbstractBlock; 20 21final class NodeWalker 22{ 23 /** @psalm-readonly */ 24 private Node $root; 25 26 /** @psalm-readonly-allow-private-mutation */ 27 private ?Node $current = null; 28 29 /** @psalm-readonly-allow-private-mutation */ 30 private bool $entering; 31 32 public function __construct(Node $root) 33 { 34 $this->root = $root; 35 $this->current = $this->root; 36 $this->entering = true; 37 } 38 39 /** 40 * Returns an event which contains node and entering flag 41 * (entering is true when we enter a Node from a parent or sibling, 42 * and false when we reenter it from child) 43 */ 44 public function next(): ?NodeWalkerEvent 45 { 46 $current = $this->current; 47 $entering = $this->entering; 48 if ($current === null) { 49 return null; 50 } 51 52 if ($entering && ($current instanceof AbstractBlock || $current->hasChildren())) { 53 if ($current->firstChild()) { 54 $this->current = $current->firstChild(); 55 $this->entering = true; 56 } else { 57 $this->entering = false; 58 } 59 } elseif ($current === $this->root) { 60 $this->current = null; 61 } elseif ($current->next() === null) { 62 $this->current = $current->parent(); 63 $this->entering = false; 64 } else { 65 $this->current = $current->next(); 66 $this->entering = true; 67 } 68 69 return new NodeWalkerEvent($current, $entering); 70 } 71 72 /** 73 * Resets the iterator to resume at the specified node 74 */ 75 public function resumeAt(Node $node, bool $entering = true): void 76 { 77 $this->current = $node; 78 $this->entering = $entering; 79 } 80} 81