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"
121 /*******************************
122 * local structures and defines *
123 *******************************/
125 /* per server data */
126 typedef struct _xmlrpc_server_data {
128 zval* introspection_map;
129 XMLRPC_SERVER server_ptr;
130 } xmlrpc_server_data;
133 /* how to format output */
134 typedef struct _php_output_options {
137 STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS xmlrpc_out;
138 } php_output_options;
140 /* data passed to C callback */
141 typedef struct _xmlrpc_callback_data {
146 xmlrpc_server_data* server;
148 } xmlrpc_callback_data;
151 #define OUTPUT_TYPE_KEY "output_type"
152 #define OUTPUT_TYPE_KEY_LEN (sizeof(OUTPUT_TYPE_KEY) - 1)
153 #define OUTPUT_TYPE_VALUE_PHP "php"
154 #define OUTPUT_TYPE_VALUE_XML "xml"
156 #define VERBOSITY_KEY "verbosity"
157 #define VERBOSITY_KEY_LEN (sizeof(VERBOSITY_KEY) - 1)
158 #define VERBOSITY_VALUE_NO_WHITE_SPACE "no_white_space"
159 #define VERBOSITY_VALUE_NEWLINES_ONLY "newlines_only"
160 #define VERBOSITY_VALUE_PRETTY "pretty"
162 #define ESCAPING_KEY "escaping"
163 #define ESCAPING_KEY_LEN (sizeof(ESCAPING_KEY) - 1)
164 #define ESCAPING_VALUE_CDATA "cdata"
165 #define ESCAPING_VALUE_NON_ASCII "non-ascii"
166 #define ESCAPING_VALUE_NON_PRINT "non-print"
167 #define ESCAPING_VALUE_MARKUP "markup"
169 #define VERSION_KEY "version"
170 #define VERSION_KEY_LEN (sizeof(VERSION_KEY) - 1)
171 #define VERSION_VALUE_SIMPLE "simple"
172 #define VERSION_VALUE_XMLRPC "xmlrpc"
173 #define VERSION_VALUE_SOAP11 "soap 1.1"
174 #define VERSION_VALUE_AUTO "auto"
176 #define ENCODING_KEY "encoding"
177 #define ENCODING_KEY_LEN (sizeof(ENCODING_KEY) - 1)
178 #define ENCODING_DEFAULT "iso-8859-1"
181 #define OBJECT_TYPE_ATTR "xmlrpc_type"
182 #define OBJECT_VALUE_ATTR "scalar"
183 #define OBJECT_VALUE_TS_ATTR "timestamp"
186 #define FAULT_CODE "faultCode"
187 #define FAULT_CODE_LEN (sizeof(FAULT_CODE) - 1)
188 #define FAULT_STRING "faultString"
189 #define FAULT_STRING_LEN (sizeof(FAULT_STRING) - 1)
191 /***********************
192 * forward declarations *
193 ***********************/
194 XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(zval* value, zval** newvalue);
195 static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data);
196 int sset_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE type);
197 zval* decode_request_worker(zval* xml_in, zval* encoding_in, zval* method_name_out);
198 const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype);
199 XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str);
200 XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str);
201 int set_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE type);
203 /*********************
204 * startup / shutdown *
205 *********************/
207 static void destroy_server_data(xmlrpc_server_data *server)
210 XMLRPC_ServerDestroy(server->server_ptr);
212 zval_dtor(server->method_map);
213 FREE_ZVAL(server->method_map);
215 zval_dtor(server->introspection_map);
216 FREE_ZVAL(server->introspection_map);
222 /* called when server is being destructed. either when xmlrpc_server_destroy
223 * is called, or when request ends. */
224 static void xmlrpc_server_destructor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
226 if (rsrc && rsrc->ptr) {
227 destroy_server_data((xmlrpc_server_data*) rsrc->ptr);
232 PHP_MINIT_FUNCTION(xmlrpc)
234 le_xmlrpc_server = zend_register_list_destructors_ex(xmlrpc_server_destructor, NULL, "xmlrpc server", module_number);
239 /* module shutdown */
240 PHP_MSHUTDOWN_FUNCTION(xmlrpc)
245 /* Remove if there's nothing to do at request start */
246 PHP_RINIT_FUNCTION(xmlrpc)
251 /* Remove if there's nothing to do at request end */
252 PHP_RSHUTDOWN_FUNCTION(xmlrpc)
257 /* display info in phpinfo() */
258 PHP_MINFO_FUNCTION(xmlrpc)
260 php_info_print_table_start();
261 php_info_print_table_row(2, "core library version", XMLRPC_GetVersionString());
262 php_info_print_table_row(2, "php extension version", PHP_EXT_VERSION);
263 php_info_print_table_row(2, "author", "Dan Libby");
264 php_info_print_table_row(2, "homepage", "http://xmlrpc-epi.sourceforge.net");
265 php_info_print_table_row(2, "open sourced by", "Epinions.com");
266 php_info_print_table_end();
273 /* Utility functions for adding data types to arrays, with or without key (assoc, non-assoc).
274 * Could easily be further generalized to work with objects.
277 static int add_long(zval* list, char* id, int num) {
278 if(id) return add_assoc_long(list, id, num);
279 else return add_next_index_long(list, num);
282 static int add_double(zval* list, char* id, double num) {
283 if(id) return add_assoc_double(list, id, num);
284 else return add_next_index_double(list, num);
287 static int add_string(zval* list, char* id, char* string, int duplicate) {
288 if(id) return add_assoc_string(list, id, string, duplicate);
289 else return add_next_index_string(list, string, duplicate);
292 static int add_stringl(zval* list, char* id, char* string, uint length, int duplicate) {
293 if(id) return add_assoc_stringl(list, id, string, length, duplicate);
294 else return add_next_index_stringl(list, string, length, duplicate);
299 static int add_zval(zval* list, const char* id, zval** val)
303 return zend_hash_update(Z_ARRVAL_P(list), (char*) id, strlen(id) + 1, (void *) val, sizeof(zval **), NULL);
305 return zend_hash_next_index_insert(Z_ARRVAL_P(list), (void *) val, sizeof(zval **), NULL);
308 /* what is the correct return on error? */
312 #define my_zend_hash_get_current_key(ht, my_key, num_index) zend_hash_get_current_key(ht, my_key, num_index, 0)
315 /*************************
316 * input / output options *
317 *************************/
319 /* parse an array (user input) into output options suitable for use by xmlrpc engine
320 * and determine whether to return data as xml or php vars */
321 static void set_output_options(php_output_options* options, zval* output_opts)
326 options->b_php_out = 0;
327 options->b_auto_version = 1;
328 options->xmlrpc_out.version = xmlrpc_version_1_0;
329 options->xmlrpc_out.xml_elem_opts.encoding = ENCODING_DEFAULT;
330 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty;
331 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping | xml_elem_non_ascii_escaping | xml_elem_non_print_escaping;
333 if (output_opts && Z_TYPE_P(output_opts) == IS_ARRAY) {
336 /* type of output (xml/php) */
337 if (zend_hash_find(Z_ARRVAL_P(output_opts), OUTPUT_TYPE_KEY, OUTPUT_TYPE_KEY_LEN + 1, (void**) &val) == SUCCESS) {
338 if (Z_TYPE_PP(val) == IS_STRING) {
339 if (!strcmp(Z_STRVAL_PP(val), OUTPUT_TYPE_VALUE_PHP)) {
340 options->b_php_out = 1;
341 } else if (!strcmp(Z_STRVAL_PP(val), OUTPUT_TYPE_VALUE_XML)) {
342 options->b_php_out = 0;
347 /* verbosity of generated xml */
348 if (zend_hash_find(Z_ARRVAL_P(output_opts), VERBOSITY_KEY, VERBOSITY_KEY_LEN + 1, (void**) &val) == SUCCESS) {
349 if (Z_TYPE_PP(val) == IS_STRING) {
350 if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_NO_WHITE_SPACE)) {
351 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_no_white_space;
352 } else if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_NEWLINES_ONLY)) {
353 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_newlines_only;
354 } else if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_PRETTY)) {
355 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty;
360 /* version of xml to output */
361 if (zend_hash_find(Z_ARRVAL_P(output_opts), VERSION_KEY, VERSION_KEY_LEN + 1, (void**) &val) == SUCCESS) {
362 if (Z_TYPE_PP(val) == IS_STRING) {
363 options->b_auto_version = 0;
364 if (!strcmp(Z_STRVAL_PP(val), VERSION_VALUE_XMLRPC)) {
365 options->xmlrpc_out.version = xmlrpc_version_1_0;
366 } else if (!strcmp(Z_STRVAL_PP(val), VERSION_VALUE_SIMPLE)) {
367 options->xmlrpc_out.version = xmlrpc_version_simple;
368 } else if (!strcmp((*val)->value.str.val, VERSION_VALUE_SOAP11)) {
369 options->xmlrpc_out.version = xmlrpc_version_soap_1_1;
370 } else { /* if(!strcmp((*val)->value.str.val, VERSION_VALUE_AUTO)) { */
371 options->b_auto_version = 1;
376 /* encoding code set */
377 if(zend_hash_find(Z_ARRVAL_P(output_opts),
378 ENCODING_KEY, ENCODING_KEY_LEN + 1,
379 (void**)&val) == SUCCESS) {
380 if(Z_TYPE_PP(val) == IS_STRING) {
381 options->xmlrpc_out.xml_elem_opts.encoding = estrdup(Z_STRVAL_PP(val));
385 /* escaping options */
386 if(zend_hash_find(Z_ARRVAL_P(output_opts),
387 ESCAPING_KEY, ESCAPING_KEY_LEN + 1,
388 (void**)&val) == SUCCESS) {
389 /* multiple values allowed. check if array */
390 if(Z_TYPE_PP(val) == IS_ARRAY) {
392 zend_hash_internal_pointer_reset(Z_ARRVAL_PP(val));
393 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_no_escaping;
395 if(zend_hash_get_current_data(Z_ARRVAL_PP(val), (void**)&iter_val) == SUCCESS) {
396 if(Z_TYPE_PP(iter_val) == IS_STRING && Z_STRVAL_PP(iter_val)) {
397 if(!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_CDATA)) {
398 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_cdata_escaping;
400 else if(!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_NON_ASCII)) {
401 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_ascii_escaping;
403 else if(!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_NON_PRINT)) {
404 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_print_escaping;
406 else if(!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_MARKUP)) {
407 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_markup_escaping;
415 zend_hash_move_forward(Z_ARRVAL_PP(val));
418 /* else, check for single value */
419 else if(Z_TYPE_PP(val) == IS_STRING) {
420 if(!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_CDATA)) {
421 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_cdata_escaping;
423 else if(!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_NON_ASCII)) {
424 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_ascii_escaping;
426 else if(!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_NON_PRINT)) {
427 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_print_escaping;
429 else if(!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_MARKUP)) {
430 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping;
443 /* php arrays have no distinction between array and struct types.
444 * they even allow mixed. Thus, we determine the type by iterating
445 * through the entire array and figuring out each element.
446 * room for some optimation here if we stop after a specific # of elements.
448 static XMLRPC_VECTOR_TYPE determine_vector_type (HashTable *ht)
450 int bArray = 0, bStruct = 0, bMixed = 0;
451 unsigned long num_index;
454 zend_hash_internal_pointer_reset(ht);
456 int res = my_zend_hash_get_current_key(ht, &my_key, &num_index);
457 if(res == HASH_KEY_IS_LONG) {
464 else if(res == HASH_KEY_NON_EXISTANT) {
467 else if(res == HASH_KEY_IS_STRING) {
475 zend_hash_move_forward(ht);
477 return bMixed ? xmlrpc_vector_mixed : (bStruct ? xmlrpc_vector_struct : xmlrpc_vector_array);
480 /* recursively convert php values into xmlrpc values */
481 static XMLRPC_VALUE PHP_to_XMLRPC_worker (const char* key, zval* in_val, int depth)
483 XMLRPC_VALUE xReturn = NULL;
486 XMLRPC_VALUE_TYPE type = get_zval_xmlrpc_type(in_val, &val);
490 if(Z_TYPE_P(val) == IS_NULL) {
491 xReturn = XMLRPC_CreateValueEmpty();
492 XMLRPC_SetValueID(xReturn, key, 0);
495 xReturn = XMLRPC_CreateValueBase64(key, Z_STRVAL_P(val), Z_STRLEN_P(val));
498 case xmlrpc_datetime:
499 convert_to_string(val);
500 xReturn = XMLRPC_CreateValueDateTime_ISO8601(key, Z_STRVAL_P(val));
503 convert_to_boolean(val);
504 xReturn = XMLRPC_CreateValueBoolean(key, Z_LVAL_P(val));
507 convert_to_long(val);
508 xReturn = XMLRPC_CreateValueInt(key, Z_LVAL_P(val));
511 convert_to_double(val);
512 xReturn = XMLRPC_CreateValueDouble(key, Z_DVAL_P(val));
515 convert_to_string(val);
516 xReturn = XMLRPC_CreateValueString(key, Z_STRVAL_P(val), Z_STRLEN_P(val));
520 unsigned long num_index;
524 convert_to_array(val);
526 xReturn = XMLRPC_CreateVector(key, determine_vector_type(Z_ARRVAL_P(val)));
528 zend_hash_internal_pointer_reset(Z_ARRVAL_P(val));
530 int res = my_zend_hash_get_current_key(Z_ARRVAL_P(val), &my_key, &num_index);
531 if(res == HASH_KEY_IS_LONG) {
532 if(zend_hash_get_current_data(Z_ARRVAL_P(val), (void**)&pIter) == SUCCESS) {
533 XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(0, *pIter, depth++));
536 else if(res == HASH_KEY_NON_EXISTANT) {
539 else if(res == HASH_KEY_IS_STRING) {
540 if(zend_hash_get_current_data(Z_ARRVAL_P(val), (void**)&pIter) == SUCCESS) {
541 XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(my_key, *pIter, depth++));
545 zend_hash_move_forward(Z_ARRVAL_P(val));
557 static XMLRPC_VALUE PHP_to_XMLRPC(zval* root_val)
559 return PHP_to_XMLRPC_worker(NULL, root_val, 0);
562 /* recursively convert xmlrpc values into php values */
563 static zval* XMLRPC_to_PHP(XMLRPC_VALUE el)
569 XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(el);
571 MAKE_STD_ZVAL(elem); /* init. very important. spent a frustrating day finding this out. */
575 Z_TYPE_P(elem) = IS_NULL;
578 pStr = XMLRPC_GetValueString(el);
580 Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el);
581 Z_STRVAL_P(elem) = estrndup(pStr, Z_STRLEN_P(elem));
582 Z_TYPE_P(elem) = IS_STRING;
586 Z_LVAL_P(elem) = XMLRPC_GetValueInt(el);
587 Z_TYPE_P(elem) = IS_LONG;
590 Z_LVAL_P(elem) = XMLRPC_GetValueBoolean(el);
591 Z_TYPE_P(elem) = IS_BOOL;
594 Z_DVAL_P(elem) = XMLRPC_GetValueDouble(el);
595 Z_TYPE_P(elem) = IS_DOUBLE;
597 case xmlrpc_datetime:
598 Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el);
599 Z_STRVAL_P(elem) = estrndup(XMLRPC_GetValueDateTime_ISO8601(el), Z_STRLEN_P(elem));
600 Z_TYPE_P(elem) = IS_STRING;
603 pStr = XMLRPC_GetValueBase64(el);
605 Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el);
606 Z_STRVAL_P(elem) = estrndup(pStr, Z_STRLEN_P(elem));
607 Z_TYPE_P(elem) = IS_STRING;
613 XMLRPC_VALUE xIter = XMLRPC_VectorRewind(el);
616 zval *val = XMLRPC_to_PHP(xIter);
618 add_zval(elem, XMLRPC_GetValueID(xIter), &val);
620 xIter = XMLRPC_VectorNext(el);
627 set_zval_xmlrpc_type(elem, type);
632 /* {{{ proto string xmlrpc_encode_request(string method, mixed params)
633 Generates XML for a method request */
634 PHP_FUNCTION(xmlrpc_encode_request)
636 XMLRPC_REQUEST xRequest = NULL;
637 zval **method, **vals, **out_opts;
639 php_output_options out;
641 if (ZEND_NUM_ARGS() < 2 || ZEND_NUM_ARGS() > 3 || (zend_get_parameters_ex(ZEND_NUM_ARGS(), &method, &vals, &out_opts) == FAILURE)) {
642 WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
645 set_output_options(&out, (ZEND_NUM_ARGS() == 3) ? *out_opts : 0);
647 if(return_value_used) {
648 xRequest = XMLRPC_RequestNew();
651 XMLRPC_RequestSetOutputOptions(xRequest, &out.xmlrpc_out);
652 if (Z_TYPE_PP(method) == IS_NULL) {
653 XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_response);
655 XMLRPC_RequestSetMethodName(xRequest, Z_STRVAL_PP(method));
656 XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_call);
658 if (Z_TYPE_PP(vals) != IS_NULL) {
659 XMLRPC_RequestSetData(xRequest, PHP_to_XMLRPC(*vals));
662 outBuf = XMLRPC_REQUEST_ToXML(xRequest, 0);
664 RETVAL_STRING(outBuf, 1);
667 XMLRPC_RequestFree(xRequest, 1);
673 /* {{{ proto string xmlrpc_encode(mixed value)
674 Generates XML for a PHP value */
675 PHP_FUNCTION(xmlrpc_encode)
677 XMLRPC_VALUE xOut = NULL;
681 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg1) == FAILURE)) {
685 if( return_value_used ) {
686 /* convert native php type to xmlrpc type */
687 xOut = PHP_to_XMLRPC(*arg1);
689 /* generate raw xml from xmlrpc data */
690 outBuf = XMLRPC_VALUE_ToXML(xOut, 0);
694 RETVAL_STRING(outBuf, 1);
698 XMLRPC_CleanupValue(xOut);
705 zval* decode_request_worker (zval* xml_in, zval* encoding_in, zval* method_name_out)
708 XMLRPC_REQUEST response;
709 STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS opts = {{0}};
710 opts.xml_elem_opts.encoding = encoding_in ? utf8_get_encoding_id_from_string(Z_STRVAL_P(encoding_in)) : ENCODING_DEFAULT;
712 /* generate XMLRPC_REQUEST from raw xml */
713 response = XMLRPC_REQUEST_FromXML(Z_STRVAL_P(xml_in), Z_STRLEN_P(xml_in), &opts);
715 /* convert xmlrpc data to native php types */
716 retval = XMLRPC_to_PHP(XMLRPC_RequestGetData(response));
718 if(XMLRPC_RequestGetRequestType(response) == xmlrpc_request_call) {
719 if(method_name_out) {
720 convert_to_string(method_name_out);
721 Z_TYPE_P(method_name_out) = IS_STRING;
722 Z_STRVAL_P(method_name_out) = estrdup(XMLRPC_RequestGetMethodName(response));
723 Z_STRLEN_P(method_name_out) = strlen(Z_STRVAL_P(method_name_out));
727 /* dust, sweep, and mop */
728 XMLRPC_RequestFree(response, 1);
733 /* {{{ proto array xmlrpc_decode_request(string xml, string& method [, string encoding])
734 Decodes XML into native PHP types */
735 PHP_FUNCTION(xmlrpc_decode_request)
737 zval **xml, **method, **encoding = NULL;
738 int argc = ZEND_NUM_ARGS();
740 if (argc < 2 || argc > 3 || (zend_get_parameters_ex(argc, &xml, &method, &encoding) == FAILURE)) {
744 convert_to_string_ex(xml);
745 convert_to_string_ex(method);
747 convert_to_string_ex(encoding);
750 if(return_value_used) {
751 zval* retval = decode_request_worker(*xml, encoding ? *encoding : NULL, *method);
753 *return_value = *retval;
761 /* {{{ proto array xmlrpc_decode(string xml [, string encoding])
762 Decodes XML into native PHP types */
763 PHP_FUNCTION(xmlrpc_decode)
765 zval **arg1, **arg2 = NULL;
766 int argc = ZEND_NUM_ARGS();
768 if (argc < 1 || argc > 2 || (zend_get_parameters_ex(argc, &arg1, &arg2) == FAILURE)) {
772 convert_to_string_ex(arg1);
774 convert_to_string_ex(arg2);
777 if(return_value_used) {
778 zval* retval = decode_request_worker(*arg1, arg2 ? *arg2 : NULL, NULL);
780 *return_value = *retval;
788 /*************************
789 * server related methods *
790 *************************/
792 /* {{{ proto resource xmlrpc_server_create(void)
793 Creates an xmlrpc server */
794 PHP_FUNCTION(xmlrpc_server_create)
796 if(ZEND_NUM_ARGS() != 0) {
800 if(return_value_used) {
801 zval *method_map, *introspection_map;
802 xmlrpc_server_data *server = emalloc(sizeof(xmlrpc_server_data));
803 MAKE_STD_ZVAL(method_map);
804 MAKE_STD_ZVAL(introspection_map);
806 array_init(method_map);
807 array_init(introspection_map);
809 /* allocate server data. free'd in destroy_server_data() */
810 server->method_map = method_map;
811 server->introspection_map = introspection_map;
812 server->server_ptr = XMLRPC_ServerCreate();
814 XMLRPC_ServerRegisterIntrospectionCallback(server->server_ptr, php_xmlrpc_introspection_callback);
816 /* store for later use */
817 ZEND_REGISTER_RESOURCE(return_value,server, le_xmlrpc_server);
822 /* {{{ proto int xmlrpc_server_destroy(resource server)
823 Destroys server resources */
824 PHP_FUNCTION(xmlrpc_server_destroy)
827 int bSuccess = FAILURE;
829 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg1) == FAILURE)) {
833 if(Z_TYPE_PP(arg1) == IS_RESOURCE) {
836 xmlrpc_server_data *server = zend_list_find(Z_LVAL_PP(arg1), &type);
838 if(server && type == le_xmlrpc_server) {
839 bSuccess = zend_list_delete(Z_LVAL_PP(arg1));
841 /* called by hashtable destructor
842 * destroy_server_data(server);
846 RETVAL_LONG(bSuccess == SUCCESS);
851 /* called by xmlrpc C engine as method handler for all registered methods.
852 * it then calls the corresponding PHP function to handle the method.
854 static XMLRPC_VALUE php_xmlrpc_callback(XMLRPC_SERVER server, XMLRPC_REQUEST xRequest, void* data)
856 xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
858 zval* callback_params[3];
861 /* convert xmlrpc to native php types */
862 xmlrpc_params = XMLRPC_to_PHP(XMLRPC_RequestGetData(xRequest));
864 /* setup data hoojum */
865 callback_params[0] = pData->xmlrpc_method;
866 callback_params[1] = xmlrpc_params;
867 callback_params[2] = pData->caller_params;
869 /* Use same C function for all methods */
871 /* php func prototype: function user_func($method_name, $xmlrpc_params, $user_params) */
872 call_user_function(CG(function_table), NULL, pData->php_function, pData->return_data, 3, callback_params TSRMLS_CC);
874 pData->php_executed = 1;
876 zval_dtor(xmlrpc_params);
877 FREE_ZVAL(xmlrpc_params);
882 /* called by the C server when it first receives an introspection request. We pass this on to
883 * our PHP listeners, if any
885 static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data)
887 zval *retval_ptr, **php_function;
888 zval* callback_params[1];
889 xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
892 MAKE_STD_ZVAL(retval_ptr);
893 Z_TYPE_P(retval_ptr) = IS_NULL;
895 /* setup data hoojum */
896 callback_params[0] = pData->caller_params;
898 /* loop through and call all registered callbacks */
899 zend_hash_internal_pointer_reset(Z_ARRVAL_P(pData->server->introspection_map));
901 if(zend_hash_get_current_data(Z_ARRVAL_P(pData->server->introspection_map),
902 (void**)&php_function) == SUCCESS) {
904 /* php func prototype: function string user_func($user_params) */
905 if(call_user_function(CG(function_table), NULL, *php_function,
906 retval_ptr, 1, callback_params TSRMLS_CC) == SUCCESS) {
908 STRUCT_XMLRPC_ERROR err = {0};
910 /* return value should be a string */
911 convert_to_string(retval_ptr);
913 xData = XMLRPC_IntrospectionCreateDescription(Z_STRVAL_P(retval_ptr), &err);
916 if(!XMLRPC_ServerAddIntrospectionData(server, xData)) {
917 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to add introspection data returned from %s(), improper element structure", Z_STRVAL_PP(php_function));
919 XMLRPC_CleanupValue(xData);
922 /* could not create description */
923 if(err.xml_elem_error.parser_code) {
924 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()",
925 err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error, Z_STRVAL_PP(php_function));
928 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to add introspection data returned from %s()",
929 Z_STRVAL_PP(php_function));
934 /* user func failed */
935 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error calling user introspection callback: %s()", Z_STRVAL_PP(php_function));
942 zend_hash_move_forward(Z_ARRVAL_P(pData->server->introspection_map));
945 /* so we don't call the same callbacks ever again */
946 zend_hash_clean(Z_ARRVAL_P(pData->server->introspection_map));
949 /* {{{ proto bool xmlrpc_server_register_method(resource server, string method_name, string function)
950 Register a PHP function to handle method matching method_name */
951 PHP_FUNCTION(xmlrpc_server_register_method)
953 zval **method_key, **method_name, **handle, *method_name_save;
955 xmlrpc_server_data* server;
957 if (ZEND_NUM_ARGS() != 3 || (zend_get_parameters_ex(3, &handle, &method_key, &method_name) == FAILURE)) {
961 server = zend_list_find(Z_LVAL_PP(handle), &type);
963 if(type == le_xmlrpc_server) {
964 /* register with C engine. every method just calls our standard callback,
965 * and it then dispatches to php as necessary
967 if(XMLRPC_ServerRegisterMethod(server->server_ptr, Z_STRVAL_PP(method_key), php_xmlrpc_callback)) {
968 /* save for later use */
969 MAKE_STD_ZVAL(method_name_save);
970 *method_name_save = **method_name;
971 zval_copy_ctor(method_name_save);
973 /* register our php method */
974 add_zval(server->method_map, Z_STRVAL_PP(method_key), &method_name_save);
984 /* {{{ proto bool xmlrpc_server_register_introspection_callback(resource server, string function)
985 Register a PHP function to generate documentation */
986 PHP_FUNCTION(xmlrpc_server_register_introspection_callback)
988 zval **method_name, **handle, *method_name_save;
990 xmlrpc_server_data* server;
992 if (ZEND_NUM_ARGS() != 2 || (zend_get_parameters_ex(2, &handle, &method_name) == FAILURE)) {
996 server = zend_list_find(Z_LVAL_PP(handle), &type);
998 if(type == le_xmlrpc_server) {
999 /* save for later use */
1000 MAKE_STD_ZVAL(method_name_save);
1001 *method_name_save = **method_name;
1002 zval_copy_ctor(method_name_save);
1004 /* register our php method */
1005 add_zval(server->introspection_map, NULL, &method_name_save);
1014 /* this function is itchin for a re-write */
1016 /* {{{ proto mixed xmlrpc_server_call_method(resource server, string xml, mixed user_data [, array output_options])
1017 Parses XML requests and call methods */
1018 PHP_FUNCTION(xmlrpc_server_call_method)
1020 xmlrpc_callback_data data = {0};
1021 XMLRPC_REQUEST xRequest;
1022 STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS input_opts;
1023 xmlrpc_server_data* server;
1024 zval **rawxml, **caller_params, **handle, **output_opts = NULL;
1026 php_output_options out;
1027 int argc =ZEND_NUM_ARGS();
1029 if (argc < 3 || argc > 4 || (zend_get_parameters_ex(argc, &handle, &rawxml, &caller_params, &output_opts) != SUCCESS)) {
1032 /* user output options */
1034 set_output_options(&out, NULL);
1037 set_output_options(&out, *output_opts);
1040 server = zend_list_find(Z_LVAL_PP(handle), &type);
1042 if(type == le_xmlrpc_server) {
1043 /* HACK: use output encoding for now */
1044 input_opts.xml_elem_opts.encoding = utf8_get_encoding_id_from_string(out.xmlrpc_out.xml_elem_opts.encoding);
1046 /* generate an XMLRPC_REQUEST from the raw xml input */
1047 xRequest = XMLRPC_REQUEST_FromXML(Z_STRVAL_PP(rawxml), Z_STRLEN_PP(rawxml), &input_opts);
1050 const char* methodname = XMLRPC_RequestGetMethodName(xRequest);
1051 zval **php_function;
1052 XMLRPC_VALUE xAnswer = NULL;
1053 MAKE_STD_ZVAL(data.xmlrpc_method); /* init. very important. spent a frustrating day finding this out. */
1054 MAKE_STD_ZVAL(data.return_data);
1055 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 */
1056 Z_TYPE_P(data.xmlrpc_method) = IS_NULL;
1062 /* setup some data to pass to the callback function */
1063 Z_STRVAL_P(data.xmlrpc_method) = estrdup(methodname);
1064 Z_STRLEN_P(data.xmlrpc_method) = strlen(methodname);
1065 Z_TYPE_P(data.xmlrpc_method) = IS_STRING;
1066 data.caller_params = *caller_params;
1067 data.php_executed = 0;
1068 data.server = server;
1070 /* check if the called method has been previous registered */
1071 if(zend_hash_find(Z_ARRVAL_P(server->method_map),
1072 Z_STRVAL_P(data.xmlrpc_method),
1073 Z_STRLEN_P(data.xmlrpc_method) + 1,
1074 (void**)&php_function) == SUCCESS) {
1076 data.php_function = *php_function;
1079 /* We could just call the php method directly ourselves at this point, but we do this
1080 * with a C callback in case the xmlrpc library ever implements some cool usage stats,
1083 xAnswer = XMLRPC_ServerCallMethod(server->server_ptr, xRequest, &data);
1084 if(xAnswer && out.b_php_out) {
1085 zval_dtor(data.return_data);
1086 FREE_ZVAL(data.return_data);
1087 data.return_data = XMLRPC_to_PHP(xAnswer);
1088 } else if(data.php_executed && !out.b_php_out) {
1089 xAnswer = PHP_to_XMLRPC(data.return_data);
1092 /* should we return data as xml? */
1093 if(!out.b_php_out) {
1094 XMLRPC_REQUEST xResponse = XMLRPC_RequestNew();
1099 /* automagically determine output serialization type from request type */
1100 if (out.b_auto_version) {
1101 XMLRPC_REQUEST_OUTPUT_OPTIONS opts = XMLRPC_RequestGetOutputOptions(xRequest);
1103 out.xmlrpc_out.version = opts->version;
1106 /* set some required request hoojum */
1107 XMLRPC_RequestSetOutputOptions(xResponse, &out.xmlrpc_out);
1108 XMLRPC_RequestSetRequestType(xResponse, xmlrpc_request_response);
1109 XMLRPC_RequestSetData(xResponse, xAnswer);
1110 XMLRPC_RequestSetMethodName(xResponse, methodname);
1113 outBuf = XMLRPC_REQUEST_ToXML(xResponse, &buf_len);
1115 RETVAL_STRINGL(outBuf, buf_len, 1);
1118 /* cleanup after ourselves. what a sty! */
1119 XMLRPC_RequestFree(xResponse, 0);
1121 } else { /* or as native php types? */
1122 *return_value = *data.return_data;
1123 zval_copy_ctor(return_value);
1126 /* cleanup after ourselves. what a sty! */
1127 zval_dtor(data.xmlrpc_method);
1128 FREE_ZVAL(data.xmlrpc_method);
1129 zval_dtor(data.return_data);
1130 FREE_ZVAL(data.return_data);
1133 XMLRPC_CleanupValue(xAnswer);
1136 XMLRPC_RequestFree(xRequest, 1);
1143 /* {{{ proto int xmlrpc_server_add_introspection_data(resource server, array desc)
1144 Adds introspection documentation */
1145 PHP_FUNCTION(xmlrpc_server_add_introspection_data)
1147 zval **handle, **desc;
1149 xmlrpc_server_data* server;
1151 if (ZEND_NUM_ARGS() != 2 || (zend_get_parameters_ex(2, &handle, &desc) == FAILURE)) {
1155 server = zend_list_find(Z_LVAL_PP(handle), &type);
1157 if (type == le_xmlrpc_server) {
1158 XMLRPC_VALUE xDesc = PHP_to_XMLRPC(*desc);
1160 int retval = XMLRPC_ServerAddIntrospectionData(server->server_ptr, xDesc);
1161 XMLRPC_CleanupValue(xDesc);
1162 RETURN_LONG(retval);
1170 /* {{{ proto array xmlrpc_parse_method_descriptions(string xml)
1171 Decodes XML into a list of method descriptions */
1172 PHP_FUNCTION(xmlrpc_parse_method_descriptions)
1174 zval **arg1, *retval;
1176 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg1) == FAILURE)) {
1180 convert_to_string_ex(arg1);
1182 if(return_value_used) {
1183 STRUCT_XMLRPC_ERROR err = {0};
1184 XMLRPC_VALUE xVal = XMLRPC_IntrospectionCreateDescription(Z_STRVAL_PP(arg1), &err);
1186 retval = XMLRPC_to_PHP(xVal);
1189 *return_value = *retval;
1190 zval_copy_ctor(return_value);
1192 /* dust, sweep, and mop */
1193 XMLRPC_CleanupValue(xVal);
1195 /* could not create description */
1196 if(err.xml_elem_error.parser_code) {
1197 php_error_docref(NULL TSRMLS_CC, E_WARNING, "xml parse error: [line %ld, column %ld, message: %s] Unable to create introspection data",
1198 err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error);
1200 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid xml structure. Unable to create introspection data");
1203 php_error_docref(NULL TSRMLS_CC, E_WARNING, "xml parse error. no method description created");
1214 #define XMLRPC_TYPE_COUNT 9
1215 #define XMLRPC_VECTOR_TYPE_COUNT 4
1216 #define TYPE_STR_MAP_SIZE (XMLRPC_TYPE_COUNT + XMLRPC_VECTOR_TYPE_COUNT)
1218 /* return a string matching a given xmlrpc type */
1219 static const char** get_type_str_mapping(void)
1221 static const char* str_mapping[TYPE_STR_MAP_SIZE];
1222 static int first = 1;
1224 /* warning. do not add/delete without changing size define */
1225 str_mapping[xmlrpc_none] = "none";
1226 str_mapping[xmlrpc_empty] = "empty";
1227 str_mapping[xmlrpc_base64] = "base64";
1228 str_mapping[xmlrpc_boolean] = "boolean";
1229 str_mapping[xmlrpc_datetime] = "datetime";
1230 str_mapping[xmlrpc_double] = "double";
1231 str_mapping[xmlrpc_int] = "int";
1232 str_mapping[xmlrpc_string] = "string";
1233 str_mapping[xmlrpc_vector] = "vector";
1234 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_none] = "none";
1235 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_array] = "array";
1236 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_mixed] = "mixed";
1237 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_struct] = "struct";
1240 return (const char**)str_mapping;
1243 /* map an xmlrpc type to a string */
1244 const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype)
1246 const char** str_mapping = get_type_str_mapping();
1248 if (vtype == xmlrpc_vector_none) {
1249 return str_mapping[type];
1251 return str_mapping[XMLRPC_TYPE_COUNT + vtype];
1255 /* map a string to an xmlrpc type */
1256 XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str)
1258 const char** str_mapping = get_type_str_mapping();
1262 for (i = 0; i < XMLRPC_TYPE_COUNT; i++) {
1263 if (!strcmp(str_mapping[i], str)) {
1264 return (XMLRPC_VALUE_TYPE) i;
1271 /* map a string to an xmlrpc vector type */
1272 XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str)
1274 const char** str_mapping = get_type_str_mapping();
1278 for (i = XMLRPC_TYPE_COUNT; i < TYPE_STR_MAP_SIZE; i++) {
1279 if (!strcmp(str_mapping[i], str)) {
1280 return (XMLRPC_VECTOR_TYPE) (i - XMLRPC_TYPE_COUNT);
1288 /* set a given value to a particular type.
1289 * note: this only works on strings, and only for date and base64,
1290 * which do not have native php types. black magic lies herein.
1292 int set_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE newtype)
1294 int bSuccess = FAILURE;
1297 /* we only really care about strings because they can represent
1298 * base64 and datetime. all other types have corresponding php types
1300 if (Z_TYPE_P(value) == IS_STRING) {
1301 if (newtype == xmlrpc_base64 || newtype == xmlrpc_datetime) {
1302 const char* typestr = xmlrpc_type_as_str(newtype, xmlrpc_vector_none);
1305 MAKE_STD_ZVAL(type);
1307 Z_TYPE_P(type) = IS_STRING;
1308 Z_STRVAL_P(type) = estrdup(typestr);
1309 Z_STRLEN_P(type) = strlen(typestr);
1311 if(newtype == xmlrpc_datetime) {
1312 XMLRPC_VALUE v = XMLRPC_CreateValueDateTime_ISO8601(NULL, value->value.str.val);
1314 time_t timestamp = XMLRPC_GetValueDateTime(v);
1318 MAKE_STD_ZVAL(ztimestamp);
1320 ztimestamp->type = IS_LONG;
1321 ztimestamp->value.lval = timestamp;
1323 convert_to_object(value);
1324 if(SUCCESS == zend_hash_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void *) &type, sizeof(zval *), NULL)) {
1325 bSuccess = zend_hash_update(Z_OBJPROP_P(value), OBJECT_VALUE_TS_ATTR, sizeof(OBJECT_VALUE_TS_ATTR), (void *) &ztimestamp, sizeof(zval *), NULL);
1328 XMLRPC_CleanupValue(v);
1332 convert_to_object(value);
1333 bSuccess = zend_hash_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void *) &type, sizeof(zval *), NULL);
1341 /* return xmlrpc type of a php value */
1342 XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(zval* value, zval** newvalue)
1344 XMLRPC_VALUE_TYPE type = xmlrpc_none;
1348 switch (Z_TYPE_P(value)) {
1350 type = xmlrpc_base64;
1352 #ifndef BOOL_AS_LONG
1354 /* Right thing to do, but it breaks some legacy code. */
1356 type = xmlrpc_boolean;
1366 type = xmlrpc_double;
1369 type = xmlrpc_string;
1372 type = xmlrpc_string;
1375 case IS_CONSTANT_ARRAY:
1376 type = xmlrpc_vector;
1381 type = xmlrpc_vector;
1383 if (zend_hash_find(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void**) &attr) == SUCCESS) {
1384 if (Z_TYPE_PP(attr) == IS_STRING) {
1385 type = xmlrpc_str_as_type(Z_STRVAL_PP(attr));
1392 /* if requested, return an unmolested (magic removed) copy of the value */
1396 if ((type == xmlrpc_base64 && Z_TYPE_P(value) != IS_NULL) || type == xmlrpc_datetime) {
1397 if (zend_hash_find(Z_OBJPROP_P(value), OBJECT_VALUE_ATTR, sizeof(OBJECT_VALUE_ATTR), (void**) &val) == SUCCESS) {
1410 /* {{{ proto bool xmlrpc_set_type(string value, string type)
1411 Sets xmlrpc type, base64 or datetime, for a PHP string value */
1412 PHP_FUNCTION(xmlrpc_set_type)
1415 XMLRPC_VALUE_TYPE vtype;
1417 if (ZEND_NUM_ARGS() != 2 || (zend_get_parameters_ex(2, &arg, &type) == FAILURE)) {
1421 convert_to_string_ex(type);
1422 vtype = xmlrpc_str_as_type(Z_STRVAL_PP(type));
1423 if (vtype != xmlrpc_none) {
1424 if (set_zval_xmlrpc_type(*arg, vtype) == SUCCESS) {
1428 zend_error(E_WARNING,"invalid type '%s' passed to xmlrpc_set_type()", Z_STRVAL_PP(type));
1434 /* {{{ proto string xmlrpc_get_type(mixed value)
1435 Gets xmlrpc type for a PHP value. Especially useful for base64 and datetime strings */
1436 PHP_FUNCTION(xmlrpc_get_type)
1439 XMLRPC_VALUE_TYPE type;
1440 XMLRPC_VECTOR_TYPE vtype = xmlrpc_vector_none;
1442 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg) == FAILURE)) {
1446 type = get_zval_xmlrpc_type(*arg, 0);
1447 if (type == xmlrpc_vector) {
1448 vtype = determine_vector_type(Z_ARRVAL_PP(arg));
1451 RETURN_STRING((char*) xmlrpc_type_as_str(type, vtype), 1);
1455 /* {{{ proto bool xmlrpc_is_fault(array)
1456 Determines if an array value represents an XMLRPC fault. */
1457 PHP_FUNCTION(xmlrpc_is_fault)
1461 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg) == FAILURE)) {
1465 if (Z_TYPE_PP(arg) != IS_ARRAY) {
1466 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array argument expected");
1468 /* The "correct" way to do this would be to call the xmlrpc
1469 * library XMLRPC_ValueIsFault() func. However, doing that
1470 * would require us to create an xmlrpc value from the php
1471 * array, which is rather expensive, especially if it was
1472 * a big array. Thus, we resort to this not so clever hackery.
1474 if (zend_hash_find(Z_ARRVAL_PP(arg), FAULT_CODE, FAULT_CODE_LEN + 1, (void**) &val) == SUCCESS &&
1475 zend_hash_find(Z_ARRVAL_PP(arg), FAULT_STRING, FAULT_STRING_LEN + 1, (void**) &val) == SUCCESS) {