2 This file is part of, or distributed with, libXMLRPC - a C library for
3 xml-encoded function calls.
5 Author: Dan Libby (dan@libby.com)
6 Epinions.com may be contacted at feedback@epinions-inc.com
10 Copyright 2001 Epinions, Inc.
12 Subject to the following 3 conditions, Epinions, Inc. permits you, free
13 of charge, to (a) use, copy, distribute, modify, perform and display this
14 software and associated documentation files (the "Software"), and (b)
15 permit others to whom the Software is furnished to do so as well.
17 1) The above copyright notice and this permission notice shall be included
18 without modification in all copies or substantial portions of the
21 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF
22 ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY
23 IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR
24 PURPOSE OR NONINFRINGEMENT.
26 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT,
27 SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
28 OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING
29 NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH
34 /* auto-generated portions of this file are also subject to the php license */
37 +----------------------------------------------------------------------+
39 +----------------------------------------------------------------------+
40 | Copyright (c) 1997-2004 The PHP Group |
41 +----------------------------------------------------------------------+
42 | This source file is subject to version 3.0 of the PHP license, |
43 | that is bundled with this package in the file LICENSE, and is |
44 | available through the world-wide-web at the following url: |
45 | http://www.php.net/license/3_0.txt. |
46 | If you did not receive a copy of the PHP license and are unable to |
47 | obtain it through the world-wide-web, please send a note to |
48 | license@php.net so we can mail you a copy immediately. |
49 +----------------------------------------------------------------------+
51 +----------------------------------------------------------------------+
54 /* $Id: xmlrpc-epi-php.c,v 1.37 2004/01/08 08:17:47 andi Exp $ */
56 /**********************************************************************
58 * - when calling a php user function, there appears to be no way to *
59 * distinguish between a return value of null, and no return value *
60 * at all. The xml serialization layer(s) will then return a value *
61 * of null, when the right thing may be no value at all. (SOAP) *
62 **********************************************************************/
69 #include "ext/standard/info.h"
71 #include "php_xmlrpc.h"
74 #define PHP_EXT_VERSION "0.51"
76 /* You should tweak config.m4 so this symbol (or some else suitable)
79 ZEND_DECLARE_MODULE_GLOBALS(xmlrpc)
81 static int le_xmlrpc_server;
83 function_entry xmlrpc_functions[] = {
84 PHP_FE(xmlrpc_encode, NULL)
85 PHP_FE(xmlrpc_decode, NULL)
86 PHP_FE(xmlrpc_decode_request, second_arg_force_ref)
87 PHP_FE(xmlrpc_encode_request, NULL)
88 PHP_FE(xmlrpc_get_type, NULL)
89 PHP_FE(xmlrpc_set_type, first_arg_force_ref)
90 PHP_FE(xmlrpc_is_fault, NULL)
91 PHP_FE(xmlrpc_server_create, NULL)
92 PHP_FE(xmlrpc_server_destroy, NULL)
93 PHP_FE(xmlrpc_server_register_method, NULL)
94 PHP_FE(xmlrpc_server_call_method, NULL)
95 PHP_FE(xmlrpc_parse_method_descriptions, NULL)
96 PHP_FE(xmlrpc_server_add_introspection_data, NULL)
97 PHP_FE(xmlrpc_server_register_introspection_callback, NULL)
101 zend_module_entry xmlrpc_module_entry = {
102 STANDARD_MODULE_HEADER,
106 PHP_MSHUTDOWN(xmlrpc),
107 PHP_RINIT(xmlrpc), /* Replace with NULL if there's nothing to do at request start */
108 PHP_RSHUTDOWN(xmlrpc), /* Replace with NULL if there's nothing to do at request end */
111 STANDARD_MODULE_PROPERTIES
114 #ifdef COMPILE_DL_XMLRPC
115 ZEND_GET_MODULE(xmlrpc)
117 # include "zend_arg_defs.c"
122 STD_PHP_INI_BOOLEAN("xmlrpc.allow_null", "0", PHP_INI_ALL, OnUpdateBool, allow_null, zend_xmlrpc_globals, xmlrpc_globals)
125 static void php_xmlrpc_init_globals(zend_xmlrpc_globals *xmlrpc_globals)
127 memset(xmlrpc_globals, 0, sizeof(zend_xmlrpc_globals));
130 /*******************************
131 * local structures and defines *
132 *******************************/
134 /* per server data */
135 typedef struct _xmlrpc_server_data {
137 zval* introspection_map;
138 XMLRPC_SERVER server_ptr;
139 } xmlrpc_server_data;
142 /* how to format output */
143 typedef struct _php_output_options {
147 STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS xmlrpc_out;
148 } php_output_options;
150 /* data passed to C callback */
151 typedef struct _xmlrpc_callback_data {
156 xmlrpc_server_data* server;
158 } xmlrpc_callback_data;
161 #define OUTPUT_TYPE_KEY "output_type"
162 #define OUTPUT_TYPE_KEY_LEN (sizeof(OUTPUT_TYPE_KEY) - 1)
163 #define OUTPUT_TYPE_VALUE_PHP "php"
164 #define OUTPUT_TYPE_VALUE_XML "xml"
166 #define ALLOW_NULL_KEY "allow_null"
167 #define ALLOW_NULL_KEY_LEN (sizeof(ALLOW_NULL_KEY) - 1)
169 #define VERBOSITY_KEY "verbosity"
170 #define VERBOSITY_KEY_LEN (sizeof(VERBOSITY_KEY) - 1)
171 #define VERBOSITY_VALUE_NO_WHITE_SPACE "no_white_space"
172 #define VERBOSITY_VALUE_NEWLINES_ONLY "newlines_only"
173 #define VERBOSITY_VALUE_PRETTY "pretty"
175 #define ESCAPING_KEY "escaping"
176 #define ESCAPING_KEY_LEN (sizeof(ESCAPING_KEY) - 1)
177 #define ESCAPING_VALUE_CDATA "cdata"
178 #define ESCAPING_VALUE_NON_ASCII "non-ascii"
179 #define ESCAPING_VALUE_NON_PRINT "non-print"
180 #define ESCAPING_VALUE_MARKUP "markup"
182 #define VERSION_KEY "version"
183 #define VERSION_KEY_LEN (sizeof(VERSION_KEY) - 1)
184 #define VERSION_VALUE_SIMPLE "simple"
185 #define VERSION_VALUE_XMLRPC "xmlrpc"
186 #define VERSION_VALUE_SOAP11 "soap 1.1"
187 #define VERSION_VALUE_AUTO "auto"
189 #define ENCODING_KEY "encoding"
190 #define ENCODING_KEY_LEN (sizeof(ENCODING_KEY) - 1)
191 #define ENCODING_DEFAULT "iso-8859-1"
194 #define OBJECT_TYPE_ATTR "xmlrpc_type"
195 #define OBJECT_VALUE_ATTR "scalar"
196 #define OBJECT_VALUE_TS_ATTR "timestamp"
199 #define FAULT_CODE "faultCode"
200 #define FAULT_CODE_LEN (sizeof(FAULT_CODE) - 1)
201 #define FAULT_STRING "faultString"
202 #define FAULT_STRING_LEN (sizeof(FAULT_STRING) - 1)
204 /***********************
205 * forward declarations *
206 ***********************/
207 XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(php_output_options* out, zval* value, zval** newvalue);
208 static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data);
209 int sset_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE type);
210 zval* decode_request_worker(zval* xml_in, zval* encoding_in, zval* method_name_out);
211 const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype);
212 XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str);
213 XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str);
214 int set_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE type);
216 /*********************
217 * startup / shutdown *
218 *********************/
220 static void destroy_server_data(xmlrpc_server_data *server)
223 XMLRPC_ServerDestroy(server->server_ptr);
225 zval_dtor(server->method_map);
226 FREE_ZVAL(server->method_map);
228 zval_dtor(server->introspection_map);
229 FREE_ZVAL(server->introspection_map);
235 /* called when server is being destructed. either when xmlrpc_server_destroy
236 * is called, or when request ends. */
237 static void xmlrpc_server_destructor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
239 if (rsrc && rsrc->ptr) {
240 destroy_server_data((xmlrpc_server_data*) rsrc->ptr);
245 PHP_MINIT_FUNCTION(xmlrpc)
247 ZEND_INIT_MODULE_GLOBALS(xmlrpc, php_xmlrpc_init_globals, NULL);
249 REGISTER_INI_ENTRIES();
251 le_xmlrpc_server = zend_register_list_destructors_ex(xmlrpc_server_destructor, NULL, "xmlrpc server", module_number);
256 /* module shutdown */
257 PHP_MSHUTDOWN_FUNCTION(xmlrpc)
262 /* Remove if there's nothing to do at request start */
263 PHP_RINIT_FUNCTION(xmlrpc)
268 /* Remove if there's nothing to do at request end */
269 PHP_RSHUTDOWN_FUNCTION(xmlrpc)
274 /* display info in phpinfo() */
275 PHP_MINFO_FUNCTION(xmlrpc)
277 php_info_print_table_start();
278 php_info_print_table_row(2, "core library version", XMLRPC_GetVersionString());
279 php_info_print_table_row(2, "php extension version", PHP_EXT_VERSION);
280 php_info_print_table_row(2, "author", "Dan Libby");
281 php_info_print_table_row(2, "homepage", "http://xmlrpc-epi.sourceforge.net");
282 php_info_print_table_row(2, "open sourced by", "Epinions.com");
283 php_info_print_table_end();
290 /* Utility functions for adding data types to arrays, with or without key (assoc, non-assoc).
291 * Could easily be further generalized to work with objects.
294 static int add_long(zval* list, char* id, int num) {
295 if(id) return add_assoc_long(list, id, num);
296 else return add_next_index_long(list, num);
299 static int add_double(zval* list, char* id, double num) {
300 if(id) return add_assoc_double(list, id, num);
301 else return add_next_index_double(list, num);
304 static int add_string(zval* list, char* id, char* string, int duplicate) {
305 if(id) return add_assoc_string(list, id, string, duplicate);
306 else return add_next_index_string(list, string, duplicate);
309 static int add_stringl(zval* list, char* id, char* string, uint length, int duplicate) {
310 if(id) return add_assoc_stringl(list, id, string, length, duplicate);
311 else return add_next_index_stringl(list, string, length, duplicate);
316 static int add_zval(zval* list, const char* id, zval** val)
320 return zend_hash_update(Z_ARRVAL_P(list), (char*) id, strlen(id) + 1, (void *) val, sizeof(zval **), NULL);
322 return zend_hash_next_index_insert(Z_ARRVAL_P(list), (void *) val, sizeof(zval **), NULL);
325 /* what is the correct return on error? */
329 #define my_zend_hash_get_current_key(ht, my_key, num_index) zend_hash_get_current_key(ht, my_key, num_index, 0)
332 /*************************
333 * input / output options *
334 *************************/
336 /* parse an array (user input) into output options suitable for use by xmlrpc engine
337 * and determine whether to return data as xml or php vars */
338 static void set_output_options(php_output_options* options, zval* output_opts)
345 options->b_php_out = 0;
346 options->b_auto_version = 1;
347 options->b_allow_null = 0;
348 options->xmlrpc_out.version = xmlrpc_version_1_0;
349 options->xmlrpc_out.xml_elem_opts.encoding = ENCODING_DEFAULT;
350 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty;
351 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping | xml_elem_non_ascii_escaping | xml_elem_non_print_escaping;
353 if (output_opts && Z_TYPE_P(output_opts) == IS_ARRAY) {
357 if (zend_hash_find(Z_ARRVAL_P(output_opts), ALLOW_NULL_KEY, ALLOW_NULL_KEY_LEN + 1, (void**) &val) == SUCCESS) {
358 if (Z_TYPE_PP(val) == IS_BOOL) {
359 if (Z_LVAL_PP(val)) {
360 options->b_allow_null = 1;
362 options->b_allow_null = 0;
367 /* type of output (xml/php) */
368 if (zend_hash_find(Z_ARRVAL_P(output_opts), OUTPUT_TYPE_KEY, OUTPUT_TYPE_KEY_LEN + 1, (void**) &val) == SUCCESS) {
369 if (Z_TYPE_PP(val) == IS_STRING) {
370 if (!strcmp(Z_STRVAL_PP(val), OUTPUT_TYPE_VALUE_PHP)) {
371 options->b_php_out = 1;
372 } else if (!strcmp(Z_STRVAL_PP(val), OUTPUT_TYPE_VALUE_XML)) {
373 options->b_php_out = 0;
378 /* verbosity of generated xml */
379 if (zend_hash_find(Z_ARRVAL_P(output_opts), VERBOSITY_KEY, VERBOSITY_KEY_LEN + 1, (void**) &val) == SUCCESS) {
380 if (Z_TYPE_PP(val) == IS_STRING) {
381 if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_NO_WHITE_SPACE)) {
382 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_no_white_space;
383 } else if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_NEWLINES_ONLY)) {
384 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_newlines_only;
385 } else if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_PRETTY)) {
386 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty;
391 /* version of xml to output */
392 if (zend_hash_find(Z_ARRVAL_P(output_opts), VERSION_KEY, VERSION_KEY_LEN + 1, (void**) &val) == SUCCESS) {
393 if (Z_TYPE_PP(val) == IS_STRING) {
394 options->b_auto_version = 0;
395 if (!strcmp(Z_STRVAL_PP(val), VERSION_VALUE_XMLRPC)) {
396 options->xmlrpc_out.version = xmlrpc_version_1_0;
397 } else if (!strcmp(Z_STRVAL_PP(val), VERSION_VALUE_SIMPLE)) {
398 options->xmlrpc_out.version = xmlrpc_version_simple;
399 } else if (!strcmp((*val)->value.str.val, VERSION_VALUE_SOAP11)) {
400 options->xmlrpc_out.version = xmlrpc_version_soap_1_1;
401 } else { /* if(!strcmp((*val)->value.str.val, VERSION_VALUE_AUTO)) { */
402 options->b_auto_version = 1;
407 /* encoding code set */
408 if(zend_hash_find(Z_ARRVAL_P(output_opts),
409 ENCODING_KEY, ENCODING_KEY_LEN + 1,
410 (void**)&val) == SUCCESS) {
411 if(Z_TYPE_PP(val) == IS_STRING) {
412 options->xmlrpc_out.xml_elem_opts.encoding = estrdup(Z_STRVAL_PP(val));
416 /* escaping options */
417 if(zend_hash_find(Z_ARRVAL_P(output_opts),
418 ESCAPING_KEY, ESCAPING_KEY_LEN + 1,
419 (void**)&val) == SUCCESS) {
420 /* multiple values allowed. check if array */
421 if(Z_TYPE_PP(val) == IS_ARRAY) {
423 zend_hash_internal_pointer_reset(Z_ARRVAL_PP(val));
424 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_no_escaping;
426 if(zend_hash_get_current_data(Z_ARRVAL_PP(val), (void**)&iter_val) == SUCCESS) {
427 if(Z_TYPE_PP(iter_val) == IS_STRING && Z_STRVAL_PP(iter_val)) {
428 if(!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_CDATA)) {
429 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_cdata_escaping;
431 else if(!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_NON_ASCII)) {
432 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_ascii_escaping;
434 else if(!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_NON_PRINT)) {
435 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_print_escaping;
437 else if(!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_MARKUP)) {
438 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_markup_escaping;
446 zend_hash_move_forward(Z_ARRVAL_PP(val));
449 /* else, check for single value */
450 else if(Z_TYPE_PP(val) == IS_STRING) {
451 if(!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_CDATA)) {
452 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_cdata_escaping;
454 else if(!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_NON_ASCII)) {
455 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_ascii_escaping;
457 else if(!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_NON_PRINT)) {
458 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_print_escaping;
460 else if(!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_MARKUP)) {
461 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping;
474 /* php arrays have no distinction between array and struct types.
475 * they even allow mixed. Thus, we determine the type by iterating
476 * through the entire array and figuring out each element.
477 * room for some optimation here if we stop after a specific # of elements.
479 static XMLRPC_VECTOR_TYPE determine_vector_type (HashTable *ht)
481 int bArray = 0, bStruct = 0, bMixed = 0;
482 unsigned long num_index;
485 zend_hash_internal_pointer_reset(ht);
487 int res = my_zend_hash_get_current_key(ht, &my_key, &num_index);
488 if(res == HASH_KEY_IS_LONG) {
495 else if(res == HASH_KEY_NON_EXISTANT) {
498 else if(res == HASH_KEY_IS_STRING) {
506 zend_hash_move_forward(ht);
508 return bMixed ? xmlrpc_vector_mixed : (bStruct ? xmlrpc_vector_struct : xmlrpc_vector_array);
511 /* recursively convert php values into xmlrpc values */
512 static XMLRPC_VALUE PHP_to_XMLRPC_worker (php_output_options* out, const char* key, zval* in_val, int depth)
514 XMLRPC_VALUE xReturn = NULL;
517 XMLRPC_VALUE_TYPE type = get_zval_xmlrpc_type(out, in_val, &val);
521 xReturn = XMLRPC_CreateValueEmpty();
522 XMLRPC_SetValueID(xReturn, key, 0);
525 xReturn = XMLRPC_CreateValueBase64(key, Z_STRVAL_P(val), Z_STRLEN_P(val));
527 case xmlrpc_datetime:
528 convert_to_string(val);
529 xReturn = XMLRPC_CreateValueDateTime_ISO8601(key, Z_STRVAL_P(val));
532 convert_to_boolean(val);
533 xReturn = XMLRPC_CreateValueBoolean(key, Z_LVAL_P(val));
536 convert_to_long(val);
537 xReturn = XMLRPC_CreateValueInt(key, Z_LVAL_P(val));
540 convert_to_double(val);
541 xReturn = XMLRPC_CreateValueDouble(key, Z_DVAL_P(val));
544 convert_to_string(val);
545 xReturn = XMLRPC_CreateValueString(key, Z_STRVAL_P(val), Z_STRLEN_P(val));
549 unsigned long num_index;
553 convert_to_array(val);
555 xReturn = XMLRPC_CreateVector(key, determine_vector_type(Z_ARRVAL_P(val)));
557 zend_hash_internal_pointer_reset(Z_ARRVAL_P(val));
559 int res = my_zend_hash_get_current_key(Z_ARRVAL_P(val), &my_key, &num_index);
560 if(res == HASH_KEY_IS_LONG) {
561 if(zend_hash_get_current_data(Z_ARRVAL_P(val), (void**)&pIter) == SUCCESS) {
562 XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(out, 0, *pIter, depth++));
565 else if(res == HASH_KEY_NON_EXISTANT) {
568 else if(res == HASH_KEY_IS_STRING) {
569 if(zend_hash_get_current_data(Z_ARRVAL_P(val), (void**)&pIter) == SUCCESS) {
570 XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(out, my_key, *pIter, depth++));
574 zend_hash_move_forward(Z_ARRVAL_P(val));
586 static XMLRPC_VALUE PHP_to_XMLRPC(php_output_options* out, zval* root_val)
588 return PHP_to_XMLRPC_worker(out, NULL, root_val, 0);
591 /* recursively convert xmlrpc values into php values */
592 static zval* XMLRPC_to_PHP(XMLRPC_VALUE el)
598 XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(el);
600 MAKE_STD_ZVAL(elem); /* init. very important. spent a frustrating day finding this out. */
605 Z_TYPE_P(elem) = IS_NULL;
608 pStr = XMLRPC_GetValueString(el);
610 Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el);
611 Z_STRVAL_P(elem) = estrndup(pStr, Z_STRLEN_P(elem));
612 Z_TYPE_P(elem) = IS_STRING;
616 Z_LVAL_P(elem) = XMLRPC_GetValueInt(el);
617 Z_TYPE_P(elem) = IS_LONG;
620 Z_LVAL_P(elem) = XMLRPC_GetValueBoolean(el);
621 Z_TYPE_P(elem) = IS_BOOL;
624 Z_DVAL_P(elem) = XMLRPC_GetValueDouble(el);
625 Z_TYPE_P(elem) = IS_DOUBLE;
627 case xmlrpc_datetime:
628 Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el);
629 Z_STRVAL_P(elem) = estrndup(XMLRPC_GetValueDateTime_ISO8601(el), Z_STRLEN_P(elem));
630 Z_TYPE_P(elem) = IS_STRING;
633 pStr = XMLRPC_GetValueBase64(el);
635 Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el);
636 Z_STRVAL_P(elem) = estrndup(pStr, Z_STRLEN_P(elem));
637 Z_TYPE_P(elem) = IS_STRING;
643 XMLRPC_VALUE xIter = XMLRPC_VectorRewind(el);
646 zval *val = XMLRPC_to_PHP(xIter);
648 add_zval(elem, XMLRPC_GetValueID(xIter), &val);
650 xIter = XMLRPC_VectorNext(el);
657 set_zval_xmlrpc_type(elem, type);
662 /* {{{ proto string xmlrpc_encode_request(string method, mixed params)
663 Generates XML for a method request */
664 PHP_FUNCTION(xmlrpc_encode_request)
666 XMLRPC_REQUEST xRequest = NULL;
667 zval **method, **vals, **out_opts;
669 php_output_options out;
671 if (ZEND_NUM_ARGS() < 2 || ZEND_NUM_ARGS() > 3 || (zend_get_parameters_ex(ZEND_NUM_ARGS(), &method, &vals, &out_opts) == FAILURE)) {
672 WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
675 set_output_options(&out, (ZEND_NUM_ARGS() == 3) ? *out_opts : 0);
677 if(return_value_used) {
678 xRequest = XMLRPC_RequestNew();
681 XMLRPC_RequestSetOutputOptions(xRequest, &out.xmlrpc_out);
682 if (Z_TYPE_PP(method) == IS_NULL) {
683 XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_response);
685 XMLRPC_RequestSetMethodName(xRequest, Z_STRVAL_PP(method));
686 XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_call);
688 if (Z_TYPE_PP(vals) != IS_NULL) {
689 XMLRPC_RequestSetData(xRequest, PHP_to_XMLRPC(&out, *vals));
692 outBuf = XMLRPC_REQUEST_ToXML(xRequest, 0);
694 RETVAL_STRING(outBuf, 1);
697 XMLRPC_RequestFree(xRequest, 1);
703 /* {{{ proto string xmlrpc_encode(mixed value)
704 Generates XML for a PHP value */
705 PHP_FUNCTION(xmlrpc_encode)
707 XMLRPC_VALUE xOut = NULL;
711 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg1) == FAILURE)) {
715 if( return_value_used ) {
716 /* convert native php type to xmlrpc type */
717 xOut = PHP_to_XMLRPC(NULL, *arg1);
719 /* generate raw xml from xmlrpc data */
720 outBuf = XMLRPC_VALUE_ToXML(xOut, 0);
724 RETVAL_STRING(outBuf, 1);
728 XMLRPC_CleanupValue(xOut);
735 zval* decode_request_worker (zval* xml_in, zval* encoding_in, zval* method_name_out)
738 XMLRPC_REQUEST response;
739 STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS opts = {{0}};
740 opts.xml_elem_opts.encoding = encoding_in ? utf8_get_encoding_id_from_string(Z_STRVAL_P(encoding_in)) : ENCODING_DEFAULT;
742 /* generate XMLRPC_REQUEST from raw xml */
743 response = XMLRPC_REQUEST_FromXML(Z_STRVAL_P(xml_in), Z_STRLEN_P(xml_in), &opts);
745 /* convert xmlrpc data to native php types */
746 retval = XMLRPC_to_PHP(XMLRPC_RequestGetData(response));
748 if(XMLRPC_RequestGetRequestType(response) == xmlrpc_request_call) {
749 if(method_name_out) {
750 convert_to_string(method_name_out);
751 Z_TYPE_P(method_name_out) = IS_STRING;
752 Z_STRVAL_P(method_name_out) = estrdup(XMLRPC_RequestGetMethodName(response));
753 Z_STRLEN_P(method_name_out) = strlen(Z_STRVAL_P(method_name_out));
757 /* dust, sweep, and mop */
758 XMLRPC_RequestFree(response, 1);
763 /* {{{ proto array xmlrpc_decode_request(string xml, string& method [, string encoding])
764 Decodes XML into native PHP types */
765 PHP_FUNCTION(xmlrpc_decode_request)
767 zval **xml, **method, **encoding = NULL;
768 int argc = ZEND_NUM_ARGS();
770 if (argc < 2 || argc > 3 || (zend_get_parameters_ex(argc, &xml, &method, &encoding) == FAILURE)) {
774 convert_to_string_ex(xml);
775 convert_to_string_ex(method);
777 convert_to_string_ex(encoding);
780 if(return_value_used) {
781 zval* retval = decode_request_worker(*xml, encoding ? *encoding : NULL, *method);
783 *return_value = *retval;
791 /* {{{ proto array xmlrpc_decode(string xml [, string encoding])
792 Decodes XML into native PHP types */
793 PHP_FUNCTION(xmlrpc_decode)
795 zval **arg1, **arg2 = NULL;
796 int argc = ZEND_NUM_ARGS();
798 if (argc < 1 || argc > 2 || (zend_get_parameters_ex(argc, &arg1, &arg2) == FAILURE)) {
802 convert_to_string_ex(arg1);
804 convert_to_string_ex(arg2);
807 if(return_value_used) {
808 zval* retval = decode_request_worker(*arg1, arg2 ? *arg2 : NULL, NULL);
810 *return_value = *retval;
818 /*************************
819 * server related methods *
820 *************************/
822 /* {{{ proto resource xmlrpc_server_create(void)
823 Creates an xmlrpc server */
824 PHP_FUNCTION(xmlrpc_server_create)
826 if(ZEND_NUM_ARGS() != 0) {
830 if(return_value_used) {
831 zval *method_map, *introspection_map;
832 xmlrpc_server_data *server = emalloc(sizeof(xmlrpc_server_data));
833 MAKE_STD_ZVAL(method_map);
834 MAKE_STD_ZVAL(introspection_map);
836 array_init(method_map);
837 array_init(introspection_map);
839 /* allocate server data. free'd in destroy_server_data() */
840 server->method_map = method_map;
841 server->introspection_map = introspection_map;
842 server->server_ptr = XMLRPC_ServerCreate();
844 XMLRPC_ServerRegisterIntrospectionCallback(server->server_ptr, php_xmlrpc_introspection_callback);
846 /* store for later use */
847 ZEND_REGISTER_RESOURCE(return_value,server, le_xmlrpc_server);
852 /* {{{ proto int xmlrpc_server_destroy(resource server)
853 Destroys server resources */
854 PHP_FUNCTION(xmlrpc_server_destroy)
857 int bSuccess = FAILURE;
859 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg1) == FAILURE)) {
863 if(Z_TYPE_PP(arg1) == IS_RESOURCE) {
866 xmlrpc_server_data *server = zend_list_find(Z_LVAL_PP(arg1), &type);
868 if(server && type == le_xmlrpc_server) {
869 bSuccess = zend_list_delete(Z_LVAL_PP(arg1));
871 /* called by hashtable destructor
872 * destroy_server_data(server);
876 RETVAL_LONG(bSuccess == SUCCESS);
881 /* called by xmlrpc C engine as method handler for all registered methods.
882 * it then calls the corresponding PHP function to handle the method.
884 static XMLRPC_VALUE php_xmlrpc_callback(XMLRPC_SERVER server, XMLRPC_REQUEST xRequest, void* data)
886 xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
888 zval* callback_params[3];
891 /* convert xmlrpc to native php types */
892 xmlrpc_params = XMLRPC_to_PHP(XMLRPC_RequestGetData(xRequest));
894 /* setup data hoojum */
895 callback_params[0] = pData->xmlrpc_method;
896 callback_params[1] = xmlrpc_params;
897 callback_params[2] = pData->caller_params;
899 /* Use same C function for all methods */
901 /* php func prototype: function user_func($method_name, $xmlrpc_params, $user_params) */
902 call_user_function(CG(function_table), NULL, pData->php_function, pData->return_data, 3, callback_params TSRMLS_CC);
904 pData->php_executed = 1;
906 zval_dtor(xmlrpc_params);
907 FREE_ZVAL(xmlrpc_params);
912 /* called by the C server when it first receives an introspection request. We pass this on to
913 * our PHP listeners, if any
915 static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data)
917 zval *retval_ptr, **php_function;
918 zval* callback_params[1];
919 xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
922 MAKE_STD_ZVAL(retval_ptr);
923 Z_TYPE_P(retval_ptr) = IS_NULL;
925 /* setup data hoojum */
926 callback_params[0] = pData->caller_params;
928 /* loop through and call all registered callbacks */
929 zend_hash_internal_pointer_reset(Z_ARRVAL_P(pData->server->introspection_map));
931 if(zend_hash_get_current_data(Z_ARRVAL_P(pData->server->introspection_map),
932 (void**)&php_function) == SUCCESS) {
934 /* php func prototype: function string user_func($user_params) */
935 if(call_user_function(CG(function_table), NULL, *php_function,
936 retval_ptr, 1, callback_params TSRMLS_CC) == SUCCESS) {
938 STRUCT_XMLRPC_ERROR err = {0};
940 /* return value should be a string */
941 convert_to_string(retval_ptr);
943 xData = XMLRPC_IntrospectionCreateDescription(Z_STRVAL_P(retval_ptr), &err);
946 if(!XMLRPC_ServerAddIntrospectionData(server, xData)) {
947 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to add introspection data returned from %s(), improper element structure", Z_STRVAL_PP(php_function));
949 XMLRPC_CleanupValue(xData);
952 /* could not create description */
953 if(err.xml_elem_error.parser_code) {
954 php_error_docref(NULL TSRMLS_CC, E_WARNING, "xml parse error: [line %ld, column %ld, message: %s] Unable to add introspection data returned from %s()",
955 err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error, Z_STRVAL_PP(php_function));
958 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to add introspection data returned from %s()",
959 Z_STRVAL_PP(php_function));
964 /* user func failed */
965 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error calling user introspection callback: %s()", Z_STRVAL_PP(php_function));
972 zend_hash_move_forward(Z_ARRVAL_P(pData->server->introspection_map));
975 /* so we don't call the same callbacks ever again */
976 zend_hash_clean(Z_ARRVAL_P(pData->server->introspection_map));
979 /* {{{ proto bool xmlrpc_server_register_method(resource server, string method_name, string function)
980 Register a PHP function to handle method matching method_name */
981 PHP_FUNCTION(xmlrpc_server_register_method)
983 zval **method_key, **method_name, **handle, *method_name_save;
985 xmlrpc_server_data* server;
987 if (ZEND_NUM_ARGS() != 3 || (zend_get_parameters_ex(3, &handle, &method_key, &method_name) == FAILURE)) {
991 server = zend_list_find(Z_LVAL_PP(handle), &type);
993 if(type == le_xmlrpc_server) {
994 /* register with C engine. every method just calls our standard callback,
995 * and it then dispatches to php as necessary
997 if(XMLRPC_ServerRegisterMethod(server->server_ptr, Z_STRVAL_PP(method_key), php_xmlrpc_callback)) {
998 /* save for later use */
999 MAKE_STD_ZVAL(method_name_save);
1000 *method_name_save = **method_name;
1001 zval_copy_ctor(method_name_save);
1003 /* register our php method */
1004 add_zval(server->method_map, Z_STRVAL_PP(method_key), &method_name_save);
1014 /* {{{ proto bool xmlrpc_server_register_introspection_callback(resource server, string function)
1015 Register a PHP function to generate documentation */
1016 PHP_FUNCTION(xmlrpc_server_register_introspection_callback)
1018 zval **method_name, **handle, *method_name_save;
1020 xmlrpc_server_data* server;
1022 if (ZEND_NUM_ARGS() != 2 || (zend_get_parameters_ex(2, &handle, &method_name) == FAILURE)) {
1026 server = zend_list_find(Z_LVAL_PP(handle), &type);
1028 if(type == le_xmlrpc_server) {
1029 /* save for later use */
1030 MAKE_STD_ZVAL(method_name_save);
1031 *method_name_save = **method_name;
1032 zval_copy_ctor(method_name_save);
1034 /* register our php method */
1035 add_zval(server->introspection_map, NULL, &method_name_save);
1044 /* this function is itchin for a re-write */
1046 /* {{{ proto mixed xmlrpc_server_call_method(resource server, string xml, mixed user_data [, array output_options])
1047 Parses XML requests and call methods */
1048 PHP_FUNCTION(xmlrpc_server_call_method)
1050 xmlrpc_callback_data data = {0};
1051 XMLRPC_REQUEST xRequest;
1052 STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS input_opts;
1053 xmlrpc_server_data* server;
1054 zval **rawxml, **caller_params, **handle, **output_opts = NULL;
1056 php_output_options out;
1057 int argc =ZEND_NUM_ARGS();
1059 if (argc < 3 || argc > 4 || (zend_get_parameters_ex(argc, &handle, &rawxml, &caller_params, &output_opts) != SUCCESS)) {
1062 /* user output options */
1064 set_output_options(&out, NULL);
1067 set_output_options(&out, *output_opts);
1070 server = zend_list_find(Z_LVAL_PP(handle), &type);
1072 if(type == le_xmlrpc_server) {
1073 /* HACK: use output encoding for now */
1074 input_opts.xml_elem_opts.encoding = utf8_get_encoding_id_from_string(out.xmlrpc_out.xml_elem_opts.encoding);
1076 /* generate an XMLRPC_REQUEST from the raw xml input */
1077 xRequest = XMLRPC_REQUEST_FromXML(Z_STRVAL_PP(rawxml), Z_STRLEN_PP(rawxml), &input_opts);
1080 const char* methodname = XMLRPC_RequestGetMethodName(xRequest);
1081 zval **php_function;
1082 XMLRPC_VALUE xAnswer = NULL;
1083 MAKE_STD_ZVAL(data.xmlrpc_method); /* init. very important. spent a frustrating day finding this out. */
1084 MAKE_STD_ZVAL(data.return_data);
1085 Z_TYPE_P(data.return_data) = IS_NULL; /* in case value is never init'd, we don't dtor to think it is a string or something */
1086 Z_TYPE_P(data.xmlrpc_method) = IS_NULL;
1092 /* setup some data to pass to the callback function */
1093 Z_STRVAL_P(data.xmlrpc_method) = estrdup(methodname);
1094 Z_STRLEN_P(data.xmlrpc_method) = strlen(methodname);
1095 Z_TYPE_P(data.xmlrpc_method) = IS_STRING;
1096 data.caller_params = *caller_params;
1097 data.php_executed = 0;
1098 data.server = server;
1100 /* check if the called method has been previous registered */
1101 if(zend_hash_find(Z_ARRVAL_P(server->method_map),
1102 Z_STRVAL_P(data.xmlrpc_method),
1103 Z_STRLEN_P(data.xmlrpc_method) + 1,
1104 (void**)&php_function) == SUCCESS) {
1106 data.php_function = *php_function;
1109 /* We could just call the php method directly ourselves at this point, but we do this
1110 * with a C callback in case the xmlrpc library ever implements some cool usage stats,
1113 xAnswer = XMLRPC_ServerCallMethod(server->server_ptr, xRequest, &data);
1114 if(xAnswer && out.b_php_out) {
1115 zval_dtor(data.return_data);
1116 FREE_ZVAL(data.return_data);
1117 data.return_data = XMLRPC_to_PHP(xAnswer);
1118 } else if(data.php_executed && !out.b_php_out) {
1119 xAnswer = PHP_to_XMLRPC(&out, data.return_data);
1122 /* should we return data as xml? */
1123 if(!out.b_php_out) {
1124 XMLRPC_REQUEST xResponse = XMLRPC_RequestNew();
1129 /* automagically determine output serialization type from request type */
1130 if (out.b_auto_version) {
1131 XMLRPC_REQUEST_OUTPUT_OPTIONS opts = XMLRPC_RequestGetOutputOptions(xRequest);
1133 out.xmlrpc_out.version = opts->version;
1136 /* set some required request hoojum */
1137 XMLRPC_RequestSetOutputOptions(xResponse, &out.xmlrpc_out);
1138 XMLRPC_RequestSetRequestType(xResponse, xmlrpc_request_response);
1139 XMLRPC_RequestSetData(xResponse, xAnswer);
1140 XMLRPC_RequestSetMethodName(xResponse, methodname);
1143 outBuf = XMLRPC_REQUEST_ToXML(xResponse, &buf_len);
1145 RETVAL_STRINGL(outBuf, buf_len, 1);
1148 /* cleanup after ourselves. what a sty! */
1149 XMLRPC_RequestFree(xResponse, 0);
1151 } else { /* or as native php types? */
1152 *return_value = *data.return_data;
1153 zval_copy_ctor(return_value);
1156 /* cleanup after ourselves. what a sty! */
1157 zval_dtor(data.xmlrpc_method);
1158 FREE_ZVAL(data.xmlrpc_method);
1159 zval_dtor(data.return_data);
1160 FREE_ZVAL(data.return_data);
1163 XMLRPC_CleanupValue(xAnswer);
1166 XMLRPC_RequestFree(xRequest, 1);
1173 /* {{{ proto int xmlrpc_server_add_introspection_data(resource server, array desc)
1174 Adds introspection documentation */
1175 PHP_FUNCTION(xmlrpc_server_add_introspection_data)
1177 zval **handle, **desc;
1179 xmlrpc_server_data* server;
1181 if (ZEND_NUM_ARGS() != 2 || (zend_get_parameters_ex(2, &handle, &desc) == FAILURE)) {
1185 server = zend_list_find(Z_LVAL_PP(handle), &type);
1187 if (type == le_xmlrpc_server) {
1188 XMLRPC_VALUE xDesc = PHP_to_XMLRPC(NULL, *desc);
1190 int retval = XMLRPC_ServerAddIntrospectionData(server->server_ptr, xDesc);
1191 XMLRPC_CleanupValue(xDesc);
1192 RETURN_LONG(retval);
1200 /* {{{ proto array xmlrpc_parse_method_descriptions(string xml)
1201 Decodes XML into a list of method descriptions */
1202 PHP_FUNCTION(xmlrpc_parse_method_descriptions)
1204 zval **arg1, *retval;
1206 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg1) == FAILURE)) {
1210 convert_to_string_ex(arg1);
1212 if(return_value_used) {
1213 STRUCT_XMLRPC_ERROR err = {0};
1214 XMLRPC_VALUE xVal = XMLRPC_IntrospectionCreateDescription(Z_STRVAL_PP(arg1), &err);
1216 retval = XMLRPC_to_PHP(xVal);
1219 *return_value = *retval;
1220 zval_copy_ctor(return_value);
1222 /* dust, sweep, and mop */
1223 XMLRPC_CleanupValue(xVal);
1225 /* could not create description */
1226 if(err.xml_elem_error.parser_code) {
1227 php_error_docref(NULL TSRMLS_CC, E_WARNING, "xml parse error: [line %ld, column %ld, message: %s] Unable to create introspection data",
1228 err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error);
1230 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid xml structure. Unable to create introspection data");
1233 php_error_docref(NULL TSRMLS_CC, E_WARNING, "xml parse error. no method description created");
1244 #define XMLRPC_TYPE_COUNT 10
1245 #define XMLRPC_VECTOR_TYPE_COUNT 4
1246 #define TYPE_STR_MAP_SIZE (XMLRPC_TYPE_COUNT + XMLRPC_VECTOR_TYPE_COUNT)
1248 /* return a string matching a given xmlrpc type */
1249 static const char** get_type_str_mapping(void)
1251 static const char* str_mapping[TYPE_STR_MAP_SIZE];
1252 static int first = 1;
1254 /* warning. do not add/delete without changing size define */
1255 str_mapping[xmlrpc_none] = "none";
1256 str_mapping[xmlrpc_empty] = "empty";
1257 str_mapping[xmlrpc_nil] = "nil";
1258 str_mapping[xmlrpc_base64] = "base64";
1259 str_mapping[xmlrpc_boolean] = "boolean";
1260 str_mapping[xmlrpc_datetime] = "datetime";
1261 str_mapping[xmlrpc_double] = "double";
1262 str_mapping[xmlrpc_int] = "int";
1263 str_mapping[xmlrpc_string] = "string";
1264 str_mapping[xmlrpc_vector] = "vector";
1265 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_none] = "none";
1266 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_array] = "array";
1267 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_mixed] = "mixed";
1268 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_struct] = "struct";
1271 return (const char**)str_mapping;
1274 /* map an xmlrpc type to a string */
1275 const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype)
1277 const char** str_mapping = get_type_str_mapping();
1279 if (vtype == xmlrpc_vector_none) {
1280 return str_mapping[type];
1282 return str_mapping[XMLRPC_TYPE_COUNT + vtype];
1286 /* map a string to an xmlrpc type */
1287 XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str)
1289 const char** str_mapping = get_type_str_mapping();
1293 for (i = 0; i < XMLRPC_TYPE_COUNT; i++) {
1294 if (!strcmp(str_mapping[i], str)) {
1295 return (XMLRPC_VALUE_TYPE) i;
1302 /* map a string to an xmlrpc vector type */
1303 XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str)
1305 const char** str_mapping = get_type_str_mapping();
1309 for (i = XMLRPC_TYPE_COUNT; i < TYPE_STR_MAP_SIZE; i++) {
1310 if (!strcmp(str_mapping[i], str)) {
1311 return (XMLRPC_VECTOR_TYPE) (i - XMLRPC_TYPE_COUNT);
1319 /* set a given value to a particular type.
1320 * note: this only works on strings, and only for date and base64,
1321 * which do not have native php types. black magic lies herein.
1323 int set_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE newtype)
1325 int bSuccess = FAILURE;
1328 /* we only really care about strings because they can represent
1329 * base64 and datetime. all other types have corresponding php types
1331 if (Z_TYPE_P(value) == IS_STRING) {
1332 if (newtype == xmlrpc_base64 || newtype == xmlrpc_datetime) {
1333 const char* typestr = xmlrpc_type_as_str(newtype, xmlrpc_vector_none);
1336 MAKE_STD_ZVAL(type);
1338 Z_TYPE_P(type) = IS_STRING;
1339 Z_STRVAL_P(type) = estrdup(typestr);
1340 Z_STRLEN_P(type) = strlen(typestr);
1342 if(newtype == xmlrpc_datetime) {
1343 XMLRPC_VALUE v = XMLRPC_CreateValueDateTime_ISO8601(NULL, value->value.str.val);
1345 time_t timestamp = XMLRPC_GetValueDateTime(v);
1349 MAKE_STD_ZVAL(ztimestamp);
1351 ztimestamp->type = IS_LONG;
1352 ztimestamp->value.lval = timestamp;
1354 convert_to_object(value);
1355 if(SUCCESS == zend_hash_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void *) &type, sizeof(zval *), NULL)) {
1356 bSuccess = zend_hash_update(Z_OBJPROP_P(value), OBJECT_VALUE_TS_ATTR, sizeof(OBJECT_VALUE_TS_ATTR), (void *) &ztimestamp, sizeof(zval *), NULL);
1359 XMLRPC_CleanupValue(v);
1363 convert_to_object(value);
1364 bSuccess = zend_hash_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void *) &type, sizeof(zval *), NULL);
1372 /* return xmlrpc type of a php value */
1373 XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(php_output_options* out, zval* value, zval** newvalue)
1375 XMLRPC_VALUE_TYPE type = xmlrpc_none;
1380 switch (Z_TYPE_P(value)) {
1382 if (XMLRPCG(allow_null) || (out && out->b_allow_null)) {
1385 type = xmlrpc_string;
1388 #ifndef BOOL_AS_LONG
1390 /* Right thing to do, but it breaks some legacy code. */
1392 type = xmlrpc_boolean;
1402 type = xmlrpc_double;
1405 type = xmlrpc_string;
1408 type = xmlrpc_string;
1411 case IS_CONSTANT_ARRAY:
1412 type = xmlrpc_vector;
1417 type = xmlrpc_vector;
1419 if (zend_hash_find(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void**) &attr) == SUCCESS) {
1420 if (Z_TYPE_PP(attr) == IS_STRING) {
1421 type = xmlrpc_str_as_type(Z_STRVAL_PP(attr));
1428 /* if requested, return an unmolested (magic removed) copy of the value */
1432 if ((type == xmlrpc_base64 && Z_TYPE_P(value) != IS_NULL) || type == xmlrpc_datetime) {
1433 if (zend_hash_find(Z_OBJPROP_P(value), OBJECT_VALUE_ATTR, sizeof(OBJECT_VALUE_ATTR), (void**) &val) == SUCCESS) {
1446 /* {{{ proto bool xmlrpc_set_type(string value, string type)
1447 Sets xmlrpc type, base64 or datetime or nil, for a PHP string value */
1448 PHP_FUNCTION(xmlrpc_set_type)
1451 XMLRPC_VALUE_TYPE vtype;
1453 if (ZEND_NUM_ARGS() != 2 || (zend_get_parameters_ex(2, &arg, &type) == FAILURE)) {
1457 convert_to_string_ex(type);
1458 vtype = xmlrpc_str_as_type(Z_STRVAL_PP(type));
1459 if (vtype != xmlrpc_none) {
1460 if (set_zval_xmlrpc_type(*arg, vtype) == SUCCESS) {
1464 zend_error(E_WARNING,"invalid type '%s' passed to xmlrpc_set_type()", Z_STRVAL_PP(type));
1470 /* {{{ proto string xmlrpc_get_type(mixed value)
1471 Gets xmlrpc type for a PHP value. Especially useful for base64 and datetime strings */
1472 PHP_FUNCTION(xmlrpc_get_type)
1475 XMLRPC_VALUE_TYPE type;
1476 XMLRPC_VECTOR_TYPE vtype = xmlrpc_vector_none;
1478 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg) == FAILURE)) {
1482 type = get_zval_xmlrpc_type(NULL, *arg, 0);
1483 if (type == xmlrpc_vector) {
1484 vtype = determine_vector_type(Z_ARRVAL_PP(arg));
1487 RETURN_STRING((char*) xmlrpc_type_as_str(type, vtype), 1);
1491 /* {{{ proto bool xmlrpc_is_fault(array)
1492 Determines if an array value represents an XMLRPC fault. */
1493 PHP_FUNCTION(xmlrpc_is_fault)
1497 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg) == FAILURE)) {
1501 if (Z_TYPE_PP(arg) != IS_ARRAY) {
1502 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array argument expected");
1504 /* The "correct" way to do this would be to call the xmlrpc
1505 * library XMLRPC_ValueIsFault() func. However, doing that
1506 * would require us to create an xmlrpc value from the php
1507 * array, which is rather expensive, especially if it was
1508 * a big array. Thus, we resort to this not so clever hackery.
1510 if (zend_hash_find(Z_ARRVAL_PP(arg), FAULT_CODE, FAULT_CODE_LEN + 1, (void**) &val) == SUCCESS &&
1511 zend_hash_find(Z_ARRVAL_PP(arg), FAULT_STRING, FAULT_STRING_LEN + 1, (void**) &val) == SUCCESS) {