Issues (321)

tests/08ServerTest.php (1 issue)

1
<?php
2
3
include_once __DIR__ . '/../lib/xmlrpc_wrappers.inc';
4
5
include_once __DIR__ . '/ServerAwareTestCase.php';
6
7
/**
8
 * Tests which involve interaction with the server - carried out via the client.
9
 * They are run against the server found in demo/server.php.
10
 * Includes testing of (some of) the Wrapper class
11
 */
12
class ServerTest extends PhpXmlRpc_ServerAwareTestCase
13
{
14
    /** @var xmlrpc_client $client */
15
    protected $client = null;
16
    protected $method = 'http';
17
    protected $timeout = 10;
18
    protected $request_compression = null;
19
    protected $accepted_compression = '';
20
21
    protected static $failed_tests = array();
22
    protected static $originalInternalEncoding;
23
24
    /**
25
     * @todo instead of overriding fail via _fail, implement Yoast\PHPUnitPolyfills\TestListeners\TestListenerDefaultImplementation
26
     */
27
    public static function _fail($message = '')
28
    {
29
        // save in a static var that this particular test has failed
30
        // (but only if not called from subclass objects / multitests)
31
        if (function_exists('debug_backtrace') && strtolower(get_called_class()) == 'servertest') {
32
            $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
33
            for ($i = 0; $i < count($trace); $i++) {
34
                if (strpos($trace[$i]['function'], 'test') === 0) {
35
                    self::$failed_tests[$trace[$i]['function']] = true;
36
                    break;
37
                }
38
            }
39
        }
40
41
        parent::_fail($message);
42
    }
43
44
    public static function set_up_before_class()
45
    {
46
        parent::set_up_before_class();
47
48
        self::$originalInternalEncoding = \PhpXmlRpc\PhpXmlRpc::$xmlrpc_internalencoding;
49
    }
50
51
    public function set_up()
52
    {
53
        parent::set_up();
54
55
        $this->timeout = 10;
56
57
        $this->client = $this->getClient();
58
        $this->client->request_compression = $this->request_compression;
59
        $this->client->accepted_compression = $this->accepted_compression;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->accepted_compression of type string is incompatible with the declared type array of property $accepted_compression.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
60
    }
61
62
    public function tear_down()
63
    {
64
        \PhpXmlRpc\PhpXmlRpc::$xmlrpc_internalencoding = self::$originalInternalEncoding;
65
66
        parent::tear_down();
67
    }
68
69
    /**
70
     * @param PhpXmlRpc\Request|array $msg
71
     * @param int|int[] $errorCode expected error codes
72
     * @param bool $returnResponse
73
     * @return mixed|\PhpXmlRpc\Response|\PhpXmlRpc\Response[]|\PhpXmlRpc\Value|string|null
74
     * @todo allo callers to disable checking of faultCode
75
     */
76
    protected function send($msg, $errorCode = 0, $returnResponse = false)
77
    {
78
        $r = $this->client->send($msg, $this->timeout, $this->method);
79
        // for multicall, return directly array of responses
80
        if (is_array($r)) {
81
            return $r;
82
        }
83
        $this->validateResponse($r);
84
        if (is_array($errorCode)) {
85
            $this->assertContains($r->faultCode(), $errorCode, 'Error ' . $r->faultCode() . ' connecting to server: ' . $r->faultString());
86
        } else {
87
            $this->assertEquals($errorCode, $r->faultCode(), 'Error ' . $r->faultCode() . ' connecting to server: ' . $r->faultString());
88
        }
89
        if (!$r->faultCode()) {
90
            if ($returnResponse) {
91
                return $r;
92
            } else {
93
                return $r->value();
94
            }
95
        } else {
96
            return null;
97
        }
98
    }
99
100
    protected function validateResponse($r)
101
    {
102
        // to be implemented in subclasses
103
    }
104
105
    /**
106
     * Adds (and replaces) query params to the url currently used by the client
107
     * @param array $data
108
     */
109
    protected function addQueryParams($data)
110
    {
111
        $query = parse_url($this->client->path, PHP_URL_QUERY);
112
        parse_str($query, $vars);
113
        $query = http_build_query(array_merge($vars, $data));
114
        $this->client->path = parse_url($this->client->path, PHP_URL_PATH) . '?' . $query;
115
    }
116
117
    public function testString()
118
    {
119
        $sendString = "here are 3 \"entities\": < > & " .
120
            "and here's a dollar sign: \$pretendvarname and a backslash too: " . chr(92) .
121
            " - isn't that great? \\\"hackery\\\" at it's best " .
122
            " also don't want to miss out on \$item[0]. " .
123
            "The real weird stuff follows: CRLF here" . chr(13) . chr(10) .
124
            "a simple CR here" . chr(13) .
125
            "a simple LF here" . chr(10) .
126
            "and then LFCR" . chr(10) . chr(13) .
127
            "last but not least weird names: G" . chr(252) . "nter, El" . chr(232) . "ne, and an xml comment closing tag: -->";
128
        $m = new xmlrpcmsg('examples.stringecho', array(
129
            new xmlrpcval($sendString, 'string'),
130
        ));
131
        $v = $this->send($m);
132
        if ($v) {
133
            // when sending/receiving non-US-ASCII encoded strings, XML says cr-lf can be normalized.
134
            // so we relax our tests...
135
            $l1 = strlen($sendString);
136
            $l2 = strlen($v->scalarval());
137
            if ($l1 == $l2) {
138
                $this->assertEquals($sendString, $v->scalarval());
139
            } else {
140
                $this->assertEquals(str_replace(array("\r\n", "\r"), array("\n", "\n"), $sendString), $v->scalarval());
141
            }
142
        }
143
    }
144
145
    public function testLatin1String()
146
    {
147
        $sendString =
148
            "last but not least weird names: G" . chr(252) . "nter, El" . chr(232) . "ne";
149
        $x = '<?xml version="1.0" encoding="ISO-8859-1"?><methodCall><methodName>examples.stringecho</methodName><params><param><value>'.
150
            $sendString.
151
            '</value></param></params></methodCall>';
152
        $v = $this->send($x);
153
        if ($v) {
154
            $this->assertEquals($sendString, $v->scalarval());
155
        }
156
    }
157
158
    public function testExoticCharsetsRequests()
159
    {
160
        // note that we should disable this call also when mbstring is missing server-side
161
        if (!function_exists('mb_convert_encoding')) {
162
            $this->markTestSkipped('Miss mbstring extension to test exotic charsets');
163
            return;
164
        }
165
        $sendString = 'κόσμε'; // Greek word 'kosme'
166
        $str = '<?xml version="1.0" encoding="_ENC_"?>
167
<methodCall>
168
    <methodName>examples.stringecho</methodName>
169
    <params>
170
        <param>
171
        <value><string>'.$sendString.'</string></value>
172
        </param>
173
    </params>
174
</methodCall>';
175
176
        PhpXmlRpc\PhpXmlRpc::$xmlrpc_internalencoding = 'UTF-8';
177
        // This test is known to fail with old mbstring versions, at least the ones we get with php 5.4, 5.5 as present
178
        // in the CI test vms
179
        if (version_compare(PHP_VERSION, '5.6.0', '>=')) {
180
            // we have to set the encoding declaration either in the http header or xml prolog, as mb_detect_encoding
181
            // (used on the server side) will fail recognizing these 2 charsets
182
            $v = $this->send(mb_convert_encoding(str_replace('_ENC_', 'UCS-4', $str), 'UCS-4', 'UTF-8'));
183
            $this->assertEquals($sendString, $v->scalarval());
184
        }
185
        $v = $this->send(mb_convert_encoding(str_replace('_ENC_', 'UTF-16', $str), 'UTF-16', 'UTF-8'));
186
        $this->assertEquals($sendString, $v->scalarval());
187
        //PhpXmlRpc\PhpXmlRpc::$xmlrpc_internalencoding = 'ISO-8859-1';
188
    }
189
190
    public function testExoticCharsetsRequests2()
191
    {
192
        // note that we should disable this call also when mbstring is missing server-side
193
        if (!function_exists('mb_convert_encoding')) {
194
            $this->markTestSkipped('Miss mbstring extension to test exotic charsets');
195
            return;
196
        }
197
        $sendString = '安室奈美恵'; // Japanese name "Namie Amuro"
198
        $str = '<?xml version="1.0"?>
199
<methodCall>
200
    <methodName>examples.stringecho</methodName>
201
    <params>
202
        <param>
203
        <value><string>'.$sendString.'</string></value>
204
        </param>
205
    </params>
206
</methodCall>';
207
208
        PhpXmlRpc\PhpXmlRpc::$xmlrpc_internalencoding = 'UTF-8';
209
        // no encoding declaration either in the http header or xml prolog, let mb_detect_encoding
210
        // (used on the server side) sort it out
211
        $this->addQueryParams(array('DETECT_ENCODINGS' => array('EUC-JP', 'UTF-8')));
212
        $v = $this->send(mb_convert_encoding($str, 'EUC-JP', 'UTF-8'));
213
        //PhpXmlRpc\PhpXmlRpc::$xmlrpc_internalencoding = 'ISO-8859-1';
214
        $this->assertEquals($sendString, $v->scalarval());
215
    }
216
217
    public function testExoticCharsetsRequests3()
218
    {
219
        // note that we should disable this call also when mbstring is missing server-side
220
        if (!function_exists('mb_convert_encoding')) {
221
            $this->markTestSkipped('Miss mbstring extension to test exotic charsets');
222
            return;
223
        }
224
        // the warning suppression is due to utf8_decode being deprecated in php 8.2
225
        $sendString = @utf8_decode('élève');
226
        $str = '<?xml version="1.0"?>
227
<methodCall>
228
    <methodName>examples.stringecho</methodName>
229
    <params>
230
        <param>
231
        <value><string>'.$sendString.'</string></value>
232
        </param>
233
    </params>
234
</methodCall>';
235
236
        // no encoding declaration either in the http header or xml prolog, let mb_detect_encoding
237
        // (used on the server side) sort it out
238
        $this->addQueryParams(array('DETECT_ENCODINGS' => array('ISO-8859-1', 'UTF-8')));
239
        $v = $this->send($str);
240
        $this->assertEquals($sendString, $v->scalarval());
241
    }
242
243
    /*public function testLatin1Method()
244
    {
245
        $f = new xmlrpcmsg("tests.iso88591methodname." . chr(224) . chr(252) . chr(232), array(
246
            new xmlrpcval('hello')
247
        ));
248
        $v = $this->send($f);
249
        if ($v) {
250
            $this->assertEquals('hello', $v->scalarval());
251
        }
252
    }*/
253
254
    public function testUtf8Method()
255
    {
256
        PhpXmlRpc\PhpXmlRpc::$xmlrpc_internalencoding = 'UTF-8';
257
        $m = new xmlrpcmsg("tests.utf8methodname." . 'κόσμε', array(
258
            new xmlrpcval('hello')
259
        ));
260
        $v = $this->send($m);
261
        //PhpXmlRpc\PhpXmlRpc::$xmlrpc_internalencoding = 'ISO-8859-1';
262
        if ($v) {
263
            $this->assertEquals('hello', $v->scalarval());
264
        }
265
    }
266
267
    public function testAddingDoubles()
268
    {
269
        // note that rounding errors mean we keep precision to sensible levels here ;-)
270
        $a = 12.13;
271
        $b = -23.98;
272
        $m = new xmlrpcmsg('examples.addtwodouble', array(
273
            new xmlrpcval($a, 'double'),
274
            new xmlrpcval($b, 'double'),
275
        ));
276
        $v = $this->send($m);
277
        if ($v) {
278
            $this->assertEquals($a + $b, $v->scalarval());
279
        }
280
    }
281
282
    public function testAdding()
283
    {
284
        $m = new xmlrpcmsg('examples.addtwo', array(
285
            new xmlrpcval(12, 'int'),
286
            new xmlrpcval(-23, 'int'),
287
        ));
288
        $v = $this->send($m);
289
        if ($v) {
290
            $this->assertEquals(12 - 23, $v->scalarval());
291
        }
292
    }
293
294
    public function testInvalidNumber()
295
    {
296
        $m = new xmlrpcmsg('examples.addtwo', array(
297
            new xmlrpcval('fred', 'int'),
298
            new xmlrpcval("\"; exec('ls')", 'int'),
299
        ));
300
        $v = $this->send($m);
301
        /// @todo a specific fault should be generated here by the server, which we can check
302
        if ($v) {
303
            $this->assertEquals(0, $v->scalarval());
304
        }
305
    }
306
307
    public function testUnknownMethod()
308
    {
309
        $m = new xmlrpcmsg('examples.a_very_unlikely.method', array());
310
        $this->send($m, \PhpXmlRpc\PhpXmlRpc::$xmlrpcerr['unknown_method']);
311
    }
312
313
    public function testBoolean()
314
    {
315
        $m = new xmlrpcmsg('examples.invertBooleans', array(
316
            new xmlrpcval(array(
317
                new xmlrpcval(true, 'boolean'),
318
                new xmlrpcval(false, 'boolean'),
319
                new xmlrpcval(1, 'boolean'),
320
                new xmlrpcval(0, 'boolean')
321
            ),
322
                'array'
323
            ),));
324
        $answer = '0101';
325
        $v = $this->send($m);
326
        if ($v) {
327
            $sz = $v->arraysize();
328
            $got = '';
329
            for ($i = 0; $i < $sz; $i++) {
330
                $b = $v->arraymem($i);
331
                if ($b->scalarval()) {
332
                    $got .= '1';
333
                } else {
334
                    $got .= '0';
335
                }
336
            }
337
            $this->assertEquals($answer, $got);
338
        }
339
    }
340
341
    public function testBase64()
342
    {
343
        $sendString = 'Mary had a little lamb,
344
Whose fleece was white as snow,
345
And everywhere that Mary went
346
the lamb was sure to go.
347
348
Mary had a little lamb
349
She tied it to a pylon
350
Ten thousand volts went down its back
351
And turned it into nylon';
352
        $m = new xmlrpcmsg('examples.decode64', array(
353
            new xmlrpcval($sendString, 'base64'),
354
        ));
355
        $v = $this->send($m);
356
        if ($v) {
357
            if (strlen($sendString) == strlen($v->scalarval())) {
358
                $this->assertEquals($sendString, $v->scalarval());
359
            } else {
360
                $this->assertEquals(str_replace(array("\r\n", "\r"), array("\n", "\n"), $sendString), $v->scalarval());
361
            }
362
        }
363
    }
364
365
    public function testCountEntities()
366
    {
367
        $sendString = "h'fd>onc>>l>>rw&bpu>q>e<v&gxs<ytjzkami<";
368
        $m = new xmlrpcmsg('validator1.countTheEntities', array(
369
            new xmlrpcval($sendString, 'string'),
370
        ));
371
        $v = $this->send($m);
372
        if ($v) {
373
            $got = '';
374
            $expected = '37210';
375
            $expect_array = array('ctLeftAngleBrackets', 'ctRightAngleBrackets', 'ctAmpersands', 'ctApostrophes', 'ctQuotes');
376
            foreach($expect_array as $val) {
377
                $b = $v->structmem($val);
378
                $got .= $b->scalarVal();
379
            }
380
            $this->assertEquals($expected, $got);
381
        }
382
    }
383
384
    protected function _multicall_msg($method, $params)
385
    {
386
        $struct = array();
387
        $struct['methodName'] = new xmlrpcval($method, 'string');
388
        $struct['params'] = new xmlrpcval($params, 'array');
389
390
        return new xmlrpcval($struct, 'struct');
391
    }
392
393
    public function testServerMulticall()
394
    {
395
        // We manually construct a system.multicall() call to ensure
396
        // that the server supports it.
397
398
        // NB: This test will NOT pass if server does not support system.multicall.
399
400
        // Based on http://xmlrpc-c.sourceforge.net/hacks/test_multicall.py
401
        $good1 = $this->_multicall_msg(
402
            'system.methodHelp',
403
            array(php_xmlrpc_encode('system.listMethods')));
404
        $bad = $this->_multicall_msg(
405
            'test.nosuch',
406
            array(php_xmlrpc_encode(1), php_xmlrpc_encode(2)));
407
        $recursive = $this->_multicall_msg(
408
            'system.multicall',
409
            array(new xmlrpcval(array(), 'array')));
410
        $good2 = $this->_multicall_msg(
411
            'system.methodSignature',
412
            array(php_xmlrpc_encode('system.listMethods')));
413
        $arg = new xmlrpcval(
414
            array($good1, $bad, $recursive, $good2),
415
            'array'
416
        );
417
418
        $m = new xmlrpcmsg('system.multicall', array($arg));
419
        $v = $this->send($m);
420
        if ($v) {
421
            //$this->assertEquals(0, $r->faultCode(), "fault from system.multicall");
422
            $this->assertEquals(4, $v->arraysize(), "bad number of return values");
423
424
            $r1 = $v->arraymem(0);
425
            $this->assertTrue(
426
                $r1->kindOf() == 'array' && $r1->arraysize() == 1,
427
                "did not get array of size 1 from good1"
428
            );
429
430
            $r2 = $v->arraymem(1);
431
            $this->assertEquals('struct', $r2->kindOf(), "no fault from bad");
432
433
            $r3 = $v->arraymem(2);
434
            $this->assertEquals('struct', $r3->kindOf(), "recursive system.multicall did not fail");
435
436
            $r4 = $v->arraymem(3);
437
            $this->assertTrue(
438
                $r4->kindOf() == 'array' && $r4->arraysize() == 1,
439
                "did not get array of size 1 from good2"
440
            );
441
        }
442
    }
443
444
    public function testClientMulticall1()
445
    {
446
        // NB: This test will NOT pass if server does not support system.multicall.
447
448
        $noMultiCall = $this->client->no_multicall;
449
        $this->client->no_multicall = false;
450
451
        $good1 = new xmlrpcmsg('system.methodHelp',
452
            array(php_xmlrpc_encode('system.listMethods')));
453
        $bad = new xmlrpcmsg('test.nosuch',
454
            array(php_xmlrpc_encode(1), php_xmlrpc_encode(2)));
455
        $recursive = new xmlrpcmsg('system.multicall',
456
            array(new xmlrpcval(array(), 'array')));
457
        $good2 = new xmlrpcmsg('system.methodSignature',
458
            array(php_xmlrpc_encode('system.listMethods'))
459
        );
460
461
        $r = $this->send(array($good1, $bad, $recursive, $good2));
462
        if ($r) {
463
            $this->assertEquals(4, count($r), "wrong number of return values");
464
        }
465
466
        $this->assertEquals(0, $r[0]->faultCode(), "fault from good1");
467
        if (!$r[0]->faultCode()) {
468
            $val = $r[0]->value();
469
            $this->assertTrue(
470
                $val->kindOf() == 'scalar' && $val->scalartyp() == 'string',
471
                "good1 did not return string"
472
            );
473
        }
474
        $this->assertNotEquals(0, $r[1]->faultCode(), "no fault from bad");
475
        $this->assertNotEquals(0, $r[2]->faultCode(), "no fault from recursive system.multicall");
476
        $this->assertEquals(0, $r[3]->faultCode(), "fault from good2");
477
        if (!$r[3]->faultCode()) {
478
            $val = $r[3]->value();
479
            $this->assertEquals('array', $val->kindOf(), "good2 did not return array");
480
        }
481
        // This is the only assert in this test which should fail
482
        // if the test server does not support system.multicall.
483
        $this->assertEquals(false, $this->client->no_multicall, "server does not support system.multicall");
484
485
        $this->client->no_multicall = $noMultiCall;
486
    }
487
488
    public function testClientMulticall2()
489
    {
490
        // NB: This test will NOT pass if server does not support system.multicall.
491
492
        $noMultiCall = $this->client->no_multicall;
493
        $this->client->no_multicall = true;
494
495
        $good1 = new xmlrpcmsg('system.methodHelp',
496
            array(php_xmlrpc_encode('system.listMethods')));
497
        $bad = new xmlrpcmsg('test.nosuch',
498
            array(php_xmlrpc_encode(1), php_xmlrpc_encode(2)));
499
        $recursive = new xmlrpcmsg('system.multicall',
500
            array(new xmlrpcval(array(), 'array')));
501
        $good2 = new xmlrpcmsg('system.methodSignature',
502
            array(php_xmlrpc_encode('system.listMethods'))
503
        );
504
505
        $r = $this->send(array($good1, $bad, $recursive, $good2));
506
        if ($r) {
507
            $this->assertEquals(4, count($r), "wrong number of return values");
508
        }
509
510
        $this->assertEquals(0, $r[0]->faultCode(), "fault from good1");
511
        if (!$r[0]->faultCode()) {
512
            $val = $r[0]->value();
513
            $this->assertTrue(
514
                $val->kindOf() == 'scalar' && $val->scalartyp() == 'string',
515
                "good1 did not return string");
516
        }
517
        $this->assertNotEquals(0, $r[1]->faultCode(), "no fault from bad");
518
        $this->assertEquals(0, $r[2]->faultCode(), "fault from (non recursive) system.multicall");
519
        $this->assertEquals(0, $r[3]->faultCode(), "fault from good2");
520
        if (!$r[3]->faultCode()) {
521
            $val = $r[3]->value();
522
            $this->assertEquals('array', $val->kindOf(), "good2 did not return array");
523
        }
524
525
        $this->client->no_multicall = $noMultiCall;
526
    }
527
528
    public function testClientMulticall3()
529
    {
530
        // NB: This test will NOT pass if server does not support system.multicall.
531
532
        $noMultiCall = $this->client->no_multicall;
533
        $returnType = $this->client->return_type;
534
535
        $this->client->return_type = 'phpvals';
536
        $this->client->no_multicall = false;
537
538
        $good1 = new xmlrpcmsg('system.methodHelp',
539
            array(php_xmlrpc_encode('system.listMethods')));
540
        $bad = new xmlrpcmsg('test.nosuch',
541
            array(php_xmlrpc_encode(1), php_xmlrpc_encode(2)));
542
        $recursive = new xmlrpcmsg('system.multicall',
543
            array(new xmlrpcval(array(), 'array')));
544
        $good2 = new xmlrpcmsg('system.methodSignature',
545
            array(php_xmlrpc_encode('system.listMethods'))
546
        );
547
548
        $r = $this->send(array($good1, $bad, $recursive, $good2));
549
        if ($r) {
550
            $this->assertEquals(4, count($r), "wrong number of return values");
551
        }
552
        $this->assertEquals(0, $r[0]->faultCode(), "fault from good1");
553
        if (!$r[0]->faultCode()) {
554
            $val = $r[0]->value();
555
            $this->assertIsString($val, "good1 did not return string");
556
        }
557
        $this->assertNotEquals(0, $r[1]->faultCode(), "no fault from bad");
558
        $this->assertNotEquals(0, $r[2]->faultCode(), "no fault from recursive system.multicall");
559
        $this->assertEquals(0, $r[3]->faultCode(), "fault from good2");
560
        if (!$r[3]->faultCode()) {
561
            $val = $r[3]->value();
562
            $this->assertIsArray($val, "good2 did not return array");
563
        }
564
565
        $this->client->return_type = $returnType;
566
        $this->client->no_multicall = $noMultiCall;
567
    }
568
569
    public function testClientMulticall4()
570
    {
571
        // NB: This test will NOT pass if server does not support system.multicall.
572
573
        $noMultiCall = $this->client->no_multicall;
574
        $returnType = $this->client->return_type;
575
576
        $this->client->return_type = 'xml';
577
        $this->client->no_multicall = false;
578
579
        $good1 = new xmlrpcmsg('system.methodHelp',
580
            array(php_xmlrpc_encode('system.listMethods')));
581
        $good2 = new xmlrpcmsg('system.methodSignature',
582
            array(php_xmlrpc_encode('system.listMethods'))
583
        );
584
585
        $r = $this->send(array($good1, $good2));
586
        if ($r) {
587
            $this->assertEquals(2, count($r), "wrong number of return values");
588
        }
589
        $this->assertEquals(0, $r[0]->faultCode(), "fault from good1");
590
        $this->assertEquals(0, $r[1]->faultCode(), "fault from good2");
591
592
        $hr = $r[0]->httpResponse();
593
        $this->assertEquals(200, $hr['status_code'], "http response of multicall has no status code");
594
        $this->assertEquals($r[0]->httpResponse(), $r[1]->httpResponse(), "http response of multicall items differs");
595
596
        $this->client->return_type = $returnType;
597
        $this->client->no_multicall = $noMultiCall;
598
    }
599
600
    public function testCatchWarnings()
601
    {
602
        $m = new xmlrpcmsg('tests.generatePHPWarning', array(
603
            new xmlrpcval('whatever', 'string'),
604
        ));
605
        $this->addQueryParams(array('FORCE_DEBUG' => 0));
606
        $v = $this->send($m);
607
        if ($v) {
608
            $this->assertEquals(true, $v->scalarval());
609
        }
610
    }
611
612
    public function testCatchExceptions()
613
    {
614
        // this tests for the server to catch exceptions with error code 0
615
        $m = new xmlrpcmsg('tests.raiseException', array(
616
            new xmlrpcval(0, 'int'),
617
        ));
618
        $v = $this->send($m, $GLOBALS['xmlrpcerr']['server_error']);
619
620
        // these test for the different server exception catching modes
621
        $m = new xmlrpcmsg('tests.raiseException', array(
622
            new xmlrpcval(3, 'int'),
623
        ));
624
        $v = $this->send($m, $GLOBALS['xmlrpcerr']['server_error']);
625
        $this->addQueryParams(array('EXCEPTION_HANDLING' => 1));
626
        $v = $this->send($m, 3); // the error code of the expected exception
627
        $this->addQueryParams(array('EXCEPTION_HANDLING' => 2));
628
        // depending on whether display_errors is ON or OFF on the server, we will get back a different error here,
629
        // as php will generate an http status code of either 200 or 500...
630
        $v = $this->send($m, array($GLOBALS['xmlrpcerr']['invalid_return'], $GLOBALS['xmlrpcerr']['http_error']));
631
    }
632
633
    public function testCatchErrors()
634
    {
635
        if (version_compare(PHP_VERSION, '7.0.0', '<'))
636
        {
637
            $this->markTestSkipped('Cannot test php Error on php < 7.0');
638
            return;
639
        }
640
641
        // these test for the different server error catching modes
642
        $m = new xmlrpcmsg('tests.raiseError');
643
        $v = $this->send($m, $GLOBALS['xmlrpcerr']['server_error']);
644
        $this->addQueryParams(array('EXCEPTION_HANDLING' => 1));
645
        $v = $this->send($m, 1); // the error code of the expected exception
646
        $this->addQueryParams(array('EXCEPTION_HANDLING' => 2));
647
        // depending on whether display_errors is ON or OFF on the server, we will get back a different error here,
648
        // as php will generate an http status code of either 200 or 500...
649
        $v = $this->send($m, array($GLOBALS['xmlrpcerr']['invalid_return'], $GLOBALS['xmlrpcerr']['http_error']));
650
    }
651
652
    public function testZeroParams()
653
    {
654
        $m = new xmlrpcmsg('system.listMethods');
655
        $v = $this->send($m);
656
    }
657
658
    public function testNullParams()
659
    {
660
        $m = new xmlrpcmsg('tests.getStateName.12', array(
661
            new xmlrpcval('whatever', 'null'),
662
            new xmlrpcval(23, 'int'),
663
        ));
664
        $v = $this->send($m);
665
        if ($v) {
666
            $this->assertEquals('Michigan', $v->scalarval());
667
        }
668
        $m = new xmlrpcmsg('tests.getStateName.12', array(
669
            new xmlrpcval(23, 'int'),
670
            new xmlrpcval('whatever', 'null'),
671
        ));
672
        $v = $this->send($m);
673
        if ($v) {
674
            $this->assertEquals('Michigan', $v->scalarval());
675
        }
676
        $m = new xmlrpcmsg('tests.getStateName.12', array(
677
            new xmlrpcval(23, 'int')
678
        ));
679
        $v = $this->send($m, array($GLOBALS['xmlrpcerr']['incorrect_params']));
680
    }
681
682
    public function testCodeInjectionServerSide()
683
    {
684
        $m = new xmlrpcmsg('system.MethodHelp');
685
        $m->payload = "<?xml version=\"1.0\"?><methodCall><methodName>validator1.echoStructTest</methodName><params><param><value><struct><member><name>','')); echo('gotcha!'); die(); //</name></member></struct></value></param></params></methodCall>";
686
        $v = $this->send($m);
687
        if ($v) {
688
            $this->assertEquals(0, $v->structsize());
689
        }
690
    }
691
692
    public function testServerWrappedFunction()
693
    {
694
        $m = new xmlrpcmsg('tests.getStateName.2', array(
695
            new xmlrpcval(23, 'int'),
696
        ));
697
        $v = $this->send($m);
698
        $this->assertEquals('Michigan', $v->scalarval());
699
700
        // this generates an exception in the function which was wrapped, which is by default wrapped in a known error response
701
        $m = new xmlrpcmsg('tests.getStateName.2', array(
702
            new xmlrpcval(0, 'int'),
703
        ));
704
        $v = $this->send($m, $GLOBALS['xmlrpcerr']['server_error']);
705
706
        // check if the generated function dispatch map is fine, by checking if the server registered it
707
        $m = new xmlrpcmsg('system.methodSignature', array(
708
            new xmlrpcval('tests.getStateName.2'),
709
        ));
710
        $v = $this->send($m);
711
        $encoder = new \PhpXmlRpc\Encoder();
712
        $this->assertEquals(array(array('string', 'int')), $encoder->decode($v));
713
    }
714
715
    public function testServerWrappedFunctionAsSource()
716
    {
717
        $m = new xmlrpcmsg('tests.getStateName.6', array(
718
            new xmlrpcval(23, 'int'),
719
        ));
720
        $v = $this->send($m);
721
        $this->assertEquals('Michigan', $v->scalarval());
722
723
        // this generates an exception in the function which was wrapped, which is by default wrapped in a known error response
724
        $m = new xmlrpcmsg('tests.getStateName.6', array(
725
            new xmlrpcval(0, 'int'),
726
        ));
727
        $v = $this->send($m, $GLOBALS['xmlrpcerr']['server_error']);
728
    }
729
730
    public function testServerWrappedObjectMethods()
731
    {
732
        $m = new xmlrpcmsg('tests.getStateName.3', array(
733
            new xmlrpcval(23, 'int'),
734
        ));
735
        $v = $this->send($m);
736
        $this->assertEquals('Michigan', $v->scalarval());
737
738
        $m = new xmlrpcmsg('tests.getStateName.4', array(
739
            new xmlrpcval(23, 'int'),
740
        ));
741
        $v = $this->send($m);
742
        $this->assertEquals('Michigan', $v->scalarval());
743
744
        $m = new xmlrpcmsg('tests.getStateName.5', array(
745
            new xmlrpcval(23, 'int'),
746
        ));
747
        $v = $this->send($m);
748
        $this->assertEquals('Michigan', $v->scalarval());
749
750
        $m = new xmlrpcmsg('tests.getStateName.7', array(
751
            new xmlrpcval(23, 'int'),
752
        ));
753
        $v = $this->send($m);
754
        $this->assertEquals('Michigan', $v->scalarval());
755
756
        $m = new xmlrpcmsg('tests.getStateName.8', array(
757
            new xmlrpcval(23, 'int'),
758
        ));
759
        $v = $this->send($m);
760
        $this->assertEquals('Michigan', $v->scalarval());
761
762
        $m = new xmlrpcmsg('tests.getStateName.9', array(
763
            new xmlrpcval(23, 'int'),
764
        ));
765
        $v = $this->send($m);
766
        $this->assertEquals('Michigan', $v->scalarval());
767
    }
768
769
    public function testServerWrappedObjectMethodsAsSource()
770
    {
771
        $m = new xmlrpcmsg('tests.getStateName.7', array(
772
            new xmlrpcval(23, 'int'),
773
        ));
774
        $v = $this->send($m);
775
        $this->assertEquals('Michigan', $v->scalarval());
776
777
        $m = new xmlrpcmsg('tests.getStateName.8', array(
778
            new xmlrpcval(23, 'int'),
779
        ));
780
        $v = $this->send($m);
781
        $this->assertEquals('Michigan', $v->scalarval());
782
783
        $m = new xmlrpcmsg('tests.getStateName.9', array(
784
            new xmlrpcval(23, 'int'),
785
        ));
786
        $v = $this->send($m);
787
        $this->assertEquals('Michigan', $v->scalarval());
788
    }
789
790
    public function testServerClosure()
791
    {
792
        $m = new xmlrpcmsg('tests.getStateName.10', array(
793
            new xmlrpcval(23, 'int'),
794
        ));
795
        $v = $this->send($m);
796
        $this->assertEquals('Michigan', $v->scalarval());
797
    }
798
799
    public function testServerWrappedClosure()
800
    {
801
        $m = new xmlrpcmsg('tests.getStateName.11', array(
802
            new xmlrpcval(23, 'int'),
803
        ));
804
        $v = $this->send($m);
805
        $this->assertEquals('Michigan', $v->scalarval());
806
    }
807
808
    public function testServerWrappedClass()
809
    {
810
        $m = new xmlrpcmsg('tests.handlersContainer.findState', array(
811
            new xmlrpcval(23, 'int'),
812
        ));
813
        $v = $this->send($m);
814
        $this->assertEquals('Michigan', $v->scalarval());
815
    }
816
817
    public function testServerWrappedClassWithNamespace()
818
    {
819
        $m = new xmlrpcmsg('namespacetest.findState', array(
820
            new xmlrpcval(23, 'int'),
821
        ));
822
        $v = $this->send($m);
823
        $this->assertEquals('Michigan', $v->scalarval());
824
    }
825
826
    public function testWrapInexistentMethod()
827
    {
828
        // make a 'deep client copy' as the original one might have many properties set
829
        $func = wrap_xmlrpc_method($this->client, 'examples.getStateName.notexisting', array('simple_client_copy' => 0));
830
        $this->assertEquals(false, $func);
831
    }
832
833
    public function testWrapInexistentUrl()
834
    {
835
        $this->client->path = '/notexisting';
836
        // make a 'deep client copy' as the original one might have many properties set
837
        $func = wrap_xmlrpc_method($this->client, 'examples.getStateName', array('simple_client_copy' => 0));
838
        $this->assertEquals(false, $func);
839
    }
840
841
    public function testWrappedMethod()
842
    {
843
        // make a 'deep client copy' as the original one might have many properties set
844
        $func = wrap_xmlrpc_method($this->client, 'examples.getStateName', array('simple_client_copy' => 0));
845
        if ($func == false) {
846
            $this->fail('Registration of examples.getStateName failed');
847
        } else {
848
            $v = $func(23);
849
            // work around bug in current (or old?) version of phpunit when reporting the error
850
            /*if (is_object($v)) {
851
                $v = var_export($v, true);
852
            }*/
853
            $this->assertEquals('Michigan', $v);
854
        }
855
    }
856
857
    public function testWrappedMethodAsSource()
858
    {
859
        // make a 'deep client copy' as the original one might have many properties set
860
        $func = wrap_xmlrpc_method($this->client, 'examples.getStateName', array('simple_client_copy' => 0, 'return_source' => true));
861
        if ($func == false) {
862
            $this->fail('Registration of examples.getStateName failed');
863
        } else {
864
            eval($func['source']);
865
            $func = $func['function'];
866
            $v = $func(23);
867
            // work around bug in current (or old?) version of phpunit when reporting the error
868
            /*if (is_object($v)) {
869
                $v = var_export($v, true);
870
            }*/
871
            $this->assertEquals('Michigan', $v);
872
        }
873
    }
874
875
    public function testWrappedClass()
876
    {
877
        // make a 'deep client copy' as the original one might have many properties set
878
        // also for speed only wrap one method of the whole server
879
        $class = wrap_xmlrpc_server($this->client, array('simple_client_copy' => 0, 'method_filter' => '/examples\.getStateName/' ));
880
        if ($class == '') {
881
            $this->fail('Registration of remote server failed');
882
        } else {
883
            $obj = new $class();
884
            if (!is_callable(array($obj, 'examples_getStateName'))) {
885
                $this->fail('Registration of remote server failed to import method "examples_getStateName"');
886
            } else {
887
                $v = $obj->examples_getStateName(23);
888
                // work around bug in current (or old?) version of phpunit when reporting the error
889
                /*if (is_object($v)) {
890
                    $v = var_export($v, true);
891
                }*/
892
                $this->assertEquals('Michigan', $v);
893
            }
894
        }
895
    }
896
897
    public function testTransferOfObjectViaWrapping()
898
    {
899
        // make a 'deep client copy' as the original one might have many properties set
900
        $func = wrap_xmlrpc_method($this->client, 'tests.returnPhpObject', array('simple_client_copy' => 0,
901
            'decode_php_objs' => true));
902
        if ($func == false) {
903
            $this->fail('Registration of tests.returnPhpObject failed');
904
        } else {
905
            $v = $func();
906
            $obj = new stdClass();
907
            $obj->hello = 'world';
908
            $this->assertEquals($obj, $v);
909
        }
910
    }
911
912
    public function testGetCookies()
913
    {
914
        // let server set to us some cookies we tell it
915
        $cookies = array(
916
            //'c1' => array(),
917
            'c2' => array('value' => 'c2'),
918
            'c3' => array('value' => 'c3', 'expires' => time() + 60 * 60 * 24 * 30),
919
            'c4' => array('value' => 'c4', 'expires' => time() + 60 * 60 * 24 * 30, 'path' => '/'),
920
            'c5' => array('value' => 'c5', 'expires' => time() + 60 * 60 * 24 * 30, 'path' => '/', 'domain' => 'localhost'),
921
        );
922
        $cookiesval = php_xmlrpc_encode($cookies);
923
        $m = new xmlrpcmsg('tests.setcookies', array($cookiesval));
924
        $r = $this->send($m, 0, true);
925
        if ($r) {
926
            $v = $r->value();
927
            $this->assertEquals(1, $v->scalarval());
928
            // now check if we decoded the cookies as we had set them
929
            $rcookies = $r->cookies();
930
            // remove extra cookies which might have been set by proxies
931
            foreach ($rcookies as $c => $v) {
932
                if (!in_array($c, array('c2', 'c3', 'c4', 'c5'))) {
933
                    unset($rcookies[$c]);
934
                }
935
                // Seems like we get this when using php-fpm and php 5.5+ ...
936
                if (isset($rcookies[$c]['Max-Age'])) {
937
                    unset($rcookies[$c]['Max-Age']);
938
                }
939
            }
940
            foreach ($cookies as $c => $v) {
941
                // format for date string in cookies: 'Mon, 31 Oct 2005 13:50:56 GMT'
942
                // but PHP versions differ on that, some use 'Mon, 31-Oct-2005 13:50:56 GMT'...
943
                if (isset($v['expires'])) {
944
                    if (isset($rcookies[$c]['expires']) && strpos($rcookies[$c]['expires'], '-')) {
945
                        $cookies[$c]['expires'] = gmdate('D, d\-M\-Y H:i:s \G\M\T', $cookies[$c]['expires']);
946
                    } else {
947
                        $cookies[$c]['expires'] = gmdate('D, d M Y H:i:s \G\M\T', $cookies[$c]['expires']);
948
                    }
949
                }
950
            }
951
952
            $this->assertEquals($cookies, $rcookies);
953
        }
954
    }
955
956
    public function testSetCookies()
957
    {
958
        // let server set to us some cookies we tell it
959
        $cookies = array(
960
            'c0' => null,
961
            'c1' => 1,
962
            'c2' => '2 3',
963
            'c3' => '!@#$%^&*()_+|}{":?><,./\';[]\\=-',
964
        );
965
        $m = new xmlrpcmsg('tests.getcookies', array());
966
        foreach ($cookies as $cookie => $val) {
967
            $this->client->setCookie($cookie, $val);
968
            $cookies[$cookie] = (string)$cookies[$cookie];
969
        }
970
        $r = $this->client->send($m, $this->timeout, $this->method);
971
        $this->assertEquals(0, $r->faultCode(), 'Error ' . $r->faultCode() . ' connecting to server: ' . $r->faultString());
972
        if (!$r->faultCode()) {
973
            $v = $r->value();
974
            $v = php_xmlrpc_decode($v);
975
976
            // take care of the extra cookies used for coverage collection and test mechanics
977
            if (isset($v['PHPUNIT_SELENIUM_TEST_ID'])) {
978
                unset($v['PHPUNIT_SELENIUM_TEST_ID']);
979
            }
980
            if (isset($v['PHPUNIT_RANDOM_TEST_ID'])) {
981
                unset($v['PHPUNIT_RANDOM_TEST_ID']);
982
            }
983
984
            // on IIS and Apache getallheaders returns something slightly different...
985
            $this->assertEquals($cookies, $v);
986
        }
987
    }
988
989
    public function testServerComments()
990
    {
991
        $m = new xmlrpcmsg('tests.handlersContainer.debugMessageGenerator', array(
992
            new xmlrpcval('hello world', 'string'),
993
        ));
994
        $r = $this->send($m, 0, true);
995
        $this->assertStringContainsString('hello world', $r->raw_data);
996
    }
997
998
    public function testSendTwiceSameMsg()
999
    {
1000
        $m = new xmlrpcmsg('examples.stringecho', array(
1001
            new xmlrpcval('hello world', 'string'),
1002
        ));
1003
        $v1 = $this->send($m);
1004
        $v2 = $this->send($m);
1005
        if ($v1 && $v2) {
1006
            $this->assertEquals($v1, $v2);
1007
        }
1008
    }
1009
1010
    public function testNegativeDebug()
1011
    {
1012
        $m = new xmlrpcmsg('examples.stringecho', array(
1013
            new xmlrpcval('hello world', 'string'),
1014
        ));
1015
        $v1 = $this->send($m, 0, true);
1016
        $h = $v1->httpResponse();
1017
        $this->assertEquals('200', $h['status_code']);
1018
        $this->assertNotEmpty($h['headers']);
1019
1020
        $d = $this->client->getOption('debug');
1021
        $this->client->setDebug(-1);
1022
        $v2 = $this->send($m, 0, true);
1023
        $this->client->setDebug($d);
1024
        $h = $v2->httpResponse();
1025
        $this->assertEmpty($h['headers']);
1026
        $this->assertEmpty($h['raw_data']);
1027
    }
1028
}
1029