xref: /PHP-8.3/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 
376 	return &intern->std;
377 }
378 /* }}} */
379 
380 /* {{{ php_xmlreader_string_arg */
php_xmlreader_string_arg(INTERNAL_FUNCTION_PARAMETERS,xmlreader_read_one_char_t internal_function)381 static void php_xmlreader_string_arg(INTERNAL_FUNCTION_PARAMETERS, xmlreader_read_one_char_t internal_function) {
382 	zval *id;
383 	size_t name_len = 0;
384 	char *retchar = NULL;
385 	xmlreader_object *intern;
386 	char *name;
387 
388 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
389 		RETURN_THROWS();
390 	}
391 
392 	if (!name_len) {
393 		zend_argument_value_error(1, "cannot be empty");
394 		RETURN_THROWS();
395 	}
396 
397 	id = ZEND_THIS;
398 
399 	intern = Z_XMLREADER_P(id);
400 	if (intern->ptr) {
401 		retchar = (char *)internal_function(intern->ptr, (const unsigned char *)name);
402 	}
403 	if (retchar) {
404 		RETVAL_STRING(retchar);
405 		xmlFree(retchar);
406 		return;
407 	} else {
408 		RETVAL_NULL();
409 	}
410 }
411 /* }}} */
412 
413 /* {{{ php_xmlreader_no_arg */
php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAMETERS,xmlreader_read_int_t internal_function)414 static void php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAMETERS, xmlreader_read_int_t internal_function) {
415 	zval *id;
416 	int retval;
417 	xmlreader_object *intern;
418 
419 	if (zend_parse_parameters_none() == FAILURE) {
420 		RETURN_THROWS();
421 	}
422 
423 	id = ZEND_THIS;
424 
425 	intern = Z_XMLREADER_P(id);
426 	if (intern->ptr) {
427 		retval = internal_function(intern->ptr);
428 		if (retval == 1) {
429 			RETURN_TRUE;
430 		}
431 	}
432 
433 	RETURN_FALSE;
434 }
435 /* }}} */
436 
php_xmlreader_free_prop_handler(zval * el)437 static void php_xmlreader_free_prop_handler(zval *el) /* {{{ */ {
438 	pefree(Z_PTR_P(el), 1);
439 } /* }}} */
440 
441 /* {{{ php_xmlreader_no_arg_string */
php_xmlreader_no_arg_string(INTERNAL_FUNCTION_PARAMETERS,xmlreader_read_char_t internal_function)442 static void php_xmlreader_no_arg_string(INTERNAL_FUNCTION_PARAMETERS, xmlreader_read_char_t internal_function) {
443 	zval *id;
444 	char *retchar = NULL;
445 	xmlreader_object *intern;
446 
447 	if (zend_parse_parameters_none() == FAILURE) {
448 		RETURN_THROWS();
449 	}
450 
451 	id = ZEND_THIS;
452 
453 	intern = Z_XMLREADER_P(id);
454 	if (intern->ptr) {
455 		retchar = (char *)internal_function(intern->ptr);
456 	}
457 	if (retchar) {
458 		RETVAL_STRING(retchar);
459 		xmlFree(retchar);
460 		return;
461 	} else {
462 		RETVAL_EMPTY_STRING();
463 	}
464 }
465 /* }}} */
466 
467 /* {{{ php_xmlreader_set_relaxng_schema */
php_xmlreader_set_relaxng_schema(INTERNAL_FUNCTION_PARAMETERS,int type)468 static void php_xmlreader_set_relaxng_schema(INTERNAL_FUNCTION_PARAMETERS, int type) {
469 #ifdef LIBXML_SCHEMAS_ENABLED
470 	zval *id;
471 	size_t source_len = 0;
472 	int retval = -1;
473 	xmlreader_object *intern;
474 	xmlRelaxNGPtr schema = NULL;
475 	char *source;
476 
477 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p!", &source, &source_len) == FAILURE) {
478 		RETURN_THROWS();
479 	}
480 
481 	if (source != NULL && !source_len) {
482 		zend_argument_value_error(1, "cannot be empty");
483 		RETURN_THROWS();
484 	}
485 
486 	id = ZEND_THIS;
487 
488 	intern = Z_XMLREADER_P(id);
489 	if (intern->ptr) {
490 		if (source) {
491 			schema =  _xmlreader_get_relaxNG(source, source_len, type, NULL, NULL);
492 			if (schema) {
493 				retval = xmlTextReaderRelaxNGSetSchema(intern->ptr, schema);
494 			}
495 		} else {
496 			/* unset the associated relaxNG context and schema if one exists */
497 			retval = xmlTextReaderRelaxNGSetSchema(intern->ptr, NULL);
498 		}
499 
500 		if (retval == 0) {
501 			if (intern->schema) {
502 				xmlRelaxNGFree((xmlRelaxNGPtr) intern->schema);
503 			}
504 
505 			intern->schema = schema;
506 
507 			RETURN_TRUE;
508 		} else {
509 			php_error_docref(NULL, E_WARNING, "Schema contains errors");
510 			RETURN_FALSE;
511 		}
512 	} else {
513 		zend_throw_error(NULL, "Schema must be set prior to reading");
514 		RETURN_THROWS();
515 	}
516 #else
517 	php_error_docref(NULL, E_WARNING, "No schema support built into libxml");
518 	RETURN_FALSE;
519 #endif
520 }
521 /* }}} */
522 
523 /* {{{ Closes xmlreader - current frees resources until xmlTextReaderClose is fixed in libxml */
PHP_METHOD(XMLReader,close)524 PHP_METHOD(XMLReader, close)
525 {
526 	zval *id;
527 	xmlreader_object *intern;
528 
529 	if (zend_parse_parameters_none() == FAILURE) {
530 		RETURN_THROWS();
531 	}
532 
533 	id = ZEND_THIS;
534 	intern = Z_XMLREADER_P(id);
535 	/* libxml is segfaulting in versions up to 2.6.8 using xmlTextReaderClose so for
536 	now we will free the whole reader when close is called as it would get rebuilt on
537 	a new load anyways */
538 	xmlreader_free_resources(intern);
539 
540 	RETURN_TRUE;
541 }
542 /* }}} */
543 
544 /* {{{ Get value of an attribute from current element */
PHP_METHOD(XMLReader,getAttribute)545 PHP_METHOD(XMLReader, getAttribute)
546 {
547 	php_xmlreader_string_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderGetAttribute);
548 }
549 /* }}} */
550 
551 /* {{{ Get value of an attribute at index from current element */
PHP_METHOD(XMLReader,getAttributeNo)552 PHP_METHOD(XMLReader, getAttributeNo)
553 {
554 	zval *id;
555 	zend_long attr_pos;
556 	char *retchar = NULL;
557 	xmlreader_object *intern;
558 
559 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &attr_pos) == FAILURE) {
560 		RETURN_THROWS();
561 	}
562 
563 	id = ZEND_THIS;
564 
565 	intern = Z_XMLREADER_P(id);
566 	if (intern->ptr) {
567 		retchar = (char *)xmlTextReaderGetAttributeNo(intern->ptr, attr_pos);
568 	}
569 	if (retchar) {
570 		RETVAL_STRING(retchar);
571 		xmlFree(retchar);
572 	}
573 }
574 /* }}} */
575 
576 /* {{{ Get value of a attribute via name and namespace from current element */
PHP_METHOD(XMLReader,getAttributeNs)577 PHP_METHOD(XMLReader, getAttributeNs)
578 {
579 	zval *id;
580 	size_t name_len = 0, ns_uri_len = 0;
581 	xmlreader_object *intern;
582 	char *name, *ns_uri, *retchar = NULL;
583 
584 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &name, &name_len, &ns_uri, &ns_uri_len) == FAILURE) {
585 		RETURN_THROWS();
586 	}
587 
588 	if (name_len == 0) {
589 		zend_argument_value_error(1, "cannot be empty");
590 		RETURN_THROWS();
591 	}
592 
593 	if (ns_uri_len == 0) {
594 		zend_argument_value_error(2, "cannot be empty");
595 		RETURN_THROWS();
596 	}
597 
598 	id = ZEND_THIS;
599 
600 	intern = Z_XMLREADER_P(id);
601 	if (intern->ptr) {
602 		retchar = (char *)xmlTextReaderGetAttributeNs(intern->ptr, (xmlChar *)name, (xmlChar *)ns_uri);
603 	}
604 	if (retchar) {
605 		RETVAL_STRING(retchar);
606 		xmlFree(retchar);
607 	}
608 }
609 /* }}} */
610 
611 /* {{{ Indicates whether given property (one of the parser option constants) is set or not on parser */
PHP_METHOD(XMLReader,getParserProperty)612 PHP_METHOD(XMLReader, getParserProperty)
613 {
614 	zval *id;
615 	zend_long property;
616 	xmlreader_object *intern;
617 
618 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &property) == FAILURE) {
619 		RETURN_THROWS();
620 	}
621 
622 	id = ZEND_THIS;
623 
624 	intern = Z_XMLREADER_P(id);
625 	if (!intern || !intern->ptr) {
626 		zend_throw_error(NULL, "Cannot access parser properties before loading data");
627 		RETURN_THROWS();
628 	}
629 
630 	int retval = xmlTextReaderGetParserProp(intern->ptr,property);
631 	if (retval == -1) {
632 		zend_argument_value_error(1, "must be a valid parser property");
633 		RETURN_THROWS();
634 	}
635 
636 	RETURN_BOOL(retval);
637 }
638 /* }}} */
639 
640 /* {{{ Returns boolean indicating if parsed document is valid or not.
641 Must set XMLREADER_LOADDTD or XMLREADER_VALIDATE parser option prior to the first call to read
642 or this method will always return FALSE */
PHP_METHOD(XMLReader,isValid)643 PHP_METHOD(XMLReader, isValid)
644 {
645 	php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderIsValid);
646 }
647 /* }}} */
648 
649 /* {{{ Return namespaceURI for associated prefix on current node */
PHP_METHOD(XMLReader,lookupNamespace)650 PHP_METHOD(XMLReader, lookupNamespace)
651 {
652 	php_xmlreader_string_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderLookupNamespace);
653 }
654 /* }}} */
655 
656 /* {{{ Positions reader at specified attribute - Returns TRUE on success and FALSE on failure */
PHP_METHOD(XMLReader,moveToAttribute)657 PHP_METHOD(XMLReader, moveToAttribute)
658 {
659 	zval *id;
660 	size_t name_len = 0;
661 	int retval;
662 	xmlreader_object *intern;
663 	char *name;
664 
665 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
666 		RETURN_THROWS();
667 	}
668 
669 	if (name_len == 0) {
670 		zend_argument_value_error(1, "cannot be empty");
671 		RETURN_THROWS();
672 	}
673 
674 	id = ZEND_THIS;
675 
676 	intern = Z_XMLREADER_P(id);
677 	if (intern->ptr) {
678 		retval = xmlTextReaderMoveToAttribute(intern->ptr, (xmlChar *)name);
679 		if (retval == 1) {
680 			RETURN_TRUE;
681 		}
682 	}
683 
684 	RETURN_FALSE;
685 }
686 /* }}} */
687 
688 /* {{{ Positions reader at attribute at specified index.
689 Returns TRUE on success and FALSE on failure */
PHP_METHOD(XMLReader,moveToAttributeNo)690 PHP_METHOD(XMLReader, moveToAttributeNo)
691 {
692 	zval *id;
693 	zend_long attr_pos;
694 	int retval;
695 	xmlreader_object *intern;
696 
697 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &attr_pos) == FAILURE) {
698 		RETURN_THROWS();
699 	}
700 
701 	id = ZEND_THIS;
702 
703 	intern = Z_XMLREADER_P(id);
704 	if (intern->ptr) {
705 		retval = xmlTextReaderMoveToAttributeNo(intern->ptr, attr_pos);
706 		if (retval == 1) {
707 			RETURN_TRUE;
708 		}
709 	}
710 
711 	RETURN_FALSE;
712 }
713 /* }}} */
714 
715 /* {{{ Positions reader at attribute spcified by name and namespaceURI.
716 Returns TRUE on success and FALSE on failure */
PHP_METHOD(XMLReader,moveToAttributeNs)717 PHP_METHOD(XMLReader, moveToAttributeNs)
718 {
719 	zval *id;
720 	size_t name_len=0, ns_uri_len=0;
721 	int retval;
722 	xmlreader_object *intern;
723 	char *name, *ns_uri;
724 
725 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &name, &name_len, &ns_uri, &ns_uri_len) == FAILURE) {
726 		RETURN_THROWS();
727 	}
728 
729 	if (name_len == 0) {
730 		zend_argument_value_error(1, "cannot be empty");
731 		RETURN_THROWS();
732 	}
733 
734 	if (ns_uri_len == 0) {
735 		zend_argument_value_error(2, "cannot be empty");
736 		RETURN_THROWS();
737 	}
738 
739 	id = ZEND_THIS;
740 
741 	intern = Z_XMLREADER_P(id);
742 	if (intern->ptr) {
743 		retval = xmlTextReaderMoveToAttributeNs(intern->ptr, (xmlChar *)name, (xmlChar *)ns_uri);
744 		if (retval == 1) {
745 			RETURN_TRUE;
746 		}
747 	}
748 
749 	RETURN_FALSE;
750 }
751 /* }}} */
752 
753 /* {{{ Moves the position of the current instance to the node that contains the current Attribute node. */
PHP_METHOD(XMLReader,moveToElement)754 PHP_METHOD(XMLReader, moveToElement)
755 {
756 	php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderMoveToElement);
757 }
758 /* }}} */
759 
760 /* {{{ Moves the position of the current instance to the first attribute associated with the current node. */
PHP_METHOD(XMLReader,moveToFirstAttribute)761 PHP_METHOD(XMLReader, moveToFirstAttribute)
762 {
763 	php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderMoveToFirstAttribute);
764 }
765 /* }}} */
766 
767 /* {{{ Moves the position of the current instance to the next attribute associated with the current node. */
PHP_METHOD(XMLReader,moveToNextAttribute)768 PHP_METHOD(XMLReader, moveToNextAttribute)
769 {
770 	php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderMoveToNextAttribute);
771 }
772 /* }}} */
773 
774 /* {{{ Moves the position of the current instance to the next node in the stream. */
PHP_METHOD(XMLReader,read)775 PHP_METHOD(XMLReader, read)
776 {
777 	zval *id;
778 	int retval;
779 	xmlreader_object *intern;
780 
781 	if (zend_parse_parameters_none() == FAILURE) {
782 		RETURN_THROWS();
783 	}
784 
785 	id = ZEND_THIS;
786 	intern = Z_XMLREADER_P(id);
787 	if (!intern->ptr) {
788 		zend_throw_error(NULL, "Data must be loaded before reading");
789 		RETURN_THROWS();
790 	}
791 
792 	retval = xmlTextReaderRead(intern->ptr);
793 	if (retval == -1) {
794 		RETURN_FALSE;
795 	} else {
796 		RETURN_BOOL(retval);
797 	}
798 }
799 /* }}} */
800 
801 /* {{{ Moves the position of the current instance to the next node in the stream. */
PHP_METHOD(XMLReader,next)802 PHP_METHOD(XMLReader, next)
803 {
804 	zval *id;
805 	int retval;
806 	size_t name_len=0;
807 	xmlreader_object *intern;
808 	char *name = NULL;
809 
810 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &name, &name_len) == FAILURE) {
811 		RETURN_THROWS();
812 	}
813 
814 	id = ZEND_THIS;
815 	intern = Z_XMLREADER_P(id);
816 	if (intern->ptr) {
817 		retval = xmlTextReaderNext(intern->ptr);
818 		while (name != NULL && retval == 1) {
819 			if (xmlStrEqual(xmlTextReaderConstLocalName(intern->ptr), (xmlChar *)name)) {
820 				RETURN_TRUE;
821 			}
822 			retval = xmlTextReaderNext(intern->ptr);
823 		}
824 		if (retval == -1) {
825 			RETURN_FALSE;
826 		} else {
827 			RETURN_BOOL(retval);
828 		}
829 	}
830 
831 	zend_throw_error(NULL, "Data must be loaded before reading");
832 }
833 /* }}} */
834 
835 /* {{{ Sets the URI that the XMLReader will parse. */
PHP_METHOD(XMLReader,open)836 PHP_METHOD(XMLReader, open)
837 {
838 	zval *id;
839 	size_t source_len = 0, encoding_len = 0;
840 	zend_long options = 0;
841 	xmlreader_object *intern = NULL;
842 	char *source, *valid_file = NULL;
843 	char *encoding = NULL;
844 	char resolved_path[MAXPATHLEN + 1];
845 	xmlTextReaderPtr reader = NULL;
846 
847 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|s!l", &source, &source_len, &encoding, &encoding_len, &options) == FAILURE) {
848 		RETURN_THROWS();
849 	}
850 
851 	id = getThis();
852 	if (id != NULL) {
853 		ZEND_ASSERT(instanceof_function(Z_OBJCE_P(id), xmlreader_class_entry));
854 		intern = Z_XMLREADER_P(id);
855 		xmlreader_free_resources(intern);
856 	}
857 
858 	if (!source_len) {
859 		zend_argument_value_error(1, "cannot be empty");
860 		RETURN_THROWS();
861 	}
862 
863 	if (encoding && CHECK_NULL_PATH(encoding, encoding_len)) {
864 		php_error_docref(NULL, E_WARNING, "Encoding must not contain NUL bytes");
865 		RETURN_FALSE;
866 	}
867 
868 	valid_file = _xmlreader_get_valid_file_path(source, resolved_path, MAXPATHLEN );
869 
870 	if (valid_file) {
871 		PHP_LIBXML_SANITIZE_GLOBALS(reader_for_file);
872 		reader = xmlReaderForFile(valid_file, encoding, options);
873 		PHP_LIBXML_RESTORE_GLOBALS(reader_for_file);
874 	}
875 
876 	if (reader == NULL) {
877 		php_error_docref(NULL, E_WARNING, "Unable to open source data");
878 		RETURN_FALSE;
879 	}
880 
881 	if (id == NULL) {
882 		object_init_ex(return_value, xmlreader_class_entry);
883 		intern = Z_XMLREADER_P(return_value);
884 		intern->ptr = reader;
885 		return;
886 	}
887 
888 	intern->ptr = reader;
889 
890 	RETURN_TRUE;
891 
892 }
893 /* }}} */
894 
895 /* Not Yet Implemented in libxml - functions exist just not coded
896 PHP_METHOD(XMLReader, resetState)
897 {
898 
899 }
900 */
901 
902 /* {{{ Reads the contents of the current node, including child nodes and markup. */
PHP_METHOD(XMLReader,readInnerXml)903 PHP_METHOD(XMLReader, readInnerXml)
904 {
905 	php_xmlreader_no_arg_string(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderReadInnerXml);
906 }
907 /* }}} */
908 
909 /* {{{ Reads the contents of the current node, including child nodes and markup. */
PHP_METHOD(XMLReader,readOuterXml)910 PHP_METHOD(XMLReader, readOuterXml)
911 {
912 	php_xmlreader_no_arg_string(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderReadOuterXml);
913 }
914 /* }}} */
915 
916 /* {{{ Reads the contents of an element or a text node as a string. */
PHP_METHOD(XMLReader,readString)917 PHP_METHOD(XMLReader, readString)
918 {
919 	php_xmlreader_no_arg_string(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderReadString);
920 }
921 /* }}} */
922 
923 /* {{{ Use W3C XSD schema to validate the document as it is processed. Activation is only possible before the first Read(). */
PHP_METHOD(XMLReader,setSchema)924 PHP_METHOD(XMLReader, setSchema)
925 {
926 #ifdef LIBXML_SCHEMAS_ENABLED
927 	zval *id;
928 	size_t source_len = 0;
929 	int retval = -1;
930 	xmlreader_object *intern;
931 	char *source;
932 
933 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p!", &source, &source_len) == FAILURE) {
934 		RETURN_THROWS();
935 	}
936 
937 	if (source != NULL && !source_len) {
938 		zend_argument_value_error(1, "cannot be empty");
939 		RETURN_THROWS();
940 	}
941 
942 	id = ZEND_THIS;
943 
944 	intern = Z_XMLREADER_P(id);
945 	if (intern && intern->ptr) {
946 		PHP_LIBXML_SANITIZE_GLOBALS(schema);
947 		retval = xmlTextReaderSchemaValidate(intern->ptr, source);
948 		PHP_LIBXML_RESTORE_GLOBALS(schema);
949 
950 		if (retval == 0) {
951 			RETURN_TRUE;
952 		} else {
953 			php_error_docref(NULL, E_WARNING, "Schema contains errors");
954 			RETURN_FALSE;
955 		}
956 	} else {
957 		zend_throw_error(NULL, "Schema must be set prior to reading");
958 		RETURN_THROWS();
959 	}
960 #else
961 	php_error_docref(NULL, E_WARNING, "No schema support built into libxml");
962 	RETURN_FALSE;
963 #endif
964 }
965 /* }}} */
966 
967 /* {{{ Sets parser property (one of the parser option constants).
968 Properties must be set after open() or XML() and before the first read() is called */
PHP_METHOD(XMLReader,setParserProperty)969 PHP_METHOD(XMLReader, setParserProperty)
970 {
971 	zval *id;
972 	zend_long property;
973 	bool value;
974 	xmlreader_object *intern;
975 
976 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lb", &property, &value) == FAILURE) {
977 		RETURN_THROWS();
978 	}
979 
980 	id = ZEND_THIS;
981 
982 	intern = Z_XMLREADER_P(id);
983 	if (!intern || !intern->ptr) {
984 		zend_throw_error(NULL, "Cannot access parser properties before loading data");
985 		RETURN_THROWS();
986 	}
987 
988 	int retval = xmlTextReaderSetParserProp(intern->ptr,property, value);
989 	if (retval == -1) {
990 		zend_argument_value_error(1, "must be a valid parser property");
991 		RETURN_THROWS();
992 	}
993 
994 	RETURN_TRUE;
995 }
996 /* }}} */
997 
998 /* {{{ Sets the string that the XMLReader will parse. */
PHP_METHOD(XMLReader,setRelaxNGSchema)999 PHP_METHOD(XMLReader, setRelaxNGSchema)
1000 {
1001 	php_xmlreader_set_relaxng_schema(INTERNAL_FUNCTION_PARAM_PASSTHRU, XMLREADER_LOAD_FILE);
1002 }
1003 /* }}} */
1004 
1005 /* {{{ Sets the string that the XMLReader will parse. */
PHP_METHOD(XMLReader,setRelaxNGSchemaSource)1006 PHP_METHOD(XMLReader, setRelaxNGSchemaSource)
1007 {
1008 	php_xmlreader_set_relaxng_schema(INTERNAL_FUNCTION_PARAM_PASSTHRU, XMLREADER_LOAD_STRING);
1009 }
1010 /* }}} */
1011 
1012 /* TODO
1013 XMLPUBFUN int XMLCALL
1014 		    xmlTextReaderSetSchema	(xmlTextReaderPtr reader,
1015 		    				 xmlSchemaPtr schema);
1016 */
1017 
1018 /* {{{ Sets the string that the XMLReader will parse. */
PHP_METHOD(XMLReader,XML)1019 PHP_METHOD(XMLReader, XML)
1020 {
1021 	zval *id;
1022 	size_t source_len = 0, encoding_len = 0;
1023 	zend_long options = 0;
1024 	xmlreader_object *intern = NULL;
1025 	char *source, *uri = NULL, *encoding = NULL;
1026 	int resolved_path_len, ret = 0;
1027 	char *directory=NULL, resolved_path[MAXPATHLEN + 1];
1028 	xmlParserInputBufferPtr inputbfr;
1029 	xmlTextReaderPtr reader;
1030 
1031 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s!l", &source, &source_len, &encoding, &encoding_len, &options) == FAILURE) {
1032 		RETURN_THROWS();
1033 	}
1034 
1035 	id = getThis();
1036 	if (id != NULL) {
1037 		ZEND_ASSERT(instanceof_function(Z_OBJCE_P(id), xmlreader_class_entry));
1038 		intern = Z_XMLREADER_P(id);
1039 		xmlreader_free_resources(intern);
1040 	}
1041 
1042 	if (!source_len) {
1043 		zend_argument_value_error(1, "cannot be empty");
1044 		RETURN_THROWS();
1045 	}
1046 
1047 	if (encoding && CHECK_NULL_PATH(encoding, encoding_len)) {
1048 		php_error_docref(NULL, E_WARNING, "Encoding must not contain NUL bytes");
1049 		RETURN_FALSE;
1050 	}
1051 
1052 	inputbfr = xmlParserInputBufferCreateMem(source, source_len, XML_CHAR_ENCODING_NONE);
1053 
1054 	if (inputbfr != NULL) {
1055 /* Get the URI of the current script so that we can set the base directory in libxml */
1056 #ifdef HAVE_GETCWD
1057 		directory = VCWD_GETCWD(resolved_path, MAXPATHLEN);
1058 #elif defined(HAVE_GETWD)
1059 		directory = VCWD_GETWD(resolved_path);
1060 #endif
1061 		if (directory) {
1062 			resolved_path_len = strlen(resolved_path);
1063 			if (resolved_path[resolved_path_len - 1] != DEFAULT_SLASH) {
1064 				resolved_path[resolved_path_len] = DEFAULT_SLASH;
1065 				resolved_path[++resolved_path_len] = '\0';
1066 			}
1067 			uri = (char *) xmlCanonicPath((const xmlChar *) resolved_path);
1068 		}
1069 		PHP_LIBXML_SANITIZE_GLOBALS(text_reader);
1070 		reader = xmlNewTextReader(inputbfr, uri);
1071 
1072 		if (reader != NULL) {
1073 			ret = xmlTextReaderSetup(reader, NULL, uri, encoding, options);
1074 			if (ret == 0) {
1075 				if (id == NULL) {
1076 					object_init_ex(return_value, xmlreader_class_entry);
1077 					intern = Z_XMLREADER_P(return_value);
1078 				} else {
1079 					RETVAL_TRUE;
1080 				}
1081 				intern->input = inputbfr;
1082 				intern->ptr = reader;
1083 
1084 				if (uri) {
1085 					xmlFree(uri);
1086 				}
1087 
1088 				PHP_LIBXML_RESTORE_GLOBALS(text_reader);
1089 				return;
1090 			}
1091 		}
1092 		PHP_LIBXML_RESTORE_GLOBALS(text_reader);
1093 	}
1094 
1095 	if (uri) {
1096 		xmlFree(uri);
1097 	}
1098 
1099 	if (inputbfr) {
1100 		xmlFreeParserInputBuffer(inputbfr);
1101 	}
1102 	php_error_docref(NULL, E_WARNING, "Unable to load source data");
1103 	RETURN_FALSE;
1104 }
1105 /* }}} */
1106 
1107 /* {{{ Moves the position of the current instance to the next node in the stream. */
PHP_METHOD(XMLReader,expand)1108 PHP_METHOD(XMLReader, expand)
1109 {
1110 #ifdef HAVE_DOM
1111 	zval *id, *basenode = NULL;
1112 	int ret;
1113 	xmlreader_object *intern;
1114 	xmlNode *node, *nodec;
1115 	xmlDocPtr docp = NULL;
1116 	php_libxml_node_object *domobj = NULL;
1117 
1118 	id = ZEND_THIS;
1119 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!", &basenode, dom_node_class_entry) == FAILURE) {
1120 		RETURN_THROWS();
1121 	}
1122 
1123 	if (basenode != NULL) {
1124 		NODE_GET_OBJ(node, basenode, xmlNodePtr, domobj);
1125 		docp = node->doc;
1126 	}
1127 
1128 	intern = Z_XMLREADER_P(id);
1129 
1130 	if (intern->ptr) {
1131 		node = xmlTextReaderExpand(intern->ptr);
1132 
1133 		if (node == NULL) {
1134 			php_error_docref(NULL, E_WARNING, "An Error Occurred while expanding");
1135 			RETURN_FALSE;
1136 		} else {
1137 			nodec = xmlDocCopyNode(node, docp, 1);
1138 			if (nodec == NULL) {
1139 				php_error_docref(NULL, E_NOTICE, "Cannot expand this node type");
1140 				RETURN_FALSE;
1141 			} else {
1142 				DOM_RET_OBJ(nodec, &ret, (dom_object *)domobj);
1143 			}
1144 		}
1145 	} else {
1146 		zend_throw_error(NULL, "Data must be loaded before expanding");
1147 		RETURN_THROWS();
1148 	}
1149 #else
1150 	zval *dummy;
1151 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|z!", &dummy) == FAILURE) {
1152 		RETURN_THROWS();
1153 	}
1154 
1155 	zend_throw_error(NULL, "XMLReader::expand() requires the DOM extension to be enabled");
1156 	RETURN_THROWS();
1157 #endif
1158 }
1159 /* }}} */
1160 
1161 static zend_result (*prev_zend_post_startup_cb)(void);
xmlreader_fixup_temporaries(void)1162 static zend_result xmlreader_fixup_temporaries(void) {
1163 	if (ZEND_OBSERVER_ENABLED) {
1164 		++xmlreader_open_fn.T;
1165 		++xmlreader_xml_fn.T;
1166 	}
1167 	if (prev_zend_post_startup_cb) {
1168 		return prev_zend_post_startup_cb();
1169 	}
1170 	return SUCCESS;
1171 }
1172 
1173 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(xmlreader)1174 PHP_MINIT_FUNCTION(xmlreader)
1175 {
1176 
1177 	memcpy(&xmlreader_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
1178 	xmlreader_object_handlers.offset = XtOffsetOf(xmlreader_object, std);
1179 	xmlreader_object_handlers.free_obj = xmlreader_objects_free_storage;
1180 	xmlreader_object_handlers.read_property = xmlreader_read_property;
1181 	xmlreader_object_handlers.write_property = xmlreader_write_property;
1182 	xmlreader_object_handlers.get_property_ptr_ptr = xmlreader_get_property_ptr_ptr;
1183 	xmlreader_object_handlers.get_method = xmlreader_get_method;
1184 	xmlreader_object_handlers.clone_obj = NULL;
1185 
1186 	xmlreader_class_entry = register_class_XMLReader();
1187 	xmlreader_class_entry->create_object = xmlreader_objects_new;
1188 	xmlreader_class_entry->default_object_handlers = &xmlreader_object_handlers;
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