Completed
Push — master ( ebe674...d0049c )
by Nelson
04:31
created

Version::getMajor()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
ccs 2
cts 2
cp 1
crap 1
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 28
    public function __construct(int $major, int $minor, $build = null, $revision = null)
54
    {
55 28
        parent::__construct();
56
57 28
        if ($major < 0) {
58
            $args = [
59 1
                'name'     => 'major',
60 1
                'pos'      => 0,
61 1
                'actual'   => $major,
62
            ];
63
64 1
            $msg  = msg('Invalid argument value.');
65 1
            $msg .= msg(
66 1
                ' "{name}" (position {pos}) must to be a positive number; "{actual}" given.',
67 1
                $args
68
            );
69
70 1
            throw new InvalidArgumentException($msg);
71
        }
72
73 27
        if ($minor < 0) {
74
            $args = [
75 1
                'name'     => 'minor',
76 1
                'pos'      => 1,
77 1
                'actual'   => $minor,
78
            ];
79
80 1
            $msg  = msg('Invalid argument value.');
81 1
            $msg .= msg(
82 1
                ' "{name}" (position {pos}) must to be a positive number; "{actual}" given.',
83 1
                $args
84
            );
85
86 1
            throw new InvalidArgumentException($msg);
87
        }
88
89 26
        $this->major    = $major;
0 ignored issues
show
Bug introduced by
The property major is declared read-only in NelsonMartell\Version.
Loading history...
90 26
        $this->minor    = $minor;
0 ignored issues
show
Bug introduced by
The property minor is declared read-only in NelsonMartell\Version.
Loading history...
91 26
        $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 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...
93 23
    }
94
95
    /**
96
     * Convierte una cadena a su representación del tipo Version.
97
     *
98
     * @param Version|string|int|float|array $value Objeto a convertir.
99
     *
100
     * @return Version Objeto convertido desde $value.
101
     * */
102 11
    public static function parse($value) : Version
103
    {
104 11
        if ($value instanceof Version) {
105
            return $value;
106
        }
107
108 11
        $version = [];
109
110
        // Try to convert into an array
111 11
        if (is_integer($value)) {
112
            // Integer for major value
113
            $version = [$value, 0];
114 11
        } elseif (is_float($value)) {
115
            // Integer part as major, and decimal part as minor
116
            $version = sprintf('%F', $value);
117
            $version = explode('.', $version);
118 11
        } elseif (is_array($value)) {
119
            // Implode first 4 places for major, minor, build and revision respectivally.
120 5
            $version = array_slice($value, 0, 4);
121 6
        } elseif (is_string($value)) {
0 ignored issues
show
introduced by
The condition is_string($value) is always true.
Loading history...
122 6
            $version = explode('.', $value);
123
        } else {
124
            $msg = msg('Unable to parse. Argument passed has an invalid type: "{0}".', typeof($value));
125
            throw new InvalidArgumentException($msg);
126
        }
127
128
        // $value ya debería ser un array.
129 11
        $c = count($version);
130
131 11
        if ($c > 4 || $c < 2) {
132 4
            $msg = msg('Unable to parse. Argument passed has an invalid format: "{0}".', $value);
133 4
            throw new InvalidArgumentException($msg);
134
        }
135
136
137 9
        $major    = (int) $version[0];
138 9
        $minor    = (int) $version[1];
139 9
        $build    = null;
140 9
        $revision = null;
141
142 9
        if (count($version) >= 3) {
143 7
            $build = VersionComponent::Parse($version[2]);
144
145 7
            if (count($version) == 4) {
146 1
                $revision = VersionComponent::Parse($version[3]);
147
            }
148
        }
149
150 9
        return new Version($major, $minor, $build, $revision);
151
    }
152
153
    private $major;
154
155
    /**
156
     * Getter for major property.
157
     *
158
     * @return int
159
     * @see    Version::$major
160
     */
161 1
    protected function getMajor()
162
    {
163 1
        return $this->major;
164
    }
