1<?php
2
3declare(strict_types=1);
4
5namespace JMS\Serializer;
6
7use JMS\Serializer\Exception\RuntimeException;
8use Metadata\MetadataFactoryInterface;
9
10class SerializationContext extends Context
11{
12    /** @var \SplObjectStorage */
13    private $visitingSet;
14
15    /** @var \SplStack */
16    private $visitingStack;
17
18    /**
19     * @var string
20     */
21    private $initialType;
22
23    /**
24     * @var bool
25     */
26    private $serializeNull = false;
27
28    public static function create(): self
29    {
30        return new self();
31    }
32
33    public function initialize(string $format, VisitorInterface $visitor, GraphNavigatorInterface $navigator, MetadataFactoryInterface $factory): void
34    {
35        parent::initialize($format, $visitor, $navigator, $factory);
36
37        $this->visitingSet = new \SplObjectStorage();
38        $this->visitingStack = new \SplStack();
39    }
40
41    /**
42     * Set if NULLs should be serialized (TRUE) ot not (FALSE)
43     */
44    public function setSerializeNull(bool $bool): self
45    {
46        $this->serializeNull = $bool;
47
48        return $this;
49    }
50
51    /**
52     * Returns TRUE when NULLs should be serialized
53     * Returns FALSE when NULLs should not be serialized
54     */
55    public function shouldSerializeNull(): bool
56    {
57        return $this->serializeNull;
58    }
59
60    /**
61     * @param mixed $object
62     */
63    public function startVisiting($object): void
64    {
65        if (!\is_object($object)) {
66            return;
67        }
68        $this->visitingSet->attach($object);
69        $this->visitingStack->push($object);
70    }
71
72    /**
73     * @param mixed $object
74     */
75    public function stopVisiting($object): void
76    {
77        if (!\is_object($object)) {
78            return;
79        }
80        $this->visitingSet->detach($object);
81        $poppedObject = $this->visitingStack->pop();
82
83        if ($object !== $poppedObject) {
84            throw new RuntimeException('Context visitingStack not working well');
85        }
86    }
87
88    /**
89     * @param mixed $object
90     */
91    public function isVisiting($object): bool
92    {
93        if (!\is_object($object)) {
94            return false;
95        }
96
97        return $this->visitingSet->contains($object);
98    }
99
100    public function getPath(): ?string
101    {
102        $path = [];
103        foreach ($this->visitingStack as $obj) {
104            $path[] = \get_class($obj);
105        }
106
107        if (!$path) {
108            return null;
109        }
110
111        return implode(' -> ', $path);
112    }
113
114    public function getDirection(): int
115    {
116        return GraphNavigatorInterface::DIRECTION_SERIALIZATION;
117    }
118
119    public function getDepth(): int
120    {
121        return $this->visitingStack->count();
122    }
123
124    public function getObject(): ?object
125    {
126        return !$this->visitingStack->isEmpty() ? $this->visitingStack->top() : null;
127    }
128
129    public function getVisitingStack(): \SplStack
130    {
131        return $this->visitingStack;
132    }
133
134
135    public function getVisitingSet(): \SplObjectStorage
136    {
137        return $this->visitingSet;
138    }
139
140    /**
141     * @return $this
142     */
143    public function setInitialType(string $type): self
144    {
145        $this->initialType = $type;
146        $this->setAttribute('initial_type', $type);
147        return $this;
148    }
149
150    public function getInitialType(): ?string
151    {
152        return $this->initialType
153            ? $this->initialType
154            : $this->hasAttribute('initial_type') ? $this->getAttribute('initial_type') : null;
155    }
156}
157