This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * PHPFeature Class. |
||
4 | * |
||
5 | * @package brightnucleus/phpfeature |
||
6 | * @author Alain Schlesser <[email protected]> |
||
7 | * @license MIT |
||
8 | * @link http://www.brightnucleus.com/ |
||
9 | * @copyright 2016 Alain Schlesser, Bright Nucleus |
||
10 | */ |
||
11 | |||
12 | use BrightNucleus_Config as Config; |
||
13 | use BrightNucleus_ConfigInterface as ConfigInterface; |
||
14 | use PHPFeature_SemanticVersion as SemanticVersion; |
||
15 | |||
16 | /** |
||
17 | * Class PHPFeature. |
||
18 | * |
||
19 | * The main PHP Feature implementation of the `FeatureInterface`. |
||
20 | * |
||
21 | * @since 0.1.0 |
||
22 | * |
||
23 | * @author Alain Schlesser <[email protected]> |
||
24 | */ |
||
25 | class PHPFeature implements FeatureInterface |
||
26 | { |
||
27 | |||
28 | /** |
||
29 | * RegEx pattern that matches the comparison string. |
||
30 | * |
||
31 | * @since 0.1.0 |
||
32 | * |
||
33 | * @var string |
||
34 | */ |
||
35 | const COMPARISON_PATTERN = '/^(?:(<=|lt|<|le|>=|gt|>|ge|=|==|eq|!=|<>|ne))([0-9].*)$/'; |
||
36 | |||
37 | /** |
||
38 | * Reference to the Configuration object. |
||
39 | * |
||
40 | * @since 0.1.0 |
||
41 | * |
||
42 | * @var ConfigInterface |
||
43 | */ |
||
44 | protected $config; |
||
45 | |||
46 | /** |
||
47 | * Reference to the Version object. |
||
48 | * |
||
49 | * @since 0.1.0 |
||
50 | * |
||
51 | * @var SemanticVersion |
||
52 | */ |
||
53 | protected $version; |
||
54 | |||
55 | /** |
||
56 | * Reference to the PHP releases. |
||
57 | * |
||
58 | * @since 0.2.4 |
||
59 | * |
||
60 | * @var PHPReleases |
||
61 | */ |
||
62 | protected $releases; |
||
63 | |||
64 | /** |
||
65 | * Instantiate a PHPFeature object. |
||
66 | * |
||
67 | * @since 0.1.0 |
||
68 | * |
||
69 | * @param SemanticVersion|string|int|null $phpVersion Version of PHP to check the features for. |
||
70 | * @param ConfigInterface|null $config Configuration that contains the known features. |
||
71 | * |
||
72 | * @throws RuntimeException If the PHP version could not be validated. |
||
73 | */ |
||
74 | 66 | public function __construct($phpVersion = null, ConfigInterface $config = null) |
|
75 | { |
||
76 | |||
77 | // TODO: Better way to bootstrap this while still allowing DI? |
||
78 | 66 | if ( ! $config) { |
|
79 | 46 | $config = new Config(include(dirname(__FILE__) . '/../config/known_features.php')); |
|
80 | } |
||
81 | |||
82 | 66 | $this->config = $config; |
|
83 | |||
84 | 66 | if (null === $phpVersion) { |
|
85 | $phpVersion = phpversion(); |
||
86 | } |
||
87 | |||
88 | 66 | if (is_int($phpVersion)) { |
|
89 | $phpVersion = (string)$phpVersion; |
||
90 | } |
||
91 | |||
92 | 66 | if (is_string($phpVersion)) { |
|
93 | 66 | $phpVersion = new SemanticVersion($phpVersion, true); |
|
94 | } |
||
95 | |||
96 | 66 | $this->version = $phpVersion; |
|
97 | 66 | } |
|
98 | |||
99 | /** |
||
100 | * Check whether a feature or a collection of features is supported. |
||
101 | * |
||
102 | * Accepts either a string or an array of strings. Returns true if all the passed-in features are supported, or |
||
103 | * false if at least one of them is not. |
||
104 | * |
||
105 | * @since 0.1.0 |
||
106 | * |
||
107 | * @param string|array $features What features to check the support of. |
||
108 | * |
||
109 | * @return bool Whether the set of features as a whole is supported. |
||
110 | * @throws InvalidArgumentException If the wrong type of argument is passed in. |
||
111 | * @throws RuntimeException If a requirement could not be parsed. |
||
112 | */ |
||
113 | 23 | public function isSupported($features) |
|
114 | { |
||
115 | |||
116 | 23 | if (is_string($features)) { |
|
117 | 21 | $features = array($features); |
|
118 | } |
||
119 | |||
120 | 23 | if ( ! is_array($features)) { |
|
121 | throw new InvalidArgumentException(sprintf( |
||
122 | 'Wrong type of argument passed in to is_supported(): "%1$s".', |
||
123 | gettype($features) |
||
124 | )); |
||
125 | } |
||
126 | |||
127 | 23 | $isSupported = true; |
|
128 | |||
129 | 23 | View Code Duplication | while ($isSupported && count($features) > 0) { |
0 ignored issues
–
show
|
|||
130 | 23 | $feature = array_pop($features); |
|
131 | 23 | $isSupported &= (bool)$this->checkSupport($feature); |
|
132 | } |
||
133 | |||
134 | 23 | return (bool)$isSupported; |
|
135 | } |
||
136 | |||
137 | /** |
||
138 | * Get the minimum required version that supports all of the requested features. |
||
139 | * |
||
140 | * Accepts either a string or an array of strings. Returns a SemanticVersion object for the version number that is |
||
141 | * known to support all the passed-in features, or false if at least one of them is not supported by any known |
||
142 | * version. |
||
143 | * |
||
144 | * @since 0.2.0 |
||
145 | * |
||
146 | * @param string|array $features What features to check the support of. |
||
147 | * |
||
148 | * @return SemanticVersion|false SemanticVersion object for the version number that is known to support all the |
||
149 | * passed-in features, false if none. |
||
150 | * @throws InvalidArgumentException If the wrong type of argument is passed in. |
||
151 | * @throws RuntimeException If a requirement could not be parsed. |
||
152 | */ |
||
153 | 43 | public function getMinimumRequired($features) |
|
154 | { |
||
155 | |||
156 | 43 | if (is_string($features)) { |
|
157 | 41 | $features = array($features); |
|
158 | } |
||
159 | |||
160 | 43 | if ( ! is_array($features)) { |
|
161 | throw new InvalidArgumentException(sprintf( |
||
162 | 'Wrong type of argument passed in to get_minimum_required(): "%1$s".', |
||
163 | gettype($features) |
||
164 | )); |
||
165 | } |
||
166 | |||
167 | 43 | $minimumRequired = '0.0.0'; |
|
168 | 43 | $isSupported = true; |
|
169 | |||
170 | 43 | View Code Duplication | while (count($features) > 0) { |
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
171 | 43 | $feature = array_pop($features); |
|
172 | 43 | $isSupported &= (bool)$this->checkSupport($feature, $minimumRequired); |
|
173 | } |
||
174 | |||
175 | 43 | return $minimumRequired !== '0.0.0' ? new SemanticVersion($minimumRequired, true) : false; |
|
176 | } |
||
177 | |||
178 | /** |
||
179 | * Check whether a single feature is supported. |
||
180 | * |
||
181 | * @since 0.1.0 |
||
182 | * |
||
183 | * @param string $feature The feature to check. |
||
184 | * @param string|null $minimumRequired Optional. Minimum required version that supports all features. |
||
185 | * |
||
186 | * @return bool Whether the requested feature is supported. |
||
187 | * @throws RuntimeException If the requirement could not be parsed. |
||
188 | */ |
||
189 | 66 | protected function checkSupport($feature, &$minimumRequired = null) |
|
190 | { |
||
191 | |||
192 | 66 | if ( ! $this->config->hasKey($feature)) { |
|
193 | 2 | return false; |
|
194 | } |
||
195 | |||
196 | 64 | $requirements = (array)$this->config->getKey($feature); |
|
197 | |||
198 | 64 | $isSupported = true; |
|
199 | |||
200 | 64 | while (($isSupported || null !== $minimumRequired) && count($requirements) > 0) { |
|
201 | 64 | $requirement = array_pop($requirements); |
|
202 | 64 | $isSupported &= (bool)$this->checkRequirement($requirement, $minimumRequired); |
|
203 | } |
||
204 | |||
205 | 64 | return (bool)$isSupported; |
|
206 | } |
||
207 | |||
208 | /** |
||
209 | * Check whether a single requirement is met. |
||
210 | * |
||
211 | * @since 0.1.0 |
||
212 | * |
||
213 | * @param string $requirement A requirement that is composed of an operator and a version milestone. |
||
214 | * @param string|null $minimumRequired Optional. Minimum required version that supports all features. |
||
215 | * |
||
216 | * @return bool Whether the requirement is met. |
||
217 | * @throws RuntimeException If the requirement could not be parsed. |
||
218 | */ |
||
219 | 64 | protected function checkRequirement($requirement, &$minimumRequired = null) |
|
220 | { |
||
221 | |||
222 | 64 | $requirement = trim($requirement); |
|
223 | 64 | $pattern = self::COMPARISON_PATTERN; |
|
224 | |||
225 | 64 | $arguments = array(); |
|
226 | 64 | $result = preg_match($pattern, $requirement, $arguments); |
|
227 | |||
228 | 64 | if ( ! $result || ! isset($arguments[1]) || ! isset($arguments[2])) { |
|
229 | throw new RuntimeException(sprintf( |
||
230 | 'Could not parse the requirement "%1$s".', |
||
231 | (string)$requirement |
||
232 | )); |
||
233 | } |
||
234 | |||
235 | 64 | $operator = isset($arguments[1]) ? (string)$arguments[1] : '>='; |
|
236 | 64 | $milestone = isset($arguments[2]) ? (string)$arguments[2] : '0.0.0'; |
|
237 | |||
238 | 64 | $isSupported = (bool)version_compare($this->version->getVersion(), $milestone, $operator); |
|
239 | |||
240 | 64 | if (null !== $minimumRequired) { |
|
241 | 42 | $requiredVersion = $this->getRequiredVersion($milestone, $operator); |
|
242 | 42 | if (version_compare($requiredVersion, $minimumRequired, '>')) { |
|
243 | 42 | $minimumRequired = $requiredVersion; |
|
244 | } |
||
245 | } |
||
246 | |||
247 | 64 | return $isSupported; |
|
248 | } |
||
249 | |||
250 | /** |
||
251 | * Get the required version for a single requirement. |
||
252 | * |
||
253 | * @todo The entire algorithm is only an approximation. A 5.2 SemVer library is needed. |
||
254 | * |
||
255 | * @since 0.2.0 |
||
256 | * |
||
257 | * @param string $milestone A version milestone that is used to define the requirement. |
||
258 | * @param string $operator An operator that gets applied to the milestone. |
||
259 | * Possible values: '<=', 'lt', '<', 'le', '>=', 'gt', '>', 'ge', '=', '==', 'eq', '!=', |
||
260 | * '<>', 'ne' |
||
261 | * |
||
262 | * @return string Version string that meets a single requirement. |
||
0 ignored issues
–
show
|
|||
263 | * @throws RuntimeException If the requirement could not be satisfied. |
||
264 | * @throws RuntimeException If the NotEqual is used. |
||
265 | */ |
||
266 | 42 | protected function getRequiredVersion($milestone, $operator) |
|
267 | { |
||
268 | 42 | if (null === $this->releases) { |
|
269 | 42 | $this->releases = new PHPReleases(); |
|
270 | } |
||
271 | |||
272 | switch ($operator) { |
||
273 | 42 | case '>': |
|
274 | case 'gt': |
||
275 | 4 | return $this->getGreaterThanVersion($milestone); |
|
276 | case '<': |
||
277 | case 'lt': |
||
278 | 6 | return $this->getLesserThanVersion($milestone); |
|
279 | case '>=': |
||
280 | case 'ge': |
||
281 | 26 | return $this->getGreaterEqualVersion($milestone); |
|
282 | case '<=': |
||
283 | case 'le': |
||
284 | 6 | return $this->getLesserEqualVersion($milestone); |
|
285 | case '!=': |
||
286 | case '<>': |
||
287 | case 'ne': |
||
288 | throw new RuntimeException('NotEqual operator is not implemented.'); |
||
289 | } |
||
290 | |||
291 | return $milestone; |
||
292 | } |
||
293 | |||
294 | /** |
||
295 | * Get a version greater than the milestone. |
||
296 | * |
||
297 | * @since 0.2.4 |
||
298 | * |
||
299 | * @param string $milestone A version milestone that is used to define the requirement. |
||
300 | * |
||
301 | * @return string Version number that meets the requirement. |
||
0 ignored issues
–
show
|
|||
302 | * @throws RuntimeException If the requirement could not be satisfied. |
||
303 | */ |
||
304 | 4 | View Code Duplication | protected function getGreaterThanVersion($milestone) |
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
305 | { |
||
306 | 4 | $data = $this->releases->getAll(); |
|
307 | 4 | foreach ($data as $version => $date) { |
|
308 | 4 | if (version_compare($version, $milestone, '>')) { |
|
309 | 4 | return $version; |
|
310 | } |
||
311 | } |
||
312 | |||
313 | throw new RuntimeException('Could not satisfy version requirements.'); |
||
314 | } |
||
315 | |||
316 | /** |
||
317 | * Get a version lesser than the milestone. |
||
318 | * |
||
319 | * @since 0.2.4 |
||
320 | * |
||
321 | * @param string $milestone A version milestone that is used to define the requirement. |
||
322 | * |
||
323 | * @return string Version number that meets the requirement. |
||
0 ignored issues
–
show
|
|||
324 | * @throws RuntimeException If the requirement could not be satisfied. |
||
325 | */ |
||
326 | 6 | View Code Duplication | protected function getLesserThanVersion($milestone) |
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
327 | { |
||
328 | 6 | if (version_compare($this->version->getVersion(), $milestone, '<')) { |
|
329 | 4 | return $this->version->getVersion(); |
|
330 | } |
||
331 | 2 | $data = array_reverse($this->releases->getAll()); |
|
332 | 2 | foreach ($data as $version => $date) { |
|
333 | 2 | if (version_compare($version, $milestone, '<')) { |
|
334 | 2 | return $version; |
|
335 | } |
||
336 | } |
||
337 | |||
338 | throw new RuntimeException('Could not satisfy version requirements.'); |
||
339 | } |
||
340 | |||
341 | /** |
||
342 | * Get a version greater or equal than the milestone. |
||
343 | * |
||
344 | * @since 0.2.4 |
||
345 | * |
||
346 | * @param string $milestone A version milestone that is used to define the requirement. |
||
347 | * |
||
348 | * @return string Version number that meets the requirement. |
||
0 ignored issues
–
show
|
|||
349 | */ |
||
350 | 26 | View Code Duplication | protected function getGreaterEqualVersion($milestone) |
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
351 | { |
||
352 | 26 | if ($this->releases->exists($milestone)) { |
|
353 | 25 | return $milestone; |
|
354 | } |
||
355 | |||
356 | 1 | $data = $this->releases->getAll(); |
|
357 | 1 | foreach ($data as $version => $date) { |
|
358 | 1 | if (version_compare($version, $milestone, '>=')) { |
|
359 | 1 | return $version; |
|
360 | } |
||
361 | } |
||
362 | |||
363 | throw new RuntimeException('Could not satisfy version requirements.'); |
||
364 | } |
||
365 | |||
366 | /** |
||
367 | * Get a version lesser or equal than the milestone. |
||
368 | * |
||
369 | * @since 0.2.4 |
||
370 | * |
||
371 | * @param string $milestone A version milestone that is used to define the requirement. |
||
372 | * |
||
373 | * @return string Version number that meets the requirement. |
||
0 ignored issues
–
show
|
|||
374 | */ |
||
375 | 6 | View Code Duplication | protected function getLesserEqualVersion($milestone) |
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
376 | { |
||
377 | 6 | if (version_compare($this->version->getVersion(), $milestone, '<=')) { |
|
378 | 4 | return $this->version->getVersion(); |
|
379 | } |
||
380 | |||
381 | 2 | $data = array_reverse($this->releases->getAll()); |
|
382 | 2 | foreach ($data as $version => $date) { |
|
383 | 2 | if (version_compare($version, $milestone, '<=')) { |
|
384 | 2 | return $version; |
|
385 | } |
||
386 | } |
||
387 | |||
388 | throw new RuntimeException('Could not satisfy version requirements.'); |
||
389 | } |
||
390 | } |
||
391 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.