grazulex / php-semver-sieve
A pure PHP package for checking if a version string is included in one or more version ranges. Supports SemVer 2.0.0 and multiple version dialects (Composer, npm, PyPI, etc.)
Fund package maintenance!
Grazulex
Buy Me A Coffee
paypal.me/strauven
Requires
- php: ^8.1
Requires (Dev)
- ergebnis/composer-normalize: ^2.42
- friendsofphp/php-cs-fixer: ^3.59
- infection/infection: ^0.28
- pestphp/pest: ^2.34
- phpstan/phpstan: ^1.10
- rector/rector: ^1.1
README

Universal semantic version range checking across all major package managers - powerful, fast, and dependency-free PHP package.
A comprehensive library for evaluating version constraints from Composer, npm, PyPI, RubyGems, Maven, NuGet, Go modules, and more with a single unified API.
๐ Overview
A powerful, universal PHP package for checking if version strings match version ranges across all major package managers.
๐ฏ One API to rule them all - Handle version constraints from Composer, npm, PyPI, RubyGems, Maven, NuGet, Go modules, and more!
โจ Features
- ๐๏ธ Framework-agnostic - No Laravel or Composer dependencies
- ๐ Zero runtime dependencies - Pure PHP logic with performance focus
- ๐ SemVer 2.0.0 compatible - Follows semantic versioning specification
- ๐ 8 Package Manager Dialects - Universal support for all major ecosystems:
- ๐ PHP (Composer) -
^1.0
,~1.2
,>=1.0 <2.0
- ๐ฆ JavaScript (npm) - Special tags, workspace protocol, X-ranges
- ๐ Python (PyPI) - PEP 440 with dev/post releases, epochs
- ๐ Ruby (RubyGems) - Pessimistic constraints (
~>
) - โ Java (Maven) - SNAPSHOT versions, range notation
- ๐ท C#/.NET (NuGet) - 4-segment versions, interval notation
- ๐น Go (Go modules) -
v
prefix, pseudo-versions, incompatible versions - ๐ฏ Generic SemVer - Standards-compliant base implementation
- ๐ PHP (Composer) -
- ๐๏ธ SOLID Architecture - Clean, extensible, maintainable codebase
- ๐งช Fully tested - 42 tests, 126 assertions, mutation testing
- ๐ Static analysis - PHPStan level 6 compliant
- โก PHP 8.2+ - Modern features (readonly classes, enums, match expressions)
๐ Project Stats
- 24 source files with 3,600+ lines of code
- 42 passing tests with 126 assertions
- 8 complete dialects covering all major package managers
- Zero runtime dependencies for maximum compatibility
- 100% type coverage with PHPStan
Installation
composer require grazulex/php-semver-sieve
๐ Quick Start
use Grazulex\SemverSieve\Sieve; use Grazulex\SemverSieve\Dialects\GenericSemverDialect; // Create a sieve instance $sieve = new Sieve(new GenericSemverDialect()); // Simple boolean check $matches = $sieve->includes('1.2.3', ['>=1.0 <2.0', '^1.2.0']); // โ true // Detailed analysis with metadata $result = $sieve->match('2.0.0-beta.2', ['^2.0', '>=1.9 <2.0.0-rc.1']); // โ [ // 'matched' => true, // 'matched_ranges' => ['>=1.9 <2.0.0-rc.1'], // 'normalized_ranges' => ['>=2.0.0-0 <3.0.0-0', '>=1.9.0-0 <2.0.0-rc.1'] // ]
๐ฏ Universal Package Manager Support
๐ One API, All Ecosystems
// PHP/Composer $composer = new Sieve(new ComposerDialect()); $composer->includes('1.2.3', ['~1.2', '^1.0']); // โ true // JavaScript/npm $npm = new Sieve(new NpmDialect()); $npm->includes('1.2.3', ['latest', 'workspace:^1.0']); // โ true // Python/PyPI $pypi = new Sieve(new PypiDialect()); $pypi->includes('1.2.3', ['>=1.2,<2.0', '~=1.2']); // โ true // Ruby/RubyGems $rubygems = new Sieve(new RubyGemsDialect()); $rubygems->includes('1.2.3', ['~> 1.2', '>= 1.0']); // โ true // Java/Maven $maven = new Sieve(new MavenDialect()); $maven->includes('1.2.3', ['[1.0,2.0)', '1.2+']); // โ true // .NET/NuGet $nuget = new Sieve(new NugetDialect()); $nuget->includes('1.2.3.4', ['[1.0,2.0)', '1.2.*']); // โ true // Go/Go Modules $gomod = new Sieve(new GoModDialect()); $gomod->includes('v1.2.3', ['v1.2.3', 'v1.2+incompatible']); // โ true
๐ Dialect-Specific Features
๐ Composer Dialect (PHP)
use Grazulex\SemverSieve\Dialects\ComposerDialect; $sieve = new Sieve(new ComposerDialect()); // Composer-specific syntax $sieve->includes('1.2.3', [ '^1.0', // Caret: >=1.0.0 <2.0.0-0 '~1.2', // Tilde: >=1.2.0 <1.3.0-0 '>=1.0 <2.0', // Combined constraints '1.2.*' // Wildcard ]);
๐ฆ npm Dialect (JavaScript/Node.js)
use Grazulex\SemverSieve\Dialects\NpmDialect; $sieve = new Sieve(new NpmDialect()); // npm-specific features $sieve->includes('1.2.3', [ 'latest', // Special tags 'next', // Distribution tags 'workspace:*', // Workspace protocol 'workspace:^1.0', // Workspace with range '1.2.x', // X-ranges '1.x.x', // Multi-segment X-ranges ]); // Special npm tags supported: // latest, next, alpha, beta, rc, canary, experimental, dev, nightly
๐ PyPI Dialect (Python)
use Grazulex\SemverSieve\Dialects\PypiDialect; $sieve = new Sieve(new PypiDialect()); // PEP 440 compliant syntax $sieve->includes('1.2.3', [ '~=1.2', // Compatible release '===1.2.3', // Arbitrary equality '>=1.0,<2.0', // Comma-separated constraints '!=1.2.4', // Exclusion ]); // Supports: dev releases, post releases, epochs, local versions
๐ RubyGems Dialect (Ruby)
use Grazulex\SemverSieve\Dialects\RubyGemsDialect; $sieve = new Sieve(new RubyGemsDialect()); // Ruby-specific pessimistic constraints $sieve->includes('1.2.3', [ '~> 1.2', // Pessimistic: >= 1.2, < 1.3 '~> 1.2.0', // Pessimistic: >= 1.2.0, < 1.3.0 '>= 1.0', // Standard comparison ]);
โ Maven Dialect (Java)
use Grazulex\SemverSieve\Dialects\MavenDialect; $sieve = new Sieve(new MavenDialect()); // Maven version ranges and qualifiers $sieve->includes('1.2.3-SNAPSHOT', [ '[1.0,2.0)', // Range: >= 1.0, < 2.0 '[1.0,2.0]', // Range: >= 1.0, <= 2.0 '(1.0,2.0)', // Range: > 1.0, < 2.0 '1.0+', // Soft requirement: >= 1.0 ]); // Maven qualifiers ordering: // alpha < beta < milestone < rc < snapshot < release < sp
๐ท NuGet Dialect (.NET)
use Grazulex\SemverSieve\Dialects\NugetDialect; $sieve = new Sieve(new NugetDialect()); // NuGet 4-segment versions and interval notation $sieve->includes('1.2.3.4', [ '[1.0,2.0)', // Interval notation '1.2.*', // Floating versions '1.*', // Major floating '*', // Any version ]);
๐น Go Modules Dialect
use Grazulex\SemverSieve\Dialects\GoModDialect; $sieve = new Sieve(new GoModDialect()); // Go module versions (require 'v' prefix) $sieve->includes('v1.2.3', [ 'v1.2.3', // Exact version 'v2.0.0+incompatible', // Incompatible version ]); // Supports pseudo-versions: v0.0.0-20191109021931-daa7c04131f5
๐ฏ Generic SemVer Dialect
use Grazulex\SemverSieve\Dialects\GenericSemverDialect; $sieve = new Sieve(new GenericSemverDialect()); // Standards-compliant SemVer 2.0.0 $sieve->includes('1.2.3-alpha.1+build.1', [ '>=1.0.0', // Comparison operators '^1.2', // Caret ranges '~1.2.3', // Tilde ranges '1.2.3-alpha.1', // Prerelease versions ]);
๐ Complete Syntax Reference
Syntax | Example | Dialects | Description |
---|---|---|---|
Exact | 1.2.3 |
All | Exact version match |
Comparators | >=1.0 , <2.0 , !=1.5 |
All | Greater/less than, not equal |
Caret | ^1.2.3 |
Composer, npm, Generic | Compatible within major version |
Tilde | ~1.2.3 |
Composer, npm, Generic | Compatible within minor version |
Pessimistic | ~> 1.2 |
RubyGems | Ruby pessimistic constraint |
Wildcards | 1.2.* , 1.x , * |
Most | Wildcard matching |
X-Ranges | 1.2.x , 1.x.x |
npm | npm-style X-ranges |
Hyphen Ranges | 1.2 - 1.4 |
Generic, Composer | Inclusive range |
Interval | [1.0,2.0) , (1.0,2.0] |
Maven, NuGet | Mathematical intervals |
OR Logic | ^1.0 || ^2.0 |
Most | Multiple range options |
AND Logic | >=1.0 <2.0 |
Most | Combined constraints |
Special Tags | latest , next , beta |
npm | Distribution tags |
Workspace | workspace:* , workspace:^1.0 |
npm | Workspace protocol |
Floating | 1.2.* , 1.* |
NuGet | Floating versions |
Soft Req | 1.0+ |
Maven | Soft requirements |
Qualifiers | 1.0-SNAPSHOT , 1.0-RELEASE |
Maven | Maven qualifiers |
Compatible | ~=1.2 |
PyPI | PEP 440 compatible release |
Arbitrary | ===1.2.3 |
PyPI | Arbitrary equality |
v-prefix | v1.2.3 |
Go | Go module versions |
4-segment | 1.2.3.4 |
NuGet | .NET revision numbers |
๐ง Advanced Configuration
Configuration Options
use Grazulex\SemverSieve\Configuration\SieveConfiguration; $config = new SieveConfiguration( includePreReleases: true, // Include alpha, beta, rc versions strictSegments: false, // Treat "1.2" as "1.2.0" allowVPrefix: true, // Accept "v1.2.3" format caseInsensitive: true, // "RC" vs "rc" allowLeadingZeros: false, // "01.02.03" format maxVersionLength: 256 // Security limit ); $sieve = new Sieve(new GenericSemverDialect(), $config);
Preset Configurations
// Default: Balanced settings for most use cases $config = SieveConfiguration::default(); // Strict: Enforce strict SemVer compliance $config = SieveConfiguration::strict(); // - strictSegments: true // - allowVPrefix: false // - allowLeadingZeros: false // Lenient: Accept more version formats $config = SieveConfiguration::lenient(); // - includePreReleases: true // - allowLeadingZeros: true
๐จ Custom Dialects & Extensions
Creating Custom Dialects
use Grazulex\SemverSieve\Contracts\DialectInterface; use Grazulex\SemverSieve\ValueObjects\ParsedVersion; use Grazulex\SemverSieve\ValueObjects\ParsedRange; final class CustomDialect implements DialectInterface { public function parseVersion(string $version, array $options): ParsedVersion { // Custom version parsing logic // Handle your specific version format } public function parseRange(string $range, array $options): ParsedRange { // Custom range parsing logic // Handle your specific constraint syntax } public function getName(): string { return 'custom'; } public function getSupportedOperators(): array { return ['=', '>', '<', '>=', '<=', '~', '^']; } } $sieve = new Sieve(new CustomDialect());
Real-World Custom Dialect Example
// Example: Custom calendar versioning dialect final class CalVerDialect implements DialectInterface { public function parseVersion(string $version, array $options): ParsedVersion { // Parse versions like: 2023.12, 2024.01.15 if (preg_match('/^(\d{4})\.(\d{1,2})(?:\.(\d{1,2}))?$/', $version, $matches)) { $year = (int) $matches[1]; $month = (int) $matches[2]; $day = isset($matches[3]) ? (int) $matches[3] : 1; return new ParsedVersion($year, $month, $day, [], [], $version); } throw new InvalidArgumentException("Invalid CalVer format: {$version}"); } // ... rest of implementation }
๐จ Comprehensive Error Handling
use Grazulex\SemverSieve\Exceptions\{ InvalidVersionException, InvalidRangeException, ConfigurationException, SemverSieveException }; try { $sieve->includes('invalid.version.format', ['^1.0']); } catch (InvalidVersionException $e) { echo "Invalid version: " . $e->getMessage(); // Access error context $context = $e->getContext(); echo "Failed version: " . $context['version']; } catch (InvalidRangeException $e) { echo "Invalid range: " . $e->getMessage(); } catch (ConfigurationException $e) { echo "Configuration error: " . $e->getMessage(); } catch (SemverSieveException $e) { // Catch-all for any sieve-related errors echo "Sieve error: " . $e->getMessage(); }
๐๏ธ SOLID Architecture Deep Dive
Single Responsibility Principle (SRP)
Each class has one clear purpose:
Sieve
- Main API facadeVersionParser
- Parse version stringsRangeParser
- Parse range expressionsVersionComparator
- Compare versionsRangeEvaluator
- Evaluate range matching
Open/Closed Principle (OCP)
- โ Add new dialects without modifying existing code
- โ Extend with new operators via configuration
- โ Plugin architecture for custom behaviors
Liskov Substitution Principle (LSP)
- โ
All dialects are interchangeable via
DialectInterface
- โ Consistent behavior across implementations
- โ Polymorphic usage guaranteed
Interface Segregation Principle (ISP)
- โ Small, focused interfaces
- โ Clients depend only on methods they use
- โ No forced dependencies on unused functionality
Dependency Inversion Principle (DIP)
- โ Core classes depend on abstractions (interfaces)
- โ Dependency injection throughout
- โ High-level modules independent of low-level details
๐ Performance & Benchmarks
Optimizations
- Immutable objects - Thread-safe, cacheable
- Lazy evaluation - Parse only when needed
- Memory efficient - Minimal object allocation
- Type safety - PHP 8.2+ strict typing
Typical Performance
// Version parsing: ~0.1ms per version // Range evaluation: ~0.05ms per constraint // Memory usage: ~2KB per Sieve instance
๐งช Testing & Quality Assurance
Test Coverage
- 42 tests with 126 assertions
- Unit tests for all components
- Integration tests for end-to-end scenarios
- Property-based testing for edge cases
- Mutation testing with Infection
Quality Tools
- PHPStan Level 6 - Static analysis
- PHP-CS-Fixer - PSR-12 code style
- Rector - Automated refactoring
- Pest - Modern testing framework
# Run complete quality suite composer quality # Individual commands composer test # Run tests composer stan # Static analysis composer cs-fix # Fix code style composer infection # Mutation testing
๐ผ Real-World Use Cases
๐ Dependency Analysis Tools
// Analyze if package versions satisfy constraints $composer = new Sieve(new ComposerDialect()); $compatible = $composer->includes('2.1.5', ['^2.0', '!=2.1.3']); // Multi-ecosystem dependency checker $ecosystems = [ 'php' => new ComposerDialect(), 'js' => new NpmDialect(), 'python' => new PypiDialect(), 'java' => new MavenDialect(), ]; foreach ($ecosystems as $name => $dialect) { $sieve = new Sieve($dialect); $results[$name] = $sieve->includes($version, $constraints); }
๐ฆ Package Registry & Mirrors
// Filter package versions by compatibility $npm = new Sieve(new NpmDialect()); $compatibleVersions = array_filter($allVersions, function($version) use ($npm) { return $npm->includes($version, ['>=14.0.0', '<16.0.0']); });
๐ CI/CD Pipeline Integration
// Validate release versions against policies $policy = ['^1.0', '!=1.2.3', '<2.0.0-0']; // No prereleases $sieve = new Sieve(new GenericSemverDialect()); if (!$sieve->includes($releaseVersion, $policy)) { throw new Exception("Version {$releaseVersion} violates release policy"); }
๐ง Version Range Intersection
// Find common version ranges across dependencies $result1 = $sieve->match('1.5.0', ['^1.0', '>=1.4']); $result2 = $sieve->match('1.5.0', ['~1.5', '<1.6']); $intersection = array_intersect($result1['matched_ranges'], $result2['matched_ranges']);
๐ Ecosystem Compatibility Matrix
Feature | Generic | Composer | npm | PyPI | RubyGems | Maven | NuGet | Go |
---|---|---|---|---|---|---|---|---|
Basic Comparisons | โ | โ | โ | โ | โ | โ | โ | โ |
Caret Ranges | โ | โ | โ | โ | โ | โ | โ | โ |
Tilde Ranges | โ | โ | โ | โ | โ | โ | โ | โ |
Pessimistic | โ | โ | โ | โ | โ | โ | โ | โ |
Wildcards | โ | โ | โ | โ | โ | โ | โ | โ |
X-Ranges | โ | โ | โ | โ | โ | โ | โ | โ |
Interval Notation | โ | โ | โ | โ | โ | โ | โ | โ |
Special Tags | โ | โ | โ | โ | โ | โ | โ | โ |
4+ Segments | โ | โ | โ | โ | โ | โ | โ | โ |
v-Prefix Required | โ | โ | โ | โ | โ | โ | โ | โ |
Prerelease Support | โ | โ | โ | โ | โ | โ | โ | โ |
๐ป Development & Contributing
๐ ๏ธ Local Development Setup
# Clone repository git clone https://github.com/Grazulex/php-semver-sieve.git cd php-semver-sieve # Install dependencies composer install # Run tests composer test # Check code quality composer quality
๐งช Development Commands
# Testing composer test # Run all tests composer test-coverage # Run with coverage report composer test-unit # Unit tests only composer test-integration # Integration tests only # Code Quality composer stan # PHPStan static analysis composer cs-check # Check code style composer cs-fix # Fix code style issues composer rector # Modernize code composer infection # Mutation testing # Combined composer quality # Full quality check suite
๐๏ธ Project Structure
src/
โโโ Sieve.php # ๐ฏ Main entry point
โโโ Contracts/
โ โโโ DialectInterface.php # ๐ Dialect contract
โ โโโ VersionComparatorInterface.php # ๐ Comparison contract
โ โโโ ParserInterface.php # ๐ Parser contract
โโโ Dialects/ # ๐ 8 Package manager dialects
โ โโโ GenericSemverDialect.php # ๐ฏ Generic SemVer
โ โโโ ComposerDialect.php # ๐ PHP/Composer
โ โโโ NpmDialect.php # ๐ฆ JavaScript/npm
โ โโโ PypiDialect.php # ๐ Python/PyPI
โ โโโ RubyGemsDialect.php # ๐ Ruby/RubyGems
โ โโโ MavenDialect.php # โ Java/Maven
โ โโโ NugetDialect.php # ๐ท .NET/NuGet
โ โโโ GoModDialect.php # ๐น Go/Go modules
โโโ Parsers/ # ๐ Parsing logic
โโโ Comparators/ # โ๏ธ Version comparison
โโโ Evaluators/ # ๐๏ธ Range evaluation
โโโ ValueObjects/ # ๐ฆ Immutable data objects
โโโ Exceptions/ # ๐จ Error handling
โโโ Configuration/ # โ๏ธ Configuration management
tests/
โโโ Unit/ # ๐งช Unit tests (Pest)
โโโ Integration/ # ๐ Integration tests
โโโ Fixtures/ # ๐ Test data
๐ค Contributing
We welcome contributions! Here's how to get started:
๐ Quick Contribution Guide
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Write tests for your changes
- Run quality checks (
composer quality
) - Commit your changes (
git commit -m 'Add amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
๐ฏ Contribution Areas
- New dialects for other package managers
- Performance optimizations
- Additional test cases and edge case coverage
- Documentation improvements
- Bug fixes and issue resolution
๐ Coding Standards
- PSR-12 code style (enforced by PHP-CS-Fixer)
- PHPStan Level 6 compliance required
- 100% test coverage for new features
- SOLID principles architecture
- PHP 8.2+ modern features encouraged
๐งช Adding New Dialects
// 1. Create dialect class implementing DialectInterface final class NewDialect implements DialectInterface { /* ... */ } // 2. Add comprehensive tests describe('NewDialect', function () { /* ... */ }); // 3. Update README with dialect documentation // 4. Add to compatibility matrix
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
MIT License
Copyright (c) 2024 Jean-Marc Strauven
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
๐ Credits & Acknowledgments
๐จโ๐ป Author
Jean-Marc Strauven - Initial work and architecture
- GitHub: @Grazulex
- Email: your-email@example.com
๐ฏ Inspiration & References
- SemVer 2.0.0 Specification - The foundation for semantic versioning
- Composer (getcomposer.org) - PHP dependency management
- npm - Node.js package manager version handling
- PEP 440 - Python packaging version identification
- RubyGems - Ruby package manager conventions
- Maven - Java project management and versioning
- NuGet - .NET package management
- Go Modules - Go dependency management system
๐๏ธ Architecture Principles
- SOLID Principles - Clean, maintainable object-oriented design
- Domain-Driven Design - Value objects and ubiquitous language
- Immutable Objects - Thread-safe, predictable behavior
- Dependency Injection - Testable, flexible architecture
๐ ๏ธ Tools & Technologies
- PHP 8.2+ - Modern PHP with latest features
- Pest - Elegant PHP testing framework
- PHPStan - Static analysis for type safety
- Infection - Mutation testing for test quality
- PHP-CS-Fixer - Code style automation
- GitHub Actions - Continuous integration
๐ PHP SemVer Sieve - Universal version range checking for the modern PHP ecosystem
Made with โค๏ธ for the PHP community