В класс VersionInfo добавлены статические методы MinVersion и MaxVersion. Метод CompareWithRange обновлён с учётом новых методов
		
			
				
	
	
		
			391 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			391 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | ||
| 
 | ||
| namespace goodboyalex\php_components_pack\classes;
 | ||
| 
 | ||
| use goodboyalex\php_components_pack\enums\VersionCompareRangeOption;
 | ||
| use goodboyalex\php_components_pack\enums\VersionInfoStage;
 | ||
| use goodboyalex\php_components_pack\extensions\StringExtension;
 | ||
| 
 | ||
| /**
 | ||
|  * Класс, описывающий информацию о версии.
 | ||
|  *
 | ||
|  * @author Александр Бабаев
 | ||
|  * @package php_components_pack
 | ||
|  * @version 1.2
 | ||
|  * @since 1.0.24
 | ||
|  */
 | ||
| final class VersionInfo
 | ||
| {
 | ||
|     /**
 | ||
|      * @var array $StagesNames Имена стадий сборки.
 | ||
|      */
 | ||
|     public const array StagesNames = [
 | ||
|         VersionInfoStage::PreAlpha->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();
 | ||
|     }
 | ||
| 
 | ||
|     /**
 | ||
|      * Проверяет, находится ли текущая версия в заданном диапазоне.
 | ||
|      *
 | ||
|      * @param VersionInfo|null $left Левая граница диапазона или null, если граница не задана (будет использована
 | ||
|      *     минимальная версия).
 | ||
|      * @param VersionInfo|null $right Правая граница диапазона или null, если граница не задана (будет использована
 | ||
|      *     максимальная версия).
 | ||
|      * @param ObjectArray|null $options Опции проверки (по умолчанию массив, который разрешает включать границы).
 | ||
|      *
 | ||
|      * @return int Возвращает 1, если текущая версия старше интервала, 0, если находится в интервале, -1, если текущая
 | ||
|      *     версия младше интервала.
 | ||
|      *
 | ||
|      * @version 1.1
 | ||
|      * @since 1.0.27
 | ||
|      */
 | ||
|     public function CompareWithRange (?VersionInfo $left = null, ?VersionInfo $right = null,
 | ||
|         ?ObjectArray $options = null): int
 | ||
|     {
 | ||
|         // Подготавливаем опции, если они не переданы
 | ||
|         $options ??= new ObjectArray([
 | ||
|             VersionCompareRangeOption::LeftBorderIncluded,
 | ||
|             VersionCompareRangeOption::RightBorderIncluded
 | ||
|         ]);
 | ||
| 
 | ||
|         // Устанавливаем левую границу
 | ||
|         $leftBorder = $left ?? self::MinVersion();
 | ||
| 
 | ||
|         // Устанавливаем правую границу
 | ||
|         $rightBorder = $right ?? self::MaxVersion();
 | ||
| 
 | ||
|         // Проверяем, что границы не перепутаны
 | ||
|         if ($leftBorder->CompareWith($rightBorder) == 0)
 | ||
|             // - если границы перепутаны, то меняем их местами
 | ||
|             [$leftBorder, $rightBorder] = [$rightBorder, $leftBorder];
 | ||
| 
 | ||
|         // Проверяем текущую версию относительно границ
 | ||
|         $compareLeft = $this->CompareWith($leftBorder);
 | ||
|         $compareRight = $this->CompareWith($rightBorder);
 | ||
| 
 | ||
|         // Проверяем, что границы включены
 | ||
|         // - левая
 | ||
|         $leftBorderIncluded = $options->IsExist(fn (VersionCompareRangeOption $option)
 | ||
|             => $option === VersionCompareRangeOption::LeftBorderIncluded);
 | ||
|         // - правая
 | ||
|         $rightBorderIncluded = $options->IsExist(fn (VersionCompareRangeOption $option)
 | ||
|             => $option === VersionCompareRangeOption::RightBorderIncluded);
 | ||
| 
 | ||
|         // Проверяем включение границ
 | ||
|         $isInInterval =
 | ||
|             (
 | ||
|                 ($compareLeft >= 0 && $leftBorderIncluded)
 | ||
|                 || ($compareLeft > 0)
 | ||
|             )
 | ||
|             && (
 | ||
|                 ($compareRight <= 0 && $rightBorderIncluded)
 | ||
|                 || ($compareRight < 0)
 | ||
|             );
 | ||
| 
 | ||
|         // Итоговая обработка результатов
 | ||
|         if ($isInInterval)
 | ||
|             // - версия в пределах диапазона
 | ||
|             return 0;
 | ||
|         else // - версия не в пределах диапазона, и если она младше левой границы (или равна ей при не включении границы)
 | ||
|             if ($compareLeft <= 0)
 | ||
|                 // -- то возвращаем -1
 | ||
|                 return -1;
 | ||
|             else
 | ||
|                 // -- в противном случае, версия старше правой границы (или равна ей при не включении границы)
 | ||
|                 return 1;
 | ||
|     }
 | ||
| 
 | ||
|     /**
 | ||
|      * Возвращает минимальную версию.
 | ||
|      *
 | ||
|      * @return VersionInfo Минимальная версия.
 | ||
|      *
 | ||
|      * @since 1.0.28
 | ||
|      */
 | ||
|     public static function MinVersion (): VersionInfo
 | ||
|     {
 | ||
|         return new VersionInfo(0, 0, 0, 0, VersionInfoStage::PreAlpha, 0);
 | ||
|     }
 | ||
| 
 | ||
|     /**
 | ||
|      * Возвращает максимальную версию.
 | ||
|      *
 | ||
|      * @return VersionInfo Максимальная версия.
 | ||
|      *
 | ||
|      * @since 1.0.28
 | ||
|      */
 | ||
|     public static function MaxVersion (): VersionInfo
 | ||
|     {
 | ||
|         return new VersionInfo(PHP_INT_MAX, PHP_INT_MAX, PHP_INT_MAX, PHP_INT_MAX, VersionInfoStage::Stable);
 | ||
|     }
 | ||
| } |