Completed
Pull Request — master (#2)
by René
04:17 queued 02:16
created

EntityProperty   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 283
Duplicated Lines 3.18 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 15.69%

Importance

Changes 0
Metric Value
wmc 49
lcom 1
cbo 7
dl 9
loc 283
ccs 16
cts 102
cp 0.1569
rs 8.5454
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 29 7
A setType() 0 8 2
D validateValue() 9 99 26
C formatValueForEntity() 0 50 11
A formatValueForDatabase() 0 20 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like EntityProperty often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use EntityProperty, and based on these observations, apply Extract Interface, too.

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
226
        // @todo handle null values from database
227
228
        switch ($this->type) {
229
            case self::STRING:
230
                $value = "$value";
231
                break;
232
233
            case self::INTEGER:
234
                $value = (int)$value;
235
                break;
236
237
            case self::FLOAT:
238
            case self::DOUBLE:
239
                $value = (float)$value;
240
                break;
241
242
            case self::DATE:
243
            case self::DATETIME:
244
                $value = new \DateTime($value);
245
                break;
246
247
            case self::VARBINARY:
248
                // @todo Implement this
249
250
                break;
251
252
            case self::BOOL:
253
                $value = $value === '1'; // @todo test that this works
254
                break;
255
256
            case self::UUID:
257
                // @todo Implement this
258
259
                break;
260
261
            case self::ENUM:
262
                // @todo Implement this
263
264
                break;
265
266
            default:
267
                throw new EntityPropertyTypeNotImplementedException($this->type);
268
                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...
269
        }
270
271
        return $value;
272
    }
273
274
    /**
275
     * Format value for insertion into the database
276
     *
277
     * @param mixed $value Value
278
     *
279
     * @return mixed Value
280
     */
281
    public function formatValueForDatabase($value)
282
    {
283
        switch ($this->type) {
284
            case self::DATE:
285
                /**
286
                 * @var \DateTime $value
287
                 */
288
                $value = $value->format('Y-m-d');
289
                break;
290
291
            case self::DATETIME:
292
                /**
293
                 * @var \DateTime $value
294
                 */
295
                $value = $value->format('Y-m-d H:i:s');
296
                break;
297
        }
298
299
        return $value;
300
    }
301
}
302