Completed
Push — master ( 017efb...6986c5 )
by Vitalijs
03:40 queued 45s
created

DTOBase::serialize()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 8
ccs 3
cts 3
cp 1
rs 9.4285
cc 2
eloc 4
nc 2
nop 0
crap 2
1
<?php
2
3
namespace CodinPro\DataTransferObject;
4
5
use ArrayAccess;
6
use Countable;
7
use IteratorAggregate;
8
9
class DTOBase implements ArrayAccess, IteratorAggregate, Countable
10
{
11
    protected $data;
12
    protected $default = [];
13
    private $serializer = null;
14
15
    /**
16
     * DTO constructor.
17
     * @param array $default
18
     * @param array|object|string $data
19
     * @param DTOSerializerInterface $serializer
20
     * @throws \InvalidArgumentException
21
     */
22 38
    public function __construct($default = [], $data = [], DTOSerializerInterface $serializer = null)
23
    {
24 38
        if (count($default) > 0) {
25 36
            $this->default = $default;
26
        }
27
28 38
        $this->serializer = $serializer === null ? new JsonSerializer() : $serializer;
29
30 38
        $this->build($data);
31 34
    }
32
33
    /**
34
     * Build DTO from given type of data
35
     * @param $data
36
     * @throws \InvalidArgumentException
37
     */
38 38
    private function build($data)
39
    {
40 38
        switch (gettype($data)) {
41 38
            case 'array':
42 10
            case 'object':
43 31
                $this->buildFromData($data);
44 31
                break;
45 8
            case 'string':
46 6
                $this->buildFromJson($data);
47 4
                break;
48
            default:
49 2
                throw new \InvalidArgumentException('DTO can be built from array|object|json, "'.gettype($data).'" given.');
50
        }
51 34
    }
52
53
    /**
54
     * Build DTO from provided data
55
     * @param object|array $data
56
     */
57 34
    private function buildFromData($data)
58
    {
59 34
        foreach ($this->default as $key => $value) {
60 32
            if (is_object($data) && isset($data->{$key})) {
61 6
                $this->data[$key] = $data->{$key};
62 32
            } else if (is_array($data) && isset($data[$key])) {
63 13
                $this->data[$key] = $data[$key];
64
            } else {
65 32
                $this->data[$key] = $value;
66
            }
67
        }
68 34
    }
69
70
    /**
71
     * Get custom iterator
72
     * @return DTOIterator
73
     */
74 2
    public function getIterator()
75
    {
76 2
        return new DTOIterator($this->data);
77
    }
78
79
    /**
80
     * Check if offset exists
81
     * @param string $offset
82
     * @return bool
83
     */
84 2
    public function offsetExists($offset)
85
    {
86 2
        return isset($this->data[$offset]);
87
    }
88
89
    /**
90
     * Get data at scalar offset or default value instead
91
     * @param string $offset
92
     * @return mixed
93
     * @throws \InvalidArgumentException
94
     */
95 21
    private function offsetGetScalar($offset)
96
    {
97 21
        if (isset($this->data[$offset])) {
98 18
            return $this->data[$offset];
99
        }
100
101 4
        return $this->getDefault($offset);
102
    }
103
104
    /**
105
     * Get data at offset or default value instead
106
     * @param string $offset
107
     * @return mixed
108
     * @throws \InvalidArgumentException
109
     */
110 9
    public function offsetGet($offset)
111
    {
112 9
        return $this->get($offset);
113
    }
114
115
    /**
116
     * Set data at offset
117
     * @param string $offset
118
     * @param mixed $value
119
     */
120 4
    public function offsetSet($offset, $value)
121
    {
122 4
        $this->data[$offset] = $value;
123 4
    }
124
125
    /**
126
     * Remove data at offset
127
     * @param string $offset
128
     */
129 2
    public function offsetUnset($offset)
130
    {
131 2
        unset($this->data[$offset]);
132 2
    }
133
134
    /**
135
     * Count data elements
136
     * @return int
137
     */
138 2
    public function count()
139
    {
140 2
        return count($this->data);
141
    }
142
143
    /**
144
     * Get default value at offset if set
145
     * @param string $offset
146
     * @return mixed
147
     * @throws \InvalidArgumentException
148
     */
149 4
    private function getDefault($offset)
150
    {
151 4
        if (isset($this->default[$offset])) {
152 2
            return $this->default[$offset];
153
        }
154
155 2
        throw new \InvalidArgumentException('Offset '.$offset.' does not exist.');
156
    }
157
158 5
    public function __get($key)
159
    {
160 5
        return $this->offsetGet($key);
161
    }
162
163 2
    public function __set($key, $value)
164
    {
165 2
        $this->offsetSet($key, $value);
166 2
    }
167
168 2
    public function __isset($key)
169
    {
170 2
        return isset($this->data[$key]);
171
    }
172
173
    /**
174
     * Get nested values using "dot" notation
175
     * @param string $offset
176
     * @return mixed
177
     * @throws \InvalidArgumentException
178
     */
179 23
    public function get($offset)
180
    {
181 23
        if (strpos($offset, '.') === false) {
182 21
            return $this->offsetGetScalar($offset);
183
        } else {
184 5
            $keys = explode('.', $offset);
185 5
            $scope = $this->data;
186 5
            foreach ($keys as $key) {
187 5
                if ((is_array($scope) || $scope instanceof ArrayAccess) && isset($scope[$key])) {
188 3
                    $scope = $scope[$key];
189 4
                } elseif (is_object($scope) && isset($scope->{$key})) {
190 2
                    $scope = $scope->{$key};
191
                } else {
192 5
                    throw new \InvalidArgumentException('Non existent offset given in offset chain: '.$key);
193
                }
194
            }
195
196 3
            return $scope;
197
        }
198
    }
199
200
    /**
201
     * Converts data to string
202
     * @return string
203
     */
204 3
    public function __toString()
205
    {
206 3
        return $this->serialize();
207
    }
208
209
    /**
210
     * Serializes the data using serializer
211
     * @return string
212
     */
213 3
    private function serialize()
214
    {
215 3
        if ($this->serializer === null) {
216
            return 'Serializer not set';
217
        }
218
219 3
        return $this->serializer->serialize($this->data);
220
    }
221
222
    /**
223
     * @return DTOSerializerInterface
224
     */
225 4
    public function getSerializer()
226
    {
227 4
        return $this->serializer;
228
    }
229
230
    /**
231
     * @param DTOSerializerInterface $serializer
232
     * @return DTOBase
233
     */
234 1
    public function setSerializer(DTOSerializerInterface $serializer)
235
    {
236 1
        $this->serializer = $serializer;
237
238 1
        return $this;
239
    }
240
241
    /**
242
     * Try to build from provided string as JSON
243
     * @param string $data
244
     * @throws \InvalidArgumentException
245
     */
246 6
    private function buildFromJson($data)
247
    {
248 6
        $triedToDecodeData = json_decode($data);
249
250 6
        if ($triedToDecodeData !== null) {
251 4
            $this->buildFromData($triedToDecodeData);
252
        } else {
253 2
            throw new \InvalidArgumentException(
254 2
                'DTO can be built from array|object|json, "'.gettype($data).'" given. Probably tried to pass invalid JSON.'
255
            );
256
        }
257
    }
258
}