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 intern->std.handlers = &xmlreader_object_handlers;
376
377 return &intern->std;
378 }
379 /* }}} */
380
381 /* {{{ php_xmlreader_string_arg */
php_xmlreader_string_arg(INTERNAL_FUNCTION_PARAMETERS,xmlreader_read_one_char_t internal_function)382 static void php_xmlreader_string_arg(INTERNAL_FUNCTION_PARAMETERS, xmlreader_read_one_char_t internal_function) {
383 zval *id;
384 size_t name_len = 0;
385 char *retchar = NULL;
386 xmlreader_object *intern;
387 char *name;
388
389 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
390 RETURN_THROWS();
391 }
392
393 if (!name_len) {
394 zend_argument_value_error(1, "cannot be empty");
395 RETURN_THROWS();
396 }
397
398 id = ZEND_THIS;
399
400 intern = Z_XMLREADER_P(id);
401 if (intern->ptr) {
402 retchar = (char *)internal_function(intern->ptr, (const unsigned char *)name);
403 }
404 if (retchar) {
405 RETVAL_STRING(retchar);
406 xmlFree(retchar);
407 return;
408 } else {
409 RETVAL_NULL();
410 }
411 }
412 /* }}} */
413
414 /* {{{ php_xmlreader_no_arg */
php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAMETERS,xmlreader_read_int_t internal_function)415 static void php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAMETERS, xmlreader_read_int_t internal_function) {
416 zval *id;
417 int retval;
418 xmlreader_object *intern;
419
420 if (zend_parse_parameters_none() == FAILURE) {
421 RETURN_THROWS();
422 }
423
424 id = ZEND_THIS;
425
426 intern = Z_XMLREADER_P(id);
427 if (intern->ptr) {
428 retval = internal_function(intern->ptr);
429 if (retval == 1) {
430 RETURN_TRUE;
431 }
432 }
433
434 RETURN_FALSE;
435 }
436 /* }}} */
437
php_xmlreader_free_prop_handler(zval * el)438 static void php_xmlreader_free_prop_handler(zval *el) /* {{{ */ {
439 pefree(Z_PTR_P(el), 1);
440 } /* }}} */
441
442 /* {{{ php_xmlreader_no_arg_string */
php_xmlreader_no_arg_string(INTERNAL_FUNCTION_PARAMETERS,xmlreader_read_char_t internal_function)443 static void php_xmlreader_no_arg_string(INTERNAL_FUNCTION_PARAMETERS, xmlreader_read_char_t internal_function) {
444 zval *id;
445 char *retchar = NULL;
446 xmlreader_object *intern;
447
448 if (zend_parse_parameters_none() == FAILURE) {
449 RETURN_THROWS();
450 }
451
452 id = ZEND_THIS;
453
454 intern = Z_XMLREADER_P(id);
455 if (intern->ptr) {
456 retchar = (char *)internal_function(intern->ptr);
457 }
458 if (retchar) {
459 RETVAL_STRING(retchar);
460 xmlFree(retchar);
461 return;
462 } else {
463 RETVAL_EMPTY_STRING();
464 }
465 }
466 /* }}} */
467
468 /* {{{ php_xmlreader_set_relaxng_schema */
php_xmlreader_set_relaxng_schema(INTERNAL_FUNCTION_PARAMETERS,int type)469 static void php_xmlreader_set_relaxng_schema(INTERNAL_FUNCTION_PARAMETERS, int type) {
470 #ifdef LIBXML_SCHEMAS_ENABLED
471 zval *id;
472 size_t source_len = 0;
473 int retval = -1;
474 xmlreader_object *intern;
475 xmlRelaxNGPtr schema = NULL;
476 char *source;
477
478 if (zend_parse_parameters(ZEND_NUM_ARGS(), "p!", &source, &source_len) == FAILURE) {
479 RETURN_THROWS();
480 }
481
482 if (source != NULL && !source_len) {
483 zend_argument_value_error(1, "cannot be empty");
484 RETURN_THROWS();
485 }
486
487 id = ZEND_THIS;
488
489 intern = Z_XMLREADER_P(id);
490 if (intern->ptr) {
491 if (source) {
492 schema = _xmlreader_get_relaxNG(source, source_len, type, NULL, NULL);
493 if (schema) {
494 retval = xmlTextReaderRelaxNGSetSchema(intern->ptr, schema);
495 }
496 } else {
497 /* unset the associated relaxNG context and schema if one exists */
498 retval = xmlTextReaderRelaxNGSetSchema(intern->ptr, NULL);
499 }
500
501 if (retval == 0) {
502 if (intern->schema) {
503 xmlRelaxNGFree((xmlRelaxNGPtr) intern->schema);
504 }
505
506 intern->schema = schema;
507
508 RETURN_TRUE;
509 } else {
510 php_error_docref(NULL, E_WARNING, "Schema contains errors");
511 RETURN_FALSE;
512 }
513 } else {
514 zend_throw_error(NULL, "Schema must be set prior to reading");
515 RETURN_THROWS();
516 }
517 #else
518 php_error_docref(NULL, E_WARNING, "No schema support built into libxml");
519 RETURN_FALSE;
520 #endif
521 }
522 /* }}} */
523
524 /* {{{ Closes xmlreader - current frees resources until xmlTextReaderClose is fixed in libxml */
PHP_METHOD(XMLReader,close)525 PHP_METHOD(XMLReader, close)
526 {
527 zval *id;
528 xmlreader_object *intern;
529
530 if (zend_parse_parameters_none() == FAILURE) {
531 RETURN_THROWS();
532 }
533
534 id = ZEND_THIS;
535 intern = Z_XMLREADER_P(id);
536 /* libxml is segfaulting in versions up to 2.6.8 using xmlTextReaderClose so for
537 now we will free the whole reader when close is called as it would get rebuilt on
538 a new load anyways */
539 xmlreader_free_resources(intern);
540
541 RETURN_TRUE;
542 }
543 /* }}} */
544
545 /* {{{ Get value of an attribute from current element */
PHP_METHOD(XMLReader,getAttribute)546 PHP_METHOD(XMLReader, getAttribute)
547 {
548 php_xmlreader_string_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderGetAttribute);
549 }
550 /* }}} */
551
552 /* {{{ Get value of an attribute at index from current element */
PHP_METHOD(XMLReader,getAttributeNo)553 PHP_METHOD(XMLReader, getAttributeNo)
554 {
555 zval *id;
556 zend_long attr_pos;
557 char *retchar = NULL;
558 xmlreader_object *intern;
559
560 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &attr_pos) == FAILURE) {
561 RETURN_THROWS();
562 }
563
564 id = ZEND_THIS;
565
566 intern = Z_XMLREADER_P(id);
567 if (intern->ptr) {
568 retchar = (char *)xmlTextReaderGetAttributeNo(intern->ptr, attr_pos);
569 }
570 if (retchar) {
571 RETVAL_STRING(retchar);
572 xmlFree(retchar);
573 }
574 }
575 /* }}} */
576
577 /* {{{ Get value of a attribute via name and namespace from current element */
PHP_METHOD(XMLReader,getAttributeNs)578 PHP_METHOD(XMLReader, getAttributeNs)
579 {
580 zval *id;
581 size_t name_len = 0, ns_uri_len = 0;
582 xmlreader_object *intern;
583 char *name, *ns_uri, *retchar = NULL;
584
585 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &name, &name_len, &ns_uri, &ns_uri_len) == FAILURE) {
586 RETURN_THROWS();
587 }
588
589 if (name_len == 0) {
590 zend_argument_value_error(1, "cannot be empty");
591 RETURN_THROWS();
592 }
593
594 if (ns_uri_len == 0) {
595 zend_argument_value_error(2, "cannot be empty");
596 RETURN_THROWS();
597 }
598
599 id = ZEND_THIS;
600
601 intern = Z_XMLREADER_P(id);
602 if (intern->ptr) {
603 retchar = (char *)xmlTextReaderGetAttributeNs(intern->ptr, (xmlChar *)name, (xmlChar *)ns_uri);
604 }
605 if (retchar) {
606 RETVAL_STRING(retchar);
607 xmlFree(retchar);
608 }
609 }
610 /* }}} */
611
612 /* {{{ Indicates whether given property (one of the parser option constants) is set or not on parser */
PHP_METHOD(XMLReader,getParserProperty)613 PHP_METHOD(XMLReader, getParserProperty)
614 {
615 zval *id;
616 zend_long property;
617 xmlreader_object *intern;
618
619 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &property) == FAILURE) {
620 RETURN_THROWS();
621 }
622
623 id = ZEND_THIS;
624
625 intern = Z_XMLREADER_P(id);
626 if (!intern || !intern->ptr) {
627 zend_throw_error(NULL, "Cannot access parser properties before loading data");
628 RETURN_THROWS();
629 }
630
631 int retval = xmlTextReaderGetParserProp(intern->ptr,property);
632 if (retval == -1) {
633 zend_argument_value_error(1, "must be a valid parser property");
634 RETURN_THROWS();
635 }
636
637 RETURN_BOOL(retval);
638 }
639 /* }}} */
640
641 /* {{{ Returns boolean indicating if parsed document is valid or not.
642 Must set XMLREADER_LOADDTD or XMLREADER_VALIDATE parser option prior to the first call to read
643 or this method will always return FALSE */
PHP_METHOD(XMLReader,isValid)644 PHP_METHOD(XMLReader, isValid)
645 {
646 php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderIsValid);
647 }
648 /* }}} */
649
650 /* {{{ Return namespaceURI for associated prefix on current node */
PHP_METHOD(XMLReader,lookupNamespace)651 PHP_METHOD(XMLReader, lookupNamespace)
652 {
653 php_xmlreader_string_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderLookupNamespace);
654 }
655 /* }}} */
656
657 /* {{{ Positions reader at specified attribute - Returns TRUE on success and FALSE on failure */
PHP_METHOD(XMLReader,moveToAttribute)658 PHP_METHOD(XMLReader, moveToAttribute)
659 {
660 zval *id;
661 size_t name_len = 0;
662 int retval;
663 xmlreader_object *intern;
664 char *name;
665
666 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
667 RETURN_THROWS();
668 }
669
670 if (name_len == 0) {
671 zend_argument_value_error(1, "cannot be empty");
672 RETURN_THROWS();
673 }
674
675 id = ZEND_THIS;
676
677 intern = Z_XMLREADER_P(id);
678 if (intern->ptr) {
679 retval = xmlTextReaderMoveToAttribute(intern->ptr, (xmlChar *)name);
680 if (retval == 1) {
681 RETURN_TRUE;
682 }
683 }
684
685 RETURN_FALSE;
686 }
687 /* }}} */
688
689 /* {{{ Positions reader at attribute at specified index.
690 Returns TRUE on success and FALSE on failure */
PHP_METHOD(XMLReader,moveToAttributeNo)691 PHP_METHOD(XMLReader, moveToAttributeNo)
692 {
693 zval *id;
694 zend_long attr_pos;
695 int retval;
696 xmlreader_object *intern;
697
698 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &attr_pos) == FAILURE) {
699 RETURN_THROWS();
700 }
701
702 id = ZEND_THIS;
703
704 intern = Z_XMLREADER_P(id);
705 if (intern->ptr) {
706 retval = xmlTextReaderMoveToAttributeNo(intern->ptr, attr_pos);
707 if (retval == 1) {
708 RETURN_TRUE;
709 }
710 }
711
712 RETURN_FALSE;
713 }
714 /* }}} */
715
716 /* {{{ Positions reader at attribute spcified by name and namespaceURI.
717 Returns TRUE on success and FALSE on failure */
PHP_METHOD(XMLReader,moveToAttributeNs)718 PHP_METHOD(XMLReader, moveToAttributeNs)
719 {
720 zval *id;
721 size_t name_len=0, ns_uri_len=0;
722 int retval;
723 xmlreader_object *intern;
724 char *name, *ns_uri;
725
726 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &name, &name_len, &ns_uri, &ns_uri_len) == FAILURE) {
727 RETURN_THROWS();
728 }
729
730 if (name_len == 0) {
731 zend_argument_value_error(1, "cannot be empty");
732 RETURN_THROWS();
733 }
734
735 if (ns_uri_len == 0) {
736 zend_argument_value_error(2, "cannot be empty");
737 RETURN_THROWS();
738 }
739
740 id = ZEND_THIS;
741
742 intern = Z_XMLREADER_P(id);
743 if (intern->ptr) {
744 retval = xmlTextReaderMoveToAttributeNs(intern->ptr, (xmlChar *)name, (xmlChar *)ns_uri);
745 if (retval == 1) {
746 RETURN_TRUE;
747 }
748 }
749
750 RETURN_FALSE;
751 }
752 /* }}} */
753
754 /* {{{ Moves the position of the current instance to the node that contains the current Attribute node. */
PHP_METHOD(XMLReader,moveToElement)755 PHP_METHOD(XMLReader, moveToElement)
756 {
757 php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderMoveToElement);
758 }
759 /* }}} */
760
761 /* {{{ Moves the position of the current instance to the first attribute associated with the current node. */
PHP_METHOD(XMLReader,moveToFirstAttribute)762 PHP_METHOD(XMLReader, moveToFirstAttribute)
763 {
764 php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderMoveToFirstAttribute);
765 }
766 /* }}} */
767
768 /* {{{ Moves the position of the current instance to the next attribute associated with the current node. */
PHP_METHOD(XMLReader,moveToNextAttribute)769 PHP_METHOD(XMLReader, moveToNextAttribute)
770 {
771 php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderMoveToNextAttribute);
772 }
773 /* }}} */
774
775 /* {{{ Moves the position of the current instance to the next node in the stream. */
PHP_METHOD(XMLReader,read)776 PHP_METHOD(XMLReader, read)
777 {
778 zval *id;
779 int retval;
780 xmlreader_object *intern;
781
782 if (zend_parse_parameters_none() == FAILURE) {
783 RETURN_THROWS();
784 }
785
786 id = ZEND_THIS;
787 intern = Z_XMLREADER_P(id);
788 if (!intern->ptr) {
789 zend_throw_error(NULL, "Data must be loaded before reading");
790 RETURN_THROWS();
791 }
792
793 retval = xmlTextReaderRead(intern->ptr);
794 if (retval == -1) {
795 RETURN_FALSE;
796 } else {
797 RETURN_BOOL(retval);
798 }
799 }
800 /* }}} */
801
802 /* {{{ Moves the position of the current instance to the next node in the stream. */
PHP_METHOD(XMLReader,next)803 PHP_METHOD(XMLReader, next)
804 {
805 zval *id;
806 int retval;
807 size_t name_len=0;
808 xmlreader_object *intern;
809 char *name = NULL;
810
811 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &name, &name_len) == FAILURE) {
812 RETURN_THROWS();
813 }
814
815 id = ZEND_THIS;
816 intern = Z_XMLREADER_P(id);
817 if (intern->ptr) {
818 retval = xmlTextReaderNext(intern->ptr);
819 while (name != NULL && retval == 1) {
820 if (xmlStrEqual(xmlTextReaderConstLocalName(intern->ptr), (xmlChar *)name)) {
821 RETURN_TRUE;
822 }
823 retval = xmlTextReaderNext(intern->ptr);
824 }
825 if (retval == -1) {
826 RETURN_FALSE;
827 } else {
828 RETURN_BOOL(retval);
829 }
830 }
831
832 zend_throw_error(NULL, "Data must be loaded before reading");
833 }
834 /* }}} */
835
836 /* {{{ Sets the URI that the XMLReader will parse. */
PHP_METHOD(XMLReader,open)837 PHP_METHOD(XMLReader, open)
838 {
839 zval *id;
840 size_t source_len = 0, encoding_len = 0;
841 zend_long options = 0;
842 xmlreader_object *intern = NULL;
843 char *source, *valid_file = NULL;
844 char *encoding = NULL;
845 char resolved_path[MAXPATHLEN + 1];
846 xmlTextReaderPtr reader = NULL;
847
848 if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|s!l", &source, &source_len, &encoding, &encoding_len, &options) == FAILURE) {
849 RETURN_THROWS();
850 }
851
852 id = getThis();
853 if (id != NULL) {
854 ZEND_ASSERT(instanceof_function(Z_OBJCE_P(id), xmlreader_class_entry));
855 intern = Z_XMLREADER_P(id);
856 xmlreader_free_resources(intern);
857 }
858
859 if (!source_len) {
860 zend_argument_value_error(1, "cannot be empty");
861 RETURN_THROWS();
862 }
863
864 if (encoding && CHECK_NULL_PATH(encoding, encoding_len)) {
865 php_error_docref(NULL, E_WARNING, "Encoding must not contain NUL bytes");
866 RETURN_FALSE;
867 }
868
869 valid_file = _xmlreader_get_valid_file_path(source, resolved_path, MAXPATHLEN );
870
871 if (valid_file) {
872 PHP_LIBXML_SANITIZE_GLOBALS(reader_for_file);
873 reader = xmlReaderForFile(valid_file, encoding, options);
874 PHP_LIBXML_RESTORE_GLOBALS(reader_for_file);
875 }
876
877 if (reader == NULL) {
878 php_error_docref(NULL, E_WARNING, "Unable to open source data");
879 RETURN_FALSE;
880 }
881
882 if (id == NULL) {
883 object_init_ex(return_value, xmlreader_class_entry);
884 intern = Z_XMLREADER_P(return_value);
885 intern->ptr = reader;
886 return;
887 }
888
889 intern->ptr = reader;
890
891 RETURN_TRUE;
892
893 }
894 /* }}} */
895
896 /* Not Yet Implemented in libxml - functions exist just not coded
897 PHP_METHOD(XMLReader, resetState)
898 {
899
900 }
901 */
902
903 /* {{{ Reads the contents of the current node, including child nodes and markup. */
PHP_METHOD(XMLReader,readInnerXml)904 PHP_METHOD(XMLReader, readInnerXml)
905 {
906 php_xmlreader_no_arg_string(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderReadInnerXml);
907 }
908 /* }}} */
909
910 /* {{{ Reads the contents of the current node, including child nodes and markup. */
PHP_METHOD(XMLReader,readOuterXml)911 PHP_METHOD(XMLReader, readOuterXml)
912 {
913 php_xmlreader_no_arg_string(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderReadOuterXml);
914 }
915 /* }}} */
916
917 /* {{{ Reads the contents of an element or a text node as a string. */
PHP_METHOD(XMLReader,readString)918 PHP_METHOD(XMLReader, readString)
919 {
920 php_xmlreader_no_arg_string(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderReadString);
921 }
922 /* }}} */
923
924 /* {{{ Use W3C XSD schema to validate the document as it is processed. Activation is only possible before the first Read(). */
PHP_METHOD(XMLReader,setSchema)925 PHP_METHOD(XMLReader, setSchema)
926 {
927 #ifdef LIBXML_SCHEMAS_ENABLED
928 zval *id;
929 size_t source_len = 0;
930 int retval = -1;
931 xmlreader_object *intern;
932 char *source;
933
934 if (zend_parse_parameters(ZEND_NUM_ARGS(), "p!", &source, &source_len) == FAILURE) {
935 RETURN_THROWS();
936 }
937
938 if (source != NULL && !source_len) {
939 zend_argument_value_error(1, "cannot be empty");
940 RETURN_THROWS();
941 }
942
943 id = ZEND_THIS;
944
945 intern = Z_XMLREADER_P(id);
946 if (intern && intern->ptr) {
947 PHP_LIBXML_SANITIZE_GLOBALS(schema);
948 retval = xmlTextReaderSchemaValidate(intern->ptr, source);
949 PHP_LIBXML_RESTORE_GLOBALS(schema);
950
951 if (retval == 0) {
952 RETURN_TRUE;
953 } else {
954 php_error_docref(NULL, E_WARNING, "Schema contains errors");
955 RETURN_FALSE;
956 }
957 } else {
958 zend_throw_error(NULL, "Schema must be set prior to reading");
959 RETURN_THROWS();
960 }
961 #else
962 php_error_docref(NULL, E_WARNING, "No schema support built into libxml");
963 RETURN_FALSE;
964 #endif
965 }
966 /* }}} */
967
968 /* {{{ Sets parser property (one of the parser option constants).
969 Properties must be set after open() or XML() and before the first read() is called */
PHP_METHOD(XMLReader,setParserProperty)970 PHP_METHOD(XMLReader, setParserProperty)
971 {
972 zval *id;
973 zend_long property;
974 bool value;
975 xmlreader_object *intern;
976
977 if (zend_parse_parameters(ZEND_NUM_ARGS(), "lb", &property, &value) == FAILURE) {
978 RETURN_THROWS();
979 }
980
981 id = ZEND_THIS;
982
983 intern = Z_XMLREADER_P(id);
984 if (!intern || !intern->ptr) {
985 zend_throw_error(NULL, "Cannot access parser properties before loading data");
986 RETURN_THROWS();
987 }
988
989 int retval = xmlTextReaderSetParserProp(intern->ptr,property, value);
990 if (retval == -1) {
991 zend_argument_value_error(1, "must be a valid parser property");
992 RETURN_THROWS();
993 }
994
995 RETURN_TRUE;
996 }
997 /* }}} */
998
999 /* {{{ Sets the string that the XMLReader will parse. */
PHP_METHOD(XMLReader,setRelaxNGSchema)1000 PHP_METHOD(XMLReader, setRelaxNGSchema)
1001 {
1002 php_xmlreader_set_relaxng_schema(INTERNAL_FUNCTION_PARAM_PASSTHRU, XMLREADER_LOAD_FILE);
1003 }
1004 /* }}} */
1005
1006 /* {{{ Sets the string that the XMLReader will parse. */
PHP_METHOD(XMLReader,setRelaxNGSchemaSource)1007 PHP_METHOD(XMLReader, setRelaxNGSchemaSource)
1008 {
1009 php_xmlreader_set_relaxng_schema(INTERNAL_FUNCTION_PARAM_PASSTHRU, XMLREADER_LOAD_STRING);
1010 }
1011 /* }}} */
1012
1013 /* TODO
1014 XMLPUBFUN int XMLCALL
1015 xmlTextReaderSetSchema (xmlTextReaderPtr reader,
1016 xmlSchemaPtr schema);
1017 */
1018
1019 /* {{{ Sets the string that the XMLReader will parse. */
PHP_METHOD(XMLReader,XML)1020 PHP_METHOD(XMLReader, XML)
1021 {
1022 zval *id;
1023 size_t source_len = 0, encoding_len = 0;
1024 zend_long options = 0;
1025 xmlreader_object *intern = NULL;
1026 char *source, *uri = NULL, *encoding = NULL;
1027 int resolved_path_len, ret = 0;
1028 char *directory=NULL, resolved_path[MAXPATHLEN + 1];
1029 xmlParserInputBufferPtr inputbfr;
1030 xmlTextReaderPtr reader;
1031
1032 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s!l", &source, &source_len, &encoding, &encoding_len, &options) == FAILURE) {
1033 RETURN_THROWS();
1034 }
1035
1036 id = getThis();
1037 if (id != NULL) {
1038 ZEND_ASSERT(instanceof_function(Z_OBJCE_P(id), xmlreader_class_entry));
1039 intern = Z_XMLREADER_P(id);
1040 xmlreader_free_resources(intern);
1041 }
1042
1043 if (!source_len) {
1044 zend_argument_value_error(1, "cannot be empty");
1045 RETURN_THROWS();
1046 }
1047
1048 if (encoding && CHECK_NULL_PATH(encoding, encoding_len)) {
1049 php_error_docref(NULL, E_WARNING, "Encoding must not contain NUL bytes");
1050 RETURN_FALSE;
1051 }
1052
1053 inputbfr = xmlParserInputBufferCreateMem(source, source_len, XML_CHAR_ENCODING_NONE);
1054
1055 if (inputbfr != NULL) {
1056 /* Get the URI of the current script so that we can set the base directory in libxml */
1057 #ifdef HAVE_GETCWD
1058 directory = VCWD_GETCWD(resolved_path, MAXPATHLEN);
1059 #elif defined(HAVE_GETWD)
1060 directory = VCWD_GETWD(resolved_path);
1061 #endif
1062 if (directory) {
1063 resolved_path_len = strlen(resolved_path);
1064 if (resolved_path[resolved_path_len - 1] != DEFAULT_SLASH) {
1065 resolved_path[resolved_path_len] = DEFAULT_SLASH;
1066 resolved_path[++resolved_path_len] = '\0';
1067 }
1068 uri = (char *) xmlCanonicPath((const xmlChar *) resolved_path);
1069 }
1070 PHP_LIBXML_SANITIZE_GLOBALS(text_reader);
1071 reader = xmlNewTextReader(inputbfr, uri);
1072
1073 if (reader != NULL) {
1074 ret = xmlTextReaderSetup(reader, NULL, uri, encoding, options);
1075 if (ret == 0) {
1076 if (id == NULL) {
1077 object_init_ex(return_value, xmlreader_class_entry);
1078 intern = Z_XMLREADER_P(return_value);
1079 } else {
1080 RETVAL_TRUE;
1081 }
1082 intern->input = inputbfr;
1083 intern->ptr = reader;
1084
1085 if (uri) {
1086 xmlFree(uri);
1087 }
1088
1089 PHP_LIBXML_RESTORE_GLOBALS(text_reader);
1090 return;
1091 }
1092 }
1093 PHP_LIBXML_RESTORE_GLOBALS(text_reader);
1094 }
1095
1096 if (uri) {
1097 xmlFree(uri);
1098 }
1099
1100 if (inputbfr) {
1101 xmlFreeParserInputBuffer(inputbfr);
1102 }
1103 php_error_docref(NULL, E_WARNING, "Unable to load source data");
1104 RETURN_FALSE;
1105 }
1106 /* }}} */
1107
1108 /* {{{ Moves the position of the current instance to the next node in the stream. */
PHP_METHOD(XMLReader,expand)1109 PHP_METHOD(XMLReader, expand)
1110 {
1111 #ifdef HAVE_DOM
1112 zval *id, *basenode = NULL;
1113 int ret;
1114 xmlreader_object *intern;
1115 xmlNode *node, *nodec;
1116 xmlDocPtr docp = NULL;
1117 php_libxml_node_object *domobj = NULL;
1118
1119 id = ZEND_THIS;
1120 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!", &basenode, dom_node_class_entry) == FAILURE) {
1121 RETURN_THROWS();
1122 }
1123
1124 if (basenode != NULL) {
1125 /* Note: cannot use NODE_GET_OBJ here because of the wrong return type */
1126 domobj = Z_LIBXML_NODE_P(basenode);
1127 if (UNEXPECTED(domobj->node == NULL || domobj->node->node == NULL)) {
1128 php_error_docref(NULL, E_WARNING, "Couldn't fetch %s", ZSTR_VAL(Z_OBJCE_P(basenode)->name));
1129 RETURN_FALSE;
1130 }
1131 node = domobj->node->node;
1132 docp = node->doc;
1133 }
1134
1135 intern = Z_XMLREADER_P(id);
1136
1137 if (intern->ptr) {
1138 node = xmlTextReaderExpand(intern->ptr);
1139
1140 if (node == NULL) {
1141 php_error_docref(NULL, E_WARNING, "An Error Occurred while expanding");
1142 RETURN_FALSE;
1143 } else {
1144 nodec = xmlDocCopyNode(node, docp, 1);
1145 if (nodec == NULL) {
1146 php_error_docref(NULL, E_NOTICE, "Cannot expand this node type");
1147 RETURN_FALSE;
1148 } else {
1149 DOM_RET_OBJ(nodec, &ret, (dom_object *)domobj);
1150 }
1151 }
1152 } else {
1153 zend_throw_error(NULL, "Data must be loaded before expanding");
1154 RETURN_THROWS();
1155 }
1156 #else
1157 zval *dummy;
1158 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|z!", &dummy) == FAILURE) {
1159 RETURN_THROWS();
1160 }
1161
1162 zend_throw_error(NULL, "XMLReader::expand() requires the DOM extension to be enabled");
1163 RETURN_THROWS();
1164 #endif
1165 }
1166 /* }}} */
1167
1168 static zend_result (*prev_zend_post_startup_cb)(void);
xmlreader_fixup_temporaries(void)1169 static zend_result xmlreader_fixup_temporaries(void) {
1170 if (ZEND_OBSERVER_ENABLED) {
1171 ++xmlreader_open_fn.T;
1172 ++xmlreader_xml_fn.T;
1173 }
1174 if (prev_zend_post_startup_cb) {
1175 return prev_zend_post_startup_cb();
1176 }
1177 return SUCCESS;
1178 }
1179
1180 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(xmlreader)1181 PHP_MINIT_FUNCTION(xmlreader)
1182 {
1183
1184 memcpy(&xmlreader_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
1185 xmlreader_object_handlers.offset = XtOffsetOf(xmlreader_object, std);
1186 xmlreader_object_handlers.free_obj = xmlreader_objects_free_storage;
1187 xmlreader_object_handlers.read_property = xmlreader_read_property;
1188 xmlreader_object_handlers.write_property = xmlreader_write_property;
1189 xmlreader_object_handlers.get_property_ptr_ptr = xmlreader_get_property_ptr_ptr;
1190 xmlreader_object_handlers.get_method = xmlreader_get_method;
1191 xmlreader_object_handlers.clone_obj = NULL;
1192
1193 xmlreader_class_entry = register_class_XMLReader();
1194 xmlreader_class_entry->create_object = xmlreader_objects_new;
1195
1196 memcpy(&xmlreader_open_fn, zend_hash_str_find_ptr(&xmlreader_class_entry->function_table, "open", sizeof("open")-1), sizeof(zend_internal_function));
1197 xmlreader_open_fn.fn_flags &= ~ZEND_ACC_STATIC;
1198 memcpy(&xmlreader_xml_fn, zend_hash_str_find_ptr(&xmlreader_class_entry->function_table, "xml", sizeof("xml")-1), sizeof(zend_internal_function));
1199 xmlreader_xml_fn.fn_flags &= ~ZEND_ACC_STATIC;
1200
1201 prev_zend_post_startup_cb = zend_post_startup_cb;
1202 zend_post_startup_cb = xmlreader_fixup_temporaries;
1203
1204 zend_hash_init(&xmlreader_prop_handlers, 0, NULL, php_xmlreader_free_prop_handler, 1);
1205 xmlreader_register_prop_handler(&xmlreader_prop_handlers, "attributeCount", xmlTextReaderAttributeCount, NULL, IS_LONG);
1206 xmlreader_register_prop_handler(&xmlreader_prop_handlers, "baseURI", NULL, xmlTextReaderConstBaseUri, IS_STRING);
1207 xmlreader_register_prop_handler(&xmlreader_prop_handlers, "depth", xmlTextReaderDepth, NULL, IS_LONG);
1208 xmlreader_register_prop_handler(&xmlreader_prop_handlers, "hasAttributes", xmlTextReaderHasAttributes, NULL, _IS_BOOL);
1209 xmlreader_register_prop_handler(&xmlreader_prop_handlers, "hasValue", xmlTextReaderHasValue, NULL, _IS_BOOL);
1210 xmlreader_register_prop_handler(&xmlreader_prop_handlers, "isDefault", xmlTextReaderIsDefault, NULL, _IS_BOOL);
1211 xmlreader_register_prop_handler(&xmlreader_prop_handlers, "isEmptyElement", xmlTextReaderIsEmptyElement, NULL, _IS_BOOL);
1212 xmlreader_register_prop_handler(&xmlreader_prop_handlers, "localName", NULL, xmlTextReaderConstLocalName, IS_STRING);
1213 xmlreader_register_prop_handler(&xmlreader_prop_handlers, "name", NULL, xmlTextReaderConstName, IS_STRING);
1214 xmlreader_register_prop_handler(&xmlreader_prop_handlers, "namespaceURI", NULL, xmlTextReaderConstNamespaceUri, IS_STRING);
1215 xmlreader_register_prop_handler(&xmlreader_prop_handlers, "nodeType", xmlTextReaderNodeType, NULL, IS_LONG);
1216 xmlreader_register_prop_handler(&xmlreader_prop_handlers, "prefix", NULL, xmlTextReaderConstPrefix, IS_STRING);
1217 xmlreader_register_prop_handler(&xmlreader_prop_handlers, "value", NULL, xmlTextReaderConstValue, IS_STRING);
1218 xmlreader_register_prop_handler(&xmlreader_prop_handlers, "xmlLang", NULL, xmlTextReaderConstXmlLang, IS_STRING);
1219
1220 return SUCCESS;
1221 }
1222 /* }}} */
1223
1224 /* {{{ PHP_MSHUTDOWN_FUNCTION */
PHP_MSHUTDOWN_FUNCTION(xmlreader)1225 PHP_MSHUTDOWN_FUNCTION(xmlreader)
1226 {
1227 zend_hash_destroy(&xmlreader_prop_handlers);
1228 return SUCCESS;
1229 }
1230 /* }}} */
1231
1232 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(xmlreader)1233 PHP_MINFO_FUNCTION(xmlreader)
1234 {
1235 php_info_print_table_start();
1236 {
1237 php_info_print_table_row(2, "XMLReader", "enabled");
1238 }
1239 php_info_print_table_end();
1240 }
1241 /* }}} */
1242