Skip to content

Commit 402b1c2

Browse files
committed
1 parent f0e1847 commit 402b1c2

File tree

9 files changed

+131
-13
lines changed

9 files changed

+131
-13
lines changed

ext/dom/dom_properties.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ zend_result dom_element_id_write(dom_object *obj, zval *newval);
8686
zend_result dom_element_schema_type_info_read(dom_object *obj, zval *retval);
8787
zend_result dom_element_inner_html_read(dom_object *obj, zval *retval);
8888
zend_result dom_element_inner_html_write(dom_object *obj, zval *newval);
89+
zend_result dom_element_outer_html_read(dom_object *obj, zval *retval);
8990
zend_result dom_element_class_list_read(dom_object *obj, zval *retval);
9091
zend_result dom_modern_element_substituted_node_value_read(dom_object *obj, zval *retval);
9192
zend_result dom_modern_element_substituted_node_value_write(dom_object *obj, zval *newval);

ext/dom/inner_html_mixin.c

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,9 @@ static int dom_write_smart_str(void *context, const char *buffer, int len)
5555
return len;
5656
}
5757

58-
/* https://w3c.github.io/DOM-Parsing/#the-innerhtml-mixin
59-
* and https://w3c.github.io/DOM-Parsing/#dfn-fragment-serializing-algorithm */
60-
zend_result dom_element_inner_html_read(dom_object *obj, zval *retval)
58+
/* https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#fragment-serializing-algorithm-steps */
59+
static zend_string *dom_element_html_fragment_serialize(dom_object *obj, xmlNodePtr node)
6160
{
62-
DOM_PROP_NODE(xmlNodePtr, node, obj);
63-
6461
/* 1. Let context document be the value of node's node document. */
6562
const xmlDoc *context_document = node->doc;
6663

@@ -73,7 +70,7 @@ zend_result dom_element_inner_html_read(dom_object *obj, zval *retval)
7370
ctx.write_string = dom_inner_html_write_string;
7471
ctx.write_string_len = dom_inner_html_write_string_len;
7572
dom_html5_serialize(&ctx, node);
76-
ZVAL_STR(retval, smart_str_extract(&output));
73+
return smart_str_extract(&output);
7774
}
7875
/* 3. Otherwise, context document is an XML document; return an XML serialization of node passing the flag require well-formed. */
7976
else {
@@ -104,11 +101,21 @@ zend_result dom_element_inner_html_read(dom_object *obj, zval *retval)
104101
if (UNEXPECTED(status < 0)) {
105102
smart_str_free_ex(&str, false);
106103
php_dom_throw_error_with_message(SYNTAX_ERR, "The resulting XML serialization is not well-formed", true);
107-
return FAILURE;
104+
return NULL;
108105
}
109-
ZVAL_STR(retval, smart_str_extract(&str));
106+
return smart_str_extract(&str);
110107
}
108+
}
111109

110+
/* https://w3c.github.io/DOM-Parsing/#the-innerhtml-mixin */
111+
zend_result dom_element_inner_html_read(dom_object *obj, zval *retval)
112+
{
113+
DOM_PROP_NODE(xmlNodePtr, node, obj);
114+
zend_string *serialization = dom_element_html_fragment_serialize(obj, node);
115+
if (serialization == NULL) {
116+
return FAILURE;
117+
}
118+
ZVAL_STR(retval, serialization);
112119
return SUCCESS;
113120
}
114121

@@ -363,4 +370,31 @@ zend_result dom_element_inner_html_write(dom_object *obj, zval *newval)
363370
return php_dom_pre_insert(obj->document, fragment, context_node, NULL) ? SUCCESS : FAILURE;
364371
}
365372

373+
/* https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#the-outerhtml-property */
374+
zend_result dom_element_outer_html_read(dom_object *obj, zval *retval)
375+
{
376+
DOM_PROP_NODE(xmlNodePtr, this, obj);
377+
378+
/* 1. Let element be a fictional node whose only child is this. */
379+
xmlNode element;
380+
memset(&element, 0, sizeof(element));
381+
element.type = XML_DOCUMENT_FRAG_NODE;
382+
element.children = element.last = this;
383+
element.doc = this->doc;
384+
385+
xmlNodePtr old_parent = this->parent;
386+
this->parent = &element;
387+
388+
/* 2. Return the result of running fragment serializing algorithm steps with element and true. */
389+
zend_string *serialization = dom_element_html_fragment_serialize(obj, &element);
390+
391+
this->parent = old_parent;
392+
393+
if (serialization == NULL) {
394+
return FAILURE;
395+
}
396+
ZVAL_STR(retval, serialization);
397+
return SUCCESS;
398+
}
399+
366400
#endif

