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