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';
13 * Tests involving parsing of xml and handling of xmlrpc values
15 class ParsingBugsTests extends PhpXmlRpc_PolyfillTestCase
17 public $args = array();
19 protected function set_up()
21 $this->args = argParser::getArgs();
22 if ($this->args['DEBUG'] == 1)
26 protected function tear_down()
28 if ($this->args['DEBUG'] != 1)
30 $out = ob_get_clean();
31 $status = $this->getStatus();
32 if ($status == PHPUnit_Runner_BaseTestRunner::STATUS_ERROR
33 || $status == PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE) {
38 protected function newMsg($methodName, $params = array())
40 $msg = new xmlrpcmsg($methodName, $params);
41 $msg->setDebug($this->args['DEBUG']);
45 public function testMinusOneString()
47 $v = new xmlrpcval('-1');
48 $u = new xmlrpcval('-1', 'string');
49 $t = new xmlrpcval(-1, 'string');
50 $this->assertEquals($v->scalarval(), $u->scalarval());
51 $this->assertEquals($v->scalarval(), $t->scalarval());
55 * This looks funny, and we might call it a bug. But we strive for 100 backwards compat...
57 public function testMinusOneInt()
60 $v = new xmlrpcval(-1);
61 $this->assertEquals($u->scalarval(), $v->scalarval());
64 public function testUnicodeInMemberName()
66 $str = "G" . chr(252) . "nter, El" . chr(232) . "ne";
67 $v = array($str => new xmlrpcval(1));
68 $r = new xmlrpcresp(new xmlrpcval($v, 'struct'));
70 $m = $this->newMsg('dummy');
71 $r = $m->parseResponse($r);
73 $this->assertEquals(true, $v->structmemexists($str));
76 public function testUnicodeInErrorString()
78 $response = utf8_encode(
79 '<?xml version="1.0"?>
81 <!-- found by G. Giunta, covers what happens when lib receives UTF8 chars in response text and comments -->
82 <!-- ' . chr(224) . chr(252) . chr(232) . 'àüè -->
88 <name>faultCode</name>
89 <value><int>888</int></value>
92 <name>faultString</name>
93 <value><string>' . chr(224) . chr(252) . chr(232) . 'àüè</string></value>
99 $m = $this->newMsg('dummy');
100 $r = $m->parseResponse($response);
101 $v = $r->faultString();
102 $this->assertEquals(chr(224) . chr(252) . chr(232) . chr(224) . chr(252) . chr(232), $v);
105 public function testValidNumbers()
107 $m = $this->newMsg('dummy');
109 '<?xml version="1.0"?>
116 <name>integer1</name>
117 <value><int>01</int></value>
120 <name>integer2</name>
121 <value><int>+1</int></value>
124 <name>integer3</name>
125 <value><i4>1</i4></value>
129 <value><double>01.10</double></value>
133 <value><double>+1.10</double></value>
137 <value><double>-1.10e2</double></value>
144 $r = $m->parseResponse($fp);
146 $s = $v->structmem('integer1');
147 $t = $v->structmem('integer2');
148 $u = $v->structmem('integer3');
149 $x = $v->structmem('float1');
150 $y = $v->structmem('float2');
151 $z = $v->structmem('float3');
152 $this->assertEquals(1, $s->scalarval());
153 $this->assertEquals(1, $t->scalarval());
154 $this->assertEquals(1, $u->scalarval());
156 $this->assertEquals(1.1, $x->scalarval());
157 $this->assertEquals(1.1, $y->scalarval());
158 $this->assertEquals(-110.0, $z->scalarval());
161 public function testI8()
163 if (PHP_INT_SIZE == 4 ) {
164 $this->markTestSkipped('Can not test i8 as php is compiled in 32 bit mode');
168 $m = $this->newMsg('dummy');
170 '<?xml version="1.0"?>
177 <name>integer1</name>
178 <value><i8>1</i8></value>
185 $r = $m->parseResponse($fp);
187 $s = $v->structmem('integer1');
188 $this->assertEquals(1, $s->scalarval());
191 public function testAddScalarToStruct()
193 $v = new xmlrpcval(array('a' => 'b'), 'struct');
194 // use @ operator in case error_log gets on screen
195 $r = @$v->addscalar('c');
196 $this->assertEquals(0, $r);
199 public function testAddStructToStruct()
201 $v = new xmlrpcval(array('a' => new xmlrpcval('b')), 'struct');
202 $r = $v->addstruct(array('b' => new xmlrpcval('c')));
203 $this->assertEquals(2, $v->structsize());
204 $this->assertEquals(1, $r);
205 $r = $v->addstruct(array('b' => new xmlrpcval('b')));
206 $this->assertEquals(2, $v->structsize());
209 public function testAddArrayToArray()
211 $v = new xmlrpcval(array(new xmlrpcval('a'), new xmlrpcval('b')), 'array');
212 $r = $v->addarray(array(new xmlrpcval('b'), new xmlrpcval('c')));
213 $this->assertEquals(4, $v->arraysize());
214 $this->assertEquals(1, $r);
217 public function testEncodeArray()
220 $v = php_xmlrpc_encode($r);
221 $this->assertEquals('array', $v->kindof());
224 public function testEncodeRecursive()
226 $v = php_xmlrpc_encode(php_xmlrpc_encode('a simple string'));
227 $this->assertEquals('scalar', $v->kindof());
230 public function testBrokenRequests()
232 $s = new xmlrpc_server();
233 // omitting the 'params' tag: not tolerated by the lib anymore
234 $f = '<?xml version="1.0"?>
236 <methodName>system.methodHelp</methodName>
238 <value><string>system.methodHelp</string></value>
241 $r = $s->parserequest($f);
242 $this->assertEquals(15, $r->faultCode());
243 // omitting a 'param' tag
244 $f = '<?xml version="1.0"?>
246 <methodName>system.methodHelp</methodName>
248 <value><string>system.methodHelp</string></value>
251 $r = $s->parserequest($f);
252 $this->assertEquals(15, $r->faultCode());
253 // omitting a 'value' tag
254 $f = '<?xml version="1.0"?>
256 <methodName>system.methodHelp</methodName>
258 <param><string>system.methodHelp</string></param>
261 $r = $s->parserequest($f);
262 $this->assertEquals(15, $r->faultCode());
265 public function testBrokenResponses()
267 $m = $this->newMsg('dummy');
268 // omitting the 'params' tag: no more tolerated by the lib...
269 $f = '<?xml version="1.0"?>
272 <value><string>system.methodHelp</string></value>
275 $r = $m->parseResponse($f);
276 $this->assertEquals(2, $r->faultCode());
277 // omitting the 'param' tag: no more tolerated by the lib...
278 $f = '<?xml version="1.0"?>
281 <value><string>system.methodHelp</string></value>
284 $r = $m->parseResponse($f);
285 $this->assertEquals(2, $r->faultCode());
286 // omitting a 'value' tag: KO
287 $f = '<?xml version="1.0"?>
290 <param><string>system.methodHelp</string></param>
293 $r = $m->parseResponse($f);
294 $this->assertEquals(2, $r->faultCode());
297 public function testBuggyHttp()
299 $s = $this->newMsg('dummy');
300 $f = 'HTTP/1.1 100 Welcome to the jungle
303 X-Content-Marx-Brothers: Harpo
305 Content-Length: who knows?
309 <?xml version="1.0"?>
310 <!-- First of all, let\'s check out if the lib properly handles a commented </methodResponse> tag... -->
311 <methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
312 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>hello world. 2 newlines follow
315 and there they were.</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>
316 <script type="text\javascript">document.write(\'Hello, my name is added nag, I\\\'m happy to serve your content for free\');</script>
318 $r = $s->parseResponse($f);
320 $s = $v->structmem('content');
321 $this->assertEquals("hello world. 2 newlines follow\n\n\nand there they were.", $s->scalarval());
324 public function testStringBug()
326 $s = $this->newMsg('dummy');
327 $f = '<?xml version="1.0"?>
329 <!-- found by 2z69xks7bpy001@sneakemail.com, amongst others
330 covers what happens when there\'s character data after </string>
331 and before </value> -->
344 <name>sessionID</name>
346 <string>S300510007I</string>
354 $r = $s->parseResponse($f);
356 $s = $v->structmem('sessionID');
357 $this->assertEquals('S300510007I', $s->scalarval());
360 public function testWhiteSpace()
362 $s = $this->newMsg('dummy');
363 $f = '<?xml version="1.0"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
364 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>hello world. 2 newlines follow
367 and there they were.</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>
369 $r = $s->parseResponse($f);
371 $s = $v->structmem('content');
372 $this->assertEquals("hello world. 2 newlines follow\n\n\nand there they were.", $s->scalarval());
375 public function testDoubleDataInArrayTag()
377 $s = $this->newMsg('dummy');
378 $f = '<?xml version="1.0"?><methodResponse><params><param><value><array>
381 </array></value></param></params></methodResponse>
383 $r = $s->parseResponse($f);
384 $v = $r->faultCode();
385 $this->assertEquals(2, $v);
386 $f = '<?xml version="1.0"?><methodResponse><params><param><value><array>
387 <data><value>Hello world</value></data>
389 </array></value></param></params></methodResponse>
391 $r = $s->parseResponse($f);
392 $v = $r->faultCode();
393 $this->assertEquals(2, $v);
396 public function testDoubleStuffInValueTag()
398 $s = $this->newMsg('dummy');
399 $f = '<?xml version="1.0"?><methodResponse><params><param><value>
400 <string>hello world</string>
401 <array><data></data></array>
402 </value></param></params></methodResponse>
404 $r = $s->parseResponse($f);
405 $v = $r->faultCode();
406 $this->assertEquals(2, $v);
407 $f = '<?xml version="1.0"?><methodResponse><params><param><value>
408 <string>hello</string>
409 <string>world</string>
410 </value></param></params></methodResponse>
412 $r = $s->parseResponse($f);
413 $v = $r->faultCode();
414 $this->assertEquals(2, $v);
415 $f = '<?xml version="1.0"?><methodResponse><params><param><value>
416 <string>hello</string>
417 <struct><member><name>hello><value>world</value></member></struct>
418 </value></param></params></methodResponse>
420 $r = $s->parseResponse($f);
421 $v = $r->faultCode();
422 $this->assertEquals(2, $v);
425 public function testAutodecodeResponse()
427 $s = $this->newMsg('dummy');
428 $f = '<?xml version="1.0"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
429 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>hello world. 2 newlines follow
432 and there they were.</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>
434 $r = $s->parseResponse($f, true, 'phpvals');
437 $this->assertEquals("hello world. 2 newlines follow\n\n\nand there they were.", $s);
440 public function testNoDecodeResponse()
442 $s = $this->newMsg('dummy');
443 $f = '<?xml version="1.0"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
444 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>hello world. 2 newlines follow
447 and there they were.</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>';
448 $r = $s->parseResponse($f, true, 'xml');
450 $this->assertEquals($f, $v);
453 public function testAutoCoDec()
455 $data1 = array(1, 1.0, 'hello world', true, '20051021T23:43:00', -1, 11.0, '~!@#$%^&*()_+|', false, '20051021T23:43:00');
456 $data2 = array('zero' => $data1, 'one' => $data1, 'two' => $data1, 'three' => $data1, 'four' => $data1, 'five' => $data1, 'six' => $data1, 'seven' => $data1, 'eight' => $data1, 'nine' => $data1);
457 $data = array($data2, $data2, $data2, $data2, $data2, $data2, $data2, $data2, $data2, $data2);
458 //$keys = array('zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine');
459 $v1 = php_xmlrpc_encode($data, array('auto_dates'));
460 $v2 = php_xmlrpc_decode_xml($v1->serialize());
461 $this->assertEquals($v1, $v2);
462 $r1 = new PhpXmlRpc\Response($v1);
463 $r2 = php_xmlrpc_decode_xml($r1->serialize());
464 $r2->serialize(); // needed to set internal member payload
465 $this->assertEquals($r1, $r2);
466 $m1 = new PhpXmlRpc\Request('hello dolly', array($v1));
467 $m2 = php_xmlrpc_decode_xml($m1->serialize());
468 $m2->serialize(); // needed to set internal member payload
469 $this->assertEquals($m1, $m2);
472 public function testUTF8Request()
474 $sendstring = 'κόσμε'; // Greek word 'kosme'. NB: NOT a valid ISO8859 string!
475 $GLOBALS['xmlrpc_internalencoding'] = 'UTF-8';
476 \PhpXmlRpc\PhpXmlRpc::importGlobals();
477 $f = new xmlrpcval($sendstring, 'string');
478 $v = $f->serialize();
479 $this->assertEquals("<value><string>κόσμε</string></value>\n", $v);
480 $GLOBALS['xmlrpc_internalencoding'] = 'ISO-8859-1';
481 \PhpXmlRpc\PhpXmlRpc::importGlobals();
484 public function testUTF8Response()
486 $string = chr(224) . chr(252) . chr(232);
488 $s = $this->newMsg('dummy');
489 $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>
490 <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>
492 $r = $s->parseResponse($f, false, 'phpvals');
495 $this->assertEquals($string, $v);
497 $f = '<?xml version="1.0" encoding="UTF-8"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
498 <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>
500 $r = $s->parseResponse($f, false, 'phpvals');
503 $this->assertEquals($string, $v);
505 $r = php_xmlrpc_decode_xml($f);
507 $v = $v->structmem('content')->scalarval();
508 $this->assertEquals($string, $v);
511 public function testLatin1Response()
513 $string = chr(224) . chr(252) . chr(232);
515 $s = $this->newMsg('dummy');
516 $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>
517 <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>
519 $r = $s->parseResponse($f, false, 'phpvals');
522 $this->assertEquals($string, $v);
524 $f = '<?xml version="1.0" encoding="ISO-8859-1"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
525 <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>
527 $r = $s->parseResponse($f, false, 'phpvals');
530 $this->assertEquals($string, $v);
532 $r = php_xmlrpc_decode_xml($f);
534 $v = $v->structmem('content')->scalarval();
535 $this->assertEquals($string, $v);
538 public function testUTF8IntString()
540 $v = new xmlrpcval(100, 'int');
541 $s = $v->serialize('UTF-8');
542 $this->assertequals("<value><int>100</int></value>\n", $s);
545 public function testStringInt()
547 $v = new xmlrpcval('hello world', 'int');
548 $s = $v->serialize();
549 $this->assertequals("<value><int>0</int></value>\n", $s);
552 public function testStructMemExists()
554 $v = php_xmlrpc_encode(array('hello' => 'world'));
555 $b = $v->structmemexists('hello');
556 $this->assertequals(true, $b);
557 $b = $v->structmemexists('world');
558 $this->assertequals(false, $b);
561 public function testNilvalue()
563 // default case: we do not accept nil values received
564 $v = new xmlrpcval('hello', 'null');
565 $r = new xmlrpcresp($v);
566 $s = $r->serialize();
567 $m = $this->newMsg('dummy');
568 $r = $m->parseresponse($s);
569 $this->assertequals(2, $r->faultCode());
570 // enable reception of nil values
571 $GLOBALS['xmlrpc_null_extension'] = true;
572 \PhpXmlRpc\PhpXmlRpc::importGlobals();
573 $r = $m->parseresponse($s);
575 $this->assertequals('null', $v->scalartyp());
576 // test with the apache version: EX:NIL
577 $GLOBALS['xmlrpc_null_apache_encoding'] = true;
578 \PhpXmlRpc\PhpXmlRpc::importGlobals();
580 $v = new xmlrpcval('hello', 'null');
581 $s = $v->serialize();
582 $this->assertequals(1, preg_match('#<value><ex:nil/></value>#', $s));
584 $r = new xmlrpcresp($v);
585 $s = $r->serialize();
586 $r = $m->parseresponse($s);
588 $this->assertequals('null', $v->scalartyp());
589 $GLOBALS['xmlrpc_null_extension'] = false;
590 \PhpXmlRpc\PhpXmlRpc::importGlobals();
591 $r = $m->parseresponse($s);
592 $this->assertequals(2, $r->faultCode());
595 public function testLocale()
597 $locale = setlocale(LC_NUMERIC, 0);
598 /// @todo on php 5.3/win setting locale to german does not seem to set decimal separator to comma...
599 if (setlocale(LC_NUMERIC, 'deu', 'de_DE@euro', 'de_DE', 'de', 'ge') !== false) {
600 $v = new xmlrpcval(1.1, 'double');
601 if (strpos($v->scalarval(), ',') == 1) {
602 $r = $v->serialize();
603 $this->assertequals(false, strpos($r, ','));
604 setlocale(LC_NUMERIC, $locale);
606 setlocale(LC_NUMERIC, $locale);
607 $this->markTestSkipped('did not find a locale which sets decimal separator to comma');
610 $this->markTestSkipped('did not find a locale which sets decimal separator to comma');
614 public function testArrayAccess()
616 $v1 = new xmlrpcval(array(new xmlrpcval('one'), new xmlrpcval('two')), 'array');
617 $this->assertequals(1, count($v1));
618 $out = array('me' => array(), 'mytype' => 2, '_php_class' => null);
620 foreach($v1 as $key => $val)
622 $this->assertArrayHasKey($key, $out);
623 $expected = $out[$key];
624 if (gettype($expected) == 'array') {
625 $this->assertequals('array', gettype($val));
627 $this->assertequals($expected, $val);
631 $v2 = new \PhpXmlRpc\Value(array(new \PhpXmlRpc\Value('one'), new \PhpXmlRpc\Value('two')), 'array');
632 $this->assertequals(2, count($v2));
633 $out = array(array('key' => 0, 'value' => 'object'), array('key' => 1, 'value' => 'object'));
635 foreach($v2 as $key => $val)
637 $expected = $out[$i];
638 $this->assertequals($expected['key'], $key);
639 $this->assertequals($expected['value'], gettype($val));
644 function testBigXML()
646 // nb: make sure that the serialized xml corresponding to this is > 10MB in size
648 for ($i = 0; $i < 500000; $i++ ) {
649 $data[] = 'hello world';
652 $encoder = new \PhpXmlRpc\Encoder();
653 $val = $encoder->encode($data);
654 $req = new \PhpXmlRpc\Request('test', array($val));
655 $xml = $req->serialize();
656 $parser = new \PhpXmlRpc\Helper\XMLParser();
657 $parser->parse($xml);
659 $this->assertequals(0, $parser->_xh['isf']);