xref: /PHP-8.4/ext/xmlreader/php_xmlreader.c (revision 5c124939)
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   | Author: Rob Richards <rrichards@php.net>                             |
14   +----------------------------------------------------------------------+
15 */
16 
17 #ifdef HAVE_CONFIG_H
18 #include <config.h>
19 #endif
20 
21 
22 #include "php.h"
23 #include "php_ini.h"
24 #include "ext/standard/info.h"
25 #include "zend_observer.h"
26 #include "php_xmlreader.h"
27 #ifdef HAVE_DOM
28 #include "ext/dom/xml_common.h"
29 #include "ext/dom/dom_ce.h"
30 #endif
31 #include <libxml/xmlreader.h>
32 #include <libxml/uri.h>
33 #include "php_xmlreader_arginfo.h"
34 
35 zend_class_entry *xmlreader_class_entry;
36 
37 static zend_object_handlers xmlreader_object_handlers;
38 
39 static HashTable xmlreader_prop_handlers;
40 
41 static zend_internal_function xmlreader_open_fn;
42 static zend_internal_function xmlreader_xml_fn;
43 
44 typedef int (*xmlreader_read_int_t)(xmlTextReaderPtr reader);
45 typedef unsigned char *(*xmlreader_read_char_t)(xmlTextReaderPtr reader);
46 typedef const unsigned char *(*xmlreader_read_const_char_t)(xmlTextReaderPtr reader);
47 
48 typedef unsigned char *(*xmlreader_read_one_char_t)(xmlTextReaderPtr reader, const unsigned char *);
49 
50 typedef struct _xmlreader_prop_handler {
51 	xmlreader_read_int_t read_int_func;
52 	xmlreader_read_const_char_t read_char_func;
53 	int type;
54 } xmlreader_prop_handler;
55 
56 #define XMLREADER_LOAD_STRING 0
57 #define XMLREADER_LOAD_FILE 1
58 
xmlreader_register_prop_handler(HashTable * prop_handler,const char * name,size_t name_len,const xmlreader_prop_handler * hnd)59 static void xmlreader_register_prop_handler(HashTable *prop_handler, const char *name, size_t name_len, const xmlreader_prop_handler *hnd)
60 {
61 	zend_string *str = zend_string_init_interned(name, name_len, true);
62 	zend_hash_add_new_ptr(prop_handler, str, (void *) hnd);
63 	zend_string_release_ex(str, true);
64 }
65 
66 #define XMLREADER_REGISTER_PROP_HANDLER(prop_handler, name, prop_read_int_func, prop_read_char_func, prop_type) do { \
67 		static const xmlreader_prop_handler hnd = {.read_int_func = prop_read_int_func, .read_char_func = prop_read_char_func, .type = prop_type}; \
68 		xmlreader_register_prop_handler(prop_handler, "" name, sizeof("" name) - 1, &hnd); \
69 	} while (0)
70 
71 /* {{{ xmlreader_property_reader */
xmlreader_property_reader(xmlreader_object * obj,xmlreader_prop_handler * hnd,zval * rv)72 static int xmlreader_property_reader(xmlreader_object *obj, xmlreader_prop_handler *hnd, zval *rv)
73 {
74 	const xmlChar *retchar = NULL;
75 	int retint = 0;
76 
77 	if (obj->ptr != NULL) {
78 		if (hnd->read_char_func) {
79 			retchar = hnd->read_char_func(obj->ptr);
80 		} else {
81 			if (hnd->read_int_func) {
82 				retint = hnd->read_int_func(obj->ptr);
83 				if (retint == -1) {
84 					zend_throw_error(NULL, "Failed to read property because no XML data has been read yet");
85 					return FAILURE;
86 				}
87 			}
88 		}
89 	}
90 
91 	switch (hnd->type) {
92 		case IS_STRING:
93 			if (retchar) {
94 				ZVAL_STRING(rv, (char *) retchar);
95 			} else {
96 				ZVAL_EMPTY_STRING(rv);
97 			}
98 			break;
99 		case _IS_BOOL:
100 			ZVAL_BOOL(rv, retint);
101 			break;
102 		case IS_LONG:
103 			ZVAL_LONG(rv, retint);
104 			break;
105 		EMPTY_SWITCH_DEFAULT_CASE()
106 	}
107 
108 	return SUCCESS;
109 }
110 /* }}} */
111 
112 /* {{{ xmlreader_get_property_ptr_ptr */
xmlreader_get_property_ptr_ptr(zend_object * object,zend_string * name,int type,void ** cache_slot)113 zval *xmlreader_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot)
114 {
115 	zval *retval = NULL;
116 	xmlreader_prop_handler *hnd = zend_hash_find_ptr(&xmlreader_prop_handlers, name);
117 
118 	if (hnd == NULL) {
119 		retval = zend_std_get_property_ptr_ptr(object, name, type, cache_slot);
120 	}
121 
122 	return retval;
123 }
124 /* }}} */
125 
xmlreader_has_property(zend_object * object,zend_string * name,int type,void ** cache_slot)126 static int xmlreader_has_property(zend_object *object, zend_string *name, int type, void **cache_slot)
127 {
128 	xmlreader_object *obj = php_xmlreader_fetch_object(object);
129 	xmlreader_prop_handler *hnd = zend_hash_find_ptr(&xmlreader_prop_handlers, name);
130 
131 	if (hnd != NULL) {
132 		if (type == ZEND_PROPERTY_EXISTS) {
133 			return 1;
134 		}
135 
136 		zval rv;
137 		if (xmlreader_property_reader(obj, hnd, &rv) == FAILURE) {
138 			return 0;
139 		}
140 
141 		bool result;
142 
143 		if (type == ZEND_PROPERTY_NOT_EMPTY) {
144 			result = zend_is_true(&rv);
145 		} else if (type == ZEND_PROPERTY_ISSET) {
146 			result = (Z_TYPE(rv) != IS_NULL);
147 		} else {
148 			ZEND_UNREACHABLE();
149 		}
150 
151 		zval_ptr_dtor(&rv);
152 
153 		return result;
154 	}
155 
156 	return zend_std_has_property(object, name, type, cache_slot);
157 }
158 
159 
160 /* {{{ xmlreader_read_property */
xmlreader_read_property(zend_object * object,zend_string * name,int type,void ** cache_slot,zval * rv)161 zval *xmlreader_read_property(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv)
162 {
163 	zval *retval = NULL;
164 	xmlreader_object *obj = php_xmlreader_fetch_object(object);
165 	xmlreader_prop_handler *hnd = zend_hash_find_ptr(&xmlreader_prop_handlers, name);
166 
167 	if (hnd != NULL) {
168 		if (xmlreader_property_reader(obj, hnd, rv) == FAILURE) {
169 			retval = &EG(uninitialized_zval);
170 		} else {
171 			retval = rv;
172 		}
173 	} else {
174 		retval = zend_std_read_property(object, name, type, cache_slot, rv);
175 	}
176 
177 	return retval;
178 }
179 /* }}} */
180 
181 /* {{{ xmlreader_write_property */
xmlreader_write_property(zend_object * object,zend_string * name,zval * value,void ** cache_slot)182 zval *xmlreader_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot)
183 {
184 	xmlreader_prop_handler *hnd = zend_hash_find_ptr(&xmlreader_prop_handlers, name);
185 
186 	if (hnd != NULL) {
187 		zend_readonly_property_modification_error_ex(ZSTR_VAL(object->ce->name), ZSTR_VAL(name));
188 	} else {
189 		value = zend_std_write_property(object, name, value, cache_slot);
190 	}
191 
192 	return value;
193 }
194 /* }}} */
195 
xmlreader_unset_property(zend_object * object,zend_string * name,void ** cache_slot)196 void xmlreader_unset_property(zend_object *object, zend_string *name, void **cache_slot)
197 {
198 	xmlreader_prop_handler *hnd = zend_hash_find_ptr(&xmlreader_prop_handlers, name);
199 
200 	if (hnd != NULL) {
201 		zend_throw_error(NULL, "Cannot unset %s::$%s", ZSTR_VAL(object->ce->name), ZSTR_VAL(name));
202 		return;
203 	}
204 
205 	zend_std_unset_property(object, name, cache_slot);
206 }
207 
208 /* {{{ */
xmlreader_get_method(zend_object ** obj,zend_string * name,const zval * key)209 static zend_function *xmlreader_get_method(zend_object **obj, zend_string *name, const zval *key)
210 {
211 	zend_function *method = zend_std_get_method(obj, name, key);
212 	if (method && (method->common.fn_flags & ZEND_ACC_STATIC) && method->common.type == ZEND_INTERNAL_FUNCTION) {
213 		/* There are only two static internal methods and they both have overrides. */
214 		if (ZSTR_LEN(name) == sizeof("xml") - 1) {
215 			return (zend_function *) &xmlreader_xml_fn;
216 		} else if (ZSTR_LEN(name) == sizeof("open") - 1) {
217 			return (zend_function *) &xmlreader_open_fn;
218 		}
219 	}
220 	return method;
221 }
222 /* }}} */
223 
xmlreader_get_debug_info(zend_object * object,int * is_temp)224 static HashTable* xmlreader_get_debug_info(zend_object *object, int *is_temp)
225 {
226 	*is_temp = 1;
227 
228 	xmlreader_object *obj = php_xmlreader_fetch_object(object);
229 	HashTable *std_props = zend_std_get_properties(object);
230 	HashTable *debug_info = zend_array_dup(std_props);
231 
232 	zend_string *string_key;
233 	xmlreader_prop_handler *entry;
234 	ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&xmlreader_prop_handlers, string_key, entry) {
235 		ZEND_ASSERT(string_key != NULL);
236 
237 		zval value;
238 		if (xmlreader_property_reader(obj, entry, &value) == SUCCESS) {
239 			zend_hash_update(debug_info, string_key, &value);
240 		}
241 	} ZEND_HASH_FOREACH_END();
242 
243 	return debug_info;
244 }
245 
246 /* {{{ _xmlreader_get_valid_file_path */
247 /* _xmlreader_get_valid_file_path and _xmlreader_get_relaxNG should be made a
248 	common function in libxml extension as code is common to a few xml extensions */
_xmlreader_get_valid_file_path(char * source,char * resolved_path,int resolved_path_len)249 char *_xmlreader_get_valid_file_path(char *source, char *resolved_path, int resolved_path_len ) {
250 	xmlURI *uri;
251 	xmlChar *escsource;
252 	char *file_dest;
253 	int isFileUri = 0;
254 
255 	uri = xmlCreateURI();
256 	if (uri == NULL) {
257 		return NULL;
258 	}
259 	escsource = xmlURIEscapeStr((xmlChar *)source, (xmlChar *)":");
260 	xmlParseURIReference(uri, (const char *)escsource);
261 	xmlFree(escsource);
262 
263 	if (uri->scheme != NULL) {
264 		/* absolute file uris - libxml only supports localhost or empty host */
265 		if (strncasecmp(source, "file:///",8) == 0) {
266 			isFileUri = 1;
267 #ifdef PHP_WIN32
268 			source += 8;
269 #else
270 			source += 7;
271 #endif
272 		} else if (strncasecmp(source, "file://localhost/",17) == 0) {
273 			isFileUri = 1;
274 #ifdef PHP_WIN32
275 			source += 17;
276 #else
277 			source += 16;
278 #endif
279 		}
280 	}
281 
282 	file_dest = source;
283 
284 	if ((uri->scheme == NULL || isFileUri)) {
285 		if (!VCWD_REALPATH(source, resolved_path) && !expand_filepath(source, resolved_path)) {
286 			xmlFreeURI(uri);
287 			return NULL;
288 		}
289 		file_dest = resolved_path;
290 	}
291 
292 	xmlFreeURI(uri);
293 
294 	return file_dest;
295 }
296 /* }}} */
297 
298 #ifdef LIBXML_SCHEMAS_ENABLED
299 /* {{{ _xmlreader_get_relaxNG */
_xmlreader_get_relaxNG(char * source,size_t source_len,size_t type,xmlRelaxNGValidityErrorFunc error_func,xmlRelaxNGValidityWarningFunc warn_func)300 static xmlRelaxNGPtr _xmlreader_get_relaxNG(char *source, size_t source_len, size_t type,
301 											xmlRelaxNGValidityErrorFunc error_func,
302 											xmlRelaxNGValidityWarningFunc warn_func)
303 {
304 	char *valid_file = NULL;
305 	xmlRelaxNGParserCtxtPtr parser = NULL;
306 	xmlRelaxNGPtr           sptr;
307 	char resolved_path[MAXPATHLEN + 1];
308 
309 	switch (type) {
310 	case XMLREADER_LOAD_FILE:
311 		valid_file = _xmlreader_get_valid_file_path(source, resolved_path, MAXPATHLEN );
312 		if (!valid_file) {
313 			return NULL;
314 		}
315 		parser = xmlRelaxNGNewParserCtxt(valid_file);
316 		break;
317 	case XMLREADER_LOAD_STRING:
318 		parser = xmlRelaxNGNewMemParserCtxt(source, source_len);
319 		/* If loading from memory, we need to set the base directory for the document
320 		   but it is not apparent how to do that for schema's */
321 		break;
322 	default:
323 		return NULL;
324 	}
325 
326 	if (parser == NULL) {
327 		return NULL;
328 	}
329 
330 	PHP_LIBXML_SANITIZE_GLOBALS(parse);
331 	if (error_func || warn_func) {
332 		xmlRelaxNGSetParserErrors(parser,
333 			(xmlRelaxNGValidityErrorFunc) error_func,
334 			(xmlRelaxNGValidityWarningFunc) warn_func,
335 			parser);
336 	}
337 	sptr = xmlRelaxNGParse(parser);
338 	xmlRelaxNGFreeParserCtxt(parser);
339 	PHP_LIBXML_RESTORE_GLOBALS(parse);
340 
341 	return sptr;
342 }
343 /* }}} */
344 #endif
345 
346 static const zend_module_dep xmlreader_deps[] = {
347 #ifdef HAVE_DOM
348 	ZEND_MOD_REQUIRED("dom")
349 #endif
350 	ZEND_MOD_REQUIRED("libxml")
351 	ZEND_MOD_END
352 };
353 
354 /* {{{ xmlreader_module_entry */
355 zend_module_entry xmlreader_module_entry = {
356 	STANDARD_MODULE_HEADER_EX, NULL,
357 	xmlreader_deps,
358 	"xmlreader",
359 	NULL,
360 	PHP_MINIT(xmlreader),
361 	PHP_MSHUTDOWN(xmlreader),
362 	NULL,
363 	NULL,
364 	PHP_MINFO(xmlreader),
365 	PHP_XMLREADER_VERSION,
366 	STANDARD_MODULE_PROPERTIES
367 };
368 /* }}} */
369 
370 #ifdef COMPILE_DL_XMLREADER
ZEND_GET_MODULE(xmlreader)371 ZEND_GET_MODULE(xmlreader)
372 #endif
373 
374 /* {{{ xmlreader_free_resources */
375 static void xmlreader_free_resources(xmlreader_object *intern) {
376 	if (intern->input) {
377 		xmlFreeParserInputBuffer(intern->input);
378 		intern->input = NULL;
379 	}
380 
381 	if (intern->ptr) {
382 		xmlFreeTextReader(intern->ptr);
383 		intern->ptr = NULL;
384 	}
385 #ifdef LIBXML_SCHEMAS_ENABLED
386 	if (intern->schema) {
387 		xmlRelaxNGFree((xmlRelaxNGPtr) intern->schema);
388 		intern->schema = NULL;
389 	}
390 #endif
391 }
392 /* }}} */
393 
394 /* {{{ xmlreader_objects_free_storage */
xmlreader_objects_free_storage(zend_object * object)395 void xmlreader_objects_free_storage(zend_object *object)
396 {
397 	xmlreader_object *intern = php_xmlreader_fetch_object(object);
398 
399 	zend_object_std_dtor(&intern->std);
400 
401 	xmlreader_free_resources(intern);
402 }
403 /* }}} */
404 
405 /* {{{ xmlreader_objects_new */
xmlreader_objects_new(zend_class_entry * class_type)406 zend_object *xmlreader_objects_new(zend_class_entry *class_type)
407 {
408 	xmlreader_object *intern;
409 
410 	intern = zend_object_alloc(sizeof(xmlreader_object), class_type);
411 	zend_object_std_init(&intern->std, class_type);
412 	object_properties_init(&intern->std, class_type);
413 
414 	return &intern->std;
415 }
416 /* }}} */
417 
418 /* {{{ php_xmlreader_string_arg */
php_xmlreader_string_arg(INTERNAL_FUNCTION_PARAMETERS,xmlreader_read_one_char_t internal_function)419 static void php_xmlreader_string_arg(INTERNAL_FUNCTION_PARAMETERS, xmlreader_read_one_char_t internal_function) {
420 	zval *id;
421 	size_t name_len = 0;
422 	char *retchar = NULL;
423 	xmlreader_object *intern;
424 	char *name;
425 
426 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
427 		RETURN_THROWS();
428 	}
429 
430 	if (!name_len) {
431 		zend_argument_must_not_be_empty_error(1);
432 		RETURN_THROWS();
433 	}
434 
435 	id = ZEND_THIS;
436 
437 	intern = Z_XMLREADER_P(id);
438 	if (intern->ptr) {
439 		retchar = (char *)internal_function(intern->ptr, (const unsigned char *)name);
440 	}
441 	if (retchar) {
442 		RETVAL_STRING(retchar);
443 		xmlFree(retchar);
444 		return;
445 	} else {
446 		RETVAL_NULL();
447 	}
448 }
449 /* }}} */
450 
451 /* {{{ php_xmlreader_no_arg */
php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAMETERS,xmlreader_read_int_t internal_function)452 static void php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAMETERS, xmlreader_read_int_t internal_function) {
453 	zval *id;
454 	int retval;
455 	xmlreader_object *intern;
456 
457 	if (zend_parse_parameters_none() == FAILURE) {
458 		RETURN_THROWS();
459 	}
460 
461 	id = ZEND_THIS;
462 
463 	intern = Z_XMLREADER_P(id);
464 	if (intern->ptr) {
465 		retval = internal_function(intern->ptr);
466 		if (retval == 1) {
467 			RETURN_TRUE;
468 		}
469 	}
470 
471 	RETURN_FALSE;
472 }
473 /* }}} */
474 
475 /* {{{ php_xmlreader_no_arg_string */
php_xmlreader_no_arg_string(INTERNAL_FUNCTION_PARAMETERS,xmlreader_read_char_t internal_function)476 static void php_xmlreader_no_arg_string(INTERNAL_FUNCTION_PARAMETERS, xmlreader_read_char_t internal_function) {
477 	zval *id;
478 	char *retchar = NULL;
479 	xmlreader_object *intern;
480 
481 	if (zend_parse_parameters_none() == FAILURE) {
482 		RETURN_THROWS();
483 	}
484 
485 	id = ZEND_THIS;
486 
487 	intern = Z_XMLREADER_P(id);
488 	if (intern->ptr) {
489 		retchar = (char *)internal_function(intern->ptr);
490 	}
491 	if (retchar) {
492 		RETVAL_STRING(retchar);
493 		xmlFree(retchar);
494 		return;
495 	} else {
496 		RETVAL_EMPTY_STRING();
497 	}
498 }
499 /* }}} */
500 
501 /* {{{ php_xmlreader_set_relaxng_schema */
php_xmlreader_set_relaxng_schema(INTERNAL_FUNCTION_PARAMETERS,int type)502 static void php_xmlreader_set_relaxng_schema(INTERNAL_FUNCTION_PARAMETERS, int type) {
503 #ifdef LIBXML_SCHEMAS_ENABLED
504 	zval *id;
505 	size_t source_len = 0;
506 	int retval = -1;
507 	xmlreader_object *intern;
508 	xmlRelaxNGPtr schema = NULL;
509 	char *source;
510 
511 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p!", &source, &source_len) == FAILURE) {
512 		RETURN_THROWS();
513 	}
514 
515 	if (source != NULL && !source_len) {
516 		zend_argument_must_not_be_empty_error(1);
517 		RETURN_THROWS();
518 	}
519 
520 	id = ZEND_THIS;
521 
522 	intern = Z_XMLREADER_P(id);
523 	if (intern->ptr) {
524 		if (source) {
525 			schema =  _xmlreader_get_relaxNG(source, source_len, type, NULL, NULL);
526 			if (schema) {
527 				retval = xmlTextReaderRelaxNGSetSchema(intern->ptr, schema);
528 			}
529 		} else {
530 			/* unset the associated relaxNG context and schema if one exists */
531 			retval = xmlTextReaderRelaxNGSetSchema(intern->ptr, NULL);
532 		}
533 
534 		if (retval == 0) {
535 			if (intern->schema) {
536 				xmlRelaxNGFree((xmlRelaxNGPtr) intern->schema);
537 			}
538 
539 			intern->schema = schema;
540 
541 			RETURN_TRUE;
542 		} else {
543 			php_error_docref(NULL, E_WARNING, "Schema contains errors");
544 			RETURN_FALSE;
545 		}
546 	} else {
547 		zend_throw_error(NULL, "Schema must be set prior to reading");
548 		RETURN_THROWS();
549 	}
550 #else
551 	php_error_docref(NULL, E_WARNING, "No schema support built into libxml");
552 	RETURN_FALSE;
553 #endif
554 }
555 /* }}} */
556 
557 /* {{{ Closes xmlreader - current frees resources until xmlTextReaderClose is fixed in libxml */
PHP_METHOD(XMLReader,close)558 PHP_METHOD(XMLReader, close)
559 {
560 	zval *id;
561 	xmlreader_object *intern;
562 
563 	if (zend_parse_parameters_none() == FAILURE) {
564 		RETURN_THROWS();
565 	}
566 
567 	id = ZEND_THIS;
568 	intern = Z_XMLREADER_P(id);
569 	/* libxml is segfaulting in versions up to 2.6.8 using xmlTextReaderClose so for
570 	now we will free the whole reader when close is called as it would get rebuilt on
571 	a new load anyways */
572 	xmlreader_free_resources(intern);
573 
574 	RETURN_TRUE;
575 }
576 /* }}} */
577 
578 /* {{{ Get value of an attribute from current element */
PHP_METHOD(XMLReader,getAttribute)579 PHP_METHOD(XMLReader, getAttribute)
580 {
581 	php_xmlreader_string_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderGetAttribute);
582 }
583 /* }}} */
584 
585 /* {{{ Get value of an attribute at index from current element */
PHP_METHOD(XMLReader,getAttributeNo)586 PHP_METHOD(XMLReader, getAttributeNo)
587 {
588 	zval *id;
589 	zend_long attr_pos;
590 	char *retchar = NULL;
591 	xmlreader_object *intern;
592 
593 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &attr_pos) == FAILURE) {
594 		RETURN_THROWS();
595 	}
596 
597 	id = ZEND_THIS;
598 
599 	intern = Z_XMLREADER_P(id);
600 	if (intern->ptr) {
601 		retchar = (char *)xmlTextReaderGetAttributeNo(intern->ptr, attr_pos);
602 	}
603 	if (retchar) {
604 		RETVAL_STRING(retchar);
605 		xmlFree(retchar);
606 	}
607 }
608 /* }}} */
609 
610 /* {{{ Get value of a attribute via name and namespace from current element */
PHP_METHOD(XMLReader,getAttributeNs)611 PHP_METHOD(XMLReader, getAttributeNs)
612 {
613 	zval *id;
614 	size_t name_len = 0, ns_uri_len = 0;
615 	xmlreader_object *intern;
616 	char *name, *ns_uri, *retchar = NULL;
617 
618 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &name, &name_len, &ns_uri, &ns_uri_len) == FAILURE) {
619 		RETURN_THROWS();
620 	}
621 
622 	if (name_len == 0) {
623 		zend_argument_must_not_be_empty_error(1);
624 		RETURN_THROWS();
625 	}
626 
627 	if (ns_uri_len == 0) {
628 		zend_argument_must_not_be_empty_error(2);
629 		RETURN_THROWS();
630 	}
631 
632 	id = ZEND_THIS;
633 
634 	intern = Z_XMLREADER_P(id);
635 	if (intern->ptr) {
636 		retchar = (char *)xmlTextReaderGetAttributeNs(intern->ptr, (xmlChar *)name, (xmlChar *)ns_uri);
637 	}
638 	if (retchar) {
639 		RETVAL_STRING(retchar);
640 		xmlFree(retchar);
641 	}
642 }
643 /* }}} */
644 
645 /* {{{ Indicates whether given property (one of the parser option constants) is set or not on parser */
PHP_METHOD(XMLReader,getParserProperty)646 PHP_METHOD(XMLReader, getParserProperty)
647 {
648 	zval *id;
649 	zend_long property;
650 	xmlreader_object *intern;
651 
652 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &property) == FAILURE) {
653 		RETURN_THROWS();
654 	}
655 
656 	id = ZEND_THIS;
657 
658 	intern = Z_XMLREADER_P(id);
659 	if (!intern || !intern->ptr) {
660 		zend_throw_error(NULL, "Cannot access parser properties before loading data");
661 		RETURN_THROWS();
662 	}
663 
664 	int retval = xmlTextReaderGetParserProp(intern->ptr,property);
665 	if (retval == -1) {
666 		zend_argument_value_error(1, "must be a valid parser property");
667 		RETURN_THROWS();
668 	}
669 
670 	RETURN_BOOL(retval);
671 }
672 /* }}} */
673 
674 /* {{{ Returns boolean indicating if parsed document is valid or not.
675 Must set XMLREADER_LOADDTD or XMLREADER_VALIDATE parser option prior to the first call to read
676 or this method will always return FALSE */
PHP_METHOD(XMLReader,isValid)677 PHP_METHOD(XMLReader, isValid)
678 {
679 	php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderIsValid);
680 }
681 /* }}} */
682 
683 /* {{{ Return namespaceURI for associated prefix on current node */
PHP_METHOD(XMLReader,lookupNamespace)684 PHP_METHOD(XMLReader, lookupNamespace)
685 {
686 	php_xmlreader_string_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderLookupNamespace);
687 }
688 /* }}} */
689 
690 /* {{{ Positions reader at specified attribute - Returns TRUE on success and FALSE on failure */
PHP_METHOD(XMLReader,moveToAttribute)691 PHP_METHOD(XMLReader, moveToAttribute)
692 {
693 	zval *id;
694 	size_t name_len = 0;
695 	int retval;
696 	xmlreader_object *intern;
697 	char *name;
698 
699 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
700 		RETURN_THROWS();
701 	}
702 
703 	if (name_len == 0) {
704 		zend_argument_must_not_be_empty_error(1);
705 		RETURN_THROWS();
706 	}
707 
708 	id = ZEND_THIS;
709 
710 	intern = Z_XMLREADER_P(id);
711 	if (intern->ptr) {
712 		retval = xmlTextReaderMoveToAttribute(intern->ptr, (xmlChar *)name);
713 		if (retval == 1) {
714 			RETURN_TRUE;
715 		}
716 	}
717 
718 	RETURN_FALSE;
719 }
720 /* }}} */
721 
722 /* {{{ Positions reader at attribute at specified index.
723 Returns TRUE on success and FALSE on failure */
PHP_METHOD(XMLReader,moveToAttributeNo)724 PHP_METHOD(XMLReader, moveToAttributeNo)
725 {
726 	zval *id;
727 	zend_long attr_pos;
728 	int retval;
729 	xmlreader_object *intern;
730 
731 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &attr_pos) == FAILURE) {
732 		RETURN_THROWS();
733 	}
734 
735 	id = ZEND_THIS;
736 
737 	intern = Z_XMLREADER_P(id);
738 	if (intern->ptr) {
739 		retval = xmlTextReaderMoveToAttributeNo(intern->ptr, attr_pos);
740 		if (retval == 1) {
741 			RETURN_TRUE;
742 		}
743 	}
744 
745 	RETURN_FALSE;
746 }
747 /* }}} */
748 
749 /* {{{ Positions reader at attribute spcified by name and namespaceURI.
750 Returns TRUE on success and FALSE on failure */
PHP_METHOD(XMLReader,moveToAttributeNs)751 PHP_METHOD(XMLReader, moveToAttributeNs)
752 {
753 	zval *id;
754 	size_t name_len=0, ns_uri_len=0;
755 	int retval;
756 	xmlreader_object *intern;
757 	char *name, *ns_uri;
758 
759 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &name, &name_len, &ns_uri, &ns_uri_len) == FAILURE) {
760 		RETURN_THROWS();
761 	}
762 
763 	if (name_len == 0) {
764 		zend_argument_must_not_be_empty_error(1);
765 		RETURN_THROWS();
766 	}
767 
768 	if (ns_uri_len == 0) {
769 		zend_argument_must_not_be_empty_error(2);
770 		RETURN_THROWS();
771 	}
772 
773 	id = ZEND_THIS;
774 
775 	intern = Z_XMLREADER_P(id);
776 	if (intern->ptr) {
777 		retval = xmlTextReaderMoveToAttributeNs(intern->ptr, (xmlChar *)name, (xmlChar *)ns_uri);
778 		if (retval == 1) {
779 			RETURN_TRUE;
780 		}
781 	}
782 
783 	RETURN_FALSE;
784 }
785 /* }}} */
786 
787 /* {{{ Moves the position of the current instance to the node that contains the current Attribute node. */
PHP_METHOD(XMLReader,moveToElement)788 PHP_METHOD(XMLReader, moveToElement)
789 {
790 	php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderMoveToElement);
791 }
792 /* }}} */
793 
794 /* {{{ Moves the position of the current instance to the first attribute associated with the current node. */
PHP_METHOD(XMLReader,moveToFirstAttribute)795 PHP_METHOD(XMLReader, moveToFirstAttribute)
796 {
797 	php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderMoveToFirstAttribute);
798 }
799 /* }}} */
800 
801 /* {{{ Moves the position of the current instance to the next attribute associated with the current node. */
PHP_METHOD(XMLReader,moveToNextAttribute)802 PHP_METHOD(XMLReader, moveToNextAttribute)
803 {
804 	php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderMoveToNextAttribute);
805 }
806 /* }}} */
807 
808 /* {{{ Moves the position of the current instance to the next node in the stream. */
PHP_METHOD(XMLReader,read)809 PHP_METHOD(XMLReader, read)
810 {
811 	zval *id;
812 	int retval;
813 	xmlreader_object *intern;
814 
815 	if (zend_parse_parameters_none() == FAILURE) {
816 		RETURN_THROWS();
817 	}
818 
819 	id = ZEND_THIS;
820 	intern = Z_XMLREADER_P(id);
821 	if (!intern->ptr) {
822 		zend_throw_error(NULL, "Data must be loaded before reading");
823 		RETURN_THROWS();
824 	}
825 
826 	retval = xmlTextReaderRead(intern->ptr);
827 	if (retval == -1) {
828 		RETURN_FALSE;
829 	} else {
830 		RETURN_BOOL(retval);
831 	}
832 }
833 /* }}} */
834 
835 /* {{{ Moves the position of the current instance to the next node in the stream. */
PHP_METHOD(XMLReader,next)836 PHP_METHOD(XMLReader, next)
837 {
838 	zval *id;
839 	int retval;
840 	size_t name_len=0;
841 	xmlreader_object *intern;
842 	char *name = NULL;
843 
844 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &name, &name_len) == FAILURE) {
845 		RETURN_THROWS();
846 	}
847 
848 	id = ZEND_THIS;
849 	intern = Z_XMLREADER_P(id);
850 	if (intern->ptr) {
851 		retval = xmlTextReaderNext(intern->ptr);
852 		while (name != NULL && retval == 1) {
853 			if (xmlStrEqual(xmlTextReaderConstLocalName(intern->ptr), (xmlChar *)name)) {
854 				RETURN_TRUE;
855 			}
856 			retval = xmlTextReaderNext(intern->ptr);
857 		}
858 		if (retval == -1) {
859 			RETURN_FALSE;
860 		} else {
861 			RETURN_BOOL(retval);
862 		}
863 	}
864 
865 	zend_throw_error(NULL, "Data must be loaded before reading");
866 }
867 /* }}} */
868 
xmlreader_valid_encoding(const char * encoding)869 static bool xmlreader_valid_encoding(const char *encoding)
870 {
871 	if (!encoding) {
872 		return true;
873 	}
874 
875 	/* Normally we could use xmlTextReaderConstEncoding() afterwards but libxml2 < 2.12.0 has a bug of course
876 	 * where it returns NULL for some valid encodings instead. */
877 	xmlCharEncodingHandlerPtr handler = xmlFindCharEncodingHandler(encoding);
878 	if (!handler) {
879 		return false;
880 	}
881 	xmlCharEncCloseFunc(handler);
882 	return true;
883 }
884 
885 /* {{{ Sets the URI that the XMLReader will parse. */
xml_reader_from_uri(INTERNAL_FUNCTION_PARAMETERS,zend_class_entry * instance_ce,bool use_exceptions)886 static void xml_reader_from_uri(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *instance_ce, bool use_exceptions)
887 {
888 	zval *id;
889 	size_t source_len = 0, encoding_len = 0;
890 	zend_long options = 0;
891 	xmlreader_object *intern = NULL;
892 	char *source, *valid_file = NULL;
893 	char *encoding = NULL;
894 	char resolved_path[MAXPATHLEN + 1];
895 	xmlTextReaderPtr reader = NULL;
896 
897 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|p!l", &source, &source_len, &encoding, &encoding_len, &options) == FAILURE) {
898 		RETURN_THROWS();
899 	}
900 
901 	id = getThis();
902 	if (id != NULL) {
903 		ZEND_ASSERT(instanceof_function(Z_OBJCE_P(id), xmlreader_class_entry));
904 		intern = Z_XMLREADER_P(id);
905 		xmlreader_free_resources(intern);
906 	}
907 
908 	if (!source_len) {
909 		zend_argument_must_not_be_empty_error(1);
910 		RETURN_THROWS();
911 	}
912 
913 	if (!xmlreader_valid_encoding(encoding)) {
914 		zend_argument_value_error(2, "must be a valid character encoding");
915 		RETURN_THROWS();
916 	}
917 
918 	valid_file = _xmlreader_get_valid_file_path(source, resolved_path, MAXPATHLEN );
919 
920 	if (valid_file) {
921 		PHP_LIBXML_SANITIZE_GLOBALS(reader_for_file);
922 		reader = xmlReaderForFile(valid_file, encoding, options);
923 		PHP_LIBXML_RESTORE_GLOBALS(reader_for_file);
924 	}
925 
926 	if (reader == NULL) {
927 		if (use_exceptions) {
928 			zend_throw_error(NULL, "Unable to open source data");
929 			RETURN_THROWS();
930 		} else {
931 			php_error_docref(NULL, E_WARNING, "Unable to open source data");
932 			RETURN_FALSE;
933 		}
934 	}
935 
936 	if (id == NULL) {
937 		if (UNEXPECTED(object_init_with_constructor(return_value, instance_ce, 0, NULL, NULL) != SUCCESS)) {
938 			xmlFreeTextReader(reader);
939 			RETURN_THROWS();
940 		}
941 		intern = Z_XMLREADER_P(return_value);
942 		intern->ptr = reader;
943 		return;
944 	}
945 
946 	intern->ptr = reader;
947 
948 	RETURN_TRUE;
949 
950 }
951 
PHP_METHOD(XMLReader,open)952 PHP_METHOD(XMLReader, open)
953 {
954 	xml_reader_from_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlreader_class_entry, false);
955 }
956 
PHP_METHOD(XMLReader,fromUri)957 PHP_METHOD(XMLReader, fromUri)
958 {
959 	xml_reader_from_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, Z_CE_P(ZEND_THIS), true);
960 }
961 /* }}} */
962 
xml_reader_stream_read(void * context,char * buffer,int len)963 static int xml_reader_stream_read(void *context, char *buffer, int len)
964 {
965 	zend_resource *resource = context;
966 	if (EXPECTED(resource->ptr)) {
967 		php_stream *stream = resource->ptr;
968 		return php_stream_read(stream, buffer, len);
969 	}
970 	return -1;
971 }
972 
xml_reader_stream_close(void * context)973 static int xml_reader_stream_close(void *context)
974 {
975 	zend_resource *resource = context;
976 	/* Don't close it as others may still use it! We don't own the resource!
977 	 * Just delete our reference (and clean up if we're the last one). */
978 	zend_list_delete(resource);
979 	return 0;
980 }
981 
PHP_METHOD(XMLReader,fromStream)982 PHP_METHOD(XMLReader, fromStream)
983 {
984 	zval *stream_zv;
985 	php_stream *stream;
986 	char *document_uri = NULL;
987 	char *encoding_name = NULL;
988 	size_t document_uri_len, encoding_name_len;
989 	zend_long flags = 0;
990 
991 	ZEND_PARSE_PARAMETERS_START(1, 4)
992 		Z_PARAM_RESOURCE(stream_zv);
993 		Z_PARAM_OPTIONAL
994 		Z_PARAM_PATH_OR_NULL(encoding_name, encoding_name_len)
995 		Z_PARAM_LONG(flags)
996 		Z_PARAM_PATH_OR_NULL(document_uri, document_uri_len)
997 	ZEND_PARSE_PARAMETERS_END();
998 
999 	php_stream_from_res(stream, Z_RES_P(stream_zv));
1000 
1001 	if (!xmlreader_valid_encoding(encoding_name)) {
1002 		zend_argument_value_error(2, "must be a valid character encoding");
1003 		RETURN_THROWS();
1004 	}
1005 
1006 	PHP_LIBXML_SANITIZE_GLOBALS(reader_for_stream);
1007 	xmlTextReaderPtr reader = xmlReaderForIO(
1008 		xml_reader_stream_read,
1009 		xml_reader_stream_close,
1010 		stream->res,
1011 		document_uri,
1012 		encoding_name,
1013 		flags
1014 	);
1015 	PHP_LIBXML_RESTORE_GLOBALS(reader_for_stream);
1016 
1017 	if (UNEXPECTED(reader == NULL)) {
1018 		zend_throw_error(NULL, "Could not construct libxml reader");
1019 		RETURN_THROWS();
1020 	}
1021 
1022 	/* When the reader is closed (even in error paths) the reference is destroyed. */
1023 	Z_ADDREF_P(stream_zv);
1024 
1025 	if (object_init_with_constructor(return_value, Z_CE_P(ZEND_THIS), 0, NULL, NULL) == SUCCESS) {
1026 		xmlreader_object *intern = Z_XMLREADER_P(return_value);
1027 		intern->ptr = reader;
1028 	} else {
1029 		xmlFreeTextReader(reader);
1030 	}
1031 }
1032 
1033 /* Not Yet Implemented in libxml - functions exist just not coded
1034 PHP_METHOD(XMLReader, resetState)
1035 {
1036 
1037 }
1038 */
1039 
1040 /* {{{ Reads the contents of the current node, including child nodes and markup. */
PHP_METHOD(XMLReader,readInnerXml)1041 PHP_METHOD(XMLReader, readInnerXml)
1042 {
1043 	php_xmlreader_no_arg_string(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderReadInnerXml);
1044 }
1045 /* }}} */
1046 
1047 /* {{{ Reads the contents of the current node, including child nodes and markup. */
PHP_METHOD(XMLReader,readOuterXml)1048 PHP_METHOD(XMLReader, readOuterXml)
1049 {
1050 	php_xmlreader_no_arg_string(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderReadOuterXml);
1051 }
1052 /* }}} */
1053 
1054 /* {{{ Reads the contents of an element or a text node as a string. */
PHP_METHOD(XMLReader,readString)1055 PHP_METHOD(XMLReader, readString)
1056 {
1057 	php_xmlreader_no_arg_string(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderReadString);
1058 }
1059 /* }}} */
1060 
1061 /* {{{ Use W3C XSD schema to validate the document as it is processed. Activation is only possible before the first Read(). */
PHP_METHOD(XMLReader,setSchema)1062 PHP_METHOD(XMLReader, setSchema)
1063 {
1064 #ifdef LIBXML_SCHEMAS_ENABLED
1065 	zval *id;
1066 	size_t source_len = 0;
1067 	int retval = -1;
1068 	xmlreader_object *intern;
1069 	char *source;
1070 
1071 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p!", &source, &source_len) == FAILURE) {
1072 		RETURN_THROWS();
1073 	}
1074 
1075 	if (source != NULL && !source_len) {
1076 		zend_argument_must_not_be_empty_error(1);
1077 		RETURN_THROWS();
1078 	}
1079 
1080 	id = ZEND_THIS;
1081 
1082 	intern = Z_XMLREADER_P(id);
1083 	if (intern && intern->ptr) {
1084 		PHP_LIBXML_SANITIZE_GLOBALS(schema);
1085 		retval = xmlTextReaderSchemaValidate(intern->ptr, source);
1086 		PHP_LIBXML_RESTORE_GLOBALS(schema);
1087 
1088 		if (retval == 0) {
1089 			RETURN_TRUE;
1090 		} else {
1091 			php_error_docref(NULL, E_WARNING, "Schema contains errors");
1092 			RETURN_FALSE;
1093 		}
1094 	} else {
1095 		zend_throw_error(NULL, "Schema must be set prior to reading");
1096 		RETURN_THROWS();
1097 	}
1098 #else
1099 	php_error_docref(NULL, E_WARNING, "No schema support built into libxml");
1100 	RETURN_FALSE;
1101 #endif
1102 }
1103 /* }}} */
1104 
1105 /* {{{ Sets parser property (one of the parser option constants).
1106 Properties must be set after open() or XML() and before the first read() is called */
PHP_METHOD(XMLReader,setParserProperty)1107 PHP_METHOD(XMLReader, setParserProperty)
1108 {
1109 	zval *id;
1110 	zend_long property;
1111 	bool value;
1112 	xmlreader_object *intern;
1113 
1114 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lb", &property, &value) == FAILURE) {
1115 		RETURN_THROWS();
1116 	}
1117 
1118 	id = ZEND_THIS;
1119 
1120 	intern = Z_XMLREADER_P(id);
1121 	if (!intern || !intern->ptr) {
1122 		zend_throw_error(NULL, "Cannot access parser properties before loading data");
1123 		RETURN_THROWS();
1124 	}
1125 
1126 	int retval = xmlTextReaderSetParserProp(intern->ptr,property, value);
1127 	if (retval == -1) {
1128 		zend_argument_value_error(1, "must be a valid parser property");
1129 		RETURN_THROWS();
1130 	}
1131 
1132 	RETURN_TRUE;
1133 }
1134 /* }}} */
1135 
1136 /* {{{ Sets the string that the XMLReader will parse. */
PHP_METHOD(XMLReader,setRelaxNGSchema)1137 PHP_METHOD(XMLReader, setRelaxNGSchema)
1138 {
1139 	php_xmlreader_set_relaxng_schema(INTERNAL_FUNCTION_PARAM_PASSTHRU, XMLREADER_LOAD_FILE);
1140 }
1141 /* }}} */
1142 
1143 /* {{{ Sets the string that the XMLReader will parse. */
PHP_METHOD(XMLReader,setRelaxNGSchemaSource)1144 PHP_METHOD(XMLReader, setRelaxNGSchemaSource)
1145 {
1146 	php_xmlreader_set_relaxng_schema(INTERNAL_FUNCTION_PARAM_PASSTHRU, XMLREADER_LOAD_STRING);
1147 }
1148 /* }}} */
1149 
1150 /* TODO
1151 XMLPUBFUN int XMLCALL
1152 		    xmlTextReaderSetSchema	(xmlTextReaderPtr reader,
1153 		    				 xmlSchemaPtr schema);
1154 */
1155 
1156 /* {{{ Sets the string that the XMLReader will parse. */
xml_reader_from_string(INTERNAL_FUNCTION_PARAMETERS,zend_class_entry * instance_ce,bool throw)1157 static void xml_reader_from_string(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *instance_ce, bool throw)
1158 {
1159 	zval *id;
1160 	size_t source_len = 0, encoding_len = 0;
1161 	zend_long options = 0;
1162 	xmlreader_object *intern = NULL;
1163 	char *source, *uri = NULL, *encoding = NULL;
1164 	int resolved_path_len, ret = 0;
1165 	char *directory=NULL, resolved_path[MAXPATHLEN + 1];
1166 	xmlParserInputBufferPtr inputbfr;
1167 	xmlTextReaderPtr reader;
1168 
1169 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|p!l", &source, &source_len, &encoding, &encoding_len, &options) == FAILURE) {
1170 		RETURN_THROWS();
1171 	}
1172 
1173 	id = getThis();
1174 	if (id != NULL) {
1175 		ZEND_ASSERT(instanceof_function(Z_OBJCE_P(id), xmlreader_class_entry));
1176 		intern = Z_XMLREADER_P(id);
1177 		xmlreader_free_resources(intern);
1178 	}
1179 
1180 	if (!source_len) {
1181 		zend_argument_must_not_be_empty_error(1);
1182 		RETURN_THROWS();
1183 	}
1184 
1185 	if (!xmlreader_valid_encoding(encoding)) {
1186 		zend_argument_value_error(2, "must be a valid character encoding");
1187 		RETURN_THROWS();
1188 	}
1189 
1190 	inputbfr = xmlParserInputBufferCreateMem(source, source_len, XML_CHAR_ENCODING_NONE);
1191 
1192 	if (inputbfr != NULL) {
1193 /* Get the URI of the current script so that we can set the base directory in libxml */
1194 #ifdef HAVE_GETCWD
1195 		directory = VCWD_GETCWD(resolved_path, MAXPATHLEN);
1196 #elif defined(HAVE_GETWD)
1197 		directory = VCWD_GETWD(resolved_path);
1198 #endif
1199 		if (directory) {
1200 			resolved_path_len = strlen(resolved_path);
1201 			if (resolved_path[resolved_path_len - 1] != DEFAULT_SLASH) {
1202 				resolved_path[resolved_path_len] = DEFAULT_SLASH;
1203 				resolved_path[++resolved_path_len] = '\0';
1204 			}
1205 			uri = (char *) xmlCanonicPath((const xmlChar *) resolved_path);
1206 		}
1207 		PHP_LIBXML_SANITIZE_GLOBALS(text_reader);
1208 		reader = xmlNewTextReader(inputbfr, uri);
1209 
1210 		if (reader != NULL) {
1211 			ret = xmlTextReaderSetup(reader, NULL, uri, encoding, options);
1212 			if (ret == 0) {
1213 				if (id == NULL) {
1214 					if (UNEXPECTED(object_init_with_constructor(return_value, instance_ce, 0, NULL, NULL) != SUCCESS)) {
1215 						xmlFree(uri);
1216 						xmlFreeParserInputBuffer(inputbfr);
1217 						xmlFreeTextReader(reader);
1218 						RETURN_THROWS();
1219 					}
1220 					intern = Z_XMLREADER_P(return_value);
1221 				} else {
1222 					RETVAL_TRUE;
1223 				}
1224 				intern->input = inputbfr;
1225 				intern->ptr = reader;
1226 
1227 				if (uri) {
1228 					xmlFree(uri);
1229 				}
1230 
1231 				PHP_LIBXML_RESTORE_GLOBALS(text_reader);
1232 				return;
1233 			}
1234 		}
1235 		PHP_LIBXML_RESTORE_GLOBALS(text_reader);
1236 	}
1237 
1238 	if (uri) {
1239 		xmlFree(uri);
1240 	}
1241 
1242 	if (inputbfr) {
1243 		xmlFreeParserInputBuffer(inputbfr);
1244 	}
1245 
1246 	if (throw) {
1247 		zend_throw_error(NULL, "Unable to load source data");
1248 		RETURN_THROWS();
1249 	} else {
1250 		php_error_docref(NULL, E_WARNING, "Unable to load source data");
1251 		RETURN_FALSE;
1252 	}
1253 }
1254 /* }}} */
1255 
PHP_METHOD(XMLReader,XML)1256 PHP_METHOD(XMLReader, XML)
1257 {
1258 	xml_reader_from_string(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlreader_class_entry, false);
1259 }
1260 
PHP_METHOD(XMLReader,fromString)1261 PHP_METHOD(XMLReader, fromString)
1262 {
1263 	xml_reader_from_string(INTERNAL_FUNCTION_PARAM_PASSTHRU, Z_CE_P(ZEND_THIS), true);
1264 }
1265 
1266 /* {{{ Moves the position of the current instance to the next node in the stream. */
PHP_METHOD(XMLReader,expand)1267 PHP_METHOD(XMLReader, expand)
1268 {
1269 #ifdef HAVE_DOM
1270 	zval *id, *basenode = NULL;
1271 	xmlreader_object *intern;
1272 	xmlNode *node, *nodec;
1273 	xmlDocPtr docp = NULL;
1274 	php_libxml_node_object *domobj = NULL;
1275 
1276 	id = ZEND_THIS;
1277 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!", &basenode, dom_node_class_entry) == FAILURE) {
1278 		RETURN_THROWS();
1279 	}
1280 
1281 	if (basenode != NULL) {
1282 		/* Note: cannot use NODE_GET_OBJ here because of the wrong return type */
1283 		domobj = Z_LIBXML_NODE_P(basenode);
1284 		if (UNEXPECTED(domobj->node == NULL)) {
1285 			php_error_docref(NULL, E_WARNING, "Couldn't fetch %s", ZSTR_VAL(Z_OBJCE_P(basenode)->name));
1286 			RETURN_FALSE;
1287 		}
1288 		node = domobj->node->node;
1289 		docp = node->doc;
1290 	}
1291 
1292 	intern = Z_XMLREADER_P(id);
1293 
1294 	if (intern->ptr) {
1295 		node = xmlTextReaderExpand(intern->ptr);
1296 
1297 		if (node == NULL) {
1298 			php_error_docref(NULL, E_WARNING, "An Error Occurred while expanding");
1299 			RETURN_FALSE;
1300 		} else {
1301 			nodec = xmlDocCopyNode(node, docp, 1);
1302 			if (nodec == NULL) {
1303 				php_error_docref(NULL, E_NOTICE, "Cannot expand this node type");
1304 				RETURN_FALSE;
1305 			} else {
1306 				DOM_RET_OBJ(nodec, (dom_object *)domobj);
1307 			}
1308 		}
1309 	} else {
1310 		zend_throw_error(NULL, "Data must be loaded before expanding");
1311 		RETURN_THROWS();
1312 	}
1313 #else
1314 	zval *dummy;
1315 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|z!", &dummy) == FAILURE) {
1316 		RETURN_THROWS();
1317 	}
1318 
1319 	zend_throw_error(NULL, "XMLReader::expand() requires the DOM extension to be enabled");
1320 	RETURN_THROWS();
1321 #endif
1322 }
1323 /* }}} */
1324 
1325 static zend_result (*prev_zend_post_startup_cb)(void);
xmlreader_fixup_temporaries(void)1326 static zend_result xmlreader_fixup_temporaries(void) {
1327 	if (ZEND_OBSERVER_ENABLED) {
1328 		++xmlreader_open_fn.T;
1329 		++xmlreader_xml_fn.T;
1330 	}
1331 #ifndef ZTS
1332 	ZEND_MAP_PTR(xmlreader_open_fn.run_time_cache) = ZEND_MAP_PTR(((zend_internal_function *)zend_hash_str_find_ptr(&xmlreader_class_entry->function_table, "open", sizeof("open")-1))->run_time_cache);
1333 	ZEND_MAP_PTR(xmlreader_xml_fn.run_time_cache) = ZEND_MAP_PTR(((zend_internal_function *)zend_hash_str_find_ptr(&xmlreader_class_entry->function_table, "xml", sizeof("xml")-1))->run_time_cache);
1334 #endif
1335 	if (prev_zend_post_startup_cb) {
1336 		return prev_zend_post_startup_cb();
1337 	}
1338 	return SUCCESS;
1339 }
1340 
1341 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(xmlreader)1342 PHP_MINIT_FUNCTION(xmlreader)
1343 {
1344 
1345 	memcpy(&xmlreader_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
1346 	xmlreader_object_handlers.offset = XtOffsetOf(xmlreader_object, std);
1347 	xmlreader_object_handlers.free_obj = xmlreader_objects_free_storage;
1348 	xmlreader_object_handlers.has_property = xmlreader_has_property;
1349 	xmlreader_object_handlers.read_property = xmlreader_read_property;
1350 	xmlreader_object_handlers.write_property = xmlreader_write_property;
1351 	xmlreader_object_handlers.unset_property = xmlreader_unset_property;
1352 	xmlreader_object_handlers.get_property_ptr_ptr = xmlreader_get_property_ptr_ptr;
1353 	xmlreader_object_handlers.get_method = xmlreader_get_method;
1354 	xmlreader_object_handlers.clone_obj = NULL;
1355 	xmlreader_object_handlers.get_debug_info = xmlreader_get_debug_info;
1356 
1357 	xmlreader_class_entry = register_class_XMLReader();
1358 	xmlreader_class_entry->create_object = xmlreader_objects_new;
1359 	xmlreader_class_entry->default_object_handlers = &xmlreader_object_handlers;
1360 
1361 	memcpy(&xmlreader_open_fn, zend_hash_str_find_ptr(&xmlreader_class_entry->function_table, "open", sizeof("open")-1), sizeof(zend_internal_function));
1362 	xmlreader_open_fn.fn_flags &= ~ZEND_ACC_STATIC;
1363 	memcpy(&xmlreader_xml_fn, zend_hash_str_find_ptr(&xmlreader_class_entry->function_table, "xml", sizeof("xml")-1), sizeof(zend_internal_function));
1364 	xmlreader_xml_fn.fn_flags &= ~ZEND_ACC_STATIC;
1365 
1366 	prev_zend_post_startup_cb = zend_post_startup_cb;
1367 	zend_post_startup_cb = xmlreader_fixup_temporaries;
1368 
1369 	/* Note: update the size upon adding properties. */
1370 	zend_hash_init(&xmlreader_prop_handlers, 14, NULL, NULL, true);
1371 	XMLREADER_REGISTER_PROP_HANDLER(&xmlreader_prop_handlers, "attributeCount", xmlTextReaderAttributeCount, NULL, IS_LONG);
1372 	XMLREADER_REGISTER_PROP_HANDLER(&xmlreader_prop_handlers, "baseURI", NULL, xmlTextReaderConstBaseUri, IS_STRING);
1373 	XMLREADER_REGISTER_PROP_HANDLER(&xmlreader_prop_handlers, "depth", xmlTextReaderDepth, NULL, IS_LONG);
1374 	XMLREADER_REGISTER_PROP_HANDLER(&xmlreader_prop_handlers, "hasAttributes", xmlTextReaderHasAttributes, NULL, _IS_BOOL);
1375 	XMLREADER_REGISTER_PROP_HANDLER(&xmlreader_prop_handlers, "hasValue", xmlTextReaderHasValue, NULL, _IS_BOOL);
1376 	XMLREADER_REGISTER_PROP_HANDLER(&xmlreader_prop_handlers, "isDefault", xmlTextReaderIsDefault, NULL, _IS_BOOL);
1377 	XMLREADER_REGISTER_PROP_HANDLER(&xmlreader_prop_handlers, "isEmptyElement", xmlTextReaderIsEmptyElement, NULL, _IS_BOOL);
1378 	XMLREADER_REGISTER_PROP_HANDLER(&xmlreader_prop_handlers, "localName", NULL, xmlTextReaderConstLocalName, IS_STRING);
1379 	XMLREADER_REGISTER_PROP_HANDLER(&xmlreader_prop_handlers, "name", NULL, xmlTextReaderConstName, IS_STRING);
1380 	XMLREADER_REGISTER_PROP_HANDLER(&xmlreader_prop_handlers, "namespaceURI", NULL, xmlTextReaderConstNamespaceUri, IS_STRING);
1381 	XMLREADER_REGISTER_PROP_HANDLER(&xmlreader_prop_handlers, "nodeType", xmlTextReaderNodeType, NULL, IS_LONG);
1382 	XMLREADER_REGISTER_PROP_HANDLER(&xmlreader_prop_handlers, "prefix", NULL, xmlTextReaderConstPrefix, IS_STRING);
1383 	XMLREADER_REGISTER_PROP_HANDLER(&xmlreader_prop_handlers, "value", NULL, xmlTextReaderConstValue, IS_STRING);
1384 	XMLREADER_REGISTER_PROP_HANDLER(&xmlreader_prop_handlers, "xmlLang", NULL, xmlTextReaderConstXmlLang, IS_STRING);
1385 
1386 	return SUCCESS;
1387 }
1388 /* }}} */
1389 
1390 /* {{{ PHP_MSHUTDOWN_FUNCTION */
PHP_MSHUTDOWN_FUNCTION(xmlreader)1391 PHP_MSHUTDOWN_FUNCTION(xmlreader)
1392 {
1393 	zend_hash_destroy(&xmlreader_prop_handlers);
1394 	return SUCCESS;
1395 }
1396 /* }}} */
1397 
1398 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(xmlreader)1399 PHP_MINFO_FUNCTION(xmlreader)
1400 {
1401 	php_info_print_table_start();
1402 	{
1403 		php_info_print_table_row(2, "XMLReader", "enabled");
1404 	}
1405 	php_info_print_table_end();
1406 }
1407 /* }}} */
1408