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