Issues (320)

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