Implement HTMLCollection::namedItem()

This commit is contained in:
Niels Dossche 2024-04-10 17:22:21 +02:00
parent 5c69b2e86f
commit ac039cf716
No known key found for this signature in database
GPG key ID: B8A8AD166DF0E2E5
8 changed files with 137 additions and 4 deletions

View file

@ -229,4 +229,15 @@ xmlChar *dom_attr_value(const xmlAttr *attr, bool *free)
return value; return value;
} }
bool dom_compare_value(const xmlAttr *attr, const xmlChar *value)
{
bool free;
xmlChar *attr_value = dom_attr_value(attr, &free);
bool result = xmlStrEqual(attr_value, value);
if (free) {
xmlFree(attr_value);
}
return result;
}
#endif #endif

View file

@ -32,7 +32,7 @@ if test "$PHP_DOM" != "no"; then
documentfragment.c domimplementation.c \ documentfragment.c domimplementation.c \
element.c node.c characterdata.c \ element.c node.c characterdata.c \
documenttype.c entity.c \ documenttype.c entity.c \
nodelist.c text.c comment.c \ nodelist.c html_collection.c text.c comment.c \
entityreference.c \ entityreference.c \
notation.c xpath.c dom_iterators.c \ notation.c xpath.c dom_iterators.c \
namednodemap.c xpath_callbacks.c \ namednodemap.c xpath_callbacks.c \

View file

@ -12,7 +12,7 @@ if (PHP_DOM == "yes") {
domexception.c parentnode.c processinginstruction.c \ domexception.c parentnode.c processinginstruction.c \
cdatasection.c documentfragment.c domimplementation.c element.c \ cdatasection.c documentfragment.c domimplementation.c element.c \
node.c characterdata.c documenttype.c \ node.c characterdata.c documenttype.c \
entity.c nodelist.c text.c comment.c \ entity.c nodelist.c html_collection.c text.c comment.c \
entityreference.c \ entityreference.c \
notation.c xpath.c dom_iterators.c \ notation.c xpath.c dom_iterators.c \
namednodemap.c xpath_callbacks.c", null, "-Iext/dom/lexbor"); namednodemap.c xpath_callbacks.c", null, "-Iext/dom/lexbor");

74
ext/dom/html_collection.c Normal file
View file

@ -0,0 +1,74 @@
/*
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| https://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Niels Dossche <nielsdos@php.net> |
+----------------------------------------------------------------------+
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
#include "php_dom.h"
#include "namespace_compat.h"
/* https://dom.spec.whatwg.org/#dom-htmlcollection-nameditem-key */
PHP_METHOD(DOM_HTMLCollection, namedItem)
{
zend_string *key;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_PATH_STR(key)
ZEND_PARSE_PARAMETERS_END();
/* 1. If key is the empty string, return null. */
if (ZSTR_LEN(key) == 0) {
RETURN_NULL();
}
dom_object *intern = Z_DOMOBJ_P(ZEND_THIS);
dom_nnodemap_object *objmap = intern->ptr;
/* 2. Return the first element in the collection for which at least one of the following is true: */
xmlNodePtr basep = dom_object_get_node(objmap->baseobj);
if (basep != NULL) {
int cur = 0;
int next = cur;
xmlNodePtr candidate = basep->children;
while (candidate != NULL) {
candidate = dom_get_elements_by_tag_name_ns_raw(basep, candidate, objmap->ns, objmap->local, objmap->local_lower, &cur, next);
if (candidate == NULL) {
break;
}
xmlAttrPtr attr;
/* it has an ID which is key; */
if ((attr = xmlHasNsProp(candidate, BAD_CAST "id", NULL)) != NULL && dom_compare_value(attr, BAD_CAST ZSTR_VAL(key))) {
DOM_RET_OBJ(candidate, objmap->baseobj);
return;
}
/* it is in the HTML namespace and has a name attribute whose value is key; */
else if (php_dom_ns_is_fast(candidate, php_dom_ns_is_html_magic_token)) {
if ((attr = xmlHasNsProp(candidate, BAD_CAST "name", NULL)) != NULL && dom_compare_value(attr, BAD_CAST ZSTR_VAL(key))) {
DOM_RET_OBJ(candidate, objmap->baseobj);
return;
}
}
next = cur + 1;
}
}
}
#endif

View file

