liquidrazor / diregistry
Strict descriptor-driven dependency injection registry library for PHP 8.3+.
v0.1.0
2026-03-26 19:03 UTC
Requires
- php: >=8.3
Requires (Dev)
- phpunit/phpunit: ^11.5
README
DI Registry is a PHP 8.3+ library for descriptor-driven dependency resolution and instantiation.
It is intentionally strict:
- descriptors are explicit data, not executable configuration
- resolution keys are limited to contract or class path
- resolution is deterministic or it fails
- runtime instances are stored outside descriptor metadata
- scalar and configuration values are provided explicitly through a value provider
The library is built from four concrete runtime pieces:
RegistryCompilerturns normalized descriptor definitions into a validatedCompiledRegistryDescriptorResolverselects exactly oneProspectDescriptorDescriptorInstantiatorbuilds an object from that descriptorInMemoryRuntimeInstanceStorehandles singleton, scoped, and pooled runtime behavior
Installation
composer require liquidrazor/diregistry
Requirements:
- PHP 8.3 or newer
- Composer autoloading
- a source of descriptor definitions before runtime
Quick Start
<?php declare(strict_types=1); use LiquidRazor\DIRegistry\Bootstrap\RegistryCompiler; use LiquidRazor\DIRegistry\Contract\DependencyValueProviderInterface; use LiquidRazor\DIRegistry\Descriptor\DependencyDescriptor; use LiquidRazor\DIRegistry\Instantiator\DescriptorInstantiator; use LiquidRazor\DIRegistry\Resolver\DescriptorResolver; use LiquidRazor\DIRegistry\Runtime\InMemoryRuntimeInstanceStore; use LiquidRazor\DIRegistry\Value\RuntimeContext; use LiquidRazor\DIRegistry\Enum\Environment; interface MailerInterface { } final class SmtpMailer implements MailerInterface { public function __construct(public string $dsn) { } } final class NewsletterService { public function __construct(public MailerInterface $mailer) { } } final class ArrayValueProvider implements DependencyValueProviderInterface { public function __construct(private array $values) { } public function canProvide(DependencyDescriptor $dependency, RuntimeContext $context): bool { return array_key_exists($dependency->name, $this->values); } public function provide(DependencyDescriptor $dependency, RuntimeContext $context): mixed { return $this->values[$dependency->name]; } } $definitions = [ [ 'id' => 'service.mailer.smtp', 'classPath' => SmtpMailer::class, 'contracts' => [MailerInterface::class], 'source' => 'config/services.php', 'lifecycle' => 'singleton', 'environments' => ['prod'], 'priority' => 0, 'isDefault' => true, 'constructionStrategy' => 'constructor', 'dependencies' => [ [ 'name' => 'dsn', 'position' => 0, 'kind' => 'scalar', 'target' => 'mailer.dsn', 'required' => true, 'nullable' => false, 'hasDefault' => false, ], ], ], [ 'id' => 'service.newsletter', 'classPath' => NewsletterService::class, 'contracts' => [], 'source' => 'config/services.php', 'lifecycle' => 'transient', 'environments' => ['prod'], 'priority' => 0, 'isDefault' => false, 'constructionStrategy' => 'constructor', 'dependencies' => [ [ 'name' => 'mailer', 'position' => 0, 'kind' => 'contract', 'target' => MailerInterface::class, 'required' => true, 'nullable' => false, 'hasDefault' => false, ], ], ], ]; $registry = (new RegistryCompiler())->compile($definitions, 'build-2026-03-26'); $resolver = DescriptorResolver::forRegistry($registry); $instantiator = DescriptorInstantiator::forRuntime( $resolver, new ArrayValueProvider(['dsn' => 'smtp://localhost']), new InMemoryRuntimeInstanceStore(), ); $context = new RuntimeContext(Environment::Prod); $descriptor = $resolver->resolveClassPath(NewsletterService::class, $context->environment); $service = $instantiator->instantiate($descriptor, $context);
What this example shows:
- the compiler computes descriptor checksums and validates the full descriptor set
- contract dependencies are resolved through the resolver
- scalar dependencies come only from
DependencyValueProviderInterface - singleton reuse is handled by the runtime store, not by registry mutation
Key Constraints
- No alias system.
- No autowiring.
- No fallback from one environment to another.
- No best-effort choice when multiple candidates remain tied.
- No automatic handling of scalar or config dependencies.
- No runtime mutation of compiled registry contents.
Documentation
Start with Documentation Home.
Recommended entry points:
- Overview: design constraints, architecture, and library fit.
- Getting Started: installation, first registry compilation, and first resolution.
- Usage Guide: concrete runtime wiring and operational behavior.
- Reference: public contracts, descriptors, enums, and exceptions.
- Validation & Debug: compile checks, debug validation, and troubleshooting.
Development
composer install vendor/bin/phpunit