'); $this->assertInstanceOf(Document::class, $d); $this->assertSame('CSS1Compat', $d->compatMode); $d->destroy(); // Construction with charset $d = new Document('', 'gb2312'); $this->assertSame('GBK', $d->charset); $this->assertSame('GBK', $d->characterSet); $this->assertSame('GBK', $d->inputEncoding); $d->destroy(); // Empty document with charset $d = new Document(charset: 'gb2312'); $this->assertSame('GBK', $d->charset); $this->assertSame('GBK', $d->characterSet); $this->assertSame('GBK', $d->inputEncoding); $this->assertSame('CSS1Compat', $d->compatMode); $d->destroy(); // Quirks mode $d = new Document(''); $this->assertSame('BackCompat', $d->compatMode); $d->destroy(); } public function testMethod_adoptNode(): void { $d = new Document(); $documentElement = $d->appendChild($d->createElement('html')); $body = $documentElement->appendChild($d->createElement('body')); $template = $body->appendChild($d->createElement('template')); $d2 = new Document(); $d2->adoptNode($documentElement); $this->assertSame($d2, $documentElement->ownerDocument); $d2->adoptNode($template->content); $this->assertSame($d, $template->content->ownerDocument); $d->destroy(); $d2->destroy(); } public function testMethod_createAttribute(): void { // Attributes are lowercased in HTML documents $d = new Document(''); $a = $d->createAttribute('LANG'); $this->assertSame('lang', $a->localName); $d->destroy(); // They're not in XML documents $d = new XMLDocument(''); $a = $d->createAttribute('LANG'); $this->assertSame('LANG', $a->localName); $d->destroy(); // PHP normally can't create attributes if there's no document element, but // HTML-DOM can $d = new Document(); $a = $d->createAttribute('LANG'); $this->assertSame('lang', $a->localName); $this->assertSame($d, $a->ownerDocument); // PHP doesn't support unicode characters as attribute names, but HTML-DOM can // by internally replacing the characters $a = $d->createAttribute('💩'); $this->assertSame('💩', $a->localName); $d->destroy(); } public function testMethod_createAttributeNS(): void { // Local names are NOT lowercased in HTML documents $d = new Document(); $a = $d->createAttributeNS('https://💩.com', '💩:POO'); $this->assertSame('POO', $a->localName); $this->assertSame('https://💩.com', $a->namespaceURI); $this->assertSame('💩', $a->prefix); // Empty string namespaces and empty prefixes are supposed to be null $a = $d->createAttributeNS('', '💩'); $this->assertSame('💩', $a->localName); $this->assertSame(null, $a->namespaceURI); $this->assertSame(null, $a->prefix); $d->destroy(); } public function testMethod_createCDATASection(): void { $d = new XMLDocument(); $c = $d->createCDATASection('💩poo'); $this->assertSame('💩poo', $c->data); $this->assertInstanceOf(CDataSection::class, $c); $d->destroy(); } public function testMethod_createComment(): void { $d = new Document(); $c = $d->createComment('💩poo'); $this->assertSame('💩poo', $c->data); $this->assertInstanceOf(Comment::class, $c); $d->destroy(); } public function testMethod_createDocumentFragment(): void { $d = new Document(); $f = $d->createDocumentFragment(); $t = $d->createTextNode('ook'); $f->appendChild($t); $this->assertSame($t, $f->firstChild); $this->assertInstanceOf(DocumentFragment::class, $f); $d->destroy(); } public function testMethod_createElement(): void { $d = new Document(); $c = $d->createElement('poo💩'); $this->assertSame('poo💩', $c->localName); $this->assertInstanceOf(Element::class, $c); $d->destroy(); } public function testMethod_createElementNS(): void { // Local names ARE lowercased in HTML documents when namespaced $d = new Document(); $a = $d->createElementNS('https://💩.com', '💩:POO'); $this->assertSame('POO', $a->localName); $this->assertSame('https://💩.com', $a->namespaceURI); $this->assertSame('💩', $a->prefix); // Empty string namespaces and empty prefixes are supposed to be null even in // HTML documents $a = $d->createElementNS('', '💩'); $this->assertSame('💩', $a->localName); $this->assertSame(null, $a->namespaceURI); $this->assertSame(null, $a->prefix); $d->destroy(); } public function testMethod_createProcessingInstruction(): void { $d = new Document(); $c = $d->createProcessingInstruction('💩', '💩poo'); $this->assertSame('💩', $c->target); $this->assertSame('💩poo', $c->data); $this->assertInstanceOf(ProcessingInstruction::class, $c); $d->destroy(); } public function testMethod_evaluate(): void { $d = new Document('ookeek
ack
'); // NodeList return value (XPath is NOT coerced) $r = $d->evaluate('//pooU01F4A9//text()'); $this->assertEquals(3, count($r)); // Number return value (XPath is NOT coerced) $r = $d->evaluate('count(//pooU01F4A9//text())'); $this->assertEquals(3, $r); // String return value (XPath is NOT coerced) $r = $d->evaluate('name(//pooU01F4A9)'); $this->assertSame('poo💩', $r); // Context node - NodeList return value (XPath is NOT coerced) $r = $d->evaluate('./pooU01F4A9//text()', $d->getElementById('poo')); $this->assertEquals(1, count($r)); $d->destroy(); } public function testMethod_getElementsByName(): void { $d = new Document('
ook
eek
ack
'); $l = $d->getElementsByName('poo💩'); $this->assertEquals(3, $l->length); $this->assertEquals('
ook
', (string)$l[0]); $d->destroy(); } public function testMethod_loadFile(): void { // Test with a local file $d = new Document(); $d->loadFile(__DIR__ . '/../misc/test.html'); $this->assertSame('ISO-2022-JP', $d->charset); $this->assertStringEndsWith('tests/misc/test.html', $d->URL); $d->destroy(); // Test with a virtual stream $d = new Document(); $v = vfsStream::setup('ook', 0777, [ '1.html' => << Ook HTML ]); $d->loadFile($v->url() . '/1.html'); $this->assertSame('ISO-2022-JP', $d->charset); $this->assertStringStartsWith('vfs://', $d->URL); $d->destroy(); // Test with an http stream $d = new Document(); $d->loadFile('https://google.com'); $this->assertSame('https://google.com', $d->documentURI); $d->destroy(); } public function testMethod_offsetExists(): void { $d = new Document(''); $this->assertTrue(isset($d['poo💩'])); $d->destroy(); } public function testMethod_offsetGet(): void { $d = new Document('
'); // Returning an HTMLCollection $this->assertInstanceOf(HTMLCollection::class, $d['poo💩']); $this->assertEquals(3, $d['poo💩']->length); // Returning one element $this->assertInstanceOf(Element::class, $d['ook']); $this->assertSame('IMG', $d['ook']->nodeName); // Returning null $this->assertNull($d['eek']); $d->destroy(); } public function testMethod_registerXPathFunctions(): void { $d = new Document('

