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