value => 'PreAlpha', VersionInfoStage::Alpha->value => 'Alpha', VersionInfoStage::Beta->value => 'Beta', VersionInfoStage::ReleaseCandidate->value => 'rc', VersionInfoStage::Stable->value => 'Stable' ]; /** * @var string $DefaultTemplate Шаблон вывода по умолчанию. */ public const string DefaultTemplate = '#{Major}.#{Minor}.#{Release}.#{Build} #{Stage} #{StageNumber}'; /** * @var int $Major Мажорная версия. */ private(set) int $Major = 0; /** * @var int $Minor Минорная версия. */ private(set) int $Minor = 0; /** * @var int $Release Номер релиза. */ private(set) int $Release = 0; /** * @var int $Build Номер сборки. */ private(set) int $Build = 0; /** * @var VersionInfoStage $Stage Стадия сборки. */ private(set) VersionInfoStage $Stage = VersionInfoStage::Undefined; /** * @var int $StageNumber Номер стадии сборки. */ private(set) int $StageNumber = 0; /** * Конструктор. * * @param int $Major Мажорная версия. * @param int $Minor Минорная версия. * @param int $Release Номер релиза. * @param int $Build Номер сборки. * @param VersionInfoStage $Stage Стадия сборки. * @param int $StageNumber Номер стадии сборки. */ public function __construct (int $Major, int $Minor, int $Release, int $Build, VersionInfoStage $Stage = VersionInfoStage::Undefined, int $StageNumber = 0) { $this->Major = $Major; $this->Minor = $Minor; $this->Release = $Release; $this->Build = $Build; $this->Stage = $Stage; $this->StageNumber = $StageNumber; } /** * Сравнивает две версии. * * @param VersionInfo $version1 Версия 1. * @param VersionInfo $version2 Версия 2. * * @return int Возвращает 1, если версия 1 больше версии 2, 0, если равны, -1, если версия 1 меньше версии 2. */ public static function Compare (VersionInfo $version1, VersionInfo $version2): int { return $version1->CompareWith($version2); } /** * Сравнивает текущую версию с переданной. * * @param VersionInfo $version Переданная версия. * * @return int Возвращает 1, если текущая версия больше переданной, 0, если равны, -1, если текущая версия меньше * переданной. */ public function CompareWith (VersionInfo $version): int { // Задаем шаблон вывода $toStringTemplate = '#{Major}.#{Minor}.#{Release}.#{Build}'; // Сначала сравним числа (мажорную, минорную версии, номер релиза и сборки): $compareResult = version_compare( $this->ToString($toStringTemplate), $version->ToString($toStringTemplate) ); // Если численные части совпадают, проверяем стадии сборки if ($compareResult === 0) { // - особый случай: стабильная версия всегда больше любых промежуточных стадий if ($this->Stage === VersionInfoStage::Stable && self::IsNotUndefinedOrStable($version->Stage)) // - считаем нашу старшей return 1; // - особый случай: если наша версия не stable, а другая stable if ($version->Stage === VersionInfoStage::Stable && self::IsNotUndefinedOrStable($this->Stage)) // - считаем нашу младшей return -1; // - преобразуем стадии в целые числа для прямого сравнения $currentStageValue = $this->Stage->ToInt(); $otherStageValue = $version->Stage->ToInt(); // - если стадии отличаются if ($currentStageValue !== $otherStageValue) // - сравниваем их return $currentStageValue <=> $otherStageValue; // - если стадии одинаковые if (self::IsNotUndefinedOrStable($this->Stage) && self::IsNotUndefinedOrStable($version->Stage) && $this->StageNumber !== $version->StageNumber) return $this->StageNumber <=> $version->StageNumber; // - если все совпадает, то вернём 0 return 0; } // Возврат результата сравнения return $compareResult; } /** * Возвращает строковое представление версии. * * @param string $template Шаблон вывода. В шаблоне маркеры #{Major}, #{Minor}, #{Release}, #{Build}, #{Stage} и * #{StageNumber} заменяются на значения соответствующих свойств. По умолчанию используется * {@link DefaultTemplate}. * @param array $stagesNames Имена стадий сборки (по умолчанию из константы {@link StagesNames}). * * @return string Строковое представление версии. */ public function ToString (string $template = self::DefaultTemplate, array $stagesNames = self::StagesNames): string { // Получаем строковое представление стадии сборки $stage = match ($this->Stage) { // - для неопределенной стадии сборки и релиза возвращаем пустую строку VersionInfoStage::Undefined, VersionInfoStage::Stable => '', // - для остальных стадий сборки возвращаем строковое представление стадии сборки VersionInfoStage::PreAlpha => $stagesNames[VersionInfoStage::PreAlpha->value], VersionInfoStage::Alpha => $stagesNames[VersionInfoStage::Alpha->value], VersionInfoStage::Beta => $stagesNames[VersionInfoStage::Beta->value], VersionInfoStage::ReleaseCandidate => $stagesNames[VersionInfoStage::ReleaseCandidate->value] }; // Возвращаем строковое представление номера стадии сборки, если конечно стадия определена и она не релиз // и номер сборки должен быть задан (больше 0) $stageNum = self::IsNotUndefinedOrStable($this->Stage) && $this->StageNumber > 0 ? "$this->StageNumber" : ''; // Создаём массив для замены $replaceData = [ '#{Major}' => "$this->Major", '#{Minor}' => "$this->Minor", '#{Release}' => "$this->Release", '#{Build}' => "$this->Build", '#{Stage}' => $stage, '#{StageNumber}' => $stageNum ]; // Заменяем все в шаблоне $result = StringExtension::ReplaceAll($replaceData, $template); // Возвращаем результат, удаляя лишние пробелы в конце return rtrim($result, ' '); } /** * Проверяет, является ли версия неопределённой или релизом. * * @param VersionInfoStage $versionStage Стадия сборки. * * @return bool Возвращает true, если стадия сборки определена и не является релизом. */ public static function IsNotUndefinedOrStable (VersionInfoStage $versionStage): bool { return $versionStage !== VersionInfoStage::Undefined && $versionStage !== VersionInfoStage::Stable; } /** * Преобразует строку с версией в объект {@link VersionInfo}. * * @param string $version Строка с версией (она должна быть в формате {@link DefaultTemplate}). * @param array $stagesNames Имена стадий сборки (по умолчанию из константы {@link StagesNames}). * * @return VersionInfo Возвращает объект {@link VersionInfo} с данными о версии. */ public static function Parse (string $version, array $stagesNames = self::StagesNames): VersionInfo { // Убираем пробелы в начале и конце строки $version = trim($version); // Разбиваем строку на части по пробелам (1 часть - основная - мажор, минор, релиз, сборка, // 2 часть - стадия и 3 - номер стадии) $versionParts = explode(' ', $version); // Проверяем, что строка содержит хотя бы 1 часть if (count($versionParts) < 1) // - если нет, то возвращаем пустую версию return new VersionInfo(0, 0, 0, 0); // Составим регулярное выражение для парсинга базовой информации о версии $pattern = '/^(\d+)\.(\d+)\.(\d+)\.(\d+)/'; // Парсим базовую информацию о версии if (!preg_match($pattern, $versionParts[0], $matches)) // - если не удалось, то возвращаем пустую версию return new VersionInfo(0, 0, 0, 0); // Проверяем, что найдены все 4 части if (!$matches || count($matches) < 5) // - если нет, то возвращаем пустую версию return new VersionInfo(0, 0, 0, 0); // Получаем значения [, $major, $minor, $release, $build] = $matches; // Парсим обязательные цифры // - мажорная версия $major = intval($major); // - минорная версия $minor = intval($minor); // - номер релиза $release = intval($release); // - номер сборки $build = intval($build); // Если частей версии больше 1 if (count($versionParts) > 1) { // - получаем массив для перевода строковых имен стадий в числа $stageEnumMap = array_flip($stagesNames); // - получаем стадию $stage = VersionInfoStage::FromInt(intval($stageEnumMap[$versionParts[1]] ?? 0)); // - если стадия определена и не релиз, и кроме того, есть номер стадии if (self::IsNotUndefinedOrStable($stage) && count($versionParts) > 2) // -- получаем номер стадии $stageNumber = intval($versionParts[2]); else // -- иначе, устанавливаем номер стадии 0 $stageNumber = 0; } else { // - иначе, устанавливаем стадию неопределённой $stage = VersionInfoStage::Undefined; // - и номер стадии 0 $stageNumber = 0; } // Создание объекта VersionInfo return new VersionInfo($major, $minor, $release, $build, $stage, $stageNumber); } /** * Выводит строковое представление версии (упрощённая версия ToString() с шаблоном по умолчанию, необходимая для * вывода при попытке привести объект к типу string). * * @return string Строковое представление версии. */ public function __toString (): string { // Получаем строковое представление версии и возвращаем его return $this->ToString(); } }