ext/dom/php_dom.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,6 +1121,7 @@ PHP_MINIT_FUNCTION(dom)
11211121
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "previousElementSibling", dom_node_previous_element_sibling_read, NULL);
11221122
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "nextElementSibling", dom_node_next_element_sibling_read, NULL);
11231123
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "innerHTML", dom_element_inner_html_read, dom_element_inner_html_write);
1124+
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "outerHTML", dom_element_outer_html_read, NULL);
11241125
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "substitutedNodeValue", dom_modern_element_substituted_node_value_read, dom_modern_element_substituted_node_value_write);
11251126
zend_hash_merge(&dom_modern_element_prop_handlers, &dom_modern_node_prop_handlers, NULL, false);
11261127
DOM_OVERWRITE_PROP_HANDLER(&dom_modern_element_prop_handlers, "textContent", dom_node_text_content_read, dom_node_text_content_write);

ext/dom/php_dom.stub.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,6 +1688,9 @@ public function matches(string $selectors): bool {}
16881688
/** @virtual */
16891689
public string $innerHTML;
16901690

1691+
/** @virtual */
1692+
public string $outerHTML;
1693+
16911694
/** @virtual */
16921695
public string $substitutedNodeValue;
16931696

ext/dom/php_dom_arginfo.h

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/dom/tests/gh15192.phpt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ $element = $dom2->firstChild;
1010
$dom = new DomDocument();
1111
var_dump($element);
1212
?>
13-
--EXPECTF--
14-
object(Dom\HTMLElement)#3 (29) {
13+
--EXPECT--
14+
object(Dom\HTMLElement)#3 (30) {
1515
["namespaceURI"]=>
1616
string(28) "http://www.w3.org/1999/xhtml"
1717
["prefix"]=>
@@ -40,6 +40,8 @@ object(Dom\HTMLElement)#3 (29) {
4040
NULL
4141
["innerHTML"]=>
4242
string(36) "<head></head><body><p>foo</p></body>"
43+
["outerHTML"]=>
44+
string(49) "<html><head></head><body><p>foo</p></body></html>"
4345
["substitutedNodeValue"]=>
4446
string(3) "foo"
4547
["nodeType"]=>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
Test reading Element::$outerHTML on HTML documents
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
$dom = Dom\HTMLDocument::createFromString('<p>foo</p>', LIBXML_NOERROR);
9+
10+
$p = $dom->body->firstChild;
11+
var_dump($p->outerHTML);
12+
13+
$root = $dom->documentElement;
14+
var_dump($root->outerHTML);
15+
16+
$unattached_element = $dom->createElement('unattached');
17+
var_dump($unattached_element->outerHTML);
18+
19+
$template = $dom->createElement('template');
20+
$template->innerHTML = '<p>foo</p>';
21+
var_dump($template->outerHTML);
22+
23+
?>
24+
--EXPECT--
25+
string(10) "<p>foo</p>"
26+
string(49) "<html><head></head><body><p>foo</p></body></html>"
27+
string(25) "<unattached></unattached>"
28+
string(31) "<template><p>foo</p></template>"

