Skip to content

Commit a0da32a

Browse files
authored
Cleanup and optimize attribute value reading (php#13897)
When the attribute has a single text child, we can avoid an allocating call to libxml2 and read the contents directly. On my i7-4790, I tested the optimization with both the $value and $nodeValue property. ``` Summary ./sapi/cli/php bench_value.php ran 1.82 ± 0.09 times faster than ./sapi/cli/php_old bench_value.php Summary ./sapi/cli/php bench_nodeValue.php ran 1.78 ± 0.10 times faster than ./sapi/cli/php_old bench_nodeValue.php ``` Test code: ``` $dom = new DOMDocument; $dom->loadXML('<root attrib="this is a relatively short text"/>'); $attrib = $dom->documentElement->attributes[0]; for ($i=0; $i<1000*1000; $i++) { $attrib->value; // or nodeValue } ```
1 parent 0086815 commit a0da32a

File tree

2 files changed

+47
-33
lines changed

2 files changed

+47
-33
lines changed

ext/dom/attr.c

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -113,19 +113,9 @@ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-22
113113
*/
114114
zend_result dom_attr_value_read(dom_object *obj, zval *retval)
115115
{
116-
DOM_PROP_NODE(xmlAttrPtr, attrp, obj);
117-
xmlChar *content;
118-
119-
/* Can't avoid a content copy because it's an attribute node */
120-
if ((content = xmlNodeGetContent((xmlNodePtr) attrp)) != NULL) {
121-
ZVAL_STRING(retval, (char *) content);
122-
xmlFree(content);
123-
} else {
124-
ZVAL_EMPTY_STRING(retval);
125-
}
126-
116+
DOM_PROP_NODE(xmlNodePtr, attrp, obj);
117+
php_dom_get_content_into_zval(attrp, retval, false);
127118
return SUCCESS;
128-
129119
}
130120

131121
zend_result dom_attr_value_write(dom_object *obj, zval *newval)

ext/dom/php_dom.c

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2255,36 +2255,60 @@ void dom_remove_all_children(xmlNodePtr nodep)
22552255
}
22562256
}
22572257

2258-
void php_dom_get_content_into_zval(const xmlNode *nodep, zval *retval, bool default_is_null)
2258+
void php_dom_get_content_into_zval(const xmlNode *nodep, zval *return_value, bool null_on_failure)
22592259
{
22602260
ZEND_ASSERT(nodep != NULL);
22612261

2262-
if (nodep->type == XML_TEXT_NODE
2263-
|| nodep->type == XML_CDATA_SECTION_NODE
2264-
|| nodep->type == XML_PI_NODE
2265-
|| nodep->type == XML_COMMENT_NODE) {
2266-
char *str = (char * ) nodep->content;
2267-
if (str != NULL) {
2268-
ZVAL_STRING(retval, str);
2269-
} else {
2270-
goto failure;
2262+
switch (nodep->type) {
2263+
case XML_TEXT_NODE:
2264+
case XML_CDATA_SECTION_NODE:
2265+
case XML_PI_NODE:
2266+
case XML_COMMENT_NODE: {
2267+
char *str = (char * ) nodep->content;
2268+
if (str != NULL) {
2269+
RETURN_STRING(str);
2270+
}
2271+
2272+
break;
22712273
}
2272-
return;
2273-
}
22742274

2275-
char *str = (char *) xmlNodeGetContent(nodep);
2275+
case XML_ATTRIBUTE_NODE: {
2276+
/* For attributes we can also have an optimized fast-path.
2277+
* This fast-path is only possible in the (common) case where the attribute
2278+
* has a single text child. Note that if the child or the content is NULL, this
2279+
* is equivalent to not having content (i.e. the attribute has the empty string as value). */
22762280

2277-
if (str != NULL) {
2278-
ZVAL_STRING(retval, str);
2279-
xmlFree(str);
2280-
return;
2281+
if (nodep->children == NULL) {
2282+
RETURN_EMPTY_STRING();
2283+
}
2284+
2285+
if (nodep->children->type == XML_TEXT_NODE && nodep->children->next == NULL) {
2286+
if (nodep->children->content == NULL) {
2287+
RETURN_EMPTY_STRING();
2288+
} else {
2289+
RETURN_STRING((const char *) nodep->children->content);
2290+
}
2291+
}
2292+
2293+
ZEND_FALLTHROUGH;
2294+
}
2295+
2296+
default: {
2297+
char *str = (char *) xmlNodeGetContent(nodep);
2298+
if (str != NULL) {
2299+
RETVAL_STRING(str);
2300+
xmlFree(str);
2301+
return;
2302+
}
2303+
2304+
break;
2305+
}
22812306
}
22822307

2283-
failure:
2284-
if (default_is_null) {
2285-
ZVAL_NULL(retval);
2308+
if (null_on_failure) {
2309+
RETURN_NULL();
22862310
} else {
2287-
ZVAL_EMPTY_STRING(retval);
2311+
RETURN_EMPTY_STRING();
22882312
}
22892313
}
22902314

0 commit comments

Comments
 (0)