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