xref: /PHP-8.4/ext/dom/characterdata.c (revision 3c68661e)
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_properties.h"
26 
27 /*
28 * class DOMCharacterData extends DOMNode
29 *
30 * URL: https://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-FF21A306
31 * Since:
32 */
33 
34 /* For some peculiar reason, many of these methods operate on unsigned numbers.
35  * Unfortunately, "old DOM" doesn't, so we have to conditionally convert...
36  * And the reason we're using "unsigned int" instead of "unsigned zend_long" is because libxml2 internally works with ints. */
dom_convert_number_unsigned(dom_object * intern,zend_long input,unsigned int * output)37 static bool dom_convert_number_unsigned(dom_object *intern, zend_long input, unsigned int *output)
38 {
39 	if (input < 0) {
40 		if (php_dom_follow_spec_intern(intern)) {
41 			*output = (unsigned int) input;
42 		} else {
43 			php_dom_throw_error(INDEX_SIZE_ERR, dom_get_strict_error(intern->document));
44 			return false;
45 		}
46 	} else {
47 		*output = input;
48 	}
49 	return true;
50 }
51 
52 /* {{{ data	string
53 readonly=no
54 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-72AB8359
55 Since:
56 */
dom_characterdata_data_read(dom_object * obj,zval * retval)57 zend_result dom_characterdata_data_read(dom_object *obj, zval *retval)
58 {
59 	DOM_PROP_NODE(xmlNodePtr, nodep, obj);
60 	php_dom_get_content_into_zval(nodep, retval, false);
61 	return SUCCESS;
62 }
63 
dom_characterdata_data_write(dom_object * obj,zval * newval)64 zend_result dom_characterdata_data_write(dom_object *obj, zval *newval)
65 {
66 	DOM_PROP_NODE(xmlNodePtr, nodep, obj);
67 
68 	/* Typed property, this is already a string */
69 	ZEND_ASSERT(Z_TYPE_P(newval) == IS_STRING);
70 	zend_string *str = Z_STR_P(newval);
71 
72 	xmlNodeSetContentLen(nodep, BAD_CAST ZSTR_VAL(str), ZSTR_LEN(str));
73 
74 	return SUCCESS;
75 }
76 
77 /* }}} */
78 
79 /* {{{ length	long
80 readonly=yes
81 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-7D61178C
82 Since:
83 */
dom_characterdata_length_read(dom_object * obj,zval * retval)84 zend_result dom_characterdata_length_read(dom_object *obj, zval *retval)
85 {
86 	DOM_PROP_NODE(xmlNodePtr, nodep, obj);
87 
88 	long length = 0;
89 	if (nodep->content) {
90 		length = xmlUTF8Strlen(nodep->content);
91 	}
92 
93 	ZVAL_LONG(retval, length);
94 
95 	return SUCCESS;
96 }
97 
98 /* }}} */
99 
100 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-6531BCCF
101 Modern spec URL: https://dom.spec.whatwg.org/#dom-characterdata-substringdata
102 Since:
103 */
PHP_METHOD(DOMCharacterData,substringData)104 PHP_METHOD(DOMCharacterData, substringData)
105 {
106 	zval *id;
107 	xmlChar *substring;
108 	xmlNodePtr node;
109 	zend_long offset_input, count_input;
110 	unsigned int count, offset;
111 	int length;
112 	dom_object *intern;
113 
114 	id = ZEND_THIS;
115 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &offset_input, &count_input) == FAILURE) {
116 		RETURN_THROWS();
117 	}
118 
119 	DOM_GET_OBJ(node, id, xmlNodePtr, intern);
120 
121 	const xmlChar *cur = php_dom_get_content_or_empty(node);
122 
123 	length = xmlUTF8Strlen(cur);
124 	if (ZEND_LONG_INT_OVFL(offset_input) || ZEND_LONG_INT_OVFL(count_input)) {
125 		php_dom_throw_error(INDEX_SIZE_ERR, dom_get_strict_error(intern->document));
126 		RETURN_FALSE;
127 	}
128 
129 	if (!dom_convert_number_unsigned(intern, offset_input, &offset) || !dom_convert_number_unsigned(intern, count_input, &count)) {
130 		RETURN_FALSE;
131 	}
132 
133 	if (offset > (unsigned int)length) {
134 		php_dom_throw_error(INDEX_SIZE_ERR, dom_get_strict_error(intern->document));
135 		RETURN_FALSE;
136 	}
137 
138 	if (count > length - offset) {
139 		count = length - offset;
140 	}
141 
142 	substring = xmlUTF8Strsub(cur, (int)offset, (int)count);
143 
144 	if (substring) {
145 		RETVAL_STRING((char *) substring);
146 		xmlFree(substring);
147 	} else {
148 		RETVAL_EMPTY_STRING();
149 	}
150 }
151 /* }}} end dom_characterdata_substring_data */
152 
153 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-32791A2F
154 Modern spec URL: https://dom.spec.whatwg.org/#dom-characterdata-appenddata
155 Since:
156 */
dom_character_data_append_data(INTERNAL_FUNCTION_PARAMETERS)157 static void dom_character_data_append_data(INTERNAL_FUNCTION_PARAMETERS)
158 {
159 	xmlNode *nodep;
160 	dom_object *intern;
161 	char *arg;
162 	size_t arg_len;
163 
164 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg, &arg_len) == FAILURE) {
165 		RETURN_THROWS();
166 	}
167 
168 	DOM_GET_OBJ(nodep, ZEND_THIS, xmlNodePtr, intern);
169 	xmlTextConcat(nodep, BAD_CAST arg, arg_len);
170 }
171 
PHP_METHOD(DOMCharacterData,appendData)172 PHP_METHOD(DOMCharacterData, appendData)
173 {
174 	dom_character_data_append_data(INTERNAL_FUNCTION_PARAM_PASSTHRU);
175 	RETURN_TRUE;
176 }
177 
PHP_METHOD(Dom_CharacterData,appendData)178 PHP_METHOD(Dom_CharacterData, appendData)
179 {
180 	dom_character_data_append_data(INTERNAL_FUNCTION_PARAM_PASSTHRU);
181 }
182 /* }}} end dom_characterdata_append_data */
183 
184 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-3EDB695F
185 Modern spec URL: https://dom.spec.whatwg.org/#dom-characterdata-insertdata
186 Since:
187 */
dom_character_data_insert_data(INTERNAL_FUNCTION_PARAMETERS,bool return_true)188 static void dom_character_data_insert_data(INTERNAL_FUNCTION_PARAMETERS, bool return_true)
189 {
190 	zval *id;
191 	xmlChar		*first, *second;
192 	xmlNodePtr  node;
193 	char		*arg;
194 	zend_long        offset_input;
195 	unsigned int offset;
196 	int         length;
197 	size_t arg_len;
198 	dom_object	*intern;
199 
200 	id = ZEND_THIS;
201 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &offset_input, &arg, &arg_len) == FAILURE) {
202 		RETURN_THROWS();
203 	}
204 
205 	DOM_GET_OBJ(node, id, xmlNodePtr, intern);
206 
207 	const xmlChar *cur = php_dom_get_content_or_empty(node);
208 
209 	length = xmlUTF8Strlen(cur);
210 
211 	if (ZEND_LONG_INT_OVFL(offset_input)) {
212 		php_dom_throw_error(INDEX_SIZE_ERR, dom_get_strict_error(intern->document));
213 		RETURN_FALSE;
214 	}
215 
216 	if (!dom_convert_number_unsigned(intern, offset_input, &offset)) {
217 		RETURN_FALSE;
218 	}
219 
220 	if (offset > (unsigned int)length) {
221 		php_dom_throw_error(INDEX_SIZE_ERR, dom_get_strict_error(intern->document));
222 		RETURN_FALSE;
223 	}
224 
225 	first = xmlUTF8Strndup(cur, (int)offset);
226 	second = xmlUTF8Strsub(cur, (int)offset, length - (int)offset);
227 
228 	xmlNodeSetContent(node, first);
229 	xmlNodeAddContent(node, BAD_CAST arg);
230 	xmlNodeAddContent(node, second);
231 
232 	xmlFree(first);
233 	xmlFree(second);
234 
235 	if (return_true) {
236 		RETURN_TRUE;
237 	}
238 }
239 
PHP_METHOD(DOMCharacterData,insertData)240 PHP_METHOD(DOMCharacterData, insertData)
241 {
242 	dom_character_data_insert_data(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
243 }
244 
PHP_METHOD(Dom_CharacterData,insertData)245 PHP_METHOD(Dom_CharacterData, insertData)
246 {
247 	dom_character_data_insert_data(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
248 }
249 /* }}} end dom_characterdata_insert_data */
250 
251 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-7C603781
252 Modern spec URL: https://dom.spec.whatwg.org/#dom-characterdata-deletedata
253 Since:
254 */
dom_character_data_delete_data(INTERNAL_FUNCTION_PARAMETERS,bool return_true)255 static void dom_character_data_delete_data(INTERNAL_FUNCTION_PARAMETERS, bool return_true)
256 {
257 	zval *id;
258 	xmlChar    *substring, *second;
259 	xmlNodePtr  node;
260 	zend_long        offset, count_input;
261 	unsigned int count;
262 	int         length;
263 	dom_object	*intern;
264 
265 	id = ZEND_THIS;
266 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &offset, &count_input) == FAILURE) {
267 		RETURN_THROWS();
268 	}
269 
270 	DOM_GET_OBJ(node, id, xmlNodePtr, intern);
271 
272 	const xmlChar *cur = php_dom_get_content_or_empty(node);
273 
274 	length = xmlUTF8Strlen(cur);
275 
276 	if (offset < 0 || ZEND_LONG_INT_OVFL(offset) || ZEND_LONG_INT_OVFL(count_input) || offset > length) {
277 		php_dom_throw_error(INDEX_SIZE_ERR, dom_get_strict_error(intern->document));
278 		RETURN_FALSE;
279 	}
280 
281 	if (!dom_convert_number_unsigned(intern, count_input, &count)) {
282 		RETURN_FALSE;
283 	}
284 
285 	if (offset > 0) {
286 		substring = xmlUTF8Strsub(cur, 0, (int)offset);
287 	} else {
288 		substring = NULL;
289 	}
290 
291 	if (count > length - offset) {
292 		count = length - offset;
293 	}
294 
295 	second = xmlUTF8Strsub(cur, (int)offset + (int)count, length - (int)offset);
296 	substring = xmlStrcat(substring, second);
297 
298 	xmlNodeSetContent(node, substring);
299 
300 	xmlFree(second);
301 	xmlFree(substring);
302 
303 	if (return_true) {
304 		RETURN_TRUE;
305 	}
306 }
307 
PHP_METHOD(DOMCharacterData,deleteData)308 PHP_METHOD(DOMCharacterData, deleteData)
309 {
310 	dom_character_data_delete_data(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
311 }
312 
PHP_METHOD(Dom_CharacterData,deleteData)313 PHP_METHOD(Dom_CharacterData, deleteData)
314 {
315 	dom_character_data_delete_data(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
316 }
317 /* }}} end dom_characterdata_delete_data */
318 
319 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-E5CBA7FB
320 Modern spec URL: https://dom.spec.whatwg.org/#dom-characterdata-replacedata
321 Since:
322 */
dom_character_data_replace_data(INTERNAL_FUNCTION_PARAMETERS,bool return_true)323 static void dom_character_data_replace_data(INTERNAL_FUNCTION_PARAMETERS, bool return_true)
324 {
325 	zval *id;
326 	xmlChar		*substring, *second = NULL;
327 	xmlNodePtr  node;
328 	char		*arg;
329 	zend_long        offset, count_input;
330 	unsigned int count;
331 	int         length;
332 	size_t arg_len;
333 	dom_object	*intern;
334 
335 	id = ZEND_THIS;
336 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lls", &offset, &count_input, &arg, &arg_len) == FAILURE) {
337 		RETURN_THROWS();
338 	}
339 
340 	DOM_GET_OBJ(node, id, xmlNodePtr, intern);
341 
342 	const xmlChar *cur = php_dom_get_content_or_empty(node);
343 
344 	length = xmlUTF8Strlen(cur);
345 
346 	if (offset < 0 || ZEND_LONG_INT_OVFL(offset) || ZEND_LONG_INT_OVFL(count_input) || offset > length) {
347 		php_dom_throw_error(INDEX_SIZE_ERR, dom_get_strict_error(intern->document));
348 		RETURN_FALSE;
349 	}
350 
351 	if (!dom_convert_number_unsigned(intern, count_input, &count)) {
352 		RETURN_FALSE;
353 	}
354 
355 	if (offset > 0) {
356 		substring = xmlUTF8Strsub(cur, 0, (int)offset);
357 	} else {
358 		substring = NULL;
359 	}
360 
361 	if (count > length - offset) {
362 		count = length - offset;
363 	}
364 
365 	if (offset < length) {
366 		second = xmlUTF8Strsub(cur, (int)offset + count, length - (int)offset);
367 	}
368 
369 	substring = xmlStrcat(substring, BAD_CAST arg);
370 	substring = xmlStrcat(substring, second);
371 
372 	xmlNodeSetContent(node, substring);
373 
374 	if (second) {
375 		xmlFree(second);
376 	}
377 	xmlFree(substring);
378 
379 	if (return_true) {
380 		RETURN_TRUE;
381 	}
382 }
383 
PHP_METHOD(DOMCharacterData,replaceData)384 PHP_METHOD(DOMCharacterData, replaceData)
385 {
386 	dom_character_data_replace_data(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
387 }
388 
PHP_METHOD(Dom_CharacterData,replaceData)389 PHP_METHOD(Dom_CharacterData, replaceData)
390 {
391 	dom_character_data_replace_data(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
392 }
393 /* }}} end dom_characterdata_replace_data */
394 
395 #endif
396