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