1<?php
2
3declare(strict_types=1);
4
5namespace JMS\Serializer\Tests\Serializer;
6
7use JMS\Serializer\Metadata\ClassMetadata;
8use JMS\Serializer\Metadata\PropertyMetadata;
9use JMS\Serializer\SerializationContext;
10use JMS\Serializer\SerializerBuilder;
11use JMS\Serializer\Tests\Fixtures\Author;
12use JMS\Serializer\Tests\Fixtures\BlogPost;
13use JMS\Serializer\Tests\Fixtures\InlineChild;
14use JMS\Serializer\Tests\Fixtures\Node;
15use JMS\Serializer\Tests\Fixtures\Publisher;
16use JMS\Serializer\Tests\Fixtures\VersionedObject;
17use PHPUnit\Framework\TestCase;
18
19class ContextTest extends TestCase
20{
21    public function testSerializationContextPathAndDepth()
22    {
23        $object = new Node([
24            new Node(),
25            new Node([new Node()]),
26        ]);
27        $objects = [$object, $object->children[0], $object->children[1], $object->children[1]->children[0]];
28
29        $self = $this;
30
31        $exclusionStrategy = $this->getMockBuilder('JMS\Serializer\Exclusion\ExclusionStrategyInterface')->getMock();
32        $exclusionStrategy->expects($this->any())
33            ->method('shouldSkipClass')
34            ->with($this->anything(), $this->callback(static function (SerializationContext $context) use ($self, $objects) {
35                $expectedDepth = $expectedPath = null;
36
37                if ($context->getObject() === $objects[0]) {
38                    $expectedDepth = 1;
39                    $expectedPath = 'JMS\Serializer\Tests\Fixtures\Node';
40                } elseif ($context->getObject() === $objects[1]) {
41                    $expectedDepth = 2;
42                    $expectedPath = 'JMS\Serializer\Tests\Fixtures\Node -> JMS\Serializer\Tests\Fixtures\Node';
43                } elseif ($context->getObject() === $objects[2]) {
44                    $expectedDepth = 2;
45                    $expectedPath = 'JMS\Serializer\Tests\Fixtures\Node -> JMS\Serializer\Tests\Fixtures\Node';
46                } elseif ($context->getObject() === $objects[3]) {
47                    $expectedDepth = 3;
48                    $expectedPath = 'JMS\Serializer\Tests\Fixtures\Node -> JMS\Serializer\Tests\Fixtures\Node -> JMS\Serializer\Tests\Fixtures\Node';
49                }
50
51                $self->assertEquals($expectedDepth, $context->getDepth(), 'shouldSkipClass depth');
52                $self->assertEquals($expectedPath, $context->getPath(), 'shouldSkipClass path');
53
54                return true;
55            }))
56            ->will($this->returnValue(false));
57
58        $exclusionStrategy->expects($this->any())
59            ->method('shouldSkipProperty')
60            ->with($this->anything(), $this->callback(static function (SerializationContext $context) use ($self, $objects) {
61                $expectedDepth = $expectedPath = null;
62
63                if ($context->getObject() === $objects[0]) {
64                    $expectedDepth = 1;
65                    $expectedPath = 'JMS\Serializer\Tests\Fixtures\Node';
66                } elseif ($context->getObject() === $objects[1]) {
67                    $expectedDepth = 2;
68                    $expectedPath = 'JMS\Serializer\Tests\Fixtures\Node -> JMS\Serializer\Tests\Fixtures\Node';
69                } elseif ($context->getObject() === $objects[2]) {
70                    $expectedDepth = 2;
71                    $expectedPath = 'JMS\Serializer\Tests\Fixtures\Node -> JMS\Serializer\Tests\Fixtures\Node';
72                } elseif ($context->getObject() === $objects[3]) {
73                    $expectedDepth = 3;
74                    $expectedPath = 'JMS\Serializer\Tests\Fixtures\Node -> JMS\Serializer\Tests\Fixtures\Node -> JMS\Serializer\Tests\Fixtures\Node';
75                }
76
77                $self->assertEquals($expectedDepth, $context->getDepth(), 'shouldSkipProperty depth');
78                $self->assertEquals($expectedPath, $context->getPath(), 'shouldSkipProperty path');
79
80                return true;
81            }))
82            ->will($this->returnValue(false));
83
84        $serializer = SerializerBuilder::create()->build();
85
86        $serializer->serialize($object, 'json', SerializationContext::create()->addExclusionStrategy($exclusionStrategy));
87    }
88
89    public function testSerializationMetadataStack()
90    {
91        $object = new Node([
92            $child = new InlineChild(),
93        ]);
94        $self = $this;
95
96        $exclusionStrategy = $this->getMockBuilder('JMS\Serializer\Exclusion\ExclusionStrategyInterface')->getMock();
97        $exclusionStrategy->expects($this->any())
98            ->method('shouldSkipClass')
99            ->will($this->returnCallback(static function (ClassMetadata $classMetadata, SerializationContext $context) use ($self, $object, $child) {
100                $stack = $context->getMetadataStack();
101
102                if ($object === $context->getObject()) {
103                    $self->assertEquals(0, $stack->count());
104                }
105
106                if ($child === $context->getObject()) {
107                    $self->assertEquals(2, $stack->count());
108                    $self->assertEquals('JMS\Serializer\Tests\Fixtures\Node', $stack[1]->name);
109                    $self->assertEquals('children', $stack[0]->name);
110                }
111
112                return false;
113            }));
114
115        $exclusionStrategy->expects($this->any())
116            ->method('shouldSkipProperty')
117            ->will($this->returnCallback(static function (PropertyMetadata $propertyMetadata, SerializationContext $context) use ($self) {
118                $stack = $context->getMetadataStack();
119
120                if ('JMS\Serializer\Tests\Fixtures\Node' === $propertyMetadata->class && 'children' === $propertyMetadata->name) {
121                    $self->assertEquals(1, $stack->count());
122                    $self->assertEquals('JMS\Serializer\Tests\Fixtures\Node', $stack[0]->name);
123                }
124
125                if ('JMS\Serializer\Tests\Fixtures\InlineChild' === $propertyMetadata->class) {
126                    $self->assertEquals(3, $stack->count());
127                    $self->assertEquals('JMS\Serializer\Tests\Fixtures\Node', $stack[2]->name);
128                    $self->assertEquals('children', $stack[1]->name);
129                    $self->assertEquals('JMS\Serializer\Tests\Fixtures\InlineChild', $stack[0]->name);
130                }
131
132                return false;
133            }));
134
135        $serializer = SerializerBuilder::create()->build();
136        $serializer->serialize($object, 'json', SerializationContext::create()->addExclusionStrategy($exclusionStrategy));
137    }
138
139    public function getScalars()
140    {
141        return [
142            ['string'],
143            [5],
144            [5.5],
145            [[]],
146        ];
147    }
148
149    /**
150     * @dataProvider getScalars
151     */
152    public function testCanVisitScalars($scalar)
153    {
154        $context = SerializationContext::create();
155
156        $context->startVisiting($scalar);
157        self::assertFalse($context->isVisiting($scalar));
158        $context->stopVisiting($scalar);
159    }
160
161    public function testInitialTypeCompatibility()
162    {
163        $context = SerializationContext::create();
164        $context->setInitialType('foo');
165        self::assertEquals('foo', $context->getInitialType());
166        self::assertEquals('foo', $context->getAttribute('initial_type'));
167
168        $context = SerializationContext::create();
169        $context->setAttribute('initial_type', 'foo');
170        self::assertEquals('foo', $context->getInitialType());
171    }
172
173    public function testMultipleCallsOnGroupsDoNotCreateMultipleExclusionStrategies()
174    {
175        $serializer = SerializerBuilder::create()->build();
176
177        $context = SerializationContext::create();
178        $context->setGroups(['foo', 'Default']);
179        $context->setGroups('post');
180
181        $object = new BlogPost('serializer', new Author('me'), new \DateTime(), new Publisher('php'));
182        $serialized = $serializer->serialize($object, 'json', $context);
183
184        $data = json_decode($serialized, true);
185
186        self::assertArrayHasKey('id', $data);
187        self::assertArrayNotHasKey('created_at', $data);
188    }
189
190    public function testMultipleCallsOnVersionDoNotCreateMultipleExclusionStrategies()
191    {
192        $serializer = SerializerBuilder::create()->build();
193
194        $context = SerializationContext::create();
195        $context->setVersion('1.0.1');
196        $context->setVersion('1.0.0');
197
198        $object = new VersionedObject('a', 'b');
199        $serialized = $serializer->serialize($object, 'json', $context);
200
201        $data = json_decode($serialized, true);
202
203        self::assertEquals('a', $data['name']);
204    }
205
206    public function testSerializeNullOption()
207    {
208        $context = SerializationContext::create();
209        self::assertFalse($context->shouldSerializeNull());
210
211        $context->setSerializeNull(false);
212        self::assertFalse($context->shouldSerializeNull());
213
214        $context->setSerializeNull(true);
215        self::assertTrue($context->shouldSerializeNull());
216    }
217}
218