Browse Source

Doing some cleanup and appeasement of Intelephense, testing is broken

master
Dustin Wilson 5 months ago
parent
commit
1435ecbab3
  1. 150
      RoboFile.php
  2. 52
      composer.json
  3. 217
      composer.lock
  4. 11
      lib/ArgumentCountError.php
  5. 7
      lib/Attr.php
  6. 1
      lib/CharacterData.php
  7. 4
      lib/ChildNode.php
  8. 44
      lib/DOMException.php
  9. 15
      lib/DOMException/HierarchyRequestError.php
  10. 15
      lib/DOMException/InUseAttributeError.php
  11. 15
      lib/DOMException/InvalidCharacterError.php
  12. 15
      lib/DOMException/NamespaceError.php
  13. 15
      lib/DOMException/NoModificationAllowedError.php
  14. 15
      lib/DOMException/NotFoundError.php
  15. 15
      lib/DOMException/NotSupportedError.php
  16. 15
      lib/DOMException/SyntaxError.php
  17. 15
      lib/DOMException/WrongDocumentError.php
  18. 6
      lib/DOMImplementation.php
  19. 19
      lib/DOMTokenList.php
  20. 39
      lib/Document.php
  21. 4
      lib/DocumentOrElement.php
  22. 1
      lib/DocumentType.php
  23. 49
      lib/Element.php
  24. 73
      lib/Exception.php
  25. 15
      lib/FileNotFoundException.php
  26. 1
      lib/HTMLCollection.php
  27. 24
      lib/HTMLElement.php
  28. 11
      lib/IOException.php
  29. 4
      lib/Inner/Document.php
  30. 9
      lib/NamedNodeMap.php
  31. 90
      lib/Node.php
  32. 4
      lib/NonDocumentTypeChildNode.php
  33. 5
      lib/NonElementParentNode.php
  34. 11
      lib/OutOfBoundsException.php
  35. 9
      lib/ParentNode.php
  36. 1
      lib/ProcessingInstruction.php
  37. 20
      lib/Serializer.php
  38. 17
      lib/Text.php
  39. 15
      lib/UnknownException.php
  40. 2
      lib/XMLDocument.php
  41. 4
      lib/XPathEvaluate.php
  42. 20
      lib/XPathException.php
  43. 14
      robo
  44. 21
      robo.bat

150
RoboFile.php

@ -1,150 +0,0 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
use Robo\Result;
const BASE = __DIR__.\DIRECTORY_SEPARATOR;
const BASE_TEST = BASE."tests".\DIRECTORY_SEPARATOR;
define("IS_WIN", defined("PHP_WINDOWS_VERSION_MAJOR"));
define("IS_MAC", php_uname("s") === "Darwin");
error_reporting(0);
function norm(string $path): string {
$out = realpath($path);
if (!$out) {
$out = str_replace(["/", "\\"], \DIRECTORY_SEPARATOR, $path);
}
return $out;
}
class RoboFile extends \Robo\Tasks {
/** Runs the typical test suite
*
* Arguments passed to the task are passed on to PHPUnit. Thus one may, for
* example, run the following command and get the expected results:
*
* ./robo test --testsuite Tokenizer --exclude-group slow --testdox
*
* Please see the PHPUnit documentation for available options.
*/
public function test(array $args): Result {
return $this->runTests(escapeshellarg(\PHP_BINARY), "typical", $args);
}
/** Runs the full test suite
*
* This includes pedantic tests which may help to identify problems.
* See help for the "test" task for more details.
*/
public function testFull(array $args): Result {
return $this->runTests(escapeshellarg(\PHP_BINARY), "full", $args);
}
/**
* Runs a quick subset of the test suite
*
* See help for the "test" task for more details.
*/
public function testQuick(array $args): Result {
return $this->runTests(escapeshellarg(\PHP_BINARY), "quick", $args);
}
/** Manually updates the imported html5lib test suite */
public function testUpdate(): Result {
$dir = BASE_TEST."html5lib-tests";
if (is_dir($dir)) {
return $this->taskGitStack()->dir($dir)->pull()->run();
} else {
return $this->taskGitStack()->cloneRepo("https://github.com/html5lib/html5lib-tests", $dir)->run();
}
}
/** Produces a code coverage report
*
* By default this task produces an HTML-format coverage report in
* tests/coverage/. Additional reports may be produced by passing
* arguments to this task as one would to PHPUnit.
*/
public function coverage(array $args): Result {
// run tests with code coverage reporting enabled
$exec = $this->findCoverageEngine();
return $this->runTests($exec, "coverage", array_merge(["--coverage-html", BASE_TEST."coverage"], $args));
}
/** Produces a code coverage report, with redundant tests
*
* Depending on the environment, some tests that normally provide
* coverage may be skipped, while working alternatives are normally
* suppressed for reasons of time. This coverage report will try to
* run all tests which may cover code.
*
* See also help for the "coverage" task for more details.
*/
public function coverageFull(array $args): Result {
// run tests with code coverage reporting enabled
$exec = $this->findCoverageEngine();
return $this->runTests($exec, "typical", array_merge(["--coverage-html", BASE_TEST."coverage"], $args));
}
protected function findCoverageEngine(): string {
$dir = rtrim(ini_get("extension_dir"), "/").\DIRECTORY_SEPARATOR;
$ext = IS_WIN ? "dll" : "so";
$php = escapeshellarg(\PHP_BINARY);
$code = escapeshellarg(BASE."lib");
if (extension_loaded("pcov")) {
return "$php -d opcache.enable_cli=0 -d pcov.enabled=1 -d pcov.directory=$code";
} elseif (extension_loaded("xdebug")) {
return "$php -d opcache.enable_cli=0 -d xdebug.mode=coverage";
} elseif (file_exists($dir."pcov.$ext")) {
return "$php -d opcache.enable_cli=0 -d extension=pcov.$ext -d pcov.enabled=1 -d pcov.directory=$code";
} elseif (file_exists($dir."xdebug.$ext")) {
return "$php -d opcache.enable_cli=0 -d zend_extension=xdebug.$ext -d xdebug.mode=coverage";
} else {
if (IS_WIN) {
$dbg = dirname(\PHP_BINARY)."\\phpdbg.exe";
$dbg = file_exists($dbg) ? $dbg : "";
} else {
$dbg = trim(`which phpdbg 2>/dev/null`);
}
if ($dbg) {
return escapeshellarg($dbg)." -qrr";
} else {
return $php;
}
}
}
protected function blackhole(bool $all = false): string {
$hole = IS_WIN ? "nul" : "/dev/null";
return $all ? ">$hole 2>&1" : "2>$hole";
}
protected function runTests(string $executor, string $set, array $args) : Result {
switch ($set) {
case "typical":
$set = ["--exclude-group", "optional"];
break;
case "quick":
$set = ["--exclude-group", "optional,slow"];
break;
case "coverage":
$set = ["--exclude-group", "optional,coverageOptional"];
break;
case "full":
$set = [];
break;
default:
throw new \Exception;
}
$execpath = norm(BASE."vendor-bin/phpunit/vendor/phpunit/phpunit/phpunit");
$confpath = realpath(BASE_TEST."phpunit.dist.xml") ?: norm(BASE_TEST."phpunit.xml");
// clone the html5lib test suite if it's not already present
if (!is_dir(BASE_TEST."html5lib-tests")) {
$this->testUpdate();
}
return $this->taskExec($executor)->option("-d", "zend.assertions=1")->arg($execpath)->option("-c", $confpath)->args(array_merge($set, $args))->run();
}
}

52
composer.json

