Skip to content

Instantly share code, notes, and snippets.

@ArrayIterator
Created October 19, 2024 01:29
Show Gist options
  • Save ArrayIterator/dd25215a2fcbbbd7daff2ccb13faa6e9 to your computer and use it in GitHub Desktop.
Save ArrayIterator/dd25215a2fcbbbd7daff2ccb13faa6e9 to your computer and use it in GitHub Desktop.
The semantic version parser
<?php
declare(strict_types=1);
class SemanticVersion implements JsonSerializable
{
/**
* Regex pattern for semantic versioning
* eg:
* 1.2.3
* v1.2.3
* 1.2.3-beta
* 1.2.3-beta.1
* 1.2.3-beta.1+build.1
* 1.2.3+build.1
* 1.2.3+build.1.2
*
* contains array matched keys:
* {
* "prefix"?: "[a-zA-Z]+",
* "major": "[0-9]+",
* "minor": "[0-9]+",
* "patch": "[0-9]+",
* "release_separator"?: "[+\-]", // optional
* "release"?: "[a-zA-Z0-9]+([a-zA-Z0-9.+\-]*[a-zA-Z0-9])?", // optional the release version
* }
*/
public const SEMANTIC_VERSION_REGEX = '^
(?x)
(?<prefix>[a-zA-Z]+)? # optional prefix: eg : "v" in "v1.2.3"
(?P<major>[0-9]+) # major version
\.(?P<minor>[0-9]+) # minor version
\.(?P<patch>[0-9]+) # patch version
(?:
(?P<release_separator>[+\-])
(?P<release>
[a-zA-Z0-9]+ # should start with a number or a letter
(?:
[a-zA-Z0-9.+\-]* # allow any number of letters, numbers, dots, plus and minus
[a-zA-Z0-9] # should end with a number or a letter
)?
) # release version
)?
$';
/**
* The version string
*
* @var string
*/
public readonly string $version;
/**
* Is the version string valid
*
* @var bool
*/
public readonly bool $valid;
/**
* The major version
*
* @var int|null
*/
public readonly ?int $major;
/**
* The minor version
*
* @var int|null
*/
public readonly ?int $minor;
/**
* The patch version
*
* @var int|null
*/
public readonly ?int $patch;
/**
* The release separator
*
* @var string|null
*/
public readonly ?string $releaseSeparator;
/**
* The release version
*
* @var string|null
*/
public readonly ?string $release;
/**
* SemanticVersion constructor.
*
* @param string $version
*/
public function __construct(string $version)
{
$this->version = trim($version);
preg_match('/' . self::SEMANTIC_VERSION_REGEX . '/', $this->version, $matches);
$this->valid = !empty($matches);
$this->major = is_array($matches) && isset($matches['major']) ? (int) $matches['major'] : null;
$this->minor = is_array($matches) && isset($matches['minor']) ? (int) $matches['minor'] : null;
$this->patch = is_array($matches) && isset($matches['patch']) ? (int) $matches['patch'] : null;
$this->releaseSeparator = is_array($matches)
&& isset($matches['release_separator'])
? $matches['release_separator']
: null;
$this->release = is_array($matches) && isset($matches['release']) ? $matches['release'] : null;
}
public function __toString(): string
{
return $this->version;
}
/**
* @return array{
* version: string,
* valid: bool,
* major: int|null,
* minor: int|null,
* patch: int|null,
* release_separator: string|null,
* release: string|null
* }
*/
public function toArray(): array
{
return [
'version' => $this->version,
'valid' => $this->valid,
'major' => $this->major,
'minor' => $this->minor,
'patch' => $this->patch,
'release_separator' => $this->releaseSeparator,
'release' => $this->release,
];
}
/**
* @return array{
* version: string,
* valid: bool,
* major: int|null,
* minor: int|null,
* patch: int|null,
* release_separator: string|null,
* release: string|null
* }
*/
public function jsonSerialize(): array
{
return $this->toArray();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment