Issues (321)

extras/benchmark.php (7 issues)

1
<?php
2
/**
3
 * Benchmarking suite for the PHPXMLRPC lib.
4
 *
5
 * @author Gaetano Giunta
6
 * @copyright (C) 2005-2025 G. Giunta
7
 * @license code licensed under the BSD License: see file license.txt
8
 *
9
 * @todo add a check for response ok in call testing
10
 * @todo add support for --help option to give users the list of supported parameters
11
 * @todo make number of test iterations flexible
12
 **/
13
14
use PhpXmlRpc\PhpXmlRpc;
15
use PhpXmlRpc\Value;
16
use PhpXmlRpc\Request;
17
use PhpXmlRpc\Client;
18
use PhpXmlRpc\Response;
19
use PhpXmlRpc\Encoder;
20
21
/// @todo allow autoloading when the library is installed as dependency
22
include_once __DIR__ . '/../vendor/autoload.php';
23
24
include __DIR__ . '/../tests/parse_args.php';
25
$args = argParser::getArgs();
26
27
function begin_test($test_name, $test_case)
28
{
29
    global $test_results;
30
    if (!isset($test_results[$test_name])) {
31
        $test_results[$test_name] = array();
32
    }
33
    $test_results[$test_name][$test_case] = array();
34
    $test_results[$test_name][$test_case]['time'] = microtime(true);
35
}
36
37
function end_test($test_name, $test_case, $test_result)
38
{
39
    global $test_results;
40
    $end = microtime(true);
41
    if (!isset($test_results[$test_name][$test_case])) {
42
        trigger_error('ending test that was not started');
43
    }
44
    $test_results[$test_name][$test_case]['time'] = $end - $test_results[$test_name][$test_case]['time'];
45
    $test_results[$test_name][$test_case]['result'] = $test_result;
46
    echo '.';
47
    flush();
48
    @ob_flush();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ob_flush(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

48
    /** @scrutinizer ignore-unhandled */ @ob_flush();

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
49
}
50
51
// Set up PHP structures to be used in many tests
52
53
$data1 = array(1, 1.0, 'hello world', true, '20051021T23:43:00', -1, 11.0, '~!@#$%^&*()_+|', false, '20051021T23:43:00');
54
$data2 = array('zero' => $data1, 'one' => $data1, 'two' => $data1, 'three' => $data1, 'four' => $data1, 'five' => $data1, 'six' => $data1, 'seven' => $data1, 'eight' => $data1, 'nine' => $data1);
55
$data = array($data2, $data2, $data2, $data2, $data2, $data2, $data2, $data2, $data2, $data2);
56
$keys = array('zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine');
57
58
// Begin execution
59
60
$test_results = array();
61
$is_web = isset($_SERVER['REQUEST_METHOD']);
62
$xd = extension_loaded('xdebug') && ini_get('xdebug.profiler_enable');
63
if ($xd) {
64
    $num_tests = 1;
65
} else {
66
    $num_tests = 10;
67
}
68
69
$title = 'XML-RPC Benchmark Tests';
70
71
if ($is_web) {
72
    echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">\n<head>\n<title>$title</title>\n</head>\n<body>\n<h1>$title</h1>\n<pre>\n";
73
} else {
74
    echo "$title\n\n";
75
}
76
77
if ($is_web) {
78
    echo "<h3>Using lib version: " . PhpXmlRpc::$xmlrpcVersion . " on PHP version: " . phpversion() . "</h3>\n";
79
    if ($xd) {
80
        echo "<h4>XDEBUG profiling enabled: skipping remote tests. Trace file is: " . htmlspecialchars(xdebug_get_profiler_filename()) . "</h4>\n";
81
    }
82
    flush();
83
    ob_flush();
84
} else {
85
    echo "Using lib version: " . PhpXmlRpc::$xmlrpcVersion . " on PHP version: " . phpversion() . "\n";
86
    if ($xd) {
87
        echo "XDEBUG profiling enabled: skipping remote tests\nTrace file is: " . xdebug_get_profiler_filename() . "\n";
88
    }
89
}
90
91
// test 'manual style' data encoding vs. 'automatic style' encoding
92
begin_test('Data encoding (large array)', 'manual encoding');
93
for ($i = 0; $i < $num_tests; $i++) {
94
    $vals = array();
95
    for ($j = 0; $j < 10; $j++) {
96
        $valArray = array();
97
        foreach ($data[$j] as $key => $val) {
98
            $values = array();
99
            $values[] = new Value($val[0], 'int');
100
            $values[] = new Value($val[1], 'double');
101
            $values[] = new Value($val[2], 'string');
102
            $values[] = new Value($val[3], 'boolean');
103
            $values[] = new Value($val[4], 'dateTime.iso8601');
104
            $values[] = new Value($val[5], 'int');
105
            $values[] = new Value($val[6], 'double');
106
            $values[] = new Value($val[7], 'string');
107
            $values[] = new Value($val[8], 'boolean');
108
            $values[] = new Value($val[9], 'dateTime.iso8601');
109
            $valArray[$key] = new Value($values, 'array');
110
        }
111
        $vals[] = new Value($valArray, 'struct');
112
    }
113
    $value = new Value($vals, 'array');
114
    $out = $value->serialize();
115
}
116
end_test('Data encoding (large array)', 'manual encoding', $out);
117
118
begin_test('Data encoding (large array)', 'automatic encoding');
119
$encoder = new Encoder();
120
for ($i = 0; $i < $num_tests; $i++) {
121
    $value = $encoder->encode($data, array('auto_dates'));
122
    $out = $value->serialize();
123
}
124
end_test('Data encoding (large array)', 'automatic encoding', $out);
125
126
if (function_exists('xmlrpc_set_type')) {
127
    begin_test('Data encoding (large array)', 'xmlrpc-epi encoding');
128
    for ($i = 0; $i < $num_tests; $i++) {
129
        for ($j = 0; $j < 10; $j++) {
130
            foreach ($keys as $k) {
131
                xmlrpc_set_type($data[$j][$k][4], 'datetime');
132
                xmlrpc_set_type($data[$j][$k][8], 'datetime');
0 ignored issues
show
$data[$j][$k][8] of type false is incompatible with the type string expected by parameter $value of xmlrpc_set_type(). ( Ignorable by Annotation )

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

132
                xmlrpc_set_type(/** @scrutinizer ignore-type */ $data[$j][$k][8], 'datetime');
Loading history...
133
            }
134
        }
135
        $out = xmlrpc_encode($data);
136
    }
137
    end_test('Data encoding (large array)', 'xmlrpc-epi encoding', $out);
138
}
139
140
// test 'old style' data decoding vs. 'automatic style' decoding
141
$dummy = new Request('');
142
$out = new Response($value);
143
$in = '<?xml version="1.0" ?>' . "\n" . $out->serialize();
144
145
begin_test('Data decoding (large array)', 'manual decoding');
146
for ($i = 0; $i < $num_tests; $i++) {
147
    $response = $dummy->ParseResponse($in, true);
148
    $value = $response->value();
149
    $result = array();
150
    foreach($value as $val1) {
151
        $out = array();
152
        foreach($val1 as $name => $val) {
153
            $out[$name] = array();
154
            foreach($val as $data) {
155
                $out[$name][] = $data->scalarVal();
156
            }
157
        }
158
        $result[] = $out;
159
    }
160
}
161
end_test('Data decoding (large array)', 'manual decoding', $result);
162
163
begin_test('Data decoding (large array)', 'manual decoding deprecated');
164
for ($i = 0; $i < $num_tests; $i++) {
165
    $response = $dummy->ParseResponse($in, true);
166
    $value = $response->value();
167
    $result = array();
168
    $l = $value->arraySize();
0 ignored issues
show
Deprecated Code introduced by
The function PhpXmlRpc\Value::arraySize() has been deprecated: use count() instead ( Ignorable by Annotation )

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

168
    $l = /** @scrutinizer ignore-deprecated */ $value->arraySize();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
169
    for ($k = 0; $k < $l; $k++) {
170
        $val1 = $value->arrayMem($k);
0 ignored issues
show
Deprecated Code introduced by
The function PhpXmlRpc\Value::arrayMem() has been deprecated: use array access, e.g. $val[$key] ( Ignorable by Annotation )

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

170
        $val1 = /** @scrutinizer ignore-deprecated */ $value->arrayMem($k);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
171
        $out = array();
172
        foreach($val1 as $name => $val) {
173
            $out[$name] = array();
174
            $m = $val->arraySize();
175
            for ($j = 0; $j < $m; $j++) {
176
                $data = $val->arrayMem($j);
177
                $out[$name][] = $data->scalarVal();
178
            }
179
        } // while
180
        $result[] = $out;
181
    }
182
}
183
end_test('Data decoding (large array)', 'manual decoding deprecated', $result);
184
185
begin_test('Data decoding (large array)', 'automatic decoding');
186
for ($i = 0; $i < $num_tests; $i++) {
187
    $response = $dummy->parseResponse($in, true, 'phpvals');
188
    $value = $response->value();
189
}
190
end_test('Data decoding (large array)', 'automatic decoding', $value);
191
192
if (function_exists('xmlrpc_decode')) {
193
    begin_test('Data decoding (large array)', 'xmlrpc-epi decoding');
194
    for ($i = 0; $i < $num_tests; $i++) {
195
        $response = $dummy->parseResponse($in, true, 'xml');
196
        $value = xmlrpc_decode($response->value());
0 ignored issues
show
It seems like $response->value() can also be of type PhpXmlRpc\Value; however, parameter $xml of xmlrpc_decode() does only seem to accept string, 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

196
        $value = xmlrpc_decode(/** @scrutinizer ignore-type */ $response->value());
Loading history...
197
    }
198
    end_test('Data decoding (large array)', 'xmlrpc-epi decoding', $value);
199
}
200
201
if (!$xd) {
202
203
    $num_tests = 25;
204
205
    /// test multicall vs. many calls vs. keep-alives - HTTP
206
207
    $encoder = new Encoder();
208
    $value = $encoder->encode($data1, array('auto_dates'));
209
    $req = new Request('interopEchoTests.echoValue', array($value));
210
    $reqs = array();
211
    for ($i = 0; $i < $num_tests; $i++) {
212
        $reqs[] = $req;
213
    }
214
215
    $server = explode(':', $args['HTTPSERVER']);
216
    if (count($server) > 1) {
217
        $srv = 'http://' . $server[0] . '://' . $server[1] . $args['HTTPURI'];
218
        $c = new Client($args['HTTPURI'], $server[0], $server[1]);
0 ignored issues
show
$server[1] of type string is incompatible with the type integer expected by parameter $port of PhpXmlRpc\Client::__construct(). ( Ignorable by Annotation )

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

218
        $c = new Client($args['HTTPURI'], $server[0], /** @scrutinizer ignore-type */ $server[1]);
Loading history...
219
    } else {
220
        $srv = 'http://' . $args['HTTPSERVER'] . $args['HTTPURI'];
221
        $c = new Client($args['HTTPURI'], $args['HTTPSERVER']);
222
    }
223
224
    // do not interfere with http compression
225
    $c->setAcceptedCompression(false);
0 ignored issues
show
false of type false is incompatible with the type string expected by parameter $compMethod of PhpXmlRpc\Client::setAcceptedCompression(). ( Ignorable by Annotation )

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

225
    $c->setAcceptedCompression(/** @scrutinizer ignore-type */ false);
Loading history...
226
    //$c->setDebug(1);
227
228
    $testName = "Repeated send (small array) to $srv";
229
230
    begin_test($testName, 'http 10');
231
    $response = array();
232
    for ($i = 0; $i < $num_tests; $i++) {
233
        $resp = $c->send($req);
234
        $response[] = $resp->value();
235
    }
236
    end_test($testName, 'http 10', $response);
237
238
    if (function_exists('curl_init')) {
239
        $c->setOption(Client::OPT_KEEPALIVE, false);
240
        begin_test($testName, 'http 11 no keepalive');
241
        $response = array();
242
        for ($i = 0; $i < $num_tests; $i++) {
243
            $resp = $c->send($req, 10, 'http11');
244
            $response[] = $resp->value();
245
        }
246
        end_test($testName, 'http 11 no keepalive', $response);
247
248
        begin_test($testName, 'http 11 w. keep-alive');
249
        $response = array();
250
        for ($i = 0; $i < $num_tests; $i++) {
251
            $resp = $c->send($req, 10, 'http11');
252
            $response[] = $resp->value();
253
        }
254
        end_test($testName, 'http 11 w. keep-alive', $response);
255
        $c->xmlrpc_curl_handle = null;
256
    }
257
258
    // this is a single http call - keepalive on/off does not bother us
259
    begin_test($testName, 'multicall');
260
    $response = $c->send($reqs);
261
    foreach ($response as $key => & $val) {
262
        $val = $val->value();
263
    }
264
    end_test($testName, 'multicall', $response);
265
266
    if (function_exists('gzinflate')) {
267
        $c->setOption(Client::OPT_ACCEPTED_COMPRESSION, array('gzip'));
268
        $c->setOption(Client::OPT_REQUEST_COMPRESSION, 'gzip');
269
270
        begin_test($testName, 'http 10 w. compression');
271
        $response = array();
272
        for ($i = 0; $i < $num_tests; $i++) {
273
            $resp = $c->send($req);
274
            $response[] = $resp->value();
275
        }
276
        end_test($testName, 'http 10 w. compression', $response);
277
278
        if (function_exists('curl_init')) {
279
            $c->setOption(Client::OPT_KEEPALIVE, false);
280
            begin_test($testName, 'http 11 w. compression and no keepalive');
281
            $response = array();
282
            for ($i = 0; $i < $num_tests; $i++) {
283
                $resp = $c->send($req, 10, 'http11');
284
                $response[] = $resp->value();
285
            }
286
            end_test($testName, 'http 11 w. compression and no keepalive', $response);
287
288
            $c->setOption(Client::OPT_KEEPALIVE, true);
289
            begin_test($testName, 'http 11 w. keep-alive and compression');
290
            $response = array();
291
            for ($i = 0; $i < $num_tests; $i++) {
292
                $resp = $c->send($req, 10, 'http11');
293
                $response[] = $resp->value();
294
            }
295
            end_test($testName, 'http 11 w. keep-alive and compression', $response);
296
            $c->xmlrpc_curl_handle = null;
297
        }
298
299
        begin_test($testName, 'multicall w. compression');
300
        $response = $c->send($reqs);
301
        foreach ($response as $key => & $val) {
302
            $val = $val->value();
303
        }
304
        end_test($testName, 'multicall w. compression', $response);
305
    }
306
307
    if (function_exists('curl_init')) {
308
309
        /// test multicall vs. many calls vs. keep-alives - HTTPS
310
311
        $server = explode(':', $args['HTTPSSERVER']);
312
        if (count($server) > 1) {
313
            $srv = 'https://' . $server[0] . ':' . $server[1] . $args['HTTPSURI'];
314
            $c = new Client($args['HTTPSURI'], $server[0], $server[1], 'https');
315
        } else {
316
            $srv = 'https://' . $args['HTTPSSERVER'] . $args['HTTPSURI'];
317
            $c = new Client($args['HTTPSURI'], $args['HTTPSSERVER'], 443, 'https');
318
        }
319
        $c->setOption(Client::OPT_VERIFY_PEER, !$args['HTTPSIGNOREPEER']);
320
        $c->setOption(Client::OPT_VERIFY_HOST, $args['HTTPSVERIFYHOST']);
321
        // do not interfere with http compression
322
        $c->setOption(Client::OPT_ACCEPTED_COMPRESSION, false);
323
        //$c->debug = 1;
324
325
        $testName = "Repeated send (small array) to $srv";
326
327
        $c->setOption(Client::OPT_KEEPALIVE, false);
328
        begin_test($testName, 'https no keep-alive');
329
        $response = array();
330
        for ($i = 0; $i < $num_tests; $i++) {
331
            $resp = $c->send($req);
332
            $response[] = $resp->value();
333
        }
334
        end_test($testName, 'https no keep-alive', $response);
335
336
        $c->setOption(Client::OPT_KEEPALIVE, true);
337
        begin_test($testName, 'https w. keep-alive');
338
        $response = array();
339
        for ($i = 0; $i < $num_tests; $i++) {
340
            $resp = $c->send($req, 10);
341
            $response[] = $resp->value();
342
        }
343
        end_test($testName, 'https w. keep-alive', $response);
344
        $c->xmlrpc_curl_handle = null;
345
346
        begin_test($testName, 'https multicall');
347
        $response = $c->send($reqs);
348
        foreach ($response as $key => & $val) {
349
            $val = $val->value();
350
        }
351
        end_test($testName, 'https multicall', $response);
352
353
        if (function_exists('gzinflate')) {
354
            $c->setOption(Client::OPT_ACCEPTED_COMPRESSION, array('gzip'));
355
            $c->setOption(Client::OPT_REQUEST_COMPRESSION, 'gzip');
356
357
            $c->setOption(Client::OPT_KEEPALIVE, false);
358
            begin_test($testName, 'https w. compression and no keepalive');
359
            $response = array();
360
            for ($i = 0; $i < $num_tests; $i++) {
361
                $resp = $c->send($req);
362
                $response[] = $resp->value();
363
            }
364
            end_test($testName, 'https w. compression and no keepalive', $response);
365
366
            $c->setOption(Client::OPT_KEEPALIVE, true);
367
            begin_test($testName, 'https w. keep-alive and compression');
368
            $response = array();
369
            for ($i = 0; $i < $num_tests; $i++) {
370
                $resp = $c->send($req, 10);
371
                $response[] = $resp->value();
372
            }
373
            end_test($testName, 'https w. keep-alive and compression', $response);
374
            $c->xmlrpc_curl_handle = null;
375
376
            begin_test($testName, 'multicall w. https and compression');
377
            $response = $c->send($reqs);
378
            foreach ($response as $key => & $val) {
379
                $val = $val->value();
380
            }
381
            end_test($testName, 'multicall w. https and compression', $response);
382
        }
383
    }
384
385
    if (function_exists('curl_init') && defined('CURL_HTTP_VERSION_2_0')) {
386
387
        /// test multicall vs. many calls vs. keep-alives - HTTP/2
388
389
        $server = explode(':', $args['HTTPSSERVER']);
390
        if (count($server) > 1) {
391
            $srv = 'https://' . $server[0] . ':' . $server[1] . $args['HTTPSURI'];
392
            $c = new Client($args['HTTPSURI'], $server[0], $server[1], 'https');
393
        } else {
394
            $srv = 'https://' . $args['HTTPSSERVER'] . $args['HTTPSURI'];
395
            $c = new Client($args['HTTPSURI'], $args['HTTPSSERVER'], 443, 'h2');
396
        }
397
        $c->setOption(Client::OPT_VERIFY_PEER, !$args['HTTPSIGNOREPEER']);
398
        $c->setOption(Client::OPT_VERIFY_HOST, $args['HTTPSVERIFYHOST']);
399
        // do not interfere with http compression
400
        $c->setOption(Client::OPT_ACCEPTED_COMPRESSION, false);
401
        //$c->setDebug(1);
402
403
        $testName = "Repeated send (small array) to $srv - HTTP/2";
404
405
        $c->setOption(Client::OPT_KEEPALIVE, false);
406
        begin_test($testName, 'http2 no keep-alive');
407
        $response = array();
408
        for ($i = 0; $i < $num_tests; $i++) {
409
            $resp = $c->send($req);
410
            $response[] = $resp->value();
411
        }
412
        end_test($testName, 'http2 no keep-alive', $response);
413
414
        $c->setOption(Client::OPT_KEEPALIVE, true);
415
        begin_test($testName, 'http2 w. keep-alive');
416
        $response = array();
417
        for ($i = 0; $i < $num_tests; $i++) {
418
            $resp = $c->send($req, 10);
419
            $response[] = $resp->value();
420
        }
421
        end_test($testName, 'http2 w. keep-alive', $response);
422
        $c->xmlrpc_curl_handle = null;
423
424
        begin_test($testName, 'http2 multicall');
425
        $response = $c->send($reqs);
426
        foreach ($response as $key => & $val) {
427
            $val = $val->value();
428
        }
429
        end_test($testName, 'http2 multicall', $response);
430
431
        if (function_exists('gzinflate')) {
432
            $c->setOption(Client::OPT_ACCEPTED_COMPRESSION, array('gzip'));
433
            $c->setOption(Client::OPT_REQUEST_COMPRESSION, 'gzip');
434
435
            $c->setOption(Client::OPT_KEEPALIVE, false);
436
            begin_test($testName, 'http2 w. compression and no keepalive');
437
            $response = array();
438
            for ($i = 0; $i < $num_tests; $i++) {
439
                $resp = $c->send($req);
440
                $response[] = $resp->value();
441
            }
442
            end_test($testName, 'http2 w. compression and no keepalive', $response);
443
444
            $c->setOption(Client::OPT_KEEPALIVE, true);
445
            begin_test($testName, 'http2 w. keep-alive and compression');
446
            $response = array();
447
            for ($i = 0; $i < $num_tests; $i++) {
448
                $resp = $c->send($req, 10);
449
                $response[] = $resp->value();
450
            }
451
            end_test($testName, 'http2 w. keep-alive and compression', $response);
452
            $c->xmlrpc_curl_handle = null;
453
454
            begin_test($testName, 'multicall w. http2 and compression');
455
            $response = $c->send($reqs);
456
            foreach ($response as $key => & $val) {
457
                $val = $val->value();
458
            }
459
            end_test($testName, 'multicall w. http2 and compression', $response);
460
        }
461
    }
462
} // end of 'if no xdebug profiling'
463
464
465
echo "\n";
466
foreach ($test_results as $test => $results) {
467
    echo "\nTEST: $test\n";
468
    foreach ($results as $case => $data) {
469
        echo "  $case: {$data['time']} secs - Output data CRC: " . crc32(serialize($data['result'])) . "\n";
470
    }
471
}
472
473
if ($is_web) {
474
    echo "\n</pre>\n</body>\n</html>\n";
475
}
476