Passed
Push — master ( 071710...17a827 )
by Nelson
02:21
created

Version   B

Complexity

Total Complexity 51

Size/Duplication

Total Lines 327
Duplicated Lines 0 %

Test Coverage

Coverage 94.69%

Importance

Changes 0
Metric Value
eloc 111
dl 0
loc 327
ccs 107
cts 113
cp 0.9469
rs 7.92
c 0
b 0
f 0
wmc 51

10 Methods

Rating   Name   Duplication   Size   Complexity  
A toString() 0 16 3
A getMajor() 0 3 1
A __construct() 0 40 3
A getMinor() 0 3 1
A getBuild() 0 3 1
A getRevision() 0 3 1
B parse() 0 49 10
C isValid() 0 36 13
C compareTo() 0 38 12
A equals() 0 13 6

How to fix   Complexity   

Complex Class

Complex classes like Version 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.

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 Version, and based on these observations, apply Extract Interface, too.

1
<?php declare(strict_types=1);
2
/**
3
 * PHP: Nelson Martell Library file
4
 *
5
 * Copyright © 2015-2019 Nelson Martell (http://nelson6e65.github.io)
6
 *
7
 * Licensed under The MIT License (MIT)
8
 * For full copyright and license information, please see the LICENSE
9
 * Redistributions of files must retain the above copyright notice.
10
 *
11
 * @copyright 2015-2019 Nelson Martell
12
 * @link      http://nelson6e65.github.io/php_nml/
13
 * @since     0.1.1
14
 * @license   http://www.opensource.org/licenses/mit-license.php The MIT License (MIT)
15
 * */
16
17
namespace NelsonMartell;
18
19
use InvalidArgumentException;
20
21
/**
22
 * Representa el número de versión de un programa o ensamblado, de la forma "1.2.3.4". Sólo
23
 * siendo obligatorios el primer y segundo componente.
24
 * No se puede heredar esta clase.
25
 *
26
 * @author Nelson Martell <[email protected]>
27
 * @since 0.1.1
28
 *
29
 * @property-read int               $major    Obtiene el valor del componente principal del número de versión. Esta
30
 *   propiedad es de sólo lectura.
31
 * @property-read int               $minor    Obtiene el valor del componente secundario del número de versión. Esta
32
 *   propiedad es de sólo lectura.
33
 * @property-read VersionComponent  $build    Obtiene el valor del componente de compilación del número de versión.
34
 *   Esta propiedad es de sólo lectura.
35
 * @property-read VersionComponent  $revision Obtiene el valor del componente de revisión del número de versión. Esta
36
 *   propiedad es de sólo lectura.
37
 *
38
 * */