@ -1,22 +1,14 @@
{
"name": "mensbeam/html-dom",
"description": "Modern DOM library written in PHP for HTML documents",
"type": "library",
"require": {
"php": ">=8.0.2",
"ext-dom": "*",
"mensbeam/html-parser": ">=1.2.1",
"symfony/css-selector": ">=5.3",
"mensbeam/getters-and-setters": ">=1.1"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.3",
"mikey179/vfsstream": "^1.6",
"nikic/php-parser": "^4.13"
},
"scripts": {
"post-install-cmd": ["@composer bin all install"],
"post-update-cmd": ["@composer bin all update"]
"autoload": {
"psr-4": {
"MensBeam\\HTML\\DOM\\": [
"lib/",
"lib/DOMException",
"lib/Exception",
"lib/HTMLElement"
]
}
},
"license": "MIT",
"authors": [
@ -31,27 +23,9 @@
"homepage": "https://jkingweb.ca/"
}
],
"autoload": {
"psr-4": {
"MensBeam\\HTML\\DOM\\": [
"lib/",
"lib/HTMLElement"
]
}
},
"autoload-dev": {
"psr-4": {
"MensBeam\\HTML\\DOM\\Test\\": "tests/lib/",
"MensBeam\\HTML\\DOM\\TestCase\\": [
"tests/cases/",
"tests/cases/Document",
"tests/cases/Serializer"
]
}
},
"config": {
"allow-plugins": {
"bamarni/composer-bin-plugin": true
}
"require": {
"mensbeam/html-parser": "^1.3",
"symfony/css-selector": "^6.3",
"mensbeam/getters-and-setters": "^1.1"
}
}

217
composer.lock

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "dc49ff7a1b170e6b1f368a4f5145543e",
"content-hash": "b4a731d7d5cb3bb8b0520670bb74f4c5",
"packages": [
{
"name": "mensbeam/getters-and-setters",
@ -51,16 +51,16 @@
},
{
"name": "mensbeam/html-parser",
"version": "1.2.6",
"version": "1.3.1",
"source": {
"type": "git",
"url": "https://github.com/mensbeam/HTML-Parser.git",
"reference": "fa0591bd9ce241631894bda290cb99f30b7c288d"
"reference": "2b8a31ce472190013faab710f6f7ad41a486a740"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mensbeam/HTML-Parser/zipball/fa0591bd9ce241631894bda290cb99f30b7c288d",
"reference": "fa0591bd9ce241631894bda290cb99f30b7c288d",
"url": "https://api.github.com/repos/mensbeam/HTML-Parser/zipball/2b8a31ce472190013faab710f6f7ad41a486a740",
"reference": "2b8a31ce472190013faab710f6f7ad41a486a740",
"shasum": ""
},
"require": {
@ -115,10 +115,9 @@
"parsing"
],
"support": {
"issues": "https://github.com/mensbeam/HTML-Parser/issues",
"source": "https://github.com/mensbeam/HTML-Parser/tree/1.2.6"
"source": "https://github.com/mensbeam/HTML-Parser/tree/1.3.1"
},
"time": "2023-01-25T22:49:58+00:00"
"time": "2023-04-20T19:55:47+00:00"
},
{
"name": "mensbeam/intl",
@ -228,25 +227,25 @@
},
{
"name": "psr/http-message",
"version": "1.0.1",
"version": "1.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
"reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
"reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
"php": "^7.2 || ^8.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
"dev-master": "1.1.x-dev"
}
},
"autoload": {
@ -275,22 +274,22 @@
"response"
],
"support": {
"source": "https://github.com/php-fig/http-message/tree/master"
"source": "https://github.com/php-fig/http-message/tree/1.1"
},
"time": "2016-08-06T14:39:51+00:00"
"time": "2023-04-04T09:50:52+00:00"
},
{
"name": "symfony/css-selector",
"version": "v6.2.5",
"version": "v6.3.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
"reference": "bf1b9d4ad8b1cf0dbde8b08e0135a2f6259b9ba1"
"reference": "883d961421ab1709877c10ac99451632a3d6fa57"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/bf1b9d4ad8b1cf0dbde8b08e0135a2f6259b9ba1",
"reference": "bf1b9d4ad8b1cf0dbde8b08e0135a2f6259b9ba1",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/883d961421ab1709877c10ac99451632a3d6fa57",
"reference": "883d961421ab1709877c10ac99451632a3d6fa57",
"shasum": ""
},
"require": {
@ -326,7 +325,7 @@
"description": "Converts CSS selectors to XPath expressions",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/css-selector/tree/v6.2.5"
"source": "https://github.com/symfony/css-selector/tree/v6.3.2"
},
"funding": [
{
@ -342,184 +341,16 @@
"type": "tidelift"
}
],
"time": "2023-01-01T08:38:09+00:00"
}
],
"packages-dev": [
{
"name": "bamarni/composer-bin-plugin",
"version": "1.8.2",
"source": {
"type": "git",
"url": "https://github.com/bamarni/composer-bin-plugin.git",
"reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/bamarni/composer-bin-plugin/zipball/92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880",
"reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880",
"shasum": ""
},
"require": {
"composer-plugin-api": "^2.0",
"php": "^7.2.5 || ^8.0"
},
"require-dev": {
"composer/composer": "^2.0",
"ext-json": "*",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^1.8",
"phpstan/phpstan-phpunit": "^1.1",
"phpunit/phpunit": "^8.5 || ^9.5",
"symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0",
"symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0",
"symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0"
},
"type": "composer-plugin",
"extra": {
"class": "Bamarni\\Composer\\Bin\\BamarniBinPlugin"
},
"autoload": {
"psr-4": {
"Bamarni\\Composer\\Bin\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "No conflicts for your bin dependencies",
"keywords": [
"composer",
"conflict",
"dependency",
"executable",
"isolation",
"tool"
],
"support": {
"issues": "https://github.com/bamarni/composer-bin-plugin/issues",
"source": "https://github.com/bamarni/composer-bin-plugin/tree/1.8.2"
},
"time": "2022-10-31T08:38:03+00:00"
},
{
"name": "mikey179/vfsstream",
"version": "v1.6.11",
"source": {
"type": "git",
"url": "https://github.com/bovigo/vfsStream.git",
"reference": "17d16a85e6c26ce1f3e2fa9ceeacdc2855db1e9f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/bovigo/vfsStream/zipball/17d16a85e6c26ce1f3e2fa9ceeacdc2855db1e9f",
"reference": "17d16a85e6c26ce1f3e2fa9ceeacdc2855db1e9f",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "^4.5|^5.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.6.x-dev"
}
},
"autoload": {
"psr-0": {
"org\\bovigo\\vfs\\": "src/main/php"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Frank Kleine",
"homepage": "http://frankkleine.de/",
"role": "Developer"
}
],
"description": "Virtual file system to mock the real file system in unit tests.",
"homepage": "http://vfs.bovigo.org/",
"support": {
"issues": "https://github.com/bovigo/vfsStream/issues",
"source": "https://github.com/bovigo/vfsStream/tree/master",
"wiki": "https://github.com/bovigo/vfsStream/wiki"
},
"time": "2022-02-23T02:02:42+00:00"
},
{
"name": "nikic/php-parser",
"version": "v4.15.3",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039",
"reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039",
"shasum": ""
},
"require": {
"ext-tokenizer": "*",
"php": ">=7.0"
},
"require-dev": {
"ircmaxell/php-yacc": "^0.0.7",
"phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
},
"bin": [
"bin/php-parse"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.9-dev"
}
},
"autoload": {
"psr-4": {
"PhpParser\\": "lib/PhpParser"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Nikita Popov"
}
],
"description": "A PHP parser written in PHP",
"keywords": [
"parser",
"php"
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3"
},
"time": "2023-01-16T22:05:37+00:00"
"time": "2023-07-12T16:00:22+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=8.0.2",
"ext-dom": "*"
},
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.3.0"
"plugin-api-version": "2.6.0"
}

11
lib/ArgumentCountError.php

@ -0,0 +1,11 @@
<?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 ArgumentCountError extends \ArgumentCountError {}

7
lib/Attr.php

