mediawiki-extensions-Discus.../includes/TreeWalker.php
Bartosz Dziewoński af68c835bb Update exception handling for new code conventions
Change code to match the documented consensus formed on T321683:
https://www.mediawiki.org/wiki/Manual:Coding_conventions/PHP#Exception_handling

* Do not directly throw Exception, Error or MWException
* Document checked exceptions with @throws
* Do not document unchecked exceptions

For this extension, I think it makes sense to consider DOMException an
unchecked exception too (in addition to the usual LogicException and
RuntimeException).

Depends-On: Id07e301c3f20afa135e5469ee234a27354485652
Depends-On: I869af06896b9757af18488b916211c5a41a8c563
Depends-On: I42d9b7465d1406a22ef1b3f6d8de426c60c90e2c
Change-Id: Ic9d9efd031a87fa5a93143f714f0adb20f0dd956
2023-01-22 18:17:11 +00:00

146 lines
3.1 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace MediaWiki\Extension\DiscussionTools;
use DOMException;
use Throwable;
use Wikimedia\Parsoid\DOM\Node;
/**
* Partial implementation of W3 DOM4 TreeWalker interface.
*
* See also:
* - https://dom.spec.whatwg.org/#interface-treewalker
*
* Ported from https://github.com/TRowbotham/PHPDOM (MIT)
*/
class TreeWalker {
public $root;
public $whatToShow;
public $currentNode;
public $filter;
private $isActive = false;
/**
* See https://dom.spec.whatwg.org/#interface-treewalker
*
* @param Node $root
* @param int $whatToShow
* @param callable|null $filter
*/
public function __construct(
Node $root,
int $whatToShow = NodeFilter::SHOW_ALL,
callable $filter = null
) {
$this->currentNode = $root;
$this->filter = $filter;
$this->root = $root;
$this->whatToShow = $whatToShow;
}
/**
* See https://dom.spec.whatwg.org/#dom-treewalker-nextnode
*
* @return Node|null The current node
*/
public function nextNode(): ?Node {
$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;
}
'@phan-var Node $node';
$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 Node $node The node to check.
* @return int Returns one of NodeFilter's FILTER_* constants.
* - NodeFilter::FILTER_ACCEPT
* - NodeFilter::FILTER_REJECT
* - NodeFilter::FILTER_SKIP
*/
private function filterNode( Node $node ): int {
if ( $this->isActive ) {
throw new DOMException( 'InvalidStateError' );
}
// Let n be nodes 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;
}
}