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