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