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