Skip to content

Commit

Permalink
DOMNodeList elements are accessible through array notation
Browse files Browse the repository at this point in the history
Fixes #67949
  • Loading branch information
ralt committed Sep 26, 2014
1 parent 71c3961 commit 9469db9
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 28 deletions.
2 changes: 2 additions & 0 deletions ext/dom/dom_properties.h
Expand Up @@ -137,6 +137,8 @@ int dom_node_text_content_write(dom_object *obj, zval *newval TSRMLS_DC);

/* nodelist properties */
int dom_nodelist_length_read(dom_object *obj, zval **retval TSRMLS_DC);
xmlNodePtr dom_nodelist_xml_item(dom_nnodemap_object *objmap, long index);
xmlNodePtr dom_nodelist_baseobj_item(dom_nnodemap_object *objmap, long index);

/* notation properties */
int dom_notation_public_id_read(dom_object *obj, zval **retval TSRMLS_DC);
Expand Down
71 changes: 44 additions & 27 deletions ext/dom/nodelist.c
Expand Up @@ -98,6 +98,47 @@ int dom_nodelist_length_read(dom_object *obj, zval **retval TSRMLS_DC)

/* }}} */

xmlNodePtr dom_nodelist_xml_item(dom_nnodemap_object *objmap, long index) /* {{{ */
{
xmlNodePtr itemnode = NULL;

if (objmap->nodetype == XML_ENTITY_NODE) {
itemnode = php_dom_libxml_hash_iter(objmap->ht, index);
} else {
itemnode = php_dom_libxml_notation_iter(objmap->ht, index);
}

return itemnode;
} /* }}} end dom_nodelist_xml_item */

xmlNodePtr dom_nodelist_baseobj_item(dom_nnodemap_object *objmap, long index) /* {{{ */
{
xmlNodePtr itemnode = NULL;
xmlNodePtr nodep, curnode;
int count = 0;

nodep = dom_object_get_node(objmap->baseobj);
if (nodep) {
if (objmap->nodetype == XML_ATTRIBUTE_NODE || objmap->nodetype == XML_ELEMENT_NODE) {
curnode = nodep->children;
while (count < index && curnode != NULL) {
count++;
curnode = curnode->next;
}
itemnode = curnode;
} else {
if (nodep->type == XML_DOCUMENT_NODE || nodep->type == XML_HTML_DOCUMENT_NODE) {
nodep = xmlDocGetRootElement((xmlDoc *) nodep);
} else {
nodep = nodep->children;
}
itemnode = dom_get_elements_by_tag_name_ns_raw(nodep, (char *) objmap->ns, (char *) objmap->local, &count, index);
}
}

return itemnode;
} /* }}} end dom_nodelist_baseobj_item */

/* {{{ proto DOMNode dom_nodelist_item(int index);
URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-844377136
Since:
Expand All @@ -111,8 +152,6 @@ PHP_FUNCTION(dom_nodelist_item)
xmlNodePtr itemnode = NULL;

dom_nnodemap_object *objmap;
xmlNodePtr nodep, curnode;
int count = 0;
HashTable *nodeht;
zval **entry;

Expand All @@ -126,38 +165,16 @@ PHP_FUNCTION(dom_nodelist_item)
objmap = (dom_nnodemap_object *)intern->ptr;
if (objmap != NULL) {
if (objmap->ht) {
if (objmap->nodetype == XML_ENTITY_NODE) {
itemnode = php_dom_libxml_hash_iter(objmap->ht, index);
} else {
itemnode = php_dom_libxml_notation_iter(objmap->ht, index);
}
itemnode = dom_nodelist_xml_item(objmap, index);
} else {
if (objmap->nodetype == DOM_NODESET) {
nodeht = HASH_OF(objmap->baseobjptr);
if (zend_hash_index_find(nodeht, index, (void **) &entry)==SUCCESS) {
*return_value = **entry;
zval_copy_ctor(return_value);
MAKE_COPY_ZVAL(entry, return_value);
return;
}
} else if (objmap->baseobj) {
nodep = dom_object_get_node(objmap->baseobj);
if (nodep) {
if (objmap->nodetype == XML_ATTRIBUTE_NODE || objmap->nodetype == XML_ELEMENT_NODE) {
curnode = nodep->children;
while (count < index && curnode != NULL) {
count++;
curnode = curnode->next;
}
itemnode = curnode;
} else {
if (nodep->type == XML_DOCUMENT_NODE || nodep->type == XML_HTML_DOCUMENT_NODE) {
nodep = xmlDocGetRootElement((xmlDoc *) nodep);
} else {
nodep = nodep->children;
}
itemnode = dom_get_elements_by_tag_name_ns_raw(nodep, objmap->ns, objmap->local, &count, index);
}
}
itemnode = dom_nodelist_baseobj_item(objmap, index);
}
}
}
Expand Down
84 changes: 83 additions & 1 deletion ext/dom/php_dom.c
Expand Up @@ -72,6 +72,7 @@ zend_class_entry *dom_namespace_node_class_entry;
/* }}} */

zend_object_handlers dom_object_handlers;
zend_object_handlers dom_nnodemap_object_handlers;

static HashTable classes;
/* {{{ prop handler tables */
Expand Down Expand Up @@ -668,6 +669,10 @@ PHP_MINIT_FUNCTION(dom)
dom_object_handlers.has_property = dom_property_exists;
dom_object_handlers.get_debug_info = dom_get_debug_info;

