PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.2.0
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.2.0
5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / vendor / composer / semver / src / VersionParser.php
matomo / app / vendor / composer / semver / src Last commit date
Constraint 3 years ago Comparator.php 6 years ago Semver.php 3 years ago VersionParser.php 3 years ago
VersionParser.php
579 lines
1 <?php
2
3 /*
4 * This file is part of composer/semver.
5 *
6 * (c) Composer <https://github.com/composer>
7 *
8 * For the full copyright and license information, please view
9 * the LICENSE file that was distributed with this source code.
10 */
11
12 namespace Composer\Semver;
13
14 use Composer\Semver\Constraint\ConstraintInterface;
15 use Composer\Semver\Constraint\EmptyConstraint;
16 use Composer\Semver\Constraint\MultiConstraint;
17 use Composer\Semver\Constraint\Constraint;
18
19 /**
20 * Version parser.
21 *
22 * @author Jordi Boggiano <j.boggiano@seld.be>
23 */
24 class VersionParser
25 {
26 /**
27 * Regex to match pre-release data (sort of).
28 *
29 * Due to backwards compatibility:
30 * - Instead of enforcing hyphen, an underscore, dot or nothing at all are also accepted.
31 * - Only stabilities as recognized by Composer are allowed to precede a numerical identifier.
32 * - Numerical-only pre-release identifiers are not supported, see tests.
33 *
34 * |--------------|
35 * [major].[minor].[patch] -[pre-release] +[build-metadata]
36 *
37 * @var string
38 */
39 private static $modifierRegex = '[._-]?(?:(stable|beta|b|RC|alpha|a|patch|pl|p)((?:[.-]?\d+)*+)?)?([.-]?dev)?';
40
41 /** @var string */
42 private static $stabilitiesRegex = 'stable|RC|beta|alpha|dev';
43
44 /**
45 * Returns the stability of a version.
46 *
47 * @param string $version
48 *
49 * @return string
50 */
51 public static function parseStability($version)
52 {
53 $version = preg_replace('{#.+$}i', '', $version);
54
55 if (strpos($version, 'dev-') === 0 || '-dev' === substr($version, -4)) {
56 return 'dev';
57 }
58
59 preg_match('{' . self::$modifierRegex . '(?:\+.*)?$}i', strtolower($version), $match);
60
61 if (!empty($match[3])) {
62 return 'dev';
63 }
64
65 if (!empty($match[1])) {
66 if ('beta' === $match[1] || 'b' === $match[1]) {
67 return 'beta';
68 }
69 if ('alpha' === $match[1] || 'a' === $match[1]) {
70 return 'alpha';
71 }
72 if ('rc' === $match[1]) {
73 return 'RC';
74 }
75 }
76
77 return 'stable';
78 }
79
80 /**
81 * @param string $stability
82 *
83 * @return string
84 */
85 public static function normalizeStability($stability)
86 {
87 $stability = strtolower($stability);
88
89 return $stability === 'rc' ? 'RC' : $stability;
90 }
91
92 /**
93 * Normalizes a version string to be able to perform comparisons on it.
94 *
95 * @param string $version
96 * @param string $fullVersion optional complete version string to give more context
97 *
98 * @throws \UnexpectedValueException
99 *
100 * @return string
101 */
102 public function normalize($version, $fullVersion = null)
103 {
104 $version = trim($version);
105 $origVersion = $version;
106 if (null === $fullVersion) {
107 $fullVersion = $version;
108 }
109
110 // strip off aliasing
111 if (preg_match('{^([^,\s]++) ++as ++([^,\s]++)$}', $version, $match)) {
112 $version = $match[1];
113 }
114
115 // strip off stability flag
116 if (preg_match('{@(?:' . self::$stabilitiesRegex . ')$}i', $version, $match)) {
117 $version = substr($version, 0, strlen($version) - strlen($match[0]));
118 }
119
120 // match master-like branches
121 if (preg_match('{^(?:dev-)?(?:master|trunk|default)$}i', $version)) {
122 return '9999999-dev';
123 }
124
125 // if requirement is branch-like, use full name
126 if (stripos($version, 'dev-') === 0) {
127 return 'dev-' . substr($version, 4);
128 }
129
130 // strip off build metadata
131 if (preg_match('{^([^,\s+]++)\+[^\s]++$}', $version, $match)) {
132 $version = $match[1];
133 }
134
135 // match classical versioning
136 if (preg_match('{^v?(\d{1,5})(\.\d++)?(\.\d++)?(\.\d++)?' . self::$modifierRegex . '$}i', $version, $matches)) {
137 $version = $matches[1]
138 . (!empty($matches[2]) ? $matches[2] : '.0')
139 . (!empty($matches[3]) ? $matches[3] : '.0')
140 . (!empty($matches[4]) ? $matches[4] : '.0');
141 $index = 5;
142 // match date(time) based versioning
143 } elseif (preg_match('{^v?(\d{4}(?:[.:-]?\d{2}){1,6}(?:[.:-]?\d{1,3})?)' . self::$modifierRegex . '$}i', $version, $matches)) {
144 $version = preg_replace('{\D}', '.', $matches[1]);
145 $index = 2;
146 }
147
148 // add version modifiers if a version was matched
149 if (isset($index)) {
150 if (!empty($matches[$index])) {
151 if ('stable' === $matches[$index]) {
152 return $version;
153 }
154 $version .= '-' . $this->expandStability($matches[$index]) . (isset($matches[$index + 1]) && '' !== $matches[$index + 1] ? ltrim($matches[$index + 1], '.-') : '');
155 }
156
157 if (!empty($matches[$index + 2])) {
158 $version .= '-dev';
159 }
160
161 return $version;
162 }
163
164 // match dev branches
165 if (preg_match('{(.*?)[.-]?dev$}i', $version, $match)) {
166 try {
167 $normalized = $this->normalizeBranch($match[1]);
168 // a branch ending with -dev is only valid if it is numeric
169 // if it gets prefixed with dev- it means the branch name should
170 // have had a dev- prefix already when passed to normalize
171 if (strpos($normalized, 'dev-') === false) {
172 return $normalized;
173 }
174 } catch (\Exception $e) {
175 }
176 }
177
178 $extraMessage = '';
179 if (preg_match('{ +as +' . preg_quote($version) . '(?:@(?:'.self::$stabilitiesRegex.'))?$}', $fullVersion)) {
180 $extraMessage = ' in "' . $fullVersion . '", the alias must be an exact version';
181 } elseif (preg_match('{^' . preg_quote($version) . '(?:@(?:'.self::$stabilitiesRegex.'))? +as +}', $fullVersion)) {
182 $extraMessage = ' in "' . $fullVersion . '", the alias source must be an exact version, if it is a branch name you should prefix it with dev-';
183 }
184
185 throw new \UnexpectedValueException('Invalid version string "' . $origVersion . '"' . $extraMessage);
186 }
187
188 /**
189 * Extract numeric prefix from alias, if it is in numeric format, suitable for version comparison.
190 *
191 * @param string $branch Branch name (e.g. 2.1.x-dev)
192 *
193 * @return string|false Numeric prefix if present (e.g. 2.1.) or false
194 */
195 public function parseNumericAliasPrefix($branch)
196 {
197 if (preg_match('{^(?P<version>(\d++\\.)*\d++)(?:\.x)?-dev$}i', $branch, $matches)) {
198 return $matches['version'] . '.';
199 }
200
201 return false;
202 }
203
204 /**
205 * Normalizes a branch name to be able to perform comparisons on it.
206 *
207 * @param string $name
208 *
209 * @return string
210 */
211 public function normalizeBranch($name)
212 {
213 $name = trim($name);
214
215 if (in_array($name, array('master', 'trunk', 'default'))) {
216 return $this->normalize($name);
217 }
218
219 if (preg_match('{^v?(\d++)(\.(?:\d++|[xX*]))?(\.(?:\d++|[xX*]))?(\.(?:\d++|[xX*]))?$}i', $name, $matches)) {
220 $version = '';
221 for ($i = 1; $i < 5; ++$i) {
222 $version .= isset($matches[$i]) ? str_replace(array('*', 'X'), 'x', $matches[$i]) : '.x';
223 }
224
225 return str_replace('x', '9999999', $version) . '-dev';
226 }
227
228 return 'dev-' . $name;
229 }
230
231 /**
232 * Parses a constraint string into MultiConstraint and/or Constraint objects.
233 *
234 * @param string $constraints
235 *
236 * @return ConstraintInterface
237 */
238 public function parseConstraints($constraints)
239 {
240 $prettyConstraint = $constraints;
241
242 $orConstraints = preg_split('{\s*\|\|?\s*}', trim($constraints));
243 $orGroups = array();
244
245 foreach ($orConstraints as $constraints) {
246 $andConstraints = preg_split('{(?<!^|as|[=>< ,]) *(?<!-)[, ](?!-) *(?!,|as|$)}', $constraints);
247 if (count($andConstraints) > 1) {
248 $constraintObjects = array();
249 foreach ($andConstraints as $constraint) {
250 foreach ($this->parseConstraint($constraint) as $parsedConstraint) {
251 $constraintObjects[] = $parsedConstraint;
252 }
253 }
254 } else {
255 $constraintObjects = $this->parseConstraint($andConstraints[0]);
256 }
257
258 if (1 === count($constraintObjects)) {
259 $constraint = $constraintObjects[0];
260 } else {
261 $constraint = new MultiConstraint($constraintObjects);
262 }
263
264 $orGroups[] = $constraint;
265 }
266
267 if (1 === count($orGroups)) {
268 $constraint = $orGroups[0];
269 } elseif (2 === count($orGroups)
270 // parse the two OR groups and if they are contiguous we collapse
271 // them into one constraint
272 && $orGroups[0] instanceof MultiConstraint
273 && $orGroups[1] instanceof MultiConstraint
274 && 2 === count($orGroups[0]->getConstraints())
275 && 2 === count($orGroups[1]->getConstraints())
276 && ($a = (string) $orGroups[0])
277 && strpos($a, '[>=') === 0 && (false !== ($posA = strpos($a, '<', 4)))
278 && ($b = (string) $orGroups[1])
279 && strpos($b, '[>=') === 0 && (false !== ($posB = strpos($b, '<', 4)))
280 && substr($a, $posA + 2, -1) === substr($b, 4, $posB - 5)
281 ) {
282 $constraint = new MultiConstraint(array(
283 new Constraint('>=', substr($a, 4, $posA - 5)),
284 new Constraint('<', substr($b, $posB + 2, -1)),
285 ));
286 } else {
287 $constraint = new MultiConstraint($orGroups, false);
288 }
289
290 $constraint->setPrettyString($prettyConstraint);
291
292 return $constraint;
293 }
294
295 /**
296 * @param string $constraint
297 *
298 * @throws \UnexpectedValueException
299 *
300 * @return array
301 */
302 private function parseConstraint($constraint)
303 {
304 // strip off aliasing
305 if (preg_match('{^([^,\s]++) ++as ++([^,\s]++)$}', $constraint, $match)) {
306 $constraint = $match[1];
307 }
308
309 // strip @stability flags, and keep it for later use
310 if (preg_match('{^([^,\s]*?)@(' . self::$stabilitiesRegex . ')$}i', $constraint, $match)) {
311 $constraint = '' !== $match[1] ? $match[1] : '*';
312 if ($match[2] !== 'stable') {
313 $stabilityModifier = $match[2];
314 }
315 }
316
317 // get rid of #refs as those are used by composer only
318 if (preg_match('{^(dev-[^,\s@]+?|[^,\s@]+?\.x-dev)#.+$}i', $constraint, $match)) {
319 $constraint = $match[1];
320 }
321
322 if (preg_match('{^v?[xX*](\.[xX*])*$}i', $constraint)) {
323 return array(new EmptyConstraint());
324 }
325
326 $versionRegex = 'v?(\d++)(?:\.(\d++))?(?:\.(\d++))?(?:\.(\d++))?(?:' . self::$modifierRegex . '|\.([xX*][.-]?dev))(?:\+[^\s]+)?';
327
328 // Tilde Range
329 //
330 // Like wildcard constraints, unsuffixed tilde constraints say that they must be greater than the previous
331 // version, to ensure that unstable instances of the current version are allowed. However, if a stability
332 // suffix is added to the constraint, then a >= match on the current version is used instead.
333 if (preg_match('{^~>?' . $versionRegex . '$}i', $constraint, $matches)) {
334 if (strpos($constraint, '~>') === 0) {
335 throw new \UnexpectedValueException(
336 'Could not parse version constraint ' . $constraint . ': ' .
337 'Invalid operator "~>", you probably meant to use the "~" operator'
338 );
339 }
340
341 // Work out which position in the version we are operating at
342 if (isset($matches[4]) && '' !== $matches[4] && null !== $matches[4]) {
343 $position = 4;
344 } elseif (isset($matches[3]) && '' !== $matches[3] && null !== $matches[3]) {
345 $position = 3;
346 } elseif (isset($matches[2]) && '' !== $matches[2] && null !== $matches[2]) {
347 $position = 2;
348 } else {
349 $position = 1;
350 }
351
352 // when matching 2.x-dev or 3.0.x-dev we have to shift the second or third number, despite no second/third number matching above
353 if (!empty($matches[8])) {
354 $position++;
355 }
356
357 // Calculate the stability suffix
358 $stabilitySuffix = '';
359 if (empty($matches[5]) && empty($matches[7]) && empty($matches[8])) {
360 $stabilitySuffix .= '-dev';
361 }
362
363 $lowVersion = $this->normalize(substr($constraint . $stabilitySuffix, 1));
364 $lowerBound = new Constraint('>=', $lowVersion);
365
366 // For upper bound, we increment the position of one more significance,
367 // but highPosition = 0 would be illegal
368 $highPosition = max(1, $position - 1);
369 $highVersion = $this->manipulateVersionString($matches, $highPosition, 1) . '-dev';
370 $upperBound = new Constraint('<', $highVersion);
371
372 return array(
373 $lowerBound,
374 $upperBound,
375 );
376 }
377
378 // Caret Range
379 //
380 // Allows changes that do not modify the left-most non-zero digit in the [major, minor, patch] tuple.
381 // In other words, this allows patch and minor updates for versions 1.0.0 and above, patch updates for
382 // versions 0.X >=0.1.0, and no updates for versions 0.0.X
383 if (preg_match('{^\^' . $versionRegex . '($)}i', $constraint, $matches)) {
384 // Work out which position in the version we are operating at
385 if ('0' !== $matches[1] || '' === $matches[2] || null === $matches[2]) {
386 $position = 1;
387 } elseif ('0' !== $matches[2] || '' === $matches[3] || null === $matches[3]) {
388 $position = 2;
389 } else {
390 $position = 3;
391 }
392
393 // Calculate the stability suffix
394 $stabilitySuffix = '';
395 if (empty($matches[5]) && empty($matches[7]) && empty($matches[8])) {
396 $stabilitySuffix .= '-dev';
397 }
398
399 $lowVersion = $this->normalize(substr($constraint . $stabilitySuffix, 1));
400 $lowerBound = new Constraint('>=', $lowVersion);
401
402 // For upper bound, we increment the position of one more significance,
403 // but highPosition = 0 would be illegal
404 $highVersion = $this->manipulateVersionString($matches, $position, 1) . '-dev';
405 $upperBound = new Constraint('<', $highVersion);
406
407 return array(
408 $lowerBound,
409 $upperBound,
410 );
411 }
412
413 // X Range
414 //
415 // Any of X, x, or * may be used to "stand in" for one of the numeric values in the [major, minor, patch] tuple.
416 // A partial version range is treated as an X-Range, so the special character is in fact optional.
417 if (preg_match('{^v?(\d++)(?:\.(\d++))?(?:\.(\d++))?(?:\.[xX*])++$}', $constraint, $matches)) {
418 if (isset($matches[3]) && '' !== $matches[3] && null !== $matches[3]) {
419 $position = 3;
420 } elseif (isset($matches[2]) && '' !== $matches[2] && null !== $matches[2]) {
421 $position = 2;
422 } else {
423 $position = 1;
424 }
425
426 $lowVersion = $this->manipulateVersionString($matches, $position) . '-dev';
427 $highVersion = $this->manipulateVersionString($matches, $position, 1) . '-dev';
428
429 if ($lowVersion === '0.0.0.0-dev') {
430 return array(new Constraint('<', $highVersion));
431 }
432
433 return array(
434 new Constraint('>=', $lowVersion),
435 new Constraint('<', $highVersion),
436 );
437 }
438
439 // Hyphen Range
440 //
441 // Specifies an inclusive set. If a partial version is provided as the first version in the inclusive range,
442 // then the missing pieces are replaced with zeroes. If a partial version is provided as the second version in
443 // the inclusive range, then all versions that start with the supplied parts of the tuple are accepted, but
444 // nothing that would be greater than the provided tuple parts.
445 if (preg_match('{^(?P<from>' . $versionRegex . ') +- +(?P<to>' . $versionRegex . ')($)}i', $constraint, $matches)) {
446 // Calculate the stability suffix
447 $lowStabilitySuffix = '';
448 if (empty($matches[6]) && empty($matches[8]) && empty($matches[9])) {
449 $lowStabilitySuffix = '-dev';
450 }
451
452 $lowVersion = $this->normalize($matches['from']);
453 $lowerBound = new Constraint('>=', $lowVersion . $lowStabilitySuffix);
454
455 $empty = function ($x) {
456 return ($x === 0 || $x === '0') ? false : empty($x);
457 };
458
459 if ((!$empty($matches[12]) && !$empty($matches[13])) || !empty($matches[15]) || !empty($matches[17]) || !empty($matches[18])) {
460 $highVersion = $this->normalize($matches['to']);
461 $upperBound = new Constraint('<=', $highVersion);
462 } else {
463 $highMatch = array('', $matches[11], $matches[12], $matches[13], $matches[14]);
464
465 // validate to version
466 $this->normalize($matches['to']);
467
468 $highVersion = $this->manipulateVersionString($highMatch, $empty($matches[12]) ? 1 : 2, 1) . '-dev';
469 $upperBound = new Constraint('<', $highVersion);
470 }
471
472 return array(
473 $lowerBound,
474 $upperBound,
475 );
476 }
477
478 // Basic Comparators
479 if (preg_match('{^(<>|!=|>=?|<=?|==?)?\s*(.*)}', $constraint, $matches)) {
480 try {
481 try {
482 $version = $this->normalize($matches[2]);
483 } catch (\UnexpectedValueException $e) {
484 // recover from an invalid constraint like foobar-dev which should be dev-foobar
485 // except if the constraint uses a known operator, in which case it must be a parse error
486 if (substr($matches[2], -4) === '-dev' && preg_match('{^[0-9a-zA-Z-./]+$}', $matches[2])) {
487 $version = $this->normalize('dev-'.substr($matches[2], 0, -4));
488 } else {
489 throw $e;
490 }
491 }
492
493 $op = $matches[1] ?: '=';
494
495 if ($op !== '==' && $op !== '=' && !empty($stabilityModifier) && self::parseStability($version) === 'stable') {
496 $version .= '-' . $stabilityModifier;
497 } elseif ('<' === $op || '>=' === $op) {
498 if (!preg_match('/-' . self::$modifierRegex . '$/', strtolower($matches[2]))) {
499 if (strpos($matches[2], 'dev-') !== 0) {
500 $version .= '-dev';
501 }
502 }
503 }
504
505 return array(new Constraint($matches[1] ?: '=', $version));
506 } catch (\Exception $e) {
507 }
508 }
509
510 $message = 'Could not parse version constraint ' . $constraint;
511 if (isset($e)) {
512 $message .= ': ' . $e->getMessage();
513 }
514
515 throw new \UnexpectedValueException($message);
516 }
517
518 /**
519 * Increment, decrement, or simply pad a version number.
520 *
521 * Support function for {@link parseConstraint()}
522 *
523 * @param array $matches Array with version parts in array indexes 1,2,3,4
524 * @param int $position 1,2,3,4 - which segment of the version to increment/decrement
525 * @param int $increment
526 * @param string $pad The string to pad version parts after $position
527 *
528 * @return string|null The new version
529 */
530 private function manipulateVersionString($matches, $position, $increment = 0, $pad = '0')
531 {
532 for ($i = 4; $i > 0; --$i) {
533 if ($i > $position) {
534 $matches[$i] = $pad;
535 } elseif ($i === $position && $increment) {
536 $matches[$i] += $increment;
537 // If $matches[$i] was 0, carry the decrement
538 if ($matches[$i] < 0) {
539 $matches[$i] = $pad;
540 --$position;
541
542 // Return null on a carry overflow
543 if ($i === 1) {
544 return null;
545 }
546 }
547 }
548 }
549
550 return $matches[1] . '.' . $matches[2] . '.' . $matches[3] . '.' . $matches[4];
551 }
552
553 /**
554 * Expand shorthand stability string to long version.
555 *
556 * @param string $stability
557 *
558 * @return string
559 */
560 private function expandStability($stability)
561 {
562 $stability = strtolower($stability);
563
564 switch ($stability) {
565 case 'a':
566 return 'alpha';
567 case 'b':
568 return 'beta';
569 case 'p':
570 case 'pl':
571 return 'patch';
572 case 'rc':
573 return 'RC';
574 default:
575 return $stability;
576 }
577 }
578 }
579