currentNode = $root; $this->filter = $filter; $this->root = $root; $this->whatToShow = $whatToShow; } /** * See https://dom.spec.whatwg.org/#dom-treewalker-nextnode * * @return DOMNode|null The current node */ public function nextNode() : ?DOMNode { $node = $this->currentNode; $result = NodeFilter::FILTER_ACCEPT; while ( true ) { while ( $result !== NodeFilter::FILTER_REJECT && $node->firstChild !== null ) { $node = $node->firstChild; $result = $this->filterNode( $node ); if ( $result === NodeFilter::FILTER_ACCEPT ) { $this->currentNode = $node; return $node; } } $sibling = null; $temp = $node; while ( $temp !== null ) { if ( $temp === $this->root ) { return null; } $sibling = $temp->nextSibling; if ( $sibling !== null ) { $node = $sibling; break; } $temp = $temp->parentNode; } $result = $this->filterNode( $node ); if ( $result === NodeFilter::FILTER_ACCEPT ) { $this->currentNode = $node; return $node; } } } /** * Filters a node. * * @internal * * @see https://dom.spec.whatwg.org/#concept-node-filter * * @param DOMNode $node The node to check. * @return int Returns one of NodeFilter's FILTER_* constants. * - NodeFilter::FILTER_ACCEPT * - NodeFilter::FILTER_REJECT * - NodeFilter::FILTER_SKIP * @throws Exception */ private function filterNode( DOMNode $node ): int { if ( $this->isActive ) { throw new Exception( 'InvalidStateError' ); } // Let n be node’s nodeType attribute value minus 1. $n = $node->nodeType - 1; // If the nth bit (where 0 is the least significant bit) of whatToShow // is not set, return FILTER_SKIP. if ( !( ( 1 << $n ) & $this->whatToShow ) ) { return NodeFilter::FILTER_SKIP; } // If filter is null, return FILTER_ACCEPT. if ( !$this->filter ) { return NodeFilter::FILTER_ACCEPT; } $this->isActive = true; try { // Let $result be the return value of call a user object's operation // with traverser's filter, "acceptNode", and Node. If this throws // an exception, then unset traverser's active flag and rethrow the // exception. $result = $this->filter instanceof NodeFilter ? $this->filter->acceptNode( $node ) : ( $this->filter )( $node ); } catch ( Throwable $e ) { $this->isActive = false; throw $e; } $this->isActive = false; return $result; } }