165
166
    private $minor;
167
168
    /**
169
     * Getter for minor property.
170
     *
171
     * @return int
172
     * @see    Version::$minor
173
     */
174 1
    protected function getMinor()
175
    {
176 1
        return $this->minor;
177
    }
178
179
    private $build;
180
181
    /**
182
     * Getter for build property.
183
     *
184
     * @return VersionComponent
185
     * @see    Version::$build
186
     */
187 1
    protected function getBuild() : VersionComponent
188
    {
189 1
        return $this->build;
190
    }
191
192
    private $revision;
193
194
    /**
195
     * Getter for revision property.
196
     *
197
     * @return VersionComponent
198
     * @see    Version::$revision
199
     */
200 1
    protected function getRevision() : VersionComponent
201
    {
202 1
        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
     *
213
     * @return string Representación de la versión en forma de cadena:
214
     *   'major.minor[.build[.revision]]'
215
     * @see    VersionComponent::isNull()
216
     * @see    Version::isValid()
217
     * */
218 16
    public function toString() : string
219
    {
220 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...
221 16
        $s[1] = $this->minor;
222
223 16
        if ($this->revision->isNotNull()) {
224 6
            $s[2] = $this->build;
225 6
            $s[3] = $this->revision;
226
        } else {
227 10
            if ($this->build->isNotNull()) {
228 6
                $s[2] = $this->build;
229
            }
230
        }
231 16
        $v = implode('.', $s);
232
233 16
        return $v;
234
    }
235
236
    /**
237
     * Indica si la instancia actual es un número de versión válido.
238
     *
239
     * Se considera válido si:
240
     * 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 16
    public function isValid() : bool
251
    {
252
        // Validación de major y minor:
253 16
        $r = ($this->major > 0 || $this->minor > 0); // 1
254
255
        // Validación de build y revision:
256 16
        if ($r) {
257 15
            $r = ($this->build->isNull() && $this->revision->isNull()); // 2
258
259 15
            if (!$r) {
260 13
                if ($this->build->isNotNull() && $this->revision->isNotNull()) {
261
                    // Si ambos están definidos...
262
263 9
                    $r = (bool) ($this->build->stringValue == ''); // 5
264
265 9
                    if (!$r) {
266
                        // 4
267 6
                        $r = (bool) (($this->build->stringValue == '') && ($this->revision->stringValue == ''));
268
269 6
                        if (!$r) {
270 6
                            if ($this->build->stringValue != '') {
271 6
                                $r = $this->revision->isNull(); // 6
272
                            }
273
274 6
                            if ($this->revision->stringValue != '') {
275 9
                                $r = ($this->build->stringValue == ''); // 7
276
                            }
277
                        }
278
                    }
279
                } else {
280 4
                    $r = ($this->build->isNotNull() && $this->revision->isNull()); // 3
281
                }
282
            }
283
        }
284
285 16
        return (bool) $r;
286
    }
287
288
    /**
289
     * Indicates whether the specified object is equal to the current instance.
290
     *
291
     * @param Version $other El otro objeto a comparar.
292
     *
293
     * @return bool `true` si $other es igual esta instancia; caso contrario,
294
     *   `false`.
295
     * */
296 32
    public function equals($other)
297
    {
298 32
        if ($other instanceof Version) {
0 ignored issues
show
introduced by
$other is always a sub-type of NelsonMartell\Version.
Loading history...
299 25
            if ($this->major == $other->major && $this->minor == $other->minor) {
300 16
                if ($this->build->equals($other->build)) {
301 11
                    if ($this->revision->equals($other->revision)) {
302 10
                        return true;
303
                    }
304
                }
305
            }
306
        }
307
308 29
        return false;
309
    }
310
311
312
    // region IComparable
313
314
    /**
315
     * Determina la posición relativa de esta instancia con respecto al objeto especificado.
316
     *
317
     * For types different than ``Version``:
318
     * - ``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
     *   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 24
    public function compareTo($other)
334
    {
335 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

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