xref: /php-src/ext/dom/html_collection.c (revision 73b7993b)
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    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors: Niels Dossche <nielsdos@php.net>                            |
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 "nodelist.h"
25 #include "html_collection.h"
26 #include "namespace_compat.h"
27 
28 typedef struct dom_named_item {
29 	dom_object *context_intern;
30 	xmlNodePtr node;
31 } dom_named_item;
32 
33 /* https://dom.spec.whatwg.org/#dom-htmlcollection-nameditem-key */
dom_html_collection_named_item(zend_string * key,zend_object * zobj)34 static dom_named_item dom_html_collection_named_item(zend_string *key, zend_object *zobj)
35 {
36 	dom_named_item ret = {NULL, NULL};
37 
38 	/* 1. If key is the empty string, return null. */
39 	if (ZSTR_LEN(key) == 0) {
40 		return ret;
41 	}
42 
43 	dom_object *intern = php_dom_obj_from_obj(zobj);
44 	dom_nnodemap_object *objmap = intern->ptr;
45 
46 	/* 2. Return the first element in the collection for which at least one of the following is true: */
47 	xmlNodePtr basep = dom_object_get_node(objmap->baseobj);
48 	if (basep != NULL) {
49 		zend_long cur = 0;
50 		zend_long next = cur; /* not +1, otherwise we skip the first candidate */
51 		xmlNodePtr candidate = basep->children;
52 		while (candidate != NULL) {
53 			candidate = dom_get_elements_by_tag_name_ns_raw(basep, candidate, objmap->ns, objmap->local, objmap->local_lower, &cur, next);
54 			if (candidate == NULL) {
55 				break;
56 			}
57 
58 			xmlAttrPtr attr;
59 
60 			/* it has an ID which is key; */
61 			if ((attr = xmlHasNsProp(candidate, BAD_CAST "id", NULL)) != NULL && dom_compare_value(attr, BAD_CAST ZSTR_VAL(key))) {
62 				ret.context_intern = objmap->baseobj;
63 				ret.node = candidate;
64 				return ret;
65 			}
66 			/* it is in the HTML namespace and has a name attribute whose value is key; */
67 			else if (php_dom_ns_is_fast(candidate, php_dom_ns_is_html_magic_token)) {
68 				if ((attr = xmlHasNsProp(candidate, BAD_CAST "name", NULL)) != NULL && dom_compare_value(attr, BAD_CAST ZSTR_VAL(key))) {
69 					ret.context_intern = objmap->baseobj;
70 					ret.node = candidate;
71 					return ret;
72 				}
73 			}
74 
75 			next = cur + 1;
76 		}
77 	}
78 
79 	return ret;
80 }
81 
dom_html_collection_named_item_into_zval(zval * return_value,zend_string * key,zend_object * zobj)82 static void dom_html_collection_named_item_into_zval(zval *return_value, zend_string *key, zend_object *zobj)
83 {
84 	dom_named_item named_item = dom_html_collection_named_item(key, zobj);
85 	if (named_item.node != NULL) {
86 		DOM_RET_OBJ(named_item.node, named_item.context_intern);
87 	} else {
88 		RETURN_NULL();
89 	}
90 }
91 
PHP_METHOD(Dom_HTMLCollection,namedItem)92 PHP_METHOD(Dom_HTMLCollection, namedItem)
93 {
94 	zend_string *key;
95 	ZEND_PARSE_PARAMETERS_START(1, 1)
96 		Z_PARAM_STR(key)
97 	ZEND_PARSE_PARAMETERS_END();
98 	dom_html_collection_named_item_into_zval(return_value, key, Z_OBJ_P(ZEND_THIS));
99 }
100 
dom_html_collection_read_dimension(zend_object * object,zval * offset,int type,zval * rv)101 zval *dom_html_collection_read_dimension(zend_object *object, zval *offset, int type, zval *rv)
102 {
103 	if (UNEXPECTED(!offset)) {
104 		zend_throw_error(NULL, "Cannot append to %s", ZSTR_VAL(object->ce->name));
105 		return NULL;
106 	}
107 
108 	dom_nodelist_dimension_index index = dom_modern_nodelist_get_index(offset);
109 	if (UNEXPECTED(index.type == DOM_NODELIST_DIM_ILLEGAL)) {
110 		zend_illegal_container_offset(object->ce->name, offset, type);
111 		return NULL;
112 	}
113 
114 	if (index.type == DOM_NODELIST_DIM_STRING) {
115 		dom_html_collection_named_item_into_zval(rv, index.str, object);
116 	} else {
117 		ZEND_ASSERT(index.type == DOM_NODELIST_DIM_LONG);
118 		php_dom_nodelist_get_item_into_zval(php_dom_obj_from_obj(object)->ptr, index.lval, rv);
119 	}
120 
121 	return rv;
122 }
123 
dom_html_collection_has_dimension(zend_object * object,zval * member,int check_empty)124 int dom_html_collection_has_dimension(zend_object *object, zval *member, int check_empty)
125 {
126 	/* If it exists, it cannot be empty because nodes aren't empty. */
127 	ZEND_IGNORE_VALUE(check_empty);
128 
129 	dom_nodelist_dimension_index index = dom_modern_nodelist_get_index(member);
130 	if (UNEXPECTED(index.type == DOM_NODELIST_DIM_ILLEGAL)) {
131 		zend_illegal_container_offset(object->ce->name, member, BP_VAR_IS);
132 		return 0;
133 	}
134 
135 	if (index.type == DOM_NODELIST_DIM_STRING) {
136 		return dom_html_collection_named_item(index.str, object).node != NULL;
137 	} else {
138 		ZEND_ASSERT(index.type == DOM_NODELIST_DIM_LONG);
139 		return index.lval >= 0 && index.lval < php_dom_get_nodelist_length(php_dom_obj_from_obj(object));
140 	}
141 }
142 
143 #endif
144