Dustin Wilson
2 years ago
14 changed files with 685 additions and 4 deletions
@ -0,0 +1,136 @@ |
|||
<?php |
|||
/** |
|||
* @license MIT |
|||
* Copyright 2017 Dustin Wilson, J. King, et al. |
|||
* See LICENSE and AUTHORS files for details |
|||
*/ |
|||
|
|||
declare(strict_types=1); |
|||
namespace MensBeam\HTML\DOM; |
|||
use MensBeam\HTML\DOM\Inner\Reflection; |
|||
|
|||
|
|||
trait XPathEvaluate { |
|||
protected function xpathEvaluate(string $expression, Node $contextNode, int $type = XPathResult::ANY_TYPE, ?XPathResult $result = null): XPathResult { |
|||
$innerContextNode = Reflection::getProtectedProperty($contextNode, 'innerNode'); |
|||
$doc = ($innerContextNode instanceof \DOMDocument) ? $innerContextNode : $innerContextNode->ownerDocument; |
|||
|
|||
set_error_handler(function(int $errno, string $errstr, string $errfile, int $errline) { |
|||
$lowerErrstr = strtolower($errstr); |
|||
|
|||
if (str_contains(needle: 'invalid expression', haystack: $lowerErrstr)) { |
|||
throw new XPathException(XPathException::INVALID_EXPRESSION); |
|||
} |
|||
|
|||
if (str_contains(needle: 'undefined namespace prefix', haystack: $lowerErrstr)) { |
|||
throw new XPathException(XPathException::UNDEFINED_NAMESPACE_PREFIX); |
|||
} |
|||
}); |
|||
$result = $doc->xpath->evaluate($expression, $innerContextNode); |
|||
restore_error_handler(); |
|||
|
|||
if ($type === XPathResult::ANY_TYPE) { |
|||
$typeOfResult = gettype($result); |
|||
if ($typeOfResult === 'object') { |
|||
$typeOfResult = $result::class; |
|||
} |
|||
|
|||
switch ($typeOfResult) { |
|||
case 'integer': |
|||
case 'double': |
|||
$resultType = XPathResult::NUMBER_TYPE; |
|||
break; |
|||
case 'string': |
|||
$resultType = XPathResult::STRING_TYPE; |
|||
break; |
|||
case 'boolean': |
|||
$resultType = XPathResult::BOOLEAN_TYPE; |
|||
break; |
|||
case 'DOMNodeList': |
|||
$resultType = XPathResult::ORDERED_NODE_ITERATOR_TYPE; |
|||
break; |
|||
default: |
|||
throw new DOMException(DOMException::NOT_SUPPORTED); |
|||
} |
|||
} else { |
|||
switch ($type) { |
|||
case XPathResult::NUMBER_TYPE: |
|||
if ($result instanceof \DOMNodeList) { |
|||
throw new XPathException(XPathException::TYPE_ERROR); |
|||
} |
|||
|
|||
$result = (float)$result; |
|||
break; |
|||
|
|||
case XPathResult::STRING_TYPE: |
|||
if ($result instanceof \DOMNodeList) { |
|||
throw new XPathException(XPathException::TYPE_ERROR); |
|||
} |
|||
|
|||
$result = (string)$result; |
|||
break; |
|||
|
|||
case XPathResult::BOOLEAN_TYPE: |
|||
if ($result instanceof \DOMNodeList) { |
|||
$result = ($result->length > 0); |
|||
} |
|||
|
|||
$result = (bool)$result; |
|||
break; |
|||
|
|||
// In this implementation there's no difference between these because PHP's |
|||
// XPath DOM (ALMOST!) always returns in document order, and that cannot be |
|||
// changed. |
|||
case XPathResult::UNORDERED_NODE_ITERATOR_TYPE: |
|||
case XPathResult::ORDERED_NODE_ITERATOR_TYPE: |
|||
if (!$result instanceof \DOMNodeList) { |
|||
throw new XPathException(XPathException::TYPE_ERROR); |
|||
} |
|||
break; |
|||
|
|||
// In this implementation there's no difference between these because PHP's |
|||
// XPath DOM (ALMOST!) always returns in document order, and that cannot be |
|||
// changed. |
|||
case XPathResult::UNORDERED_NODE_SNAPSHOT_TYPE: |
|||
case XPathResult::ORDERED_NODE_SNAPSHOT_TYPE: |
|||
if (!$result instanceof \DOMNodeList) { |
|||
throw new XPathException(XPathException::TYPE_ERROR); |
|||
} |
|||
|
|||
$temp = []; |
|||
foreach ($result as $node) { |
|||
$temp[] = $node; |
|||
} |
|||
$result = $temp; |
|||
break; |
|||
|
|||
// In this implementation there's no difference between these because PHP's |
|||
// XPath DOM (ALMOST!) always returns in document order, and that cannot be |
|||
// changed. |
|||
case XPathResult::ANY_UNORDERED_NODE_TYPE: |
|||
case XPathResult::FIRST_ORDERED_NODE_TYPE: |
|||
if (!$result instanceof \DOMNodeList) { |
|||
throw new XPathException(XPathException::TYPE_ERROR); |
|||
} |
|||
|
|||
$result = $result->item(0); |
|||
break; |
|||
|
|||
default: throw new DOMException(DOMException::NOT_SUPPORTED); |
|||
} |
|||
|
|||
$resultType = $type; |
|||
} |
|||
|
|||
// XPathResult cannot be created from their constructors normally. |
|||
return Reflection::createFromProtectedConstructor(__NAMESPACE__ . '\\XPathResult', $resultType, ($result instanceof \DOMNodeList || is_array($result)) ? $result : [ $result ]); |
|||
} |
|||
|
|||
protected function xpathRegisterPhpFunctions(Document $document, string|array|null $restrict = null): void { |
|||
Reflection::getProtectedProperty($document, 'innerNode')->xpath->registerPhpFunctions($restrict); |
|||
} |
|||
|
|||
protected function xpathRegisterNamespace(Document $document, string $prefix, string $namespace): bool { |
|||
return Reflection::getProtectedProperty($document, 'innerNode')->xpath->registerNamespace($prefix, $namespace); |
|||
} |
|||
} |
@ -0,0 +1,23 @@ |
|||
<?php |
|||
/** |
|||
* @license MIT |
|||
* Copyright 2017 Dustin Wilson, J. King, et al. |
|||
* See LICENSE and AUTHORS files for details |
|||
*/ |
|||
|
|||
declare(strict_types=1); |
|||
namespace MensBeam\HTML\DOM; |
|||
|
|||
|
|||
class XPathEvaluator { |
|||
use XPathEvaluatorBase; |
|||
|
|||
|
|||
public function registerXPathFunctions(Document $document, string|array|null $restrict = null): void { |
|||
$this->xpathRegisterPhpFunctions($document, $restrict); |
|||
} |
|||
|
|||
public function registerXPathNamespace(Document $document, string $prefix, string $namespace): bool { |
|||
return $this->xpathRegisterNamespace($document, $prefix, $namespace); |
|||
} |
|||
} |
@ -0,0 +1,25 @@ |
|||
<?php |
|||
/** |
|||
* @license MIT |
|||
* Copyright 2017 Dustin Wilson, J. King, et al. |
|||
* See LICENSE and AUTHORS files for details |
|||
*/ |
|||
|
|||
declare(strict_types=1); |
|||
namespace MensBeam\HTML\DOM; |
|||
use MensBeam\HTML\DOM\Inner\Reflection; |
|||
|
|||
|
|||
trait XPathEvaluatorBase { |
|||
use XPathEvaluate; |
|||
|
|||
|
|||
public function createExpression(string $expression, ?XPathNSResolver $resolver = null): XPathExpression { |
|||
// XPathExpression cannot be created from their constructors normally. |
|||
return Reflection::createFromProtectedConstructor(__NAMESPACE__ . '\\XPathExpression', $expression); |
|||
} |
|||
|
|||
public function evaluate(string $expression, Node $contextNode, ?XPathNSResolver $resolver = null, int $type = XPathResult::ANY_TYPE, ?XPathResult $result = null): XPathResult { |
|||
return $this->xpathEvaluate($expression, $contextNode, $type, $result); |
|||
} |
|||
} |
@ -0,0 +1,27 @@ |
|||
<?php |
|||
/** |
|||
* @license MIT |
|||
* Copyright 2017 Dustin Wilson, J. King, et al. |
|||
* See LICENSE and AUTHORS files for details |
|||
*/ |
|||
|
|||
declare(strict_types=1); |
|||
namespace MensBeam\HTML\DOM; |
|||
use MensBeam\Framework\Exception; |
|||
|
|||
|
|||
class XPathException extends Exception { |
|||
public const INVALID_EXPRESSION = 51; |
|||
public const TYPE_ERROR = 52; |
|||
public const UNDEFINED_NAMESPACE_PREFIX = 53; |
|||
|
|||
public function __construct(int $code, ...$args) { |
|||
self::$messages = array_replace(parent::$messages, [ |
|||
51 => 'Invalid expression error', |
|||
52 => 'Expression cannot be converted to the specified type', |
|||
53 => 'Undefined namespace prefix' |
|||
]); |
|||
|
|||
parent::__construct($code, ...$args); |
|||
} |
|||
} |
@ -0,0 +1,46 @@ |
|||
<?php |
|||
/** |
|||
* @license MIT |
|||
* Copyright 2017 Dustin Wilson, J. King, et al. |
|||
* See LICENSE and AUTHORS files for details |
|||
*/ |
|||
|
|||
declare(strict_types=1); |
|||
namespace MensBeam\HTML\DOM; |
|||
|
|||
|
|||
class XPathExpression { |
|||
use XPathEvaluate; |
|||
|
|||
protected string $expression; |
|||
|
|||
|
|||
protected function __construct(string $expression) { |
|||
// Test the expression by attempting to run it on an empty document. PHP's DOM |
|||
// XPath incorrectly issues a warning on an invalid expression rather than an |
|||
// exception, so we must use a custom error handler here to "catch" it and throw |
|||
// an exception in its place. |
|||
set_error_handler(function(int $errno, string $errstr, string $errfile, int $errline) { |
|||
$lowerErrstr = strtolower($errstr); |
|||
|
|||
if (str_contains(needle: 'invalid expression', haystack: $lowerErrstr)) { |
|||
throw new XPathException(XPathException::INVALID_EXPRESSION); |
|||
} |
|||
|
|||
// Ignore undefined namespace prefix warnings here because there's no way to |
|||
// register namespace prefixes before the expression is created. |
|||
}); |
|||
|
|||
$xpath = new \DOMXPath(new \DOMDocument()); |
|||
$xpath->evaluate($expression); |
|||
|
|||
restore_error_handler(); |
|||
|
|||
$this->expression = $expression; |
|||
} |
|||
|
|||
|
|||
protected function evaluate(Node $contextNode, int $type = XPathResult::ANY_TYPE, ?XPathResult $result = null): XPathResult { |
|||
return $this->xpathEvaluate($this->expression, $contextNode, $type, $result); |
|||
} |
|||
} |
@ -0,0 +1,12 @@ |
|||
<?php |
|||
/** |
|||
* @license MIT |
|||
* Copyright 2017 Dustin Wilson, J. King, et al. |
|||
* See LICENSE and AUTHORS files for details |
|||
*/ |
|||
|
|||
declare(strict_types=1); |
|||
namespace MensBeam\HTML\DOM; |
|||
|
|||
|
|||
interface XPathNSResolver {} |
@ -0,0 +1,168 @@ |
|||
<?php |
|||
/** |
|||
* @license MIT |
|||
* Copyright 2017 Dustin Wilson, J. King, et al. |
|||
* See LICENSE and AUTHORS files for details |
|||
*/ |
|||
|
|||
declare(strict_types=1); |
|||
namespace MensBeam\HTML\DOM; |
|||
use MensBeam\Framework\MagicProperties, |
|||
MensBeam\HTML\DOM\Inner\Reflection; |
|||
|
|||
|
|||
class XPathResult implements \ArrayAccess, \Countable, \Iterator { |
|||
use MagicProperties; |
|||
|
|||
public const ANY_TYPE = 0; |
|||
public const NUMBER_TYPE = 1; |
|||
public const STRING_TYPE = 2; |
|||
public const BOOLEAN_TYPE = 3; |
|||
public const UNORDERED_NODE_ITERATOR_TYPE = 4; |
|||
public const ORDERED_NODE_ITERATOR_TYPE = 5; |
|||
public const UNORDERED_NODE_SNAPSHOT_TYPE = 6; |
|||
public const ORDERED_NODE_SNAPSHOT_TYPE = 7; |
|||
public const ANY_UNORDERED_NODE_TYPE = 8; |
|||
public const FIRST_ORDERED_NODE_TYPE = 9; |
|||
|
|||
protected bool $_invalidIteratorState = false; |
|||
protected int $position = 0; |
|||
protected int $_resultType; |
|||
protected \DOMNodeList|array $storage; |
|||
|
|||
protected function __get_booleanValue(): bool { |
|||
if ($this->_resultType !== self::BOOLEAN_TYPE) { |
|||
throw new XPathException(XPathException::TYPE_ERROR); |
|||
} |
|||
|
|||
return $this->storage[0]; |
|||
} |
|||
|
|||
protected function __get_invalidIteratorState(): bool { |
|||
return $this->_invalidIteratorState; |
|||
} |
|||
|
|||
protected function __get_numberValue(): float { |
|||
if ($this->_resultType !== self::NUMBER_TYPE) { |
|||
throw new XPathException(XPathException::TYPE_ERROR); |
|||
} |
|||
|
|||
return $this->storage[0]; |
|||
} |
|||
|
|||
protected function __get_resultType(): int { |
|||
return $this->_resultType; |
|||
} |
|||
|
|||
protected function __get_singleNodeValue(): Node { |
|||
if (!in_array($this->_resultType, [ self::ANY_UNORDERED_NODE_TYPE, self::FIRST_ORDERED_NODE_TYPE ])) { |
|||
throw new XPathException(XPathException::TYPE_ERROR); |
|||
} |
|||
|
|||
$node = $this->storage[0]; |
|||
return $node->ownerDocument->getWrapperNode($node); |
|||
} |
|||
|
|||
protected function __get_snapshotLength(): bool { |
|||
if (!in_array($this->_resultType, [ self::ORDERED_NODE_SNAPSHOT_TYPE, self::UNORDERED_NODE_SNAPSHOT_TYPE ])) { |
|||
throw new XPathException(XPathException::TYPE_ERROR); |
|||
} |
|||
|
|||
return $this->count; |
|||
} |
|||
|
|||
protected function __get_stringValue(): string { |
|||
if ($this->_resultType !== self::STRING_TYPE) { |
|||
throw new XPathException(XPathException::TYPE_ERROR); |
|||
} |
|||
|
|||
return $this->storage[0]; |
|||
} |
|||
|
|||
|
|||
protected function __construct(int $type, \DOMNodeList|array $object) { |
|||
$this->storage = $object; |
|||
$this->_resultType = $type; |
|||
} |
|||
|
|||
|
|||
public function count(): int { |
|||
$this->validateStorage(); |
|||
return (is_array($this->storage)) ? count($this->storage) : $this->storage->length; |
|||
} |
|||
|
|||
public function current(): ?Node { |
|||
$this->validateStorage(); |
|||
$node = $this->storage[$this->position]; |
|||
return $node->ownerDocument->getWrapperNode($node); |
|||
} |
|||
|
|||
public function iterateNext(): ?Node { |
|||
if (!in_array($this->_resultType, [ self::ORDERED_NODE_ITERATOR_TYPE, self::UNORDERED_NODE_ITERATOR_TYPE ])) { |
|||
throw new XPathException(XPathException::TYPE_ERROR); |
|||
} |
|||
|
|||
$node = $this->storage[$this->position++]; |
|||
return $node->ownerDocument->getWrapperNode($node); |
|||
} |
|||
|
|||
public function key(): int { |
|||
$this->validateStorage(); |
|||
return $this->position; |
|||
} |
|||
|
|||
public function next(): void { |
|||
$this->validateStorage(); |
|||
$this->position++; |
|||
} |
|||
|
|||
public function rewind(): void { |
|||
$this->validateStorage(); |
|||
$this->position = 0; |
|||
} |
|||
|
|||
public function offsetExists($offset): bool { |
|||
$this->validateStorage(); |
|||
return isset($this->storage[$offset]); |
|||
} |
|||
|
|||
public function offsetGet($offset): ?Node { |
|||
$this->validateStorage(); |
|||
|
|||
$node = $this->storage[$this->position]; |
|||
return $node->ownerDocument->getWrapperNode($node); |
|||
} |
|||
|
|||
public function offsetSet($offset, $value): void { |
|||
$this->validateStorage(); |
|||
} |
|||
|
|||
public function offsetUnset($offset): void { |
|||
$this->validateStorage(); |
|||
} |
|||
|
|||
public function snapshotItem(int $index): ?Node { |
|||
if (!in_array($this->_resultType, [ self::ORDERED_NODE_SNAPSHOT_TYPE, self::UNORDERED_NODE_SNAPSHOT_TYPE ])) { |
|||
throw new XPathException(XPathException::TYPE_ERROR); |
|||
} |
|||
|
|||
if (!isset($this->storage[$index])) { |
|||
return null; |
|||
} |
|||
|
|||
$node = $this->storage[$index]; |
|||
return $node->ownerDocument->getWrapperNode($node); |
|||
} |
|||
|
|||
public function valid(): bool { |
|||
$this->validateStorage(); |
|||
return $this->offsetExists($this->position); |
|||
} |
|||
|
|||
|
|||
protected function validateStorage(): void { |
|||
if (!in_array($this->_resultType, [ self::ORDERED_NODE_ITERATOR_TYPE, self::UNORDERED_NODE_ITERATOR_TYPE, self::ORDERED_NODE_SNAPSHOT_TYPE, self::UNORDERED_NODE_SNAPSHOT_TYPE ])) { |
|||
throw new XPathException(XPathException::TYPE_ERROR); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,197 @@ |
|||
<?php |
|||
/** |
|||
* @license MIT |
|||
* Copyright 2017 Dustin Wilson, J. King, et al. |
|||
* See LICENSE and AUTHORS files for details |
|||
*/ |
|||
|
|||
declare(strict_types=1); |
|||
namespace MensBeam\HTML\DOM\TestCase; |
|||
|
|||
use MensBeam\HTML\DOM\{ |
|||
Document, |
|||
DOMException, |
|||
Node, |
|||
XPathException, |
|||
XPathResult |
|||
}; |
|||
|
|||
|
|||
/** @covers \MensBeam\HTML\DOM\XPathEvaluate */ |
|||
class TestXPathEvaluate extends \PHPUnit\Framework\TestCase { |
|||
/** |
|||
* @covers \MensBeam\HTML\DOM\XPathEvaluate::xpathEvaluate |
|||
* |
|||
* @covers \MensBeam\HTML\DOM\Document::__construct |
|||
* @covers \MensBeam\HTML\DOM\Document::__get_body |
|||
* @covers \MensBeam\HTML\DOM\Document::load |
|||
* @covers \MensBeam\HTML\DOM\Document::registerXPathFunctions |
|||
* @covers \MensBeam\HTML\DOM\DOMImplementation::__construct |
|||
* @covers \MensBeam\HTML\DOM\Element::__construct |
|||
* @covers \MensBeam\HTML\DOM\Node::__construct |
|||
* @covers \MensBeam\HTML\DOM\Node::getInnerDocument |
|||
* @covers \MensBeam\HTML\DOM\Node::hasChildNodes |
|||
* @covers \MensBeam\HTML\DOM\Node::postParsingTemplatesFix |
|||
* @covers \MensBeam\HTML\DOM\XPathEvaluate::xpathRegisterPhpFunctions |
|||
* @covers \MensBeam\HTML\DOM\XPathEvaluatorBase::evaluate |
|||
* @covers \MensBeam\HTML\DOM\XPathResult::__construct |
|||
* @covers \MensBeam\HTML\DOM\XPathResult::__get_booleanValue |
|||
* @covers \MensBeam\HTML\DOM\XPathResult::__get_numberValue |
|||
* @covers \MensBeam\HTML\DOM\XPathResult::__get_singleNodeValue |
|||
* @covers \MensBeam\HTML\DOM\XPathResult::__get_stringValue |
|||
* @covers \MensBeam\HTML\DOM\XPathResult::count |
|||
* @covers \MensBeam\HTML\DOM\XPathResult::validateStorage |
|||
* @covers \MensBeam\HTML\DOM\Inner\Document::__construct |
|||
* @covers \MensBeam\HTML\DOM\Inner\Document::__get_wrapperNode |
|||
* @covers \MensBeam\HTML\DOM\Inner\Document::__get_xpath |
|||
* @covers \MensBeam\HTML\DOM\Inner\Document::getWrapperNode |
|||
* @covers \MensBeam\HTML\DOM\Inner\NodeCache::get |
|||
* @covers \MensBeam\HTML\DOM\Inner\NodeCache::has |
|||
* @covers \MensBeam\HTML\DOM\Inner\NodeCache::key |
|||
* @covers \MensBeam\HTML\DOM\Inner\NodeCache::set |
|||
* @covers \MensBeam\HTML\DOM\Inner\Reflection::createFromProtectedConstructor |
|||
* @covers \MensBeam\HTML\DOM\Inner\Reflection::getProtectedProperty |
|||
*/ |
|||
function testMethod_xpathEvaluate(): void { |
|||
$d = new Document('<!DOCTYPE html><html><body><span><span>Ook</span></span><span></span></body></html>'); |
|||
$d->registerXPathFunctions(); |
|||
$result = $d->evaluate('.//span', $d->body); |
|||
$this->assertEquals(3, count($result)); |
|||
|
|||
$result = $d->evaluate('count(.//span)', $d->body, null, XPathResult::NUMBER_TYPE); |
|||
$this->assertEquals(3, $result->numberValue); |
|||
$result = $d->evaluate('count(.//span)', $d->body, null); |
|||
$this->assertEquals(3, $result->numberValue); |
|||
$result = $d->evaluate('name(.//span)', $d->body, null, XPathResult::STRING_TYPE); |
|||
$this->assertEquals('span', $result->stringValue); |
|||
$result = $d->evaluate('name(.//span)', $d->body, null); |
|||
$this->assertEquals('span', $result->stringValue); |
|||
$result = $d->evaluate('.//span', $d->body, null, XPathResult::BOOLEAN_TYPE); |
|||
$this->assertTrue($result->booleanValue); |
|||
$result = $d->evaluate('not(.//span)', $d->body, null, XPathResult::BOOLEAN_TYPE); |
|||
$this->assertFalse($result->booleanValue); |
|||
$result = $d->evaluate('not(.//span)', $d->body, null); |
|||
$this->assertFalse($result->booleanValue); |
|||
$result = $d->evaluate('.//span', $d->body, null, XPathResult::ORDERED_NODE_ITERATOR_TYPE); |
|||
$this->assertEquals(3, count($result)); |
|||
$result = $d->evaluate('.//span', $d->body, null); |
|||
$this->assertEquals(3, count($result)); |
|||
$result = $d->evaluate('.//span', $d->body, null, XPathResult::UNORDERED_NODE_SNAPSHOT_TYPE); |
|||
$this->assertEquals(3, count($result)); |
|||
$result = $d->evaluate('.//span', $d->body, null, XPathResult::FIRST_ORDERED_NODE_TYPE); |
|||
$this->assertSame($d->body->firstChild, $result->singleNodeValue); |
|||
} |
|||
|
|||
|
|||
function provideMethod_xpathEvaluate__errors(): iterable { |
|||
return [ |
|||
[ function() { |
|||
$d = new Document(); |
|||
$d->evaluate('fail?', $d); |
|||
}, |
|||
XPathException::class, |
|||
XPathException::INVALID_EXPRESSION ], |
|||
|
|||
[ function() { |
|||
$d = new Document(); |
|||
$d->evaluate('//fail', $d, null, XPathResult::NUMBER_TYPE); |
|||
}, |
|||
XPathException::class, |
|||
XPathException::TYPE_ERROR ], |
|||
|
|||
[ function() { |
|||
$d = new Document(); |
|||
$d->evaluate('//fail', $d, null, XPathResult::STRING_TYPE); |
|||
}, |
|||
XPathException::class, |
|||
XPathException::TYPE_ERROR ], |
|||
|
|||
[ function() { |
|||
$d = new Document(); |
|||
$d->evaluate('count(//fail)', $d, null, XPathResult::UNORDERED_NODE_ITERATOR_TYPE); |
|||
}, |
|||
XPathException::class, |
|||
XPathException::TYPE_ERROR ], |
|||
|
|||
[ function() { |
|||
$d = new Document(); |
|||
$d->evaluate('count(//fail)', $d, null, XPathResult::ORDERED_NODE_SNAPSHOT_TYPE); |
|||
}, |
|||
XPathException::class, |
|||
XPathException::TYPE_ERROR ], |
|||
|
|||
[ function() { |
|||
$d = new Document(); |
|||
$d->evaluate('count(//fail)', $d, null, XPathResult::ANY_UNORDERED_NODE_TYPE); |
|||
}, |
|||
XPathException::class, |
|||
XPathException::TYPE_ERROR ], |
|||
|
|||
[ function() { |
|||
$d = new Document(); |
|||
$d->evaluate('//svg:svg', $d, null); |
|||
}, |
|||
XPathException::class, |
|||
XPathException::UNDEFINED_NAMESPACE_PREFIX ], |
|||
|
|||
[ function() { |
|||
$d = new Document(); |
|||
$d->evaluate('count(//fail)', $d, null, 2112); |
|||
}, |
|||
DOMException::class, |
|||
DOMException::NOT_SUPPORTED ] |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider provideMethod_xpathEvaluate__errors |
|||
* @covers \MensBeam\HTML\DOM\XPathEvaluate::xpathEvaluate |
|||
* |
|||
* |
|||
* @covers \MensBeam\HTML\DOM\Document::__construct |
|||
* @covers \MensBeam\HTML\DOM\DOMImplementation::__construct |
|||
* @covers \MensBeam\HTML\DOM\Node::__construct |
|||
* @covers \MensBeam\HTML\DOM\XPathEvaluatorBase::evaluate |
|||
* @covers \MensBeam\HTML\DOM\XPathException::__construct |
|||
* @covers \MensBeam\HTML\DOM\Inner\Document::__construct |
|||
* @covers \MensBeam\HTML\DOM\Inner\Document::__get_xpath |
|||
* @covers \MensBeam\HTML\DOM\Inner\NodeCache::has |
|||
* @covers \MensBeam\HTML\DOM\Inner\NodeCache::key |
|||
* @covers \MensBeam\HTML\DOM\Inner\NodeCache::set |
|||
* @covers \MensBeam\HTML\DOM\Inner\Reflection::getProtectedProperty |
|||
*/ |
|||
function testMethod_xpathEvaluate__errors(\Closure $closure, string $errorClass, int $errorCode): void { |
|||
$this->expectException($errorClass); |
|||
$this->expectExceptionCode($errorCode); |
|||
$closure(); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* @covers \MensBeam\HTML\DOM\XPathEvaluate::xpathRegisterNamespace |
|||
* |
|||
* @covers \MensBeam\HTML\DOM\Document::__construct |
|||
* @covers \MensBeam\HTML\DOM\Document::load |
|||
* @covers \MensBeam\HTML\DOM\Document::registerXPathNamespace |
|||
* @covers \MensBeam\HTML\DOM\DOMImplementation::__construct |
|||
* @covers \MensBeam\HTML\DOM\Node::__construct |
|||
* @covers \MensBeam\HTML\DOM\Node::getInnerDocument |
|||
* @covers \MensBeam\HTML\DOM\Node::hasChildNodes |
|||
* @covers \MensBeam\HTML\DOM\Node::postParsingTemplatesFix |
|||
* @covers \MensBeam\HTML\DOM\XPathEvaluate::xpathEvaluate |
|||
* @covers \MensBeam\HTML\DOM\XPathEvaluatorBase::evaluate |
|||
* @covers \MensBeam\HTML\DOM\XPathResult::__construct |
|||
* @covers \MensBeam\HTML\DOM\Inner\Document::__construct |
|||
* @covers \MensBeam\HTML\DOM\Inner\Document::__get_xpath |
|||
* @covers \MensBeam\HTML\DOM\Inner\NodeCache::has |
|||
* @covers \MensBeam\HTML\DOM\Inner\NodeCache::key |
|||
* @covers \MensBeam\HTML\DOM\Inner\NodeCache::set |
|||
* @covers \MensBeam\HTML\DOM\Inner\Reflection::createFromProtectedConstructor |
|||
* @covers \MensBeam\HTML\DOM\Inner\Reflection::getProtectedProperty |
|||
*/ |
|||
function testMethod_xpathRegisterNamespace(): void { |
|||
$d = new Document('<!DOCTYPE html><html><body><svg></svg></body></html>'); |
|||
$d->registerXPathNamespace('svg', Node::SVG_NAMESPACE); |
|||
$this->assertEquals(1, count($d->evaluate('//svg:svg', $d))); |
|||
} |
|||
} |
@ -0,0 +1,33 @@ |
|||
<?php |
|||
/** |
|||
* @license MIT |
|||
* Copyright 2017 Dustin Wilson, J. King, et al. |
|||
* See LICENSE and AUTHORS files for details |
|||
*/ |
|||
|
|||
declare(strict_types=1); |
|||
namespace MensBeam\HTML\DOM\TestCase; |
|||
|
|||
use MensBeam\HTML\DOM\{ |
|||
Document, |
|||
Node, |
|||
XPathEvaluator |
|||
}; |
|||
|
|||
|
|||
/** @covers \MensBeam\HTML\DOM\XPathEvaluator */ |
|||
class TestXPathEvaluator extends \PHPUnit\Framework\TestCase { |
|||
function testMethod_registerXPathFunctions(): void { |
|||
$d = new Document(); |
|||
$e = new XPathEvaluator(); |
|||
$this->assertNull($e->registerXPathFunctions($d)); |
|||
} |
|||
|
|||
|
|||
function testMethod_xpathRegisterNamespace(): void { |
|||
$d = new Document('<!DOCTYPE html><html><body><svg></svg></body></html>'); |
|||
$e = new XPathEvaluator(); |
|||
$e->registerXPathNamespace($d, 'svg', Node::SVG_NAMESPACE); |
|||
$this->assertEquals(1, count($e->evaluate('//svg:svg', $d))); |
|||
} |
|||
} |
Loading…
Reference in new issue