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';
11 * Tests involving parsing of xml and handling of xmlrpc values
13 class ParsingBugsTests extends PHPUnit_Framework_TestCase
15 public $args = array();
17 protected function setUp()
19 $this->args = argParser::getArgs();
20 if ($this->args['DEBUG'] == 1)
24 protected function tearDown()
26 if ($this->args['DEBUG'] != 1)
28 $out = ob_get_clean();
29 $status = $this->getStatus();
30 if ($status == PHPUnit_Runner_BaseTestRunner::STATUS_ERROR
31 || $status == PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE) {
36 protected function newMsg($methodName, $params = array())
38 $msg = new xmlrpcmsg($methodName, $params);
39 $msg->setDebug($this->args['DEBUG']);
43 public function testMinusOneString()
45 $v = new xmlrpcval('-1');
46 $u = new xmlrpcval('-1', 'string');
47 $t = new xmlrpcval(-1, 'string');
48 $this->assertEquals($v->scalarval(), $u->scalarval());
49 $this->assertEquals($v->scalarval(), $t->scalarval());
53 * This looks funny, and we might call it a bug. But we strive for 100 backwards compat...
55 public function testMinusOneInt()
58 $v = new xmlrpcval(-1);
59 $this->assertEquals($u->scalarval(), $v->scalarval());
62 public function testUnicodeInMemberName()
64 $str = "G" . chr(252) . "nter, El" . chr(232) . "ne";
65 $v = array($str => new xmlrpcval(1));
66 $r = new xmlrpcresp(new xmlrpcval($v, 'struct'));
68 $m = $this->newMsg('dummy');
69 $r = $m->parseResponse($r);
71 $this->assertEquals(true, $v->structmemexists($str));
74 public function testUnicodeInErrorString()
76 $response = utf8_encode(
77 '<?xml version="1.0"?>
79 <!-- found by G. Giunta, covers what happens when lib receives UTF8 chars in response text and comments -->
80 <!-- ' . chr(224) . chr(252) . chr(232) . 'àüè -->
86 <name>faultCode</name>
87 <value><int>888</int></value>
90 <name>faultString</name>
91 <value><string>' . chr(224) . chr(252) . chr(232) . 'àüè</string></value>
97 $m = $this->newMsg('dummy');
98 $r = $m->parseResponse($response);
99 $v = $r->faultString();
100 $this->assertEquals(chr(224) . chr(252) . chr(232) . chr(224) . chr(252) . chr(232), $v);
103 public function testValidNumbers()
105 $m = $this->newMsg('dummy');
107 '<?xml version="1.0"?>
114 <name>integer1</name>
115 <value><int>01</int></value>
118 <name>integer2</name>
119 <value><int>+1</int></value>
122 <name>integer3</name>
123 <value><i4>1</i4></value>
127 <value><double>01.10</double></value>
131 <value><double>+1.10</double></value>
135 <value><double>-1.10e2</double></value>
142 $r = $m->parseResponse($fp);
144 $s = $v->structmem('integer1');
145 $t = $v->structmem('integer2');
146 $u = $v->structmem('integer3');
147 $x = $v->structmem('float1');
148 $y = $v->structmem('float2');
149 $z = $v->structmem('float3');
150 $this->assertEquals(1, $s->scalarval());
151 $this->assertEquals(1, $t->scalarval());
152 $this->assertEquals(1, $u->scalarval());
154 $this->assertEquals(1.1, $x->scalarval());
155 $this->assertEquals(1.1, $y->scalarval());
156 $this->assertEquals(-110.0, $z->scalarval());
159 public function testI8()
161 if (PHP_INT_SIZE == 4 ) {
162 $this->markTestSkipped('did not find a locale which sets decimal separator to comma');
166 $m = $this->newMsg('dummy');
168 '<?xml version="1.0"?>
175 <name>integer1</name>
176 <value><i8>1</i8></value>
183 $r = $m->parseResponse($fp);
185 $s = $v->structmem('integer1');
186 $this->assertEquals(1, $s->scalarval());
189 public function testAddScalarToStruct()
191 $v = new xmlrpcval(array('a' => 'b'), 'struct');
192 // use @ operator in case error_log gets on screen
193 $r = @$v->addscalar('c');
194 $this->assertEquals(0, $r);
197 public function testAddStructToStruct()
199 $v = new xmlrpcval(array('a' => new xmlrpcval('b')), 'struct');
200 $r = $v->addstruct(array('b' => new xmlrpcval('c')));
201 $this->assertEquals(2, $v->structsize());
202 $this->assertEquals(1, $r);
203 $r = $v->addstruct(array('b' => new xmlrpcval('b')));
204 $this->assertEquals(2, $v->structsize());
207 public function testAddArrayToArray()
209 $v = new xmlrpcval(array(new xmlrpcval('a'), new xmlrpcval('b')), 'array');
210 $r = $v->addarray(array(new xmlrpcval('b'), new xmlrpcval('c')));
211 $this->assertEquals(4, $v->arraysize());
212 $this->assertEquals(1, $r);
215 public function testEncodeArray()
218 $v = php_xmlrpc_encode($r);
219 $this->assertEquals('array', $v->kindof());
222 public function testEncodeRecursive()
224 $v = php_xmlrpc_encode(php_xmlrpc_encode('a simple string'));
225 $this->assertEquals('scalar', $v->kindof());
228 public function testBrokenRequests()
230 $s = new xmlrpc_server();
231 // omitting the 'params' tag: not tolerated by the lib anymore
232 $f = '<?xml version="1.0"?>
234 <methodName>system.methodHelp</methodName>
236 <value><string>system.methodHelp</string></value>
239 $r = $s->parserequest($f);
240 $this->assertEquals(15, $r->faultCode());
241 // omitting a 'param' tag
242 $f = '<?xml version="1.0"?>
244 <methodName>system.methodHelp</methodName>
246 <value><string>system.methodHelp</string></value>
249 $r = $s->parserequest($f);
250 $this->assertEquals(15, $r->faultCode());
251 // omitting a 'value' tag
252 $f = '<?xml version="1.0"?>
254 <methodName>system.methodHelp</methodName>
256 <param><string>system.methodHelp</string></param>
259 $r = $s->parserequest($f);
260 $this->assertEquals(15, $r->faultCode());
263 public function testBrokenResponses()
265 $m = $this->newMsg('dummy');
266 // omitting the 'params' tag: no more tolerated by the lib...
267 $f = '<?xml version="1.0"?>
270 <value><string>system.methodHelp</string></value>
273 $r = $m->parseResponse($f);
274 $this->assertEquals(2, $r->faultCode());
275 // omitting the 'param' tag: no more tolerated by the lib...
276 $f = '<?xml version="1.0"?>
279 <value><string>system.methodHelp</string></value>
282 $r = $m->parseResponse($f);
283 $this->assertEquals(2, $r->faultCode());
284 // omitting a 'value' tag: KO
285 $f = '<?xml version="1.0"?>
288 <param><string>system.methodHelp</string></param>
291 $r = $m->parseResponse($f);
292 $this->assertEquals(2, $r->faultCode());
295 public function testBuggyHttp()
297 $s = $this->newMsg('dummy');
298 $f = 'HTTP/1.1 100 Welcome to the jungle
301 X-Content-Marx-Brothers: Harpo
303 Content-Length: who knows?
307 <?xml version="1.0"?>
308 <!-- First of all, let\'s check out if the lib properly handles a commented </methodResponse> tag... -->
309 <methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
310 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>hello world. 2 newlines follow
313 and there they were.</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>
314 <script type="text\javascript">document.write(\'Hello, my name is added nag, I\\\'m happy to serve your content for free\');</script>
316 $r = $s->parseResponse($f);
318 $s = $v->structmem('content');
319 $this->assertEquals("hello world. 2 newlines follow\n\n\nand there they were.", $s->scalarval());
322 public function testStringBug()
324 $s = $this->newMsg('dummy');
325 $f = '<?xml version="1.0"?>
327 <!-- found by 2z69xks7bpy001@sneakemail.com, amongst others
328 covers what happens when there\'s character data after </string>
329 and before </value> -->
342 <name>sessionID</name>
344 <string>S300510007I</string>
352 $r = $s->parseResponse($f);
354 $s = $v->structmem('sessionID');
355 $this->assertEquals('S300510007I', $s->scalarval());
358 public function testWhiteSpace()
360 $s = $this->newMsg('dummy');
361 $f = '<?xml version="1.0"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
362 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>hello world. 2 newlines follow
365 and there they were.</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>
367 $r = $s->parseResponse($f);
369 $s = $v->structmem('content');
370 $this->assertEquals("hello world. 2 newlines follow\n\n\nand there they were.", $s->scalarval());
373 public function testDoubleDataInArrayTag()
375 $s = $this->newMsg('dummy');
376 $f = '<?xml version="1.0"?><methodResponse><params><param><value><array>
379 </array></value></param></params></methodResponse>
381 $r = $s->parseResponse($f);
382 $v = $r->faultCode();
383 $this->assertEquals(2, $v);
384 $f = '<?xml version="1.0"?><methodResponse><params><param><value><array>
385 <data><value>Hello world</value></data>
387 </array></value></param></params></methodResponse>
389 $r = $s->parseResponse($f);
390 $v = $r->faultCode();
391 $this->assertEquals(2, $v);
394 public function testDoubleStuffInValueTag()
396 $s = $this->newMsg('dummy');
397 $f = '<?xml version="1.0"?><methodResponse><params><param><value>
398 <string>hello world</string>
399 <array><data></data></array>
400 </value></param></params></methodResponse>
402 $r = $s->parseResponse($f);
403 $v = $r->faultCode();
404 $this->assertEquals(2, $v);
405 $f = '<?xml version="1.0"?><methodResponse><params><param><value>
406 <string>hello</string>
407 <string>world</string>
408 </value></param></params></methodResponse>
410 $r = $s->parseResponse($f);
411 $v = $r->faultCode();
412 $this->assertEquals(2, $v);
413 $f = '<?xml version="1.0"?><methodResponse><params><param><value>
414 <string>hello</string>
415 <struct><member><name>hello><value>world</value></member></struct>
416 </value></param></params></methodResponse>
418 $r = $s->parseResponse($f);
419 $v = $r->faultCode();
420 $this->assertEquals(2, $v);
423 public function testAutodecodeResponse()
425 $s = $this->newMsg('dummy');
426 $f = '<?xml version="1.0"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
427 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>hello world. 2 newlines follow
430 and there they were.</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>
432 $r = $s->parseResponse($f, true, 'phpvals');
435 $this->assertEquals("hello world. 2 newlines follow\n\n\nand there they were.", $s);
438 public function testNoDecodeResponse()
440 $s = $this->newMsg('dummy');
441 $f = '<?xml version="1.0"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
442 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>hello world. 2 newlines follow
445 and there they were.</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>';
446 $r = $s->parseResponse($f, true, 'xml');
448 $this->assertEquals($f, $v);
451 public function testAutoCoDec()
453 $data1 = array(1, 1.0, 'hello world', true, '20051021T23:43:00', -1, 11.0, '~!@#$%^&*()_+|', false, '20051021T23:43:00');
454 $data2 = array('zero' => $data1, 'one' => $data1, 'two' => $data1, 'three' => $data1, 'four' => $data1, 'five' => $data1, 'six' => $data1, 'seven' => $data1, 'eight' => $data1, 'nine' => $data1);
455 $data = array($data2, $data2, $data2, $data2, $data2, $data2, $data2, $data2, $data2, $data2);
456 //$keys = array('zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine');
457 $v1 = php_xmlrpc_encode($data, array('auto_dates'));
458 $v2 = php_xmlrpc_decode_xml($v1->serialize());
459 $this->assertEquals($v1, $v2);
460 $r1 = new PhpXmlRpc\Response($v1);
461 $r2 = php_xmlrpc_decode_xml($r1->serialize());
462 $r2->serialize(); // needed to set internal member payload
463 $this->assertEquals($r1, $r2);
464 $m1 = new PhpXmlRpc\Request('hello dolly', array($v1));
465 $m2 = php_xmlrpc_decode_xml($m1->serialize());
466 $m2->serialize(); // needed to set internal member payload
467 $this->assertEquals($m1, $m2);
470 public function testUTF8Request()
472 $sendstring = 'κόσμε'; // Greek word 'kosme'. NB: NOT a valid ISO8859 string!
473 $GLOBALS['xmlrpc_internalencoding'] = 'UTF-8';
474 \PhpXmlRpc\PhpXmlRpc::importGlobals();
475 $f = new xmlrpcval($sendstring, 'string');
476 $v = $f->serialize();
477 $this->assertEquals("<value><string>κόσμε</string></value>\n", $v);
478 $GLOBALS['xmlrpc_internalencoding'] = 'ISO-8859-1';
479 \PhpXmlRpc\PhpXmlRpc::importGlobals();
482 public function testUTF8Response()
484 $string = chr(224) . chr(252) . chr(232);
486 $s = $this->newMsg('dummy');
487 $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>
488 <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>
490 $r = $s->parseResponse($f, false, 'phpvals');
493 $this->assertEquals($string, $v);
495 $f = '<?xml version="1.0" encoding="UTF-8"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
496 <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>
498 $r = $s->parseResponse($f, false, 'phpvals');
501 $this->assertEquals($string, $v);
503 $r = php_xmlrpc_decode_xml($f);
505 $v = $v->structmem('content')->scalarval();
506 $this->assertEquals($string, $v);
509 public function testLatin1Response()
511 $string = chr(224) . chr(252) . chr(232);
513 $s = $this->newMsg('dummy');
514 $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>
515 <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>
517 $r = $s->parseResponse($f, false, 'phpvals');
520 $this->assertEquals($string, $v);
522 $f = '<?xml version="1.0" encoding="ISO-8859-1"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
523 <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>
525 $r = $s->parseResponse($f, false, 'phpvals');
528 $this->assertEquals($string, $v);
530 $r = php_xmlrpc_decode_xml($f);
532 $v = $v->structmem('content')->scalarval();
533 $this->assertEquals($string, $v);
536 public function testUTF8IntString()
538 $v = new xmlrpcval(100, 'int');
539 $s = $v->serialize('UTF-8');
540 $this->assertequals("<value><int>100</int></value>\n", $s);
543 public function testStringInt()
545 $v = new xmlrpcval('hello world', 'int');
546 $s = $v->serialize();
547 $this->assertequals("<value><int>0</int></value>\n", $s);
550 public function testStructMemExists()
552 $v = php_xmlrpc_encode(array('hello' => 'world'));
553 $b = $v->structmemexists('hello');
554 $this->assertequals(true, $b);
555 $b = $v->structmemexists('world');
556 $this->assertequals(false, $b);
559 public function testNilvalue()
561 // default case: we do not accept nil values received
562 $v = new xmlrpcval('hello', 'null');
563 $r = new xmlrpcresp($v);
564 $s = $r->serialize();
565 $m = $this->newMsg('dummy');
566 $r = $m->parseresponse($s);
567 $this->assertequals(2, $r->faultCode());
568 // enable reception of nil values
569 $GLOBALS['xmlrpc_null_extension'] = true;
570 \PhpXmlRpc\PhpXmlRpc::importGlobals();
571 $r = $m->parseresponse($s);
573 $this->assertequals('null', $v->scalartyp());
574 // test with the apache version: EX:NIL
575 $GLOBALS['xmlrpc_null_apache_encoding'] = true;
576 \PhpXmlRpc\PhpXmlRpc::importGlobals();
578 $v = new xmlrpcval('hello', 'null');
579 $s = $v->serialize();
580 $this->assertequals(1, preg_match('#<value><ex:nil/></value>#', $s));
582 $r = new xmlrpcresp($v);
583 $s = $r->serialize();
584 $r = $m->parseresponse($s);
586 $this->assertequals('null', $v->scalartyp());
587 $GLOBALS['xmlrpc_null_extension'] = false;
588 \PhpXmlRpc\PhpXmlRpc::importGlobals();
589 $r = $m->parseresponse($s);
590 $this->assertequals(2, $r->faultCode());
593 public function testLocale()
595 $locale = setlocale(LC_NUMERIC, 0);
596 /// @todo on php 5.3/win setting locale to german does not seem to set decimal separator to comma...
597 if (setlocale(LC_NUMERIC, 'deu', 'de_DE@euro', 'de_DE', 'de', 'ge') !== false) {
598 $v = new xmlrpcval(1.1, 'double');
599 if (strpos($v->scalarval(), ',') == 1) {
600 $r = $v->serialize();
601 $this->assertequals(false, strpos($r, ','));
602 setlocale(LC_NUMERIC, $locale);
604 setlocale(LC_NUMERIC, $locale);
605 $this->markTestSkipped('did not find a locale which sets decimal separator to comma');
608 $this->markTestSkipped('did not find a locale which sets decimal separator to comma');
612 public function testArrayAccess()
614 $v1 = new xmlrpcval(array(new xmlrpcval('one'), new xmlrpcval('two')), 'array');
615 $this->assertequals(1, count($v1));
616 $out = array('me' => array(), 'mytype' => 2, '_php_class' => null);
618 foreach($v1 as $key => $val)
620 $this->assertArrayHasKey($key, $out);
621 $expected = $out[$key];
622 if (gettype($expected) == 'array') {
623 $this->assertequals('array', gettype($val));
625 $this->assertequals($expected, $val);
629 $v2 = new \PhpXmlRpc\Value(array(new \PhpXmlRpc\Value('one'), new \PhpXmlRpc\Value('two')), 'array');
630 $this->assertequals(2, count($v2));
631 $out = array(array('key' => 0, 'value' => 'object'), array('key' => 1, 'value' => 'object'));
633 foreach($v2 as $key => $val)
635 $expected = $out[$i];
636 $this->assertequals($expected['key'], $key);
637 $this->assertequals($expected['value'], gettype($val));
642 function testBigXML()
644 // nb: make sure that the serialized xml corresponding to this is > 10MB in size
646 for ($i = 0; $i < 500000; $i++ ) {
647 $data[] = 'hello world';
650 $encoder = new \PhpXmlRpc\Encoder();
651 $val = $encoder->encode($data);
652 $req = new \PhpXmlRpc\Request('test', array($val));
653 $xml = $req->serialize();
654 $parser = new \PhpXmlRpc\Helper\XMLParser();
655 $parser->parse($xml);
657 $this->assertequals(0, $parser->_xh['isf']);