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