Test Setup Failed
Branch master (59633e)
by Alain
02:10
created

SemanticVersion::getVersionFromComponents()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 17
rs 9.4285
cc 3
eloc 11
nc 4
nop 0
1
<?php
2
/**
3
 * SemanticVersion Class
4
 *
5
 * @package   phpfeature
6
 * @author    Alain Schlesser <[email protected]>
7
 * @license   GPL-2.0+
8
 * @link      http://www.brightnucleus.com/
9
 * @copyright 2016 Alain Schlesser, Bright Nucleus
10
 */
11
12
/**
13
 * Class SemanticVersion
14
 *
15
 * @since  0.1.0
16
 *
17
 * @author Alain Schlesser <[email protected]>
18
 */
19
class SemanticVersion
20
{
21
22
    /**
23
     * RegEx pattern that matches the different version components.
24
     *
25
     * @since 0.1.0
26
     *
27
     * @var string
28
     */
29
    const VERSION_PATTERN = '/^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([0-9A-Za-z-]*))?(?:\+([0-9A-Za-z-]*))?$/';
30
31
    /**
32
     * Version that is used.
33
     *
34
     * @since 0.1.0
35
     *
36
     * @var string
37
     */
38
    protected $version;
39
40
    /**
41
     * Different components of the version that is used.
42
     *
43
     * @since 0.1.0
44
     *
45
     * @var array
46
     */
47
    protected $components;
48
49
    /**
50
     * Instantiate a Version object.
51
     *
52
     * @since 0.1.0
53
     *
54
     * @param string|null $version Optional. The version to use. Defaults to
55
     *                             the current PHP interpreter's version.
56
     * @param bool        $partial Optional. Whether to accept a partial
57
     *                             version number. If true, the missing
58
     *                             components will default to `0` instead of
59
     *                             throwing an exception.
60
     * @throws RuntimeException When the version fails to validate.
61
     */
62
    public function __construct($version = null, $partial = false)
63
    {
64
65
        if ( ! $version) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $version of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
66
            $version = '0.0.0';
67
        }
68
69
        $version = $this->validate($version, $partial);
70
71
        $this->version = $version;
72
    }
73
74
    /**
75
     * Validate the version and assert it is in SemVer format.
76
     *
77
     * @since 0.1.0
78
     *
79
     * @param string $version The version to validate.
80
     * @param bool   $partial Optional. Whether to accept a partial version
81
     *                        number. If true, the missing components will
82
     *                        default to `0` instead of throwing an exception.
83
     * @return string
84
     * @throws RuntimeException When the version fails to validate.
85
     */
86
    protected function validate($version, $partial = false)
87
    {
88
89
        $version = trim($version);
90
        $pattern = self::VERSION_PATTERN;
91
92
        $components = array();
93
        $result     = preg_match($pattern, $version, $components);
94
95
        if ( ! $result) {
96
            throw new RuntimeException(sprintf(
97
                'Failed to validate version "%1$s".',
98
                (string)$version
99
            ));
100
        }
101
102
        if ( ! $partial && ( ! isset($components[2]) || ! isset($components[3]))) {
103
            throw new RuntimeException(sprintf(
104
                'Could not accept partial version "%1$s", requested full versions only.',
105
                (string)$version
106
            ));
107
        }
108
109
        $this->setComponent('major', isset($components[1]) ? (int)$components[1] : 0);
110
        $this->setComponent('minor', isset($components[2]) ? (int)$components[2] : 0);
111
        $this->setComponent('patch', isset($components[3]) ? (int)$components[3] : 0);
112
        $this->setComponent('pre-release', isset($components[4]) ? (string)$components[4] : '');
113
        $this->setComponent('build', isset($components[5]) ? (string)$components[5] : '');
114
115
        $version = $this->getVersionFromComponents();
116
117
        return $version;
118
    }
119
120
    /**
121
     * Get the version that is used.
122
     *
123
     * @since 0.1.0
124
     *
125
     * @return string The version that is used. '0.0.0' if not defined.
126
     */
127
    public function getVersion()
128
    {
129
        return (string)isset($this->version) ? $this->version : '0.0.0';
130
    }
131
132
    /**
133
     * Build and return a versin from the separated components.
134
     *
135
     * @since 0.1.0
136
     *
137
     * @return string
138
     */
139
    protected function getVersionFromComponents()
140
    {
141
142
        $pre_release = $this->getPreRelease() ? '-' . $this->getPreRelease() : '';
143
        $build       = $this->getBuild() ? '+' . $this->getBuild() : '';
144
145
        $version = sprintf(
146
            '%1$s.%2$s.%3$s%4$s%5$s',
147
            $this->getMajor(),
148
            $this->getMinor(),
149
            $this->getPatch(),
150
            $pre_release,
151
            $build
152
        );
153
154
        return $version;
155
    }
156
157
    /**
158
     * Get the major version number.
159
     *
160
     * @since 0.1.0
161
     *
162
     * @return int The major version that is used. 0 if not defined.
163
     */
164
    public function getMajor()
165
    {
166
        return (int)$this->getComponent('major') ?: 0;
167
    }
168
169
    /**
170
     * Get the minor version number.
171
     *
172
     * @since 0.1.0
173
     *
174
     * @return int The minor version that is used. 0 if not defined.
175
     */
176
    public function getMinor()
177
    {
178
        return (int)$this->getComponent('minor') ?: 0;
179
    }
180
181
    /**
182
     * Get the patch version number.
183
     *
184
     * @since 0.1.0
185
     *
186
     * @return int The patch version that is used. 0 if not defined.
187
     */
188
    public function getPatch()
189
    {
190
        return (int)$this->getComponent('patch') ?: 0;
191
    }
192
193
    /**
194
     * Get the pre-release label.
195
     *
196
     * @since 0.1.0
197
     *
198
     * @return int The patch version that is used. '' if not defined.
0 ignored issues
show
Documentation introduced by
Should the return type not be string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
199
     */
200
    public function getPreRelease()
201
    {
202
        return (string)$this->getComponent('pre-release') ?: '';
203
    }
204
205
    /**
206
     * Get the build metadata.
207
     *
208
     * @since 0.1.0
209
     *
210
     * @return int The build metadata for the version that is used. '' if not
0 ignored issues
show
Documentation introduced by
Should the return type not be string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
211
     *             defined.
212
     */
213
    public function getBuild()
214
    {
215
        return (string)$this->getComponent('build') ?: '';
216
    }
217
218
    /**
219
     * Get a component of the version.
220
     *
221
     * @since 0.1.0
222
     *
223
     * @param string $level What level of component to get. Possible values:
224
     *                      'major', 'minor', 'patch'
225
     * @return int The requested version component. null if not defined.
226
     */
227
    protected function getComponent($level)
228
    {
229
        return array_key_exists($level, $this->components)
230
            ? $this->components[$level]
231
            : null;
232
    }
233
234
    /**
235
     * Set a component of the version.
236
     *
237
     * @since 0.1.0
238
     *
239
     * @param string $level   What level of component to set. Possible values:
240
     *                        'major', 'minor', 'patch'
241
     * @param int    $version What version to set that component to.
242
     */
243
    protected function setComponent($level, $version)
244
    {
245
        $this->components[$level] = $version;
246
    }
247
248
    /**
249
     * Get a string representation of the object.
250
     *
251
     * @since 0.2.0
252
     *
253
     * @return string
254
     */
255
    public function __toString()
256
    {
257
        return (string)$this->getVersion();
258
    }
259
}
260