ext/dom/tests/modern/xml/Element_innerHTML_reading.phpt renamed to ext/dom/tests/modern/xml/Element_innerOuterHTML_reading.phpt

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
Test reading Element::$innerHTML on XML documents
2+
Test reading Element::${inner,outer}HTML on XML documents
33
--EXTENSIONS--
44
dom
55
--FILE--
@@ -16,43 +16,56 @@ function createContainer() {
1616
$container = createContainer();
1717
$container->append("Hello, world!");
1818
var_dump($container->innerHTML);
19+
var_dump($container->outerHTML);
1920

2021
$container = createContainer();
2122
$container->append($dom->createComment("This is -a- comment"));
2223
var_dump($container->innerHTML);
24+
var_dump($container->outerHTML);
2325

2426
$container = createContainer();
2527
// Note: intentionally typo'd to check whether the string matching against "xml" happens correctly
2628
// i.e. no bugs with prefix-matching only.
2729
$container->append($dom->createProcessingInstruction("xmll", ""));
2830
var_dump($container->innerHTML);
31+
var_dump($container->outerHTML);
2932

3033
$container = createContainer();
3134
$container->append($dom->createProcessingInstruction("almostmalformed", ">?"));
3235
var_dump($container->innerHTML);
36+
var_dump($container->outerHTML);
3337

3438
$container = createContainer();
3539
$element = $container->appendChild(createContainer());
3640
$element->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns', 'http://example.com/');
3741
var_dump($container->innerHTML);
42+
var_dump($container->outerHTML);
3843

3944
$container = createContainer();
4045
$element = $container->appendChild(createContainer());
4146
$element->setAttributeNS('urn:a', 'name', '');
4247
$element->setAttributeNS('urn:b', 'name', '');
4348
var_dump($container->innerHTML);
49+
var_dump($container->outerHTML);
4450

4551
$dom = DOM\XMLDocument::createFromFile(__DIR__ . '/../../book.xml');
4652
var_dump($dom->documentElement->innerHTML);
53+
var_dump($dom->documentElement->outerHTML);
4754

4855
?>
4956
--EXPECT--
5057
string(13) "Hello, world!"
58+
string(36) "<container>Hello, world!</container>"
5159
string(26) "<!--This is -a- comment-->"
60+
string(49) "<container><!--This is -a- comment--></container>"
5261
string(9) "<?xmll ?>"
62+
string(32) "<container><?xmll ?></container>"
5363
string(22) "<?almostmalformed >??>"
64+
string(45) "<container><?almostmalformed >??></container>"
5465
string(12) "<container/>"
66+
string(35) "<container><container/></container>"
5567
string(72) "<container xmlns:ns1="urn:a" ns1:name="" xmlns:ns2="urn:b" ns2:name=""/>"
68+
string(95) "<container><container xmlns:ns1="urn:a" ns1:name="" xmlns:ns2="urn:b" ns2:name=""/></container>"
5669
string(167) "
5770
<book>
5871
<title>The Grapes of Wrath</title>
@@ -63,3 +76,13 @@ string(167) "
6376
<author>John Steinbeck</author>
6477
</book>
6578
"
79+
string(182) "<books>
80+
<book>
81+
<title>The Grapes of Wrath</title>
82+
<author>John Steinbeck</author>
83+
</book>
84+
<book>
85+
<title>The Pearl</title>
86+
<author>John Steinbeck</author>
87+
</book>
88+
</books>"

ext/dom/tests/modern/xml/Element_innerHTML_reading_errors.phpt renamed to ext/dom/tests/modern/xml/Element_innerOuterHTML_reading_errors.phpt

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
Test reading Element::$innerHTML on XML documents - error cases
2+
Test reading Element::${inner,outer}HTML on XML documents - error cases
33
--EXTENSIONS--
44
dom
55
--FILE--
@@ -19,6 +19,11 @@ function test($container) {
1919
} catch (DOMException $e) {
2020
echo $e->getMessage(), "\n";
2121
}
22+
try {
23+
var_dump($container->outerHTML);
24+
} catch (DOMException $e) {
25+
echo $e->getMessage(), "\n";
26+
}
2227
}
2328

2429
$container = createContainer();
@@ -106,3 +111,18 @@ The resulting XML serialization is not well-formed
106111
The resulting XML serialization is not well-formed
107112
The resulting XML serialization is not well-formed
108113
The resulting XML serialization is not well-formed
114+
The resulting XML serialization is not well-formed
115+
The resulting XML serialization is not well-formed
116+
The resulting XML serialization is not well-formed
117+
The resulting XML serialization is not well-formed
118+
The resulting XML serialization is not well-formed
119+
The resulting XML serialization is not well-formed
120+
The resulting XML serialization is not well-formed
121+
The resulting XML serialization is not well-formed
122+
The resulting XML serialization is not well-formed
123+
The resulting XML serialization is not well-formed
124+
The resulting XML serialization is not well-formed
125+
The resulting XML serialization is not well-formed
126+
The resulting XML serialization is not well-formed
127+
The resulting XML serialization is not well-formed
128+
The resulting XML serialization is not well-formed

0 commit comments

Comments
 (0)