[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\nindent_size = 4\nindent_style = space\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n[*.json]\nindent_style = space\nindent_size = 4\n\n[*.md]\ntrim_trailing_whitespace = false\nindent_size = 4\n\n[*.{yml,yaml}]\nindent_size = 2\nindent_style = space\n\n"
  },
  {
    "path": ".gitattributes",
    "content": "tests           export-ignore\n.gitattributes  export-ignore\n.gitignore      export-ignore\n.php_cs.dist    export-ignore\n.travis.yml     export-ignore\nphpunit.xml     export-ignore\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: PHLAK\npatreon: PHLAK\ncustom: https://paypal.me/ChrisKankiewicz\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\n\nupdates:\n  - package-ecosystem: composer\n    directory: \"/\"\n    schedule:\n      interval: monthly\n      timezone: US/Arizona\n    open-pull-requests-limit: 10\n    assignees:\n    - PHLAK\n\n  - package-ecosystem: github-actions\n    directory: \"/\"\n    schedule:\n      interval: monthly\n      timezone: US/Arizona\n    assignees:\n      - PHLAK\n"
  },
  {
    "path": ".github/workflows/test-suite.yaml",
    "content": "name: SemVer Test Suite\non: [push, pull_request, workflow_dispatch]\n\njobs:\n  coding-standards:\n    name: Coding Standards\n    runs-on: 'ubuntu-latest'\n\n    env:\n      PHP_CS_FIXER_IGNORE_ENV: 1\n\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@v5\n\n      - name: Setup PHP\n        uses: shivammathur/setup-php@v2\n        with:\n          php-version: '8.4'\n\n      - name: Install PHP Dependencies\n        run: composer install --no-interaction --no-progress --no-scripts --prefer-dist\n\n      - name: Verify Coding Standards\n        run: vendor/bin/php-cs-fixer fix --diff --dry-run\n\n  static-analysis:\n    name: Static Analysis\n    runs-on: 'ubuntu-latest'\n\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@v5\n\n      - name: Setup PHP\n        uses: shivammathur/setup-php@v2\n        with:\n          php-version: '8.4'\n\n      - name: Install PHP Dependencies\n        run: composer install --no-interaction --no-progress --no-scripts --prefer-dist\n\n      - name: Run Static Analysis\n        run: vendor/bin/phpstan analyze\n\n  tests:\n    name: Tests\n    runs-on: 'ubuntu-latest'\n\n    strategy:\n      matrix:\n        php-versions: ['8.1', '8.2', '8.3', '8.4']\n\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@v5\n\n      - name: Setup PHP\n        uses: shivammathur/setup-php@v2\n        with:\n          php-version: ${{ matrix.php-versions }}\n          coverage: xdebug\n\n      - name: Install PHP Dependencies\n        run: composer install --no-interaction --no-progress --no-scripts --prefer-dist\n\n      - name: Run Tests\n        run: vendor/bin/phpunit --coverage-text\n"
  },
  {
    "path": ".gitignore",
    "content": "/.php-cs-fixer.cache\n/.phpunit.cache\n/composer.lock\n/vendor/\n"
  },
  {
    "path": ".php-cs-fixer.dist.php",
    "content": "<?php\n\nrequire __DIR__ . '/vendor/autoload.php';\n\n$finder = PhpCsFixer\\Finder::create()->in([\n    __DIR__ . DIRECTORY_SEPARATOR . 'src',\n    __DIR__ . DIRECTORY_SEPARATOR . 'tests',\n]);\n\nreturn PHLAK\\CodingStandards\\ConfigFactory::make($finder);\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2024 Chris Kankiewicz <Chris@ChrisKankiewicz.com>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "dev development: # Build application for development\n\t@composer install --no-interaction\n\nprod production: # Build application for production\n\t@composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader\n\ntest: # Run coding standards/static analysis checks and tests\n\t@vendor/bin/php-cs-fixer fix --diff --dry-run \\\n\t\t&& vendor/bin/phpstan analyze \\\n\t\t&& vendor/bin/phpunit --coverage-text\n\ncoverage: # Generate an HTML coverage report\n\t@vendor/bin/phpunit --coverage-html .coverage\n"
  },
  {
    "path": "README.md",
    "content": "SemVer\n======\n\n<p align=\"center\">\n  <img src=\"semver.png\" alt=\"SemVer\" width=\"50%\">\n</p>\n\n<p align=\"center\">\n    <a href=\"http://semver.org\">Semantic versioning</a> helper library\n  • Created by <a href=\"https://www.ChrisKankiewicz.com\">Chris Kankiewicz</a> (<a href=\"https://bsky.app/profile/phlak.dev\">@phlak.dev</a>)\n</p>\n\n<p align=\"center\">\n    <a href=\"https://github.com/PHLAK/SemVer/discussions\"><img src=\"https://img.shields.io/badge/Join_the-Community-7b16ff.svg?style=for-the-badge\" alt=\"Join our Community\"></a>\n    <a href=\"https://github.com/users/PHLAK/sponsorship\"><img src=\"https://img.shields.io/badge/Become_a-Sponsor-cc4195.svg?style=for-the-badge\" alt=\"Become a Sponsor\"></a>\n    <a href=\"https://paypal.me/ChrisKankiewicz\"><img src=\"https://img.shields.io/badge/Make_a-Donation-006bb6.svg?style=for-the-badge\" alt=\"One-time Donation\"></a>\n    <br>\n    <a href=\"https://packagist.org/packages/PHLAK/SemVer\"><img src=\"https://img.shields.io/packagist/v/PHLAK/SemVer.svg?style=flat-square\" alt=\"Latest Stable Version\"></a>\n    <a href=\"https://packagist.org/packages/PHLAK/SemVer\"><img src=\"https://img.shields.io/packagist/dt/PHLAK/SemVer.svg?style=flat-square\" alt=\"Total Downloads\"></a>\n    <a href=\"https://packagist.org/packages/PHLAK/SemVer\"><img src=\"https://img.shields.io/packagist/l/PHLAK/SemVer.svg?style=flat-square\" alt=\"License\"></a>\n    <a href=\"https://github.com/PHLAK/SemVer/actions\"><img src=\"https://img.shields.io/github/actions/workflow/status/PHLAK/SemVer/test-suite.yaml?style=flat-square&label=tests\" alt=\"Tests Status\"></a>\n</p>\n\n---\n\nRequirements\n------------\n\n  - [PHP](https://php.net) >= 8.1\n\nInstallation\n------------\n\n```bash\ncomposer require phlak/semver\n```\n\nInitializing\n------------\n\n```php\nuse PHLAK\\SemVer;\n\n$version = new SemVer\\Version(); // Initilializes to '0.1.0'\n```\n\nOr initialize with a custom version by passing a version string on creation.\nAccepts any valid semantic version string with or without a preceding `v`.\n\n```php\n$version = new SemVer\\Version('v1.2.3-alpha.5+sha.8d31ff4');\n```\n\nOr parse an incomple version string with the static `Version::parse()` constructor.\n\n```php\n$version = SemVer\\Version::parse('v1')   // Initializes to '1.0.0'\n$version = SemVer\\Version::parse('v1.2') // Initializes to '1.2.0'\n```\n\nUsage\n-----\n\n#### Retrieve the version or individual values\n\n```php\n$version = new SemVer\\Version('v1.2.3-beta.4+007');\n\necho $version;             // '1.2.3-beta.4+007'\necho $version->major;      // 1\necho $version->minor;      // 2\necho $version->patch;      // 3\necho $version->preRelease; // 'beta.4'\necho $version->build;      // '007'\n```\n\n#### Increment the version\n\n```php\n$version = new SemVer\\Version('v1.2.3');\n\n$version->incrementMajor();      // v1.2.3 -> v2.0.0\n$version->incrementMinor();      // v1.2.3 -> v1.3.0\n$version->incrementPatch();      // v1.2.3 -> v1.2.4\n$version->incrementPreRelease(); // v1.2.3-alpha.5 -> v1.2.3-alpha.6\n```\n\n#### Set (override) the version or individual values\n\n```php\n$version = new SemVer\\Version();\n\n$version->setVersion('v1.2.3');  // v1.2.3\n$version->setMajor(3);           // v1.2.3 -> v3.0.0\n$version->setMinor(5);           // v1.2.3 -> v1.5.0\n$version->setPatch(7);           // v1.2.3 -> 1.2.7\n$version->setPreRelease('rc.2'); // v1.2.3 -> v1.2.3-rc.2\n$version->setBuild('007');       // v1.2.3 -> v1.2.3+007\n```\n\n#### Clear pre-release / build values\n\n```php\n$version->setPreRelease(null); // v1.2.3-rc.2 -> v1.2.3\n$version->setBuild(null);      // v1.2.3+007 -> v1.2.3\n```\n\n\n#### Check for pre-release / build values\n\n```php\n$version->isPreRelease();\n$version->hasBuild();\n```\n\n#### Compare two SemVer objects\n\n```php\n$version1 = new SemVer\\Version('v1.2.3');\n$version2 = new SemVer\\Version('v3.2.1');\n\n$version1->gt($version2);  // false\n$version1->lt($version2);  // true\n$version1->eq($version2);  // false\n$version1->neq($version2); // true\n$version1->gte($version2); // false\n$version1->lte($version2); // true\n```\n\n##### Limit comparison to the major version only\n\nIgnores the minor, patch and pre-release versions completely\n\n```php\n$version1 = new SemVer\\Version('v1.2.3-alpha.4');\n$version2 = new SemVer\\Version('v1.3.4-alpha.5');\n\n$version1->gt($version2, Compare::MAJOR);  // false\n$version1->lt($version2, Compare::MAJOR);  // false\n$version1->eq($version2, Compare::MAJOR);  // true\n$version1->neq($version2, Compare::MAJOR); // false\n$version1->gte($version2, Compare::MAJOR); // true\n$version1->lte($version2, Compare::MAJOR); // true\n```\n\n##### Limit comparison to the major and minor versions only\n\nIgnores the patch and pre-release versions completely\n\n```php\n$version1 = new SemVer\\Version('v1.2.3-alpha.4');\n$version2 = new SemVer\\Version('v1.2.4-alpha.5');\n\n$version1->gt($version2, Compare::MINOR);  // false\n$version1->lt($version2, Compare::MINOR);  // false\n$version1->eq($version2, Compare::MINOR);  // true\n$version1->neq($version2, Compare::MINOR); // false\n$version1->gte($version2, Compare::MINOR); // true\n$version1->lte($version2, Compare::MINOR); // true\n```\n\n##### Limit comparison to the major, minor and patch versions only\n\nIgnores the pre-release version completely\n\n```php\n$version1 = new SemVer\\Version('v1.2.3-alpha.4');\n$version2 = new SemVer\\Version('v1.2.3-alpha.5');\n\n$version1->gt($version2, Compare::PATCH);  // false\n$version1->lt($version2, Compare::PATCH);  // false\n$version1->eq($version2, Compare::PATCH);  // true\n$version1->neq($version2, Compare::PATCH); // false\n$version1->gte($version2, Compare::PATCH); // true\n$version1->lte($version2, Compare::PATCH); // true\n```\n\nTroubleshooting\n---------------\n\nFor general help and support join our [GitHub Discussions](https://github.com/PHLAK/SemVer/discussions) or reach out on [Bluesky](https://bsky.app/profile/phlak.dev).\n\nPlease report bugs to the [GitHub Issue Tracker](https://github.com/PHLAK/SemVer/issues).\n\nCopyright\n---------\n\nThis project is liscensed under the [MIT License](https://github.com/PHLAK/SemVer/blob/master/LICENSE).\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"phlak/semver\",\n    \"description\": \"Semantic versioning helper library\",\n    \"type\": \"library\",\n    \"license\": \"MIT\",\n    \"authors\": [\n        {\n            \"name\": \"Chris Kankiewicz\",\n            \"email\": \"Chris@ChrisKankiewicz.com\"\n        }\n    ],\n    \"funding\": [\n        {\n            \"type\": \"github\",\n            \"url\": \"https://github.com/sponsors/PHLAK\"\n        },\n        {\n            \"type\": \"paypal\",\n            \"url\": \"https://paypal.me/ChrisKankiewicz\"\n        }\n    ],\n    \"support\": {\n        \"issues\": \"https://github.com/PHLAK/SemVer/issues\"\n    },\n    \"require\": {\n        \"php\": \"^8.1 || ^8.2 || ^8.3 || ^8.4\"\n    },\n    \"require-dev\": {\n        \"phlak/coding-standards\": \"^4.0\",\n        \"phpstan/phpstan\": \"^2.0\",\n        \"yoast/phpunit-polyfills\": \"^4.0\"\n    },\n    \"autoload\": {\n        \"psr-4\": {\n            \"PHLAK\\\\SemVer\\\\\": \"src/\"\n        },\n        \"files\": [\"src/Support/helpers.php\"]\n    },\n    \"autoload-dev\": {\n        \"psr-4\": {\n            \"PHLAK\\\\Semver\\\\Tests\\\\\": \"tests/\"\n        }\n    },\n    \"config\": {\n        \"sort-packages\": true,\n        \"optimize-autoloader\": true,\n        \"allow-plugins\": {\n            \"composer/package-versions-deprecated\": true\n        }\n    }\n}\n"
  },
  {
    "path": "phpstan-ignores.neon",
    "content": "parameters:\n  ignoreErrors:\n    - message: \"/^Method .+ throws checked exception .+ but it's missing from the PHPDoc @throws tag.$/\"\n      path: tests/*\n"
  },
  {
    "path": "phpstan.neon.dist",
    "content": "parameters:\n\n  paths:\n    - src\n    - tests\n\n  level: max\n\n  checkFunctionNameCase: true\n\n  exceptions:\n    implicitThrows: false\n\n    check:\n      missingCheckedExceptionInThrows: true\n      tooWideThrowType: true\n\n    uncheckedExceptionClasses:\n      - 'RuntimeException'\n      - 'PHPUnit\\Framework\\Exception'\n\nincludes:\n  - phpstan-ignores.neon\n"
  },
  {
    "path": "phpunit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit bootstrap=\"vendor/autoload.php\" colors=\"true\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:noNamespaceSchemaLocation=\"https://schema.phpunit.de/10.5/phpunit.xsd\"\n>\n  <testsuites>\n    <testsuite name=\"PHLAK/SemVer Test Suite\">\n      <directory>tests/</directory>\n    </testsuite>\n  </testsuites>\n  <source>\n    <include>\n      <directory suffix=\".php\">src/</directory>\n    </include>\n  </source>\n</phpunit>\n"
  },
  {
    "path": "src/Enums/Compare.php",
    "content": "<?php\n\nnamespace PHLAK\\SemVer\\Enums;\n\nenum Compare\n{\n    case FULL;\n    case MAJOR;\n    case MINOR;\n    case PATCH;\n}\n"
  },
  {
    "path": "src/Exceptions/InvalidVersionException.php",
    "content": "<?php\n\nnamespace PHLAK\\SemVer\\Exceptions;\n\nuse Exception;\n\nclass InvalidVersionException extends Exception {}\n"
  },
  {
    "path": "src/Support/helpers.php",
    "content": "<?php\n\nuse PHLAK\\SemVer\\Version;\n\nif (! function_exists('semver')) {\n    /**\n     * Create a SemVer version object.\n     *\n     * @throws \\PHLAK\\SemVer\\Exceptions\\InvalidVersionException\n     */\n    function semver(string $string): Version\n    {\n        return new PHLAK\\SemVer\\Version($string);\n    }\n}\n"
  },
  {
    "path": "src/Traits/Comparable.php",
    "content": "<?php\n\nnamespace PHLAK\\SemVer\\Traits;\n\nuse PHLAK\\SemVer\\Enums\\Compare;\nuse PHLAK\\SemVer\\Version;\n\ntrait Comparable\n{\n    /**\n     * Compare two versions. Returns -1, 0 or 1 if the first version is less\n     * than, equal to or greater than the second version respectively.\n     *\n     * @param Version $version1 An instance of SemVer/Version\n     * @param Version $version2 An instance of SemVer/Version\n     */\n    public static function compare(Version $version1, Version $version2, Compare $comparison = Compare::FULL): int\n    {\n        [$v1, $v2] = match ($comparison) {\n            Compare::MAJOR => [[$version1->major], [$version2->major]],\n            Compare::MINOR => [[$version1->major, $version1->minor], [$version2->major, $version2->minor]],\n            default => [[$version1->major, $version1->minor, $version1->patch], [$version2->major, $version2->minor, $version2->patch]]\n        };\n\n        $baseComparison = $v1 <=> $v2;\n\n        if ($baseComparison !== 0 || $comparison !== Compare::FULL) {\n            return $baseComparison;\n        }\n\n        if ($version1->preRelease !== null && $version2->preRelease === null) {\n            return -1;\n        }\n\n        if ($version1->preRelease === null && $version2->preRelease !== null) {\n            return 1;\n        }\n\n        $v1preReleaseParts = explode('.', $version1->preRelease ?? '');\n        $v2preReleaseParts = explode('.', $version2->preRelease ?? '');\n\n        $preReleases1 = array_pad($v1preReleaseParts, count($v2preReleaseParts), null);\n        $preReleases2 = array_pad($v2preReleaseParts, count($v1preReleaseParts), null);\n\n        return $preReleases1 <=> $preReleases2;\n    }\n\n    /**\n     * Check if this Version object is greater than another.\n     *\n     * @param Version $version An instance of SemVer/Version\n     *\n     * @return bool True if this Version object is greater than the comparing\n     *              object, otherwise false\n     */\n    public function gt(Version $version, Compare $comparison = Compare::FULL): bool\n    {\n        return self::compare($this, $version, $comparison) > 0;\n    }\n\n    /**\n     * Check if this Version object is less than another.\n     *\n     * @param Version $version An instance of SemVer/Version\n     *\n     * @return bool True if this Version object is less than the comparing\n     *              object, otherwise false\n     */\n    public function lt(Version $version, Compare $comparison = Compare::FULL): bool\n    {\n        return self::compare($this, $version, $comparison) < 0;\n    }\n\n    /**\n     * Check if this Version object is equal to than another.\n     *\n     * @param Version $version An instance of SemVer/Version\n     *\n     * @return bool True if this Version object is equal to the comparing\n     *              object, otherwise false\n     */\n    public function eq(Version $version, Compare $comparison = Compare::FULL): bool\n    {\n        return self::compare($this, $version, $comparison) === 0;\n    }\n\n    /**\n     * Check if this Version object is not equal to another.\n     *\n     * @param Version $version An instance of SemVer/Version\n     *\n     * @return bool True if this Version object is not equal to the comparing\n     *              object, otherwise false\n     */\n    public function neq(Version $version, Compare $comparison = Compare::FULL): bool\n    {\n        return self::compare($this, $version, $comparison) !== 0;\n    }\n\n    /**\n     * Check if this Version object is greater than or equal to another.\n     *\n     * @param Version $version An instance of SemVer/Version\n     *\n     * @return bool True if this Version object is greater than or equal to the\n     *              comparing object, otherwise false\n     */\n    public function gte(Version $version, Compare $comparison = Compare::FULL): bool\n    {\n        return self::compare($this, $version, $comparison) >= 0;\n    }\n\n    /**\n     * Check if this Version object is less than or equal to another.\n     *\n     * @param Version $version An instance of SemVer/Version\n     *\n     * @return bool True if this Version object is less than or equal to the\n     *              comparing object, otherwise false\n     */\n    public function lte(Version $version, Compare $comparison = Compare::FULL): bool\n    {\n        return self::compare($this, $version, $comparison) <= 0;\n    }\n}\n"
  },
  {
    "path": "src/Traits/Incrementable.php",
    "content": "<?php\n\nnamespace PHLAK\\SemVer\\Traits;\n\ntrait Incrementable\n{\n    /**\n     * Increment the major version value by one.\n     *\n     * @return static This Version object\n     */\n    public function incrementMajor(): static\n    {\n        $this->setMajor($this->major + 1);\n\n        return $this;\n    }\n\n    /**\n     * Increment the minor version value by one.\n     *\n     * @return static This Version object\n     */\n    public function incrementMinor(): static\n    {\n        $this->setMinor($this->minor + 1);\n\n        return $this;\n    }\n\n    /**\n     * Increment the patch version value by one.\n     *\n     * @return static This Version object\n     */\n    public function incrementPatch(): static\n    {\n        $this->setPatch($this->patch + 1);\n\n        return $this;\n    }\n\n    /**\n     * Increment the pre-release version value by one.\n     *\n     * @return static This Version object\n     */\n    public function incrementPreRelease(): static\n    {\n        if (empty($this->preRelease)) {\n            $this->incrementPatch();\n            $this->setPreRelease('1');\n\n            return $this;\n        }\n\n        $identifiers = explode('.', $this->preRelease);\n\n        if (! is_numeric(end($identifiers))) {\n            $this->setPreRelease(implode('.', [$this->preRelease, '1']));\n\n            return $this;\n        }\n\n        array_push($identifiers, (string) ((int) array_pop($identifiers) + 1));\n\n        $this->setPreRelease(implode('.', $identifiers));\n\n        return $this;\n    }\n}\n"
  },
  {
    "path": "src/Version.php",
    "content": "<?php\n\nnamespace PHLAK\\SemVer;\n\nuse JsonSerializable;\nuse PHLAK\\SemVer\\Exceptions\\InvalidVersionException;\nuse PHLAK\\SemVer\\Traits\\Comparable;\nuse PHLAK\\SemVer\\Traits\\Incrementable;\n\n/**\n * @property int $major Major release number\n * @property int $minor Minor release number\n * @property int $patch Patch release number\n * @property string|null $preRelease Pre-release value\n * @property string|null $build Build release value\n */\nclass Version implements JsonSerializable\n{\n    use Comparable;\n    use Incrementable;\n\n    /** @var int Major release number */\n    protected $major;\n\n    /** @var int Minor release number */\n    protected $minor;\n\n    /** @var int Patch release number */\n    protected $patch;\n\n    /** @var string|null Pre-release value */\n    protected $preRelease;\n\n    /** @var string|null Build release value */\n    protected $build;\n\n    /**\n     * Class constructor, runs on object creation.\n     *\n     * @param string $version Version string\n     *\n     * @throws \\PHLAK\\SemVer\\Exceptions\\InvalidVersionException\n     */\n    final public function __construct(string $version = '0.1.0')\n    {\n        $this->setVersion($version);\n    }\n\n    /**\n     * Magic get method; provides access to version properties.\n     *\n     * @param string $property Version property\n     *\n     * @return mixed Version property value\n     */\n    public function __get(string $property)\n    {\n        return $this->$property;\n    }\n\n    /**\n     * Magic toString method; allows object interaction as if it were a string.\n     *\n     * @return string Current version string\n     */\n    public function __toString(): string\n    {\n        $version = implode('.', [$this->major, $this->minor, $this->patch]);\n\n        if (! empty($this->preRelease)) {\n            $version .= '-' . $this->preRelease;\n        }\n\n        if (! empty($this->build)) {\n            $version .= '+' . $this->build;\n        }\n\n        return $version;\n    }\n\n    /**\n     * Attempt to parse an incomplete version string.\n     *\n     * Examples: 'v1', 'v1.2', 'v1-beta.4', 'v1.3+007'\n     *\n     * @param string $version Version string\n     *\n     * @throws \\PHLAK\\SemVer\\Exceptions\\InvalidVersionException\n     *\n     * @return static This Version object\n     */\n    public static function parse(string $version): static\n    {\n        $semverRegex = '/^v?(?<major>\\d+)(?:\\.(?<minor>\\d+)(?:\\.(?<patch>\\d+))?)?(?:-(?<pre_release>[0-9A-Za-z-.]+))?(?:\\+(?<build>[0-9A-Za-z-.]+)?)?$/';\n\n        if (! preg_match($semverRegex, $version, $matches, PREG_UNMATCHED_AS_NULL)) {\n            throw new InvalidVersionException('Invalid semantic version string provided');\n        }\n\n        $version = sprintf('%s.%s.%s', $matches['major'], $matches['minor'] ?? 0, $matches['patch'] ?? 0);\n\n        if (! empty($matches['pre_release'])) {\n            $version .= '-' . $matches['pre_release'];\n        }\n\n        if (! empty($matches['build'])) {\n            $version .= '+' . $matches['build'];\n        }\n\n        return new static($version);\n    }\n\n    /** Serialize version to JSON. */\n    public function jsonSerialize(): mixed\n    {\n        return (string) $this;\n    }\n\n    /**\n     * Set (override) the entire version value.\n     *\n     * @param string $version Version string\n     *\n     * @throws \\PHLAK\\SemVer\\Exceptions\\InvalidVersionException\n     *\n     * @return static This Version object\n     */\n    public function setVersion(string $version): static\n    {\n        $semverRegex = '/^v?(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)(?:-(?<pre_release>[0-9A-Za-z-.]+))?(?:\\+(?<build>[0-9A-Za-z-.]+)?)?$/';\n\n        if (! preg_match($semverRegex, $version, $matches, PREG_UNMATCHED_AS_NULL)) {\n            throw new InvalidVersionException('Invalid semantic version string provided');\n        }\n\n        $this->major = (int) $matches['major'];\n        $this->minor = (int) $matches['minor'];\n        $this->patch = (int) $matches['patch'];\n        $this->preRelease = $matches['pre_release'] ?? null;\n        $this->build = $matches['build'] ?? null;\n\n        return $this;\n    }\n\n    /**\n     * Set the major version to a custom value.\n     *\n     * @param int $value Positive integer value\n     *\n     * @return static This Version object\n     */\n    public function setMajor(int $value): static\n    {\n        $this->major = $value;\n        $this->setMinor(0);\n\n        return $this;\n    }\n\n    /**\n     * Set the minor version to a custom value.\n     *\n     * @param int $value Positive integer value\n     *\n     * @return static This Version object\n     */\n    public function setMinor(int $value): static\n    {\n        $this->minor = $value;\n        $this->setPatch(0);\n\n        return $this;\n    }\n\n    /**\n     * Set the patch version to a custom value.\n     *\n     * @param int $value Positive integer value\n     *\n     * @return static This Version object\n     */\n    public function setPatch(int $value): static\n    {\n        $this->patch = $value;\n        $this->setPreRelease(null);\n        $this->setBuild(null);\n\n        return $this;\n    }\n\n    /**\n     * Set the pre-release string to a custom value.\n     *\n     * @param string|null $value A new pre-release value\n     *\n     * @return static This Version object\n     */\n    public function setPreRelease($value): static\n    {\n        $this->preRelease = $value;\n\n        return $this;\n    }\n\n    /**\n     * Set the build string to a custom value.\n     *\n     * @param string|null $value A new build value\n     *\n     * @return static This Version object\n     */\n    public function setBuild($value): static\n    {\n        $this->build = $value;\n\n        return $this;\n    }\n\n    /**\n     * Get the version string prefixed with a custom string.\n     *\n     * @param string $prefix String to prepend to the version string\n     *                       (default: 'v')\n     *\n     * @return string Prefixed version string\n     */\n    public function prefix(string $prefix = 'v'): string\n    {\n        return $prefix . (string) $this;\n    }\n\n    /**\n     * Determine if the version is a pre-release.\n     *\n     * @return bool Returns true if the version is a pre-release, false otherwise\n     */\n    public function isPreRelease(): bool\n    {\n        return ! empty($this->preRelease);\n    }\n\n    /**\n     * Determine if the version has a build string.\n     *\n     * @return bool Returns true if the version has a build string, false otherwise\n     */\n    public function hasBuild(): bool\n    {\n        return ! empty($this->build);\n    }\n}\n"
  },
  {
    "path": "tests/VersionTest.php",
    "content": "<?php\n\nnamespace PHLAK\\SemVer\\Tests;\n\nuse JsonSerializable;\nuse PHLAK\\SemVer;\nuse PHLAK\\SemVer\\Enums\\Compare;\nuse PHLAK\\SemVer\\Exceptions\\InvalidVersionException;\nuse PHLAK\\SemVer\\Traits\\Comparable;\nuse PHLAK\\SemVer\\Traits\\Incrementable;\nuse PHLAK\\SemVer\\Version;\nuse PHPUnit\\Framework\\Attributes\\CoversClass;\nuse PHPUnit\\Framework\\Attributes\\CoversTrait;\nuse PHPUnit\\Framework\\Attributes\\Test;\nuse Yoast\\PHPUnitPolyfills\\TestCases\\TestCase;\n\n#[CoversClass(Version::class)]\n#[CoversTrait(Comparable::class)]\n#[CoversTrait(Incrementable::class)]\nclass VersionTest extends TestCase\n{\n    /** @return array<array<int, mixed>> */\n    public static function pre_release_comparison_provider(): array\n    {\n        return [\n            ['v1.3.37', 'v1.3.37-alpha'],\n            ['v1.3.37', 'v1.3.37-alpha.5+007'],\n            ['v1.3.0', 'v1.3.0-beta'],\n            ['v1.0.0', 'v1.0.0-rc1'],\n            // Test cases from http://semver.org\n            ['1.0.0', '1.0.0-rc.1'],\n            ['1.0.0-rc.1', '1.0.0-beta.11'],\n            ['1.0.0-beta.11', '1.0.0-beta.2'],\n            ['1.0.0-beta.2', '1.0.0-beta'],\n            ['1.0.0-beta', '1.0.0-alpha.beta'],\n            ['1.0.0-alpha.beta', '1.0.0-alpha.1'],\n            ['1.0.0-alpha.1', '1.0.0-alpha'],\n        ];\n    }\n\n    #[Test]\n    public function it_can_be_initialized(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n\n        $this->assertInstanceOf(SemVer\\Version::class, $version);\n    }\n\n    #[Test]\n    public function it_can_be_initialized_with_the_helper_function(): void\n    {\n        $version = semver('v1.3.37');\n\n        $this->assertInstanceOf(SemVer\\Version::class, $version);\n    }\n\n    #[Test]\n    public function it_throws_a_runtime_exception_for_an_invalid_version(): void\n    {\n        $this->expectException(InvalidVersionException::class);\n\n        new SemVer\\Version('not.a.version');\n    }\n\n    #[Test]\n    public function it_can_parse_an_incomplete_version_string(): void\n    {\n        $this->assertEquals('1.0.0', (string) SemVer\\Version::parse('v1'));\n        $this->assertEquals('1.3.0', (string) SemVer\\Version::parse('v1.3'));\n        $this->assertEquals('1.3.37', (string) SemVer\\Version::parse('v1.3.37'));\n        $this->assertEquals('1.0.0-alpha.5', (string) SemVer\\Version::parse('v1-alpha.5'));\n        $this->assertEquals('1.3.0-alpha.5', (string) SemVer\\Version::parse('v1.3-alpha.5'));\n        $this->assertEquals('1.3.37-alpha.5', (string) SemVer\\Version::parse('v1.3.37-alpha.5'));\n        $this->assertEquals('1.0.0+007', (string) SemVer\\Version::parse('v1+007'));\n        $this->assertEquals('1.3.0+007', (string) SemVer\\Version::parse('v1.3+007'));\n        $this->assertEquals('1.3.37+007', (string) SemVer\\Version::parse('v1.3.37+007'));\n    }\n\n    #[Test]\n    public function it_throws_a_runtime_exception_when_parsing_an_invalid_version(): void\n    {\n        $this->expectException(InvalidVersionException::class);\n\n        SemVer\\Version::parse('not.a.version');\n    }\n\n    #[Test]\n    public function it_can_set_and_retrieve_a_version(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n        $version->setVersion('v2.4.48');\n\n        $this->assertEquals('2.4.48', (string) $version);\n    }\n\n    #[Test]\n    public function it_can_return_a_prefixed_version_string(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n\n        $this->assertEquals('v1.3.37', $version->prefix());\n        $this->assertEquals('x1.3.37', $version->prefix('x'));\n    }\n\n    #[Test]\n    public function it_can_be_cast_to_a_string(): void\n    {\n        $this->assertEquals('1.3.37', (string) new SemVer\\Version('v1.3.37'));\n        $this->assertEquals('1.3.37-alpha.5', (string) new SemVer\\Version('v1.3.37-alpha.5'));\n        $this->assertEquals('1.3.37+007', (string) new SemVer\\Version('v1.3.37+007'));\n        $this->assertEquals('1.3.37-alpha.5+007', (string) new SemVer\\Version('v1.3.37-alpha.5+007'));\n    }\n\n    #[Test]\n    public function it_can_get_individual_properties(): void\n    {\n        $version = new SemVer\\Version('v1.3.37-alpha.5+007');\n\n        $this->assertEquals(1, $version->major);\n        $this->assertEquals(3, $version->minor);\n        $this->assertEquals(37, $version->patch);\n        $this->assertEquals('alpha.5', $version->preRelease);\n        $this->assertEquals('007', $version->build);\n    }\n\n    #[Test]\n    public function it_can_increment_major(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n        $version->incrementMajor();\n\n        $this->assertEquals('2.0.0', (string) $version);\n    }\n\n    #[Test]\n    public function it_can_set_major(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n        $version->setMajor(7);\n\n        $this->assertEquals('7.0.0', (string) $version);\n    }\n\n    #[Test]\n    public function it_can_increment_minor(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n        $version->incrementMinor();\n\n        $this->assertEquals('1.4.0', (string) $version);\n    }\n\n    #[Test]\n    public function it_can_set_minor(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n        $version->setMinor(5);\n\n        $this->assertEquals('1.5.0', (string) $version);\n    }\n\n    #[Test]\n    public function it_can_increment_patch(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n        $version->incrementPatch();\n\n        $this->assertEquals('1.3.38', (string) $version);\n    }\n\n    #[Test]\n    public function it_can_increment_prerelease(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n        $version->setPreRelease('alpha.5');\n        $version->incrementPreRelease();\n\n        $this->assertEquals('1.3.37-alpha.6', (string) $version);\n    }\n\n    #[Test]\n    public function it_can_increment_prerelease_without_number(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n        $version->setPreRelease('alpha');\n        $version->incrementPreRelease();\n\n        $this->assertEquals('1.3.37-alpha.1', (string) $version);\n    }\n\n    #[Test]\n    public function it_can_increment_prerelease_without_prefix_for_prerelease(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n        $version->setPreRelease('5');\n        $version->incrementPreRelease();\n\n        $this->assertEquals('1.3.37-6', (string) $version);\n    }\n\n    #[Test]\n    public function it_can_increment_prerelease_containing_multiple_dots(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n        $version->setPreRelease('alpha.a.b.5');\n        $version->incrementPreRelease();\n\n        $this->assertEquals('1.3.37-alpha.a.b.6', (string) $version);\n    }\n\n    #[Test]\n    public function it_can_increment_prerelease_containing_multiple_dots_and_without_number(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n        $version->setPreRelease('alpha.a.b');\n        $version->incrementPreRelease();\n\n        $this->assertEquals('1.3.37-alpha.a.b.1', (string) $version);\n    }\n\n    #[Test]\n    public function it_can_increment_prerelease_and_patch_when_prerelease_is_null(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n        $version->incrementPreRelease();\n\n        $this->assertEquals('1.3.38-1', (string) $version);\n    }\n\n    #[Test]\n    public function it_can_set_patch(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n        $version->setPatch(12);\n\n        $this->assertEquals('1.3.12', (string) $version);\n    }\n\n    #[Test]\n    public function it_can_set_pre_release(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n        $version->setPreRelease('alpha.5');\n\n        $this->assertEquals('1.3.37-alpha.5', (string) $version);\n    }\n\n    #[Test]\n    public function it_can_unset_pre_release(): void\n    {\n        $version = (new SemVer\\Version('v1.3.37-alpha.5'))->setPreRelease(null);\n\n        $this->assertNull($version->preRelease);\n    }\n\n    #[Test]\n    public function it_can_set_build(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n        $version->setBuild('007');\n\n        $this->assertEquals('1.3.37+007', (string) $version);\n    }\n\n    #[Test]\n    public function it_can_unset_build(): void\n    {\n        $version = (new SemVer\\Version('v1.3.37+007'))->setBuild(null);\n\n        $this->assertNull($version->build);\n    }\n\n    #[Test]\n    public function it_can_be_greater_than_another_semver_object(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n\n        $this->assertTrue($version->gt(new SemVer\\Version('v0.5.0')));\n        $this->assertTrue($version->gt(new SemVer\\Version('v1.3.36')));\n        $this->assertFalse($version->gt(new SemVer\\Version('v1.3.38')));\n        $this->assertFalse($version->gt(new SemVer\\Version('v1.3.37')));\n    }\n\n    #[Test]\n    public function it_can_be_less_than_another_semver_object(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n\n        $this->assertTrue($version->lt(new SemVer\\Version('v1.3.38')));\n        $this->assertTrue($version->lt(new SemVer\\Version('v1.4.0')));\n        $this->assertFalse($version->lt(new SemVer\\Version('v1.3.36')));\n        $this->assertFalse($version->lt(new SemVer\\Version('v1.3.37')));\n    }\n\n    #[Test]\n    public function it_can_be_equal_to_another_semver_object(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n\n        $this->assertTrue($version->eq(new SemVer\\Version('v1.3.37')));\n        $this->assertFalse($version->eq(new SemVer\\Version('v1.2.3')));\n    }\n\n    #[Test]\n    public function it_can_be_not_equal_to_another_semver_object(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n\n        $this->assertTrue($version->neq(new SemVer\\Version('v1.2.3')));\n        $this->assertFalse($version->neq(new SemVer\\Version('v1.3.37')));\n    }\n\n    #[Test]\n    public function it_can_be_greater_than_or_equal_to_another_semver_object(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n\n        $this->assertTrue($version->gte(new SemVer\\Version('v1.2.3')));\n        $this->assertTrue($version->gte(new SemVer\\Version('v1.3.37')));\n        $this->assertFalse($version->gte(new SemVer\\Version('v2.3.4')));\n    }\n\n    #[Test]\n    public function it_can_be_less_than_or_equal_to_another_semver_object(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n\n        $this->assertTrue($version->lte(new SemVer\\Version('v2.3.4')));\n        $this->assertTrue($version->lte(new SemVer\\Version('v1.3.37')));\n        $this->assertFalse($version->lte(new SemVer\\Version('v1.2.3')));\n    }\n\n    #[Test]\n    public function it_can_be_greater_than_another_semver_object_using_major(): void\n    {\n        $version = new SemVer\\Version('v2.3.37');\n\n        $this->assertTrue($version->gt(new SemVer\\Version('v0.5.0'), Compare::MAJOR));\n        $this->assertTrue($version->gt(new SemVer\\Version('v1.3.38'), Compare::MAJOR));\n        $this->assertFalse($version->gt(new SemVer\\Version('v2.3.36'), Compare::MAJOR));\n        $this->assertFalse($version->gt(new SemVer\\Version('v3.3.36'), Compare::MAJOR));\n    }\n\n    #[Test]\n    public function it_can_be_less_than_another_semver_object_using_major(): void\n    {\n        $version = new SemVer\\Version('v2.3.37');\n\n        $this->assertTrue($version->lt(new SemVer\\Version('v3.3.38'), Compare::MAJOR));\n        $this->assertFalse($version->lt(new SemVer\\Version('v2.3.36'), Compare::MAJOR));\n        $this->assertFalse($version->lt(new SemVer\\Version('v1.3.37'), Compare::MAJOR));\n    }\n\n    #[Test]\n    public function it_can_be_equal_to_another_semver_object_using_major(): void\n    {\n        $version = new SemVer\\Version('v1.3.37-alpha.5');\n\n        $this->assertTrue($version->eq(new SemVer\\Version('v1.3.37-alpha.4'), Compare::MAJOR));\n        $this->assertTrue($version->eq(new SemVer\\Version('v1.2.3'), Compare::MAJOR));\n    }\n\n    #[Test]\n    public function it_can_be_not_equal_to_another_semver_object_using_major(): void\n    {\n        $version = new SemVer\\Version('v2.3.37');\n\n        $this->assertTrue($version->neq(new SemVer\\Version('v3.2.3'), Compare::MAJOR));\n        $this->assertFalse($version->neq(new SemVer\\Version('v2.2.3'), Compare::MAJOR));\n        $this->assertTrue($version->neq(new SemVer\\Version('v1.3.37'), Compare::MAJOR));\n    }\n\n    #[Test]\n    public function it_can_be_greater_than_or_equal_to_another_semver_object_using_major(): void\n    {\n        $version = new SemVer\\Version('v3.3.37-alpha.5');\n\n        $this->assertTrue($version->gte(new SemVer\\Version('v1.3.4'), Compare::MAJOR));\n        $this->assertTrue($version->gte(new SemVer\\Version('v2.3.37-alpha.6'), Compare::MAJOR));\n        $this->assertTrue($version->gte(new SemVer\\Version('v3.2.3'), Compare::MAJOR));\n        $this->assertFalse($version->gte(new SemVer\\Version('v4.2.3'), Compare::MAJOR));\n    }\n\n    #[Test]\n    public function it_can_be_less_than_or_equal_to_another_semver_object_using_major(): void\n    {\n        $version = new SemVer\\Version('v2.3.37-alpha.5');\n\n        $this->assertTrue($version->lte(new SemVer\\Version('v3.2.3'), Compare::MAJOR));\n        $this->assertTrue($version->lte(new SemVer\\Version('v2.3.37-alpha.4'), Compare::MAJOR));\n        $this->assertFalse($version->lte(new SemVer\\Version('v1.3.4'), Compare::MAJOR));\n    }\n\n    #[Test]\n    public function it_can_be_greater_than_another_semver_object_using_minor(): void\n    {\n        $version = new SemVer\\Version('v1.3.37-alpha.5');\n\n        $this->assertTrue($version->gt(new SemVer\\Version('v0.5.0'), Compare::MINOR));\n        $this->assertTrue($version->gt(new SemVer\\Version('v1.2.40'), Compare::MINOR));\n        $this->assertFalse($version->gt(new SemVer\\Version('v1.3.38-alpha.5'), Compare::MINOR));\n        $this->assertFalse($version->gt(new SemVer\\Version('v1.4.0'), Compare::MINOR));\n    }\n\n    #[Test]\n    public function it_can_be_less_than_another_semver_object_using_minor(): void\n    {\n        $version = new SemVer\\Version('v1.3.37-alpha.5');\n\n        $this->assertTrue($version->lt(new SemVer\\Version('v1.4.38'), Compare::MINOR));\n        $this->assertTrue($version->lt(new SemVer\\Version('v1.4.0'), Compare::MINOR));\n        $this->assertFalse($version->lt(new SemVer\\Version('v1.2.36'), Compare::MINOR));\n        $this->assertFalse($version->lt(new SemVer\\Version('v1.3.37-alpha.6'), Compare::MINOR));\n    }\n\n    #[Test]\n    public function it_can_be_equal_to_another_semver_object_using_minor(): void\n    {\n        $version = new SemVer\\Version('v1.3.37-alpha.5');\n\n        $this->assertFalse($version->eq(new SemVer\\Version('v1.4.3'), Compare::MINOR));\n        $this->assertTrue($version->eq(new SemVer\\Version('v1.3.37-alpha.4'), Compare::MINOR));\n        $this->assertFalse($version->eq(new SemVer\\Version('v1.2.3'), Compare::MINOR));\n    }\n\n    #[Test]\n    public function it_can_be_not_equal_to_another_semver_object_using_minor(): void\n    {\n        $version = new SemVer\\Version('v1.3.37-alpha.5');\n\n        $this->assertTrue($version->neq(new SemVer\\Version('v1.2.37-alpha.5'), Compare::MINOR));\n        $this->assertFalse($version->neq(new SemVer\\Version('v1.3.37-alpha.6'), Compare::MINOR));\n        $this->assertTrue($version->neq(new SemVer\\Version('v1.4.37'), Compare::MINOR));\n    }\n\n    #[Test]\n    public function it_can_be_greater_than_or_equal_to_another_semver_object_using_minor(): void\n    {\n        $version = new SemVer\\Version('v1.3.37-alpha.5');\n\n        $this->assertTrue($version->gte(new SemVer\\Version('v1.3.38-alpha.4'), Compare::MINOR));\n        $this->assertTrue($version->gte(new SemVer\\Version('v1.2.3'), Compare::MINOR));\n        $this->assertFalse($version->gte(new SemVer\\Version('v1.4.4'), Compare::MINOR));\n        $this->assertFalse($version->gte(new SemVer\\Version('v2.3.4'), Compare::MINOR));\n    }\n\n    #[Test]\n    public function it_can_be_less_than_or_equal_to_another_semver_object_using_minor(): void\n    {\n        $version = new SemVer\\Version('v1.3.37-alpha.5');\n\n        $this->assertTrue($version->lte(new SemVer\\Version('v2.3.4'), Compare::MINOR));\n        $this->assertTrue($version->lte(new SemVer\\Version('v1.3.37'), Compare::MINOR));\n        $this->assertTrue($version->lte(new SemVer\\Version('v1.3.36'), Compare::MINOR));\n        $this->assertFalse($version->lte(new SemVer\\Version('v1.2.37-alpha.5'), Compare::MINOR));\n    }\n\n    #[Test]\n    public function it_can_be_greater_than_another_semver_object_using_patch(): void\n    {\n        $version = new SemVer\\Version('v1.3.37-alpha.5');\n\n        $this->assertTrue($version->gt(new SemVer\\Version('v0.5.0'), Compare::PATCH));\n        $this->assertTrue($version->gt(new SemVer\\Version('v1.3.36'), Compare::PATCH));\n        $this->assertFalse($version->gt(new SemVer\\Version('v1.3.38'), Compare::PATCH));\n        $this->assertFalse($version->gt(new SemVer\\Version('v1.3.37-alpha.4'), Compare::PATCH));\n    }\n\n    #[Test]\n    public function it_can_be_less_than_another_semver_object_using_patch(): void\n    {\n        $version = new SemVer\\Version('v1.3.37-alpha.5');\n\n        $this->assertTrue($version->lt(new SemVer\\Version('v1.3.38'), Compare::PATCH));\n        $this->assertTrue($version->lt(new SemVer\\Version('v1.4.0'), Compare::PATCH));\n        $this->assertFalse($version->lt(new SemVer\\Version('v1.3.36'), Compare::PATCH));\n        $this->assertFalse($version->lt(new SemVer\\Version('v1.3.37-alpha.6'), Compare::PATCH));\n    }\n\n    #[Test]\n    public function it_can_be_equal_to_another_semver_object_using_patch(): void\n    {\n        $version = new SemVer\\Version('v1.3.37-alpha.5');\n\n        $this->assertTrue($version->eq(new SemVer\\Version('v1.3.37-alpha.6'), Compare::PATCH));\n        $this->assertFalse($version->eq(new SemVer\\Version('v1.2.3'), Compare::PATCH));\n    }\n\n    #[Test]\n    public function it_can_be_not_equal_to_another_semver_object_using_patch(): void\n    {\n        $version = new SemVer\\Version('v1.3.37-alpha.5');\n\n        $this->assertTrue($version->neq(new SemVer\\Version('v1.2.3'), Compare::PATCH));\n        $this->assertFalse($version->neq(new SemVer\\Version('v1.3.37-alpha.6'), Compare::PATCH));\n    }\n\n    #[Test]\n    public function it_can_be_greater_than_or_equal_to_another_semver_object_using_patch(): void\n    {\n        $version = new SemVer\\Version('v1.3.37-alpha.5');\n\n        $this->assertTrue($version->gte(new SemVer\\Version('v1.2.3'), Compare::PATCH));\n        $this->assertTrue($version->gte(new SemVer\\Version('v1.3.37-alpha.6'), Compare::PATCH));\n        $this->assertFalse($version->gte(new SemVer\\Version('v2.3.4'), Compare::PATCH));\n    }\n\n    #[Test]\n    public function it_can_be_less_than_or_equal_to_another_semver_object_using_patch(): void\n    {\n        $version = new SemVer\\Version('v1.3.37-alpha.5');\n\n        $this->assertTrue($version->lte(new SemVer\\Version('v2.3.4'), Compare::PATCH));\n        $this->assertTrue($version->lte(new SemVer\\Version('v1.3.37-alpha.4'), Compare::PATCH));\n        $this->assertFalse($version->lte(new SemVer\\Version('v1.2.3'), Compare::PATCH));\n    }\n\n    #[Test]\n    public function setting_the_major_version_resets_appropriate_properties(): void\n    {\n        $version = new SemVer\\Version('v1.3.37-alpha.5+007');\n        $version->setMajor(2);\n\n        $this->assertEquals(2, $version->major);\n        $this->assertEquals(0, $version->minor);\n        $this->assertEquals(0, $version->patch);\n        $this->assertNull($version->preRelease);\n        $this->assertNull($version->build);\n    }\n\n    #[Test]\n    public function setting_the_minor_version_resets_appropriate_properties(): void\n    {\n        $version = new SemVer\\Version('v1.3.37-alpha.5+007');\n        $version->setMinor(4);\n\n        $this->assertEquals(1, $version->major);\n        $this->assertEquals(4, $version->minor);\n        $this->assertEquals(0, $version->patch);\n        $this->assertNull($version->preRelease);\n        $this->assertNull($version->build);\n    }\n\n    #[Test]\n    public function setting_the_patch_version_resets_appropriate_properties(): void\n    {\n        $version = new SemVer\\Version('v1.3.37-alpha.5+007');\n        $version->setPatch(38);\n\n        $this->assertEquals(1, $version->major);\n        $this->assertEquals(3, $version->minor);\n        $this->assertEquals(38, $version->patch);\n        $this->assertNull($version->preRelease);\n        $this->assertNull($version->build);\n    }\n\n    #[Test]\n    public function it_compares_pre_release_tags(): void\n    {\n        $alpha = new SemVer\\Version('v1.3.37-alpha');\n        $beta = new SemVer\\Version('v1.3.37-beta');\n\n        $this->assertTrue($alpha->lt($beta));\n        $this->assertFalse($alpha->gt($beta));\n        $this->assertTrue($alpha->lte($beta));\n        $this->assertFalse($alpha->gte($beta));\n        $this->assertFalse($alpha->eq($beta));\n    }\n\n    #[Test]\n    public function it_ignores_the_build_version_when_comparing_versions(): void\n    {\n        $oldBuild = new SemVer\\Version('v1.3.37-alpha.5+006');\n        $newBuild = new SemVer\\Version('v1.3.37-alpha.5+007');\n\n        $this->assertTrue($oldBuild->eq($newBuild));\n        $this->assertFalse($oldBuild->neq($newBuild));\n        $this->assertFalse($oldBuild->gt($newBuild));\n        $this->assertFalse($oldBuild->lt($newBuild));\n        $this->assertTrue($oldBuild->gte($newBuild));\n        $this->assertTrue($oldBuild->lte($newBuild));\n\n        $this->assertTrue((new SemVer\\Version('v1.3.37'))->eq(new SemVer\\Version('v1.3.37+007')));\n    }\n\n    #[Test]\n    #[\\PHPUnit\\Framework\\Attributes\\DataProvider('pre_release_comparison_provider')]\n    public function it_compares_pre_release_tags_vs_release(string $release, string $prerelease): void\n    {\n        $release = new SemVer\\Version($release);\n        $prerelease = new SemVer\\Version($prerelease);\n\n        $this->assertFalse($release->eq($prerelease));\n        $this->assertFalse($release->lt($prerelease));\n        $this->assertFalse($release->lte($prerelease));\n        $this->assertTrue($release->gt($prerelease));\n        $this->assertTrue($release->gte($prerelease));\n\n        $this->assertFalse($prerelease->gt($release));\n        $this->assertFalse($prerelease->eq($release));\n        $this->assertFalse($prerelease->gte($release));\n        $this->assertTrue($prerelease->lt($release));\n        $this->assertTrue($prerelease->lte($release));\n    }\n\n    #[Test]\n    public function it_can_compare_two_versions(): void\n    {\n        $version1 = new SemVer\\Version('v1.3.37');\n        $version2 = new SemVer\\Version('v3.2.1');\n\n        // Major Comparisons\n        $this->assertEquals(-1, SemVer\\Version::compare(new SemVer\\Version('v1.2.3'), new SemVer\\Version('v3.2.1')));\n        $this->assertEquals(0, SemVer\\Version::compare(new SemVer\\Version('v1.2.3'), new SemVer\\Version('v1.2.3')));\n        $this->assertEquals(1, SemVer\\Version::compare(new SemVer\\Version('v3.2.1'), new SemVer\\Version('v1.2.3')));\n\n        // Minor Comparisons\n        $this->assertEquals(-1, SemVer\\Version::compare(new SemVer\\Version('v0.1.2'), new SemVer\\Version('v0.2.1')));\n        $this->assertEquals(0, SemVer\\Version::compare(new SemVer\\Version('v0.1.2'), new SemVer\\Version('v0.1.2')));\n        $this->assertEquals(1, SemVer\\Version::compare(new SemVer\\Version('v0.2.1'), new SemVer\\Version('v0.1.2')));\n\n        // Patch Comparisons\n        $this->assertEquals(-1, SemVer\\Version::compare(new SemVer\\Version('v1.0.1'), new SemVer\\Version('v1.0.2')));\n        $this->assertEquals(0, SemVer\\Version::compare(new SemVer\\Version('v1.0.0'), new SemVer\\Version('v1.0.0')));\n        $this->assertEquals(1, SemVer\\Version::compare(new SemVer\\Version('v1.0.2'), new SemVer\\Version('v1.0.1')));\n    }\n\n    #[Test]\n    public function it_can_be_serialized_to_json(): void\n    {\n        $version = new SemVer\\Version('v1.3.37');\n\n        $this->assertInstanceOf(JsonSerializable::class, $version);\n        $this->assertEquals('1.3.37', $version->jsonSerialize());\n    }\n\n    #[Test]\n    public function it_can_determine_if_it_is_a_pre_release(): void\n    {\n        $version = new SemVer\\Version('v1.3.37-alpha.5+007');\n\n        $this->assertTrue($version->isPreRelease());\n    }\n\n    #[Test]\n    public function can_determine_if_it_is_not_a_pre_release(): void\n    {\n        $version = new SemVer\\Version('v1.3.37+007');\n\n        $this->assertFalse($version->isPreRelease());\n    }\n\n    #[Test]\n    public function can_determine_if_it_has_a_build_string(): void\n    {\n        $version = new SemVer\\Version('v1.3.37-alpha.5+007');\n\n        $this->assertTrue($version->hasBuild());\n    }\n\n    #[Test]\n    public function it_can_determine_if_it_has_a_build_string(): void\n    {\n        $version = new SemVer\\Version('v1.3.37-alpha');\n\n        $this->assertFalse($version->hasBuild());\n    }\n}\n"
  }
]