1<?php 2 3declare(strict_types=1); 4 5namespace JMS\Serializer\Tests; 6 7use JMS\Serializer\DeserializationContext; 8use JMS\Serializer\Expression\ExpressionEvaluator; 9use JMS\Serializer\Handler\HandlerRegistry; 10use JMS\Serializer\SerializationContext; 11use JMS\Serializer\SerializerBuilder; 12use JMS\Serializer\Tests\Fixtures\PersonSecret; 13use JMS\Serializer\Tests\Fixtures\PersonSecretWithVariables; 14use JMS\Serializer\Type\ParserInterface; 15use JMS\Serializer\Visitor\Factory\JsonSerializationVisitorFactory; 16use PHPUnit\Framework\TestCase; 17use Symfony\Component\ExpressionLanguage\ExpressionFunction; 18use Symfony\Component\ExpressionLanguage\ExpressionLanguage; 19use Symfony\Component\Filesystem\Filesystem; 20 21class SerializerBuilderTest extends TestCase 22{ 23 /** @var SerializerBuilder */ 24 private $builder; 25 private $fs; 26 private $tmpDir; 27 28 public function testBuildWithoutAnythingElse() 29 { 30 $serializer = $this->builder->build(); 31 32 self::assertEquals('"foo"', $serializer->serialize('foo', 'json')); 33 self::assertEquals('<?xml version="1.0" encoding="UTF-8"?> 34<result><![CDATA[foo]]></result> 35', $serializer->serialize('foo', 'xml')); 36 37 self::assertEquals('foo', $serializer->deserialize('"foo"', 'string', 'json')); 38 self::assertEquals('foo', $serializer->deserialize('<?xml version="1.0" encoding="UTF-8"?><result><![CDATA[foo]]></result>', 'string', 'xml')); 39 } 40 41 public function testWithCache() 42 { 43 self::assertFileNotExists($this->tmpDir); 44 45 self::assertSame($this->builder, $this->builder->setCacheDir($this->tmpDir)); 46 $serializer = $this->builder->build(); 47 48 self::assertFileExists($this->tmpDir); 49 self::assertFileExists($this->tmpDir . '/annotations'); 50 self::assertFileExists($this->tmpDir . '/metadata'); 51 52 $factory = $this->getField($serializer, 'factory'); 53 self::assertAttributeSame(false, 'debug', $factory); 54 self::assertAttributeNotSame(null, 'cache', $factory); 55 } 56 57 public function testDoesAddDefaultHandlers() 58 { 59 $serializer = $this->builder->build(); 60 61 self::assertEquals('"2020-04-16T00:00:00+00:00"', $serializer->serialize(new \DateTime('2020-04-16', new \DateTimeZone('UTC')), 'json')); 62 self::assertEquals('[1,2,3]', $serializer->serialize(new \ArrayIterator([1, 2, 3]), 'json')); 63 } 64 65 public function testCustomTypeParser() 66 { 67 $parserMock = $this->getMockBuilder(ParserInterface::class) 68 ->disableOriginalConstructor() 69 ->getMock(); 70 $parserMock 71 ->method('parse') 72 ->willReturn(['name' => 'DateTimeImmutable', 'params' => [2 => 'd-Y-m']]); 73 74 $this->builder->setTypeParser($parserMock); 75 76 $serializer = $this->builder->build(); 77 78 $result = $serializer->deserialize('"04-2020-10"', 'XXX', 'json'); 79 self::assertInstanceOf(\DateTimeImmutable::class, $result); 80 self::assertEquals('2020-10-04', $result->format('Y-m-d')); 81 } 82 83 public function testDoesNotAddDefaultHandlersWhenExplicitlyConfigured() 84 { 85 self::assertSame($this->builder, $this->builder->configureHandlers(static function (HandlerRegistry $registry) { 86 })); 87 88 self::assertEquals('{}', $this->builder->build()->serialize(new \DateTime('2020-04-16'), 'json')); 89 } 90 91 /** 92 * @expectedException JMS\Serializer\Exception\UnsupportedFormatException 93 * @expectedExceptionMessage The format "xml" is not supported for serialization. 94 */ 95 public function testDoesNotAddOtherVisitorsWhenConfiguredExplicitly() 96 { 97 self::assertSame( 98 $this->builder, 99 $this->builder->setSerializationVisitor('json', new JsonSerializationVisitorFactory()) 100 ); 101 102 $this->builder->build()->serialize('foo', 'xml'); 103 } 104 105 public function testIncludeInterfaceMetadata() 106 { 107 self::assertFalse( 108 $this->getIncludeInterfaces($this->builder), 109 'Interface metadata are not included by default' 110 ); 111 112 self::assertTrue( 113 $this->getIncludeInterfaces($this->builder->includeInterfaceMetadata(true)), 114 'Force including interface metadata' 115 ); 116 117 self::assertFalse( 118 $this->getIncludeInterfaces($this->builder->includeInterfaceMetadata(false)), 119 'Force not including interface metadata' 120 ); 121 122 self::assertSame( 123 $this->builder, 124 $this->builder->includeInterfaceMetadata(true) 125 ); 126 } 127 128 public function testSetSerializationContext() 129 { 130 $contextFactoryMock = $this->getMockForAbstractClass('JMS\\Serializer\\ContextFactory\\SerializationContextFactoryInterface'); 131 $context = new SerializationContext(); 132 $context->setSerializeNull(true); 133 134 $contextFactoryMock 135 ->expects($this->once()) 136 ->method('createSerializationContext') 137 ->will($this->returnValue($context)); 138 139 $this->builder->setSerializationContextFactory($contextFactoryMock); 140 141 $serializer = $this->builder->build(); 142 143 $result = $serializer->serialize(['value' => null], 'json'); 144 145 self::assertEquals('{"value":null}', $result); 146 } 147 148 public function testSetDeserializationContext() 149 { 150 $contextFactoryMock = $this->getMockForAbstractClass('JMS\\Serializer\\ContextFactory\\DeserializationContextFactoryInterface'); 151 $context = new DeserializationContext(); 152 153 $contextFactoryMock 154 ->expects($this->once()) 155 ->method('createDeserializationContext') 156 ->will($this->returnValue($context)); 157 158 $this->builder->setDeserializationContextFactory($contextFactoryMock); 159 160 $serializer = $this->builder->build(); 161 162 $result = $serializer->deserialize('{"value":null}', 'array', 'json'); 163 164 self::assertEquals(['value' => null], $result); 165 } 166 167 public function testSetCallbackSerializationContextWithSerializeNull() 168 { 169 $this->builder->setSerializationContextFactory(static function () { 170 return SerializationContext::create() 171 ->setSerializeNull(true); 172 }); 173 174 $serializer = $this->builder->build(); 175 176 $result = $serializer->serialize(['value' => null], 'json'); 177 178 self::assertEquals('{"value":null}', $result); 179 } 180 181 public function testSetCallbackSerializationContextWithNotSerializeNull() 182 { 183 $this->builder->setSerializationContextFactory(static function () { 184 return SerializationContext::create() 185 ->setSerializeNull(false); 186 }); 187 188 $serializer = $this->builder->build(); 189 190 $result = $serializer->serialize(['value' => null, 'not_null' => 'ok'], 'json'); 191 192 self::assertEquals('{"not_null":"ok"}', $result); 193 } 194 195 public function expressionFunctionProvider() 196 { 197 return [ 198 [ 199 new ExpressionFunction('show_data', static function () { 200 return 'true'; 201 }, static function () { 202 return true; 203 }), 204 '{"name":"mike"}', 205 ], 206 [ 207 new ExpressionFunction('show_data', static function () { 208 return 'false'; 209 }, static function () { 210 return false; 211 }), 212 '{"name":"mike","gender":"f"}', 213 ], 214 ]; 215 } 216 217 /** 218 * @param ExpressionFunction $function 219 * @param string $json 220 * 221 * @dataProvider expressionFunctionProvider 222 */ 223 public function testExpressionEngine(ExpressionFunction $function, $json) 224 { 225 $language = new ExpressionLanguage(); 226 $language->addFunction($function); 227 228 $this->builder->setExpressionEvaluator(new ExpressionEvaluator($language)); 229 230 $serializer = $this->builder->build(); 231 232 $person = new PersonSecret(); 233 $person->gender = 'f'; 234 $person->name = 'mike'; 235 236 self::assertEquals($json, $serializer->serialize($person, 'json')); 237 } 238 239 public function testExpressionEngineWhenDeserializing() 240 { 241 $language = new ExpressionLanguage(); 242 $this->builder->setExpressionEvaluator(new ExpressionEvaluator($language)); 243 244 $serializer = $this->builder->build(); 245 246 $person = new PersonSecretWithVariables(); 247 $person->gender = 'f'; 248 $person->name = 'mike'; 249 250 $serialized = $serializer->serialize($person, 'json'); 251 self::assertEquals('{"name":"mike","gender":"f"}', $serialized); 252 253 $object = $serializer->deserialize($serialized, PersonSecretWithVariables::class, 'json'); 254 self::assertEquals($person, $object); 255 } 256 257 protected function setUp() 258 { 259 $this->builder = SerializerBuilder::create(); 260 $this->fs = new Filesystem(); 261 262 $this->tmpDir = sys_get_temp_dir() . '/serializer'; 263 $this->fs->remove($this->tmpDir); 264 clearstatcache(); 265 } 266 267 protected function tearDown() 268 { 269 $this->fs->remove($this->tmpDir); 270 } 271 272 private function getField($obj, $name) 273 { 274 $ref = new \ReflectionProperty($obj, $name); 275 $ref->setAccessible(true); 276 277 return $ref->getValue($obj); 278 } 279 280 private function getIncludeInterfaces(SerializerBuilder $builder) 281 { 282 $factory = $this->getField($builder->build(), 'factory'); 283 284 return $this->getField($factory, 'includeInterfaces'); 285 } 286} 287