1<?php
2
3declare(strict_types=1);
4
5namespace JMS\Serializer\Tests\Serializer;
6
7use Doctrine\Common\Annotations\AnnotationReader;
8use JMS\Serializer\Accessor\DefaultAccessorStrategy;
9use JMS\Serializer\Construction\UnserializeObjectConstructor;
10use JMS\Serializer\DeserializationContext;
11use JMS\Serializer\EventDispatcher\EventDispatcher;
12use JMS\Serializer\Exclusion\ExclusionStrategyInterface;
13use JMS\Serializer\GraphNavigator\DeserializationGraphNavigator;
14use JMS\Serializer\GraphNavigator\SerializationGraphNavigator;
15use JMS\Serializer\GraphNavigatorInterface;
16use JMS\Serializer\Handler\HandlerRegistry;
17use JMS\Serializer\Handler\SubscribingHandlerInterface;
18use JMS\Serializer\Metadata\Driver\AnnotationDriver;
19use JMS\Serializer\Naming\IdenticalPropertyNamingStrategy;
20use JMS\Serializer\SerializationContext;
21use JMS\Serializer\Visitor\DeserializationVisitorInterface;
22use JMS\Serializer\Visitor\SerializationVisitorInterface;
23use Metadata\MetadataFactory;
24use PHPUnit\Framework\TestCase;
25
26class GraphNavigatorTest extends TestCase
27{
28    private $metadataFactory;
29    private $handlerRegistry;
30    private $objectConstructor;
31    private $dispatcher;
32    private $serializationNavigator;
33    private $deserializationNavigator;
34    private $context;
35    private $deserializationContext;
36    private $accessor;
37
38    private $serializationVisitor;
39    private $deserializationVisitor;
40
41    /**
42     * @expectedException JMS\Serializer\Exception\RuntimeException
43     * @expectedExceptionMessage Resources are not supported in serialized data.
44     */
45    public function testResourceThrowsException()
46    {
47        $this->serializationNavigator->accept(STDIN, null);
48    }
49
50    public function testNavigatorPassesInstanceOnSerialization()
51    {
52        $object = new SerializableClass();
53        $metadata = $this->metadataFactory->getMetadataForClass(get_class($object));
54
55        $self = $this;
56        $this->context = $this->getMockBuilder(SerializationContext::class)->getMock();
57        $context = $this->context;
58        $exclusionStrategy = $this->getMockBuilder('JMS\Serializer\Exclusion\ExclusionStrategyInterface')->getMock();
59        $exclusionStrategy->expects($this->once())
60            ->method('shouldSkipClass')
61            ->will($this->returnCallback(static function ($passedMetadata, $passedContext) use ($metadata, $context, $self) {
62                $self->assertSame($metadata, $passedMetadata);
63                $self->assertSame($context, $passedContext);
64                return false;
65            }));
66        $exclusionStrategy->expects($this->once())
67            ->method('shouldSkipProperty')
68            ->will($this->returnCallback(static function ($propertyMetadata, $passedContext) use ($context, $metadata, $self) {
69                $self->assertSame($metadata->propertyMetadata['foo'], $propertyMetadata);
70                $self->assertSame($context, $passedContext);
71                return false;
72            }));
73
74        $this->context->expects($this->once())
75            ->method('getExclusionStrategy')
76            ->will($this->returnValue($exclusionStrategy));
77
78        $navigator = new SerializationGraphNavigator($this->metadataFactory, $this->handlerRegistry, $this->accessor, $this->dispatcher);
79        $navigator->initialize($this->serializationVisitor, $this->context);
80        $navigator->accept($object, null);
81    }
82
83    public function testNavigatorPassesNullOnDeserialization()
84    {
85        $class = __NAMESPACE__ . '\SerializableClass';
86        $metadata = $this->metadataFactory->getMetadataForClass($class);
87
88        $this->context = $this->getMockBuilder(DeserializationContext::class)->getMock();
89
90        $context = $this->context;
91        $exclusionStrategy = $this->getMockBuilder(ExclusionStrategyInterface::class)->getMock();
92        $exclusionStrategy->expects($this->once())
93            ->method('shouldSkipClass')
94            ->with($metadata, $this->callback(static function ($navigatorContext) use ($context) {
95                return $navigatorContext === $context;
96            }));
97
98        $exclusionStrategy->expects($this->once())
99            ->method('shouldSkipProperty')
100            ->with($metadata->propertyMetadata['foo'], $this->callback(static function ($navigatorContext) use ($context) {
101                return $navigatorContext === $context;
102            }));
103
104        $this->context->expects($this->once())
105            ->method('getExclusionStrategy')
106            ->will($this->returnValue($exclusionStrategy));
107
108        $navigator = new DeserializationGraphNavigator($this->metadataFactory, $this->handlerRegistry, $this->objectConstructor, $this->accessor, $this->dispatcher);
109        $navigator->initialize($this->deserializationVisitor, $this->context);
110        $navigator->accept('random', ['name' => $class, 'params' => []]);
111    }
112
113    /**
114     * @doesNotPerformAssertions
115     */
116    public function testNavigatorChangeTypeOnSerialization()
117    {
118        $object = new SerializableClass();
119        $typeName = 'JsonSerializable';
120
121        $this->dispatcher->addListener('serializer.pre_serialize', static function ($event) use ($typeName) {
122            $type = $event->getType();
123            $type['name'] = $typeName;
124            $event->setType($type['name'], $type['params']);
125        });
126
127        $this->handlerRegistry->registerSubscribingHandler(new TestSubscribingHandler());
128
129        $navigator = new SerializationGraphNavigator($this->metadataFactory, $this->handlerRegistry, $this->accessor, $this->dispatcher);
130        $navigator->initialize($this->serializationVisitor, $this->context);
131        $navigator->accept($object, null);
132    }
133
134    protected function setUp()
135    {
136        $this->deserializationVisitor = $this->getMockBuilder(DeserializationVisitorInterface::class)->getMock();
137        $this->serializationVisitor = $this->getMockBuilder(SerializationVisitorInterface::class)->getMock();
138
139        $this->context = $this->getMockBuilder(SerializationContext::class)
140            ->enableOriginalConstructor()
141            ->setMethodsExcept(['getExclusionStrategy'])
142            ->getMock();
143
144        $this->deserializationContext = $this->getMockBuilder(DeserializationContext::class)
145            ->enableOriginalConstructor()
146            ->setMethodsExcept(['getExclusionStrategy'])
147            ->getMock();
148
149        $this->dispatcher = new EventDispatcher();
150        $this->accessor = new DefaultAccessorStrategy();
151        $this->handlerRegistry = new HandlerRegistry();
152        $this->objectConstructor = new UnserializeObjectConstructor();
153
154        $this->metadataFactory = new MetadataFactory(new AnnotationDriver(new AnnotationReader(), new IdenticalPropertyNamingStrategy()));
155
156        $this->serializationNavigator = new SerializationGraphNavigator($this->metadataFactory, $this->handlerRegistry, $this->accessor, $this->dispatcher);
157        $this->serializationNavigator->initialize($this->serializationVisitor, $this->context);
158
159        $this->deserializationNavigator = new DeserializationGraphNavigator($this->metadataFactory, $this->handlerRegistry, $this->objectConstructor, $this->accessor, $this->dispatcher);
160        $this->deserializationNavigator->initialize($this->deserializationVisitor, $this->deserializationContext);
161    }
162}
163
164class SerializableClass
165{
166    public $foo = 'bar';
167}
168
169class TestSubscribingHandler implements SubscribingHandlerInterface
170{
171    public static function getSubscribingMethods()
172    {
173        return [[
174            'type' => 'JsonSerializable',
175            'format' => 'foo',
176            'direction' => GraphNavigatorInterface::DIRECTION_SERIALIZATION,
177            'method' => 'serialize',
178        ],
179        ];
180    }
181}
182