xref: /PHP-8.3/ext/dom/dom_iterators.c (revision 9cb23a3d)
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 nodeIterator;
28 struct _nodeIterator {
29 	int cur;
30 	int index;
31 	xmlNode *node;
32 };
33 
34 typedef struct _notationIterator notationIterator;
35 struct _notationIterator {
36 	int cur;
37 	int index;
38 	xmlNotation *notation;
39 };
40 
41 /* Function pointer typedef changed in 2.9.8, see https://github.com/GNOME/libxml2/commit/e03f0a199a67017b2f8052354cf732b2b4cae787 */
42 #if LIBXML_VERSION >= 20908
itemHashScanner(void * payload,void * data,const xmlChar * name)43 static void itemHashScanner (void *payload, void *data, const xmlChar *name) /* {{{ */
44 #else
45 static void itemHashScanner (void *payload, void *data, xmlChar *name)
46 #endif
47 {
48 	nodeIterator *priv = (nodeIterator *)data;
49 
50 	if(priv->cur < priv->index) {
51 		priv->cur++;
52 	} else {
53 		if(priv->node == NULL) {
54 			priv->node = (xmlNode *)payload;
55 		}
56 	}
57 }
58 /* }}} */
59 
create_notation(const xmlChar * name,const xmlChar * ExternalID,const xmlChar * SystemID)60 xmlNodePtr create_notation(const xmlChar *name, const xmlChar *ExternalID, const xmlChar *SystemID) /* {{{ */
61 {
62 	xmlEntityPtr ret;
63 
64 	ret = (xmlEntityPtr) xmlMalloc(sizeof(xmlEntity));
65 	memset(ret, 0, sizeof(xmlEntity));
66 	ret->type = XML_NOTATION_NODE;
67 	ret->name = xmlStrdup(name);
68 	ret->ExternalID = xmlStrdup(ExternalID);
69 	ret->SystemID = xmlStrdup(SystemID);
70 	ret->length = 0;
71 	ret->content = NULL;
72 	ret->URI = NULL;
73 	ret->orig = NULL;
74 	ret->children = NULL;
75 	ret->parent = NULL;
76 	ret->doc = NULL;
77 	ret->_private = NULL;
78 	ret->last = NULL;
79 	ret->prev = NULL;
80 	return((xmlNodePtr) ret);
81 }
82 /* }}} */
83 
php_dom_libxml_hash_iter(xmlHashTable * ht,int index)84 xmlNode *php_dom_libxml_hash_iter(xmlHashTable *ht, int index) /* {{{ */
85 {
86 	xmlNode *nodep = NULL;
87 	nodeIterator *iter;
88 	int htsize;
89 
90 	if ((htsize = xmlHashSize(ht)) > 0 && index < htsize) {
91 		iter = emalloc(sizeof(nodeIterator));
92 		iter->cur = 0;
93 		iter->index = index;
94 		iter->node = NULL;
95 		xmlHashScan(ht, itemHashScanner, iter);
96 		nodep = iter->node;
97 		efree(iter);
98 		return nodep;
99 	} else {
100 		return NULL;
101 	}
102 }
103 /* }}} */
104 
php_dom_libxml_notation_iter(xmlHashTable * ht,int index)105 xmlNode *php_dom_libxml_notation_iter(xmlHashTable *ht, int index) /* {{{ */
106 {
107 	notationIterator *iter;
108 	xmlNotation *notep = NULL;
109 	int htsize;
110 
111 	if ((htsize = xmlHashSize(ht)) > 0 && index < htsize) {
112 		iter = emalloc(sizeof(notationIterator));
113 		iter->cur = 0;
114 		iter->index = index;
115 		iter->notation = NULL;
116 		xmlHashScan(ht, itemHashScanner, iter);
117 		notep = iter->notation;
118 		efree(iter);
119 		return create_notation(notep->name, notep->PublicID, notep->SystemID);
120 	} else {
121 		return NULL;
122 	}
123 }
124 /* }}} */
125 
php_dom_iterator_dtor(zend_object_iterator * iter)126 static void php_dom_iterator_dtor(zend_object_iterator *iter) /* {{{ */
127 {
128 	php_dom_iterator *iterator = (php_dom_iterator *)iter;
129 
130 	zval_ptr_dtor(&iterator->intern.data);
131 	zval_ptr_dtor(&iterator->curobj);
132 }
133 /* }}} */
134 
php_dom_iterator_valid(zend_object_iterator * iter)135 static int php_dom_iterator_valid(zend_object_iterator *iter) /* {{{ */
136 {
137 
138 	php_dom_iterator *iterator = (php_dom_iterator *)iter;
139 
140 	if (Z_TYPE(iterator->curobj) != IS_UNDEF) {
141 		return SUCCESS;
142 	} else {
143 		return FAILURE;
144 	}
145 }
146 /* }}} */
147 
php_dom_iterator_current_data(zend_object_iterator * iter)148 zval *php_dom_iterator_current_data(zend_object_iterator *iter) /* {{{ */
149 {
150 	php_dom_iterator *iterator = (php_dom_iterator *)iter;
151 	return Z_ISUNDEF(iterator->curobj) ? NULL : &iterator->curobj;
152 }
153 /* }}} */
154 
php_dom_iterator_current_key(zend_object_iterator * iter,zval * key)155 static void php_dom_iterator_current_key(zend_object_iterator *iter, zval *key) /* {{{ */
156 {
157 	php_dom_iterator *iterator = (php_dom_iterator *)iter;
158 	zval *object = &iterator->intern.data;
159 
160 	if (instanceof_function(Z_OBJCE_P(object), dom_nodelist_class_entry)) {
161 		ZVAL_LONG(key, iter->index);
162 	} else {
163 		dom_object *intern = Z_DOMOBJ_P(&iterator->curobj);
164 
165 		if (intern != NULL && intern->ptr != NULL) {
166 			xmlNodePtr curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->ptr)->node;
167 			ZVAL_STRINGL(key, (char *) curnode->name, xmlStrlen(curnode->name));
168 		} else {
169 			ZVAL_NULL(key);
170 		}
171 	}
172 }
173 /* }}} */
174 
php_dom_iterator_move_forward(zend_object_iterator * iter)175 static void php_dom_iterator_move_forward(zend_object_iterator *iter) /* {{{ */
176 {
177 	zval *object;
178 	xmlNodePtr curnode = NULL, basenode;
179 	dom_object *intern;
180 	dom_object *nnmap;
181 	dom_nnodemap_object *objmap;
182 	zend_long previndex;
183 	HashTable *nodeht;
184 	zval *entry;
185 	bool do_curobj_undef = 1;
186 
187 	php_dom_iterator *iterator = (php_dom_iterator *)iter;
188 	if (Z_ISUNDEF(iterator->curobj)) {
189 		return;
190 	}
191 
192 	intern = Z_DOMOBJ_P(&iterator->curobj);
193 	object = &iterator->intern.data;
194 	nnmap = Z_DOMOBJ_P(object);
195 	objmap = (dom_nnodemap_object *)nnmap->ptr;
196 
197 	if (intern != NULL && intern->ptr != NULL) {
198 		if (objmap->nodetype != XML_ENTITY_NODE &&
199 			objmap->nodetype != XML_NOTATION_NODE) {
200 			if (objmap->nodetype == DOM_NODESET) {
201 				nodeht = HASH_OF(&objmap->baseobj_zv);
202 				zend_hash_move_forward_ex(nodeht, &iterator->pos);
203 				if ((entry = zend_hash_get_current_data_ex(nodeht, &iterator->pos))) {
204 					zval_ptr_dtor(&iterator->curobj);
205 					ZVAL_UNDEF(&iterator->curobj);
206 					ZVAL_COPY(&iterator->curobj, entry);
207 					do_curobj_undef = 0;
208 				}
209 			} else {
210 				if (objmap->nodetype == XML_ATTRIBUTE_NODE ||
211 					objmap->nodetype == XML_ELEMENT_NODE) {
212 					curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->ptr)->node;
213 					curnode = curnode->next;
214 				} else {
215 					/* The collection is live, we nav the tree from the base object if we cannot
216 					 * use the cache to restart from the last point. */
217 					basenode = dom_object_get_node(objmap->baseobj);
218 					if (UNEXPECTED(!basenode)) {
219 						goto err;
220 					}
221 					if (php_dom_is_cache_tag_stale_from_node(&iterator->cache_tag, basenode)) {
222 						php_dom_mark_cache_tag_up_to_date_from_node(&iterator->cache_tag, basenode);
223 						previndex = 0;
224 						if (basenode->type == XML_DOCUMENT_NODE || basenode->type == XML_HTML_DOCUMENT_NODE) {
225 							curnode = xmlDocGetRootElement((xmlDoc *) basenode);
226 						} else {
227 							curnode = basenode->children;
228 						}
229 					} else {
230 						previndex = iter->index - 1;
231 						curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->ptr)->node;
232 					}
233 					curnode = dom_get_elements_by_tag_name_ns_raw(
234 						basenode, curnode, (char *) objmap->ns, (char *) objmap->local, &previndex, iter->index);
235 				}
236 			}
237 		} else {
238 			if (objmap->nodetype == XML_ENTITY_NODE) {
239 				curnode = php_dom_libxml_hash_iter(objmap->ht, iter->index);
240 			} else {
241 				curnode = php_dom_libxml_notation_iter(objmap->ht, iter->index);
242 			}
243 		}
244 	}
245 err:
246 	if (do_curobj_undef) {
247 		zval_ptr_dtor(&iterator->curobj);
248 		ZVAL_UNDEF(&iterator->curobj);
249 	}
250 	if (curnode) {
251 		php_dom_create_object(curnode, &iterator->curobj, objmap->baseobj);
252 	}
253 }
254 /* }}} */
255 
256 static const zend_object_iterator_funcs php_dom_iterator_funcs = {
257 	php_dom_iterator_dtor,
258 	php_dom_iterator_valid,
259 	php_dom_iterator_current_data,
260 	php_dom_iterator_current_key,
261 	php_dom_iterator_move_forward,
262 	NULL,
263 	NULL,
264 	NULL, /* get_gc */
265 };
266 
php_dom_get_iterator(zend_class_entry * ce,zval * object,int by_ref)267 zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
268 {
269 	dom_object *intern;
270 	dom_nnodemap_object *objmap;
271 	xmlNodePtr curnode=NULL;
272 	zend_long curindex = 0;
273 	HashTable *nodeht;
274 	zval *entry;
275 	php_dom_iterator *iterator;
276 
277 	if (by_ref) {
278 		zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
279 		return NULL;
280 	}
281 	iterator = emalloc(sizeof(php_dom_iterator));
282 	zend_iterator_init(&iterator->intern);
283 	iterator->cache_tag.modification_nr = 0;
284 
285 	ZVAL_OBJ_COPY(&iterator->intern.data, Z_OBJ_P(object));
286 	iterator->intern.funcs = &php_dom_iterator_funcs;
287 
288 	ZVAL_UNDEF(&iterator->curobj);
289 
290 	intern = Z_DOMOBJ_P(object);
291 	objmap = (dom_nnodemap_object *)intern->ptr;
292 	if (objmap != NULL) {
293 		if (objmap->nodetype != XML_ENTITY_NODE &&
294 			objmap->nodetype != XML_NOTATION_NODE) {
295 			if (objmap->nodetype == DOM_NODESET) {
296 				nodeht = HASH_OF(&objmap->baseobj_zv);
297 				zend_hash_internal_pointer_reset_ex(nodeht, &iterator->pos);
298 				if ((entry = zend_hash_get_current_data_ex(nodeht, &iterator->pos))) {
299 					ZVAL_COPY(&iterator->curobj, entry);
300 				}
301 			} else {
302 				xmlNodePtr basep = (xmlNode *)dom_object_get_node(objmap->baseobj);
303 				if (!basep) {
304 					goto err;
305 				}
306 				if (objmap->nodetype == XML_ATTRIBUTE_NODE || objmap->nodetype == XML_ELEMENT_NODE) {
307 					if (objmap->nodetype == XML_ATTRIBUTE_NODE) {
308 						curnode = (xmlNodePtr) basep->properties;
309 					} else {
310 						curnode = dom_nodelist_iter_start_first_child(basep);
311 					}
312 				} else {
313 					xmlNodePtr nodep = basep;
314 					if (nodep->type == XML_DOCUMENT_NODE || nodep->type == XML_HTML_DOCUMENT_NODE) {
315 						nodep = xmlDocGetRootElement((xmlDoc *) nodep);
316 					} else {
317 						nodep = nodep->children;
318 					}
319 					curnode = dom_get_elements_by_tag_name_ns_raw(
320 						basep, nodep, (char *) objmap->ns, (char *) objmap->local, &curindex, 0);
321 				}
322 			}
323 		} else {
324 			if (objmap->nodetype == XML_ENTITY_NODE) {
325 				curnode = php_dom_libxml_hash_iter(objmap->ht, 0);
326 			} else {
327 				curnode = php_dom_libxml_notation_iter(objmap->ht, 0);
328 			}
329 		}
330 	}
331 err:
332 	if (curnode) {
333 		php_dom_create_object(curnode, &iterator->curobj, objmap->baseobj);
334 	}
335 
336 	return &iterator->intern;
337 }
338 /* }}} */
339 
340 #endif
341