Yaffs site version 1.1
[yaffs-website] / vendor / symfony / serializer / Tests / Encoder / XmlEncoderTest.php
1 <?php
2
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Symfony\Component\Serializer\Tests\Encoder;
13
14 use PHPUnit\Framework\TestCase;
15 use Symfony\Component\Serializer\Tests\Fixtures\Dummy;
16 use Symfony\Component\Serializer\Tests\Fixtures\NormalizableTraversableDummy;
17 use Symfony\Component\Serializer\Tests\Fixtures\ScalarDummy;
18 use Symfony\Component\Serializer\Encoder\XmlEncoder;
19 use Symfony\Component\Serializer\Serializer;
20 use Symfony\Component\Serializer\Normalizer\CustomNormalizer;
21 use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
22
23 class XmlEncoderTest extends TestCase
24 {
25     /**
26      * @var XmlEncoder
27      */
28     private $encoder;
29
30     private $exampleDateTimeString = '2017-02-19T15:16:08+0300';
31
32     protected function setUp()
33     {
34         $this->encoder = new XmlEncoder();
35         $serializer = new Serializer(array(new CustomNormalizer()), array('xml' => new XmlEncoder()));
36         $this->encoder->setSerializer($serializer);
37     }
38
39     public function testEncodeScalar()
40     {
41         $obj = new ScalarDummy();
42         $obj->xmlFoo = 'foo';
43
44         $expected = '<?xml version="1.0"?>'."\n".
45             '<response>foo</response>'."\n";
46
47         $this->assertEquals($expected, $this->encoder->encode($obj, 'xml'));
48     }
49
50     public function testSetRootNodeName()
51     {
52         $obj = new ScalarDummy();
53         $obj->xmlFoo = 'foo';
54
55         $this->encoder->setRootNodeName('test');
56         $expected = '<?xml version="1.0"?>'."\n".
57             '<test>foo</test>'."\n";
58
59         $this->assertEquals($expected, $this->encoder->encode($obj, 'xml'));
60     }
61
62     /**
63      * @expectedException        \Symfony\Component\Serializer\Exception\UnexpectedValueException
64      * @expectedExceptionMessage Document types are not allowed.
65      */
66     public function testDocTypeIsNotAllowed()
67     {
68         $this->encoder->decode('<?xml version="1.0"?><!DOCTYPE foo><foo></foo>', 'foo');
69     }
70
71     public function testAttributes()
72     {
73         $obj = new ScalarDummy();
74         $obj->xmlFoo = array(
75             'foo-bar' => array(
76                 '@id' => 1,
77                 '@name' => 'Bar',
78             ),
79             'Foo' => array(
80                 'Bar' => 'Test',
81                 '@Type' => 'test',
82             ),
83             'föo_bär' => 'a',
84             'Bar' => array(1, 2, 3),
85             'a' => 'b',
86         );
87         $expected = '<?xml version="1.0"?>'."\n".
88             '<response>'.
89             '<foo-bar id="1" name="Bar"/>'.
90             '<Foo Type="test"><Bar>Test</Bar></Foo>'.
91             '<föo_bär>a</föo_bär>'.
92             '<Bar>1</Bar>'.
93             '<Bar>2</Bar>'.
94             '<Bar>3</Bar>'.
95             '<a>b</a>'.
96             '</response>'."\n";
97         $this->assertEquals($expected, $this->encoder->encode($obj, 'xml'));
98     }
99
100     public function testElementNameValid()
101     {
102         $obj = new ScalarDummy();
103         $obj->xmlFoo = array(
104             'foo-bar' => 'a',
105             'foo_bar' => 'a',
106             'föo_bär' => 'a',
107         );
108
109         $expected = '<?xml version="1.0"?>'."\n".
110             '<response>'.
111             '<foo-bar>a</foo-bar>'.
112             '<foo_bar>a</foo_bar>'.
113             '<föo_bär>a</föo_bär>'.
114             '</response>'."\n";
115
116         $this->assertEquals($expected, $this->encoder->encode($obj, 'xml'));
117     }
118
119     public function testEncodeSimpleXML()
120     {
121         $xml = simplexml_load_string('<firstname>Peter</firstname>');
122         $array = array('person' => $xml);
123
124         $expected = '<?xml version="1.0"?>'."\n".
125             '<response><person><firstname>Peter</firstname></person></response>'."\n";
126
127         $this->assertEquals($expected, $this->encoder->encode($array, 'xml'));
128     }
129
130     public function testEncodeXmlAttributes()
131     {
132         $xml = simplexml_load_string('<firstname>Peter</firstname>');
133         $array = array('person' => $xml);
134
135         $expected = '<?xml version="1.1" encoding="utf-8" standalone="yes"?>'."\n".
136             '<response><person><firstname>Peter</firstname></person></response>'."\n";
137
138         $context = array(
139             'xml_version' => '1.1',
140             'xml_encoding' => 'utf-8',
141             'xml_standalone' => true,
142         );
143
144         $this->assertSame($expected, $this->encoder->encode($array, 'xml', $context));
145     }
146
147     public function testContext()
148     {
149         $array = array('person' => array('name' => 'George Abitbol'));
150         $expected = <<<'XML'
151 <?xml version="1.0"?>
152 <response>
153   <person>
154     <name>George Abitbol</name>
155   </person>
156 </response>
157
158 XML;
159
160         $context = array(
161             'xml_format_output' => true,
162         );
163
164         $this->assertSame($expected, $this->encoder->encode($array, 'xml', $context));
165     }
166
167     public function testEncodeScalarRootAttributes()
168     {
169         $array = array(
170           '#' => 'Paul',
171           '@gender' => 'm',
172         );
173
174         $expected = '<?xml version="1.0"?>'."\n".
175             '<response gender="m">Paul</response>'."\n";
176
177         $this->assertEquals($expected, $this->encoder->encode($array, 'xml'));
178     }
179
180     public function testEncodeRootAttributes()
181     {
182         $array = array(
183           'firstname' => 'Paul',
184           '@gender' => 'm',
185         );
186
187         $expected = '<?xml version="1.0"?>'."\n".
188             '<response gender="m"><firstname>Paul</firstname></response>'."\n";
189
190         $this->assertEquals($expected, $this->encoder->encode($array, 'xml'));
191     }
192
193     public function testEncodeCdataWrapping()
194     {
195         $array = array(
196           'firstname' => 'Paul <or Me>',
197         );
198
199         $expected = '<?xml version="1.0"?>'."\n".
200             '<response><firstname><![CDATA[Paul <or Me>]]></firstname></response>'."\n";
201
202         $this->assertEquals($expected, $this->encoder->encode($array, 'xml'));
203     }
204
205     public function testEncodeScalarWithAttribute()
206     {
207         $array = array(
208             'person' => array('@gender' => 'M', '#' => 'Peter'),
209         );
210
211         $expected = '<?xml version="1.0"?>'."\n".
212             '<response><person gender="M">Peter</person></response>'."\n";
213
214         $this->assertEquals($expected, $this->encoder->encode($array, 'xml'));
215     }
216
217     public function testDecodeScalar()
218     {
219         $source = '<?xml version="1.0"?>'."\n".
220             '<response>foo</response>'."\n";
221
222         $this->assertEquals('foo', $this->encoder->decode($source, 'xml'));
223     }
224
225     public function testDecodeBigDigitAttributes()
226     {
227         $source = <<<XML
228 <?xml version="1.0"?>
229 <document index="182077241760011681341821060401202210011000045913000000017100">Name</document>
230 XML;
231
232         $this->assertSame(array('@index' => 182077241760011681341821060401202210011000045913000000017100, '#' => 'Name'), $this->encoder->decode($source, 'xml'));
233     }
234
235     public function testDecodeNegativeIntAttribute()
236     {
237         $source = <<<XML
238 <?xml version="1.0"?>
239 <document index="-1234">Name</document>
240 XML;
241
242         $this->assertSame(array('@index' => -1234, '#' => 'Name'), $this->encoder->decode($source, 'xml'));
243     }
244
245     public function testDecodeFloatAttribute()
246     {
247         $source = <<<XML
248 <?xml version="1.0"?>
249 <document index="-12.11">Name</document>
250 XML;
251
252         $this->assertSame(array('@index' => -12.11, '#' => 'Name'), $this->encoder->decode($source, 'xml'));
253     }
254
255     public function testDecodeNegativeFloatAttribute()
256     {
257         $source = <<<XML
258 <?xml version="1.0"?>
259 <document index="-12.11">Name</document>
260 XML;
261
262         $this->assertSame(array('@index' => -12.11, '#' => 'Name'), $this->encoder->decode($source, 'xml'));
263     }
264
265     public function testEncode()
266     {
267         $source = $this->getXmlSource();
268         $obj = $this->getObject();
269
270         $this->assertEquals($source, $this->encoder->encode($obj, 'xml'));
271     }
272
273     public function testEncodeWithNamespace()
274     {
275         $source = $this->getNamespacedXmlSource();
276         $array = $this->getNamespacedArray();
277
278         $this->assertEquals($source, $this->encoder->encode($array, 'xml'));
279     }
280
281     public function testEncodeSerializerXmlRootNodeNameOption()
282     {
283         $options = array('xml_root_node_name' => 'test');
284         $this->encoder = new XmlEncoder();
285         $serializer = new Serializer(array(), array('xml' => new XmlEncoder()));
286         $this->encoder->setSerializer($serializer);
287
288         $array = array(
289             'person' => array('@gender' => 'M', '#' => 'Peter'),
290         );
291
292         $expected = '<?xml version="1.0"?>'."\n".
293             '<test><person gender="M">Peter</person></test>'."\n";
294
295         $this->assertEquals($expected, $serializer->serialize($array, 'xml', $options));
296     }
297
298     public function testEncodeTraversableWhenNormalizable()
299     {
300         $this->encoder = new XmlEncoder();
301         $serializer = new Serializer(array(new CustomNormalizer()), array('xml' => new XmlEncoder()));
302         $this->encoder->setSerializer($serializer);
303
304         $expected = <<<'XML'
305 <?xml version="1.0"?>
306 <response><foo>normalizedFoo</foo><bar>normalizedBar</bar></response>
307
308 XML;
309
310         $this->assertEquals($expected, $serializer->serialize(new NormalizableTraversableDummy(), 'xml'));
311     }
312
313     public function testDecode()
314     {
315         $source = $this->getXmlSource();
316         $obj = $this->getObject();
317
318         $this->assertEquals(get_object_vars($obj), $this->encoder->decode($source, 'xml'));
319     }
320
321     public function testDecodeCdataWrapping()
322     {
323         $expected = array(
324             'firstname' => 'Paul <or Me>',
325         );
326
327         $xml = '<?xml version="1.0"?>'."\n".
328             '<response><firstname><![CDATA[Paul <or Me>]]></firstname></response>'."\n";
329
330         $this->assertEquals($expected, $this->encoder->decode($xml, 'xml'));
331     }
332
333     public function testDecodeCdataWrappingAndWhitespace()
334     {
335         $expected = array(
336             'firstname' => 'Paul <or Me>',
337         );
338
339         $xml = '<?xml version="1.0"?>'."\n".
340             '<response><firstname>'."\n".
341                 '<![CDATA[Paul <or Me>]]></firstname></response>'."\n";
342
343         $this->assertEquals($expected, $this->encoder->decode($xml, 'xml'));
344     }
345
346     public function testDecodeWithNamespace()
347     {
348         $source = $this->getNamespacedXmlSource();
349         $array = $this->getNamespacedArray();
350
351         $this->assertEquals($array, $this->encoder->decode($source, 'xml'));
352     }
353
354     public function testDecodeScalarWithAttribute()
355     {
356         $source = '<?xml version="1.0"?>'."\n".
357             '<response><person gender="M">Peter</person></response>'."\n";
358
359         $expected = array(
360             'person' => array('@gender' => 'M', '#' => 'Peter'),
361         );
362
363         $this->assertEquals($expected, $this->encoder->decode($source, 'xml'));
364     }
365
366     public function testDecodeScalarRootAttributes()
367     {
368         $source = '<?xml version="1.0"?>'."\n".
369             '<person gender="M">Peter</person>'."\n";
370
371         $expected = array(
372             '#' => 'Peter',
373             '@gender' => 'M',
374         );
375
376         $this->assertEquals($expected, $this->encoder->decode($source, 'xml'));
377     }
378
379     public function testDecodeRootAttributes()
380     {
381         $source = '<?xml version="1.0"?>'."\n".
382             '<person gender="M"><firstname>Peter</firstname><lastname>Mac Calloway</lastname></person>'."\n";
383
384         $expected = array(
385             'firstname' => 'Peter',
386             'lastname' => 'Mac Calloway',
387             '@gender' => 'M',
388         );
389
390         $this->assertEquals($expected, $this->encoder->decode($source, 'xml'));
391     }
392
393     public function testDecodeArray()
394     {
395         $source = '<?xml version="1.0"?>'."\n".
396             '<response>'.
397             '<people>'.
398             '<person><firstname>Benjamin</firstname><lastname>Alexandre</lastname></person>'.
399             '<person><firstname>Damien</firstname><lastname>Clay</lastname></person>'.
400             '</people>'.
401             '</response>'."\n";
402
403         $expected = array(
404             'people' => array('person' => array(
405                 array('firstname' => 'Benjamin', 'lastname' => 'Alexandre'),
406                 array('firstname' => 'Damien', 'lastname' => 'Clay'),
407             )),
408         );
409
410         $this->assertEquals($expected, $this->encoder->decode($source, 'xml'));
411     }
412
413     public function testDecodeXMLWithProcessInstruction()
414     {
415         $source = <<<'XML'
416 <?xml version="1.0"?>
417 <?xml-stylesheet type="text/xsl" href="/xsl/xmlverbatimwrapper.xsl"?>
418     <?display table-view?>
419     <?sort alpha-ascending?>
420     <response>
421         <foo>foo</foo>
422         <?textinfo whitespace is allowed ?>
423         <bar>a</bar>
424         <bar>b</bar>
425         <baz>
426             <key>val</key>
427             <key2>val</key2>
428             <item key="A B">bar</item>
429             <item>
430                 <title>title1</title>
431             </item>
432             <?item ignore-title ?>
433             <item>
434                 <title>title2</title>
435             </item>
436             <Barry>
437                 <FooBar id="1">
438                     <Baz>Ed</Baz>
439                 </FooBar>
440             </Barry>
441         </baz>
442         <qux>1</qux>
443     </response>
444     <?instruction <value> ?>
445 XML;
446         $obj = $this->getObject();
447
448         $this->assertEquals(get_object_vars($obj), $this->encoder->decode($source, 'xml'));
449     }
450
451     public function testDecodeIgnoreWhiteSpace()
452     {
453         $source = <<<'XML'
454 <?xml version="1.0"?>
455 <people>
456     <person>
457         <firstname>Benjamin</firstname>
458         <lastname>Alexandre</lastname>
459     </person>
460     <person>
461         <firstname>Damien</firstname>
462         <lastname>Clay</lastname>
463     </person>
464 </people>
465 XML;
466         $expected = array('person' => array(
467             array('firstname' => 'Benjamin', 'lastname' => 'Alexandre'),
468             array('firstname' => 'Damien', 'lastname' => 'Clay'),
469         ));
470
471         $this->assertEquals($expected, $this->encoder->decode($source, 'xml'));
472     }
473
474     public function testDecodeWithoutItemHash()
475     {
476         $obj = new ScalarDummy();
477         $obj->xmlFoo = array(
478             'foo-bar' => array(
479                 '@key' => 'value',
480                 'item' => array('@key' => 'key', 'key-val' => 'val'),
481             ),
482             'Foo' => array(
483                 'Bar' => 'Test',
484                 '@Type' => 'test',
485             ),
486             'föo_bär' => 'a',
487             'Bar' => array(1, 2, 3),
488             'a' => 'b',
489         );
490         $expected = array(
491             'foo-bar' => array(
492                 '@key' => 'value',
493                 'key' => array('@key' => 'key', 'key-val' => 'val'),
494             ),
495             'Foo' => array(
496                 'Bar' => 'Test',
497                 '@Type' => 'test',
498             ),
499             'föo_bär' => 'a',
500             'Bar' => array(1, 2, 3),
501             'a' => 'b',
502         );
503         $xml = $this->encoder->encode($obj, 'xml');
504         $this->assertEquals($expected, $this->encoder->decode($xml, 'xml'));
505     }
506
507     /**
508      * @expectedException \Symfony\Component\Serializer\Exception\UnexpectedValueException
509      */
510     public function testDecodeInvalidXml()
511     {
512         $this->encoder->decode('<?xml version="1.0"?><invalid><xml>', 'xml');
513     }
514
515     /**
516      * @expectedException \Symfony\Component\Serializer\Exception\UnexpectedValueException
517      */
518     public function testPreventsComplexExternalEntities()
519     {
520         $this->encoder->decode('<?xml version="1.0"?><!DOCTYPE scan[<!ENTITY test SYSTEM "php://filter/read=convert.base64-encode/resource=XmlEncoderTest.php">]><scan>&test;</scan>', 'xml');
521     }
522
523     public function testDecodeEmptyXml()
524     {
525         if (method_exists($this, 'expectException')) {
526             $this->expectException('Symfony\Component\Serializer\Exception\UnexpectedValueException');
527             $this->expectExceptionMessage('Invalid XML data, it can not be empty.');
528         } else {
529             $this->setExpectedException('Symfony\Component\Serializer\Exception\UnexpectedValueException', 'Invalid XML data, it can not be empty.');
530         }
531         $this->encoder->decode(' ', 'xml');
532     }
533
534     protected function getXmlSource()
535     {
536         return '<?xml version="1.0"?>'."\n".
537             '<response>'.
538             '<foo>foo</foo>'.
539             '<bar>a</bar><bar>b</bar>'.
540             '<baz><key>val</key><key2>val</key2><item key="A B">bar</item>'.
541             '<item><title>title1</title></item><item><title>title2</title></item>'.
542             '<Barry><FooBar id="1"><Baz>Ed</Baz></FooBar></Barry></baz>'.
543             '<qux>1</qux>'.
544             '</response>'."\n";
545     }
546
547     protected function getNamespacedXmlSource()
548     {
549         return '<?xml version="1.0"?>'."\n".
550             '<response xmlns="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app" xmlns:media="http://search.yahoo.com/mrss/" xmlns:gd="http://schemas.google.com/g/2005" xmlns:yt="http://gdata.youtube.com/schemas/2007">'.
551             '<qux>1</qux>'.
552             '<app:foo>foo</app:foo>'.
553             '<yt:bar>a</yt:bar><yt:bar>b</yt:bar>'.
554             '<media:baz><media:key>val</media:key><media:key2>val</media:key2><item key="A B">bar</item>'.
555             '<item><title>title1</title></item><item><title>title2</title></item>'.
556             '<Barry size="large"><FooBar gd:id="1"><Baz>Ed</Baz></FooBar></Barry></media:baz>'.
557             '</response>'."\n";
558     }
559
560     protected function getNamespacedArray()
561     {
562         return array(
563             '@xmlns' => 'http://www.w3.org/2005/Atom',
564             '@xmlns:app' => 'http://www.w3.org/2007/app',
565             '@xmlns:media' => 'http://search.yahoo.com/mrss/',
566             '@xmlns:gd' => 'http://schemas.google.com/g/2005',
567             '@xmlns:yt' => 'http://gdata.youtube.com/schemas/2007',
568             'qux' => '1',
569             'app:foo' => 'foo',
570             'yt:bar' => array('a', 'b'),
571             'media:baz' => array(
572                 'media:key' => 'val',
573                 'media:key2' => 'val',
574                 'A B' => 'bar',
575                 'item' => array(
576                     array(
577                         'title' => 'title1',
578                     ),
579                     array(
580                         'title' => 'title2',
581                     ),
582                 ),
583                 'Barry' => array(
584                     '@size' => 'large',
585                     'FooBar' => array(
586                         'Baz' => 'Ed',
587                         '@gd:id' => 1,
588                     ),
589                 ),
590             ),
591         );
592     }
593
594     protected function getObject()
595     {
596         $obj = new Dummy();
597         $obj->foo = 'foo';
598         $obj->bar = array('a', 'b');
599         $obj->baz = array('key' => 'val', 'key2' => 'val', 'A B' => 'bar', 'item' => array(array('title' => 'title1'), array('title' => 'title2')), 'Barry' => array('FooBar' => array('Baz' => 'Ed', '@id' => 1)));
600         $obj->qux = '1';
601
602         return $obj;
603     }
604
605     public function testEncodeXmlWithBoolValue()
606     {
607         $expectedXml = <<<'XML'
608 <?xml version="1.0"?>
609 <response><foo>1</foo><bar>0</bar></response>
610
611 XML;
612
613         $actualXml = $this->encoder->encode(array('foo' => true, 'bar' => false), 'xml');
614
615         $this->assertEquals($expectedXml, $actualXml);
616     }
617
618     public function testEncodeXmlWithDateTimeObjectValue()
619     {
620         $xmlEncoder = $this->createXmlEncoderWithDateTimeNormalizer();
621
622         $actualXml = $xmlEncoder->encode(array('dateTime' => new \DateTime($this->exampleDateTimeString)), 'xml');
623
624         $this->assertEquals($this->createXmlWithDateTime(), $actualXml);
625     }
626
627     public function testEncodeXmlWithDateTimeObjectField()
628     {
629         $xmlEncoder = $this->createXmlEncoderWithDateTimeNormalizer();
630
631         $actualXml = $xmlEncoder->encode(array('foo' => array('@dateTime' => new \DateTime($this->exampleDateTimeString))), 'xml');
632
633         $this->assertEquals($this->createXmlWithDateTimeField(), $actualXml);
634     }
635
636     /**
637      * @return XmlEncoder
638      */
639     private function createXmlEncoderWithDateTimeNormalizer()
640     {
641         $encoder = new XmlEncoder();
642         $serializer = new Serializer(array($this->createMockDateTimeNormalizer()), array('xml' => new XmlEncoder()));
643         $encoder->setSerializer($serializer);
644
645         return $encoder;
646     }
647
648     /**
649      * @return \PHPUnit_Framework_MockObject_MockObject|NormalizerInterface
650      */
651     private function createMockDateTimeNormalizer()
652     {
653         $mock = $this->getMockBuilder('\Symfony\Component\Serializer\Normalizer\CustomNormalizer')->getMock();
654
655         $mock
656             ->expects($this->once())
657             ->method('normalize')
658             ->with(new \DateTime($this->exampleDateTimeString), 'xml', array())
659             ->willReturn($this->exampleDateTimeString);
660
661         $mock
662             ->expects($this->once())
663             ->method('supportsNormalization')
664             ->with(new \DateTime($this->exampleDateTimeString), 'xml')
665             ->willReturn(true);
666
667         return $mock;
668     }
669
670     /**
671      * @return string
672      */
673     private function createXmlWithDateTime()
674     {
675         return sprintf('<?xml version="1.0"?>
676 <response><dateTime>%s</dateTime></response>
677 ', $this->exampleDateTimeString);
678     }
679
680     /**
681      * @return string
682      */
683     private function createXmlWithDateTimeField()
684     {
685         return sprintf('<?xml version="1.0"?>
686 <response><foo dateTime="%s"/></response>
687 ', $this->exampleDateTimeString);
688     }
689 }