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