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.)

v1.0.0 2025-09-04 12:54 UTC

This package is auto-updated.

Last update: 2025-09-05 08:26:07 UTC


README

PHP SemVer Sieve

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.

Latest Version Total Downloads License PHP Version Tests Static Analysis Mutation Testing

๐Ÿš€ 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
  • ๐Ÿ›๏ธ 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 facade
  • VersionParser - Parse version strings
  • RangeParser - Parse range expressions
  • VersionComparator - Compare versions
  • RangeEvaluator - 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

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Write tests for your changes
  4. Run quality checks (composer quality)
  5. Commit your changes (git commit -m 'Add amazing feature')
  6. Push to the branch (git push origin feature/amazing-feature)
  7. 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

๐ŸŽฏ 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

Stars Forks Issues

Made with โค๏ธ for the PHP community