39
final class Version extends StrictObject implements IEquatable, IComparable, IMagicPropertiesContainer
40
{
41
    /**
42
     * The version metadata.
43
     *
44
     * @var array
45
     */
46
    private $versionMetaData = [];
47
48
    /**
49
     * Crea una nueva instancia con los números principal, secundario, de
50
     * compilación (opcional) y revisión (opcional).
51
     * Para comprobar si la versión es válida, usar el método isValid.
52
     *
53
     * @param int                              $major    Componente principal
54
     * @param int                              $minor    Componente secundario
55
     * @param int|string|VersionComponent|null $build    Componente de compilación
56
     * @param int|string|VersionComponent|null $revision Componente de revisión
57
     *
58
     * @throws InvalidArgumentException
59
     * */
60 28
    public function __construct(int $major, int $minor, $build = null, $revision = null)
61
    {
62 28
        parent::__construct();
63
64 28
        if ($major < 0) {
65
            $args = [
66 1
                'name'     => 'major',
67 1
                'pos'      => 0,
68 1
                'actual'   => $major,
69
            ];
70
71 1
            $msg  = msg('Invalid argument value.');
72 1
            $msg .= msg(
73 1
                ' "{name}" (position {pos}) must to be a positive number; "{actual}" given.',
74 1
                $args
75
            );
76
77 1
            throw new InvalidArgumentException($msg);
78
        }
79
80 27
        if ($minor < 0) {
81
            $args = [
82 1
                'name'     => 'minor',
83 1
                'pos'      => 1,
84 1
                'actual'   => $minor,
85
            ];
86
87 1
            $msg  = msg('Invalid argument value.');
88 1
            $msg .= msg(
89 1
                ' "{name}" (position {pos}) must to be a positive number; "{actual}" given.',
90 1
                $args
91
            );
92
93 1
            throw new InvalidArgumentException($msg);
94
        }
95
96 26
        $build    = VersionComponent::parse($build);
97 25
        $revision = VersionComponent::parse($revision);
98
99 23
        $this->versionMetaData = compact('major', 'minor', 'build', 'revision');
100 23
    }
101
102
    /**
103
     * Convierte una cadena a su representación del tipo Version.
104
     *
105
     * @param Version|string|int|float|array $value Objeto a convertir.
106
     *
107
     * @return Version Objeto convertido desde $value.
108
     * */
109 11
    public static function parse($value) : Version
110
    {
111 11
        if ($value instanceof Version) {
112
            return $value;
113
        }
114
115 11
        $version = [];
116
117
        // Try to convert into an array
118 11
        if (is_integer($value)) {
119
            // Integer for major value
120
            $version = [$value, 0];
121 11
        } elseif (is_float($value)) {
122
            // Integer part as major, and decimal part as minor
123
            $version = sprintf('%F', $value);
124
            $version = explode('.', $version);
125 11
        } elseif (is_array($value)) {
126
            // Implode first 4 places for major, minor, build and revision respectivally.
127 5
            $version = array_slice($value, 0, 4);
128 6
        } elseif (is_string($value)) {
0 ignored issues
show
introduced by
The condition is_string($value) is always true.
Loading history...
129 6
            $version = explode('.', $value);
130
        } else {
131
            $msg = msg('Unable to parse. Argument passed has an invalid type: "{0}".', typeof($value));
132
            throw new InvalidArgumentException($msg);
133
        }
134
135
        // $value ya debería ser un array.
136 11
        $c = count($version);
137
138 11
        if ($c > 4 || $c < 2) {
139 4
            $msg = msg('Unable to parse. Argument passed has an invalid format: "{0}".', $value);
140 4
            throw new InvalidArgumentException($msg);
141
        }
142
143
144 9
        $major    = (int) $version[0];
145 9
        $minor    = (int) $version[1];
146 9
        $build    = null;
147 9
        $revision = null;
148
149 9
        if (count($version) >= 3) {
150 7
            $build = VersionComponent::parse($version[2]);
151
152 7
            if (count($version) == 4) {
153 1
                $revision = VersionComponent::parse($version[3]);
154
            }
155
        }
156
157 9
        return new Version($major, $minor, $build, $revision);
158
    }
159
160
    /**
161
     * Getter for major property.
162
     *
163
     * @return int
164
     * @see    Version::$major
165
     */
166 42
    protected function getMajor()
167
    {
168 42
        return $this->versionMetaData['major'];
169
    }
170
171
    /**
172
     * Getter for minor property.
173
     *
174
     * @return int
175
     * @see    Version::$minor
176
     */
177 20
    protected function getMinor()
178
    {
179 20
        return $this->versionMetaData['minor'];
180
    }
181
182
    /**
183
     * Getter for build property.
184
     *
185
     * @return VersionComponent
186
     * @see    Version::$build
187
     */
188 32
    protected function getBuild() : VersionComponent
189
    {
190 32
        return $this->versionMetaData['build'];
191
    }
192
193
    /**
194
     * Getter for revision property.
195
     *
196
     * @return VersionComponent
197
     * @see    Version::$revision
198
     */
199 27
    protected function getRevision() : VersionComponent
200
    {
201 27
        return $this->versionMetaData['revision'];
202
    }
203
204
205
    /**
206
     * Convierte la instancia actual en su representación en cadena.
207
     * Por defecto, si no están definidos los componentes de compilación y
208
     * revisión, no se incluyen en la salida.
209
     * Use el método isValid si quiere determinar si la versión es válida
210
     * antes de devolver esta cadena.
211
     *
212
     * @return string Representación de la versión en forma de cadena:
213
     *   'major.minor[.build[.revision]]'
214
     * @see    VersionComponent::isNull()
215
     * @see    Version::isValid()
216
     * */
217 8
    public function toString() : string
218
    {
219 8
        $s[0] = $this->major;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$s was never initialized. Although not strictly required by PHP, it is generally a good practice to add $s = array(); before regardless.
Loading history...
220 8
        $s[1] = $this->minor;
221
222 8
        if ($this->revision->isNotNull()) {
223 3
            $s[2] = $this->build;
224 3
            $s[3] = $this->revision;
225
        } else {
226 5
            if ($this->build->isNotNull()) {
227 3
                $s[2] = $this->build;
228
            }
229
        }
230 8
        $v = implode('.', $s);
231
232 8
        return $v;
233
    }
234
235
    /**
236
     * Indica si la instancia actual es un número de versión válido.
237
     *
238
     * Se considera válido si:
239
     * 1. major o minor es mayor a cero (0). No puede ser '0.0'.
240
     * 2. build y revision son nulos (no están definidos).
241
     * 3. build está definido pero revision no.
242
     * 4. Ambos están definidos, pero no poseen la parte de la cadena.
243
     * 5. Ambos están definidos, pero build no posee la parte de cadena.
244
     * 6. build está definido y tiene la cadena, pero revision no está definido.
245
     * 7. revision posee cadena, pero build no.
246
     *
247
     * @return bool Un valor que indica si la instancia actual es válida.
248
     * */
249 16
    public function isValid() : bool
250
    {
251
        // Validación de major y minor:
252 16
        $r = ($this->major > 0 || $this->minor > 0); // 1
253
254
        // Validación de build y revision:
255 16
        if ($r) {
256 15
            $r = ($this->build->isNull() && $this->revision->isNull()); // 2
257
258 15
            if (!$r) {
259 13
                if ($this->build->isNotNull() && $this->revision->isNotNull()) {
260
                    // Si ambos están definidos...
261
262 9
                    $r = (bool) ($this->build->stringValue == ''); // 5
263
264 9
                    if (!$r) {
265
                        // 4
266 6
                        $r = (bool) (($this->build->stringValue == '') && ($this->revision->stringValue == ''));
267
268 6
                        if (!$r) {
269 6
                            if ($this->build->stringValue != '') {
270 6
                                $r = $this->revision->isNull(); // 6
271
                            }
272
273 6
                            if ($this->revision->stringValue != '') {
274 9
                                $r = ($this->build->stringValue == ''); // 7
275
                            }
276
                        }
277
                    }
278
                } else {
279 4
                    $r = ($this->build->isNotNull() && $this->revision->isNull()); // 3
280
                }
281
            }
282
        }
283
284 16
        return (bool) $r;
285
    }
286
287
    /**
288
     * Indicates whether the specified object is equal to the current instance.
289
     *
290
     * @param Version|mixed $other The other object to compare with.
291
     *
292
     * @return bool
293
     * */
294 33
    public function equals($other) : bool
295
    {
296 33
        if ($other instanceof Version) {
297 25
            if ($this->major == $other->major && $this->minor == $other->minor) {
298 16
                if ($this->build->equals($other->build)) {
299 11
                    if ($this->revision->equals($other->revision)) {
300 10
                        return true;
301
                    }
302
                }
303
            }
304
        }
305
306 30
        return false;
307
    }
308
309
310
    // region IComparable
311
312
    /**
313
     * Determina la posición relativa de esta instancia con respecto al objeto especificado.
314
     *
315
     * For types different than `Version` (`null`, unparseable `string` and other value types) are considered lower
316
     * than `Version`.
317
     *
318
     * @param Version|string|mixed $other The other object to compare with. If this is of type `string`, it will
319
     *   try to convert to a `Version` object before the comparation.
320
     *
321
     * @return int|null
322
     *   - ``= 0`` if this instance is considered equivalent to $other;
323
     *   - ``> 0`` si esta instancia se considera mayor a $other;
324
     *   - ``< 0`` si esta instancia se considera menor a $other.
325
     *   - ``null`` if this instance can't be compared against $other .
326
     * @see \NelsonMartell\Extensions\Objects::compare()
327
     * */
328 25
    public function compareTo($other)
329
    {
330 25
        $r = $this->equals($other) ? 0 : 9999;
331
332 25
        if ($r !== 0) {
333 23
            if ($other instanceof Version) {
334 15
                $r = $this->major - $other->major;
335
336 15
                if ($r === 0) {
337 7
                    $r = $this->minor - $other->minor;
338
339 7
                    if ($r === 0) {
340 6
                        $r = $this->build->compareTo($other->build);
341
342 6
                        if ($r === 0) {
343 15
                            $r = $this->revision->compareTo($other->revision);
344
                        }
345
                    }
346
                }
347 18
            } elseif (typeof($other)->isValueType() || $other === null) {
348 16
                $r = 1;
349
350 16
                if (typeof($other)->name === 'string' || typeof($other)->name === 'array') {
351
                    try {
352 11
                        $tmp = Version::parse($other);
353 9
                        $r   = $this->compareTo($tmp);
354 4
                    } catch (InvalidArgumentException $e) {
355
                        // Siempre es mayor a strings o arrays que no se puedan convertir
356 16
                        $r = 1;
357
                    }
358
                }
359
            } else {
360
                // No se puede determinar comparando a otros objetos.
361 2
                $r = null;
362
            }
363
        }
364
365 25
        return $r;
366
    }
367
368
    // endregion
369
}
370