Completed
Push — master ( 9a3401...c8ceae )
by Vitalijs
03:32
created

DTOBase::buildFromData()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 12
rs 8.8571
cc 6
eloc 8
nc 4
nop 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
    public function __construct($default = [], $data = [], DTOSerializerInterface $serializer = null)
23
    {
24
        if (count($default) > 0) {
25
            $this->default = $default;
26
        }
27
28
        $this->serializer = $serializer === null ? new JsonSerializer() : $serializer;
29
30
        $this->build($data);
31
    }
32
33
    /**
34
     * Build DTO from given type of data
35
     * @param $data
36
     * @throws \InvalidArgumentException
37
     */
38
    private function build($data)
39
    {
40
        switch (gettype($data)) {
41
            case 'array':
42
                $this->buildFromData($data);
43
                break;
44
            case 'object':
45
                $this->buildFromData($data);
0 ignored issues
show
Documentation introduced by
$data is of type object, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
46
                break;
47
            case 'string':
48
                $triedToDecodeData = json_decode($data);
49
50
                if ($triedToDecodeData !== null) {
51
                    $this->buildFromData($triedToDecodeData);
52
                } else {
53
                    throw new \InvalidArgumentException(
54
                        'DTO can be built from array|object|json, "'.gettype($data).'" given. Probably tried to pass invalid JSON.'
55
                    );
56
                }
57
                break;
58
            default:
59
                throw new \InvalidArgumentException('DTO can be built from array|object|json, "'.gettype($data).'" given.');
60
        }
61
    }
62
63
    /**
64
     * Build DTO from provided data
65
     * @param array $data
66
     */
67
    private function buildFromData($data)
68
    {
69
        foreach ($this->default as $key => $value) {
70
            if (is_object($data) && isset($data->{$key})) {
71
                $this->data[$key] = $data->{$key};
72
            } else if (is_array($data) && isset($data[$key])) {
73
                $this->data[$key] = $data[$key];
74
            } else {
75
                $this->data[$key] = $value;
76
            }
77
        }
78
    }
79
80
    /**
81
     * Get custom iterator
82
     * @return DTOIterator
83
     */
84
    public function getIterator()
85
    {
86
        return new DTOIterator($this->data);
87
    }
88
89
    /**
90
     * Check if offset exists
91
     * @param string $offset
92
     * @return bool
93
     */
94
    public function offsetExists($offset)
95
    {
96
        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
    private function offsetGetScalar($offset)
106
    {
107
        if (isset($this->data[$offset])) {
108
            return $this->data[$offset];
109
        }
110
111
        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
    public function offsetGet($offset)
121
    {
122
        return $this->get($offset);
123
    }
124
125
    /**
126
     * Set data at offset
127
     * @param string $offset
128
     * @param mixed $value
129
     */
130
    public function offsetSet($offset, $value)
131
    {
132
        $this->data[$offset] = $value;
133
    }
134
135
    /**
136
     * Remove data at offset
137
     * @param string $offset
138
     */
139
    public function offsetUnset($offset)
140
    {
141
        unset($this->data[$offset]);
142
    }
143
144
    /**
145
     * Count data elements
146
     * @return int
147
     */
148
    public function count()
149
    {
150
        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
    private function getDefault($offset)
160
    {
161
        if (isset($this->default[$offset])) {
162
            return $this->default[$offset];
163
        }
164
165
        throw new \InvalidArgumentException('Offset '.$offset.' does not exist.');
166
    }
167
168
    public function __get($key)
169
    {
170
        return $this->offsetGet($key);
171
    }
172
173
    public function __set($key, $value)
174
    {
175
        $this->offsetSet($key, $value);
176
    }
177
178
    public function __isset($key)
179
    {
180
        return isset($this->data[$key]);
181
    }
182
183
    /**
184
     * Get nested values using "dot" notation
185
     * @param $offset
186
     * @return mixed
187
     * @throws \InvalidArgumentException
188
     */
189
    public function get($offset)
190
    {
191
        if (strpos($offset, '.') === false) {
192
            return $this->offsetGetScalar($offset);
193
        } else {
194
            $keys = explode('.', $offset);
195
            $scope = $this->data;
196
            foreach ($keys as $key) {
197
                if ((is_array($scope) || $scope instanceof ArrayAccess) && isset($scope[$key])) {
198
                    $scope = $scope[$key];
199
                } else if (is_object($scope) && isset($scope->{$key})) {
200
                    $scope = $scope->{$key};
201
                } else {
202
                    throw new \InvalidArgumentException('Non existent offset given in offset chain: '.$key);
203
                }
204
            }
205
206
            return $scope;
207
        }
208
    }
209
210
    /**
211
     * Converts data to string
212
     * @return string
213
     */
214
    public function __toString()
215
    {
216
        return $this->serialize();
217
    }
218
219
    /**
220
     * Serializes the data using serializer
221
     * @return string
222
     */
223
    private function serialize()
224
    {
225
        if ($this->serializer === null) {
226
            return 'Serializer not set';
227
        }
228
229
        return $this->serializer->serialize($this->data);
230
    }
231
232
    /**
233
     * @return DTOSerializerInterface
234
     */
235
    public function getSerializer()
236
    {
237
        return $this->serializer;
238
    }
239
240
    /**
241
     * @param DTOSerializerInterface $serializer
242
     * @return DTOBase
243
     */
244
    public function setSerializer(DTOSerializerInterface $serializer)
245
    {
246
        $this->serializer = $serializer;
247
248
        return $this;
249
    }
250
}