Passed
Push — master ( 5b4cde...5c6492 )
by Gaetano
12:19 queued 02:18
created

ParallelClient   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 87
Duplicated Lines 0 %

Test Coverage

Coverage 85.71%

Importance

Changes 3
Bugs 2 Features 1
Metric Value
eloc 51
c 3
b 2
f 1
dl 0
loc 87
ccs 48
cts 56
cp 0.8571
rs 10
wmc 19

1 Method

Rating   Name   Duplication   Size   Complexity  
F sendParallel() 0 85 19
1
<?php
2
require_once __DIR__ . "/_prepend.php";
3
4
use PhpXmlRpc\Encoder;
5
use PhpXmlRpc\Client;
6
use PhpXmlRpc\PhpXmlRpc;
7
use PhpXmlRpc\Request;
8
use PhpXmlRpc\Response;
9
10
/// @todo add an html header with links to view-source
11
12
/**
13
 * A class taking advantage of cURL to send many requests in parallel (to a single server), for when the given server
14
 * does not support the system.multicall method
15
 */
16
class ParallelClient extends Client
17 1
{
18
    public function sendParallel($requests, $timeout = 0, $method = '')
19 1
    {
20 1
        if ($method == '') {
21
            $method = $this->method;
22
        }
23
24
        if ($timeout == 0) {
25 1
            $timeout = $this->timeout;
26 1
        }
27
28 1
        $opts = $this->getOptions();
29 1
        $opts['timeout'] = $timeout;
30
        // this is required to avoid $this->createCurlHandle reusing the same handle
31 1
        $opts['keepalive'] = false;
32 1
33 1
        /// @todo validate that $method can be handled by the current curl install
34 1
35
        $handles = array();
36 1
        $curl = curl_multi_init();
37 1
38 1
        foreach($requests as $k => $req) {
39 1
            $req->setDebug($this->debug);
40 1
            $handle = $this->createCurlHandle($req, $method, $this->server, $this->port, $this->path, $opts);
41 1
            if (($error = curl_multi_add_handle($curl, $handle)) !== 0) {
0 ignored issues
show
Bug introduced by
It seems like $curl can also be of type true; however, parameter $multi_handle of curl_multi_add_handle() does only seem to accept CurlMultiHandle|resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

41
            if (($error = curl_multi_add_handle(/** @scrutinizer ignore-type */ $curl, $handle)) !== 0) {
Loading history...
Bug introduced by
It seems like $handle can also be of type false; however, parameter $handle of curl_multi_add_handle() does only seem to accept CurlHandle|resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

41
            if (($error = curl_multi_add_handle($curl, /** @scrutinizer ignore-type */ $handle)) !== 0) {
Loading history...
42 1
                throw new \Exception("Curl multi error: $error");
43 1
            }
44 1
            $handles[$k] = $handle;
45 1
        }
46 1
47 1
        // loop code taken from php manual
48
        $running = 0;
49 1
        do {
50 1
            $status = curl_multi_exec($curl, $running);
0 ignored issues
show
Bug introduced by
It seems like $curl can also be of type true; however, parameter $multi_handle of curl_multi_exec() does only seem to accept CurlMultiHandle|resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

50
            $status = curl_multi_exec(/** @scrutinizer ignore-type */ $curl, $running);
Loading history...
51 1
            if ($status !== CURLM_OK) {
52 1
                throw new \Exception("Curl multi error");
53
            }
54 1
            while (($info = curl_multi_info_read($curl)) !== false) {
0 ignored issues
show
Bug introduced by
It seems like $curl can also be of type true; however, parameter $multi_handle of curl_multi_info_read() does only seem to accept CurlMultiHandle|resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

54
            while (($info = curl_multi_info_read(/** @scrutinizer ignore-type */ $curl)) !== false) {
Loading history...
55 1
                if ($info['msg'] === CURLMSG_DONE) {
56
                    $handle = $info['handle'];
57
                    curl_multi_remove_handle($curl, $handle);
0 ignored issues
show
Bug introduced by
It seems like $curl can also be of type true; however, parameter $multi_handle of curl_multi_remove_handle() does only seem to accept CurlMultiHandle|resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

57
                    curl_multi_remove_handle(/** @scrutinizer ignore-type */ $curl, $handle);
Loading history...
58 1
                    if ($info['result'] !== CURLE_OK) {
59
                        /// @todo should we handle this, or is it enough to call curl_error in the loop below?
60 1
                    }
61 1
                }
62
            }
63 1
            if ($running > 0) {
64 1
                if (curl_multi_select($curl) === -1) {
0 ignored issues
show
Bug introduced by
It seems like $curl can also be of type true; however, parameter $multi_handle of curl_multi_select() does only seem to accept CurlMultiHandle|resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

64
                if (curl_multi_select(/** @scrutinizer ignore-type */ $curl) === -1) {
Loading history...
65 1
                    throw new \Exception("Curl multi error");
66
                }
67 1
            }
68
        } while ($running > 0);
69
70
        curl_multi_close($curl);
0 ignored issues
show
Bug introduced by
It seems like $curl can also be of type true; however, parameter $multi_handle of curl_multi_close() does only seem to accept CurlMultiHandle|resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

70
        curl_multi_close(/** @scrutinizer ignore-type */ $curl);
Loading history...
71
72
        $responses = array();
73
        $errors = array();
74
        foreach($handles as $k => $h) {
75
            $responses[$k] = curl_multi_getcontent($h);
76
77
            if ($this->debug > 1) {
78
                $message = "---CURL INFO---\n";
79
                foreach (curl_getinfo($h) as $name => $val) {
80 1
                    if (is_array($val)) {
81
                        $val = implode("\n", $val);
82 1
                    }
83
                    $message .= $name . ': ' . $val . "\n";
84 1
                }
85 1
                $message .= '---END---';
86
                $this->getLogger()->debugMessage($message);
87
            }
88 1
89
            if (!$responses[$k]) {
90
                $errors[$k] = curl_error($h);
91
            }
92 1
        }
93
94
        foreach($responses as $k => $resp) {
95
            if (!$resp) {
96 1
                $responses[$k] = new Response(0, PhpXmlRpc::$xmlrpcerr['curl_fail'], PhpXmlRpc::$xmlrpcstr['curl_fail'] . ': ' . $errors[$k]);
97
            } else {
98 1
                $responses[$k] = $requests[$k]->parseResponse($resp, true, $this->return_type);
99 1
            }
100 1
        }
101 1
102 1
        return $responses;
103 1
    }
104 1
}
105
106
// a minimal benchmark - use 4 strategies to execute 25 similar calls: sequentially, sequentially w. http keep-alive,
107 1
// using parallel http requests, and using a single system.multiCall request
108 1
109
$num_tests = 25;
110 1
111 1
$encoder = new Encoder();
112 1
$reqs = array();
113 1
for ($i = 0; $i < $num_tests; $i++) {
114
    $data = array($i, 1.0, 'hello world', true, '20051021T23:43:00', -1, 11.0, '~!@#$%^&*()_+|', false, '20051021T23:43:00');
115 1
    $value = $encoder->encode($data, array('auto_dates'));
116 1
    $req = new Request('interopEchoTests.echoValue', array($value));
117 1
    $reqs[] = $req;
118 1
}
119
120 1
$client = new ParallelClient(XMLRPCSERVER);
121 1
122 1
// avoid storing http info in the responses, to make the checksums comparable
123 1
$client->setDebug(-1);
124 1
125
echo "Making $num_tests calls to method interopEchoTests.echoValue on server " . XMLRPCSERVER . " ...\n";
126 1
flush();
127
128
$client->setOption(Client::OPT_NO_MULTICALL,  true);
129
$t = microtime(true);
130
$resp = $client->send($reqs);
131
$t = microtime(true) - $t;
132
echo "Sequential send: " . sprintf('%.3f', $t) . " secs.\n";
133
echo "Response checksum: " . md5(var_export($resp, true)) . "\n";
134
flush();
135
136
if (strpos(XMLRPCSERVER, 'http://') === 0) {
137
    $client->setOption(Client::OPT_USE_CURL,  Client::USE_CURL_ALWAYS);
138
    $t = microtime(true);
139
    $resp = $client->send($reqs);
140
    $t = microtime(true) - $t;
141
    echo "Sequential send, curl (w. keepalive): " . sprintf('%.3f', $t) . " secs.\n";
142
    echo "Response checksum: " . md5(var_export($resp, true)) . "\n";
143
    flush();
144
}
145
146
$t = microtime(true);
147
$resp = $client->sendParallel($reqs);
148
$t = microtime(true) - $t;
149
echo "Parallel send: " . sprintf('%.3f', $t) . " secs.\n";
150
echo "Response checksum: " . md5(var_export($resp, true)) . "\n";
151
flush();
152
153
$client->setOption(Client::OPT_NO_MULTICALL, false);
154
// make sure we don't reuse the keepalive handle
155
$client->setOption(Client::OPT_USE_CURL,  Client::USE_CURL_NEVER);
156
$t = microtime(true);
157
$resp = $client->send($reqs);
158
$t = microtime(true) - $t;
159
echo "Multicall send: " . sprintf('%.3f', $t) . " secs.\n";
160
echo "Response checksum: " . md5(var_export($resp, true)) . "\n";
161
flush();
162