Test Failed
Push — master ( 25e774...03d801 )
by Nelson
03:48
created

Version::getBuild()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 1
cts 1
cp 1
crap 1
rs 10
c 0
b 0
f 0
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
 * @property-read int               $major    Obtiene el valor del componente principal del número de versión. Esta
29
 *   propiedad es de sólo lectura.
30
 * @property-read int               $minor    Obtiene el valor del componente secundario del número de versión. Esta
31
 *   propiedad es de sólo lectura.
32
 * @property-read VersionComponent  $build    Obtiene el valor del componente de compilación del número de versión.
33
 *   Esta propiedad es de sólo lectura.
34
 * @property-read VersionComponent  $revision Obtiene el valor del componente de revisión del número de versión. Esta
35
 *   propiedad es de sólo lectura.
36
 *
37
 * */
38
final class Version extends StrictObject implements IEquatable, IComparable
39
{
40
41
    /**
42
     * Crea una nueva instancia con los números principal, secundario, de
43
     * compilación (opcional) y revisión (opcional).
44
     * Para comprobar si la versión es válida, usar el método isValid.
45
     *
46
     * @param int                              $major    Componente principal
47
     * @param int                              $minor    Componente secundario
48
     * @param int|string|VersionComponent|null $build    Componente de compilación
49
     * @param int|string|VersionComponent|null $revision Componente de revisión
50
     *
51
     * @throws InvalidArgumentException
52
     * */
53 33
    public function __construct(int $major, int $minor, $build = null, $revision = null)
54
    {
55 33
        parent::__construct();
56
57 33
        if ($major < 0) {
58
            $args = [
59 3
                'name'     => 'major',
60 3
                'pos'      => 0,
61 3
                'actual'   => $major,
62 3
            ];
63 3
64
            $msg  = msg('Invalid argument value.');
65
            $msg .= msg(
66 3
                ' "{name}" (position {pos}) must to be a positive number; "{actual}" given.',
67 3
                $args
68 3
            );
69 3
70
            throw new InvalidArgumentException($msg);
71 3
        }
72
73 3
        if ($minor < 0) {
74
            $args = [
75
                'name'     => 'minor',
76 30
                'pos'      => 1,
77
                'actual'   => $minor,
78 2
            ];
79 2
80 2
            $msg  = msg('Invalid argument value.');
81 2
            $msg .= msg(
82 2
                ' "{name}" (position {pos}) must to be a positive number; "{actual}" given.',
83
                $args
84
            );
85 2
86 2
            throw new InvalidArgumentException($msg);
87 2
        }
88 2
89
        $this->major    = $major;
0 ignored issues
show
Bug introduced by
The property major is declared read-only in NelsonMartell\Version.
Loading history...
90 2
        $this->minor    = $minor;
0 ignored issues
show
Bug introduced by
The property minor is declared read-only in NelsonMartell\Version.
Loading history...
91
        $this->build    = VersionComponent::parse($build);
0 ignored issues
show
Bug introduced by
The property build is declared read-only in NelsonMartell\Version.
Loading history...
92 2
        $this->revision = VersionComponent::parse($revision);
0 ignored issues
show
Bug introduced by
The property revision is declared read-only in NelsonMartell\Version.
Loading history...
93
    }
94
95 28
    /**
96
     * Convierte una cadena a su representación del tipo Version.
97 1
     *
98 1
     * @param Version|string|int|float|array $value Objeto a convertir.
99 1
     *
100
     * @return Version Objeto convertido desde $value.
101
     * */
102 1
    public static function parse($value) : Version
103 1
    {
104 1
        if ($value instanceof Version) {
105 1
            return $value;
106
        }
107
108 1
        $version = [];
109
110
        // Try to convert into an array
111 27
        if (is_integer($value)) {
112
            // Integer for major value
113 1
            $version = [$value, 0];
114 1
        } elseif (is_float($value)) {
115 1
            // Integer part as major, and decimal part as minor
116
            $version = sprintf('%F', $value);
117
            $version = explode('.', $version);
118 1
        } elseif (is_array($value)) {
119 1
            // Implode first 4 places for major, minor, build and revision respectivally.
120 1
            $version = array_slice($value, 0, 4);
121 1
        } elseif (is_string($value)) {
0 ignored issues
show
introduced by
The condition is_string($value) is always true.
Loading history...
122
            $version = explode('.', $value);
123
        } else {
124 1
            $msg = msg('Unable to parse. Argument passed has an invalid type: "{0}".', typeof($value));
125
            throw new InvalidArgumentException($msg);
126
        }
127 26
128 26
        // $value ya debería ser un array.
129 26
        $c = count($version);
130 25
131 23
        if ($c > 4 || $c < 2) {
132
            $msg = msg('Unable to parse. Argument passed has an invalid format: "{0}".', $value);
133
            throw new InvalidArgumentException($msg);
134
        }
135
136
137
        $major    = (int) $version[0];
138
        $minor    = (int) $version[1];
139
        $build    = null;
140 11
        $revision = null;
141
142 11
        if (count($version) >= 3) {
143
            $build = VersionComponent::Parse($version[2]);
144
145
            if (count($version) == 4) {
146 11
                $revision = VersionComponent::Parse($version[3]);
147
            }
148
        }
149 11
150
        return new Version($major, $minor, $build, $revision);
151
    }
152 11
153
    private $major;
154
155
    /**
156 11
     * Getter for major property.
157
     *
158 5
     * @return int
159 6
     * @see    Version::$major
160 6
     */
161
    protected function getMajor()
162
    {
163
        return $this->major;
164
    }
165
166
    private $minor;
167 11
168
    /**
169 11
     * Getter for minor property.
170 4
     *
171 4
     * @return int
172
     * @see    Version::$minor
173
     */
174
    protected function getMinor()
175 9
    {
176 9
        return $this->minor;
177 9
    }
178 9
179
    private $build;
180 9
181 7
    /**
182
     * Getter for build property.
183 7
     *
184 1
     * @return VersionComponent
185
     * @see    Version::$build
186
     */
187
    protected function getBuild() : VersionComponent
188 9
    {
189
        return $this->build;
190
    }
191
192
    private $revision;
193
194
    /**
195
     * Getter for revision property.
196
     *
197
     * @return VersionComponent
198
     * @see    Version::$revision
199 1
     */
200
    protected function getRevision() : VersionComponent
201 1
    {
202
        return $this->revision;
203
    }
204
205
206
    /**
207
     * Convierte la instancia actual en su representación en cadena.
208
     * Por defecto, si no están definidos los componentes de compilación y
209
     * revisión, no se incluyen en la salida.
210
     * Use el método isValid si quiere determinar si la versión es válida
211
     * antes de devolver esta cadena.
212 1
     *
213
     * @return string Representación de la versión en forma de cadena:
214 1
     *   'major.minor[.build[.revision]]'
215
     * @see    VersionComponent::isNull()
216
     * @see    Version::isValid()
217
     * */
218
    public function toString() : string
219
    {
220
        $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...
221
        $s[1] = $this->minor;
222
223
        if ($this->revision->isNotNull()) {
224
            $s[2] = $this->build;
225 1
            $s[3] = $this->revision;
226
        } else {
227 1
            if ($this->build->isNotNull()) {
228
                $s[2] = $this->build;
229
            }
230
        }
231
        $v = implode('.', $s);
232
233
        return $v;
234
    }
235
236
    /**
237
     * Indica si la instancia actual es un número de versión válido.
238 1
     *
239
     * Se considera válido si:
240 1
     * 1. major o minor es mayor a cero (0). No puede ser '0.0'.
241
     * 2. build y revision son nulos (no están definidos).
242
     * 3. build está definido pero revision no.
243
     * 4. Ambos están definidos, pero no poseen la parte de la cadena.
244
     * 5. Ambos están definidos, pero build no posee la parte de cadena.
245
     * 6. build está definido y tiene la cadena, pero revision no está definido.
246
     * 7. revision posee cadena, pero build no.
247
     *
248
     * @return bool Un valor que indica si la instancia actual es válida.
249
     * */
250
    public function isValid() : bool
251
    {
252
        // Validación de major y minor:
253
        $r = ($this->major > 0 || $this->minor > 0); // 1
254
255
        // Validación de build y revision:
256 16
        if ($r) {
257
            $r = ($this->build->isNull() && $this->revision->isNull()); // 2
258 16
259 16
            if (!$r) {
260
                if ($this->build->isNotNull() && $this->revision->isNotNull()) {
261 16
                    // Si ambos están definidos...
262 6
263 6
                    $r = (bool) ($this->build->stringValue == ''); // 5
264
265 10
                    if (!$r) {
266 6
                        // 4
267
                        $r = (bool) (($this->build->stringValue == '') && ($this->revision->stringValue == ''));
268
269 16
                        if (!$r) {
270
                            if ($this->build->stringValue != '') {
271 16
                                $r = $this->revision->isNull(); // 6
272
                            }
273
274
                            if ($this->revision->stringValue != '') {
275
                                $r = ($this->build->stringValue == ''); // 7
276
                            }
277
                        }
278
                    }
279
                } else {
280
                    $r = ($this->build->isNotNull() && $this->revision->isNull()); // 3
281
                }
282
            }
283
        }
284
285
        return (bool) $r;
286
    }
287
288 16
    /**
289
     * Indicates whether the specified object is equal to the current instance.
290
     *
291 16
     * @param Version $other El otro objeto a comparar.
292
     *
293
     * @return bool `true` si $other es igual esta instancia; caso contrario,
294 16
     *   `false`.
295 15
     * */
296
    public function equals($other)
297 15
    {
298 13
        if ($other instanceof Version) {
0 ignored issues
show
introduced by
$other is always a sub-type of NelsonMartell\Version.
Loading history...
299
            if ($this->major == $other->major && $this->minor == $other->minor) {
300
                if ($this->build->equals($other->build)) {
301 9
                    if ($this->revision->equals($other->revision)) {
302
                        return true;
303 9
                    }
304
                }
305 6
            }
306
        }
307 6
308 6
        return false;
309 6
    }
310
311
312 6
    // region IComparable
313 9
314
    /**
315
     * Determina la posición relativa de esta instancia con respecto al objeto especificado.
316
     *
317
     * For types different than ``Version``:
318 4
     * - ``integer`` and ``null`` are always < 0;
319
     * - ``string`` and ``array`` are parsed and then evaluated (if is not parseable, always > 0);
320
     * - other types are always > 0
321
     *
322
     * @param Version|int|string|mixed $other
323 16
     *   The other object to compare with.
324
     *
325
     * @return int|null
326
     *   Returns:
327
     *   - ``= 0`` if this instance is considered equivalent to $other;
328
     *   - ``> 0`` si esta instancia se considera mayor a $other;
329
     *   - ``< 0`` si esta instancia se considera menor a $other.
330
     *   - ``null`` if this instance can't be compared against $other .
331
     * @see StrictObject::compare()
332
     * */
333
    public function compareTo($other)
334 32
    {
335
        $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

335
        $r = $this->equals(/** @scrutinizer ignore-type */ $other) ? 0 : 9999;
Loading history...
336 32
337 25
        if (!($other instanceof Version)) {
338 16
            switch (typeof($other)->toString()) {
339 11
                case 'integer':
340 10
                case 'float':
341
                case 'double':
342
                case 'null':
343
                case 'NULL':
344
                    $r = 1; // Siempre es mayor a cualquier número o null
345
                    break;
346 29
347
                case 'string':
348
                case 'array':
349
                    // Se tratan de convertir las cadenas y arrays
350
                    try {
351
                        $tmp = Version::parse($other);
352
                        $r   = $this->compareTo($tmp);
353
                    } catch (InvalidArgumentException $e) {
354
                        // Siempre es mayor a strings o arrays que no se puedan convertir
355
                        $r = 1;
356
                    }
357
                    break;
358
359
                default:
360
                    // No se puede determinar comparando a otros objetos.
361
                    $r = null;
362
            }
363
364
            return $r;
365
        }
366
367
        if ($r !== 0) {
368
            $r = $this->major - $other->major;
369
370
            if ($r === 0) {
371 24
                $r = $this->minor - $other->minor;
372
373 24
                if ($r === 0) {
374
                    $r = $this->build->compareTo($other->build);
375 24
376 17
                    if ($r === 0) {
377 17
                        $r = $this->revision->compareTo($other->revision);
378 14
                    }
379 14
                }
380 13
            }
381 13
        }
382 4
383 4
        return $r;
384
    }
385 13
386 7
    // endregion
387
}
388