jardissupport / classversion
Runtime class versioning via namespace injection with configurable fallback chains and proxy caching
Requires
- php: >=8.2
- jardissupport/contract: ^1.0
Requires (Dev)
- phpstan/phpstan: ^2.0.4
- phpunit/phpunit: ^10.5
- squizlabs/php_codesniffer: ^3.11.2
This package is auto-updated.
Last update: 2026-04-23 13:24:32 UTC
README
Part of the Jardis Business Platform — Enterprise-grade PHP components for Domain-Driven Design
Runtime class versioning via namespace injection. Load different implementations of the same class by version label — without changing call sites. Configure fallback chains so a missing version silently degrades to the previous one. Register proxy instances for hot-swapping at test or runtime. Deploy new logic versions without breaking existing code.
Features
- SubDirectory Resolution — injects a version label into the namespace to locate versioned class implementations
- Extensions Resolution — inserts a fixed
Extensions/segment at a configurable namespace depth to pick up baseline and versioned overrides side by side - Proxy Registry — pre-register object instances via
LoadClassFromProxythat are returned directly, bypassing class loading - Resolution Cache — optional
ClassResolutionCachememoizes hits and misses, eliminating repeatedclass_exists()/stat()syscalls on hot paths - Fallback Chains — define ordered fallback sequences in
ClassVersionConfigso resolution degrades gracefully across versions - Version Groups + Aliases — map multiple labels to one canonical version key
- Tracing Decorator — wrap any resolver in
TracingClassVersionto record every resolution for debugging - Zero-coupling — works with any PSR-4 autoloader, no framework dependency required
Installation
composer require jardissupport/classversion
Quick Start
use JardisSupport\ClassVersion\Data\ClassVersionConfig; use JardisSupport\ClassVersion\Reader\LoadClassFromSubDirectory; use JardisSupport\ClassVersion\ClassVersion; // Map version labels to canonical subdirectory names $config = new ClassVersionConfig( version: ['V2' => ['v2', '2.0'], 'V1' => ['v1', '1.0']], ); $resolver = new ClassVersion( $config, new LoadClassFromSubDirectory($config), ); // Resolves App\Service\V2\Calculator (namespace injection) $className = $resolver(App\Service\Calculator::class, 'v2'); $instance = new $className();
Advanced Usage
use JardisSupport\ClassVersion\Data\ClassVersionConfig; use JardisSupport\ClassVersion\Reader\LoadClassFromSubDirectory; use JardisSupport\ClassVersion\Reader\LoadClassFromProxy; use JardisSupport\ClassVersion\ClassVersion; use JardisSupport\ClassVersion\Support\ClassResolutionCache; use JardisSupport\ClassVersion\Support\TracingClassVersion; // Fallback chain: if V2 namespace is missing, try V1 before the base class $config = new ClassVersionConfig( version: ['V2' => ['v2', '2.0'], 'V1' => ['v1', '1.0']], fallbacks: ['V2' => ['V1']], ); // Proxy registry: return a pre-built instance for a specific class + version $proxy = new LoadClassFromProxy($config); $proxy->addProxy(App\Service\Calculator::class, new MyTestCalculator(), 'v2'); // Optional: resolution cache memoizes hits and misses — same (class, version) // key never hits the autoloader twice. $resolver = new ClassVersion( $config, new LoadClassFromSubDirectory($config), $proxy, cache: new ClassResolutionCache(), ); // Wrap with tracing decorator to record all resolutions $tracing = new TracingClassVersion($resolver); // Returns the pre-registered proxy instance directly $result = $tracing(App\Service\Calculator::class, 'v2'); // Resolves App\Service\V2\Formatter — falls back to V1 if the V2 namespace is absent $className = $tracing(App\Service\Formatter::class, 'v2'); $instance = new $className(); // Inspect the resolution log foreach ($tracing->getTrace() as $entry) { echo $entry['requested'] . ' [' . ($entry['version'] ?? 'default') . ']' . ' → ' . $entry['type'] . PHP_EOL; } $tracing->clearTrace();
Extensions Resolution
LoadClassFromExtensions is a second, parametrised resolver for projects that
keep developer-owned overrides in a dedicated directory. Configure two pieces:
depth (how many namespace segments from the left make up the "root") and
segmentName (the directory name, e.g. "Extensions").
use JardisSupport\ClassVersion\Reader\LoadClassFromExtensions; $config = new ClassVersionConfig( version: ['v2' => ['v2'], 'v1' => ['v1']], ); $resolver = new ClassVersion( $config, new LoadClassFromExtensions(depth: 3, segmentName: 'Extensions', versionConfig: $config), ); // Lookup order for App\Order\Order\Command\Handler\CreateOrder: // 1. App\Order\Order\Extensions\{v2-chain}\Command\Handler\CreateOrder (if version set) // 2. App\Order\Order\Extensions\Command\Handler\CreateOrder (baseline override) // 3. App\Order\Order\Command\Handler\CreateOrder (generator base) $className = $resolver(App\Order\Order\Command\Handler\CreateOrder::class, 'v2');
Classes with fewer than depth + 1 namespace segments skip the override
lookup and resolve against the generator base directly. No configuration
defaults — the caller decides the layout convention explicitly.
Documentation
Full documentation, guides, and API reference:
docs.jardis.io/en/support/classversion
License
This package is licensed under the MIT License.
Jardis · Documentation · Headgent
KI-gestützte Entwicklung
Dieses Package liefert einen Skill für Claude Code, Cursor, Continue und Aider mit. Installation im Konsumentenprojekt:
composer require --dev jardis/dev-skills
Mehr Details: https://docs.jardis.io/skills