memcpy(&dom_nnodemap_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers));
dom_nnodemap_object_handlers.read_dimension = dom_nodelist_read_dimension;
dom_nnodemap_object_handlers.has_dimension = dom_nodelist_has_dimension;

zend_hash_init(&classes, 0, NULL, NULL, 1);

INIT_CLASS_ENTRY(ce, "DOMException", php_dom_domexception_class_functions);
Expand Down Expand Up @@ -1297,7 +1302,7 @@ zend_object_value dom_nnodemap_objects_new(zend_class_entry *class_type TSRMLS_D

retval.handle = zend_objects_store_put(intern, dom_nnodemap_object_dtor, (zend_objects_free_object_storage_t)dom_nnodemap_objects_free_storage, dom_objects_clone TSRMLS_CC);
intern->handle = retval.handle;
retval.handlers = dom_get_obj_handlers(TSRMLS_C);
retval.handlers = &dom_nnodemap_object_handlers;

return retval;
}
Expand Down Expand Up @@ -1674,6 +1679,83 @@ xmlNsPtr dom_get_nsdecl(xmlNode *node, xmlChar *localName) {
}
/* }}} end dom_get_nsdecl */

static int dom_nodelist_fetch_dimension(xmlNodePtr *itemnode, zval *offset, dom_nnodemap_object *objmap, zval *rv TSRMLS_DC) /* {{{ */
{
convert_to_long(offset);
long index = Z_LVAL_P(offset);
HashTable *nodeht;
zval **entry;
int ret = 0;

if (objmap->ht) {
*itemnode = dom_nodelist_xml_item(objmap, index);
} else {
if (objmap->nodetype == DOM_NODESET) {
nodeht = HASH_OF(objmap->baseobjptr);
if (zend_hash_index_find(nodeht, index, (void **) &entry) == SUCCESS) {
if (itemnode != NULL && rv != NULL) {
/* Passed by read_dimension */
MAKE_COPY_ZVAL(entry, rv);
}
ret = 1;
}
} else if (objmap->baseobj) {
if (itemnode == NULL && rv == NULL) {
/* Passed by has_dimension */
if (dom_nodelist_baseobj_item(objmap, index)) {
ret = 1;
}
} else {
*itemnode = dom_nodelist_baseobj_item(objmap, index);
}
}
}

if (rv != NULL && itemnode != NULL) {
if (*itemnode) {
ret = 1;
}
}

return ret;
} /* }}} end dom_nodelist_fetch_dimension */

zval *dom_nodelist_read_dimension(zval *object, zval *offset, int type TSRMLS_DC) /* {{{ */
{
dom_object *intern;
xmlNodePtr itemnode = NULL;
dom_nnodemap_object *objmap;
zval *rv;
int found;

ALLOC_INIT_ZVAL(rv);

intern = (dom_object *) zend_object_store_get_object(object TSRMLS_CC);

objmap = (dom_nnodemap_object *)intern->ptr;

if (dom_nodelist_fetch_dimension(&itemnode, offset, objmap, rv TSRMLS_CC)) {
if (itemnode) {
php_dom_create_object(itemnode, &found, rv, objmap->baseobj TSRMLS_CC);
}
}

Z_DELREF_P(rv);

return rv;
} /* }}} end dom_nodelist_read_dimension */

int dom_nodelist_has_dimension(zval *object, zval *member, int check_empty TSRMLS_DC)
{
dom_object *intern;
dom_nnodemap_object *objmap;

intern = (dom_object *) zend_object_store_get_object(object TSRMLS_CC);
objmap = (dom_nnodemap_object *)intern->ptr;

return dom_nodelist_fetch_dimension(NULL, member, objmap, NULL TSRMLS_CC);
} /* }}} end dom_nodelist_has_dimension */

#endif /* HAVE_DOM */

/*
Expand Down
3 changes: 3 additions & 0 deletions ext/dom/php_dom.h
Expand Up @@ -123,6 +123,9 @@ xmlNode *php_dom_libxml_hash_iter(xmlHashTable *ht, int index);
xmlNode *php_dom_libxml_notation_iter(xmlHashTable *ht, int index);
zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC);
int dom_set_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece, zend_class_entry *ce TSRMLS_DC);
zval *dom_nodelist_read_dimension(zval *object, zval *offset, int type TSRMLS_DC);
int dom_nodelist_has_dimension(zval *object, zval *member, int check_empty TSRMLS_DC);
static int dom_nodelist_fetch_dimension(xmlNodePtr *itemnode, zval *offset, dom_nnodemap_object *objmap, zval *rv TSRMLS_DC);

#define REGISTER_DOM_CLASS(ce, name, parent_ce, funcs, entry) \
INIT_CLASS_ENTRY(ce, name, funcs); \
Expand Down
20 changes: 20 additions & 0 deletions ext/dom/tests/bug67949.phpt
@@ -0,0 +1,20 @@
--TEST--
Bug #67949: DOMNodeList elements should be accessible through array notation
--FILE--
<?php

$html = <<<HTML
<div>data</div>
HTML;
$doc = new DOMDocument;
$doc->loadHTML($html);
var_dump($doc->getElementsByTagName('div')[0]->textContent);
var_dump($doc->getElementsByTagName('div')['test']->textContent); // testing that weak casting works
var_dump(isset($doc->getElementsByTagName('div')[0]));
var_dump(isset($doc->getElementsByTagName('div')[1]));

--EXPECT--
string(4) "data"
string(4) "data"
bool(true)
bool(false)

0 comments on commit 9469db9

Please sign in to comment.