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: Christian Stocker <chregu@php.net> |
14 | Rob Richards <rrichards@php.net> |
15 +----------------------------------------------------------------------+
16 */
17
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include "php.h"
23 #if defined(HAVE_LIBXML) && defined(HAVE_DOM)
24 #include "php_dom.h"
25 #include "dom_ce.h"
26
27 typedef struct nodeIterator {
28 int cur;
29 int index;
30 xmlNode *node;
31 } nodeIterator;
32
33 /* Function pointer typedef changed in 2.9.8, see https://github.com/GNOME/libxml2/commit/e03f0a199a67017b2f8052354cf732b2b4cae787 */
34 #if LIBXML_VERSION >= 20908
itemHashScanner(void * payload,void * data,const xmlChar * name)35 static void itemHashScanner (void *payload, void *data, const xmlChar *name) /* {{{ */
36 #else
37 static void itemHashScanner (void *payload, void *data, xmlChar *name)
38 #endif
39 {
40 nodeIterator *priv = data;
41
42 if (priv->cur < priv->index) {
43 priv->cur++;
44 } else {
45 if (priv->node == NULL) {
46 priv->node = payload;
47 }
48 }
49 }
50 /* }}} */
51
create_notation(const xmlChar * name,const xmlChar * ExternalID,const xmlChar * SystemID)52 xmlNodePtr create_notation(const xmlChar *name, const xmlChar *ExternalID, const xmlChar *SystemID) /* {{{ */
53 {
54 xmlEntityPtr ret = xmlMalloc(sizeof(xmlEntity));
55 memset(ret, 0, sizeof(xmlEntity));
56 ret->type = XML_NOTATION_NODE;
57 ret->name = xmlStrdup(name);
58 ret->ExternalID = xmlStrdup(ExternalID);
59 ret->SystemID = xmlStrdup(SystemID);
60 return (xmlNodePtr) ret;
61 }
62 /* }}} */
63
php_dom_libxml_hash_iter_ex(xmlHashTable * ht,int index)64 static xmlNode *php_dom_libxml_hash_iter_ex(xmlHashTable *ht, int index)
65 {
66 int htsize;
67
68 if ((htsize = xmlHashSize(ht)) > 0 && index < htsize) {
69 nodeIterator iter;
70 iter.cur = 0;
71 iter.index = index;
72 iter.node = NULL;
73 xmlHashScan(ht, itemHashScanner, &iter);
74 return iter.node;
75 } else {
76 return NULL;
77 }
78 }
79
php_dom_libxml_hash_iter(dom_nnodemap_object * objmap,int index)80 xmlNode *php_dom_libxml_hash_iter(dom_nnodemap_object *objmap, int index)
81 {
82 xmlNode *curnode = php_dom_libxml_hash_iter_ex(objmap->ht, index);
83
84 if (curnode != NULL && objmap->nodetype != XML_ENTITY_NODE) {
85 xmlNotation *notation = (xmlNotation *) curnode;
86 curnode = create_notation(notation->name, notation->PublicID, notation->SystemID);
87 }
88
89 return curnode;
90 }
91
php_dom_iterator_dtor(zend_object_iterator * iter)92 static void php_dom_iterator_dtor(zend_object_iterator *iter) /* {{{ */
93 {
94 php_dom_iterator *iterator = (php_dom_iterator *)iter;
95
96 zval_ptr_dtor(&iterator->intern.data);
97 zval_ptr_dtor(&iterator->curobj);
98 }
99 /* }}} */
100
php_dom_iterator_valid(zend_object_iterator * iter)101 static zend_result php_dom_iterator_valid(zend_object_iterator *iter) /* {{{ */
102 {
103 php_dom_iterator *iterator = (php_dom_iterator *)iter;
104
105 if (Z_TYPE(iterator->curobj) != IS_UNDEF) {
106 return SUCCESS;
107 } else {
108 return FAILURE;
109 }
110 }
111 /* }}} */
112
php_dom_iterator_current_data(zend_object_iterator * iter)113 zval *php_dom_iterator_current_data(zend_object_iterator *iter) /* {{{ */
114 {
115 php_dom_iterator *iterator = (php_dom_iterator *)iter;
116 return Z_ISUNDEF(iterator->curobj) ? NULL : &iterator->curobj;
117 }
118 /* }}} */
119
php_dom_iterator_current_key(zend_object_iterator * iter,zval * key)120 static void php_dom_iterator_current_key(zend_object_iterator *iter, zval *key) /* {{{ */
121 {
122 php_dom_iterator *iterator = (php_dom_iterator *)iter;
123 zval *object = &iterator->intern.data;
124 zend_class_entry *ce = Z_OBJCE_P(object);
125
126 /* Nodelists have the index as a key while named node maps have the name as a key. */
127 if (instanceof_function(ce, dom_nodelist_class_entry) || instanceof_function(ce, dom_modern_nodelist_class_entry)) {
128 ZVAL_LONG(key, iter->index);
129 } else {
130 dom_object *intern = Z_DOMOBJ_P(&iterator->curobj);
131
132 if (intern != NULL && intern->ptr != NULL) {
133 xmlNodePtr curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->ptr)->node;
134 ZVAL_STRINGL(key, (char *) curnode->name, xmlStrlen(curnode->name));
135 } else {
136 ZVAL_NULL(key);
137 }
138 }
139 }
140 /* }}} */
141
dom_fetch_first_iteration_item(dom_nnodemap_object * objmap)142 static xmlNodePtr dom_fetch_first_iteration_item(dom_nnodemap_object *objmap)
143 {
144 xmlNodePtr basep = dom_object_get_node(objmap->baseobj);
145 if (!basep) {
146 return NULL;
147 }
148 if (objmap->nodetype == XML_ATTRIBUTE_NODE || objmap->nodetype == XML_ELEMENT_NODE) {
149 if (objmap->nodetype == XML_ATTRIBUTE_NODE) {
150 return (xmlNodePtr) basep->properties;
151 } else {
152 return basep->children;
153 }
154 } else {
155 int curindex = 0;
156 xmlNodePtr nodep = php_dom_first_child_of_container_node(basep);
157 return dom_get_elements_by_tag_name_ns_raw(
158 basep, nodep, objmap->ns, objmap->local, objmap->local_lower, &curindex, 0);
159 }
160 }
161
php_dom_iterator_move_forward(zend_object_iterator * iter)162 static void php_dom_iterator_move_forward(zend_object_iterator *iter) /* {{{ */
163 {
164 xmlNodePtr curnode = NULL;
165
166 php_dom_iterator *iterator = (php_dom_iterator *)iter;
167
168 zval *object = &iterator->intern.data;
169 dom_object *nnmap = Z_DOMOBJ_P(object);
170 dom_nnodemap_object *objmap = nnmap->ptr;
171
172 dom_object *intern = Z_DOMOBJ_P(&iterator->curobj);
173
174 if (intern != NULL && intern->ptr != NULL) {
175 if (objmap->nodetype != XML_ENTITY_NODE &&
176 objmap->nodetype != XML_NOTATION_NODE) {
177 if (objmap->nodetype == DOM_NODESET) {
178 HashTable *nodeht = HASH_OF(&objmap->baseobj_zv);
179 zval *entry;
180 zend_hash_move_forward_ex(nodeht, &iterator->pos);
181 if ((entry = zend_hash_get_current_data_ex(nodeht, &iterator->pos))) {
182 zval_ptr_dtor(&iterator->curobj);
183 ZVAL_COPY(&iterator->curobj, entry);
184 return;
185 }
186 } else {
187 if (objmap->nodetype == XML_ATTRIBUTE_NODE ||
188 objmap->nodetype == XML_ELEMENT_NODE) {
189
190 /* Note: keep legacy behaviour for non-spec mode. */
191 if (php_dom_follow_spec_intern(intern) && php_dom_is_cache_tag_stale_from_doc_ptr(&iterator->cache_tag, intern->document)) {
192 php_dom_mark_cache_tag_up_to_date_from_doc_ref(&iterator->cache_tag, intern->document);
193 curnode = dom_fetch_first_iteration_item(objmap);
194 zend_ulong index = 0;
195 while (curnode != NULL && index++ < iter->index) {
196 curnode = curnode->next;
197 }
198 } else {
199 curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->ptr)->node;
200 curnode = curnode->next;
201 }
202 } else {
203 /* The collection is live, we nav the tree from the base object if we cannot
204 * use the cache to restart from the last point. */
205 xmlNodePtr basenode = dom_object_get_node(objmap->baseobj);
206
207 /* We have a strong reference to the base node via baseobj_zv, this cannot become NULL */
208 ZEND_ASSERT(basenode != NULL);
209
210 int previndex;
211 if (php_dom_is_cache_tag_stale_from_node(&iterator->cache_tag, basenode)) {
212 php_dom_mark_cache_tag_up_to_date_from_node(&iterator->cache_tag, basenode);
213 previndex = 0;
214 curnode = php_dom_first_child_of_container_node(basenode);
215 } else {
216 previndex = iter->index - 1;
217 curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->ptr)->node;
218 }
219 curnode = dom_get_elements_by_tag_name_ns_raw(
220 basenode, curnode, objmap->ns, objmap->local, objmap->local_lower, &previndex, iter->index);
221 }
222 }
223 } else {
224 curnode = php_dom_libxml_hash_iter(objmap, iter->index);
225 }
226 }
227
228 zval_ptr_dtor(&iterator->curobj);
229 ZVAL_UNDEF(&iterator->curobj);
230
231 if (curnode) {
232 php_dom_create_object(curnode, &iterator->curobj, objmap->baseobj);
233 }
234 }
235 /* }}} */
236
237 static const zend_object_iterator_funcs php_dom_iterator_funcs = {
238 php_dom_iterator_dtor,
239 php_dom_iterator_valid,
240 php_dom_iterator_current_data,
241 php_dom_iterator_current_key,
242 php_dom_iterator_move_forward,
243 NULL,
244 NULL,
245 NULL, /* get_gc */
246 };
247
php_dom_get_iterator(zend_class_entry * ce,zval * object,int by_ref)248 zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
249 {
250 dom_object *intern;
251 dom_nnodemap_object *objmap;
252 xmlNodePtr curnode=NULL;
253 HashTable *nodeht;
254 zval *entry;
255 php_dom_iterator *iterator;
256
257 if (by_ref) {
258 zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
259 return NULL;
260 }
261 iterator = emalloc(sizeof(php_dom_iterator));
262 zend_iterator_init(&iterator->intern);
263 iterator->cache_tag.modification_nr = 0;
264
265 ZVAL_OBJ_COPY(&iterator->intern.data, Z_OBJ_P(object));
266 iterator->intern.funcs = &php_dom_iterator_funcs;
267
268 ZVAL_UNDEF(&iterator->curobj);
269
270 intern = Z_DOMOBJ_P(object);
271 objmap = (dom_nnodemap_object *)intern->ptr;
272 if (objmap != NULL) {
273 if (objmap->nodetype != XML_ENTITY_NODE &&
274 objmap->nodetype != XML_NOTATION_NODE) {
275 if (objmap->nodetype == DOM_NODESET) {
276 nodeht = HASH_OF(&objmap->baseobj_zv);
277 zend_hash_internal_pointer_reset_ex(nodeht, &iterator->pos);
278 if ((entry = zend_hash_get_current_data_ex(nodeht, &iterator->pos))) {
279 ZVAL_COPY(&iterator->curobj, entry);
280 }
281 } else {
282 curnode = dom_fetch_first_iteration_item(objmap);
283 }
284 } else {
285 curnode = php_dom_libxml_hash_iter(objmap, 0);
286 }
287 }
288
289 if (curnode) {
290 php_dom_create_object(curnode, &iterator->curobj, objmap->baseobj);
291 }
292
293 return &iterator->intern;
294 }
295 /* }}} */
296
297 #endif
298