Passed
Push — master ( f5d129...209660 )
by Nelson
02:26
created

Version   C

Complexity

Total Complexity 56

Size/Duplication

Total Lines 385
Duplicated Lines 0 %

Test Coverage

Coverage 95.92%

Importance

Changes 0
Metric Value
eloc 150
dl 0
loc 385
ccs 141
cts 147
cp 0.9592
rs 5.5199
c 0
b 0
f 0
wmc 56

10 Methods

Rating   Name   Duplication   Size   Complexity  
A getMajor() 0 3 1
C compareTo() 0 51 15
A equals() 0 13 6
A toString() 0 16 3
B __construct() 0 78 5
A getMinor() 0 3 1
A getBuild() 0 3 1
A getRevision() 0 3 1
B parse() 0 50 10
C isValid() 0 36 13

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
2
/**
3
 * PHP: Nelson Martell Library file
4
 *
5
 * Content:
6
 * - Class definition:  [NelsonMartell]  Version
7
 *
8
 * Copyright © 2015-2017 Nelson Martell (http://nelson6e65.github.io)
9
 *
10
 * Licensed under The MIT License (MIT)
11
 * For full copyright and license information, please see the LICENSE
12
 * Redistributions of files must retain the above copyright notice.
13
 *
14
 * @copyright 2015-2017 Nelson Martell
15
 * @link      http://nelson6e65.github.io/php_nml/
16
 * @since     0.1.1
17
 * @license   http://www.opensource.org/licenses/mit-license.php The MIT License (MIT)
18
 * */
19
20
namespace NelsonMartell;
21
22
use \InvalidArgumentException;
23
24
/**
25
 * Representa el número de versión de un programa o ensamblado, de la forma "1.2.3.4". Sólo
26
 * siendo obligatorios el primer y segundo componente.
27
 * No se puede heredar esta clase.
28
 *
29
 * @author Nelson Martell <[email protected]>
30
 * @since 0.1.1
31
 * @property-read int               $major    Obtiene el valor del componente principal del número de versión. Esta
1 ignored issue
show
Coding Style introduced by
This line exceeds maximum limit of 100 characters; contains 115 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
32
 *   propiedad es de sólo lectura.
33
 * @property-read int               $minor    Obtiene el valor del componente secundario del número de versión. Esta
1 ignored issue
show
Coding Style introduced by
This line exceeds maximum limit of 100 characters; contains 116 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
34
 *   propiedad es de sólo lectura.
35
 * @property-read VersionComponent  $build    Obtiene el valor del componente de compilación del número de versión.
1 ignored issue
show
Coding Style introduced by
This line exceeds maximum limit of 100 characters; contains 115 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
36
 *   Esta propiedad es de sólo lectura.
37
 * @property-read VersionComponent  $revision Obtiene el valor del componente de revisión del número de versión. Esta
1 ignored issue
show
Coding Style introduced by
This line exceeds maximum limit of 100 characters; contains 117 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
38
 *   propiedad es de sólo lectura.
39
 *
40
 * */
