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 zend_result 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
152 return &iterator->curobj;
153 }
154 /* }}} */
155
php_dom_iterator_current_key(zend_object_iterator * iter,zval * key)156 static void php_dom_iterator_current_key(zend_object_iterator *iter, zval *key) /* {{{ */
157 {
158 php_dom_iterator *iterator = (php_dom_iterator *)iter;
159 zval *object = &iterator->intern.data;
160
161 if (instanceof_function(Z_OBJCE_P(object), dom_nodelist_class_entry)) {
162 ZVAL_LONG(key, iter->index);
163 } else {
164 dom_object *intern = Z_DOMOBJ_P(&iterator->curobj);
165
166 if (intern != NULL && intern->ptr != NULL) {
167 xmlNodePtr curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->ptr)->node;
168 ZVAL_STRINGL(key, (char *) curnode->name, xmlStrlen(curnode->name));
169 } else {
170 ZVAL_NULL(key);
171 }
172 }
173 }
174 /* }}} */
175
php_dom_iterator_move_forward(zend_object_iterator * iter)176 static void php_dom_iterator_move_forward(zend_object_iterator *iter) /* {{{ */
177 {
178 zval *object;
179 xmlNodePtr curnode = NULL, basenode;
180 dom_object *intern;
181 dom_object *nnmap;
182 dom_nnodemap_object *objmap;
183 int previndex;
184 HashTable *nodeht;
185 zval *entry;
186 bool do_curobj_undef = 1;
187
188 php_dom_iterator *iterator = (php_dom_iterator *)iter;
189
190 object = &iterator->intern.data;
191 nnmap = Z_DOMOBJ_P(object);
192 objmap = (dom_nnodemap_object *)nnmap->ptr;
193
194 intern = Z_DOMOBJ_P(&iterator->curobj);
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 if (objmap->nodetype == XML_ATTRIBUTE_NODE ||
210 objmap->nodetype == XML_ELEMENT_NODE) {
211 curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->ptr)->node;
212 curnode = curnode->next;
213 } else {
214 /* The collection is live, we nav the tree from the base object if we cannot
215 * use the cache to restart from the last point. */
216 basenode = dom_object_get_node(objmap->baseobj);
217 if (UNEXPECTED(!basenode)) {
218 goto err;
219 }
220 if (php_dom_is_cache_tag_stale_from_node(&iterator->cache_tag, basenode)) {
221 php_dom_mark_cache_tag_up_to_date_from_node(&iterator->cache_tag, basenode);
222 previndex = 0;
223 if (basenode->type == XML_DOCUMENT_NODE || basenode->type == XML_HTML_DOCUMENT_NODE) {
224 curnode = xmlDocGetRootElement((xmlDoc *) basenode);
225 } else {
226 curnode = basenode->children;
227 }
228 } else {
229 previndex = iter->index - 1;
230 curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->ptr)->node;
231 }
232 curnode = dom_get_elements_by_tag_name_ns_raw(
233 basenode, curnode, (char *) objmap->ns, (char *) objmap->local, &previndex, iter->index);
234 }
235 }
236 } else {
237 if (objmap->nodetype == XML_ENTITY_NODE) {
238 curnode = php_dom_libxml_hash_iter(objmap->ht, iter->index);
239 } else {
240 curnode = php_dom_libxml_notation_iter(objmap->ht, iter->index);
241 }
242 }
243 }
244 err:
245 if (do_curobj_undef) {
246 zval_ptr_dtor(&iterator->curobj);
247 ZVAL_UNDEF(&iterator->curobj);
248 }
249 if (curnode) {
250 php_dom_create_object(curnode, &iterator->curobj, objmap->baseobj);
251 }
252 }
253 /* }}} */
254
255 static const zend_object_iterator_funcs php_dom_iterator_funcs = {
256 php_dom_iterator_dtor,
257 php_dom_iterator_valid,
258 php_dom_iterator_current_data,
259 php_dom_iterator_current_key,
260 php_dom_iterator_move_forward,
261 NULL,
262 NULL,
263 NULL, /* get_gc */
264 };
265
php_dom_get_iterator(zend_class_entry * ce,zval * object,int by_ref)266 zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
267 {
268 dom_object *intern;
269 dom_nnodemap_object *objmap;
270 xmlNodePtr curnode=NULL;
271 int curindex = 0;
272 HashTable *nodeht;
273 zval *entry;
274 php_dom_iterator *iterator;
275
276 if (by_ref) {
277 zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
278 return NULL;
279 }
280 iterator = emalloc(sizeof(php_dom_iterator));
281 zend_iterator_init(&iterator->intern);
282 iterator->cache_tag.modification_nr = 0;
283
284 ZVAL_OBJ_COPY(&iterator->intern.data, Z_OBJ_P(object));
285 iterator->intern.funcs = &php_dom_iterator_funcs;
286
287 ZVAL_UNDEF(&iterator->curobj);
288
289 intern = Z_DOMOBJ_P(object);
290 objmap = (dom_nnodemap_object *)intern->ptr;
291 if (objmap != NULL) {
292 if (objmap->nodetype != XML_ENTITY_NODE &&
293 objmap->nodetype != XML_NOTATION_NODE) {
294 if (objmap->nodetype == DOM_NODESET) {
295 nodeht = HASH_OF(&objmap->baseobj_zv);
296 zend_hash_internal_pointer_reset_ex(nodeht, &iterator->pos);
297 if ((entry = zend_hash_get_current_data_ex(nodeht, &iterator->pos))) {
298 ZVAL_COPY(&iterator->curobj, entry);
299 }
300 } else {
301 xmlNodePtr basep = (xmlNode *)dom_object_get_node(objmap->baseobj);
302 if (!basep) {
303 goto err;
304 }
305 if (objmap->nodetype == XML_ATTRIBUTE_NODE || objmap->nodetype == XML_ELEMENT_NODE) {
306 if (objmap->nodetype == XML_ATTRIBUTE_NODE) {
307 curnode = (xmlNodePtr) basep->properties;
308 } else {
309 curnode = (xmlNodePtr) basep->children;
310 }
311 } else {
312 xmlNodePtr nodep = basep;
313 if (nodep->type == XML_DOCUMENT_NODE || nodep->type == XML_HTML_DOCUMENT_NODE) {
314 nodep = xmlDocGetRootElement((xmlDoc *) nodep);
315 } else {
316 nodep = nodep->children;
317 }
318 curnode = dom_get_elements_by_tag_name_ns_raw(
319 basep, nodep, (char *) objmap->ns, (char *) objmap->local, &curindex, 0);
320 }
321 }
322 } else {
323 if (objmap->nodetype == XML_ENTITY_NODE) {
324 curnode = php_dom_libxml_hash_iter(objmap->ht, 0);
325 } else {
326 curnode = php_dom_libxml_notation_iter(objmap->ht, 0);
327 }
328 }
329 }
330 err:
331 if (curnode) {
332 php_dom_create_object(curnode, &iterator->curobj, objmap->baseobj);
333 }
334
335 return &iterator->intern;
336 }
337 /* }}} */
338
339 #endif
340