horde / version
Handle Semantic Version 2.0.0 format and similar semantic version formats
Requires
- php: ^8.2
Requires (Dev)
This package is auto-updated.
Last update: 2026-03-04 20:26:34 UTC
README
Create, parse, increment, compare, and constrain strict SemVer V2 version tags and other common semantic versioning formats found in the wild.
Features
- ✅ Parse strict SemVer 2.0 and relaxed variants
- ✅ Compare versions with full SemVer 2.0 precedence rules
- ✅ Generate next versions (patch/minor/major bumps)
- ✅ Detect and manage stability levels (dev/alpha/beta/rc/stable)
- ✅ Version constraints (Composer-compatible syntax)
- ✅ Collection utilities (sort, filter, find latest)
- ✅ Comprehensive test coverage (193 tests, 592 assertions)
Installation
composer require horde/version
Quick Start
Parsing Versions
use Horde\Version\SemVerV2Version; use Horde\Version\RelaxedSemanticVersion; // Strict SemVer 2.0 $version = new SemVerV2Version('1.0.0'); $version = new SemVerV2Version('1.0.0-alpha.1+build.123'); // Relaxed format (prefixes, no hyphen, missing patch) $version = new RelaxedSemanticVersion('v1.0.0'); $version = new RelaxedSemanticVersion('1.0'); // → 1.0.0 $version = new RelaxedSemanticVersion('1.0.0alpha1'); // → 1.0.0-alpha1 // Access components echo $version->major; // 1 echo $version->minor; // 0 echo $version->patch; // 0 echo $version->preRelease; // 'alpha1' echo $version->buildInfo; // ''
Comparing Versions
use Horde\Version\SemVerV2Comparison; $comparison = new SemVerV2Comparison(); $result = $comparison->compare( new SemVerV2Version('1.0.0'), new SemVerV2Version('2.0.0') ); // -1 (first < second) // Or use helper methods $v1 = new SemVerV2Version('1.0.0'); $v2 = new SemVerV2Version('2.0.0'); $v1->isLessThan($v2); // true $v2->isGreaterThan($v1); // true $v1->equals($v1); // true
Version Constraints
use Horde\Version\ConstraintParser; $parser = new ConstraintParser(); // Exact match $constraint = $parser->parse('1.0.0'); $constraint->isSatisfiedBy($version); // true if version == 1.0.0 // Comparison operators $constraint = $parser->parse('>=1.0.0'); $constraint = $parser->parse('<2.0.0'); $constraint = $parser->parse('!=1.5.0'); // Caret (^) - Allow changes that don't modify left-most non-zero $constraint = $parser->parse('^1.2.3'); // >=1.2.3 <2.0.0 $constraint = $parser->parse('^0.2.3'); // >=0.2.3 <0.3.0 $constraint = $parser->parse('^0.0.3'); // =0.0.3 // Tilde (~) - Allow patch-level changes $constraint = $parser->parse('~1.2.3'); // >=1.2.3 <1.3.0 $constraint = $parser->parse('~1.2'); // >=1.2.0 <2.0.0 // Wildcard $constraint = $parser->parse('1.0.*'); // >=1.0.0 <1.1.0 $constraint = $parser->parse('1.*'); // >=1.0.0 <2.0.0 // Range $constraint = $parser->parse('1.0.0 - 2.0.0'); // AND (space-separated) $constraint = $parser->parse('>=1.0.0 <2.0.0'); // OR (||) $constraint = $parser->parse('^1.0 || ^2.0'); // Check if version satisfies constraint if ($constraint->isSatisfiedBy($version)) { echo "Version is compatible!"; }
Collection Utilities
use Horde\Version\VersionCollection; $versions = ['2.0.0', '1.0.0', '1.5.0', '2.0.0-alpha']; // Sort versions $sorted = VersionCollection::sort($versions); // ['1.0.0', '1.5.0', '2.0.0-alpha', '2.0.0'] $sortedDesc = VersionCollection::sort($versions, descending: true); // ['2.0.0', '2.0.0-alpha', '1.5.0', '1.0.0'] // Find latest version $latest = VersionCollection::latest($versions); // '2.0.0' $latestStable = VersionCollection::latest($versions, 'stable'); // '2.0.0' (excludes alpha) // Filter by constraint $compatible = VersionCollection::filter($versions, '^1.0'); // ['1.0.0', '1.5.0'] // Filter by stability $stable = VersionCollection::filter($versions, 'stable'); // ['1.0.0', '1.5.0', '2.0.0'] // Filter by callback $filtered = VersionCollection::filter($versions, fn($v) => $v->major >= 2); // ['2.0.0', '2.0.0-alpha'] // Group by major version $grouped = VersionCollection::group($versions, 'major'); // ['1.x' => ['1.0.0', '1.5.0'], '2.x' => ['2.0.0', '2.0.0-alpha']]
Generating Next Versions
use Horde\Version\NextVersion; $current = new NextVersion('1.0.0'); // Bump patch version $next = $current('patch', 'stable'); // '1.0.1' // Bump minor version $next = $current('minor', 'stable'); // '1.1.0' // Bump major version $next = $current('major', 'stable'); // '2.0.0' // Change stability $next = $current('minor', 'alpha'); // '1.1.0-alpha.1' $next = $current('patch', 'beta'); // '1.0.1-beta.1' // Keep same stability $next = $current('patch', 'unchanged'); // '1.0.1' // Increment prerelease revision $current = new NextVersion('1.0.0-alpha.1'); $next = $current('patch', 'unchanged'); // '1.0.0-alpha.2'
Stability Detection
use Horde\Version\Stability; $version = new SemVerV2Version('1.0.0-beta.2'); $stability = new Stability($version); echo $stability->stability; // 'beta' echo $stability->stabilityRank; // 2 (dev=0, alpha=1, beta=2, rc=3, stable=1000) $stability->isStable(); // false $stability->isBeta(); // true $stability->getStabilityRevision(); // 2
Motivation
SemVer V2 is a useful attempt to standardize semantic versioning. However, semantic versioning itself predates it, and many variant formats exist in the wild.
Common SemVer Deviations
- Stability order: Treating "dev" as inferior to "alpha" (SemVer says alpha < beta < dev < rc)
- No hyphen: Prerelease without hyphen (
1.0.0alpha1instead of1.0.0-alpha.1) - Missing patch: Version strings with only major.minor (
1.0instead of1.0.0) - Prefixes:
vprefix before version (v1.0.0) - Prerelease format:
alpha4more common thanalpha.4
This library handles both strict SemVer 2.0 and these common variants.
Use Cases
Dependency Management
// Check if installed version satisfies requirement $parser = new ConstraintParser(); $required = $parser->parse('^1.2.0'); $installed = new SemVerV2Version('1.5.0'); if ($required->isSatisfiedBy($installed)) { echo "Dependency satisfied!"; }
Update Checker
// Find latest compatible version $availableVersions = ['1.0.0', '1.5.0', '2.0.0', '2.1.0']; $currentVersion = '1.3.0'; // Find latest in same major version $compatible = VersionCollection::filter($availableVersions, '^' . $currentVersion); $latestCompatible = VersionCollection::latest($compatible); // '1.5.0'
Release Management
// Find all stable versions in 1.x series $allVersions = ['1.0.0', '1.5.0', '2.0.0-alpha', '2.0.0']; $v1Stable = VersionCollection::filter($allVersions, function($v) { return $v->major === 1 && (new Stability($v))->isStable(); }); // ['1.0.0', '1.5.0']
API Documentation
Core Classes
SemVerV2Version- Strict SemVer 2.0.0 parserRelaxedSemanticVersion- Lenient parser for common variantsGenericVersion- Any version stringSemVerV2Comparison- Compare versions per SemVer rulesNextVersion- Generate next version with bumpsStability- Detect and classify stability levels
Constraint System
VersionConstraint- Interface for all constraintsConstraintParser- Parse constraint stringsExactConstraint- Exact version matchComparisonConstraint->,>=,<,<=,!=CaretConstraint-^1.2.3rangesTildeConstraint-~1.2.3rangesWildcardConstraint-1.0.*patternsCompositeConstraint- AND/OR logic
Utilities
VersionCollection- Sort, filter, find versionsVersionComparable- Helper methods trait
Requirements
- PHP 8.2 or higher
- No external dependencies
Testing
composer install vendor/bin/phpunit
Test coverage: 193 tests, 592 assertions
License
LGPL-2.1-only - See LICENSE file
Contributing
This library is part of the Horde project. Contributions welcome!
Origin
Originally extracted from the horde/components developer CLI tool, rewritten for general-purpose use.