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