1<?php
2
3namespace JMS\Serializer;
4
5use JMS\Serializer\Exception\RuntimeException;
6use JMS\Serializer\Metadata\ClassMetadata;
7use JMS\Serializer\Metadata\PropertyMetadata;
8use JMS\Serializer\Naming\AdvancedNamingStrategyInterface;
9use JMS\Serializer\Naming\PropertyNamingStrategyInterface;
10
11/**
12 * Generic Deserialization Visitor.
13 * @deprecated
14 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
15 */
16abstract class GenericDeserializationVisitor extends AbstractVisitor
17{
18    private $navigator;
19    private $result;
20    private $objectStack;
21    private $currentObject;
22
23    public function setNavigator(GraphNavigator $navigator)
24    {
25        $this->navigator = $navigator;
26        $this->result = null;
27        $this->objectStack = new \SplStack;
28    }
29
30    public function getNavigator()
31    {
32        return $this->navigator;
33    }
34
35    public function prepare($data)
36    {
37        return $this->decode($data);
38    }
39
40    public function visitNull($data, array $type, Context $context)
41    {
42        return null;
43    }
44
45    public function visitString($data, array $type, Context $context)
46    {
47        $data = (string)$data;
48
49        if (null === $this->result) {
50            $this->result = $data;
51        }
52
53        return $data;
54    }
55
56    public function visitBoolean($data, array $type, Context $context)
57    {
58        $data = (Boolean)$data;
59
60        if (null === $this->result) {
61            $this->result = $data;
62        }
63
64        return $data;
65    }
66
67    public function visitInteger($data, array $type, Context $context)
68    {
69        $data = (integer)$data;
70
71        if (null === $this->result) {
72            $this->result = $data;
73        }
74
75        return $data;
76    }
77
78    public function visitDouble($data, array $type, Context $context)
79    {
80        $data = (double)$data;
81
82        if (null === $this->result) {
83            $this->result = $data;
84        }
85
86        return $data;
87    }
88
89    public function visitArray($data, array $type, Context $context)
90    {
91        if (!\is_array($data)) {
92            throw new RuntimeException(sprintf('Expected array, but got %s: %s', \gettype($data), json_encode($data)));
93        }
94
95        // If no further parameters were given, keys/values are just passed as is.
96        if (!$type['params']) {
97            if (null === $this->result) {
98                $this->result = $data;
99            }
100
101            return $data;
102        }
103
104        switch (\count($type['params'])) {
105            case 1: // Array is a list.
106                $listType = $type['params'][0];
107
108                $result = array();
109                if (null === $this->result) {
110                    $this->result = &$result;
111                }
112
113                foreach ($data as $v) {
114                    $result[] = $this->navigator->accept($v, $listType, $context);
115                }
116
117                return $result;
118
119            case 2: // Array is a map.
120                list($keyType, $entryType) = $type['params'];
121
122                $result = array();
123                if (null === $this->result) {
124                    $this->result = &$result;
125                }
126
127                foreach ($data as $k => $v) {
128                    $result[$this->navigator->accept($k, $keyType, $context)] = $this->navigator->accept($v, $entryType, $context);
129                }
130
131                return $result;
132
133            default:
134                throw new RuntimeException(sprintf('Array type cannot have more than 2 parameters, but got %s.', json_encode($type['params'])));
135        }
136    }
137
138    public function startVisitingObject(ClassMetadata $metadata, $object, array $type, Context $context)
139    {
140        $this->setCurrentObject($object);
141
142        if (null === $this->result) {
143            $this->result = $this->currentObject;
144        }
145    }
146
147    public function visitProperty(PropertyMetadata $metadata, $data, Context $context)
148    {
149        if ($this->namingStrategy instanceof AdvancedNamingStrategyInterface) {
150            $name = $this->namingStrategy->getPropertyName($metadata, $context);
151        } else {
152            $name = $this->namingStrategy->translateName($metadata);
153        }
154
155        if (null === $data) {
156            return;
157        }
158
159        if (!\is_array($data)) {
160            throw new RuntimeException(sprintf('Invalid data "%s"(%s), expected "%s".', $data, $metadata->type['name'], $metadata->reflection->class));
161        }
162
163        if (!array_key_exists($name, $data)) {
164            return;
165        }
166
167        if (!$metadata->type) {
168            throw new RuntimeException(sprintf('You must define a type for %s::$%s.', $metadata->reflection->class, $metadata->name));
169        }
170
171        $v = $data[$name] !== null ? $this->navigator->accept($data[$name], $metadata->type, $context) : null;
172
173        $this->accessor->setValue($this->currentObject, $v, $metadata);
174
175    }
176
177    public function endVisitingObject(ClassMetadata $metadata, $data, array $type, Context $context)
178    {
179        $obj = $this->currentObject;
180        $this->revertCurrentObject();
181
182        return $obj;
183    }
184
185    public function getResult()
186    {
187        return $this->result;
188    }
189
190    public function setCurrentObject($object)
191    {
192        $this->objectStack->push($this->currentObject);
193        $this->currentObject = $object;
194    }
195
196    public function getCurrentObject()
197    {
198        return $this->currentObject;
199    }
200
201    public function revertCurrentObject()
202    {
203        return $this->currentObject = $this->objectStack->pop();
204    }
205
206    abstract protected function decode($str);
207}
208