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