diff --git a/sources/classes/VersionInfo.md b/sources/classes/VersionInfo.md new file mode 100644 index 0000000..d5e64a6 --- /dev/null +++ b/sources/classes/VersionInfo.md @@ -0,0 +1,272 @@ +# Описание класса VersionInfo + +## Информация о версии + +Версия класса: 1.0 + +Впервые введено в пакет с версии: 1.0.24 + +Описание класса: Класс, описывающий информацию о версии. + +## Публичные свойства и константы класса + +В классе определены следующие константы: + +* `array StagesNames` - имена стадий сборки. +* `string DefaultTemplate` - шаблон вывода по умолчанию. + +В классе определены следующе свойства: + +* `int $Major` - мажорная версия (**только чтение**). +* `int $Minor` - минорная версия (**только чтение**). +* `int $Release` - номер релиза (**только чтение**). +* `int $Build` - номер сборки (**только чтение**). +* `VersionInfoStage $Stage` - стадия сборки (**только чтение**). +* `int $StageNumber` - номер стадии сборки (**только чтение**). + +## Методы и функции + +### Конструктор. + +Конструктор принимает **4 обязательных** и **2 дополнительных параметра**: + +* `int $Major` - мажорная версия (**обязательный**); +* `int $Minor` - минорная версия (**обязательный**); +* `int $Release` - номер релиза (**обязательный**); +* `int $Build` - номер сборки (**обязательный**); +* `VersionInfoStage` $Stage - стадия сборки (по умолчанию, `VersionInfoStage::Undefined`); +* `int $StageNumber` - номер стадии сборки (по умолчанию, `0`). + +В результате создаётся новый класс `VersionInfo`. + +Пример: + + $version = new VersionInfo (1, 0, 0, 0, VersionInfoStage::Beta, 1); + +Создаст класс `VersionInfo` для версии `1.0.0.0 Beta 1`. + +### Преобразование в строку. + +За преобразование в строку отвечает 2 метода: `ToString` и `__toString ()`. + +#### Метод `ToString` + +Этот метод возвращает строковое представление версии. Он содержит **2 необязательных параметра**: + +* `string $template` - шаблон вывода (по умолчанию, `DefaultTemplate`); +* `array $stagesNames` - имена стадий сборки (по умолчанию, `StagesNames`). + +Метод возвращает `string` - строковое представление версии. + +В шаблоне должны присутствовать маркеры: + +| Маркер | Описание | Обязательный | +|:--------------:|:--------------------|:------------:| +| #{Major} | Мажорная версия | ДА | +| #{Minor} | Минорная версия | ДА | +| #{Release} | Номер релиза | ДА | +| #{Build} | Номер сборки | ДА | +| #{Stage} | Стадия сборки | НЕТ | +| #{StageNumber} | Номер стадии сборки | НЕТ | + +Эти маркеры в шаблоне заменяются на значения соответствующих свойств. + +Синтаксис: + + public function ToString (string $template, array $stagesNames): string + +Пример, + + // Создаю объект + $version = new VersionInfo (1, 2, 3, 4, VersionInfoStage::Beta, 5); + + // Задаю шаблон + $template = #{Major}.#{Minor}.#{Release} (сборка #{Build}); + + // Вывожу + echo $version->ToString($template); + +В результате на экране появится: + + 1.2.3 (сборка 4) + +#### Метод `__toString` + +Этот метод полностью аналогичен методу `ToString` с той лишь разницей, что в этом методе невозможно задать параметры +вывода (такие, как шаблон и имена стадий). Эти параметры берутся по умолчанию. + +Этот метод необходим для вывода корректной информации при попытке привести объект класса `VersionInfo` к типу `string`. + +Метод возвращает `string` - строковое представление версии. + +Синтаксис: + + public function __toString (): string + +Пример, + + // Создаю объект + $version = new VersionInfo (1, 2, 3, 4, VersionInfoStage::Beta, 5); + + // Вывожу + echo $version; + +В результате на экране появится: + + 1.2.3.4 Beta 5 + +### Сравнение версий + +Для сравнения версий используется две аналогичные друг другу (но различающиеся по способу вызова) функции: `Compare` и +`CompareWith`. + +#### Метод `Compare` + +Это статический метод, который сравнивает две версии, заданные в **2 обязательных параметрах**: + +* `VersionInfo $version1` - версия 1; +* `VersionInfo $version2` - версия 2. + +Этот метод возвращает `int`: + +| version1 | version2 | Результат | +|:---------:|:---------:|:---------:| +| Младше | Старше | -1 | +| Совпадает | Совпадает | 0 | +| Старше | Младше | 1 | + +Синтаксис: + + public static function Compare (VersionInfo $version1, VersionInfo $version2): int + +Пример, + + // Создаю объекты + // - $version11 младше $version12 + $version11 = new VersionInfo (1, 2, 3, 4, VersionInfoStage::Beta, 5); + $version12 = new VersionInfo (1, 2, 3, 5, VersionInfoStage::Beta, 6); + + // $version21 совпадает с $version22 + $version21 = new VersionInfo (1, 2, 3, 4, VersionInfoStage::Beta, 5); + $version22 = new VersionInfo (1, 2, 3, 4, VersionInfoStage::Beta, 5); + + // $version31 старше $version32 + $version31 = new VersionInfo (1, 2, 3, 4, VersionInfoStage::Stable); + $version32 = new VersionInfo (1, 2, 3, 4, VersionInfoStage::Beta, 5); + + // Сравниваю + $verCompare1 = VersionInfo::Compare($version11, $version12); + $verCompare2 = VersionInfo::Compare($version21, $version22); + $verCompare3 = VersionInfo::Compare($version31, $version32); + + // Вывожу + echo $verCompare1 . '|' . $verCompare2 . '|' . $verCompare3; + +В результате на экране появится: + + -1|0|1 + +#### Метод `CompareWith` + +Этот метод полностью аналогичен методу `Compare`. Он отличается лишь только тем, что **не статичен** и вызывается из +ранее созданного экземпляра класса. Он сравнивает текущую версию с переданной. Этот метод имеет только **1 обязательный +параметр**: `VersionInfo $version` - переданная версия. + +Этот метод возвращает `int`: `1`, если текущая версия старше переданной, `0`, если совпадает и `-1`, если текущая версия +младше переданной. + +Синтаксис: + + public function CompareWith (VersionInfo $version): int + +Пример, + + // Создаю объекты + // - $version11 младше $version12 + $version11 = new VersionInfo (1, 2, 3, 4, VersionInfoStage::Beta, 5); + $version12 = new VersionInfo (1, 2, 3, 5, VersionInfoStage::Beta, 6); + + // $version21 совпадает с $version22 + $version21 = new VersionInfo (1, 2, 3, 4, VersionInfoStage::Beta, 5); + $version22 = new VersionInfo (1, 2, 3, 4, VersionInfoStage::Beta, 5); + + // $version31 старше $version32 + $version31 = new VersionInfo (1, 2, 3, 4, VersionInfoStage::Stable); + $version32 = new VersionInfo (1, 2, 3, 4, VersionInfoStage::Beta, 5); + + // Сравниваю + $verCompare1 = $version11->CompareWith($version12); + $verCompare2 = $version21->CompareWith($version22); + $verCompare3 = $version31->CompareWith($version32); + + // Вывожу + echo $verCompare1 . '|' . $verCompare2 . '|' . $verCompare3; + +В результате на экране появится: + + -1|0|1 + +### Парсинг версий + +Для преобразования строки с версией в объект `VersionInfo` используется статичный метод `Parse`. + +Он требует **1 обязательный параметр** и **1 необязательный**: + +* `string $version` - строка с версией (она должна быть в формате + `#{Major}.#{Minor}.#{Release}.#{Build} #{Stage} #{StageNumber}`, причём `#{Stage}` и `#{StageNumber}` можно не + указывать); +* `array $stagesNames` - имена стадий сборки (по умолчанию, `StagesNames`). + +Этот метод возвращает объект {@link VersionInfo} с данными о версии или `new VersionInfo(0, 0, 0, 0)` в случае ошибки. + +Синтаксис: + + public static function Parse (string $version, array $stagesNames = self::StagesNames): VersionInfo + +Пример, + + // Создаю текст + $verText = '1.2.3.4 Beta 2'; + + // Преобразую + $version = VersionInfo::Parse($verText); + + // Вывожу + echo $version; + +В результате на экране появится: + + 1.2.3.4 Beta 2 + +### Вспомогательные методы + +#### Метод `IsNotUndefinedOrStable` + +Этот **статический** метод проверяет, является ли версия в неопределённой стадии или уже релизом. + +Он имеет только **1 обязательный параметр**: `VersionInfoStage $versionStage` - стадия сборки. + +Этот метод возвращает `bool`: `true`, если стадия сборки определена и не является релизом и `false` в противном случае. + +Синтаксис: + + public static function IsNotUndefinedOrStable (VersionInfoStage $versionStage): bool + +Пример, + + // Создаю объекты + $version1 = new VersionInfo (1, 2, 3, 4, VersionInfoStage::Undefined, 5); + $version2 = new VersionInfo (1, 2, 3, 4, VersionInfoStage::Beta, 5); + $version3 = new VersionInfo (1, 2, 3, 4, VersionInfoStage::Stable); + + // Сравниваю + $res1 = VersionInfo::IsNotUndefinedOrStabl($version1->Stage); + $res2 = VersionInfo::IsNotUndefinedOrStabl($version2->Stage); + $res3 = VersionInfo::IsNotUndefinedOrStabl($version3->Stage); + + // Вывожу + echo $res1 . '|' . $res2 . '|' . $res3; + +В результате на экране появится: + + false|true|false \ No newline at end of file diff --git a/sources/classes/VersionInfo.php b/sources/classes/VersionInfo.php new file mode 100644 index 0000000..e73d546 --- /dev/null +++ b/sources/classes/VersionInfo.php @@ -0,0 +1,295 @@ +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(); + } +} \ No newline at end of file diff --git a/sources/enums/VersionInfoStage.php b/sources/enums/VersionInfoStage.php new file mode 100644 index 0000000..379e7e3 --- /dev/null +++ b/sources/enums/VersionInfoStage.php @@ -0,0 +1,49 @@ +PrepareForTest(); + + $versionInfo1 = new VersionInfo(1, 2, 3, 4, VersionInfoStage::Beta, 1); + $versionInfo2 = new VersionInfo(1, 1, 3, 4, VersionInfoStage::Alpha, 2); + + $this->assertEquals(1, $versionInfo1->CompareWith($versionInfo2)); + + } + + private function PrepareForTest (): void + { + require_once __DIR__ . '/../../sources/enums/VersionInfoStage.php'; + require_once __DIR__ . '/../../sources/classes/VersionInfo.php'; + } + + public function test__toString () + { + $this->PrepareForTest(); + $versionInfo = new VersionInfo(1, 2, 3, 4, VersionInfoStage::Beta, 1); + $this->assertEquals('1.2.3.4 Beta 1', $versionInfo->__toString()); + } + + public function testToString () + { + $this->PrepareForTest(); + $versionInfo = new VersionInfo(1, 2, 3, 4, VersionInfoStage::Beta, 1); + + $template = "Это #{Stage} #{StageNumber} версии #{Major}.#{Minor}.#{Release}.#{Build}"; + + $stageNames = [ + VersionInfoStage::PreAlpha->value => 'рано', + VersionInfoStage::Alpha->value => 'начало', + VersionInfoStage::Beta->value => 'середнячок', + VersionInfoStage::ReleaseCandidate->value => 'почти', + VersionInfoStage::Stable->value => 'это релиз, детка!' + ]; + + $this->assertEquals('Это середнячок 1 версии 1.2.3.4', $versionInfo->ToString($template, $stageNames)); + } + + public function testParse () + { + $this->PrepareForTest(); + + $stageNames = [ + VersionInfoStage::PreAlpha->value => 'pre', + VersionInfoStage::Alpha->value => 'alpha', + VersionInfoStage::Beta->value => 'beta', + VersionInfoStage::ReleaseCandidate->value => 'rc', + VersionInfoStage::Stable->value => 'stable' + ]; + + $version = VersionInfo::Parse('1.2.3.4 beta 1', $stageNames); + + $this->assertEquals(1, $version->Major); + $this->assertEquals(2, $version->Minor); + $this->assertEquals(3, $version->Release); + $this->assertEquals(4, $version->Build); + $this->assertEquals(VersionInfoStage::Beta, $version->Stage); + $this->assertEquals(1, $version->StageNumber); + } + + public function test__construct () + { + $this->PrepareForTest(); + + $version = new VersionInfo(1, 2, 3, 4, VersionInfoStage::Beta, 1); + + $this->assertEquals(2, $version->Minor); + } + + public function testCompare () + { + $this->PrepareForTest(); + + $versionInfo1 = new VersionInfo(1, 2, 3, 4, VersionInfoStage::Beta, 1); + $versionInfo2 = new VersionInfo(1, 1, 3, 4, VersionInfoStage::Alpha, 2); + + $this->assertEquals(1, VersionInfo::Compare($versionInfo1, $versionInfo2)); + } +} \ No newline at end of file