@ -175,6 +175,7 @@ dom_object *php_dom_instantiate_object_helper(zval *return_value, zend_class_ent
xmlDocPtr php_dom_create_html_doc(void); xmlDocPtr php_dom_create_html_doc(void);
xmlChar *dom_attr_value(const xmlAttr *attr, bool *free); xmlChar *dom_attr_value(const xmlAttr *attr, bool *free);
bool dom_compare_value(const xmlAttr *attr, const xmlChar *value);
typedef enum { typedef enum {
DOM_LOAD_STRING = 0, DOM_LOAD_STRING = 0,

View file

@ -1279,7 +1279,7 @@ namespace DOM
/** @implementation-alias DOMNodeList::item */ /** @implementation-alias DOMNodeList::item */
public function item(int $index): ?Element {} public function item(int $index): ?Element {}
/* TODO: implement namedItem */ public function namedItem(string $key): ?Element {}
/** @implementation-alias DOMNodeList::count */ /** @implementation-alias DOMNodeList::count */
public function count(): int {} public function count(): int {}

View file

@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead. /* This is a generated file, edit the .stub.php file instead.
* Stub hash: f441c789fdce91e8fc71f450b294c11059999af1 */ * Stub hash: 37a1c811bfc8c611d686f0842d06fc327b54511f */
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_dom_import_simplexml, 0, 1, DOMElement, 0) ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_dom_import_simplexml, 0, 1, DOMElement, 0)
ZEND_ARG_TYPE_INFO(0, node, IS_OBJECT, 0) ZEND_ARG_TYPE_INFO(0, node, IS_OBJECT, 0)
@ -721,6 +721,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_HTMLCollection_item, 0,
ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0)
ZEND_END_ARG_INFO() ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_HTMLCollection_namedItem, 0, 1, DOM\\Element, 1)
ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
ZEND_END_ARG_INFO()
#define arginfo_class_DOM_HTMLCollection_count arginfo_class_DOM_Node_getLineNo #define arginfo_class_DOM_HTMLCollection_count arginfo_class_DOM_Node_getLineNo
#define arginfo_class_DOM_HTMLCollection_getIterator arginfo_class_DOMNodeList_getIterator #define arginfo_class_DOM_HTMLCollection_getIterator arginfo_class_DOMNodeList_getIterator
@ -1256,6 +1260,7 @@ ZEND_METHOD(DOM_Node, appendChild);
ZEND_METHOD(DOM_Node, replaceChild); ZEND_METHOD(DOM_Node, replaceChild);
ZEND_METHOD(DOM_Node, removeChild); ZEND_METHOD(DOM_Node, removeChild);
ZEND_METHOD(DOM_Node, getNodePath); ZEND_METHOD(DOM_Node, getNodePath);
ZEND_METHOD(DOM_HTMLCollection, namedItem);
ZEND_METHOD(DOM_Element, removeAttribute); ZEND_METHOD(DOM_Element, removeAttribute);
ZEND_METHOD(DOM_Element, setAttributeNodeNS); ZEND_METHOD(DOM_Element, setAttributeNodeNS);
ZEND_METHOD(DOM_Element, removeAttributeNode); ZEND_METHOD(DOM_Element, removeAttributeNode);
@ -1620,6 +1625,7 @@ static const zend_function_entry class_DOM_DTDNamedNodeMap_methods[] = {
static const zend_function_entry class_DOM_HTMLCollection_methods[] = { static const zend_function_entry class_DOM_HTMLCollection_methods[] = {
ZEND_RAW_FENTRY("item", zim_DOMNodeList_item, arginfo_class_DOM_HTMLCollection_item, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("item", zim_DOMNodeList_item, arginfo_class_DOM_HTMLCollection_item, ZEND_ACC_PUBLIC, NULL, NULL)
ZEND_ME(DOM_HTMLCollection, namedItem, arginfo_class_DOM_HTMLCollection_namedItem, ZEND_ACC_PUBLIC)
ZEND_RAW_FENTRY("count", zim_DOMNodeList_count, arginfo_class_DOM_HTMLCollection_count, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("count", zim_DOMNodeList_count, arginfo_class_DOM_HTMLCollection_count, ZEND_ACC_PUBLIC, NULL, NULL)
ZEND_RAW_FENTRY("getIterator", zim_DOMNodeList_getIterator, arginfo_class_DOM_HTMLCollection_getIterator, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("getIterator", zim_DOMNodeList_getIterator, arginfo_class_DOM_HTMLCollection_getIterator, ZEND_ACC_PUBLIC, NULL, NULL)
ZEND_FE_END ZEND_FE_END

View file

@ -0,0 +1,41 @@
--TEST--
HTMLCollection::namedItem() and dimension handling for named accesses
--EXTENSIONS--
dom
--FILE--
<?php
$xml = <<<XML
<!DOCTYPE root [
<!ENTITY ent "test">
]>
<root>
<node name="foo">1</node>
<x id="foo">2</x>
<x id="foo&ent;">2 with entity</x>
<node test:id="foo" xmlns:test="http://example.com">3</node>
<node id="wrong">4</node>
<node id="foo">5</node>
<node name="bar">without html ns</node>
<node name="bar" xmlns="http://www.w3.org/1999/xhtml">with html ns</node>
</root>
XML;
$dom = DOM\XMLDocument::createFromString($xml);
var_dump($dom->getElementsByTagName('node')->namedItem('foo')?->textContent);
var_dump($dom->getElementsByTagName('node')->namedItem('')?->textContent);
var_dump($dom->getElementsByTagName('node')->namedItem('does not exist')?->textContent);
var_dump($dom->getElementsByTagName('node')->namedItem('wrong')?->textContent);
var_dump($dom->getElementsByTagName('node')->namedItem('bar')?->textContent);
var_dump($dom->getElementsByTagName('x')->namedItem('foo')?->textContent);
var_dump($dom->getElementsByTagName('x')->namedItem('footest')?->textContent);
?>
--EXPECT--
string(1) "5"
NULL
NULL
string(1) "4"
string(12) "with html ns"
string(1) "2"
string(13) "2 with entity"