41
final class Version extends StrictObject implements IEquatable, IComparable
42
{
43
44
    /**
45
     * Crea una nueva instancia con los números principal, secundario, de
46
     * compilación (opcional) y revisión (opcional).
47
     * Para comprobar si la versión es válida, usar el método isValid.
48
     *
49
     * @param int                              $major    Componente principal
50
     * @param int                              $minor    Componente secundario
51
     * @param int|string|VersionComponent|null $build    Componente de compilación
52
     * @param int|string|VersionComponent|null $revision Componente de revisión
53
     *
54
     * @throws InvalidArgumentException
55
     * */
56 33
    public function __construct($major, $minor, $build = null, $revision = null)
57
    {
58 33
        parent::__construct();
59
60 33
        if (!is_integer($major)) {
0 ignored issues
show
introduced by
The condition is_integer($major) is always true.
Loading history...
61
            $args = [
62 3
                'class'    => typeof($this)->Name,
63 3
                'name'     => 'major',
64 3
                'pos'      => 0,
65 3
                'expected' => typeof(0),
66 3
                'actual'   => typeof($major),
67
            ];
68
69 3
            $msg = msg('Invalid argument type.');
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
70 3
            $msg .= msg(
71 3
                ' "{name}" (position {pos}) must to be an instance of "{expected}"; "{actual}" given.',
1 ignored issue
show
Coding Style introduced by
This line exceeds maximum limit of 100 characters; contains 103 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
72 3
                $args
73
            );
74 3
            $msg .= msg(' Convert value or use the "{class}::parse" (static) method.', $args);
75
76 3
            throw new InvalidArgumentException($msg);
77
        }
78
79 30
        if (!is_integer($minor)) {
0 ignored issues
show
introduced by
The condition is_integer($minor) is always true.
Loading history...
80
            $args = [
81 2
                'class'    => typeof($this)->Name,
82 2
                'name'     => 'minor',
83 2
                'pos'      => 1,
84 2
                'expected' => typeof(0),
85 2
                'actual'   => typeof($minor),
86
            ];
87
88 2
            $msg = msg('Invalid argument type.');
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
89 2
            $msg .= msg(
90 2
                ' "{name}" (position {pos}) must to be an instance of "{expected}"; "{actual}" given.',
1 ignored issue
show
Coding Style introduced by
This line exceeds maximum limit of 100 characters; contains 103 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
91 2
                $args
92
            );
93 2
            $msg .= msg(' Convert value or use the "{class}::parse" (static) method.', $args);
94
95 2
            throw new InvalidArgumentException($msg);
96
        }
97
98 28
        if ($major < 0) {
99
            $args = [
100 1
                'name'     => 'major',
101 1
                'pos'      => 0,
102 1
                'actual'   => $major,
103
            ];
104
105 1
            $msg = msg('Invalid argument value.');
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
106 1
            $msg .= msg(
107 1
                ' "{name}" (position {pos}) must to be a positive number; "{actual}" given.',
108 1
                $args
109
            );
110
111 1
            throw new InvalidArgumentException($msg);
112
        }
113
114 27
        if ($minor < 0) {
115
            $args = [
116 1
                'name'     => 'minor',
117 1
                'pos'      => 1,
118 1
                'actual'   => $minor,
119
            ];
120
121 1
            $msg = msg('Invalid argument value.');
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
122 1
            $msg .= msg(
123 1
                ' "{name}" (position {pos}) must to be a positive number; "{actual}" given.',
124 1
                $args
125
            );
126
127 1
            throw new InvalidArgumentException($msg);
128
        }
129
130 26
        $this->major = $major;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
Bug introduced by
The property major is declared read-only in NelsonMartell\Version.
Loading history...
131 26
        $this->minor = $minor;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
Bug introduced by
The property minor is declared read-only in NelsonMartell\Version.
Loading history...
132 26
        $this->build = VersionComponent::parse($build);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
Bug introduced by
The property build is declared read-only in NelsonMartell\Version.
Loading history...
133 25
        $this->revision = VersionComponent::parse($revision);
0 ignored issues
show
Bug introduced by
The property revision is declared read-only in NelsonMartell\Version.
Loading history...
134 23
    }
135
136
    /**
137
     * Convierte una cadena a su representación del tipo Version.
138
     *
139
     * @param Version|string|int|float|array $value Objeto a convertir.
140
     *
141
     * @return Version Objeto convertido desde $value.
142
     * */
143 11
    public static function parse($value)
144
    {
145 11
        if ($value instanceof Version) {
146
            return $value;
147
        }
148
149 11
        $version = [];
150
151
        // Try to convert into an array
152 11
        if (is_integer($value)) {
153
            // Integer for major value
154
            $version = [$value, 0];
155 11
        } elseif (is_float($value)) {
156
            // Integer part as major, and decimal part as minor
157
            $version = sprintf("%F", $value);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal %F does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
158
            $version = explode('.', $version);
159 11
        } elseif (is_array($value)) {
160
            // Implode first 4 places for major, minor, build and revision respectivally.
161 5
            $version = array_slice($value, 0, 4);
162 6
        } elseif (is_string($value)) {
0 ignored issues
show
introduced by
The condition is_string($value) is always true.
Loading history...
163 6
            $version = explode('.', $value);
164
        } else {
165
            $msg = msg('Unable to parse. Argument passed has an invalid type: "{0}".', typeof($value));
1 ignored issue
show
Coding Style introduced by
This line exceeds maximum limit of 100 characters; contains 103 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
166
            throw new InvalidArgumentException($msg);
167
        }
168
169
        // $value ya debería ser un array.
170 11
        $c = count($version);
171
172 11
        if ($c > 4 || $c < 2) {
173 4
            $msg = msg('Unable to parse. Argument passed has an invalid format: "{0}".', $value);
174
            //var_dump($version);
175 4
            throw new InvalidArgumentException($msg);
176
        }
177
178
179 9
        $major = (int) $version[0];
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
180 9
        $minor = (int) $version[1];
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
181 9
        $build = null;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
182 9
        $revision = null;
183
184 9
        if (count($version) >= 3) {
185 7
            $build = VersionComponent::Parse($version[2]);
186
187 7
            if (count($version) == 4) {
188 1
                $revision = VersionComponent::Parse($version[3]);
189
            }
190
        }
191
192 9
        return new Version($major, $minor, $build, $revision);
193
    }
194
195
    private $major;
196
197
    /**
198
     * Getter for major property.
199
     *
200
     * @return int
201
     * @see    Version::$major
202
     */
203 1
    protected function getMajor()
204
    {
205 1
        return $this->major;
206
    }
207
208
    private $minor;
209
210
    /**
211
     * Getter for minor property.
212
     *
213
     * @return int
214
     * @see    Version::$minor
215
     */
216 1
    protected function getMinor()
217
    {
218 1
        return $this->minor;
219
    }
220
221
    private $build;
222
223
    /**
224
     * Getter for build property.
225
     *
226
     * @return VersionComponent
227
     * @see    Version::$build
228
     */
229 1
    protected function getBuild()
230
    {
231 1
        return $this->build;
232
    }
233
234
    private $revision;
235
236
    /**
237
     * Getter for revision property.
238
     *
239
     * @return VersionComponent
240
     * @see    Version::$revision
241
     */
242 1
    protected function getRevision()
243
    {
244 1
        return $this->revision;
245
    }
246
247
248
    /**
249
     * Convierte la instancia actual en su representación en cadena.
250
     * Por defecto, si no están definidos los componentes de compilación y
251
     * revisión, no se incluyen en la salida.
252
     * Use el método isValid si quiere determinar si la versión es válida
253
     * antes de devolver esta cadena.
254
     *
255
     * @return string Representación de la versión en forma de cadena:
256
     *   'major.minor[.build[.revision]]'
257
     * @see    VersionComponent::isNull()
258
     * @see    Version::isValid()
259
     * */
260 16
    public function toString()
261
    {
262 16
        $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...
263 16
        $s[1] = $this->minor;
264
265 16
        if ($this->revision->isNotNull()) {
266 6
            $s[2] = $this->build;
267 6
            $s[3] = $this->revision;
268
        } else {
269 10
            if ($this->build->isNotNull()) {
270 6
                $s[2] = $this->build;
271
            }
272
        }
273 16
        $v = implode('.', $s);
274
275 16
        return $v;
276
    }
277
278
    /**
279
     * Indica si la instancia actual es un número de versión válido.
280
     *
281
     * Se considera válido si:
282
     * 1. major o minor es mayor a cero (0). No puede ser '0.0'.
283
     * 2. build y revision son nulos (no están definidos).
284
     * 3. build está definido pero revision no.
285
     * 4. Ambos están definidos, pero no poseen la parte de la cadena.
286
     * 5. Ambos están definidos, pero build no posee la parte de cadena.
287
     * 6. build está definido y tiene la cadena, pero revision no está definido.
288
     * 7. revision posee cadena, pero build no.
289
     *
290
     * @return bool Un valor que indica si la instancia actual es válida.
291
     * */
292 16
    public function isValid()
293
    {
294
        // Validación de major y minor:
295 16
        $r = ($this->major > 0 or $this->minor > 0); //#1
296
297
        // Validación de build y revision:
298 16
        if ($r) {
299 15
            $r = ($this->build->isNull() and $this->revision->isNull()); // #2
300
301 15
            if (!$r) {
302 13
                if ($this->build->isNotNull() and $this->revision->isNotNull()) {
303
                    // Si ambos están definidos...
304
305 9
                    $r = (bool) ($this->build->StringValue == ''); //#5
306
307 9
                    if (!$r) {
308
                        //#4
309 6
                        $r = (bool) (($this->build->StringValue == '') and ($this->revision->StringValue == ''));
1 ignored issue
show
Coding Style introduced by
This line exceeds maximum limit of 100 characters; contains 113 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
310
311 6
                        if (!$r) {
312 6
                            if ($this->build->StringValue != '') {
313 6
                                $r = $this->revision->isNull(); #6
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
314
                            }
315
316 6
                            if ($this->revision->StringValue != '') {
317 9
                                $r = ($this->build->StringValue == ''); #7
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
318
                            }
319
                        }
320
                    }
321
                } else {
322 4
                    $r = ($this->build->isNotNull() and $this->revision->isNull()); //#3
323
                }
324
            }
325
        }
326
327 16
        return (bool) $r;
328
    }
329
330
    /**
331
     * Determina si el objeto $other especificado es igual a la instancia actual.
332
     *
333
     * @param Version $other El otro objeto a comparar.
334
     *
335
     * @return bool `true` si $other es igual esta instancia; caso contrario,
336
     *   `false`.
337
     * */
338 32
    public function equals($other)
339
    {
340 32
        if ($other instanceof Version) {
0 ignored issues
show
introduced by
$other is always a sub-type of NelsonMartell\Version.
Loading history...
341 25
            if ($this->major == $other->major && $this->minor == $other->minor) {
342 16
                if ($this->build->equals($other->build)) {
343 11
                    if ($this->revision->equals($other->revision)) {
344 10
                        return true;
345
                    }
346
                }
347
            }
348
        }
349
350 29
        return false;
351
    }
352
353
354
    #region IComparable
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
355
356
    /**
357
     * Determina la posición relativa de esta instancia con respecto al objeto especificado.
358
     *
359
     * For types different than ``Version``:
360
     * - ``integer`` and ``null`` are always < 0;
361
     * - ``string`` and ``array`` are parsed and then evaluated (if is not parseable, always > 0);
362
     * - other types are always > 0
363
     *
364
     * @param Version|int|string|mixed $other
365
     *   The other object to compare with.
366
     *
367
     * @return int|null
368
     *   Returns:
369
     *   - ``= 0`` if this instance is considered equivalent to $other;
370
     *   - ``> 0`` si esta instancia se considera mayor a $other;
371
     *   - ``< 0`` si esta instancia se considera menor a $other.
372
     *   - ``null`` if this instance can't be compared against $other .
373
     * @see StrictObject::compare()
374
     * */
375 24
    public function compareTo($other)
376
    {
377 24
        $r = $this->equals($other) ? 0 : 9999;
0 ignored issues
show
Bug introduced by
It seems like $other can also be of type integer and string; however, parameter $other of NelsonMartell\Version::equals() does only seem to accept NelsonMartell\Version, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

377
        $r = $this->equals(/** @scrutinizer ignore-type */ $other) ? 0 : 9999;
Loading history...
378
379 24
        if (!($other instanceof Version)) {
380 17
            switch (typeof($other)->toString()) {
381 17
                case 'integer':
382 14
                case 'float':
383 14
                case 'double':
384 13
                case 'null':
385 13
                case 'NULL':
386 4
                    $r = 1; // Siempre es mayor a cualquier número o null
387 4
                    break;
388
389 13
                case 'string':
390 7
                case 'array':
391
                    // Se tratan de convertir las cadenas y arrays
392
                    try {
393 11
                        $tmp = Version::parse($other);
394 9
                        $r = $this->compareTo($tmp);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
395 4
                    } catch (InvalidArgumentException $e) {
396
                        // Siempre es mayor a strings o arrays que no se puedan convertir
397 4
                        $r = 1;
398
                    }
399 11
                    break;
400
401
                default:
402
                    // No se puede determinar comparando a otros objetos.
403 2
                    $r = null;
404
            }
405
406 17
            return $r;
407
        }
408
409 17
        if ($r !== 0) {
410 15
            $r = $this->major - $other->major;
411
412 15
            if ($r === 0) {
413 7
                $r = $this->minor - $other->minor;
414
415 7
                if ($r === 0) {
416 6
                    $r = $this->build->compareTo($other->build);
417
418 6
                    if ($r === 0) {
419 1
                        $r = $this->revision->compareTo($other->revision);
420
                    }
421
                }
422
            }
423
        }
424
425 17
        return $r;
426
    }
427
428
    #endregion
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
429
}
430