7 /// @todo: do these need to be public?
11 public $method = 'http';
15 public $username = '';
16 public $password = '';
19 public $certpass = '';
21 public $cacertdir = '';
24 public $verifypeer = true;
25 public $verifyhost = 2;
26 public $no_multicall = false;
28 public $proxyport = 0;
29 public $proxy_user = '';
30 public $proxy_pass = '';
31 public $proxy_authtype = 1;
32 public $cookies = array();
33 public $extracurlopts = array();
36 * List of http compression methods accepted by the client for responses.
37 * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib.
39 * NNB: you can set it to any non-empty array for HTTP11 and HTTPS, since
40 * in those cases it will be up to CURL to decide the compression methods
41 * it supports. You might check for the presence of 'zlib' in the output of
42 * curl_version() to determine wheter compression is supported or not
44 public $accepted_compression = array();
46 * Name of compression scheme to be used for sending requests.
47 * Either null, gzip or deflate.
49 public $request_compression = '';
51 * CURL handle: used for keep-alive connections (PHP 4.3.8 up, see:
52 * http://curl.haxx.se/docs/faq.html#7.3).
54 public $xmlrpc_curl_handle = null;
55 /// Whether to use persistent connections for http 1.1 and https
56 public $keepalive = false;
57 /// Charset encodings that can be decoded without problems by the client
58 public $accepted_charset_encodings = array();
59 /// Charset encoding to be used in serializing request. NULL = use ASCII
60 public $request_charset_encoding = '';
62 * Decides the content of Response objects returned by calls to send()
63 * valid strings are 'xmlrpcvals', 'phpvals' or 'xml'.
65 public $return_type = 'xmlrpcvals';
67 * Sent to servers in http headers.
72 * @param string $path either the complete server URL or the PATH part of the xmlrc server URL, e.g. /xmlrpc/server.php
73 * @param string $server the server name / ip address
74 * @param integer $port the port the server is listening on, defaults to 80 or 443 depending on protocol used
75 * @param string $method the http protocol variant: defaults to 'http', 'https' and 'http11' can be used if CURL is installed
77 public function __construct($path, $server = '', $port = '', $method = '')
79 // allow user to specify all params in $path
80 if ($server == '' and $port == '' and $method == '') {
81 $parts = parse_url($path);
82 $server = $parts['host'];
83 $path = isset($parts['path']) ? $parts['path'] : '';
84 if (isset($parts['query'])) {
85 $path .= '?' . $parts['query'];
87 if (isset($parts['fragment'])) {
88 $path .= '#' . $parts['fragment'];
90 if (isset($parts['port'])) {
91 $port = $parts['port'];
93 if (isset($parts['scheme'])) {
94 $method = $parts['scheme'];
96 if (isset($parts['user'])) {
97 $this->username = $parts['user'];
99 if (isset($parts['pass'])) {
100 $this->password = $parts['pass'];
103 if ($path == '' || $path[0] != '/') {
104 $this->path = '/' . $path;
108 $this->server = $server;
113 $this->method = $method;
116 // if ZLIB is enabled, let the client by default accept compressed responses
117 if (function_exists('gzinflate') || (
118 function_exists('curl_init') && (($info = curl_version()) &&
119 ((is_string($info) && strpos($info, 'zlib') !== null) || isset($info['libz_version'])))
122 $this->accepted_compression = array('gzip', 'deflate');
125 // keepalives: enabled by default
126 $this->keepalive = true;
128 // by default the xml parser can support these 3 charset encodings
129 $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
131 // initialize user_agent string
132 $this->user_agent = PhpXmlRpc::$xmlrpcName . ' ' . PhpXmlRpc::$xmlrpcVersion;
136 * Enables/disables the echoing to screen of the xmlrpc responses received.
138 * @param integer $in values 0, 1 and 2 are supported (2 = echo sent msg too, before received response)
140 public function setDebug($in)
146 * Add some http BASIC AUTH credentials, used by the client to authenticate.
148 * @param string $u username
149 * @param string $p password
150 * @param integer $t auth type. See curl_setopt man page for supported auth types. Defaults to CURLAUTH_BASIC (basic auth)
152 public function setCredentials($u, $p, $t = 1)
154 $this->username = $u;
155 $this->password = $p;
156 $this->authtype = $t;
160 * Add a client-side https certificate.
162 * @param string $cert
163 * @param string $certpass
165 public function setCertificate($cert, $certpass)
168 $this->certpass = $certpass;
172 * Add a CA certificate to verify server with (see man page about
173 * CURLOPT_CAINFO for more details).
175 * @param string $cacert certificate file name (or dir holding certificates)
176 * @param bool $is_dir set to true to indicate cacert is a dir. defaults to false
178 public function setCaCertificate($cacert, $is_dir = false)
181 $this->cacertdir = $cacert;
183 $this->cacert = $cacert;
188 * Set attributes for SSL communication: private SSL key
189 * NB: does not work in older php/curl installs
190 * Thanks to Daniel Convissor.
192 * @param string $key The name of a file containing a private SSL key
193 * @param string $keypass The secret password needed to use the private SSL key
195 public function setKey($key, $keypass)
198 $this->keypass = $keypass;
202 * Set attributes for SSL communication: verify server certificate.
204 * @param bool $i enable/disable verification of peer certificate
206 public function setSSLVerifyPeer($i)
208 $this->verifypeer = $i;
212 * Set attributes for SSL communication: verify match of server cert w. hostname.
216 public function setSSLVerifyHost($i)
218 $this->verifyhost = $i;
224 * @param string $proxyhost
225 * @param string $proxyport Defaults to 8080 for HTTP and 443 for HTTPS
226 * @param string $proxyusername Leave blank if proxy has public access
227 * @param string $proxypassword Leave blank if proxy has public access
228 * @param int $proxyauthtype set to constant CURLAUTH_NTLM to use NTLM auth with proxy
230 public function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 1)
232 $this->proxy = $proxyhost;
233 $this->proxyport = $proxyport;
234 $this->proxy_user = $proxyusername;
235 $this->proxy_pass = $proxypassword;
236 $this->proxy_authtype = $proxyauthtype;
240 * Enables/disables reception of compressed xmlrpc responses.
241 * Note that enabling reception of compressed responses merely adds some standard
242 * http headers to xmlrpc requests. It is up to the xmlrpc server to return
243 * compressed responses when receiving such requests.
245 * @param string $compmethod either 'gzip', 'deflate', 'any' or ''
247 public function setAcceptedCompression($compmethod)
249 if ($compmethod == 'any') {
250 $this->accepted_compression = array('gzip', 'deflate');
251 } elseif ($compmethod == false) {
252 $this->accepted_compression = array();
254 $this->accepted_compression = array($compmethod);
259 * Enables/disables http compression of xmlrpc request.
260 * Take care when sending compressed requests: servers might not support them
261 * (and automatic fallback to uncompressed requests is not yet implemented).
263 * @param string $compmethod either 'gzip', 'deflate' or ''
265 public function setRequestCompression($compmethod)
267 $this->request_compression = $compmethod;
271 * Adds a cookie to list of cookies that will be sent to server.
272 * NB: setting any param but name and value will turn the cookie into a 'version 1' cookie:
273 * do not do it unless you know what you are doing.
275 * @param string $name
276 * @param string $value
277 * @param string $path
278 * @param string $domain
281 * @todo check correctness of urlencoding cookie value (copied from php way of doing it...)
283 public function setCookie($name, $value = '', $path = '', $domain = '', $port = null)
285 $this->cookies[$name]['value'] = urlencode($value);
286 if ($path || $domain || $port) {
287 $this->cookies[$name]['path'] = $path;
288 $this->cookies[$name]['domain'] = $domain;
289 $this->cookies[$name]['port'] = $port;
290 $this->cookies[$name]['version'] = 1;
292 $this->cookies[$name]['version'] = 0;
297 * Directly set cURL options, for extra flexibility
298 * It allows eg. to bind client to a specific IP interface / address.
300 * @param array $options
302 public function SetCurlOptions($options)
304 $this->extracurlopts = $options;
308 * Set user-agent string that will be used by this client instance
309 * in http headers sent to the server.
311 public function SetUserAgent($agentstring)
313 $this->user_agent = $agentstring;
317 * Send an xmlrpc request.
319 * @param mixed $msg The request object, or an array of requests for using multicall, or the complete xml representation of a request
320 * @param integer $timeout Connection timeout, in seconds, If unspecified, a platform specific timeout will apply
321 * @param string $method if left unspecified, the http protocol chosen during creation of the object will be used
325 public function & send($msg, $timeout = 0, $method = '')
327 // if user does not specify http protocol, use native method of this client
328 // (i.e. method set during call to constructor)
330 $method = $this->method;
333 if (is_array($msg)) {
334 // $msg is an array of Requests
335 $r = $this->multicall($msg, $timeout, $method);
338 } elseif (is_string($msg)) {
339 $n = new Message('');
344 // where msg is a Request
345 $msg->debug = $this->debug;
347 if ($method == 'https') {
348 $r = $this->sendPayloadHTTPS(
364 $this->proxy_authtype,
369 } elseif ($method == 'http11') {
370 $r = $this->sendPayloadCURL(
386 $this->proxy_authtype,
391 $r = $this->sendPayloadHTTP10(
403 $this->proxy_authtype
410 private function sendPayloadHTTP10($msg, $server, $port, $timeout = 0,
411 $username = '', $password = '', $authtype = 1, $proxyhost = '',
412 $proxyport = 0, $proxyusername = '', $proxypassword = '', $proxyauthtype = 1)
418 // Only create the payload if it was not created previously
419 if (empty($msg->payload)) {
420 $msg->createPayload($this->request_charset_encoding);
423 $payload = $msg->payload;
424 // Deflate request body and set appropriate request headers
425 if (function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate')) {
426 if ($this->request_compression == 'gzip') {
427 $a = @gzencode($payload);
430 $encoding_hdr = "Content-Encoding: gzip\r\n";
433 $a = @gzcompress($payload);
436 $encoding_hdr = "Content-Encoding: deflate\r\n";
443 // thanks to Grant Rauscher <grant7@firstworld.net> for this
445 if ($username != '') {
446 $credentials = 'Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n";
447 if ($authtype != 1) {
448 error_log('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth is supported with HTTP 1.0');
452 $accepted_encoding = '';
453 if (is_array($this->accepted_compression) && count($this->accepted_compression)) {
454 $accepted_encoding = 'Accept-Encoding: ' . implode(', ', $this->accepted_compression) . "\r\n";
457 $proxy_credentials = '';
459 if ($proxyport == 0) {
462 $connectserver = $proxyhost;
463 $connectport = $proxyport;
464 $uri = 'http://' . $server . ':' . $port . $this->path;
465 if ($proxyusername != '') {
466 if ($proxyauthtype != 1) {
467 error_log('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth to proxy is supported with HTTP 1.0');
469 $proxy_credentials = 'Proxy-Authorization: Basic ' . base64_encode($proxyusername . ':' . $proxypassword) . "\r\n";
472 $connectserver = $server;
473 $connectport = $port;
477 // Cookie generation, as per rfc2965 (version 1 cookies) or
478 // netscape's rules (version 0 cookies)
480 if (count($this->cookies)) {
482 foreach ($this->cookies as $name => $cookie) {
483 if ($cookie['version']) {
484 $version = ' $Version="' . $cookie['version'] . '";';
485 $cookieheader .= ' ' . $name . '="' . $cookie['value'] . '";';
486 if ($cookie['path']) {
487 $cookieheader .= ' $Path="' . $cookie['path'] . '";';
489 if ($cookie['domain']) {
490 $cookieheader .= ' $Domain="' . $cookie['domain'] . '";';
492 if ($cookie['port']) {
493 $cookieheader .= ' $Port="' . $cookie['port'] . '";';
496 $cookieheader .= ' ' . $name . '=' . $cookie['value'] . ";";
499 $cookieheader = 'Cookie:' . $version . substr($cookieheader, 0, -1) . "\r\n";
503 $port = ($port == 80) ? '' : (':' . $port);
505 $op = 'POST ' . $uri . " HTTP/1.0\r\n" .
506 'User-Agent: ' . $this->user_agent . "\r\n" .
507 'Host: ' . $server . $port . "\r\n" .
512 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings) . "\r\n" .
514 'Content-Type: ' . $msg->content_type . "\r\nContent-Length: " .
515 strlen($payload) . "\r\n\r\n" .
518 if ($this->debug > 1) {
519 print "<PRE>\n---SENDING---\n" . htmlentities($op) . "\n---END---\n</PRE>";
520 // let the client see this now in case http times out...
525 $fp = @fsockopen($connectserver, $connectport, $this->errno, $this->errstr, $timeout);
527 $fp = @fsockopen($connectserver, $connectport, $this->errno, $this->errstr);
530 if ($timeout > 0 && function_exists('stream_set_timeout')) {
531 stream_set_timeout($fp, $timeout);
534 $this->errstr = 'Connect error: ' . $this->errstr;
535 $r = new Response(0, PhpXmlRpc::$xmlrpcerr['http_error'], $this->errstr . ' (' . $this->errno . ')');
540 if (!fputs($fp, $op, strlen($op))) {
542 $this->errstr = 'Write error';
543 $r = new Response(0, PhpXmlRpc::$xmlrpcerr['http_error'], $this->errstr);
547 // reset errno and errstr on successful socket connection
550 // G. Giunta 2005/10/24: close socket before parsing.
551 // should yield slightly better execution times, and make easier recursive calls (e.g. to follow http redirects)
554 // shall we check for $data === FALSE?
555 // as per the manual, it signals an error
556 $ipd .= fread($fp, 32768);
557 } while (!feof($fp));
559 $r = $msg->parseResponse($ipd, false, $this->return_type);
564 private function sendPayloadHTTPS($msg, $server, $port, $timeout = 0, $username = '',
565 $password = '', $authtype = 1, $cert = '', $certpass = '', $cacert = '', $cacertdir = '',
566 $proxyhost = '', $proxyport = 0, $proxyusername = '', $proxypassword = '', $proxyauthtype = 1,
567 $keepalive = false, $key = '', $keypass = '')
569 $r = $this->sendPayloadCURL($msg, $server, $port, $timeout, $username,
570 $password, $authtype, $cert, $certpass, $cacert, $cacertdir, $proxyhost, $proxyport,
571 $proxyusername, $proxypassword, $proxyauthtype, 'https', $keepalive, $key, $keypass);
577 * Contributed by Justin Miller <justin@voxel.net>
578 * Requires curl to be built into PHP
579 * NB: CURL versions before 7.11.10 cannot use proxy to talk to https servers!
581 private function sendPayloadCURL($msg, $server, $port, $timeout = 0, $username = '',
582 $password = '', $authtype = 1, $cert = '', $certpass = '', $cacert = '', $cacertdir = '',
583 $proxyhost = '', $proxyport = 0, $proxyusername = '', $proxypassword = '', $proxyauthtype = 1, $method = 'https',
584 $keepalive = false, $key = '', $keypass = '')
586 if (!function_exists('curl_init')) {
587 $this->errstr = 'CURL unavailable on this install';
588 $r = new Response(0, PhpXmlRpc::$xmlrpcerr['no_curl'], PhpXmlRpc::$xmlrpcstr['no_curl']);
592 if ($method == 'https') {
593 if (($info = curl_version()) &&
594 ((is_string($info) && strpos($info, 'OpenSSL') === null) || (is_array($info) && !isset($info['ssl_version'])))
596 $this->errstr = 'SSL unavailable on this install';
597 $r = new Response(0, PhpXmlRpc::$xmlrpcerr['no_ssl'], PhpXmlRpc::$xmlrpcstr['no_ssl']);
604 if ($method == 'http') {
611 // Only create the payload if it was not created previously
612 if (empty($msg->payload)) {
613 $msg->createPayload($this->request_charset_encoding);
616 // Deflate request body and set appropriate request headers
617 $payload = $msg->payload;
618 if (function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate')) {
619 if ($this->request_compression == 'gzip') {
620 $a = @gzencode($payload);
623 $encoding_hdr = 'Content-Encoding: gzip';
626 $a = @gzcompress($payload);
629 $encoding_hdr = 'Content-Encoding: deflate';
636 if ($this->debug > 1) {
637 print "<PRE>\n---SENDING---\n" . htmlentities($payload) . "\n---END---\n</PRE>";
638 // let the client see this now in case http times out...
642 if (!$keepalive || !$this->xmlrpc_curl_handle) {
643 $curl = curl_init($method . '://' . $server . ':' . $port . $this->path);
645 $this->xmlrpc_curl_handle = $curl;
648 $curl = $this->xmlrpc_curl_handle;
651 // results into variable
652 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
655 curl_setopt($curl, CURLOPT_VERBOSE, 1);
657 curl_setopt($curl, CURLOPT_USERAGENT, $this->user_agent);
658 // required for XMLRPC: post the data
659 curl_setopt($curl, CURLOPT_POST, 1);
661 curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
663 // return the header too
664 curl_setopt($curl, CURLOPT_HEADER, 1);
666 // NB: if we set an empty string, CURL will add http header indicating
667 // ALL methods it is supporting. This is possibly a better option than
668 // letting the user tell what curl can / cannot do...
669 if (is_array($this->accepted_compression) && count($this->accepted_compression)) {
670 //curl_setopt($curl, CURLOPT_ENCODING, implode(',', $this->accepted_compression));
671 // empty string means 'any supported by CURL' (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
672 if (count($this->accepted_compression) == 1) {
673 curl_setopt($curl, CURLOPT_ENCODING, $this->accepted_compression[0]);
675 curl_setopt($curl, CURLOPT_ENCODING, '');
679 $headers = array('Content-Type: ' . $msg->content_type, 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings));
680 // if no keepalive is wanted, let the server know it in advance
682 $headers[] = 'Connection: close';
684 // request compression header
686 $headers[] = $encoding_hdr;
689 curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
692 curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1);
695 if ($username && $password) {
696 curl_setopt($curl, CURLOPT_USERPWD, $username . ':' . $password);
697 if (defined('CURLOPT_HTTPAUTH')) {
698 curl_setopt($curl, CURLOPT_HTTPAUTH, $authtype);
699 } elseif ($authtype != 1) {
700 error_log('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth is supported by the current PHP/curl install');
704 if ($method == 'https') {
707 curl_setopt($curl, CURLOPT_SSLCERT, $cert);
711 curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $certpass);
713 // whether to verify remote host's cert
714 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifypeer);
715 // set ca certificates file/dir
717 curl_setopt($curl, CURLOPT_CAINFO, $cacert);
720 curl_setopt($curl, CURLOPT_CAPATH, $cacertdir);
722 // set key file (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
724 curl_setopt($curl, CURLOPT_SSLKEY, $key);
726 // set key password (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
728 curl_setopt($curl, CURLOPT_SSLKEYPASSWD, $keypass);
730 // whether to verify cert's common name (CN); 0 for no, 1 to verify that it exists, and 2 to verify that it matches the hostname used
731 curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->verifyhost);
736 if ($proxyport == 0) {
737 $proxyport = 8080; // NB: even for HTTPS, local connection is on port 8080
739 curl_setopt($curl, CURLOPT_PROXY, $proxyhost . ':' . $proxyport);
740 //curl_setopt($curl, CURLOPT_PROXYPORT,$proxyport);
741 if ($proxyusername) {
742 curl_setopt($curl, CURLOPT_PROXYUSERPWD, $proxyusername . ':' . $proxypassword);
743 if (defined('CURLOPT_PROXYAUTH')) {
744 curl_setopt($curl, CURLOPT_PROXYAUTH, $proxyauthtype);
745 } elseif ($proxyauthtype != 1) {
746 error_log('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth to proxy is supported by the current PHP/curl install');
751 // NB: should we build cookie http headers by hand rather than let CURL do it?
752 // the following code does not honour 'expires', 'path' and 'domain' cookie attributes
753 // set to client obj the the user...
754 if (count($this->cookies)) {
756 foreach ($this->cookies as $name => $cookie) {
757 $cookieheader .= $name . '=' . $cookie['value'] . '; ';
759 curl_setopt($curl, CURLOPT_COOKIE, substr($cookieheader, 0, -2));
762 foreach ($this->extracurlopts as $opt => $val) {
763 curl_setopt($curl, $opt, $val);
766 $result = curl_exec($curl);
768 if ($this->debug > 1) {
769 print "<PRE>\n---CURL INFO---\n";
770 foreach (curl_getinfo($curl) as $name => $val) {
771 if (is_array($val)) {
772 $val = implode("\n", $val);
774 print $name . ': ' . htmlentities($val) . "\n";
777 print "---END---\n</PRE>";
781 /// @todo we should use a better check here - what if we get back '' or '0'?
783 $this->errstr = 'no response';
784 $resp = new Response(0, PhpXmlRpc::$xmlrpcerr['curl_fail'], PhpXmlRpc::$xmlrpcstr['curl_fail'] . ': ' . curl_error($curl));
787 $this->xmlrpc_curl_handle = null;
793 $resp = $msg->parseResponse($result, true, $this->return_type);
794 // if we got back a 302, we can not reuse the curl handle for later calls
795 if ($resp->faultCode() == PhpXmlRpc::$xmlrpcerr['http_error'] && $keepalive) {
797 $this->xmlrpc_curl_handle = null;
805 * Send an array of requests and return an array of responses.
806 * Unless $this->no_multicall has been set to true, it will try first
807 * to use one single xmlrpc call to server method system.multicall, and
808 * revert to sending many successive calls in case of failure.
809 * This failure is also stored in $this->no_multicall for subsequent calls.
810 * Unfortunately, there is no server error code universally used to denote
811 * the fact that multicall is unsupported, so there is no way to reliably
812 * distinguish between that and a temporary failure.
813 * If you are sure that server supports multicall and do not want to
814 * fallback to using many single calls, set the fourth parameter to FALSE.
816 * NB: trying to shoehorn extra functionality into existing syntax has resulted
817 * in pretty much convoluted code...
819 * @param Request[] $msgs an array of Request objects
820 * @param integer $timeout connection timeout (in seconds)
821 * @param string $method the http protocol variant to be used
822 * @param boolean fallback When true, upon receiving an error during multicall, multiple single calls will be attempted
826 public function multicall($msgs, $timeout = 0, $method = '', $fallback = true)
829 $method = $this->method;
831 if (!$this->no_multicall) {
832 $results = $this->_try_multicall($msgs, $timeout, $method);
833 if (is_array($results)) {
834 // System.multicall succeeded
837 // either system.multicall is unsupported by server,
838 // or call failed for some other reason.
840 // Don't try it next time...
841 $this->no_multicall = true;
843 if (is_a($results, '\PhpXmlRpc\Response')) {
846 $result = new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], PhpXmlRpc::$xmlrpcstr['multicall_error']);
851 // override fallback, in case careless user tries to do two
852 // opposite things at the same time
858 // system.multicall is (probably) unsupported by server:
859 // emulate multicall via multiple requests
860 foreach ($msgs as $msg) {
861 $results[] = $this->send($msg, $timeout, $method);
864 // user does NOT want to fallback on many single calls:
865 // since we should always return an array of responses,
866 // return an array with the same error repeated n times
867 foreach ($msgs as $msg) {
868 $results[] = $result;
876 * Attempt to boxcar $msgs via system.multicall.
877 * Returns either an array of xmlrpcreponses, an xmlrpc error response
878 * or false (when received response does not respect valid multicall syntax).
880 private function _try_multicall($msgs, $timeout, $method)
882 // Construct multicall request
884 foreach ($msgs as $msg) {
885 $call['methodName'] = new Value($msg->method(), 'string');
886 $numParams = $msg->getNumParams();
888 for ($i = 0; $i < $numParams; $i++) {
889 $params[$i] = $msg->getParam($i);
891 $call['params'] = new Value($params, 'array');
892 $calls[] = new Value($call, 'struct');
894 $multicall = new Request('system.multicall');
895 $multicall->addParam(new Value($calls, 'array'));
898 $result = $this->send($multicall, $timeout, $method);
900 if ($result->faultCode() != 0) {
901 // call to system.multicall failed
906 $rets = $result->value();
908 if ($this->return_type == 'xml') {
910 } elseif ($this->return_type == 'phpvals') {
911 ///@todo test this code branch...
912 $rets = $result->value();
913 if (!is_array($rets)) {
914 return false; // bad return type from system.multicall
916 $numRets = count($rets);
917 if ($numRets != count($msgs)) {
918 return false; // wrong number of return values.
922 for ($i = 0; $i < $numRets; $i++) {
924 if (!is_array($val)) {
927 switch (count($val)) {
929 if (!isset($val[0])) {
930 return false; // Bad value
932 // Normal return value
933 $response[$i] = new Response($val[0], 0, '', 'phpvals');
936 /// @todo remove usage of @: it is apparently quite slow
937 $code = @$val['faultCode'];
938 if (!is_int($code)) {
941 $str = @$val['faultString'];
942 if (!is_string($str)) {
945 $response[$i] = new Response(0, $code, $str);
954 // return type == 'xmlrpcvals'
956 $rets = $result->value();
957 if ($rets->kindOf() != 'array') {
958 return false; // bad return type from system.multicall
960 $numRets = $rets->arraysize();
961 if ($numRets != count($msgs)) {
962 return false; // wrong number of return values.
966 for ($i = 0; $i < $numRets; $i++) {
967 $val = $rets->arraymem($i);
968 switch ($val->kindOf()) {
970 if ($val->arraysize() != 1) {
971 return false; // Bad value
973 // Normal return value
974 $response[$i] = new Response($val->arraymem(0));
977 $code = $val->structmem('faultCode');
978 if ($code->kindOf() != 'scalar' || $code->scalartyp() != 'int') {
981 $str = $val->structmem('faultString');
982 if ($str->kindOf() != 'scalar' || $str->scalartyp() != 'string') {
985 $response[$i] = new Response(0, $code->scalarval(), $str->scalarval());