xref: /PHP-8.0/ext/dom/parentnode.c (revision 665e1f32)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) The PHP Group                                          |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Benjamin Eberlei <beberlei@php.net>                         |
16    +----------------------------------------------------------------------+
17 */
18 
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 
23 #include "php.h"
24 #if defined(HAVE_LIBXML) && defined(HAVE_DOM)
25 #include "php_dom.h"
26 
27 /* {{{ firstElementChild DomParentNode
28 readonly=yes
29 URL: https://www.w3.org/TR/dom/#dom-parentnode-firstelementchild
30 */
dom_parent_node_first_element_child_read(dom_object * obj,zval * retval)31 int dom_parent_node_first_element_child_read(dom_object *obj, zval *retval)
32 {
33 	xmlNode *nodep, *first = NULL;
34 
35 	nodep = dom_object_get_node(obj);
36 
37 	if (nodep == NULL) {
38 		php_dom_throw_error(INVALID_STATE_ERR, 0);
39 		return FAILURE;
40 	}
41 
42 	if (dom_node_children_valid(nodep) == SUCCESS) {
43 		first = nodep->children;
44 
45 		while (first && first->type != XML_ELEMENT_NODE) {
46 			first = first->next;
47 		}
48 	}
49 
50 	if (!first) {
51 		ZVAL_NULL(retval);
52 		return SUCCESS;
53 	}
54 
55 	php_dom_create_object(first, retval, obj);
56 	return SUCCESS;
57 }
58 /* }}} */
59 
60 /* {{{ lastElementChild DomParentNode
61 readonly=yes
62 URL: https://www.w3.org/TR/dom/#dom-parentnode-lastelementchild
63 */
dom_parent_node_last_element_child_read(dom_object * obj,zval * retval)64 int dom_parent_node_last_element_child_read(dom_object *obj, zval *retval)
65 {
66 	xmlNode *nodep, *last = NULL;
67 
68 	nodep = dom_object_get_node(obj);
69 
70 	if (nodep == NULL) {
71 		php_dom_throw_error(INVALID_STATE_ERR, 0);
72 		return FAILURE;
73 	}
74 
75 	if (dom_node_children_valid(nodep) == SUCCESS) {
76 		last = nodep->last;
77 
78 		while (last && last->type != XML_ELEMENT_NODE) {
79 			last = last->prev;
80 		}
81 	}
82 
83 	if (!last) {
84 		ZVAL_NULL(retval);
85 		return SUCCESS;
86 	}
87 
88 	php_dom_create_object(last, retval, obj);
89 	return SUCCESS;
90 }
91 /* }}} */
92 
93 /* {{{ childElementCount DomParentNode
94 readonly=yes
95 https://www.w3.org/TR/dom/#dom-parentnode-childelementcount
96 */
dom_parent_node_child_element_count(dom_object * obj,zval * retval)97 int dom_parent_node_child_element_count(dom_object *obj, zval *retval)
98 {
99 	xmlNode *nodep, *first = NULL;
100 	zend_long count = 0;
101 
102 	nodep = dom_object_get_node(obj);
103 
104 	if (nodep == NULL) {
105 		php_dom_throw_error(INVALID_STATE_ERR, 0);
106 		return FAILURE;
107 	}
108 
109 	if (dom_node_children_valid(nodep) == SUCCESS) {
110 		first = nodep->children;
111 
112 		while (first != NULL) {
113 			if (first->type == XML_ELEMENT_NODE) {
114 				count++;
115 			}
116 
117 			first = first->next;
118 		}
119 	}
120 
121 	ZVAL_LONG(retval, count);
122 
123 	return SUCCESS;
124 }
125 /* }}} */
126 
dom_zvals_to_fragment(php_libxml_ref_obj * document,xmlNode * contextNode,zval * nodes,int nodesc)127 xmlNode* dom_zvals_to_fragment(php_libxml_ref_obj *document, xmlNode *contextNode, zval *nodes, int nodesc)
128 {
129 	int i;
130 	xmlDoc *documentNode;
131 	xmlNode *fragment;
132 	xmlNode *newNode;
133 	zend_class_entry *ce;
134 	dom_object *newNodeObj;
135 	int stricterror;
136 
137 	if (document == NULL) {
138 		php_dom_throw_error(HIERARCHY_REQUEST_ERR, 1);
139 		return NULL;
140 	}
141 
142 	if (contextNode->type == XML_DOCUMENT_NODE || contextNode->type == XML_HTML_DOCUMENT_NODE) {
143 		documentNode = (xmlDoc *) contextNode;
144 	} else {
145 		documentNode = contextNode->doc;
146 	}
147 
148 	fragment = xmlNewDocFragment(documentNode);
149 
150 	if (!fragment) {
151 		return NULL;
152 	}
153 
154 	stricterror = dom_get_strict_error(document);
155 
156 	for (i = 0; i < nodesc; i++) {
157 		if (Z_TYPE(nodes[i]) == IS_OBJECT) {
158 			ce = Z_OBJCE(nodes[i]);
159 
160 			if (instanceof_function(ce, dom_node_class_entry)) {
161 				newNodeObj = Z_DOMOBJ_P(&nodes[i]);
162 				newNode = dom_object_get_node(newNodeObj);
163 
164 				if (newNode->doc != documentNode) {
165 					xmlFree(fragment);
166 					php_dom_throw_error(WRONG_DOCUMENT_ERR, stricterror);
167 					return NULL;
168 				}
169 
170 				if (newNode->parent != NULL) {
171 					xmlUnlinkNode(newNode);
172 				}
173 
174 				newNodeObj->document = document;
175 				xmlSetTreeDoc(newNode, documentNode);
176 
177 				if (newNode->type == XML_ATTRIBUTE_NODE) {
178 					xmlFree(fragment);
179 
180 					php_dom_throw_error(HIERARCHY_REQUEST_ERR, stricterror);
181 					return NULL;
182 				}
183 
184 				if (!xmlAddChild(fragment, newNode)) {
185 					xmlFree(fragment);
186 
187 					php_dom_throw_error(HIERARCHY_REQUEST_ERR, stricterror);
188 					return NULL;
189 				}
190 
191 				continue;
192 			} else {
193 				xmlFree(fragment);
194 
195 				zend_argument_type_error(i + 1, "must be of type DOMNode|string, %s given", zend_zval_type_name(&nodes[i]));
196 				return NULL;
197 			}
198 		} else if (Z_TYPE(nodes[i]) == IS_STRING) {
199 			newNode = xmlNewDocText(documentNode, (xmlChar *) Z_STRVAL(nodes[i]));
200 
201 			xmlSetTreeDoc(newNode, documentNode);
202 
203 			if (!xmlAddChild(fragment, newNode)) {
204 				xmlFree(fragment);
205 
206 				return NULL;
207 			}
208 		} else {
209 			xmlFree(fragment);
210 
211 			zend_argument_type_error(i + 1, "must be of type DOMNode|string, %s given", zend_zval_type_name(&nodes[i]));
212 
213 			return NULL;
214 		}
215 	}
216 
217 	return fragment;
218 }
219 
dom_fragment_assign_parent_node(xmlNodePtr parentNode,xmlNodePtr fragment)220 static void dom_fragment_assign_parent_node(xmlNodePtr parentNode, xmlNodePtr fragment)
221 {
222 	xmlNodePtr node = fragment->children;
223 
224 	while (node != NULL) {
225 		node->parent = parentNode;
226 
227 		if (node == fragment->last) {
228 			break;
229 		}
230 		node = node->next;
231 	}
232 
233 	fragment->children = NULL;
234 	fragment->last = NULL;
235 }
236 
dom_parent_node_append(dom_object * context,zval * nodes,int nodesc)237 void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc)
238 {
239 	xmlNode *parentNode = dom_object_get_node(context);
240 	xmlNodePtr newchild, prevsib;
241 	xmlNode *fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc);
242 
243 	if (fragment == NULL) {
244 		return;
245 	}
246 
247 	newchild = fragment->children;
248 	prevsib = parentNode->last;
249 
250 	if (newchild) {
251 		if (prevsib != NULL) {
252 			prevsib->next = newchild;
253 		} else {
254 			parentNode->children = newchild;
255 		}
256 
257 		parentNode->last = fragment->last;
258 
259 		newchild->prev = prevsib;
260 
261 		dom_fragment_assign_parent_node(parentNode, fragment);
262 
263 		dom_reconcile_ns(parentNode->doc, newchild);
264 	}
265 
266 	xmlFree(fragment);
267 }
268 
dom_parent_node_prepend(dom_object * context,zval * nodes,int nodesc)269 void dom_parent_node_prepend(dom_object *context, zval *nodes, int nodesc)
270 {
271 	xmlNode *parentNode = dom_object_get_node(context);
272 
273 	if (parentNode->children == NULL) {
274 		dom_parent_node_append(context, nodes, nodesc);
275 		return;
276 	}
277 
278 	xmlNodePtr newchild, nextsib;
279 	xmlNode *fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc);
280 
281 	if (fragment == NULL) {
282 		return;
283 	}
284 
285 	newchild = fragment->children;
286 	nextsib = parentNode->children;
287 
288 	if (newchild) {
289 		parentNode->children = newchild;
290 		fragment->last->next = nextsib;
291 		nextsib->prev = fragment->last;
292 
293 		dom_fragment_assign_parent_node(parentNode, fragment);
294 
295 		dom_reconcile_ns(parentNode->doc, newchild);
296 	}
297 
298 	xmlFree(fragment);
299 }
300 
dom_parent_node_after(dom_object * context,zval * nodes,int nodesc)301 void dom_parent_node_after(dom_object *context, zval *nodes, int nodesc)
302 {
303 	xmlNode *prevsib = dom_object_get_node(context);
304 	xmlNodePtr newchild, parentNode;
305 	xmlNode *fragment;
306 
307 	int stricterror = dom_get_strict_error(context->document);
308 
309 	if (!prevsib->parent) {
310 		php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
311 		return;
312 	}
313 
314 	parentNode = prevsib->parent;
315 	fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc);
316 
317 	if (fragment == NULL) {
318 		return;
319 	}
320 
321 	newchild = fragment->children;
322 
323 	if (newchild) {
324 		fragment->last->next = prevsib->next;
325 		prevsib->next = newchild;
326 
327 		newchild->prev = prevsib;
328 
329 		dom_fragment_assign_parent_node(parentNode, fragment);
330 		dom_reconcile_ns(prevsib->doc, newchild);
331 	}
332 
333 	xmlFree(fragment);
334 }
335 
dom_parent_node_before(dom_object * context,zval * nodes,int nodesc)336 void dom_parent_node_before(dom_object *context, zval *nodes, int nodesc)
337 {
338 	xmlNode *nextsib = dom_object_get_node(context);
339 	xmlNodePtr newchild, prevsib, parentNode;
340 	xmlNode *fragment;
341 
342 	prevsib = nextsib->prev;
343 	parentNode = nextsib->parent;
344 	fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc);
345 
346 	if (fragment == NULL) {
347 		return;
348 	}
349 
350 	newchild = fragment->children;
351 
352 	if (newchild) {
353 		if (parentNode->children == nextsib) {
354 			parentNode->children = newchild;
355 		} else {
356 			prevsib->next = newchild;
357 		}
358 		fragment->last->next = nextsib;
359 		nextsib->prev = fragment->last;
360 
361 		newchild->prev = prevsib;
362 
363 		dom_fragment_assign_parent_node(parentNode, fragment);
364 
365 		dom_reconcile_ns(nextsib->doc, newchild);
366 	}
367 
368 	xmlFree(fragment);
369 }
370 
dom_child_node_remove(dom_object * context)371 void dom_child_node_remove(dom_object *context)
372 {
373 	xmlNode *child = dom_object_get_node(context);
374 	xmlNodePtr children;
375 	int stricterror;
376 
377 	stricterror = dom_get_strict_error(context->document);
378 
379 	if (dom_node_is_read_only(child) == SUCCESS ||
380 		(child->parent != NULL && dom_node_is_read_only(child->parent) == SUCCESS)) {
381 		php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
382 		return;
383 	}
384 
385 	if (!child->parent) {
386 		php_dom_throw_error(NOT_FOUND_ERR, stricterror);
387 		return;
388 	}
389 
390 	if (dom_node_children_valid(child->parent) == FAILURE) {
391 		return;
392 	}
393 
394 	children = child->parent->children;
395 	if (!children) {
396 		php_dom_throw_error(NOT_FOUND_ERR, stricterror);
397 		return;
398 	}
399 
400 	while (children) {
401 		if (children == child) {
402 			xmlUnlinkNode(child);
403 			return;
404 		}
405 		children = children->next;
406 	}
407 
408 	php_dom_throw_error(NOT_FOUND_ERR, stricterror);
409 }
410 
411 #endif
412