Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DOMNodeList elements are accessible through array notation #846

Merged
merged 1 commit into from Oct 9, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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)