3 * NB: do not let your IDE fool you. The correct encoding for this file is NOT UTF8.
5 include_once __DIR__ . '/../lib/xmlrpc.inc';
6 include_once __DIR__ . '/../lib/xmlrpcs.inc';
8 include_once __DIR__ . '/parse_args.php';
10 include_once __DIR__ . '/PolyfillTestCase.php';
12 use PHPUnit\Runner\BaseTestRunner;
15 * Tests involving parsing of xml and handling of xmlrpc values
17 class ParsingBugsTests extends PhpXmlRpc_PolyfillTestCase
19 public $args = array();
21 protected function set_up()
23 $this->args = argParser::getArgs();
24 if ($this->args['DEBUG'] == 1)
28 protected function tear_down()
30 if ($this->args['DEBUG'] != 1)
32 $out = ob_get_clean();
33 $status = $this->getStatus();
34 if ($status == BaseTestRunner::STATUS_ERROR
35 || $status == BaseTestRunner::STATUS_FAILURE) {
40 protected function newMsg($methodName, $params = array())
42 $msg = new xmlrpcmsg($methodName, $params);
43 $msg->setDebug($this->args['DEBUG']);
47 public function testMinusOneString()
49 $v = new xmlrpcval('-1');
50 $u = new xmlrpcval('-1', 'string');
51 $t = new xmlrpcval(-1, 'string');
52 $this->assertEquals($v->scalarval(), $u->scalarval());
53 $this->assertEquals($v->scalarval(), $t->scalarval());
57 * This looks funny, and we might call it a bug. But we strive for 100 backwards compat...
59 public function testMinusOneInt()
62 $v = new xmlrpcval(-1);
63 $this->assertEquals($u->scalarval(), $v->scalarval());
66 public function testUnicodeInMemberName()
68 $str = "G" . chr(252) . "nter, El" . chr(232) . "ne";
69 $v = array($str => new xmlrpcval(1));
70 $r = new xmlrpcresp(new xmlrpcval($v, 'struct'));
72 $m = $this->newMsg('dummy');
73 $r = $m->parseResponse($r);
75 $this->assertEquals(true, $v->structmemexists($str));
78 public function testUnicodeInErrorString()
80 $response = utf8_encode(
81 '<?xml version="1.0"?>
83 <!-- found by G. Giunta, covers what happens when lib receives UTF8 chars in response text and comments -->
84 <!-- ' . chr(224) . chr(252) . chr(232) . 'àüè -->
90 <name>faultCode</name>
91 <value><int>888</int></value>
94 <name>faultString</name>
95 <value><string>' . chr(224) . chr(252) . chr(232) . 'àüè</string></value>
101 $m = $this->newMsg('dummy');
102 $r = $m->parseResponse($response);
103 $v = $r->faultString();
104 $this->assertEquals(chr(224) . chr(252) . chr(232) . chr(224) . chr(252) . chr(232), $v);
107 public function testValidNumbers()
109 $m = $this->newMsg('dummy');
111 '<?xml version="1.0"?>
118 <name>integer1</name>
119 <value><int>01</int></value>
122 <name>integer2</name>
123 <value><int>+1</int></value>
126 <name>integer3</name>
127 <value><i4>1</i4></value>
131 <value><double>01.10</double></value>
135 <value><double>+1.10</double></value>
139 <value><double>-1.10e2</double></value>
146 $r = $m->parseResponse($fp);
148 $s = $v->structmem('integer1');
149 $t = $v->structmem('integer2');
150 $u = $v->structmem('integer3');
151 $x = $v->structmem('float1');
152 $y = $v->structmem('float2');
153 $z = $v->structmem('float3');
154 $this->assertEquals(1, $s->scalarval());
155 $this->assertEquals(1, $t->scalarval());
156 $this->assertEquals(1, $u->scalarval());
158 $this->assertEquals(1.1, $x->scalarval());
159 $this->assertEquals(1.1, $y->scalarval());
160 $this->assertEquals(-110.0, $z->scalarval());
163 public function testI8()
165 if (PHP_INT_SIZE == 4 ) {
166 $this->markTestSkipped('Can not test i8 as php is compiled in 32 bit mode');
170 $m = $this->newMsg('dummy');
172 '<?xml version="1.0"?>
179 <name>integer1</name>
180 <value><i8>1</i8></value>
187 $r = $m->parseResponse($fp);
189 $s = $v->structmem('integer1');
190 $this->assertEquals(1, $s->scalarval());
193 public function testAddScalarToStruct()
195 $v = new xmlrpcval(array('a' => 'b'), 'struct');
196 // use @ operator in case error_log gets on screen
197 $r = @$v->addscalar('c');
198 $this->assertEquals(0, $r);
201 public function testAddStructToStruct()
203 $v = new xmlrpcval(array('a' => new xmlrpcval('b')), 'struct');
204 $r = $v->addstruct(array('b' => new xmlrpcval('c')));
205 $this->assertEquals(2, $v->structsize());
206 $this->assertEquals(1, $r);
207 $r = $v->addstruct(array('b' => new xmlrpcval('b')));
208 $this->assertEquals(2, $v->structsize());
211 public function testAddArrayToArray()
213 $v = new xmlrpcval(array(new xmlrpcval('a'), new xmlrpcval('b')), 'array');
214 $r = $v->addarray(array(new xmlrpcval('b'), new xmlrpcval('c')));
215 $this->assertEquals(4, $v->arraysize());
216 $this->assertEquals(1, $r);
219 public function testEncodeArray()
222 $v = php_xmlrpc_encode($r);
223 $this->assertEquals('array', $v->kindof());
226 public function testEncodeRecursive()
228 $v = php_xmlrpc_encode(php_xmlrpc_encode('a simple string'));
229 $this->assertEquals('scalar', $v->kindof());
232 public function testBrokenRequests()
234 $s = new xmlrpc_server();
235 // omitting the 'params' tag: not tolerated by the lib anymore
236 $f = '<?xml version="1.0"?>
238 <methodName>system.methodHelp</methodName>
240 <value><string>system.methodHelp</string></value>
243 $r = $s->parserequest($f);
244 $this->assertEquals(15, $r->faultCode());
245 // omitting a 'param' tag
246 $f = '<?xml version="1.0"?>
248 <methodName>system.methodHelp</methodName>
250 <value><string>system.methodHelp</string></value>
253 $r = $s->parserequest($f);
254 $this->assertEquals(15, $r->faultCode());
255 // omitting a 'value' tag
256 $f = '<?xml version="1.0"?>
258 <methodName>system.methodHelp</methodName>
260 <param><string>system.methodHelp</string></param>
263 $r = $s->parserequest($f);
264 $this->assertEquals(15, $r->faultCode());
267 public function testBrokenResponses()
269 $m = $this->newMsg('dummy');
270 // omitting the 'params' tag: no more tolerated by the lib...
271 $f = '<?xml version="1.0"?>
274 <value><string>system.methodHelp</string></value>
277 $r = $m->parseResponse($f);
278 $this->assertEquals(2, $r->faultCode());
279 // omitting the 'param' tag: no more tolerated by the lib...
280 $f = '<?xml version="1.0"?>
283 <value><string>system.methodHelp</string></value>
286 $r = $m->parseResponse($f);
287 $this->assertEquals(2, $r->faultCode());
288 // omitting a 'value' tag: KO
289 $f = '<?xml version="1.0"?>
292 <param><string>system.methodHelp</string></param>
295 $r = $m->parseResponse($f);
296 $this->assertEquals(2, $r->faultCode());
299 public function testBuggyHttp()
301 $s = $this->newMsg('dummy');
302 $f = 'HTTP/1.1 100 Welcome to the jungle
305 X-Content-Marx-Brothers: Harpo
307 Content-Length: who knows?
311 <?xml version="1.0"?>
312 <!-- First of all, let\'s check out if the lib properly handles a commented </methodResponse> tag... -->
313 <methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
314 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>hello world. 2 newlines follow
317 and there they were.</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>
318 <script type="text\javascript">document.write(\'Hello, my name is added nag, I\\\'m happy to serve your content for free\');</script>
320 $r = $s->parseResponse($f);
322 $s = $v->structmem('content');
323 $this->assertEquals("hello world. 2 newlines follow\n\n\nand there they were.", $s->scalarval());
326 public function testStringBug()
328 $s = $this->newMsg('dummy');
329 $f = '<?xml version="1.0"?>
331 <!-- found by 2z69xks7bpy001@sneakemail.com, amongst others
332 covers what happens when there\'s character data after </string>
333 and before </value> -->
346 <name>sessionID</name>
348 <string>S300510007I</string>
356 $r = $s->parseResponse($f);
358 $s = $v->structmem('sessionID');
359 $this->assertEquals('S300510007I', $s->scalarval());
362 public function testWhiteSpace()
364 $s = $this->newMsg('dummy');
365 $f = '<?xml version="1.0"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
366 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>hello world. 2 newlines follow
369 and there they were.</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>
371 $r = $s->parseResponse($f);
373 $s = $v->structmem('content');
374 $this->assertEquals("hello world. 2 newlines follow\n\n\nand there they were.", $s->scalarval());
377 public function testDoubleDataInArrayTag()
379 $s = $this->newMsg('dummy');
380 $f = '<?xml version="1.0"?><methodResponse><params><param><value><array>
383 </array></value></param></params></methodResponse>
385 $r = $s->parseResponse($f);
386 $v = $r->faultCode();
387 $this->assertEquals(2, $v);
388 $f = '<?xml version="1.0"?><methodResponse><params><param><value><array>
389 <data><value>Hello world</value></data>
391 </array></value></param></params></methodResponse>
393 $r = $s->parseResponse($f);
394 $v = $r->faultCode();
395 $this->assertEquals(2, $v);
398 public function testDoubleStuffInValueTag()
400 $s = $this->newMsg('dummy');
401 $f = '<?xml version="1.0"?><methodResponse><params><param><value>
402 <string>hello world</string>
403 <array><data></data></array>
404 </value></param></params></methodResponse>
406 $r = $s->parseResponse($f);
407 $v = $r->faultCode();
408 $this->assertEquals(2, $v);
409 $f = '<?xml version="1.0"?><methodResponse><params><param><value>
410 <string>hello</string>
411 <string>world</string>
412 </value></param></params></methodResponse>
414 $r = $s->parseResponse($f);
415 $v = $r->faultCode();
416 $this->assertEquals(2, $v);
417 $f = '<?xml version="1.0"?><methodResponse><params><param><value>
418 <string>hello</string>
419 <struct><member><name>hello><value>world</value></member></struct>
420 </value></param></params></methodResponse>
422 $r = $s->parseResponse($f);
423 $v = $r->faultCode();
424 $this->assertEquals(2, $v);
427 public function testAutodecodeResponse()
429 $s = $this->newMsg('dummy');
430 $f = '<?xml version="1.0"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
431 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>hello world. 2 newlines follow
434 and there they were.</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>
436 $r = $s->parseResponse($f, true, 'phpvals');
439 $this->assertEquals("hello world. 2 newlines follow\n\n\nand there they were.", $s);
442 public function testNoDecodeResponse()
444 $s = $this->newMsg('dummy');
445 $f = '<?xml version="1.0"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
446 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>hello world. 2 newlines follow
449 and there they were.</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>';
450 $r = $s->parseResponse($f, true, 'xml');
452 $this->assertEquals($f, $v);
455 public function testAutoCoDec()
457 $data1 = array(1, 1.0, 'hello world', true, '20051021T23:43:00', -1, 11.0, '~!@#$%^&*()_+|', false, '20051021T23:43:00');
458 $data2 = array('zero' => $data1, 'one' => $data1, 'two' => $data1, 'three' => $data1, 'four' => $data1, 'five' => $data1, 'six' => $data1, 'seven' => $data1, 'eight' => $data1, 'nine' => $data1);
459 $data = array($data2, $data2, $data2, $data2, $data2, $data2, $data2, $data2, $data2, $data2);
460 //$keys = array('zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine');
461 $v1 = php_xmlrpc_encode($data, array('auto_dates'));
462 $v2 = php_xmlrpc_decode_xml($v1->serialize());
463 $this->assertEquals($v1, $v2);
464 $r1 = new PhpXmlRpc\Response($v1);
465 $r2 = php_xmlrpc_decode_xml($r1->serialize());
466 $r2->serialize(); // needed to set internal member payload
467 $this->assertEquals($r1, $r2);
468 $m1 = new PhpXmlRpc\Request('hello dolly', array($v1));
469 $m2 = php_xmlrpc_decode_xml($m1->serialize());
470 $m2->serialize(); // needed to set internal member payload
471 $this->assertEquals($m1, $m2);
474 public function testUTF8Request()
476 $sendstring = 'κόσμε'; // Greek word 'kosme'. NB: NOT a valid ISO8859 string!
477 $GLOBALS['xmlrpc_internalencoding'] = 'UTF-8';
478 \PhpXmlRpc\PhpXmlRpc::importGlobals();
479 $f = new xmlrpcval($sendstring, 'string');
480 $v = $f->serialize();
481 $this->assertEquals("<value><string>κόσμε</string></value>\n", $v);
482 $GLOBALS['xmlrpc_internalencoding'] = 'ISO-8859-1';
483 \PhpXmlRpc\PhpXmlRpc::importGlobals();
486 public function testUTF8Response()
488 $string = chr(224) . chr(252) . chr(232);
490 $s = $this->newMsg('dummy');
491 $f = "HTTP/1.1 200 OK\r\nContent-type: text/xml; charset=UTF-8\r\n\r\n" . '<?xml version="1.0"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
492 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>' . utf8_encode($string) . '</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>
494 $r = $s->parseResponse($f, false, 'phpvals');
497 $this->assertEquals($string, $v);
499 $f = '<?xml version="1.0" encoding="UTF-8"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
500 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>' . utf8_encode($string) . '</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>
502 $r = $s->parseResponse($f, false, 'phpvals');
505 $this->assertEquals($string, $v);
507 $r = php_xmlrpc_decode_xml($f);
509 $v = $v->structmem('content')->scalarval();
510 $this->assertEquals($string, $v);
513 public function testLatin1Response()
515 $string = chr(224) . chr(252) . chr(232);
517 $s = $this->newMsg('dummy');
518 $f = "HTTP/1.1 200 OK\r\nContent-type: text/xml; charset=ISO-8859-1\r\n\r\n" . '<?xml version="1.0"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
519 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>' . $string . '</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>
521 $r = $s->parseResponse($f, false, 'phpvals');
524 $this->assertEquals($string, $v);
526 $f = '<?xml version="1.0" encoding="ISO-8859-1"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
527 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>' . $string . '</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>
529 $r = $s->parseResponse($f, false, 'phpvals');
532 $this->assertEquals($string, $v);
534 $r = php_xmlrpc_decode_xml($f);
536 $v = $v->structmem('content')->scalarval();
537 $this->assertEquals($string, $v);
540 public function testUTF8IntString()
542 $v = new xmlrpcval(100, 'int');
543 $s = $v->serialize('UTF-8');
544 $this->assertequals("<value><int>100</int></value>\n", $s);
547 public function testStringInt()
549 $v = new xmlrpcval('hello world', 'int');
550 $s = $v->serialize();
551 $this->assertequals("<value><int>0</int></value>\n", $s);
554 public function testStructMemExists()
556 $v = php_xmlrpc_encode(array('hello' => 'world'));
557 $b = $v->structmemexists('hello');
558 $this->assertequals(true, $b);
559 $b = $v->structmemexists('world');
560 $this->assertequals(false, $b);
563 public function testNilvalue()
565 // default case: we do not accept nil values received
566 $v = new xmlrpcval('hello', 'null');
567 $r = new xmlrpcresp($v);
568 $s = $r->serialize();
569 $m = $this->newMsg('dummy');
570 $r = $m->parseresponse($s);
571 $this->assertequals(2, $r->faultCode());
572 // enable reception of nil values
573 $GLOBALS['xmlrpc_null_extension'] = true;
574 \PhpXmlRpc\PhpXmlRpc::importGlobals();
575 $r = $m->parseresponse($s);
577 $this->assertequals('null', $v->scalartyp());
578 // test with the apache version: EX:NIL
579 $GLOBALS['xmlrpc_null_apache_encoding'] = true;
580 \PhpXmlRpc\PhpXmlRpc::importGlobals();
582 $v = new xmlrpcval('hello', 'null');
583 $s = $v->serialize();
584 $this->assertequals(1, preg_match('#<value><ex:nil/></value>#', $s));
586 $r = new xmlrpcresp($v);
587 $s = $r->serialize();
588 $r = $m->parseresponse($s);
590 $this->assertequals('null', $v->scalartyp());
591 $GLOBALS['xmlrpc_null_extension'] = false;
592 \PhpXmlRpc\PhpXmlRpc::importGlobals();
593 $r = $m->parseresponse($s);
594 $this->assertequals(2, $r->faultCode());
597 public function testLocale()
599 $locale = setlocale(LC_NUMERIC, 0);
600 /// @todo on php 5.3/win setting locale to german does not seem to set decimal separator to comma...
601 if (setlocale(LC_NUMERIC, 'deu', 'de_DE@euro', 'de_DE', 'de', 'ge') !== false) {
602 $v = new xmlrpcval(1.1, 'double');
603 if (strpos($v->scalarval(), ',') == 1) {
604 $r = $v->serialize();
605 $this->assertequals(false, strpos($r, ','));
606 setlocale(LC_NUMERIC, $locale);
608 setlocale(LC_NUMERIC, $locale);
609 $this->markTestSkipped('did not find a locale which sets decimal separator to comma');
612 $this->markTestSkipped('did not find a locale which sets decimal separator to comma');
616 public function testArrayAccess()
618 $v1 = new xmlrpcval(array(new xmlrpcval('one'), new xmlrpcval('two')), 'array');
619 $this->assertequals(1, count($v1));
620 $out = array('me' => array(), 'mytype' => 2, '_php_class' => null);
622 foreach($v1 as $key => $val)
624 $this->assertArrayHasKey($key, $out);
625 $expected = $out[$key];
626 if (gettype($expected) == 'array') {
627 $this->assertequals('array', gettype($val));
629 $this->assertequals($expected, $val);
633 $v2 = new \PhpXmlRpc\Value(array(new \PhpXmlRpc\Value('one'), new \PhpXmlRpc\Value('two')), 'array');
634 $this->assertequals(2, count($v2));
635 $out = array(array('key' => 0, 'value' => 'object'), array('key' => 1, 'value' => 'object'));
637 foreach($v2 as $key => $val)
639 $expected = $out[$i];
640 $this->assertequals($expected['key'], $key);
641 $this->assertequals($expected['value'], gettype($val));
646 function testBigXML()
648 // nb: make sure that the serialized xml corresponding to this is > 10MB in size
650 for ($i = 0; $i < 500000; $i++ ) {
651 $data[] = 'hello world';
654 $encoder = new \PhpXmlRpc\Encoder();
655 $val = $encoder->encode($data);
656 $req = new \PhpXmlRpc\Request('test', array($val));
657 $xml = $req->serialize();
658 $parser = new \PhpXmlRpc\Helper\XMLParser();
659 $parser->parse($xml);
661 $this->assertequals(0, $parser->_xh['isf']);