jardissupport/classversion

Runtime class versioning via namespace injection with configurable fallback chains and proxy caching

Maintainers

Package info

github.com/jardisSupport/classversion

Homepage

Issues

pkg:composer/jardissupport/classversion

Statistics

Installs: 216

Dependents: 1

Suggesters: 0

Stars: 0

v1.0.0 2026-04-23 13:21 UTC

This package is auto-updated.

Last update: 2026-04-23 13:24:32 UTC


README

Build Status License: MIT PHP Version PHPStan Level PSR-12 Coverage

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 LoadClassFromProxy that are returned directly, bypassing class loading
  • Resolution Cache — optional ClassResolutionCache memoizes hits and misses, eliminating repeated class_exists() / stat() syscalls on hot paths
  • Fallback Chains — define ordered fallback sequences in ClassVersionConfig so resolution degrades gracefully across versions
  • Version Groups + Aliases — map multiple labels to one canonical version key
  • Tracing Decorator — wrap any resolver in TracingClassVersion to 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