Completed
Pull Request — master (#2)
by René
02:13
created

EntityProperty::formatValueForDatabase()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 20
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 20
ccs 0
cts 9
cp 0
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 9
nc 3
nop 1
crap 12
1
<?php
2
declare(strict_types = 1);
3
4
namespace Zortje\MVC\Model\Table\Entity;
5
6
use Ramsey\Uuid\Uuid;
7
use Zortje\MVC\Model\Table\Entity\Exception\EntityPropertyTypeNonexistentException;
8
use Zortje\MVC\Model\Table\Entity\Exception\EntityPropertyTypeNotImplementedException;
9
use Zortje\MVC\Model\Table\Entity\Exception\EntityPropertyValueExceedingLengthException;
10
use Zortje\MVC\Model\Table\Entity\Exception\InvalidENUMValueForEntityPropertyException;
11
use Zortje\MVC\Model\Table\Entity\Exception\InvalidUUIDValueForEntityPropertyException;
12
use Zortje\MVC\Model\Table\Entity\Exception\InvalidValueTypeForEntityPropertyException;
13
14
/**
15
 * Class EntityProperty
16
 *
17
 * @package Zortje\MVC\Model\Table\Entity
18
 */
19
class EntityProperty
20
{
21
22
    const STRING = 'string';
23
    const INTEGER = 'integer';
24
    const FLOAT = 'float';
25
    const DOUBLE = 'double';
26
    const BOOL = 'bool';
27
28
    const DATE = 'date';
29
    const DATETIME = 'datetime';
30
31
    const VARBINARY = 'varbinary';
32
33
    const UUID = 'uuid';
34
35
    const ENUM = 'enum';
36
37
    /**
38
     * @var string Entity property type
39
     */
40
    protected $type;
41
42
    /**
43
     * @var int Entity property max length
44
     */
45
    protected $length;
46
47
    /**
48
     * @var array Allowed values
49
     */
50
    protected $values;
51
52
    /**
53
     * @param string|array $type
54
     */
55 13
    public function __construct($type)
56
    {
57 13
        if (is_array($type)) {
58
            /**
59
             * Type
60
             */
61 3
            if (!isset($type['type'])) {
62 1
                throw new \InvalidArgumentException('Index "type" not found in parameter array');
63
            }
64
65 2
            $this->setType($type['type']);
66
67
            /**
68
             * Length
69
             */
70 2
            if (isset($type['length']) && is_numeric($type['length'])) {
71 1
                $this->length = (int)$type['length'];
72
            }
73
74
            /**
75
             * Values
76
             */
77 2
            if (isset($type['values']) && is_array($type['values'])) {
78 2
                $this->values = array_fill_keys($type['values'], true);
79
            }
80
        } else {
81 10
            $this->setType($type);
82
        }
83 12
    }
84
85
    /**
86
     * Set entity property type
87
     *
88
     * @param string $type
89
     *
90
     * @throws EntityPropertyTypeNonexistentException
91
     */
92 13
    protected function setType(string $type)
93
    {
94 13
        if (!defined(EntityProperty::class . '::' . strtoupper($type))) {
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
95 1
            throw new EntityPropertyTypeNonexistentException([$type]);
96
        }
97
98 12
        $this->type = $type;
99 12
    }
100
101
    /**
102
     * Validate value for entity property
103
     *
104
     * @param mixed $value Entity property value
105
     *
106
     * @return bool TRUE if valid, otherwise FALSE
107
     *
108
     * @throws EntityPropertyTypeNotImplementedException If entity property type is not implemented
109
     * @throws EntityPropertyValueExceedingLengthException If value is exceeding allowed length for entity property
110
     * @throws InvalidENUMValueForEntityPropertyException If ENUM value is invalid for entity property
111
     * @throws InvalidUUIDValueForEntityPropertyException If UUID value is invalid for entity property
112
     * @throws InvalidValueTypeForEntityPropertyException If value is invalid for entity property
113
     */
114
    public function validateValue($value): bool
115
    {
116
        if (is_null($value)) {
117
            return true;
118
        }
119
120
        switch ($this->type) {
121
            case self::STRING:
122 View Code Duplication
                if (!is_string($value)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
123
                    throw new InvalidValueTypeForEntityPropertyException([gettype($value), $this->type]);
124
                }
125
126
                /**
127
                 * Check length
128
                 */
129
                $length = strlen($value);
130
131
                if (!is_null($this->length) && $length > $this->length) {
132
                    throw new EntityPropertyValueExceedingLengthException($value, $length);
133
                }
134
135
                break;
136
137
            case self::INTEGER:
138
                if (!is_int($value)) {
139
                    throw new InvalidValueTypeForEntityPropertyException([gettype($value), $this->type]);
140
                }
141
142
                break;
143
144
            case self::FLOAT:
145
                if (!is_float($value)) {
146
                    throw new InvalidValueTypeForEntityPropertyException([gettype($value), $this->type]);
147
                }
148
149
                break;
150
151
            case self::DOUBLE:
152
                if (!is_double($value)) {
153
                    throw new InvalidValueTypeForEntityPropertyException([gettype($value), $this->type]);
154
                }
155
156
                break;
157
158
            case self::BOOL:
159
                if (!is_bool($value)) {
160
                    throw new InvalidValueTypeForEntityPropertyException([gettype($value), $this->type]);
161
                }
162
163
                break;
164
165
            case self::DATE:
166
            case self::DATETIME:
167
                if (!is_object($value) || (is_object($value) && get_class($value) !== \DateTime::class)) {
168
                    throw new InvalidValueTypeForEntityPropertyException([gettype($value), $this->type]);
169
                }
170
171
                break;
172
173
            case self::VARBINARY:
174
                // @todo Implement this
175
176
                break;
177
178
            case self::UUID:
179 View Code Duplication
                if (!is_string($value)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
180
                    throw new InvalidValueTypeForEntityPropertyException([gettype($value), $this->type]);
181
                }
182
183
                /**
184
                 * Check UUID
185
                 */
186
                if (!Uuid::isValid($value)) {
187
                    throw new InvalidUUIDValueForEntityPropertyException($value);
188
                }
189
190
                break;
191
192
            case self::ENUM:
193 View Code Duplication
                if (!is_string($value)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
194
                    throw new InvalidValueTypeForEntityPropertyException([gettype($value), $this->type]);
195
                }
196
197
                /**
198
                 * Check values
199
                 */
200
                if (!isset($this->values[$value])) {
201
                    throw new InvalidENUMValueForEntityPropertyException($value);
202
                }
203
204
                break;
205
206
            default:
207
                throw new EntityPropertyTypeNotImplementedException($this->type);
208
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
209
        }
210
211
        return true;
212
    }
213
214
    /**
215
     * Format value according to entity property type
216
     *
217
     * @param mixed $value Value
218
     *
219
     * @return mixed Value
220
     *
221
     * @throws EntityPropertyTypeNotImplementedException If entity property type is not implemented
222
     */
223
    public function formatValueForEntity($value)
224
    {
225
        switch ($this->type) {
226
            case self::STRING:
227
            case self::UUID:
228
                $value = "$value";
229
                break;
230
231
            case self::INTEGER:
232
                $value = (int)$value;
233
                break;
234
235
            case self::FLOAT:
236
            case self::DOUBLE:
237
                $value = (float)$value;
238
                break;
239
240
            case self::DATE:
241
            case self::DATETIME:
242
                $value = new \DateTime($value);
243
                break;
244
245
            case self::VARBINARY:
246
                // @todo Implement this
247
248
                break;
249
250
            case self::BOOL:
251
                $value = $value === '1'; // @todo test that this works
252
                break;
253
254
            case self::ENUM:
255
                // @todo Implement this
256
257
                break;
258
259
            default:
260
                throw new EntityPropertyTypeNotImplementedException($this->type);
261
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
262
        }
263
264
        return $value;
265
    }
266
267
    /**
268
     * Format value for insertion into the database
269
     *
270
     * @param mixed $value Value
271
     *
272
     * @return mixed Value
273
     */
274
    public function formatValueForDatabase($value)
275
    {
276
        switch ($this->type) {
277
            case self::DATE:
278
                /**
279
                 * @var \DateTime $value
280
                 */
281
                $value = $value->format('Y-m-d');
282
                break;
283
284
            case self::DATETIME:
285
                /**
286
                 * @var \DateTime $value
287
                 */
288
                $value = $value->format('Y-m-d H:i:s');
289
                break;
290
        }
291
292
        return $value;
293
    }
294
}
295