@ -9,7 +9,10 @@ declare(strict_types=1);
namespace MensBeam\HTML\DOM;
/** @property \DOMAttr $_innerNode */
class Attr extends Node {
protected \DOMAttr $_innerNode;
protected function __get_localName(): string {
// PHP's DOM does this correctly already.
// Need to uncoerce string if necessary.
@ -37,7 +40,9 @@ class Attr extends Node {
return null;
}
return $this->_innerNode->ownerDocument->getWrapperNode($this->_innerNode->ownerElement);
/** @var MensBeam\HTML\DOM\Inner\Document $innerOwnerDocument */
$innerOwnerDocument = $this->_innerNode->ownerDocument;
return $innerOwnerDocument->getWrapperNode($this->_innerNode->ownerElement);
}
protected function __get_prefix(): string {

1
lib/CharacterData.php

@ -9,6 +9,7 @@ declare(strict_types=1);
namespace MensBeam\HTML\DOM;
/** @property \DOMCharacterData $_innerNode */
abstract class CharacterData extends Node {
use ChildNode, NonDocumentTypeChildNode;

4
lib/ChildNode.php

@ -7,10 +7,6 @@
declare(strict_types=1);
namespace MensBeam\HTML\DOM;
use MensBeam\HTML\DOM\Inner\{
Document as InnerDocument,
Reflection
};
trait ChildNode {

44
lib/DOMException.php

@ -8,46 +8,4 @@
declare(strict_types=1);
namespace MensBeam\HTML\DOM;
class DOMException extends Exception {
// DEVIATION (kinda): The specification states clearly that using the old
// integer values for exceptions has been deprecated, but all the browsers
// continue to honor them even if they're not explicitly used. Besides, it's
// bonkers to create numerous exception classes for each error type.
public const INDEX_SIZE_ERROR = 1;
public const HIERARCHY_REQUEST_ERROR = 3;
public const WRONG_DOCUMENT = 4;
public const INVALID_CHARACTER = 5;
public const NO_MODIFICATION_ALLOWED = 7;
public const NOT_FOUND = 8;
public const NOT_SUPPORTED = 9;
public const IN_USE_ATTRIBUTE = 10;
public const SYNTAX_ERROR = 12;
public const INVALID_MODIFICATION = 13;
public const NAMESPACE_ERROR = 14;
public const INVALID_ACCESS = 15;
public const FILE_NOT_FOUND = 301;
public function __construct(int $code, ...$args) {
self::$messages = array_replace(parent::$messages, [
1 => 'Invalid index size',
3 => 'Hierarchy request error',
4 => 'Supplied node does not belong to this document',
5 => 'Invalid character',
7 => 'Modification not allowed here',
8 => 'Not found error',
9 => 'Feature is not supported',
10 => 'The attribute is in use',
12 => 'Syntax error',
13 => 'Invalid modification error',
14 => 'Namespace error',
15 => 'Invalid access error',
301 => 'File not found'
]);
parent::__construct($code, ...$args);
}
}
abstract class DOMException extends \Exception {}

15
lib/DOMException/HierarchyRequestError.php

@ -0,0 +1,15 @@
<?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 HierarchyRequestError extends DOMException {
public function __construct(?\Throwable $previous = null) {
parent::__construct('The requested operation would yield an incorrect node tree', 3, $previous);
}
}

15
lib/DOMException/InUseAttributeError.php

@ -0,0 +1,15 @@
<?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 InUseAttributeError extends DOMException {
public function __construct(?\Throwable $previous = null) {
parent::__construct('The attribute is in use by another element', 10, $previous);
}
}

15
lib/DOMException/InvalidCharacterError.php

@ -0,0 +1,15 @@
<?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 InvalidCharacterError extends DOMException {
public function __construct(?\Throwable $previous = null) {
parent::__construct('The string contains invalid characters', 5, $previous);
}
}

15
lib/DOMException/NamespaceError.php

@ -0,0 +1,15 @@
<?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 NamespaceError extends DOMException {
public function __construct(?\Throwable $previous = null) {
parent::__construct('The operation is not allowed by namespaces', 14, $previous);
}
}

15
lib/DOMException/NoModificationAllowedError.php

@ -0,0 +1,15 @@
<?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 NoModificationAllowedError extends DOMException {
public function __construct(?\Throwable $previous = null) {
parent::__construct('The object can not be modified', 7, $previous);
}
}

15
lib/DOMException/NotFoundError.php

@ -0,0 +1,15 @@
<?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 NotFoundError extends DOMException {
public function __construct(?\Throwable $previous = null) {
parent::__construct('The object can not be found here', 8, $previous);
}
}

15
lib/DOMException/NotSupportedError.php

@ -0,0 +1,15 @@
<?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 NotSupportedError extends DOMException {
public function __construct(?\Throwable $previous = null) {
parent::__construct('The operation is not supported', 9, $previous);
}
}

15
lib/DOMException/SyntaxError.php

@ -0,0 +1,15 @@
<?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 SyntaxError extends DOMException {
public function __construct(?\Throwable $previous = null) {
parent::__construct('The string did not match the expected pattern', 12, $previous);
}
}

15
lib/DOMException/WrongDocumentError.php

@ -0,0 +1,15 @@
<?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 WrongDocumentError extends DOMException {
public function __construct(?\Throwable $previous = null) {
parent::__construct('The object is in the wrong document', 4, $previous);
}
}

6
lib/DOMImplementation.php

@ -82,7 +82,7 @@ class DOMImplementation {
$contentType = 'application/xml';
}
Reflection::setProtectedProperties($document, ['_contentType' => $contentType ]);
Reflection::setProtectedProperties($document, [ '_contentType' => $contentType ]);
# 8. Return document.
return $document;
@ -95,7 +95,7 @@ class DOMImplementation {
# To validate a qualifiedName, throw an "InvalidCharacterError" DOMException if
# qualifiedName does not match the QName production.
if (!preg_match(InnerDocument::QNAME_PRODUCTION_REGEX, $qualifiedName)) {
throw new DOMException(DOMException::INVALID_CHARACTER);
throw new InvalidCharacterError();
}
# 2. Return a new doctype, with qualifiedName as its name, publicId as its
@ -145,7 +145,7 @@ class DOMImplementation {
$documentElement->appendChild($doc->createElement('body'));
# 8. doc’s origin is this’s associated document’s origin.
// Not necessary. No scripting in this implementation.
// DEVIATION: There is no scripting in this implementation.
# 9. Return doc.
return $doc;

19
lib/DOMTokenList.php

@ -8,8 +8,7 @@
declare(strict_types=1);
namespace MensBeam\HTML\DOM;
use MensBeam\HTML\Parser\Data,
MensBeam\GettersAndSetters,
MensBeam\HTML\DOM\Inner\Reflection;
MensBeam\GettersAndSetters;
class DOMTokenList implements \ArrayAccess, \Countable, \Iterator {
@ -95,13 +94,13 @@ class DOMTokenList implements \ArrayAccess, \Countable, \Iterator {
foreach ($tokens as $token) {
# 1. If token is the empty string, then throw a "SyntaxError" DOMException.
if ($token === '') {
throw new DOMException(DOMException::SYNTAX_ERROR);
throw new SyntaxError();
}
# 2. If token contains any ASCII whitespace, then throw an
# "InvalidCharacterError" DOMException.
if (preg_match(Data::WHITESPACE_REGEX, $token)) {
throw new DOMException(DOMException::INVALID_CHARACTER);
throw new InvalidCharacterError();
}
}
@ -176,13 +175,13 @@ class DOMTokenList implements \ArrayAccess, \Countable, \Iterator {
foreach ($tokens as $token) {
# 1. If token is the empty string, then throw a "SyntaxError" DOMException.
if ($token === '') {
throw new DOMException(DOMException::SYNTAX_ERROR);
throw new SyntaxError();
}
# 2. If token contains any ASCII whitespace, then throw an
# "InvalidCharacterError" DOMException.
if (preg_match(Data::WHITESPACE_REGEX, $token)) {
throw new DOMException(DOMException::INVALID_CHARACTER);
throw new InvalidCharacterError();
}
}
@ -210,13 +209,13 @@ class DOMTokenList implements \ArrayAccess, \Countable, \Iterator {
# 1. If either token or newToken is the empty string, then throw a "SyntaxError"
# DOMException.
if ($token === '' || $newToken === '') {
throw new DOMException(DOMException::SYNTAX_ERROR);
throw new SyntaxError();
}
# 2. If either token or newToken contains any ASCII whitespace, then throw an
# "InvalidCharacterError" DOMException.
if (preg_match(Data::WHITESPACE_REGEX, $token) || preg_match(Data::WHITESPACE_REGEX, $newToken)) {
throw new DOMException(DOMException::INVALID_CHARACTER);
throw new InvalidCharacterError();
}
# 3. If this’s token set does not contain token, then return false.
@ -268,13 +267,13 @@ class DOMTokenList implements \ArrayAccess, \Countable, \Iterator {
public function toggle(string $token, ?bool $force = null): bool {
# 1. If token is the empty string, then throw a "SyntaxError" DOMException.
if ($token === '') {
throw new DOMException(DOMException::SYNTAX_ERROR);
throw new SyntaxError();
}
# 2. If token contains any ASCII whitespace, then throw an
# "InvalidCharacterError" DOMException.
if (preg_match(Data::WHITESPACE_REGEX, $token)) {
throw new DOMException(DOMException::INVALID_CHARACTER);
throw new InvalidCharacterError();
}
# 3. If this’s token set[token] exists, then:

39
lib/Document.php

@ -20,6 +20,7 @@ use MensBeam\HTML\Parser\{
};
/** @property InnerDocument $_innerNode */
class Document extends Node implements \ArrayAccess {
use DocumentOrElement, NonElementParentNode, ParentNode, XPathEvaluatorBase;
@ -45,7 +46,7 @@ class Document extends Node implements \ArrayAccess {
if ($n !== null) {
do {
if ($n instanceof \DOMElement && $n->namespaceURI === null && ($n->nodeName === 'body' || $n->nodeName === 'frameset')) {
return $n->ownerDocument->getWrapperNode($n);
return $this->_innerNode->getWrapperNode($n);
}
} while ($n = $n->nextSibling);
}
@ -328,19 +329,19 @@ class Document extends Node implements \ArrayAccess {
$element = $title;
}
# 3. Otherwise:
else {
elseif ($head !== null) {
# 1. Let element be the result of creating an element given the document
# element's node document, title, and the HTML namespace.
$element = $this->_innerNode->createElementNS(Node::SVG_NAMESPACE, 'title');
# 2. Append element to the head element.
$head->appendChild($element);
}
# 4. String replace all with the given value within element.
// This is basically what textContent will do for us...
if ($element !== null) {
$element->textContent = $value;
# 4. String replace all with the given value within element.
// This is basically what textContent will do for us...
if ($element !== null) {
$element->textContent = $value;
}
}
}
@ -380,7 +381,7 @@ class Document extends Node implements \ArrayAccess {
#
# 1. If node is a document, then throw a "NotSupportedError" DOMException.
if ($node instanceof Document) {
throw new DOMException(DOMException::NOT_SUPPORTED);
throw new NotSupportedError();
}
# 2. If node is a shadow root, then throw a "HierarchyRequestError" DOMException.
@ -390,7 +391,7 @@ class Document extends Node implements \ArrayAccess {
// DEVIATION: One can't just return here?
if ($node instanceof DocumentFragment) {
$host = Reflection::getProtectedProperty($node, 'host');
if ($host !== null || $host->get() !== null) {
if ($host instanceof \WeakReference && $host->get() !== null || $host !== null) {
return $node;
}
}
@ -419,7 +420,7 @@ class Document extends Node implements \ArrayAccess {
# 1. If localName does not match the Name production in XML, then throw an
# "InvalidCharacterError" DOMException.
if (preg_match(InnerDocument::NAME_PRODUCTION_REGEX, $localName) !== 1) {
throw new DOMException(DOMException::INVALID_CHARACTER);
throw new InvalidCharacterError();
}
# 2. If this is an HTML document, then set localName to localName in ASCII
@ -509,13 +510,13 @@ class Document extends Node implements \ArrayAccess {
#
# 1. If this is an HTML document, then throw a "NotSupportedError" DOMException.
if (!$this instanceof XMLDocument) {
throw new DOMException(DOMException::NOT_SUPPORTED);
throw new NotSupportedError();
}
# 2. If data contains the string "]]>", then throw an "InvalidCharacterError"
# DOMException.
if (str_contains(needle: ']]>', haystack: $data)) {
throw new DOMException(DOMException::INVALID_CHARACTER);
throw new InvalidCharacterError();
}
# 3. Return a new CDATASection node with its data set to data and node document
@ -539,12 +540,12 @@ class Document extends Node implements \ArrayAccess {
# 1. If localName does not match the Name production, then throw an
# "InvalidCharacterError" DOMException.
if (!preg_match(InnerDocument::NAME_PRODUCTION_REGEX, $localName)) {
throw new DOMException(DOMException::INVALID_CHARACTER);
throw new InvalidCharacterError();
}
# 2. If this is an HTML document, then set localName to localName in ASCII
# lowercase.
if (!$this instanceof XMLElement) {
if (!$this instanceof XMLDocument) {
$localName = strtolower($localName);
}
@ -642,7 +643,7 @@ class Document extends Node implements \ArrayAccess {
# DOMException.
// Because this can import from PHP's DOM we must check for more stuff.
if ($node instanceof Document || $node instanceof \DOMDocument || ($node instanceof \DOMNode && $node->ownerDocument::class !== 'DOMDocument') || $node instanceof \DOMEntityReference) {
throw new DOMException(DOMException::NOT_SUPPORTED);
throw new NotSupportedError();
}
# 2. Return a clone of node, with this and the clone children flag set if deep
@ -652,7 +653,7 @@ class Document extends Node implements \ArrayAccess {
public function load(string $source = null, ?string $charset = null): void {
if ($this->hasChildNodes()) {
throw new DOMException(DOMException::NO_MODIFICATION_ALLOWED);
throw new NoModificationAllowedError();
}
$config = new ParserConfig();
@ -675,7 +676,7 @@ class Document extends Node implements \ArrayAccess {
public function loadFile(string $filename, ?string $charset = null): void {
$f = @fopen($filename, 'r');
if (!$f) {
throw new DOMException(DOMException::FILE_NOT_FOUND);
throw new FileNotFoundException();
}
$data = stream_get_contents($f);
@ -810,7 +811,7 @@ class Document extends Node implements \ArrayAccess {
public function serialize(?Node $node = null, array $config = []): string {
$node = $node ?? $this;
if ($node !== $this && $node->ownerDocument !== $this) {
throw new DOMException(DOMException::WRONG_DOCUMENT);
throw new WrongDocumentError();
}
return Serializer::serialize($node->innerNode, $config);
@ -819,7 +820,7 @@ class Document extends Node implements \ArrayAccess {
public function serializeInner(?Node $node = null, array $config = []): string {
$node = $node ?? $this;
if ($node !== $this && $node->ownerDocument !== $this) {
throw new DOMException(DOMException::WRONG_DOCUMENT);
throw new WrongDocumentError();
}
return Serializer::serializeInner($node->innerNode, $config);

4
lib/DocumentOrElement.php

@ -160,7 +160,7 @@ trait DocumentOrElement {
# To validate a qualifiedName, throw an "InvalidCharacterError" DOMException if
# qualifiedName does not match the QName production.
if (preg_match(InnerDocument::QNAME_PRODUCTION_REGEX, $qualifiedName) !== 1) {
throw new DOMException(DOMException::INVALID_CHARACTER);
throw new InvalidCharacterError();
}
# 3. Let prefix be null.
@ -190,7 +190,7 @@ trait DocumentOrElement {
(($qualifiedName === 'xmlns' || $prefix === 'xmlns') && $namespace !== self::XMLNS_NAMESPACE) ||
($namespace === self::XMLNS_NAMESPACE && $qualifiedName !== 'xmlns' && $prefix !== 'xmlns')
) {
throw new DOMException(DOMException::NAMESPACE_ERROR);
throw new NamespaceError();
}
# 10. Return namespace, prefix, and localName.

1
lib/DocumentType.php

@ -9,6 +9,7 @@ declare(strict_types=1);
namespace MensBeam\HTML\DOM;
/** @property \DOMDocumentType $_innerNode */
class DocumentType extends Node {
use ChildNode;

49
lib/Element.php

@ -16,6 +16,7 @@ use MensBeam\HTML\Parser,
Symfony\Component\CssSelector\Exception\SyntaxErrorException as SymfonySyntaxErrorException;
/** @property \DOMElement $_innerNode */
class Element extends Node {
use ChildNode, DocumentOrElement, NonDocumentTypeChildNode, ParentNode;
@ -76,11 +77,13 @@ class Element extends Node {
// object.
$context = $this;
$innerContext = $this->_innerNode;
/** @var InnerDocument */
$innerOwnerDocument = $innerContext->ownerDocument;
# 2. Let fragment be the result of invoking the fragment parsing algorithm with
# the new value as markup, and with context element.
$innerFragment = Parser::parseFragment($innerContext, Parser::NO_QUIRKS_MODE, $value, 'UTF-8');
$fragment = $innerContext->ownerDocument->getWrapperNode($innerFragment);
$fragment = $innerOwnerDocument->getWrapperNode($innerFragment);
$this->postParsingTemplatesFix($innerFragment);
# 3. If the context object is a template element, then let context object be the
@ -136,6 +139,8 @@ class Element extends Node {
# 1. Let parent be the context object's parent.
$parent = $this->parentNode;
$innerParent = $this->_innerNode->parentNode;
/** @var InnerDocument */
$innerOwnerDocument = $this->_innerNode->ownerDocument;
# 2. If parent is null, terminate these steps. There would be no way to obtain a
# reference to the nodes created even if the remaining steps were run.
@ -145,7 +150,7 @@ class Element extends Node {
# 3. If parent is a Document, throw a "NoModificationAllowedError" DOMException.
if ($innerParent instanceof \DOMDocument) {
throw new DOMException(DOMException::NO_MODIFICATION_ALLOWED);
throw new NoModificationAllowedError();
}
# 4. If parent is a DocumentFragment, let parent be a new Element with:
@ -153,13 +158,13 @@ class Element extends Node {
# • The HTML namespace as its namespace, and
# • The context object's node document as its node document.
if ($innerParent instanceof \DOMDocumentFragment) {
$innerParent = $this->_innerNode->ownerDocument->createElement('body');
$innerParent = $innerOwnerDocument->createElement('body');
}
# 5. Let fragment be the result of invoking the fragment parsing algorithm with
# the new value as markup, and parent as the context element.
$innerFragment = Parser::parseFragment($innerParent, Parser::NO_QUIRKS_MODE, $value, 'UTF-8');
$fragment = $this->_innerNode->ownerDocument->getWrapperNode($innerFragment);
$fragment = $innerOwnerDocument->getWrapperNode($innerFragment);
$this->postParsingTemplatesFix($innerFragment);
# 6. Replace the context object with fragment within the context object's
@ -192,7 +197,7 @@ class Element extends Node {
} catch (\Exception $e) {
# 2. If s is failure, throw a "SyntaxError" DOMException.
if ($e instanceof SymfonySyntaxErrorException) {
throw new DOMException(DOMException::SYNTAX_ERROR);
throw new SyntaxError();
}
}
@ -276,6 +281,8 @@ class Element extends Node {
}
$qualifiedName = $this->coerceName($qualifiedName);
/** @var InnerDocument */
$innerOwnerDocument = $this->_innerNode->ownerDocument;
# 2. Return the first attribute in element’s attribute list whose qualified name is
# qualifiedName; otherwise null.
@ -285,7 +292,7 @@ class Element extends Node {
$attributes = $this->_innerNode->attributes;
foreach ($attributes as $attr) {
if ($attr->nodeName === $qualifiedName) {
return $this->_innerNode->ownerDocument->getWrapperNode($attr);
return $innerOwnerDocument->getWrapperNode($attr);
}
}
@ -307,6 +314,8 @@ class Element extends Node {
}
$localName = $this->coerceName($localName);
/** @var InnerDocument */
$innerOwnerDocument = $this->_innerNode->ownerDocument;
# 2. Return the attribute in element’s attribute list whose namespace is namespace
# and local name is localName, if any; otherwise null.
@ -316,7 +325,7 @@ class Element extends Node {
$attributes = $this->_innerNode->attributes;
foreach ($attributes as $attr) {
if ($attr->namespaceURI === $namespace && $attr->localName === $localName) {
return $this->_innerNode->ownerDocument->getWrapperNode($attr);
return $innerOwnerDocument->getWrapperNode($attr);
}
}
@ -419,7 +428,7 @@ class Element extends Node {
# If context is null or a Document, throw a "NoModificationAllowedError" DOMException.
if ($context === null || $context instanceof Document) {
throw new DOMException(DOMException::NO_MODIFICATION_ALLOWED);
throw new NoModificationAllowedError();
}
break;
case 'afterbegin':
@ -428,7 +437,7 @@ class Element extends Node {
$context = $this;
$innerContext = $this->_innerNode;
break;
default: throw new DOMException(DOMException::SYNTAX_ERROR);
default: throw new SyntaxError();
}
# 2. If context is not an Element or the following are all true:
@ -495,7 +504,7 @@ class Element extends Node {
} catch (\Exception $e) {
# 2. If s is failure, throw a "SyntaxError" DOMException.
if ($e instanceof SymfonySyntaxErrorException) {
throw new DOMException(DOMException::SYNTAX_ERROR);
throw new SyntaxError();
}
}
@ -503,9 +512,11 @@ class Element extends Node {
# :scope element this, returns success, then return true; otherwise, return
# false. [SELECTORS4]
$innerNode = $this->_innerNode;
/** @var InnerDocument */
$innerOwnerDocument = $innerNode->ownerDocument;
// Query the parent as the context node, yes. This is due to how the XPath
// queries are generated from Symfony.
return ($innerNode->ownerDocument->xpath->query($s, $innerNode->parentNode)->item(0) === $innerNode);
return ($innerOwnerDocument->xpath->query($s, $innerNode->parentNode)->item(0) === $innerNode);
}
public function removeAttribute(string $qualifiedName): void {
@ -527,14 +538,20 @@ class Element extends Node {
# The removeAttributeNode(attr) method steps are:
# 1. If this’s attribute list does not contain attr, then throw a
# "NotFoundError" DOMException.
// PHP's DOM does this already. Will catch its exception and rethrow as HTML-DOM
// PHP's DOM does this already. Will catch its exceptions and rethrow as HTML-DOM
// DOMException.
# 2. Remove attr.
try {
$this->_innerNode->removeAttributeNode($attr->innerNode);
} catch (\DOMException $e) {
throw new DOMException($e->code);
switch ($e->getCode()) {
case 8: throw new NotFoundError();
break;
case 9: throw new NotSupportedError();
break;
default: throw new UnknownException();
}
}
# 3. Return attr.
@ -560,7 +577,7 @@ class Element extends Node {
# 1. If qualifiedName does not match the Name production in XML, then throw an
# "InvalidCharacterError" DOMException.
if (!preg_match(InnerDocument::NAME_PRODUCTION_REGEX, $qualifiedName)) {
throw new DOMException(DOMException::INVALID_CHARACTER);
throw new InvalidCharacterError();
}
# 2. If this is in the HTML namespace and its node document is an HTML document,
@ -603,7 +620,7 @@ class Element extends Node {
# "InUseAttributeError" DOMException.
$ownerElement = $attr->ownerElement;
if ($ownerElement !== null && $ownerElement !== $this) {
throw new DOMException(DOMException::IN_USE_ATTRIBUTE);
throw new InUseAttributeError();
}
// PHP's DOM doesn't do this method correctly. It returns the old node if it is
@ -756,7 +773,7 @@ class Element extends Node {
return $element->parentNode->insertBefore($node, $element->nextSibling);
break;
default: throw new DOMException(DOMException::SYNTAX_ERROR);
default: throw new SyntaxError();
}
}
}

73
lib/Exception.php

@ -1,73 +0,0 @@
<?php
/**
* @license MIT
* Copyright 2021, Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details
*/
declare(strict_types=1);
namespace MensBeam\HTML\DOM;
class Exception extends \Exception {
const INVALID_CODE = 100;
const UNKNOWN_ERROR = 101;
const INCORRECT_PARAMETERS_FOR_MESSAGE = 102;
const UNREACHABLE_CODE = 103;
const NONEXISTENT_PROPERTY = 201;
const READONLY_PROPERTY = 202;
const ARGUMENT_TYPE_ERROR = 203;
const UNDEFINED_METHOD = 204;
const RETURN_TYPE_ERROR = 205;
protected static $messages = [
100 => 'Invalid error code',
101 => 'Unknown error; escaping',
102 => 'Incorrect number of parameters for Exception message; %s expected',
103 => 'Unreachable code',
201 => 'Property %s does not exist',
202 => 'Cannot write readonly property %s',
203 => 'Argument #%s ($%s) must be of type %s, %s given',
204 => 'Call to undefined method %s::%s()',
205 => 'Function should return type %s, %s given'
];
public function __construct(int $code, ...$args) {
if (!isset(self::$messages[$code])) {
throw new Exception(Exception::INVALID_CODE);
}
$message = self::$messages[$code];
$previous = null;
// @codeCoverageIgnoreStart
if ($args) {
// Grab a previous exception if there is one.
if ($args[0] instanceof \Throwable) {
$previous = array_shift($args);
} elseif (end($args) instanceof \Throwable) {
$previous = array_pop($args);
}
}
// @codeCoverageIgnoreEnd
// Count the number of replacements needed in the message.
preg_match_all('/(\%(?:\d+\$)?s)/', $message, $matches);
$count = count($matches[1]);
// If the number of replacements don't match the arguments then oops.
if (count($args) !== $count) {
throw new Exception(Exception::INCORRECT_PARAMETERS_FOR_MESSAGE, $count);
}
if ($count > 0) {
// Go through each of the arguments and run sprintf on the strings.
$message = call_user_func_array('sprintf', array_merge([$message], $args));
}
parent::__construct($message, $code, $previous);
}
}

15
lib/FileNotFoundException.php

@ -0,0 +1,15 @@
<?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 FileNotFoundException extends IOException {
public function __construct(int $code = 0, \Throwable $previous = null, string $path = null) {
parent::__construct(($path === null) ? 'File could not be found' : sprintf('File "%s" could not be found', $path), $code, $previous);
}
}

1
lib/HTMLCollection.php

@ -7,7 +7,6 @@
declare(strict_types=1);
namespace MensBeam\HTML\DOM;
use MensBeam\HTML\Parser;
# An HTMLCollection object is a collection of elements.

24
lib/HTMLElement.php

@ -104,7 +104,7 @@ class HTMLElement extends Element {
case 'false':
$this->setAttribute('contenteditable', $value);
break;
default: throw new DOMException(DOMException::SYNTAX_ERROR);
default: throw new SyntaxError();
}
}
@ -225,9 +225,10 @@ class HTMLElement extends Element {
}
$n = $this->_innerNode;
/** @var InnerDocument */
$doc = $n->ownerDocument;
while ($n = $n->parentNode) {
if ($doc->getWrapperNode($n) instanceof HTMLElement && $n->hasAttribute('hidden')) {
if ($doc->getWrapperNode($n) instanceof HTMLElement && $n instanceof \DOMElement && $n->hasAttribute('hidden')) {
return true;
}
}
@ -334,7 +335,9 @@ class HTMLElement extends Element {
$n = $this->_innerNode;
while ($n = $n->parentNode) {
if ($n instanceof \DOMElement) {
if ($n->getAttribute('contenteditable') === 'true' && $n->ownerDocument->getWrapperNode($n) instanceof HTMLElement) {
/** @var InnerDocument */
$ownerDocument = $n->ownerDocument;
if ($n->getAttribute('contenteditable') === 'true' && $ownerDocument->getWrapperNode($n) instanceof HTMLElement) {
return true;
}
}
@ -366,7 +369,7 @@ class HTMLElement extends Element {
# DOMException.
$innerNode = $this->_innerNode;
if ($this->parentNode === null) {
throw new DOMException(DOMException::NO_MODIFICATION_ALLOWED);
throw new NoModificationAllowedError();
}
# 2. Let next be this's next sibling.
@ -391,9 +394,14 @@ class HTMLElement extends Element {
# with the next text node given next's previous sibling.
if ($next !== null && $next->previousSibling instanceof \DOMText) {
# To merge with the next text node given a Text node node:
// This gets confusing because this is a process that's in another section of
// the spec, but in this situation _node_ is next's previous sibling, so _node's
// next sibling_ will be next.
# 1. Let next be node's next sibling.
# 2. If next is not a Text node, then return.
// Already checked for
if (!$next instanceof \DOMText) {
return;
}
# 3. Replace data with node, node's data's length, 0, and next's data.
$next->previousSibling->data .= $next->data;
@ -476,11 +484,13 @@ class HTMLElement extends Element {
}
$n = $this->_innerNode;
/** @var InnerDocument */
$doc = $n->ownerDocument;
while ($n = $n->parentNode) {
// This looks weird but it's faster to check for the method here first because
// getting a wrapper node causes a wrapper element to be created if it doesn't
// already exist. Don't want to create unnecessary wrappers.
/** @var \DOMElement $n */
if (method_exists($n, 'getAttribute') && $n->getAttribute('translate') === 'yes' && $doc->getWrapperNode($n) instanceof HTMLElement) {
return true;
}
@ -519,7 +529,9 @@ class HTMLElement extends Element {
$n = $element;
while ($n = $n->parentNode) {
if ($n->tagName === 'form' && $n->ownerDocument->getWrapperNode($n) instanceof HTMLElement) {
/** @var InnerDocumente */
$ownerDocument = $n->ownerDocument;
if ($ownerDocument->getWrapperNode($n) instanceof HTMLElement && $n instanceof \DOMElement && $n->tagName === 'form') {
return $this->autoCapitalizationHint($n);
}
}

11
lib/IOException.php

@ -0,0 +1,11 @@
<?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 IOException extends \RuntimeException {}

4
lib/Inner/Document.php

@ -68,7 +68,7 @@ class Document extends \DOMDocument {
}
if ($node instanceof \DOMDocument) {
throw new DOMException(DOMException::NOT_SUPPORTED);
throw new NotSupportedError();
}
// There's no way to see whether unappended doctypes are owned by a document in
@ -76,7 +76,7 @@ class Document extends \DOMDocument {
// document. This is worked around in the wrapper DOM but a problem here when
// creating the wrapper node.
if (!$node instanceof \DOMDocumentType && $node->ownerDocument !== $this) {
throw new DOMException(DOMException::WRONG_DOCUMENT);
throw new WrongDocumentError();
}
// If the wrapper node already exists then return that.

9
lib/NamedNodeMap.php

@ -7,11 +7,8 @@
declare(strict_types=1);
namespace MensBeam\HTML\DOM;
use MensBeam\HTML\DOM\Inner\{
Document as InnerDocument,
Reflection
};
use MensBeam\HTML\Parser\NameCoercion;
use MensBeam\HTML\DOM\Inner\Document as InnerDocument,
MensBeam\HTML\Parser\NameCoercion;
class NamedNodeMap extends Collection {
@ -124,7 +121,7 @@ class NamedNodeMap extends Collection {
# 2. If attr is null, then throw a "NotFoundError" DOMException.
if ($attr === null) {
throw new DOMException(DOMException::NOT_FOUND);
throw new NotFoundError();
}
# 3. Return attr.

90
lib/Node.php

@ -8,7 +8,6 @@
declare(strict_types=1);
namespace MensBeam\HTML\DOM;
use MensBeam\GettersAndSetters,
MensBeam\HTML\Parser,
MensBeam\HTML\Parser\NameCoercion;
use MensBeam\HTML\DOM\Inner\{
Document as InnerDocument,
@ -227,11 +226,12 @@ abstract class Node implements \Stringable {
# otherwise this’s node document.
// PHP's DOM does this correctly on everything but document types. That's taken
// care of in DocumentType.
if ($this instanceof Document || !$ownerDocument = $this->_innerNode->ownerDocument) {
/** @var InnerDocument $ownerDocument */
if ($this instanceof Document || !($ownerDocument = $this->_innerNode->ownerDocument)) {
return null;
}
return $this->_innerNode->ownerDocument->getWrapperNode($ownerDocument);
return $ownerDocument->getWrapperNode($ownerDocument);
}
protected function __get_parentElement(): ?Element {
@ -376,6 +376,7 @@ abstract class Node implements \Stringable {
# 5. If node2 is an attribute, then:
if ($node2 instanceof Attr) {
# 1. Set attr2 to node2 and node2 to attr2’s element.
/** @var \DOMAttr $innerNode2 */
$attr2 = $innerNode2;
$node2 = $attr2->ownerElement;
$innerNode2 = $innerNode2->ownerElement;
@ -480,7 +481,13 @@ abstract class Node implements \Stringable {
while ($n = $n->parentNode) {
$root = $n;
}
return (!$root instanceof InnerDocument) ? $root->ownerDocument->getWrapperNode($root) : $root->wrapperNode;
if (!$root instanceof InnerDocument) {
/** @var Node $root */
return $root->ownerDocument->getWrapperNode($root);
}
return $root->wrapperNode;
}
public function hasChildNodes(): bool {
@ -612,7 +619,7 @@ abstract class Node implements \Stringable {
# 1. If parent is not a Document, DocumentFragment, or Element node, then throw
# a "HierarchyRequestError" DOMException.
if (!$inner instanceof InnerDocument && !$inner instanceof \DOMDocumentFragment && !$inner instanceof \DOMElement) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
}
# 2. If node is a host-including inclusive ancestor of parent, then throw a
@ -621,25 +628,25 @@ abstract class Node implements \Stringable {
// host-including inclusive ancestor of child, but it should. All browsers check
// for this.
if ($this->containsInner($node, $inner) || $this->containsInner($node, $child)) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
}
# 3. If child’s parent is not parent, then throw a "NotFoundError" DOMException.
if ($child->parentNode !== $inner) {
throw new DOMException(DOMException::NOT_FOUND);
throw new NotFoundError();
}
# 4. If node is not a DocumentFragment, DocumentType, Element, or CharacterData
# node, then throw a "HierarchyRequestError" DOMException.
if (!$node instanceof \DOMDocumentFragment && !$node instanceof \DOMDocumentType && !$node instanceof \DOMElement && !$node instanceof \DOMCharacterData) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
}
# 5. If either node is a Text node and parent is a document, or node is a
# doctype and parent is not a document, then throw a "HierarchyRequestError"
# DOMException.
if (($node instanceof \DOMText && $inner instanceof InnerDocument) || ($node instanceof \DOMDocumentType && !$inner instanceof InnerDocument)) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
}
# 6. If parent is a document, and any of the statements below, switched on the
@ -653,14 +660,14 @@ abstract class Node implements \Stringable {
if ($node instanceof \DOMDocumentFragment) {
$nodeChildElementCount = $node->childElementCount;
if ($nodeChildElementCount > 1) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
}
$n = $node->firstChild;
if ($n !== null) {
do {
if ($n instanceof \DOMText) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
}
} while ($n = $n->nextSibling);
}
@ -671,7 +678,7 @@ abstract class Node implements \Stringable {
$beforeChild = ($n !== $child);
do {
if (($n instanceof \DOMElement && $n !== $child) || (!$beforeChild && $n instanceof \DOMDocumentType)) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
} elseif ($n === $child) {
$beforeChild = false;
}
@ -689,7 +696,7 @@ abstract class Node implements \Stringable {
$beforeChild = ($n !== $child);
do {
if (($n instanceof \DOMElement && $n !== $child) || (!$beforeChild && $n instanceof \DOMDocumentType)) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
} elseif ($n === $child) {
$beforeChild = false;
}
@ -706,7 +713,7 @@ abstract class Node implements \Stringable {
$beforeChild = ($n !== $child);
do {
if (($n instanceof \DOMDocumentType && $n !== $child) || $beforeChild && $n instanceof \DOMElement) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
} elseif ($n === $child) {
$beforeChild = false;
}
@ -824,14 +831,18 @@ abstract class Node implements \Stringable {
# with the clone children flag set.
# 3. Append copied contents to copy's template contents.
// Template contents are stored in the wrapper nodes.
$copyWrapperContent = $copy->ownerDocument->getWrapperNode($copy)->content;
/** @var InnerDocument */
$copyOwnerDocument = $copy->ownerDocument;
$copyWrapperContent = $copyOwnerDocument->getWrapperNode($copy)->content;
// If the cloning is called for as a result of parsing serialized markup the
// contents of the node should be appended to the wrapper element's content
// document fragment. Otherwise, clone the content document fragment instead.
if (!$parsing) {
$copyWrapperContentInner = $copyWrapperContent->innerNode;
$nodeWrapperContent = $node->ownerDocument->getWrapperNode($node)->content->innerNode;
/** @var InnerDocument */
$nodeWrapperOwnerDocument = $node->ownerDocument;
$nodeWrapperContent = $nodeWrapperOwnerDocument->getWrapperNode($node)->content->innerNode;
$childNodes = $nodeWrapperContent->childNodes;
if ($childNodes->length > 0) {
// This garbage is necessary because the appendChildInner method
@ -1063,6 +1074,7 @@ abstract class Node implements \Stringable {
# ↪ DocumentType
# Its name, public ID, and system ID.
if ($thisNode instanceof \DOMDocumentType) {
/** @var \DOMDocumentType $otherNode */
if ($thisNode->name !== $otherNode->name || $thisNode->publicId !== $otherNode->publicId || $thisNode->systemId !== $thisNode->publicId) {
return false;
}
@ -1070,6 +1082,7 @@ abstract class Node implements \Stringable {
# ↪ Element
# Its namespace, namespace prefix, local name, and its attribute list’s size.
elseif ($thisNode instanceof \DOMElement) {
/** @var \DOMElement $otherNode */
$otherAttributes = $otherNode->attributes;
if ($thisNode->namespaceURI !== $otherNode->namespaceURI || $thisNode->prefix !== $otherNode->prefix || $thisNode->localName !== $otherNode->localName || $thisNode->attributes->length !== $otherAttributes->length) {
return false;
@ -1091,6 +1104,7 @@ abstract class Node implements \Stringable {
# ↪ Attr
# Its namespace, local name, and value.
elseif ($thisNode instanceof \DOMAttr) {
/** @var \DOMAttr $otherNode */
if ($thisNode->namespaceURI !== $otherNode->namespaceURI || $thisNode->localName !== $otherNode->localName || $thisNode->value !== $otherNode->value) {
return false;
}
@ -1099,12 +1113,14 @@ abstract class Node implements \Stringable {
# ↪ Comment
# Its data.
elseif ($thisNode instanceof \DOMText || $thisNode instanceof \DOMComment) {
/** @var \DOMText|\DOMComment $otherNode */
if ($thisNode->data !== $otherNode->data) {
return false;
}
}
if ($thisNode instanceof \DOMDocument || $thisNode instanceof \DOMDocumentFragment || $thisNode instanceof \DOMElement) {
/** @var \DOMDocument|\DOMDocumentFragment|\DOMElement $otherNode */
# • A and B have the same number of children.
if ($thisNode->childNodes->length !== $otherNode->childNodes->length) {
return false;
@ -1129,7 +1145,9 @@ abstract class Node implements \Stringable {
# ↪ Element
if ($node instanceof \DOMElement) {
// Work around PHP DOM HTML namespace bug
if ($node->namespaceURI === null && !$node->ownerDocument->getWrapperNode($node->ownerDocument) instanceof XMLDocument) {
/** @var \InnerDocument */
$ownerDocument = $node->ownerDocument;
if ($node->namespaceURI === null && !$ownerDocument->getWrapperNode($ownerDocument) instanceof XMLDocument) {
$namespace = self::HTML_NAMESPACE;
} else {
$namespace = $node->namespaceURI;
@ -1289,7 +1307,9 @@ abstract class Node implements \Stringable {
// below walks through this node and temporarily replaces foreign descendants
// with bullshit elements which are then replaced once the node is inserted.
if ($element->namespaceURI === null && ($this instanceof DocumentFragment || $this->getRootNode() !== null) && $element->hasChildNodes()) {
$foreign = $element->ownerDocument->xpath->query('.//*[parent::*[namespace-uri()=""] and not(namespace-uri()="") and name()=local-name()]', $element);
/** @var InnerDocument */
$ownerDocument = $element->ownerDocument;
$foreign = $ownerDocument->xpath->query('.//*[parent::*[namespace-uri()=""] and not(namespace-uri()="") and name()=local-name()]', $element);
$this->bullshitReplacements = [];
if ($foreign->length > 0) {
$count = 0;
@ -1317,7 +1337,7 @@ abstract class Node implements \Stringable {
# 1. If parent is not a Document, DocumentFragment, or Element node, then throw
# a "HierarchyRequestError" Exception.
if (!$parent instanceof InnerDocument && !$parent instanceof \DOMDocumentFragment && !$parent instanceof \DOMElement) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
}
# 2. If node is a host-including inclusive ancestor of parent, then throw a
@ -1328,7 +1348,7 @@ abstract class Node implements \Stringable {
# host-including inclusive ancestor of B’s root’s host.
if ($node->parentNode !== null) {
if ($parent->parentNode !== null && ($parent === $node || $this->containsInner($node, $parent))) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
} else {
$n = $parent;
do {
@ -1336,10 +1356,12 @@ abstract class Node implements \Stringable {
} while ($n = $n->parentNode);
if ($parentRoot instanceof \DOMDocumentFragment) {
$wrappedParentRoot = $parentRoot->ownerDocument->getWrapperNode($parentRoot);
/** @var InnerDocument */
$ownerDocument = $parentRoot->ownerDocument;
$wrappedParentRoot = $ownerDocument->getWrapperNode($parentRoot);
$parentRootHost = Reflection::getProtectedProperty($wrappedParentRoot, 'host');
if ($parentRootHost !== null && ($parentRootHost === $node || $this->containsInner($node, $parentRootHost->get()->innerNode))) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
}
}
}
@ -1348,21 +1370,21 @@ abstract class Node implements \Stringable {
# 3. If child is non-null and its parent is not parent, then throw a
# "NotFoundError" Exception.
if ($child !== null && ($child->parentNode === null || $child->parentNode !== $parent)) {
throw new DOMException(DOMException::NOT_FOUND);
throw new NotFoundError();
}
# 4. If node is not a DocumentFragment, DocumentType, Element, Text,
# ProcessingInstruction, or Comment node, then throw a "HierarchyRequestError"
# Exception.
if (!$node instanceof \DOMDocumentFragment && !$node instanceof \DOMDocumentType && !$node instanceof \DOMElement && !$node instanceof \DOMText && !$node instanceof \DOMProcessingInstruction && !$node instanceof \DOMComment) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
}
# 5. If either node is a Text node and parent is a document, or node is a
# doctype and parent is not a document, then throw a "HierarchyRequestError"
# Exception.
if (($node instanceof \DOMText && $parent instanceof \DOMDocument) || ($node instanceof \DOMDocumentType && !$parent instanceof InnerDocument)) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
}
# 6. If parent is a document, and any of the statements below, switched on the
@ -1377,13 +1399,13 @@ abstract class Node implements \Stringable {
if ($node instanceof \DOMDocumentFragment) {
$nodeChildElementCount = $node->childElementCount;
if ($nodeChildElementCount > 1) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
} else {
$n = $node->firstChild;
if ($n !== null) {
do {
if ($n instanceof \DOMText) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
}
} while ($n = $n->nextSibling);
}
@ -1391,14 +1413,14 @@ abstract class Node implements \Stringable {
if ($nodeChildElementCount === 1) {
if ($parent->childElementCount > 0 || $child instanceof \DOMDocumentType) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
}
if ($child !== null) {
$n = $child;
while ($n = $n->nextSibling) {
if ($n instanceof \DOMDocumentType) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
}
}
}
@ -1410,14 +1432,14 @@ abstract class Node implements \Stringable {
# doctype is following child.
elseif ($node instanceof \DOMElement) {
if ($child instanceof \DOMDocumentType) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
}
if ($child !== null) {
$n = $child;
while ($n = $n->nextSibling) {
if ($n instanceof \DOMDocumentType) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
}
}
}
@ -1426,7 +1448,7 @@ abstract class Node implements \Stringable {
$n = $parent->firstChild;
do {
if ($n instanceof \DOMElement) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
}
} while ($n = $n->nextSibling);
}
@ -1441,7 +1463,7 @@ abstract class Node implements \Stringable {
$n = $firstChild;
do {
if ($n instanceof \DOMDocumentType || ($child === null && $n instanceof \DOMElement)) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
}
} while ($n = $n->nextSibling);
}
@ -1450,7 +1472,7 @@ abstract class Node implements \Stringable {
$n = $child;
while ($n = $n->previousSibling) {
if ($n instanceof \DOMElement) {
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
throw new HierarchyRequestError();
}
}
}

4
lib/NonDocumentTypeChildNode.php

@ -7,10 +7,6 @@
declare(strict_types=1);
namespace MensBeam\HTML\DOM;
use MensBeam\HTML\DOM\Inner\{
Document as InnerDocument,
Reflection
};
trait NonDocumentTypeChildNode {

5
lib/NonElementParentNode.php

@ -7,11 +7,6 @@
declare(strict_types=1);
namespace MensBeam\HTML\DOM;
use MensBeam\HTML\DOM\Inner\{
Document as InnerDocument,
Reflection
};
use MensBeam\HTML\Parser;
trait NonElementParentNode {

11
lib/OutOfBoundsException.php

@ -0,0 +1,11 @@
<?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 OutOfBoundsException extends \OutOfBoundsException {}

9
lib/ParentNode.php

@ -7,10 +7,7 @@
declare(strict_types=1);
namespace MensBeam\HTML\DOM;
use MensBeam\HTML\DOM\Inner\{
Document as InnerDocument,
Reflection
};
use MensBeam\HTML\DOM\Inner\Reflection;
use Symfony\Component\CssSelector\CssSelectorConverter,
Symfony\Component\CssSelector\Exception\SyntaxErrorException as SymfonySyntaxErrorException;
@ -121,7 +118,7 @@ trait ParentNode {
continue 2;
case Node::WALK_REJECT:
break;
default: throw new DOMException(DOMException::SYNTAX_ERROR);
default: throw new SyntaxError();
}
if ($node->parentNode !== null && $node->hasChildNodes()) {
@ -146,7 +143,7 @@ trait ParentNode {
// so only throw exception when an actual syntax error, otherwise return an
// empty nodelist.
if ($e instanceof SymfonySyntaxErrorException) {
throw new DOMException(DOMException::SYNTAX_ERROR);
throw new SyntaxError();
}
return new \DOMNodeList;

1
lib/ProcessingInstruction.php

@ -9,6 +9,7 @@ declare(strict_types=1);
namespace MensBeam\HTML\DOM;
/** @property \DOMProcessingInstruction $_innerNode */
class ProcessingInstruction extends CharacterData {
protected function __get_target(): string {
// Need to uncoerce string if necessary.

20
lib/Serializer.php

@ -8,20 +8,21 @@
declare(strict_types=1);
namespace MensBeam\HTML\DOM;
use MensBeam\HTML\DOM\Inner\Reflection,
MensBeam\HTML\Parser;
use MensBeam\HTML\Parser\{
Config,
Serializer as ParserSerializer
};
MensBeam\HTML\Parser,
MensBeam\HTML\Parser\Serializer as ParserSerializer;
class Serializer extends ParserSerializer {
protected static function fragmentHasHost(\DOMDocumentFragment $fragment): bool {
return (Reflection::getProtectedProperty($fragment->ownerDocument->getWrapperNode($fragment), 'host') !== null);
/** @var InnerDocument */
$ownerDocument = $fragment->ownerDocument;
return (Reflection::getProtectedProperty($ownerDocument->getWrapperNode($fragment), 'host') !== null);
}
protected static function getTemplateContent(\DOMElement $node): \DOMNode {
return $node->ownerDocument->getWrapperNode($node)->content->innerNode;
/** @var InnerDocument */
$ownerDocument = $node->ownerDocument;
return $ownerDocument->getWrapperNode($node)->content->innerNode;
}
protected static function isPreformattedContent(\DOMNode $node): bool {
@ -32,7 +33,9 @@ class Serializer extends ParserSerializer {
return true;
}
} elseif ($n instanceof \DOMDocumentFragment) {
$host = Reflection::getProtectedProperty($node->ownerDocument->getWrapperNode($n), 'host');
/** @var InnerDocument */
$ownerDocument = $node->ownerDocument;
$host = Reflection::getProtectedProperty($ownerDocument->getWrapperNode($n), 'host');
if ($host !== null) {
$n = $host->get()->innerNode;
}
@ -43,6 +46,7 @@ class Serializer extends ParserSerializer {
}
protected static function treatAsBlockWithTemplates(\DOMNode $node): bool {
/** @var InnerDocument */
$document = $node->ownerDocument;
$xpath = $document->xpath;
$templates = $xpath->query('.//template[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"][not(ancestor::iframe[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"] or ancestor::listing[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"] or ancestor::noembed[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"] or ancestor::noframes[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"] or ancestor::noscript[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"] or ancestor::plaintext[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"] or ancestor::pre[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"] or ancestor::style[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"] or ancestor::script[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"] or ancestor::textarea[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"] or ancestor::title[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"] or ancestor::xmp[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"])]', $node);

17
lib/Text.php

@ -9,6 +9,7 @@ declare(strict_types=1);
namespace MensBeam\HTML\DOM;
/** @property \DOMText $_innerNode */
class Text extends CharacterData {
protected function __get_wholeText(): string {
// PHP's DOM does this correctly already.
@ -19,11 +20,21 @@ class Text extends CharacterData {
public function splitText(int $offset): Text {
// PHP DOM mostly handles this correctly with the exception of not throwing an
// exception when the offset is greater than the length, so let's fix that.
if ($offset > $this->length) {
throw new DOMException(DOMException::INDEX_SIZE_ERROR);
// DEVIATION?: All browsers error when supplying negative numbers here, so let's check for that, too.
if ($offset < 0 || $offset > $this->length) {
# 2. If offset is greater than length, then throw an "IndexSizeError" DOMException.
// DEVIATION?: WebIDL standard
// (https://webidl.spec.whatwg.org/#idl-DOMException-error-names) says
// IndexSizeError is deprecated and to use RangeError instead which isn't a
// DOMException but specified to be an analog of ECMAScript's built-in errors.
// Because of this we're going to use OutOfBoundsException which is PHP's
// equivalent.
throw new OutOfBoundsException(sprintf('Offset %s is outside of Text node\'s range of %s to %s', $offset, 0, $this->length));
}
return $this->_innerNode->ownerDocument->getWrapperNode($this->_innerNode->splitText($offset));
/** @var \MensBeam\HTML\DOM\Inner\Document */
$ownerDocument = $this->_innerNode->ownerDocument;
return $ownerDocument->getWrapperNode($this->_innerNode->splitText($offset));
}

15
lib/UnknownException.php

@ -0,0 +1,15 @@
<?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 UnknownException extends \LogicException {
public function __construct(int $code = 0, ?\Throwable $previous = null) {
parent::__construct('The program reached an invalid state; this error should be reported', $code, $previous);
}
}

2
lib/XMLDocument.php

@ -16,7 +16,7 @@ class XMLDocument extends Document {
public function load(string $source = null, ?string $charset = null): void {
if ($this->hasChildNodes()) {
throw new DOMException(DOMException::NO_MODIFICATION_ALLOWED);
throw new NoModificationAllowedError();
}
$this->_innerNode->encoding = Charset::fromCharset((string)$charset) ?? 'UTF-8';

4
lib/XPathEvaluate.php

@ -82,7 +82,7 @@ trait XPathEvaluate {
$resultType = XPathResult::ORDERED_NODE_ITERATOR_TYPE;
break;
default:
throw new DOMException(DOMException::NOT_SUPPORTED);
throw new NotSupportedError();
}
} else {
switch ($type) {
@ -148,7 +148,7 @@ trait XPathEvaluate {
$result = $result->item(0);
break;
default: throw new DOMException(DOMException::NOT_SUPPORTED);
default: throw new NotSupportedError();
}
$resultType = $type;

20
lib/XPathException.php

@ -9,18 +9,22 @@ declare(strict_types=1);
namespace MensBeam\HTML\DOM;
class XPathException extends Exception {
class XPathException extends \Exception {
public const INVALID_EXPRESSION = 51;
public const TYPE_ERROR = 52;
public const UNRESOLVABLE_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 => 'Unresolvable namespace prefix'
]);
public function __construct(int $code = 0, ?\Throwable $previous = null) {
switch ($code) {
case self::INVALID_EXPRESSION: $message = 'Invalid expression error';
break;
case self::TYPE_ERROR: $message = 'Expression cannot be converted to the specified type';
break;
case self::UNRESOLVABLE_NAMESPACE_PREFIX: $message = 'Unresolvable namespace prefix';
break;
default: throw new UnknownException();
}
parent::__construct($code, ...$args);
parent::__construct($message, $code, $previous);
}
}

14
robo

@ -1,14 +0,0 @@
#! /bin/sh
base=`dirname "$0"`
roboCommand="$1"
if [ $# -eq 0 ]; then
"$base/vendor/bin/robo"
else
shift
ulimit -n 2048
if [ "$1" = "clean" ]; then
"$base/vendor/bin/robo" "$roboCommand" "$@"
else
"$base/vendor/bin/robo" "$roboCommand" -- "$@"
fi
fi

21
robo.bat

@ -1,21 +0,0 @@
@echo off
setlocal
set base=%~dp0
set roboCommand=%1
rem get all arguments except the first
shift
set "args="
:parse
if "%~1" neq "" (
set args=%args% %1
shift
goto :parse
)
if defined args set args=%args:~1%
if "%1"=="clean" (
call "%base%vendor\bin\robo" "%roboCommand%" %args%
) else (
call "%base%vendor\bin\robo" "%roboCommand%" -- %args%
)
Loading…
Cancel
Save