X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fxmlrpc.inc;h=52424b00aaaf7f50cc7d231e34a758e13fca58a1;hb=9decb91e487db7cf575f6a4cbdd43afba7ea1cac;hp=f57051af22166b6eef7775eceec1764f39d3d301;hpb=d34b765180c60be4688af802490bb7e7909a9141;p=plcapi.git diff --git a/lib/xmlrpc.inc b/lib/xmlrpc.inc index f57051a..52424b0 100644 --- a/lib/xmlrpc.inc +++ b/lib/xmlrpc.inc @@ -89,7 +89,8 @@ 'METHODNAME' => array('METHODCALL'), 'PARAMS' => array('METHODCALL', 'METHODRESPONSE'), 'FAULT' => array('METHODRESPONSE'), - 'NIL' => array('VALUE') // only used when extension activated + 'NIL' => array('VALUE'), // only used when extension activated + 'EX:NIL' => array('VALUE') // only used when extension activated ); // define extra types for supporting NULL (useful for json or ) @@ -220,9 +221,12 @@ /// @deprecated $GLOBALS['xmlrpc_backslash']=chr(92).chr(92); - // set to TRUE to enable correct decoding of values + // set to TRUE to enable correct decoding of and values $GLOBALS['xmlrpc_null_extension']=false; + // set to TRUE to enable encoding of php NULL values to instead of + $GLOBALS['xmlrpc_null_apache_encoding']=false; + // used to store state during parsing // quick explanation of components: // ac - used to accumulate values @@ -403,6 +407,7 @@ else { $GLOBALS['_xh']['rt'] = strtolower($name); + $GLOBALS['_xh']['rt'] = strtolower($name); } } else @@ -495,6 +500,7 @@ $GLOBALS['_xh']['vt']=null; break; case 'NIL': + case 'EX:NIL': if ($GLOBALS['xmlrpc_null_extension']) { if ($GLOBALS['_xh']['vt']!='value') @@ -557,7 +563,7 @@ if ($rebuild_xmlrpcvals) { // build the xmlrpc val out of the data received, and substitute it - $temp =& new xmlrpcval($GLOBALS['_xh']['value'], $GLOBALS['_xh']['vt']); + $temp = new xmlrpcval($GLOBALS['_xh']['value'], $GLOBALS['_xh']['vt']); // in case we got info about underlying php class, save it // in the object we're rebuilding if (isset($GLOBALS['_xh']['php_class'])) @@ -720,6 +726,7 @@ $GLOBALS['_xh']['method']=preg_replace('/^[\n\r\t ]+/', '', $GLOBALS['_xh']['ac']); break; case 'NIL': + case 'EX:NIL': if ($GLOBALS['xmlrpc_null_extension']) { $GLOBALS['_xh']['vt']='null'; @@ -821,6 +828,8 @@ var $proxy_pass=''; var $proxy_authtype=1; var $cookies=array(); + var $extracurlopts=array(); + /** * List of http compression methods accepted by the client for responses. * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib @@ -852,6 +861,10 @@ * valid strings are 'xmlrpcvals', 'phpvals' or 'xml' */ var $return_type = 'xmlrpcvals'; + /** + * Sent to servers in http headers + */ + var $user_agent; /** * @param string $path either the complete server URL or the PATH part of the xmlrc server URL, e.g. /xmlrpc/server.php @@ -919,11 +932,14 @@ $this->accepted_compression = array('gzip', 'deflate'); } - // keepalives: enabled by default ONLY for PHP >= 4.3.8 + // keepalives: enabled by default $this->keepalive = true; // by default the xml parser can support these 3 charset encodings $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII'); + + // initialize user_agent string + $this->user_agent = $GLOBALS['xmlrpcName'] . ' ' . $GLOBALS['xmlrpcVersion']; } /** @@ -1090,6 +1106,25 @@ } } + /** + * Directly set cURL options, for extra flexibility + * It allows eg. to bind client to a specific IP interface / address + * @param $options array + */ + function SetCurlOptions( $options ) + { + $this->extracurlopts = $options; + } + + /** + * Set user-agent string that will be used by this client instance + * in http headers sent to the server + */ + function SetUserAgent( $agentstring ) + { + $this->user_agent = $agentstring; + } + /** * Send an xmlrpc request * @param mixed $msg The message object, or an array of messages for using multicall, or the complete xml representation of a request @@ -1115,7 +1150,7 @@ } elseif(is_string($msg)) { - $n =& new xmlrpcmsg(''); + $n = new xmlrpcmsg(''); $n->payload = $msg; $msg = $n; } @@ -1244,7 +1279,7 @@ $credentials='Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n"; if ($authtype != 1) { - error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth is supported with HTTP 1.0'); + error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth is supported with HTTP 1.0'); } } @@ -1268,7 +1303,7 @@ { if ($proxyauthtype != 1) { - error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth to proxy is supported with HTTP 1.0'); + error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth to proxy is supported with HTTP 1.0'); } $proxy_credentials = 'Proxy-Authorization: Basic ' . base64_encode($proxyusername.':'.$proxypassword) . "\r\n"; } @@ -1308,7 +1343,7 @@ } $op= 'POST ' . $uri. " HTTP/1.0\r\n" . - 'User-Agent: ' . $GLOBALS['xmlrpcName'] . ' ' . $GLOBALS['xmlrpcVersion'] . "\r\n" . + 'User-Agent: ' . $this->user_agent . "\r\n" . 'Host: '. $server . ':' . $port . "\r\n" . $credentials . $proxy_credentials . @@ -1345,15 +1380,15 @@ else { $this->errstr='Connect error: '.$this->errstr; - $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr . ' (' . $this->errno . ')'); + $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr . ' (' . $this->errno . ')'); return $r; } if(!fputs($fp, $op, strlen($op))) { - fclose($fp); + fclose($fp); $this->errstr='Write error'; - $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr); + $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr); return $r; } else @@ -1404,7 +1439,7 @@ if(!function_exists('curl_init')) { $this->errstr='CURL unavailable on this install'; - $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_curl'], $GLOBALS['xmlrpcstr']['no_curl']); + $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_curl'], $GLOBALS['xmlrpcstr']['no_curl']); return $r; } if($method == 'https') @@ -1413,7 +1448,7 @@ ((is_string($info) && strpos($info, 'OpenSSL') === null) || (is_array($info) && !isset($info['ssl_version'])))) { $this->errstr='SSL unavailable on this install'; - $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_ssl'], $GLOBALS['xmlrpcstr']['no_ssl']); + $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_ssl'], $GLOBALS['xmlrpcstr']['no_ssl']); return $r; } } @@ -1491,7 +1526,7 @@ { curl_setopt($curl, CURLOPT_VERBOSE, 1); } - curl_setopt($curl, CURLOPT_USERAGENT, $GLOBALS['xmlrpcName'].' '.$GLOBALS['xmlrpcVersion']); + curl_setopt($curl, CURLOPT_USERAGENT, $this->user_agent); // required for XMLRPC: post the data curl_setopt($curl, CURLOPT_POST, 1); // the data @@ -1544,7 +1579,7 @@ } else if ($authtype != 1) { - error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth is supported by the current PHP/curl install'); + error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth is supported by the current PHP/curl install'); } } @@ -1603,7 +1638,7 @@ } else if ($proxyauthtype != 1) { - error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth to proxy is supported by the current PHP/curl install'); + error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth to proxy is supported by the current PHP/curl install'); } } } @@ -1621,6 +1656,11 @@ curl_setopt($curl, CURLOPT_COOKIE, substr($cookieheader, 0, -2)); } + foreach ($this->extracurlopts as $opt => $val) + { + curl_setopt($curl, $opt, $val); + } + $result = curl_exec($curl); if ($this->debug > 1) @@ -1634,7 +1674,7 @@ if(!$result) /// @todo we should use a better check here - what if we get back '' or '0'? { $this->errstr='no response'; - $resp=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['curl_fail'], $GLOBALS['xmlrpcstr']['curl_fail']. ': '. curl_error($curl)); + $resp=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['curl_fail'], $GLOBALS['xmlrpcstr']['curl_fail']. ': '. curl_error($curl)); curl_close($curl); if($keepalive) { @@ -1705,7 +1745,7 @@ } else { - $result =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['multicall_error'], $GLOBALS['xmlrpcstr']['multicall_error']); + $result = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['multicall_error'], $GLOBALS['xmlrpcstr']['multicall_error']); } } } @@ -1752,17 +1792,17 @@ $calls = array(); foreach($msgs as $msg) { - $call['methodName'] =& new xmlrpcval($msg->method(),'string'); + $call['methodName'] = new xmlrpcval($msg->method(),'string'); $numParams = $msg->getNumParams(); $params = array(); for($i = 0; $i < $numParams; $i++) { $params[$i] = $msg->getParam($i); } - $call['params'] =& new xmlrpcval($params, 'array'); - $calls[] =& new xmlrpcval($call, 'struct'); + $call['params'] = new xmlrpcval($params, 'array'); + $calls[] = new xmlrpcval($call, 'struct'); } - $multicall =& new xmlrpcmsg('system.multicall'); + $multicall = new xmlrpcmsg('system.multicall'); $multicall->addParam(new xmlrpcval($calls, 'array')); // Attempt RPC call @@ -1810,7 +1850,7 @@ return false; // Bad value } // Normal return value - $response[$i] =& new xmlrpcresp($val[0], 0, '', 'phpvals'); + $response[$i] = new xmlrpcresp($val[0], 0, '', 'phpvals'); break; case 2: /// @todo remove usage of @: it is apparently quite slow @@ -1824,7 +1864,7 @@ { return false; } - $response[$i] =& new xmlrpcresp(0, $code, $str); + $response[$i] = new xmlrpcresp(0, $code, $str); break; default: return false; @@ -1857,7 +1897,7 @@ return false; // Bad value } // Normal return value - $response[$i] =& new xmlrpcresp($val->arraymem(0)); + $response[$i] = new xmlrpcresp($val->arraymem(0)); break; case 'struct': $code = $val->structmem('faultCode'); @@ -1870,7 +1910,7 @@ { return false; } - $response[$i] =& new xmlrpcresp(0, $code->scalarval(), $str->scalarval()); + $response[$i] = new xmlrpcresp(0, $code->scalarval(), $str->scalarval()); break; default: return false; @@ -2181,7 +2221,7 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha * Given an open file handle, read all data available and parse it as axmlrpc response. * NB: the file handle is not closed by this function. * NNB: might have trouble in rare cases to work on network streams, as we - * check for a read of 0 bytes instead of feof($fp). + * check for a read of 0 bytes instead of feof($fp). * But since checking for feof(null) returns false, we would risk an * infinite loop in that case, because we cannot trust the caller * to give us a valid pointer to an open file... @@ -2238,8 +2278,8 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha } else { - error_log('XML-RPC: xmlrpcmsg::parseResponse: HTTPS via proxy error, tunnel connection possibly failed'); - $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (HTTPS via proxy error, tunnel connection possibly failed)'); + error_log('XML-RPC: '.__METHOD__.': HTTPS via proxy error, tunnel connection possibly failed'); + $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (HTTPS via proxy error, tunnel connection possibly failed)'); return $r; } } @@ -2259,8 +2299,8 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha if(!preg_match('/^HTTP\/[0-9.]+ 200 /', $data)) { $errstr= substr($data, 0, strpos($data, "\n")-1); - error_log('XML-RPC: xmlrpcmsg::parseResponse: HTTP error, got response: ' .$errstr); - $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (' . $errstr . ')'); + error_log('XML-RPC: '.__METHOD__.': HTTP error, got response: ' .$errstr); + $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (' . $errstr . ')'); return $r; } @@ -2289,7 +2329,7 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha } } // be tolerant to line endings, and extra empty lines - $ar = split("\r?\n", trim(substr($data, 0, $pos))); + $ar = preg_split("/\r?\n/", trim(substr($data, 0, $pos))); while(list(,$line) = @each($ar)) { // take care of multi-line headers and cookies @@ -2384,8 +2424,8 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha { if(!$data = decode_chunked($data)) { - error_log('XML-RPC: xmlrpcmsg::parseResponse: errors occurred when trying to rebuild the chunked data received from server'); - $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['dechunk_fail'], $GLOBALS['xmlrpcstr']['dechunk_fail']); + error_log('XML-RPC: '.__METHOD__.': errors occurred when trying to rebuild the chunked data received from server'); + $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['dechunk_fail'], $GLOBALS['xmlrpcstr']['dechunk_fail']); return $r; } } @@ -2414,22 +2454,22 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha } else { - error_log('XML-RPC: xmlrpcmsg::parseResponse: errors occurred when trying to decode the deflated data received from server'); - $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['decompress_fail'], $GLOBALS['xmlrpcstr']['decompress_fail']); + error_log('XML-RPC: '.__METHOD__.': errors occurred when trying to decode the deflated data received from server'); + $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['decompress_fail'], $GLOBALS['xmlrpcstr']['decompress_fail']); return $r; } } else { - error_log('XML-RPC: xmlrpcmsg::parseResponse: the server sent deflated data. Your php install must have the Zlib extension compiled in to support this.'); - $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['cannot_decompress'], $GLOBALS['xmlrpcstr']['cannot_decompress']); + error_log('XML-RPC: '.__METHOD__.': the server sent deflated data. Your php install must have the Zlib extension compiled in to support this.'); + $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['cannot_decompress'], $GLOBALS['xmlrpcstr']['cannot_decompress']); return $r; } } } } // end of 'if needed, de-chunk, re-inflate response' - // real stupid hack to avoid PHP 4 complaining about returning NULL by ref + // real stupid hack to avoid PHP complaining about returning NULL by ref $r = null; $r =& $r; return $r; @@ -2453,8 +2493,8 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha if($data == '') { - error_log('XML-RPC: xmlrpcmsg::parseResponse: no response received from server.'); - $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_data'], $GLOBALS['xmlrpcstr']['no_data']); + error_log('XML-RPC: '.__METHOD__.': no response received from server.'); + $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_data'], $GLOBALS['xmlrpcstr']['no_data']); return $r; } @@ -2498,23 +2538,16 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha // be tolerant of junk after methodResponse (e.g. javascript ads automatically inserted by free hosts) // idea from Luca Mariano originally in PEARified version of the lib - $bd = false; - // Poor man's version of strrpos for php 4... - $pos = strpos($data, ''); - while($pos || is_int($pos)) - { - $bd = $pos+17; - $pos = strpos($data, '', $bd); - } - if($bd) + $pos = strrpos($data, ''); + if($pos !== false) { - $data = substr($data, 0, $bd); + $data = substr($data, 0, $pos+17); } // if user wants back raw xml, give it to him if ($return_type == 'xml') { - $r =& new xmlrpcresp($data, 0, '', 'xml'); + $r = new xmlrpcresp($data, 0, '', 'xml'); $r->hdrs = $GLOBALS['_xh']['headers']; $r->_cookies = $GLOBALS['_xh']['cookies']; $r->raw_data = $raw_data; @@ -2539,7 +2572,7 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha // makes the lib about 200% slower... //if (!is_valid_charset($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII'))) { - error_log('XML-RPC: xmlrpcmsg::parseResponse: invalid charset encoding of received response: '.$resp_encoding); + error_log('XML-RPC: '.__METHOD__.': invalid charset encoding of received response: '.$resp_encoding); $resp_encoding = $GLOBALS['xmlrpc_defencoding']; } $parser = xml_parser_create($resp_encoding); @@ -2586,7 +2619,7 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha xml_get_current_line_number($parser), xml_get_current_column_number($parser)); } error_log($errstr); - $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'], $GLOBALS['xmlrpcstr']['invalid_return'].' ('.$errstr.')'); + $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'], $GLOBALS['xmlrpcstr']['invalid_return'].' ('.$errstr.')'); xml_parser_free($parser); if($this->debug) { @@ -2606,7 +2639,7 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha /// @todo echo something for user? } - $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'], + $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'], $GLOBALS['xmlrpcstr']['invalid_return'] . ' ' . $GLOBALS['_xh']['isf_reason']); } // third error check: parsing of the response has somehow gone boink. @@ -2616,7 +2649,7 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha // something odd has happened // and it's time to generate a client side error // indicating something odd went on - $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'], + $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'], $GLOBALS['xmlrpcstr']['invalid_return']); } else @@ -2656,11 +2689,11 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha $errno = -1; } - $r =& new xmlrpcresp(0, $errno, $errstr); + $r = new xmlrpcresp(0, $errno, $errstr); } else { - $r=&new xmlrpcresp($v, 0, '', $return_type); + $r=new xmlrpcresp($v, 0, '', $return_type); } } @@ -2714,7 +2747,7 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha $this->me['struct']=$val; break; default: - error_log("XML-RPC: xmlrpcval::xmlrpcval: not a known type ($type)"); + error_log("XML-RPC: ".__METHOD__.": not a known type ($type)"); } /*if($type=='') { @@ -2746,13 +2779,13 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha $typeof=@$GLOBALS['xmlrpcTypes'][$type]; if($typeof!=1) { - error_log("XML-RPC: xmlrpcval::addScalar: not a scalar type ($type)"); + error_log("XML-RPC: ".__METHOD__.": not a scalar type ($type)"); return 0; } // coerce booleans into correct values - // NB: we should iether do it for datetimes, integers and doubles, too, - // or just plain remove this check, implemnted on booleans only... + // NB: we should either do it for datetimes, integers and doubles, too, + // or just plain remove this check, implemented on booleans only... if($type==$GLOBALS['xmlrpcBoolean']) { if(strcasecmp($val,'true')==0 || $val==1 || ($val==true && strcasecmp($val,'false'))) @@ -2768,18 +2801,18 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha switch($this->mytype) { case 1: - error_log('XML-RPC: xmlrpcval::addScalar: scalar xmlrpcval can have only one value'); + error_log('XML-RPC: '.__METHOD__.': scalar xmlrpcval can have only one value'); return 0; case 3: - error_log('XML-RPC: xmlrpcval::addScalar: cannot add anonymous scalar to struct xmlrpcval'); + error_log('XML-RPC: '.__METHOD__.': cannot add anonymous scalar to struct xmlrpcval'); return 0; case 2: // we're adding a scalar value to an array here //$ar=$this->me['array']; - //$ar[]=&new xmlrpcval($val, $type); + //$ar[]=new xmlrpcval($val, $type); //$this->me['array']=$ar; // Faster (?) avoid all the costly array-copy-by-val done here... - $this->me['array'][]=&new xmlrpcval($val, $type); + $this->me['array'][]=new xmlrpcval($val, $type); return 1; default: // a scalar, so set the value and remember we're scalar @@ -2813,7 +2846,7 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha } else { - error_log('XML-RPC: xmlrpcval::addArray: already initialized as a [' . $this->kindOf() . ']'); + error_log('XML-RPC: '.__METHOD__.': already initialized as a [' . $this->kindOf() . ']'); return 0; } } @@ -2842,7 +2875,7 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha } else { - error_log('XML-RPC: xmlrpcval::addStruct: already initialized as a [' . $this->kindOf() . ']'); + error_log('XML-RPC: '.__METHOD__.': already initialized as a [' . $this->kindOf() . ']'); return 0; } } @@ -2914,15 +2947,41 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha $rs.="<${typ}>".(int)$val.""; break; case $GLOBALS['xmlrpcDouble']: - // avoid using standard conversion of float to string because it is locale-dependent, - // and also because the xmlrpc spec forbids exponential notation - // sprintf('%F') would be most likely ok but it is only available since PHP 4.3.10 and PHP 5.0.3. - // The code below tries its best at keeping max precision while avoiding exp notation, - // but there is of course no limit in the number of decimal places to be used... - $rs.="<${typ}>".preg_replace('/\\.?0+$/','',number_format((double)$val, 128, '.', '')).""; + // avoid using standard conversion of float to string because it is locale-dependent, + // and also because the xmlrpc spec forbids exponential notation. + // sprintf('%F') could be most likely ok but it fails eg. on 2e-14. + // The code below tries its best at keeping max precision while avoiding exp notation, + // but there is of course no limit in the number of decimal places to be used... + $rs.="<${typ}>".preg_replace('/\\.?0+$/','',number_format((double)$val, 128, '.', '')).""; + break; + case $GLOBALS['xmlrpcDateTime']: + if (is_string($val)) + { + $rs.="<${typ}>${val}"; + } + else if(is_a($val, 'DateTime')) + { + $rs.="<${typ}>".$val->format('Ymd\TH:i:s').""; + } + else if(is_int($val)) + { + $rs.="<${typ}>".strftime("%Y%m%dT%H:%M:%S", $val).""; + } + else + { + // not really a good idea here: but what shall we output anyway? left for backward compat... + $rs.="<${typ}>${val}"; + } break; case $GLOBALS['xmlrpcNull']: - $rs.=""; + if ($GLOBALS['xmlrpc_null_apache_encoding']) + { + $rs.=""; + } + else + { + $rs.=""; + } break; default: // no standard type value should arrive here, but provide a possibility @@ -3222,7 +3281,7 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha * @author Dan Libby (dan@libby.com) * * @param xmlrpcval $xmlrpc_val - * @param array $options if 'decode_php_objs' is set in the options array, xmlrpc structs can be decoded into php objects + * @param array $options if 'decode_php_objs' is set in the options array, xmlrpc structs can be decoded into php objects; if 'dates_as_objects' is set xmlrpc datetimes are decoded as php DateTime objects (standard is * @return mixed */ function php_xmlrpc_decode($xmlrpc_val, $options=array()) @@ -3249,6 +3308,27 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha return $xmlrpc_val->scalarval(); } } + if (in_array('dates_as_objects', $options) && $xmlrpc_val->scalartyp() == 'dateTime.iso8601') + { + // we return a Datetime object instead of a string + // since now the constructor of xmlrpcval accepts safely strings, ints and datetimes, + // we cater to all 3 cases here + $out = $xmlrpc_val->scalarval(); + if (is_string($out)) + { + $out= strtotime($out); + } + if (is_int($out)) + { + $result = new Datetime(); + $result->setTimestamp($out); + return $result; + } + elseif (is_a($out, 'Datetime')) + { + return $out; + } + } return $xmlrpc_val->scalarval(); case 'array': $size = $xmlrpc_val->arraysize(); @@ -3323,27 +3403,27 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha * @param array $options can include 'encode_php_objs', 'auto_dates', 'null_extension' or 'extension_api' * @return xmlrpcval */ - function &php_xmlrpc_encode($php_val, $options=array()) + function php_xmlrpc_encode($php_val, $options=array()) { $type = gettype($php_val); switch($type) { case 'string': if (in_array('auto_dates', $options) && preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $php_val)) - $xmlrpc_val =& new xmlrpcval($php_val, $GLOBALS['xmlrpcDateTime']); + $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcDateTime']); else - $xmlrpc_val =& new xmlrpcval($php_val, $GLOBALS['xmlrpcString']); + $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcString']); break; case 'integer': - $xmlrpc_val =& new xmlrpcval($php_val, $GLOBALS['xmlrpcInt']); + $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcInt']); break; case 'double': - $xmlrpc_val =& new xmlrpcval($php_val, $GLOBALS['xmlrpcDouble']); + $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcDouble']); break; // // Add support for encoding/decoding of booleans, since they are supported in PHP case 'boolean': - $xmlrpc_val =& new xmlrpcval($php_val, $GLOBALS['xmlrpcBoolean']); + $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcBoolean']); break; // case 'array': @@ -3357,7 +3437,7 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha $ko = false; foreach($php_val as $key => $val) { - $arr[$key] =& php_xmlrpc_encode($val, $options); + $arr[$key] = php_xmlrpc_encode($val, $options); if(!$ko && $key !== $j) { $ko = true; @@ -3366,11 +3446,11 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha } if($ko) { - $xmlrpc_val =& new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']); + $xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']); } else { - $xmlrpc_val =& new xmlrpcval($arr, $GLOBALS['xmlrpcArray']); + $xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcArray']); } break; case 'object': @@ -3378,14 +3458,19 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha { $xmlrpc_val = $php_val; } + else if(is_a($php_val, 'DateTime')) + { + $xmlrpc_val = new xmlrpcval($php_val->format('Ymd\TH:i:s'), $GLOBALS['xmlrpcStruct']); + } else { $arr = array(); + reset($php_val); while(list($k,$v) = each($php_val)) { $arr[$k] = php_xmlrpc_encode($v, $options); } - $xmlrpc_val =& new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']); + $xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']); if (in_array('encode_php_objs', $options)) { // let's save original class name into xmlrpcval: @@ -3397,32 +3482,32 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha case 'NULL': if (in_array('extension_api', $options)) { - $xmlrpc_val =& new xmlrpcval('', $GLOBALS['xmlrpcString']); + $xmlrpc_val = new xmlrpcval('', $GLOBALS['xmlrpcString']); } - if (in_array('null_extension', $options)) + else if (in_array('null_extension', $options)) { - $xmlrpc_val =& new xmlrpcval('', $GLOBALS['xmlrpcNull']); + $xmlrpc_val = new xmlrpcval('', $GLOBALS['xmlrpcNull']); } else { - $xmlrpc_val =& new xmlrpcval(); + $xmlrpc_val = new xmlrpcval(); } break; case 'resource': if (in_array('extension_api', $options)) { - $xmlrpc_val =& new xmlrpcval((int)$php_val, $GLOBALS['xmlrpcInt']); + $xmlrpc_val = new xmlrpcval((int)$php_val, $GLOBALS['xmlrpcInt']); } else { - $xmlrpc_val =& new xmlrpcval(); + $xmlrpc_val = new xmlrpcval(); } // catch "user function", "unknown type" default: // giancarlo pinerolo // it has to return // an empty object in case, not a boolean. - $xmlrpc_val =& new xmlrpcval(); + $xmlrpc_val = new xmlrpcval(); break; } return $xmlrpc_val; @@ -3486,15 +3571,15 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha { $vc = $v->structmem('faultCode'); $vs = $v->structmem('faultString'); - $r =& new xmlrpcresp(0, $vc->scalarval(), $vs->scalarval()); + $r = new xmlrpcresp(0, $vc->scalarval(), $vs->scalarval()); } else { - $r =& new xmlrpcresp($v); + $r = new xmlrpcresp($v); } return $r; case 'methodcall': - $m =& new xmlrpcmsg($GLOBALS['_xh']['method']); + $m = new xmlrpcmsg($GLOBALS['_xh']['method']); for($i=0; $i < count($GLOBALS['_xh']['params']); $i++) { $m->addParam($GLOBALS['_xh']['params'][$i]); @@ -3589,7 +3674,7 @@ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $cha // Note: we do not check for invalid chars in VALUE: // this had better be done using pure ereg as below // Note 2: we might be removing whitespace/tabs that ought to be left in if - // the received charset is a quoted string. But nobody uses such charset names... + // the received charset is a quoted string. But nobody uses such charset names... /// @todo this test will pass if ANY header has charset specification, not only Content-Type. Fix it? $matches = array();