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

ParallelClient::sendParallel()   F

Complexity

Conditions 19
Paths 1004

Size

Total Lines 85
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 47
CRAP Score 20.1119

Importance

Changes 3
Bugs 2 Features 1
Metric Value
cc 19
eloc 50
c 3
b 2
f 1
nc 1004
nop 3
dl 0
loc 85
ccs 47
cts 55
cp 0.8545
crap 20.1119
rs 0.3499

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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