├── .github
└── workflows
│ └── ci.yaml
├── .gitignore
├── .php-cs-fixer.dist.php
├── README.md
├── composer.json
├── lib
├── Document.php
├── Element.php
├── Exception
│ └── InvalidQueryException.php
├── XPath.php
└── XPathAware.php
├── phpstan.neon
├── phpunit.xml.dist
└── tests
└── Unit
├── DocumentTest.php
├── ElementTest.php
└── XPathTest.php
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
1 | name: "CI"
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches:
7 | - 'master'
8 |
9 | env:
10 | fail-fast: true
11 | TZ: "Europe/Paris"
12 | REQUIRED_PHP_EXTENSIONS: "dom"
13 |
14 | jobs:
15 | composer-validate:
16 | name: "Composer validate (${{ matrix.php-version }})"
17 |
18 | runs-on: "ubuntu-latest"
19 |
20 | strategy:
21 | matrix:
22 | php-version:
23 | - '7.3'
24 |
25 | steps:
26 | -
27 | name: "Checkout code"
28 | uses: "actions/checkout@v2"
29 |
30 | -
31 | name: "Install PHP"
32 | uses: "shivammathur/setup-php@v2"
33 | with:
34 | coverage: "none"
35 | php-version: "${{ matrix.php-version }}"
36 | tools: composer:v2
37 |
38 | -
39 | name: "Validate composer.json"
40 | run: "composer validate --strict --no-check-lock"
41 |
42 | php-cs-fixer:
43 | needs:
44 | - "composer-validate"
45 |
46 | name: "PHP-CS-Fixer (${{ matrix.php-version }})"
47 |
48 | runs-on: "ubuntu-latest"
49 |
50 | strategy:
51 | matrix:
52 | php-version:
53 | - '7.4'
54 |
55 | steps:
56 | -
57 | name: "Checkout code"
58 | uses: "actions/checkout@v2"
59 |
60 | -
61 | name: "Install PHP"
62 | uses: "shivammathur/setup-php@v2"
63 | with:
64 | coverage: "none"
65 | extensions: "${{ env.REQUIRED_PHP_EXTENSIONS }}"
66 | php-version: "${{ matrix.php-version }}"
67 | tools: composer:v2
68 |
69 | -
70 | name: "Composer install"
71 | uses: "ramsey/composer-install@v1"
72 | with:
73 | composer-options: "--no-scripts"
74 |
75 | -
76 | name: "Run friendsofphp/php-cs-fixer"
77 | run: "vendor/bin/php-cs-fixer fix --dry-run --diff --verbose"
78 |
79 | phpstan:
80 | needs:
81 | - "composer-validate"
82 |
83 | name: "PHPStan (${{ matrix.php-version }})"
84 |
85 | runs-on: "ubuntu-latest"
86 |
87 | strategy:
88 | matrix:
89 | php-version:
90 | - '7.4'
91 |
92 | steps:
93 | -
94 | name: "Checkout code"
95 | uses: "actions/checkout@v2"
96 |
97 | -
98 | name: "Install PHP"
99 | uses: "shivammathur/setup-php@v2"
100 | with:
101 | coverage: "none"
102 | extensions: "${{ env.REQUIRED_PHP_EXTENSIONS }}"
103 | php-version: "${{ matrix.php-version }}"
104 | tools: composer:v2
105 |
106 | -
107 | name: "Composer install"
108 | uses: "ramsey/composer-install@v1"
109 | with:
110 | composer-options: "--no-scripts"
111 |
112 | -
113 | name: "Run phpstan/phpstan"
114 | run: "vendor/bin/phpstan analyse --level=7 lib"
115 |
116 | tests:
117 | needs:
118 | - "composer-validate"
119 |
120 | name: "PHP ${{ matrix.php-version }} + ${{ matrix.dependency }}"
121 |
122 | runs-on: ubuntu-latest
123 |
124 | continue-on-error: ${{ matrix.allow-failures }}
125 |
126 | strategy:
127 | matrix:
128 | php-version:
129 | - '7.3'
130 | - '7.4'
131 | - '8.0'
132 | dependency:
133 | - 'lowest'
134 | - 'highest'
135 | with-examples: ['yes']
136 | allow-failures: [false]
137 | include:
138 | - php-version: '7.3'
139 | dependency: 'lowest'
140 | with-examples: 'no'
141 | allow-failures: false
142 | coverage: xdebug
143 | - php-version: '8.1'
144 | dependency: 'highest'
145 | with-examples: 'no'
146 | allow-failures: true
147 | coverage: xdebug
148 |
149 | steps:
150 | - name: "Checkout code"
151 | uses: actions/checkout@v2.3.3
152 |
153 | - name: "Install PHP with extensions"
154 | uses: shivammathur/setup-php@2.7.0
155 | with:
156 | extensions: "${{ env.REQUIRED_PHP_EXTENSIONS }}"
157 | php-version: ${{ matrix.php-version }}
158 | tools: composer:v2
159 |
160 | - name: "Add PHPUnit matcher"
161 | run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
162 |
163 | - name: "Remove friendsofphp/php-cs-fixer"
164 | run: composer remove --dev friendsofphp/php-cs-fixer --no-update
165 |
166 | - name: "Composer install"
167 | uses: "ramsey/composer-install@v1"
168 | with:
169 | dependency-versions: "${{ matrix.dependency }}"
170 |
171 | - name: PHP Info
172 | run: php --version
173 |
174 | - name: "Run tests with PHPUnit"
175 | run: vendor/bin/phpunit --verbose
176 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | composer.lock
2 | vendor
3 | .phpunit.result.cache
4 | .php-cs-fixer.cache
5 |
--------------------------------------------------------------------------------
/.php-cs-fixer.dist.php:
--------------------------------------------------------------------------------
1 | in([
8 | __DIR__ . '/lib',
9 | __DIR__ . '/tests',
10 | ])
11 | ;
12 |
13 | return (new Config())
14 | ->setRiskyAllowed(true)
15 | ->setRules([
16 | '@PSR12' => true,
17 | 'void_return' => true,
18 | 'binary_operator_spaces' => [
19 | 'operators' => [
20 | '=>' => null
21 | ],
22 | ],
23 | 'blank_line_before_statement' => [
24 | 'statements' => [
25 | 'break',
26 | 'continue',
27 | 'declare',
28 | 'default',
29 | 'do',
30 | 'exit',
31 | 'for',
32 | 'foreach',
33 | 'goto',
34 | 'if',
35 | 'include',
36 | 'include_once',
37 | 'require',
38 | 'require_once',
39 | 'return',
40 | 'switch',
41 | 'throw',
42 | 'try',
43 | 'while',
44 | 'yield',
45 | ],
46 | ],
47 | 'concat_space' => false,
48 | 'no_unused_imports' => true,
49 | 'php_unit_set_up_tear_down_visibility' => true,
50 | 'phpdoc_align' => [],
51 | 'phpdoc_indent' => false,
52 | 'phpdoc_separation' => true,
53 | 'no_superfluous_phpdoc_tags' => [
54 | 'allow_mixed' => true
55 | ],
56 | 'fully_qualified_strict_types' => true,
57 | ])
58 | ->setFinder($finder)
59 | ;
60 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | DOM
2 | ===
3 |
4 | **This library is abandoned**
5 |
6 | [](https://github.com/phpbench/dom/actions/workflows/ci.yaml)
7 |
8 | This library provides a wrapper for the PHP DOM library which makes your life
9 | easier.
10 |
11 | It wraps the `\DOMDocument`, `\DOMElement` and `\DOMXpath` classes and
12 | throws *exceptions*.
13 |
14 | Example:
15 |
16 | ```php
17 | $dom = new Document();
18 | $element = $dom->createRoot('example');
19 | $element->appendChild('boo', 'hello');
20 | $element->appendChild('baz', 'world');
21 |
22 | echo $dom->dump();
23 | //
24 | //
25 | // hello
26 | // world
27 | //
28 |
29 | $element->appendElement('number', 5);
30 | $element->appendElement('number', 10);
31 |
32 | echo $element->evaluate('sum(./number)'); // 15
33 |
34 | $nodeList = $element->query('./number');
35 |
36 | echo $nodeList->length; // 2
37 | ```
38 |
39 | Document
40 | --------
41 |
42 | The `PhpBench\Dom\Document` class wraps the `\DOMDocument` class and replaces the
43 | `\DOMElement` class with the `PhpBench\Dom\Element` class.
44 |
45 | It implements the `XPathAware` interface.
46 |
47 | - `createRoot($name, $value = null)`: Create and return a new root node with `$name` and optional
48 | `$value`.
49 | - `query($query, $context = null)`: Execute a given XPath query on the
50 | document.
51 | - `queryOne($query, $context = null)`: Execute a given XPath query on the
52 | document and return the first element or `NULL`.
53 | - `evaluate($query, $context = null)`: Evaluate the given XPath expression.
54 | - `dump()`: Return a formatted string representation of the document.
55 |
56 | Element
57 | -------
58 |
59 | Wraps the `\DOMElement` class and is used by default when you instantiate a
60 | `PhpBench\Dom\Document` class.
61 |
62 | It implements the `XPathAware` interface.
63 |
64 | - `appendElement($name $value)`: Create and return an element with name
65 | `$name` and value `$value`.
66 | - `query`, `queryOne` and `evaluate`: As with Document but will use the context of this element by
67 | default.
68 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "phpbench/dom",
3 | "description": "DOM wrapper to simplify working with the PHP DOM implementation",
4 | "license": "MIT",
5 | "authors": [
6 | {
7 | "name": "Daniel Leech",
8 | "email": "daniel@dantleech.com"
9 | }
10 | ],
11 | "require": {
12 | "php": "^7.3||^8.0",
13 | "ext-dom": "*"
14 | },
15 | "require-dev": {
16 | "friendsofphp/php-cs-fixer": "^3.14",
17 | "phpunit/phpunit": "^8.0||^9.0",
18 | "phpstan/phpstan": "^1.10"
19 | },
20 | "autoload": {
21 | "psr-4": {
22 | "PhpBench\\Dom\\": "lib/"
23 | }
24 | },
25 | "autoload-dev": {
26 | "psr-4": {
27 | "PhpBench\\Dom\\Tests\\": "tests/"
28 | }
29 | },
30 | "extra": {
31 | "branch-alias": {
32 | "dev-master": "1.0-dev"
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/lib/Document.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace PhpBench\Dom;
13 |
14 | use DOMNode;
15 | use DOMNodeList;
16 | use RuntimeException;
17 |
18 | /**
19 | * Wrapper for the \DOMDocument class.
20 | */
21 | class Document extends \DOMDocument implements XPathAware
22 | {
23 | /**
24 | * @var XPath|null
25 | */
26 | private $xpath;
27 |
28 | /**
29 | * @param string $version
30 | * @param string $encoding
31 | */
32 | public function __construct($version = '1.0', $encoding = null)
33 | {
34 | if ($encoding) {
35 | parent::__construct($version, $encoding);
36 | } else {
37 | parent::__construct($version);
38 | }
39 | $this->registerNodeClass('DOMElement', 'PhpBench\Dom\Element');
40 | }
41 |
42 | /**
43 | * Create and return a root DOM element.
44 | *
45 | * @param string $name
46 | *
47 | */
48 | public function createRoot($name): Element
49 | {
50 | $element = $this->appendChild(new Element($name));
51 | assert($element instanceof Element);
52 |
53 | return $element;
54 | }
55 |
56 | /**
57 | * Return the XPath object bound to this document.
58 | *
59 | */
60 | public function xpath(): XPath
61 | {
62 | if ($this->xpath) {
63 | return $this->xpath;
64 | }
65 |
66 | $this->xpath = new XPath($this);
67 |
68 | return $this->xpath;
69 | }
70 |
71 | /**
72 | * @return DOMNodeList
73 | */
74 | public function query($query, DOMNode $context = null): DOMNodeList
75 | {
76 | return $this->xpath()->query($query, $context);
77 | }
78 |
79 | /**
80 | * {@inheritdoc}
81 | */
82 | public function queryOne($query, DOMNode $context = null): ?Element
83 | {
84 | return $this->xpath()->queryOne($query, $context);
85 | }
86 |
87 | /**
88 | * @return mixed
89 | */
90 | public function evaluate($expression, DOMNode $context = null)
91 | {
92 | return $this->xpath()->evaluate($expression, $context);
93 | }
94 |
95 | /**
96 | * Return a formatted string representation of the document.
97 | *
98 | */
99 | public function dump(): string
100 | {
101 | $this->formatOutput = true;
102 | $result = $this->saveXML();
103 | $this->formatOutput = false;
104 |
105 | if (false === $result) {
106 | throw new RuntimeException('Could not dump XML');
107 | }
108 |
109 | return $result;
110 | }
111 |
112 | public function duplicate(): Document
113 | {
114 | $dom = new self();
115 |
116 | if ($this->firstChild) {
117 | $firstChild = $dom->importNode($this->firstChild, true);
118 | $dom->appendChild($firstChild);
119 | }
120 |
121 |
122 | return $dom;
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/lib/Element.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace PhpBench\Dom;
13 |
14 | use DOMNode;
15 | use DOMNodeList;
16 |
17 | /**
18 | * Wrapper for the \DOMElement class.
19 | */
20 | class Element extends \DOMElement implements XPathAware
21 | {
22 | /**
23 | * Create and append a text-node with the given name and value.
24 | */
25 | public function appendTextNode(string $name, ?string $value): Element
26 | {
27 | $el = new self($name);
28 | $element = $this->appendChild($el);
29 | assert($element instanceof Element);
30 |
31 | $element->appendChild(
32 | $this->owner()->createTextNode($value ?? '')
33 | );
34 |
35 | return $element;
36 | }
37 |
38 | /**
39 | * Create and append an element with the given name and optionally given value.
40 | *
41 | * Note: The value will not be escaped. Use DOMDocument::createTextNode() to create a text node with escaping support.
42 | */
43 | public function appendElement(string $name, ?string $value = null): Element
44 | {
45 | $element = $this->appendChild(new self($name, $value));
46 | assert($element instanceof Element);
47 |
48 | return $element;
49 | }
50 |
51 | /**
52 | * @return DOMNodeList
53 | */
54 | public function query($xpath, DOMNode $context = null): DOMNodeList
55 | {
56 | return $this->owner()->xpath()->query($xpath, $context ?: $this);
57 | }
58 |
59 | public function queryOne($xpath, DOMNode $context = null): ?Element
60 | {
61 | return $this->owner()->xpath()->queryOne($xpath, $context ?: $this);
62 | }
63 |
64 | /**
65 | * @return mixed
66 | */
67 | public function evaluate($expression, DOMNode $context = null)
68 | {
69 | return $this->owner()->xpath()->evaluate($expression, $context ?: $this);
70 | }
71 |
72 | /**
73 | * Dump the current node
74 | */
75 | public function dump(): string
76 | {
77 | $document = new Document();
78 | $document->appendChild($document->importNode($this, true));
79 |
80 | return $document->dump();
81 | }
82 |
83 | private function owner(): Document
84 | {
85 | $owner = $this->ownerDocument;
86 | assert($owner instanceof Document);
87 |
88 | return $owner;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/lib/Exception/InvalidQueryException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace PhpBench\Dom\Exception;
13 |
14 | class InvalidQueryException extends \InvalidArgumentException
15 | {
16 | }
17 |
--------------------------------------------------------------------------------
/lib/XPath.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace PhpBench\Dom;
13 |
14 | use DOMNode;
15 | use DOMNodeList;
16 | use RuntimeException;
17 |
18 | /**
19 | * Wrapper for the \DOMXPath class.
20 | */
21 | class XPath extends \DOMXPath
22 | {
23 | /**
24 | * {@inheritdoc}
25 | *
26 | * @param mixed $contextnode
27 | * @param bool $registerNodeNS
28 | */
29 | #[\ReturnTypeWillChange]
30 | public function evaluate($expression, $contextnode = null, $registerNodeNS = true)
31 | {
32 | $result = $this->execute('evaluate', 'expression', $expression, $contextnode, $registerNodeNS);
33 |
34 | return $result;
35 | }
36 |
37 | /**
38 | * @param bool $registerNodeNS
39 | * @param mixed $contextnode
40 | *
41 | * @return DOMNodeList
42 | */
43 | #[\ReturnTypeWillChange]
44 | public function query($expression, $contextnode = null, $registerNodeNS = true): DOMNodeList
45 | {
46 | $list = $this->execute('query', 'query', $expression, $contextnode, $registerNodeNS);
47 |
48 | if (!$list instanceof DOMNodeList) {
49 | throw new RuntimeException(sprintf('Expected XPAth expression to return DOMNodeList, got "%s"', is_object($list) ? get_class($list) : gettype($list)));
50 | }
51 |
52 | return $list;
53 | }
54 |
55 | public function queryOne(string $expr, DOMNode $contextEl = null, bool $registerNodeNs = false): ?Element
56 | {
57 | $nodeList = $this->query($expr, $contextEl, $registerNodeNs);
58 |
59 | if (0 === $nodeList->length) {
60 | return null;
61 | }
62 |
63 | $node = $nodeList->item(0);
64 |
65 | if (!$node instanceof Element) {
66 | throw new RuntimeException(sprintf(
67 | 'Expected "%s" but got "%s"',
68 | Element::class,
69 | $node ? get_class($node) : gettype($node)
70 | ));
71 | }
72 |
73 | return $node;
74 | }
75 |
76 | /**
77 | * Execute the given xpath method and cactch any errors.
78 | *
79 | * @param mixed $contextEl
80 | *
81 | * @return mixed
82 | */
83 | #[\ReturnTypeWillChange]
84 | private function execute(string $method, string $context, string $query, $contextEl = null, bool $registerNodeNs = false)
85 | {
86 | libxml_use_internal_errors(true);
87 |
88 | $value = @parent::$method($query, $contextEl, $registerNodeNs);
89 |
90 | $xmlErrors = libxml_get_errors();
91 |
92 | if ($xmlErrors) {
93 | $errors = [];
94 |
95 | foreach ($xmlErrors as $xmlError) {
96 | $errors[] = sprintf('[%s] %s', $xmlError->code, $xmlError->message);
97 | }
98 | libxml_clear_errors();
99 |
100 | throw new Exception\InvalidQueryException(sprintf(
101 | 'Errors encountered when evaluating XPath %s "%s": %s%s',
102 | $context,
103 | $query,
104 | PHP_EOL,
105 | implode(PHP_EOL, $errors)
106 | ));
107 | }
108 |
109 | libxml_use_internal_errors(false);
110 |
111 | return $value;
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/lib/XPathAware.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace PhpBench\Dom;
13 |
14 | use DOMNode;
15 | use DOMNodeList;
16 |
17 | interface XPathAware
18 | {
19 | /**
20 | * Perform an xpath query on this document, optionally with
21 | * the given context node.
22 | *
23 | * If this interface is applied to an Element, then the element
24 | * should be used as the context if no context is given.
25 | *
26 | * @param string $query
27 | * @param \DOMNode $context
28 | *
29 | * @return DOMNodeList
30 | */
31 | public function query($query, DOMNode $context = null);
32 |
33 | /**
34 | * As with XPathAware::query but return a single node or NULL if no node was found.
35 | *
36 | * @param string $query
37 | * @param \DOMNode $context
38 | *
39 | * @return Element|null
40 | */
41 | public function queryOne($query, DOMNode $context = null);
42 |
43 | /**
44 | * Evaluate an XPath expression on this document, optionally
45 | * with the given context node.
46 | *
47 | * If this interface is applied to an Element, then the element
48 | * should be used as the context if no context is given.
49 | *
50 | * @param string $expression
51 | * @param \DOMNode $context
52 | *
53 | * @return mixed
54 | */
55 | public function evaluate($expression, DOMNode $context = null);
56 | }
57 |
--------------------------------------------------------------------------------
/phpstan.neon:
--------------------------------------------------------------------------------
1 | parameters:
2 | level: max
3 | paths:
4 | - lib
5 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
11 | ./tests
12 |
13 |
14 |
15 |
16 |
17 | .
18 |
19 | vendor/
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/tests/Unit/DocumentTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace PhpBench\Dom\Tests\Unit;
13 |
14 | use PhpBench\Dom\Document;
15 | use PHPUnit\Framework\TestCase;
16 |
17 | class DocumentTest extends TestCase
18 | {
19 | /**
20 | * @var Document
21 | */
22 | private $document;
23 |
24 | protected function setUp(): void
25 | {
26 | $this->document = new Document(1.0);
27 | }
28 |
29 | /**
30 | * It should perform an XPath query.
31 | */
32 | public function testQuery(): void
33 | {
34 | $this->document->loadXml($this->getXml());
35 | $nodeList = $this->document->query('//record');
36 | $this->assertInstanceOf('DOMNodeList', $nodeList);
37 | $this->assertEquals(2, $nodeList->length);
38 | }
39 |
40 | /**
41 | * It should evaluate an XPath expression.
42 | */
43 | public function testEvaluate(): void
44 | {
45 | $this->document->loadXml($this->getXml());
46 | $result = $this->document->evaluate('count(//record)');
47 | $this->assertEquals(2, $result);
48 | }
49 |
50 | /**
51 | * It should create a root element.
52 | */
53 | public function testCreateRoot(): void
54 | {
55 | $this->document->createRoot('hello');
56 | $this->assertStringContainsString('', $this->document->saveXml());
57 | }
58 |
59 | /**
60 | * It should return a formatted string representation of the document.
61 | */
62 | public function testDump(): void
63 | {
64 | $this->document->loadXml($this->getXml());
65 | $this->assertEquals(
66 | trim($this->getXml()),
67 | trim($this->document->dump())
68 | );
69 | }
70 |
71 | /**
72 | * It should provide a duplicate version of itself.
73 | */
74 | public function testDuplicate(): void
75 | {
76 | $this->document->loadXml($this->getXml());
77 | $duplicate = $this->document->duplicate();
78 | $this->assertNotsame($this->document, $duplicate);
79 | $this->assertNotsame($this->document->firstChild, $duplicate->firstChild);
80 | $this->assertNotsame($this->document->firstChild->firstChild, $duplicate->firstChild->firstChild);
81 | }
82 |
83 |
84 | private function getXml()
85 | {
86 | $xml = <<
88 |
89 |
90 | Hello
91 |
92 |
93 | World
94 |
95 |
96 | EOT;
97 |
98 | return $xml;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/tests/Unit/ElementTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace PhpBench\Dom\Tests\Unit;
13 |
14 | use PhpBench\Dom\Document;
15 | use PHPUnit\Framework\TestCase;
16 |
17 | class ElementTest extends TestCase
18 | {
19 | private $element;
20 | private $document;
21 |
22 | protected function setUp(): void
23 | {
24 | $this->document = new Document();
25 | $this->element = $this->document->createRoot('test');
26 | }
27 |
28 | /**
29 | * It should create and append a child element.
30 | */
31 | public function testAppendElement(): void
32 | {
33 | $element = $this->element->appendElement('hello');
34 | $result = $this->document->evaluate('count(//hello)');
35 | $this->assertInstanceOf('PhpBench\Dom\Element', $element);
36 | $this->assertEquals(1, $result);
37 | }
38 |
39 | /**
40 | * It should create and append text.
41 | */
42 | public function testAppendTextNode(): void
43 | {
44 | $element = $this->element->appendTextNode('hello', 'fix&foxy');
45 | $result = $this->document->evaluate('count(//hello)');
46 | $this->assertInstanceOf('PhpBench\Dom\Element', $element);
47 | $this->assertEquals(1, $result);
48 | }
49 |
50 | /**
51 | * It should exeucte an XPath query.
52 | */
53 | public function testQuery(): void
54 | {
55 | $boo = $this->element->appendElement('boo');
56 | $nodeList = $this->element->query('.//*');
57 | $this->assertInstanceOf('DOMNodeList', $nodeList);
58 | $this->assertEquals(1, $nodeList->length);
59 | $nodeList = $boo->query('.//*');
60 | $this->assertEquals(0, $nodeList->length);
61 | }
62 |
63 | /**
64 | * It should evaluate an XPath expression.
65 | */
66 | public function testEvaluate(): void
67 | {
68 | $boo = $this->element->appendElement('boo');
69 | $count = $this->element->evaluate('count(.//*)');
70 | $this->assertEquals(1, $count);
71 | $count = $boo->evaluate('count(.//*)');
72 | $this->assertEquals(0, $count);
73 | }
74 |
75 | /**
76 | * It should query for one element.
77 | */
78 | public function testQueryOne(): void
79 | {
80 | $boo = $this->element->appendElement('boo');
81 | $node = $this->element->queryOne('./boo');
82 | $this->assertSame($boo, $node);
83 | }
84 |
85 | /**
86 | * It should return null if one element is queried for an it none exist.
87 | */
88 | public function testQueryOneNone(): void
89 | {
90 | $node = $this->element->queryOne('./boo');
91 | $this->assertNull($node);
92 | }
93 |
94 | /**
95 | * It should return the XML contained in the node.
96 | */
97 | public function testDumpNode(): void
98 | {
99 | $this->element->appendElement('boo');
100 | $dump = $this->element->dump();
101 |
102 | $this->assertEquals(<<
104 |
105 |
106 |
107 |
108 | EOT
109 | , $dump);
110 | }
111 |
112 | private function getXml()
113 | {
114 | $xml = <<
116 |
117 |
118 | Hello
119 |
120 |
121 | World
122 |
123 |
124 | EOT;
125 |
126 | return $xml;
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/tests/Unit/XPathTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace PhpBench\Dom\Tests\Unit;
13 |
14 | use PhpBench\Dom\Document;
15 | use PhpBench\Dom\Exception\InvalidQueryException;
16 | use PHPUnit\Framework\TestCase;
17 |
18 | class XPathTest extends TestCase
19 | {
20 | /**
21 | * It should throw an exception if the xpath query is invalid.
22 | */
23 | public function testQueryException(): void
24 | {
25 | $this->expectException(InvalidQueryException::class);
26 | $this->getDocument()->query('//article[noexistfunc() = "as"]');
27 | }
28 |
29 | /**
30 | * It should NOT throw an exception if the expression evaluates as false.
31 | */
32 | public function testEvaluateFalse(): void
33 | {
34 | $result = $this->getDocument()->evaluate('boolean(count(//foo))');
35 | $this->assertFalse($result);
36 | }
37 |
38 | private function getDocument()
39 | {
40 | $xml = <<
42 |
43 |
44 | Morning
45 |
46 |
47 | Afternoon
48 |
49 |
50 | EOT;
51 |
52 | $document = new Document();
53 | $document->loadXml($xml);
54 |
55 | return $document;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------