Completed
Push — master ( b3e4e8...528f96 )
by Vitalijs
05:12 queued 02:43
created

DTOBase::buildFromData()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 6

Importance

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