Ook

ook

eek

'); $d->registerXPathFunctions(); $r = $d->evaluate('//*[php:functionString("substr", @class, 0, 7) = "poo💩"]', $d); $this->assertEquals(2, $r->length); $d->destroy(); } public function testMethod_register_unregisterXPathNamespaces(): void { $d = new Document('Ook'); $d->registerXPathNamespaces([ 'svg' => Node::SVG_NAMESPACE ]); $r = $d->evaluate('//svg:svg/svg:text/text()', $d); $this->assertSame('Ook', $r[0]->textContent); $d->unregisterXPathNamespaces('svg'); $r = $d->evaluate('//svg:svg/svg:text/text()', $d); $this->assertEquals(0, $r->length); $d->destroy(); } public function testMethod_serializeInner(): void { $d = new Document('Ook'); $this->assertSame('Ook', $d->serializeInner($d->body)); $d->destroy(); } public function testMethod_toString(): void { $d = new Document('Ook'); $this->assertSame('Ook', (string)$d); $d->destroy(); } public function testProperty_body(): void { $d = new Document(); $this->assertNull($d->body); $d->appendChild($d->createElement('html')); $this->assertNull($d->body); $d->documentElement->appendChild($d->createElement('body')); $this->assertNotNull($d->body); $d->destroy(); } public function testProperty_contentType(): void { $d = new Document(); $this->assertSame('text/html', $d->contentType); $d->destroy(); $d = new XMLDocument(); $this->assertSame('application/xml', $d->contentType); $d->destroy(); } public function testProperty_designMode(): void { $d = new Document(); $this->assertSame('off', $d->designMode); $d->designMode = 'on'; $this->assertSame('on', $d->designMode); $d->designMode = 'off'; $this->assertSame('off', $d->designMode); $d->destroy(); } public function testProperty_dir(): void { $d = new Document(''); $this->assertSame('ltr', $d->dir); $d->dir = 'bullshit'; $this->assertSame('', $d->dir); $d->destroy(); } public function testProperty_doctype() { $d = new Document(); $this->assertNull($d->doctype); $doctype = $d->appendChild($d->implementation->createDocumentType('html', '', '')); $this->assertSame($doctype, $d->doctype); $d->destroy(); } public function testProperty_embeds() { $d = new Document('
'); $this->assertEquals(4, $d->embeds->length); $this->assertEquals(4, $d->plugins->length); $d->destroy(); } public function testProperty_forms() { $d = new Document('
'); $this->assertEquals(4, $d->forms->length); $d->destroy(); } public function testProperty_head() { $d = new Document(); $this->assertNull($d->head); $de = $d->appendChild($d->createElement('html')); $head = $de->appendChild($d->createElement('head')); $this->assertSame($head, $d->head); $d->destroy(); } public function testProperty_images() { $d = new Document('
'); $this->assertEquals(4, $d->images->length); $d->destroy(); } public function testProperty_links() { $d = new Document('
'); $this->assertEquals(4, $d->links->length); $d->destroy(); } public function testProperty_scripts() { $d = new Document('
'); $this->assertEquals(4, $d->scripts->length); $d->destroy(); } public function testProperty_title() { $d = new Document(); $this->assertSame('', $d->title); $d->title = 'fail'; $this->assertSame('', $d->title); $d->destroy(); $d = (new DOMImplementation)->createDocument(Node::SVG_NAMESPACE, 'svg'); $this->assertSame('', $d->title); $d->title = 'Ook'; $this->assertSame('Ook', $d->title); $d->title = ' Ee k '; $this->assertSame('Ee k', $d->title); $d->destroy(); $d = new Document(); $de = $d->appendChild($d->createElement('html')); $d->title = 'Ook'; $this->assertSame('', $d->title); $de->appendChild($d->createElement('head')); $d->title = 'Ook'; $this->assertSame('Ook', $d->title); $d->title = 'Eek'; $this->assertSame('Eek', $d->title); } #[DataProvider('provideFatalErrors')] public function testFatalErrors(string $throwableClassName, \Closure $closure): void { $this->expectException($throwableClassName); $d = new Document(); $closure($d); $d->destroy(); } public static function provideFatalErrors(): iterable { $iterable = [ // Attempting to adopt a Document [ NotSupportedError::class, function (Document $d): void { $d->adoptNode($d); } ], // Invalid attribute name [ InvalidCharacterError::class, function (Document $d): void { $d->createAttribute(' '); } ], // Loading into a non-empty document [ NoModificationAllowedError::class, function (Document $d): void { $d->load(''); $d->load('fail'); } ], // Importing a Document [ NotSupportedError::class, function (Document $d): void { $d->importNode(new Document()); } ], // Importing a \DOMElement without an owner document [ NotSupportedError::class, function (Document $d): void { $d->importNode(new \DOMElement('fail')); } ], // Importing a \DOMNode with a non-\DOMDocument owner [ NotSupportedError::class, function (Document $d): void { $d2 = new class extends \DOMDocument {}; $d->importNode($d2->createTextNode('fail')); } ], // Importing a \DOMEntityReference [ NotSupportedError::class, function (Document $d): void { $d2 = new \DOMDocument(); $d->importNode($d2->createEntityReference('nbsp')); } ], // Creating a CDataSection on an HTML document [ NotSupportedError::class, function (Document $d): void { $d->createCDATASection('fail'); } ], // Creating a CDataSection with ']]>' in the data [ InvalidCharacterError::class, function (Document $d): void { $d = new XMLDocument(); $d->createCDATASection(']]>'); $d->destroy(); } ], // Invalid element name [ InvalidCharacterError::class, function (Document $d): void { $d->createElement(' '); } ], // Invalid HTML element name [ InvalidCharacterError::class, function (Document $d): void { $d->createElement('💩poo'); } ], // Invalid XPath expression [ XPathException::class, function (Document $d): void { $d->evaluate(' '); } ], // Undefined namespace prefix [ XPathException::class, function (Document $d): void { $d->evaluate('//svg:svg'); } ], // Loading file when Document isn't empty [ NoModificationAllowedError::class, function (Document $d): void { $d->appendChild($d->createElement('html')); $d->loadFile('fail.html'); } ], // File not found when loading [ FileNotFoundException::class, function (Document $d): void { $d->loadFile('fail.html'); } ], // Isset on an invalid named property [ InvalidArgumentException::class, function (Document $d): void { isset($d[0]); } ], // Getting an invalid named property [ InvalidArgumentException::class, function (Document $d): void { $d[0]; } ], // Invalid prefix array [ InvalidArgumentException::class, function (Document $d): void { $d->registerXPathNamespaces([ 42, 'fail' ]); } ], // Invalid prefix array [ InvalidArgumentException::class, function (Document $d): void { $d->registerXPathNamespaces([ 'fail', 42 ]); } ], // Serializing wrong document [ WrongDocumentError::class, function (Document $d): void { $d2 = new Document(); $e = $d2->createElement('fail'); $d->serialize($e); } ], // Serializing wrong document [ WrongDocumentError::class, function (Document $d): void { $d2 = new Document(); $e = $d2->createElement('fail'); $d->serializeInner($e); } ] ]; foreach ($iterable as $i) { yield $i; } } }