Completed
Push — master ( b761c2...3431ed )
by Vitalijs
05:51 queued 05:24
created

DTOBase::count()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
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
199 5
                $isAccessibleObject = is_object($scope) && isset($scope->{$key});
200
201 5
                if ($isAccessibleArray) {
202 3
                    $scope = $scope[$key];
203 4
                } elseif ($isAccessibleObject) {
204 2
                    $scope = $scope->{$key};
205
                } else {
206 5
                    throw new \InvalidArgumentException('Non existent offset given in offset chain: '.$key);
207
                }
208
            }
209
210 3
            return $scope;
211
        }
212
    }
213
214
    /**
215
     * Converts data to string
216
     * @return string
217
     */
218 3
    public function __toString()
219
    {
220 3
        return $this->serialize();
221
    }
222
223
    /**
224
     * Serializes the data using serializer
225
     * @return string
226
     */
227 3
    private function serialize()
228
    {
229 3
        if ($this->serializer === null) {
230
            return 'Serializer not set';
231
        }
232
233 3
        return $this->serializer->serialize($this->data);
234
    }
235
236
    /**
237
     * @return DTOSerializerInterface
238
     */
239 4
    public function getSerializer()
240
    {
241 4
        return $this->serializer;
242
    }
243
244
    /**
245
     * @param DTOSerializerInterface $serializer
246
     * @return DTOBase
247
     */
248 1
    public function setSerializer(DTOSerializerInterface $serializer)
249
    {
250 1
        $this->serializer = $serializer;
251
252 1
        return $this;
253
    }
254
}