$ show ray-di/Ray.Di
Ray.Di is a dependency injection and aspect-oriented programming (AOP) framework for PHP, inspired by Google Guice. It provides a module-based DI container that supports constructor, setter, and property injection via PHP 8 attributes, along with method interception for AOP cross-cutting concerns. The core architecture centers on AbstractModule for defining bindings, a Container for storing resolved dependencies, and an Injector that weaves aspects and manages object lifetimes (prototype/singleton). The framework supports advanced features like provider binding, contextual injection, multibinding (collections), assisted injection, null-object binding, and circular dependency detection. It includes a composition-time provenance log (BindingLog) for debugging binding precedence and produces a bindings.md diagnostics artifact. The codebase is mature and thoroughly tested (100% branch coverage), with extensive contract tests that pin observable semantics.
The LLM identified 5 files worth highlighting for reuse:
Isolates Swoole/OpenSwoole coroutine id resolution behind a single static method (id()). The resolver is lazily selected from whichever extension is loaded at first call, falling back to a no-op returning 0. No hard dependency on either extension; the container never imports coroutine classes directly. Demonstrates a clean bridge pattern that can be extracted for any PHP library needing conditional coroutine support.
Implements CompilerInterface as a 'spy' that never actually compiles: it only returns a string representation of the AOP bindings for logging/diagnostics. The compile() method concatenates the class name with a human-readable list of method-to-interceptor mappings, enabling ModuleString and BindingsMarkdown to preview aspects without generating proxy code. Shows a non-obvious application of the CompilerInterface for introspection rather than production weaving.
Encapsulates all validation rules for bind(), to(), and toProvider() calls: checks that interfaces are real, targets implement interfaces, providers implement ProviderInterface, and handles the special NullInterceptor edge case. Each validation method is single-responsibility, throws specific domain exceptions (NotFound, InvalidType, InvalidProvider). Can be reused as a validation component for any DI container binding configuration.
A read-only lazy-initializing Map implementation that resolves multibindings on access via an InjectorInterface. Implements ArrayAccess (with read-only enforcement throwing ReadOnlyMapAccess), Countable, and IteratorAggregate for lazy iteration. Each entry is wrapped in a LazyInterface that defers creation until offsetGet() or iteration. Can be extracted as a generic lazy collection pattern for any DI container that needs to inject groups of services.
Records every write to the dependency container during module compositionβfirst bind, replace, keep (merge collision), and move (rename). Each write is a BindingEvent with type, index, dependency string, source module, and the discarded side for collisions. The log is excluded from serialization and never influences resolution. Demonstrates a composition-time event sourcing pattern that makes invisible merge semantics observable, directly addressing the silent-discard problem that PR #319 exposed.