Version.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <?php declare(strict_types = 1);
  2. /*
  3. * This file is part of PharIo\Version.
  4. *
  5. * (c) Arne Blankerts <arne@blankerts.de>, Sebastian Heuer <sebastian@phpeople.de>, Sebastian Bergmann <sebastian@phpunit.de>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace PharIo\Version;
  11. class Version {
  12. /** @var string */
  13. private $originalVersionString;
  14. /** @var VersionNumber */
  15. private $major;
  16. /** @var VersionNumber */
  17. private $minor;
  18. /** @var VersionNumber */
  19. private $patch;
  20. /** @var null|PreReleaseSuffix */
  21. private $preReleaseSuffix;
  22. /** @var null|BuildMetaData */
  23. private $buildMetadata;
  24. public function __construct(string $versionString) {
  25. $this->ensureVersionStringIsValid($versionString);
  26. $this->originalVersionString = $versionString;
  27. }
  28. /**
  29. * @throws NoPreReleaseSuffixException
  30. */
  31. public function getPreReleaseSuffix(): PreReleaseSuffix {
  32. if ($this->preReleaseSuffix === null) {
  33. throw new NoPreReleaseSuffixException('No pre-release suffix set');
  34. }
  35. return $this->preReleaseSuffix;
  36. }
  37. public function getOriginalString(): string {
  38. return $this->originalVersionString;
  39. }
  40. public function getVersionString(): string {
  41. $str = \sprintf(
  42. '%d.%d.%d',
  43. $this->getMajor()->getValue() ?? 0,
  44. $this->getMinor()->getValue() ?? 0,
  45. $this->getPatch()->getValue() ?? 0
  46. );
  47. if (!$this->hasPreReleaseSuffix()) {
  48. return $str;
  49. }
  50. return $str . '-' . $this->getPreReleaseSuffix()->asString();
  51. }
  52. public function hasPreReleaseSuffix(): bool {
  53. return $this->preReleaseSuffix !== null;
  54. }
  55. public function equals(Version $other): bool {
  56. if ($this->getVersionString() !== $other->getVersionString()) {
  57. return false;
  58. }
  59. if ($this->hasBuildMetaData() !== $other->hasBuildMetaData()) {
  60. return false;
  61. }
  62. if ($this->hasBuildMetaData() && $other->hasBuildMetaData() &&
  63. !$this->getBuildMetaData()->equals($other->getBuildMetaData())) {
  64. return false;
  65. }
  66. return true;
  67. }
  68. public function isGreaterThan(Version $version): bool {
  69. if ($version->getMajor()->getValue() > $this->getMajor()->getValue()) {
  70. return false;
  71. }
  72. if ($version->getMajor()->getValue() < $this->getMajor()->getValue()) {
  73. return true;
  74. }
  75. if ($version->getMinor()->getValue() > $this->getMinor()->getValue()) {
  76. return false;
  77. }
  78. if ($version->getMinor()->getValue() < $this->getMinor()->getValue()) {
  79. return true;
  80. }
  81. if ($version->getPatch()->getValue() > $this->getPatch()->getValue()) {
  82. return false;
  83. }
  84. if ($version->getPatch()->getValue() < $this->getPatch()->getValue()) {
  85. return true;
  86. }
  87. if (!$version->hasPreReleaseSuffix() && !$this->hasPreReleaseSuffix()) {
  88. return false;
  89. }
  90. if ($version->hasPreReleaseSuffix() && !$this->hasPreReleaseSuffix()) {
  91. return true;
  92. }
  93. if (!$version->hasPreReleaseSuffix() && $this->hasPreReleaseSuffix()) {
  94. return false;
  95. }
  96. return $this->getPreReleaseSuffix()->isGreaterThan($version->getPreReleaseSuffix());
  97. }
  98. public function getMajor(): VersionNumber {
  99. return $this->major;
  100. }
  101. public function getMinor(): VersionNumber {
  102. return $this->minor;
  103. }
  104. public function getPatch(): VersionNumber {
  105. return $this->patch;
  106. }
  107. /**
  108. * @psalm-assert-if-true BuildMetaData $this->buildMetadata
  109. * @psalm-assert-if-true BuildMetaData $this->getBuildMetaData()
  110. */
  111. public function hasBuildMetaData(): bool {
  112. return $this->buildMetadata !== null;
  113. }
  114. /**
  115. * @throws NoBuildMetaDataException
  116. */
  117. public function getBuildMetaData(): BuildMetaData {
  118. if (!$this->hasBuildMetaData()) {
  119. throw new NoBuildMetaDataException('No build metadata set');
  120. }
  121. return $this->buildMetadata;
  122. }
  123. /**
  124. * @param string[] $matches
  125. *
  126. * @throws InvalidPreReleaseSuffixException
  127. */
  128. private function parseVersion(array $matches): void {
  129. $this->major = new VersionNumber((int)$matches['Major']);
  130. $this->minor = new VersionNumber((int)$matches['Minor']);
  131. $this->patch = isset($matches['Patch']) ? new VersionNumber((int)$matches['Patch']) : new VersionNumber(0);
  132. if (isset($matches['PreReleaseSuffix']) && $matches['PreReleaseSuffix'] !== '') {
  133. $this->preReleaseSuffix = new PreReleaseSuffix($matches['PreReleaseSuffix']);
  134. }
  135. if (isset($matches['BuildMetadata'])) {
  136. $this->buildMetadata = new BuildMetaData($matches['BuildMetadata']);
  137. }
  138. }
  139. /**
  140. * @param string $version
  141. *
  142. * @throws InvalidVersionException
  143. */
  144. private function ensureVersionStringIsValid($version): void {
  145. $regex = '/^v?
  146. (?P<Major>0|[1-9]\d*)
  147. \\.
  148. (?P<Minor>0|[1-9]\d*)
  149. (\\.
  150. (?P<Patch>0|[1-9]\d*)
  151. )?
  152. (?:
  153. -
  154. (?<PreReleaseSuffix>(?:(dev|beta|b|rc|alpha|a|patch|p|pl)\.?\d*))
  155. )?
  156. (?:
  157. \\+
  158. (?P<BuildMetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-@]+)*)
  159. )?
  160. $/xi';
  161. if (\preg_match($regex, $version, $matches) !== 1) {
  162. throw new InvalidVersionException(
  163. \sprintf("Version string '%s' does not follow SemVer semantics", $version)
  164. );
  165. }
  166. $this->parseVersion($matches);
  167. }
  168. }