Skip to content

Commit ac039cf

Browse files
committed
Implement HTMLCollection::namedItem()
1 parent 5c69b2e commit ac039cf

File tree

8 files changed

+137
-4
lines changed

8 files changed

+137
-4
lines changed

ext/dom/attr.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,4 +229,15 @@ xmlChar *dom_attr_value(const xmlAttr *attr, bool *free)
229229
return value;
230230
}
231231

232+
bool dom_compare_value(const xmlAttr *attr, const xmlChar *value)
233+
{
234+
bool free;
235+
xmlChar *attr_value = dom_attr_value(attr, &free);
236+
bool result = xmlStrEqual(attr_value, value);
237+
if (free) {
238+
xmlFree(attr_value);
239+
}
240+
return result;
241+
}
242+
232243
#endif

ext/dom/config.m4

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ if test "$PHP_DOM" != "no"; then
3232
documentfragment.c domimplementation.c \
3333
element.c node.c characterdata.c \
3434
documenttype.c entity.c \
35-
nodelist.c text.c comment.c \
35+
nodelist.c html_collection.c text.c comment.c \
3636
entityreference.c \
3737
notation.c xpath.c dom_iterators.c \
3838
namednodemap.c xpath_callbacks.c \

ext/dom/config.w32

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ if (PHP_DOM == "yes") {
1212
domexception.c parentnode.c processinginstruction.c \
1313
cdatasection.c documentfragment.c domimplementation.c element.c \
1414
node.c characterdata.c documenttype.c \
15-
entity.c nodelist.c text.c comment.c \
15+
entity.c nodelist.c html_collection.c text.c comment.c \
1616
entityreference.c \
1717
notation.c xpath.c dom_iterators.c \
1818
namednodemap.c xpath_callbacks.c", null, "-Iext/dom/lexbor");

ext/dom/html_collection.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
+----------------------------------------------------------------------+
3+
| Copyright (c) The PHP Group |
4+
+----------------------------------------------------------------------+
5+
| This source file is subject to version 3.01 of the PHP license, |
6+
| that is bundled with this package in the file LICENSE, and is |
7+
| available through the world-wide-web at the following url: |
8+
| https://www.php.net/license/3_01.txt |
9+
| If you did not receive a copy of the PHP license and are unable to |
10+
| obtain it through the world-wide-web, please send a note to |
11+
| [email protected] so we can mail you a copy immediately. |
12+
+----------------------------------------------------------------------+
13+
| Authors: Niels Dossche <[email protected]> |
14+
+----------------------------------------------------------------------+
15+
*/
16+
17+
#ifdef HAVE_CONFIG_H
18+
#include "config.h"
19+
#endif
20+
21+
#include "php.h"
22+
#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
23+
#include "php_dom.h"
24+
#include "namespace_compat.h"
25+
26+
/* https://dom.spec.whatwg.org/#dom-htmlcollection-nameditem-key */
27+
PHP_METHOD(DOM_HTMLCollection, namedItem)
28+
{
29+
zend_string *key;
30+
ZEND_PARSE_PARAMETERS_START(1, 1)
31+
Z_PARAM_PATH_STR(key)
32+
ZEND_PARSE_PARAMETERS_END();
33+
34+
/* 1. If key is the empty string, return null. */
35+
if (ZSTR_LEN(key) == 0) {
36+
RETURN_NULL();
37+
}
38+
39+
dom_object *intern = Z_DOMOBJ_P(ZEND_THIS);
40+
dom_nnodemap_object *objmap = intern->ptr;
41+
42+
/* 2. Return the first element in the collection for which at least one of the following is true: */
43+
xmlNodePtr basep = dom_object_get_node(objmap->baseobj);
44+
if (basep != NULL) {
45+
int cur = 0;
46+
int next = cur;
47+
xmlNodePtr candidate = basep->children;
48+
while (candidate != NULL) {
49+
candidate = dom_get_elements_by_tag_name_ns_raw(basep, candidate, objmap->ns, objmap->local, objmap->local_lower, &cur, next);
50+
if (candidate == NULL) {
51+
break;
52+
}
53+
54+
xmlAttrPtr attr;
55+
56+
/* it has an ID which is key; */
57+
if ((attr = xmlHasNsProp(candidate, BAD_CAST "id", NULL)) != NULL && dom_compare_value(attr, BAD_CAST ZSTR_VAL(key))) {
58+
DOM_RET_OBJ(candidate, objmap->baseobj);
59+
return;
60+
}
61+
/* it is in the HTML namespace and has a name attribute whose value is key; */
62+
else if (php_dom_ns_is_fast(candidate, php_dom_ns_is_html_magic_token)) {
63+
if ((attr = xmlHasNsProp(candidate, BAD_CAST "name", NULL)) != NULL && dom_compare_value(attr, BAD_CAST ZSTR_VAL(key))) {
64+
DOM_RET_OBJ(candidate, objmap->baseobj);
65+
return;
66+
}
67+
}
68+
69+
next = cur + 1;
70+
}
71+
}
72+
}
73+
74+
#endif

ext/dom/php_dom.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ dom_object *php_dom_instantiate_object_helper(zval *return_value, zend_class_ent
175175
xmlDocPtr php_dom_create_html_doc(void);
176176

177177
xmlChar *dom_attr_value(const xmlAttr *attr, bool *free);
178+
bool dom_compare_value(const xmlAttr *attr, const xmlChar *value);
178179

179180
typedef enum {
180181
DOM_LOAD_STRING = 0,

ext/dom/php_dom.stub.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1279,7 +1279,7 @@ class HTMLCollection implements \IteratorAggregate, \Countable
12791279
/** @implementation-alias DOMNodeList::item */
12801280
public function item(int $index): ?Element {}
12811281

1282-
/* TODO: implement namedItem */
1282+
public function namedItem(string $key): ?Element {}
12831283

12841284
/** @implementation-alias DOMNodeList::count */
12851285
public function count(): int {}

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.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
--TEST--
2+
HTMLCollection::namedItem() and dimension handling for named accesses
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
$xml = <<<XML
9+
<!DOCTYPE root [
10+
<!ENTITY ent "test">
11+
]>
12+
<root>
13+
<node name="foo">1</node>
14+
<x id="foo">2</x>
15+
<x id="foo&ent;">2 with entity</x>
16+
<node test:id="foo" xmlns:test="http://example.com">3</node>
17+
<node id="wrong">4</node>
18+
<node id="foo">5</node>
19+
<node name="bar">without html ns</node>
20+
<node name="bar" xmlns="http://www.w3.org/1999/xhtml">with html ns</node>
21+
</root>
22+
XML;
23+
24+
$dom = DOM\XMLDocument::createFromString($xml);
25+
var_dump($dom->getElementsByTagName('node')->namedItem('foo')?->textContent);
26+
var_dump($dom->getElementsByTagName('node')->namedItem('')?->textContent);
27+
var_dump($dom->getElementsByTagName('node')->namedItem('does not exist')?->textContent);
28+
var_dump($dom->getElementsByTagName('node')->namedItem('wrong')?->textContent);
29+
var_dump($dom->getElementsByTagName('node')->namedItem('bar')?->textContent);
30+
var_dump($dom->getElementsByTagName('x')->namedItem('foo')?->textContent);
31+
var_dump($dom->getElementsByTagName('x')->namedItem('footest')?->textContent);
32+
33+
?>
34+
--EXPECT--
35+
string(1) "5"
36+
NULL
37+
NULL
38+
string(1) "4"
39+
string(12) "with html ns"
40+
string(1) "2"
41+
string(13) "2 with entity"

0 commit comments

Comments
 (0)