# Ray.Di > Ray.Di is a PHP dependency injection framework inspired by Google Guice. It automatically resolves object dependencies and enables flexible, testable code through compile-time dependency resolution. Ray.Di has been actively maintained since 2015, supporting PHP 7.2+ with a focus on performance and developer experience. It uses code generation to minimize runtime overhead while providing powerful features like aspect-oriented programming (AOP). ## Why Ray.Di? - **Compile-time safety**: Detects configuration errors before runtime - **Zero annotations**: Constructor injection works without special markers - **Performance optimized**: Generates efficient code for production - **Framework agnostic**: Integrates with any PHP application ## Getting Started ## Essential Concepts ## Common Binding Patterns ## Advanced Techniques ## Best Practices ## Production Ready ## Optional ## Getting Started - [Installation](#installation): Quick setup with Composer - `composer require ray/di` - [Overview](#overview): Core concepts and architecture of dependency injection - [Tutorial](#tutorial1): Build your first Ray.Di application step-by-step - [Mental Model](#mentalmodel): Think of Ray.Di as a map from types to their providers ## Essential Concepts - [Bindings](#bindings): Learn how to wire interfaces to implementations - [Scopes](#scopes): Control object lifecycles with Singleton and Prototype scopes - [Modules](#gettingstarted): Organize your bindings into reusable configuration units ## Common Binding Patterns - [Linked Bindings](#linkedbindings): The most common pattern - bind an interface to a concrete class - [Provider Bindings](#providerbindings): Create objects with complex initialization logic - [Instance Bindings](#instancebindings): Bind to pre-existing objects or values - [Constructor Bindings](#constructorbindings): Select specific constructors for injection ## Advanced Techniques - [AOP](#aop): Intercept method calls for logging, transactions, and security - [Contextual Bindings](#contextualbindings): Different implementations for different use cases - [Injecting Providers](#injectingproviders): Lazy loading and multiple instances - [Multibindings](#multibindings): Create plugin systems with sets and maps ## Best Practices - [Minimize Mutability](#minimizemutability): Prefer constructor injection for immutable, thread-safe objects - [Inject Only Direct Dependencies](#injectonlydirectdependencies): Don't inject factories just to get other objects - [Organize Modules by Feature](#organizemodulesbyfeature): Group related bindings together, not by type - [Avoid Static State](#avoidstaticstate): Static state makes testing difficult and should be injected instead ## Production Ready - [Performance Boost](#performanceboost): Use ScriptInjector for 10x faster production performance - [Integration](#integration): Works with Laravel, Symfony, and other frameworks - [Backward Compatibility](#backwardcompatibility): Semantic versioning with no BC breaks in minor versions ## Optional - [All Best Practices](#bestpractices): Comprehensive guide to Ray.Di best practices - [Grapher](#grapher): Visualize your dependency graph - [Additional Binding Types](#bindingattributes): Qualifiers, untargeted bindings, and more - [Object Lifecycle](#objectlifecycle): PostConstruct and other lifecycle hooks - [Injections](#injections): Method, setter, and assisted injection patterns --- # Installation The recommended way to install Ray.Di is through [Composer](https://github.com/composer/composer). ```bash composer require ray/di ^2.0 ``` The GitHub repository is at [ray-di/Ray.Di](https://github.com/ray-di/Ray.Di) ## Testing Ray.Di Here's how to install Ray.Di from source and run the unit tests and demos. ```bash git clone https://github.com/ray-di/Ray.Di.git cd Ray.Di ./vendor/bin/phpunit php demo-php8/run.php ``` Ray.Di logo # Overview Ray.Di is a dependency injection (DI) framework for PHP. It automatically resolves object dependencies and enables flexible object graph construction according to the context. ## Core Features ### Dependency Resolution at Compile Time - Resolves dependencies by describing overall rules rather than individual object assembly (autowiring) - Detects dependency issues before execution - Minimizes runtime overhead through code generation ### Flexible Object Graph Construction - Enables various contexts through the combination of independent modules - Allows dependency resolution according to the injected object; for example, changing dependencies based on the target method's attributes or the object's state (CDI: Contexts and Dependency Injection) - Injects different implementations of the same interface using `Qualifier` - Supports injection of lazily instantiated objects ### Explicit Dependency Description - Describes dependency generation using raw PHP code - Utilizes attributes for self-documented dependency definitions - Separates cross-cutting concerns through integration with AOP ## Stability and Reliability Since the release of version 2.0 in 2015, Ray.Di has expanded its features along with the evolution of PHP while maintaining backward compatibility by following semantic versioning. ## Google Guice and Ray.Di Ray.Di is a PHP DI framework inspired by [Google Guice](https://github.com/google/guice). Based on the proven API design of Google Guice, it aims for PHP-like evolution. Most of the documents on this site are also quoted from Google Guice. --- Using dependency injection offers many benefits, but doing it manually requires writing a lot of boilerplate code. Ray.Di is a framework that allows you to use dependency injection without writing such cumbersome code. For more details, please see the [Motivation](motivation.html) page. In short, Ray.Di eliminates the need to use factories or `new` in your PHP code. While you may still need to write factories, your code does not directly depend on them. Your code becomes easier to modify, unit test, and reuse in other contexts. # Ray.Di Tutorial 1 In this tutorial, you will learn the basics of the DI pattern and how to start a Ray.Di project. We will change from a non-di code to a manual DI code, then to a code using Ray.Di to add functionality. ## Preparation Create a project for the tutorial. ``` mkdir ray-tutorial cd ray-tutorial composer self-update composer init --name=ray/tutorial --require=ray/di:^2 --autoload=src -n composer update ``` Create `src/Greeter.php`. A program that greets `$users` one after another. ```php sayHello(); ``` Let's run it. ```php php bin/run.php Hello DI! Hello AOP! Hello REST! ``` ## Dependency pull Consider making `$users` variable. For example, a global variable? ```diff - $users = ['DI', 'AOP', 'REST']; + $users = $GLOBALS['users']; ``` Too wild. Let's consider other ways. ```php define("USERS", ['DI', 'AOP', 'REST']); $users = USERS; ``` ```php class User { public const NAMES = ['DI', 'AOP', 'REST']; }; $users = User::NAMES; ``` ```php $users = Config::get('users') ``` It is getting the necessary dependencies from the outside, It's "dependency pull" and in the end it is the same global as the `$GLOBALS` variable. It makes the coupling between objects tight and difficult to test. ## Dependency Injection The DI pattern is one in which dependencies are injected from other sources, rather than being obtained from the code itself. ```php class Greeter { public function __construct( private readonly Users $users ) {} public function sayHello(): void { foreach ($this->users as $user) { echo 'Hello ' . $user . '!' . PHP_EOL; } } } ``` Inject not only the data you need, but also the output as a separate service. ```diff public function __construct( - private readonly Users $users + private readonly Users $users, + private readonly PrinterInterface $printer ) {} public function sayHello() { foreach ($this->users as $user) { - echo 'Hello ' . $user . '!' . PHP_EOL; + ($this->printer)($user); } } ``` Create the following classes `src/Users.php` ```php users as $user) { ($this->printer)($user); } } } ``` ## Manual DI Create and run a script `bin/run_di.php` to do this. ```php sayHello(); ``` While the number of files may seem to increase in number and overall complexity, the individual scripts are so simple that it is difficult to make them any simpler. Each class has only one responsibility [^srp], relies on abstractions rather than implementations [^dip], and is easy to test, extend, and reuse. [^srp]: [Single Responsibility Principle (SRP)](https://en.wikipedia.org/wiki/Single_responsibility_principle) [^dip]: [Dependency Inversion Principle (DIP)](https://en.wikipedia.org/wiki/Dependency_inversion_principle) ### Compile Time and Runtime Code under `bin/` constitutes a dependency at **compile time**, while code under `src/` is executed at **run time**. PHP is a scripting language, but this distinction between compile time and run time can be considered. ### Constructor Injection DI code passes dependencies externally and receives them in the constructor. ```php $instance = new A( new B, new C( new D( new E, new F, new G ) ) ); ``` B and C needed to generate A are passed to the constructor from outside A (without being obtained from inside A); D to generate C, E,F,G to generate D... and dependencies require other dependencies, and objects generate an object graph [^og] containing dependent objects. As the size of the project grows, manual DI using such factory code becomes a reality with problems such as deep nested dependency resolution, instance management such as singletons, reusability, and maintainability. Ray.Di solves that dependency problem. [^og]: "In computer science, object-oriented applications have a complex network of interrelated objects. Objects are connected to each other either by being owned by one object or by containing other objects (or their references). This object net is called an object graph." [Object Graph](https://en.wikipedia.org/wiki/Object_graph) ### Module A module is a set of bindings. There are several types of binding, but here we will use the most basic, link binding, which binds an interface to a class, and instance binding, which binds to an actual instance, such as a value object. Create `src/AppModule.php`. ```php bind(Users::class)->toInstance(new Users(['DI', 'AOP', 'REST'])); $this->bind(PrinterInterface::class)->to(Printer::class); $this->bind(GreeterInterface::class)->to(CleanGreeter::class); } } ``` Create and run `bin/run_di.php` to run. ```php getInstance(GreeterInterface::class); $greeter->sayHello(); ``` Did it work? If something is wrong, please compare it with [tutorial1](https://github.com/ray-di/tutorial1/tree/master/src). ## Dependency Replacement Sometimes you want to change the bindings depending on the context of execution, such as only for unit testing, only for development, and so on. For example, suppose you have a test-only binding `src/TestModule.php`. ```php bind(Users::class)->toInstance(new Users(['TEST1', 'TEST2'])); } } ``` Modify the `bin/run_di.php` script to override this binding. ```diff use Ray\Tutorial\AppModule; +use Ray\Tutorial\TestModule; use Ray\Tutorial\GreeterInterface; require dirname(__DIR__) . '/vendor/autoload.php'; $module = new AppModule(); +$module->override(new TestModule()); ``` Let's run it. ``` Hello TEST1! Hello TEST2! ``` ## Dependency on Dependency Next, the greeting message, which is now fixed and retained in the Printer, is also changed to be injected to support multiple languages. Create `src/IntlPrinter.php`. ```php message, $user); } } ``` The constructor takes a message string for the greeting, but to identify this bundle [attribute bundle](https://ray-di.github.io/manuals/1.0/ja/binding_attributes.html) for the `#[Message]`attribute, `src/Message.php`. ```php bind(Users::class)->toInstance(new Users(['DI', 'AOP', 'REST'])); - $this->bind(PrinterInterface::class)->to(Printer::class); + $this->bind(PrinterInterface::class)->to(IntlPrinter::class); + $this->bind()->annotatedWith(Message::class)->toInstance('Hello %s!' . PHP_EOL); $this->bind(GreeterInterface::class)->to(CleanGreeter::class); } } ``` Run it to make sure it does not change. Then try the error. Comment out the `Message::class` binding in the `configure()` method. ```diff - $this->bind()->annotatedWith(Message::class)->toInstance('Hello %s!' . PHP_EOL); + // $this->bind()->annotatedWith(Message::class)->toInstance('Hello %s!' . PHP_EOL); ``` This means that Ray.Di does not know what to inject into the dependencies attributed as `#[Message]`. When I run it, I get the following error ``` PHP Fatal error: Uncaught exception 'Ray\Di\Exception\Unbound' with message '-Ray\Tutorial\Message' - dependency '' with name 'Ray\Tutorial\Message' used in /tmp/tutorial/src/IntlPrinter.php:8 ($message) - dependency 'Ray\Tutorial\PrinterInterface' with name '' /tmp/tutorial/src/CleanGreeter.php:6 ($printer) ``` This is an error that `$message` in `IntlPrinter.php:8` cannot resolve its dependency, so `$printer` in `CleanGreeter.php:6` which depends on it also cannot resolve its dependency and the injection failed. Thus, when a dependency of a dependency cannot be resolved, the nesting of that dependency is also displayed. Finally, let's create the following bundle as `src/SpanishModule.php` and overwrite it in the same way as TestModule. ```php bind()->annotatedWith(Message::class)->toInstance('¡Hola %s!' . PHP_EOL); } } ``` ```diff use Ray\Tutorial\AppModule; -use Ray\Tutorial\TestModule; +use Ray\Tutorial\SpanishModule; use Ray\Tutorial\GreeterInterface; require dirname(__DIR__) . '/vendor/autoload.php'; $module = new AppModule(); -$module->override(new TestModule()); +$module->override(new SpanishModule()); ``` Have you changed to the Spanish greeting as follows? ``` ¡Hola DI! ¡Hola AOP! ¡Hola REST! ``` ## Summary We have seen the basics of the DI pattern and Ray.Di. Dependencies are injected recursively from the outside rather than obtained by user code from within, and an object graph is generated. At compile time, the relationship building between objects through dependency binding is complete, and at the runtime, the running code depends only on the interface. By following the DI pattern, the SRP principle [^srp] and the DIP principle [^dip] can be followed by nature. The responsibility of securing dependencies has been removed from the code, making it loosely coupled and simple. The code is stable yet flexible, open to extensions but closed to modifications. [^ocp] [^ocp]: [OCP](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle) --- # Ray.Di Mental Model _Learn about `Key`, `Provider` and how Ray.Di is just a map_ When you are reading about "Dependency Injection", you often see many buzzwords ("Inversion of control", "Hollywood principle") that make it sound confusing. But underneath the jargon of dependency injection, the concepts aren't very complicated. In fact, you might have written something very similar already! This page walks through a simplified model of Ray.Di implementation, which should make it easier to think about how it works. ## Ray.Di is a map Fundamentally, Ray.Di helps you create and retrieve objects for your application to use. These objects that your application needs are called **dependencies**. You can think of Ray.Di as being a map[^Ray.Di-map]. Your application code declares the dependencies it needs, and Ray.Di fetches them for you from its map. Each entry in the "Ray.Di map" has two parts: * **Ray.Di key**: a key in the map which is used to fetch a particular value from the map. * **Provider**: a value in the map which is used to create objects for your application. Ray.Di keys and Providers are explained below. [^Ray.Di-map]: The actual implementation of Ray.Di is far more complicated, but a map is a reasonable approximation for how Ray.Di behaves. ### Ray.Di keys Ray.Di uses `Key` to identify a dependency that can be resolved using the "Ray.Di map". The `Greeter` class used in the [Getting Started](#gettingstarted) declares two dependencies in its constructor and those dependencies are represented as `Key` in Ray.Di: * `#[Message] string` --> `$map[$messageKey]` * `#[Count] int` --> `$map[$countKey]` The simplest form of a `Key` represents a type in php: ```php // Identifies a dependency that is an instance of string. /** @var string $databaseKey */ $databaseKey = $map[$key]; ``` However, applications often have dependencies that are of the same type: ```php class Message { public function __construct( public readonly string $text ){} } class MultilingualGreeter { public function __construct( private readonly Message $englishGreeting, private readonly Message $spanishGreeting ) {} } ``` Ray.Di uses [binding attributes](#bindingattributes) to distinguish dependencies that are of the same type, that is to make the type more specific: ```php class MultilingualGreeter { public function __construct( #[English] private readonly Message $englishGreeting, #[Spanish] private readonly Message $spanishGreeting ) {} } ``` `Key` with binding attribute can be created as: ```php $englishGreetingKey = $map[Message::class . English::class]; $spanishGreetingKey = $map[Message::class . Spanish::class]; ``` When an application calls `$injector->getInstance(MultilingualGreeter::class)` to create an instance of `MultilingualGreeter`. This is the equivalent of doing: ```php // Ray.Di internally does this for you so you don't have to wire up those // dependencies manually. $english = $injector->getInstance(Message::class, English::class)); $spanish = $injector->getInstance(Message::class, Spanish::class)); $greeter = new MultilingualGreeter($english, $spanish); ``` To summarize: **Ray.Di `Key` is a type combined with an optional binding attribute used to identify dependencies.** ### Ray.Di `Provider`s Ray.Di uses [`Provider`](https://google.github.io/Ray.Di/api-docs/latest/javadoc/com/google/inject/Provider.html) to represent factories in the "Ray.Di map" that are capable of creating objects to satisfy dependencies. `Provider` is an interface with a single method: ```php interface Provider { /** Provides an instance */ public function get(); } ``` Each class that implements `Provider` is a bit of code that knows how to give you an instance of `T`. It could call `new T()`, it could construct `T` in some other way, or it could return you a precomputed instance from a cache. Most applications do not implement `Provider` interface directly, they use `Module` to configure Ray.Di injector and Ray.Di injector internally creates `Provider`s for all the object it knows how to create. For example, the following Ray.Di module creates two `Provider`s: ```php class CountProvider implements ProviderInterface { public function get(): int { return 3; } } class MessageProvider implements ProviderInterface { public function get(): Message { return new Message('hello world'); } } class DemoModule extends AbstractModule { protected function configure(): void { $this->bind()->annotatedWith(Count::class)->toProvider(CountProvider::class); $this->bind()->annotatedWith(Message::class)->toProvider(MessageProvider::class); } } ``` * `MessageProvider` that calls the `get()` method and returns "hello world" * `CountProvider` that calls the `get()` method and returns `3` ## Using Ray.Di There are two parts to using Ray.Di: 1. **Configuration**: your application adds things into the "Ray.Di map". 1. **Injection**: your application asks Ray.Di to create and retrieve objects from the map. Configuration and injection are explained below. ### Configuration Ray.Di maps are configured using Ray.Di modules. A **Ray.Di module** is a unit of configuration logic that adds things into the Ray.Di map. There are two ways to do this: * Using the Ray.Di Domain Specific Language (DSL). Conceptually, these APIs simply provide ways to manipulate the Ray.Di map. The manipulations they do are pretty straightforward. Here are some example translations, shown using PHP syntax for brevity and clarity: | Ray.Di DSL syntax | Mental model | | ---------------------------------- | ---------------------------------------------------------------------------------- | | `bind($key)->toInstance($value)` | `$map[$key] = $value;`
(instance binding) | | `bind($key)->toProvider($provider)` | `$map[$key] = fn => $value;`
(provider binding) | | `bind(key)->to(anotherKey)` | `$map[$key] = $map[$anotherKey];`
(linked binding) | `DemoModule` adds two entries into the Ray.Di map: * `#[Message] string` --> `fn() => (new MessageProvider)->get()` * `#[Count] int` --> `fn() => (new CountProvider)->get()` ### Injection You don't *pull* things out of a map, you *declare* that you need them. This is the essence of dependency injection. If you need something, you don't go out and get it from somewhere, or even ask a class to return you something. Instead, you simply declare that you can't do your work without it, and rely on Ray.Di to give you what you need. This model is backwards from how most people think about code: it's a more *declarative* model rather than an *imperative* one. This is why dependency injection is often described as a kind of *inversion of control* (IoC). Some ways of declaring that you need something: 1. An argument to a constructor: ```php class Foo { // We need a database, from somewhere public function __construct( private Database $database ) {} } ``` 2. An argument to a `DatabaseProvider::get()` method: ```php class DatabaseProvider implements ProviderInterface { public function __construct( #[Dsn] private string $dsn ){} public function get(): Database { return new Database($this->dsn); } } ``` This example is intentionally the same as the example `Foo` class from [Getting Started Guide](GettingStarted#what-is-dependency-injection). Unlike Guice, Ray.Di does not require the `Inject` attribute to be added to the constructor. ## Dependencies form a graph When injecting a thing that has dependencies of its own, Ray.Di recursively injects the dependencies. You can imagine that in order to inject an instance of `Foo` as shown above, Ray.Di creates `Provider` implementations that look like these: ```php class FooProvider implements Provider { public function get(): Foo { global $map; $databaseProvider = $map[Database::class]); $database = $databaseProvider->get(); return new Foo($database); } } class DatabaseProvider implements Provider { public function get(): Database { global $map; $dsnProvider = $map[Dsn::class]; $dsn = $dsnProvider->get(); return new Database($dsn); } } class DsnProvider implements Provider { public function get(): string { return getenv(DB_DSN); } } ``` Dependencies form a *directed graph*, and injection works by doing a depth-first traversal of the graph from the object you want up through all its dependencies. A Ray.Di `Injector` object represents the entire dependency graph. To create an `Injector`, Ray.Di needs to validate that the entire graph works. There can't be any "dangling" nodes where a dependency is needed but not provided.[^3] If the bound is incomplete somewhere in the graph, Ray.Di will throw an `Unbound` exception. [^3]: The reverse case is not an error: it's fine to provide something even if nothing ever uses it—it's just dead code in that case. That said, just like any dead code, it's best to delete providers if nobody uses them anymore. ## What's next? Learn how to use [`Scopes`](scopes.html) to manage the lifecycle of objects created by Ray.Di and the many different ways to [add entries into the Ray.Di map](bindings.html). # Bindings _Overview of bindings in Ray.Di_ A **binding** is an object that corresponds to an entry in [Ray.Di map](mental_model.html). You add new entries into the Ray.Di map by creating bindings. ## Creating Bindings To create bindings, extend `AbstractModule` and override its `configure` method. In the method body, call `bind()` to specify each binding. These methods are type checked in compile can report errors if you use the wrong types. Once you've created your modules, pass them as arguments to `Injector` to build an injector. Use modules to create [linked bindings](linked_bindings.html), [instance bindings](instance_bindings.html), [provider bindings](provider_bindings.html), [constructor bindings](constructor_bindings.html) and [untargeted bindings](untargeted_bindings.html). ```php class TweetModule extends AbstractModule { protected function configure() { $this->bind(TweetClient::class); $this->bind(TweeterInterface::class)->to(SmsTweeter::class)->in(Scope::SINGLETON); $this->bind(UrlShortenerInterface::class)->toProvider(TinyUrlShortener::class); $this->bind('')->annotatedWith(Username::class)->toInstance("koriym"); } } ``` ## More Bindings In addition to the bindings you specify the injector includes [built-in bindings](#builtinbindings). When a dependency is requested but not found it attempts to create a just-in-time binding. The injector also includes bindings for the [providers](injecting_providers.html) of its other bindings. ## Module Install A module can install other modules to configure more bindings. * Earlier bindings have priority even if the same binding is made later. * `override` bindings in that module have priority. ```php protected function configure() { $this->install(new OtherModule); $this->override(new CustomiseModule); } ``` # Scopes By default, Ray returns a new instance each time it supplies a value. This behaviour is configurable via scopes. ```php use Ray\Di\Scope; ``` ```php $this->bind(TransactionLogInterface::class)->to(InMemoryTransactionLog::class)->in(Scope::SINGLETON); ``` # GettingStarted _How to start doing dependency injection with Ray.Di._ ## Getting Started Ray.Di is a framework that makes it easier for your application to use the dependency injection (DI) pattern. This getting started guide will walk you through a simple example of how you can use Ray.Di to incorporate dependency injection into your application. ### What is dependency injection? [Dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) is a design pattern wherein classes declare their dependencies as arguments instead of creating those dependencies directly. For example, a client that wishes to call a service should not have to know how to construct the service, rather, some external code is responsible for providing the service to the client. Here's a simple example of code that *does not* use dependency injection: ```php class Foo { private Database $database; // We need a Database to do some work public function __construct() { // Ugh. How could I test this? What if I ever want to use a different // database in another application? $this->database = new Database('/path/to/my/data'); } } ``` The `Foo` class above creates a fixed `Database` object directly. This prevents this class from being used with other `Database` objects and does not allow the real database to be swapped out for a testing database in tests. Instead of writing untestable or inflexible code, you can use dependency injection pattern to address all these issues. Here's the same example, this time using dependency injection: ```php class Foo { private Database $database; // We need a Database to do some work public function __construct(Database $database) { // The database comes from somewhere else. Where? That's not my job, that's // the job of whoever constructs me: they can choose which database to use. $this->database = $database; } } ``` The `Foo` class above can be used with any `Database` objects since `Foo` has no knowledge of how the `Database` is created. For example, you can create a test version of `Database` implementation that uses an in-memory database in tests to make the test hermetic and fast. The [Motivation](#motivation) page explains why applications should use the dependency injection pattern in more detail. ## Core Ray.Di concepts ### constructor PHP class constructors can be called by Ray.Di through a process called [constructor injection](#injections-constructor-injection), during which the constructors' arguments will be created and provided by Ray.Di. (Unlike Guice, Ray.Di does not require the "Inject" annotation in its constructor.) Here is an example of a class that uses constructor injection: ```php class Greeter { // Greeter declares that it needs a string message and an integer // representing the number of times the message is to be printed. public function __construct( #[Message] readonly string $message, #[Count] readonly int $count ) {} public function sayHello(): void { for ($i=0; $i < $this->count; $i++) { echo $this->message; } } } ``` In the example above, the `Greeter` class has a constructor that is called whenapplication asks Ray.Di to create an instance of `Greeter`. Ray.Di will create the two arguments required, then invoke the constructor. The `Greeter` class's constructor arguments are its dependencies and applications use `Module` to tell Ray.Di how to satisfy those dependencies. ### Ray.Di modules Applications contain objects that declare dependencies on other objects, and those dependencies form graphs. For example, the above `Greeter` class has two dependencies (declared in its constructor): * A `string` value for the message to be printed * An `int` value for the number of times to print the message Ray.Di modules allow applications to specify how to satisfy those dependencies. For example, the following `DemoModule` configures all the necessary dependencies for `Greeter` class: ```php class CountProvider implements ProviderInterface { public function get(): int { return 3; } } class MessageProvider implements ProviderInterface { public function get(): string { return 'hello world'; } } /** * Ray.Di module that provides bindings for message and count used in * {@link Greeter}. */ class DemoModule extends AbstractModule { protected function configure(): void { $this->bind()->annotatedWith(Count::class)->toProvider(CountProvider::class); $this->bind()->annotatedWith(Message::class)->toProvider(MessageProvider::class); } } ``` In a real application, the dependency graph for objects will be much more complicated and Ray.Di makes creating complex object easy by creating all the [transitive dependencies](https://en.wikipedia.org/wiki/Transitive_dependency) automatically. ### Ray.Di injectors To bootstrap your application, you'll need to create a Ray.Di `Injector` withone or more modules in it. For example, a web server script might that looks like this: ```php final class MyWebServer { public function __construct( private readonly RequestLoggingInterface $requestLogging, private readonly RequestHandlerInterface $requestHandler, private readonly AuthenticationInterface $authentication, private readonly Database $database ) {} public function start(): void { // ... } public function __invoke(): void { // Creates an injector that has all the necessary dependencies needed to // build a functional server. $injector = new Injector(new class extends AbstractModule { protected function configure(): void { // Install the modules that provide the necessary dependencies. $this->install(new RequestLoggingModule()); $this->install(new RequestHandlerModule()); $this->install(new AuthenticationModule()); $this->install(new DatabaseModule()); } }); // Bootstrap the application by creating an instance of the server then // start the server to handle incoming requests. $injector->getInstance(MyWebServer::class)->start(); } } (new MyWebServer)(); ``` The injector internally holds the dependency graphs described in your application. When you request an instance of a given type, the injector figures out what objects to construct, resolves their dependencies, and wires everything together. To specify how dependencies are resolved, configure your injector with [bindings](Bindings). [`Injector`]: https://github.com/ray-di/Ray.Di/blob/2.x/src/di/InjectorInterface.php ## A simple Ray.Di application The following is a simple Ray.Di application with all the necessary pieces put together: ```php bind()->annotatedWith(Count::class)->toProvider(CountProvider::class); $this->bind()->annotatedWith(Message::class)->toProvider(MessageProvider::class); } } class Greeter { public function __construct( #[Message] private string $message, #[Count] private int $count ) {} public function sayHello(): void { for ($i = 0; $i < $this->count ; $i++) { echo $this->message . PHP_EOL; } } } /* * Injector's constructor takes one or more modules. * Most applications will call this method exactly once in bootstrap. */ $injector = new Injector([new DemoModule]); /* * Now that we've got the injector, we can build objects. */ $greeter = $injector->getInstance(Greeter::class); // Prints "hello world" 3 times to the console. $greeter->sayHello(); ``` The [greeter](https://github.com/ray-di/greeter/blob/master/greeter.php) application constructed a small dependency graph using Ray.Di that is capable of building instances of `Greeter` class. Large applications usually have many `Module`s that can build complex objects. ## What's next? Read more on how to conceptualize Ray.Di with a simple [mental model](mental_model.html). ## Linked Bindings Linked bindings map a type to its implementation. This example maps the interface TransactionLogInterface to the implementation DatabaseTransactionLog: ```php $this->bind(TransactionLogInterface::class)->to(DatabaseTransactionLog::class); ``` ## Provider Bindings Provider bindings map a type to its provider. ```php $this->bind(TransactionLogInterface::class)->toProvider(DatabaseTransactionLogProvider::class); ``` The provider class implements Ray's Provider interface, which is a simple, general interface for supplying values: ```php namespace Ray\Di; interface ProviderInterface { public function get(); } ``` Our provider implementation class has dependencies of its own, which it receives via a constructor. It implements the Provider interface to define what's returned with complete type safety: ```php use Ray\Di\Di\Inject; use Ray\Di\ProviderInterface; class DatabaseTransactionLogProvider implements ProviderInterface { public function __construct( private readonly ConnectionInterface $connection) ){} public function get() { $transactionLog = new DatabaseTransactionLog; $transactionLog->setConnection($this->connection); return $transactionLog; } } ``` Finally we bind to the provider using the `toProvider()` method: ```php $this->bind(TransactionLogInterface::class)->toProvider(DatabaseTransactionLogProvider::class); ``` ## Injection Point An **InjectionPoint** is a class that has information about an injection point. It provides access to metadata via `\ReflectionParameter` or an attribute in `Provider`. For example, the following `get()` method of `Psr3LoggerProvider` class creates injectable Loggers. The log category of a Logger depends upon the class of the object into which it is injected. ```php class Psr3LoggerProvider implements ProviderInterface { public function __construct( private InjectionPointInterface $ip ){} public function get() { $logger = new \Monolog\Logger($this->ip->getClass()->getName()); $logger->pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING)); return $logger; } } ``` `InjectionPointInterface` provides following methods. ```php $ip->getClass(); // \ReflectionClass $ip->getMethod(); // \ReflectionMethod $ip->getParameter(); // \ReflectionParameter $ip->getQualifiers(); // (array) $qualifierAnnotations ``` ## Instance Bindings You can bind a type to an instance of that type. This is usually only useful for objects that don't have dependencies of their own, such as value objects: ```php $this->bind(UserInterface::class)->toInstance(new User); ``` ```php $this->bind()->annotatedWith('login_id')->toInstance('bear'); ``` Avoid using `toInstance()` with objects that are complicated to create, since it can slow down application startup. ## Constructor Bindings When `#[Inject]` attribute cannot be applied to the target constructor or setter method because it is a third party class, Or you simply don't like to use annotations. `Constructor Binding` provide the solution to this problem. By calling your target constructor explicitly, you don't need reflection and its associated pitfalls. But there are limitations of that approach: manually constructed instances do not participate in AOP. To address this, Ray.Di has `toConstructor` bindings. ```php $this->bind($interfaceName) ->toConstructor( $className, // Class name $name, // Qualifier $injectionPoint, // Setter injection $postConstruct // Initialize method ); (new InjectionPoints) ->addMethod('setGuzzle') // Setter injection method name ->addOptionalMethod('setOptionalToken'); // Optional setter injection method name ``` ### Parameter **class_name** Class name **name** Parameter name binding. If you want to add an identifier to the argument, specify an array with the variable name as the key and the value as the name of the identifier. ``` [ [$param_name1 => $binding_name1], ... ] ``` The following string formats are also supported `'param_name1=binding_name1&...'` **setter_injection** Specify the method name ($methodName) and qualifier ($named) of the setter injector in the `InjectionPoints` object. ```php (new InjectionPoints) ->addMethod($methodName1) ->addMethod($methodName2, $named) ->addOptionalMethod($methodName, $named); ``` **postConstruct** Ray.Di will invoke that constructor and setter method to satisfy the binding and invoke in `$postConstruct` method after all dependencies are injected. ### PDO Example Here is the example for the native [PDO](http://php.net/manual/ja/pdo.construct.php) class. ```php public PDO::__construct ( string $dsn [, string $username [, string $password [, array $options ]]] ) ``` ```php $this->bind(\PDO::class)->toConstructor( \PDO::class, [ 'dsn' => 'pdo_dsn', 'username' => 'pdo_username', 'password' => 'pdo_password' ] )->in(Scope::SINGLETON); $this->bind()->annotatedWith('pdo_dsn')->toInstance($dsn); $this->bind()->annotatedWith('pdo_username')->toInstance(getenv('db_user')); $this->bind()->annotatedWith('pdo_password')->toInstance(getenv('db_password')); ``` Since no argument of PDO has a type, it binds with the `Name Binding` of the second argument of the `toConstructor()` method. In the above example, the variable `username` is given the identifier `pdo_username`, and `toInstance` binds the value of the environment variable. # Aspect Oriented Programing _Intercepting methods with Ray.Di_ To complement dependency injection, Ray.Di supports *method interception*. This feature enables you to write code that is executed each time a _matching_ method is invoked. It's suited for cross cutting concerns ("aspects"), such as transactions, security and logging. Because interceptors divide a problem into aspects rather than objects, their use is called Aspect Oriented Programming (AOP). [Matcher](https://github.com/ray-di/Ray.Aop/blob/2.x/src/MatcherInterface.php) is a simple interface that either accepts or rejects a value. For Ray.Di AOP, you need two matchers: one that defines which classes participate, and another for the methods of those classes. [MethodInterceptors](https://github.com/ray-di/Ray.Aop/blob/2.x/src/MethodInterceptor.php) are executed whenever a matching method is invoked. They have the opportunity to inspect the call: the method, its arguments, and the receiving instance. They can perform their cross-cutting logic and then delegate to the underlying method. Finally, they may inspect the return value or exception and return. Since interceptors may be applied to many methods and will receive many calls, their implementation should be efficient and unintrusive. ## Example: Forbidding method calls on weekends To illustrate how method interceptors work with Ray.Di, we'll forbid calls to our pizza billing system on weekends. The delivery guys only work Monday thru Friday so we'll prevent pizza from being ordered when it can't be delivered! This example is structurally similar to use of AOP for authorization. To mark select methods as weekdays-only, we define an attribute: ```php #[Attribute(Attribute::TARGET_METHOD)] final class NotOnWeekends { } ``` ...and apply it to the methods that need to be intercepted: ```php class BillingService implements BillingServiceInterface { #[NotOnWeekends] public function chargeOrder(PizzaOrder $order, CreditCard $creditCard) { ``` Next, we define the interceptor by implementing the `MethodInterceptor` interface. When we need to call through to the underlying method, we do so by calling `$invocation->proceed()`: ```php use Ray\Aop\MethodInterceptor; use Ray\Aop\MethodInvocation; class WeekendBlocker implements MethodInterceptor { public function invoke(MethodInvocation $invocation) { $today = getdate(); if ($today['weekday'][0] === 'S') { throw new \RuntimeException( $invocation->getMethod()->getName() . " not allowed on weekends!" ); } return $invocation->proceed(); } } ``` Finally, we configure everything. In this case we match any class, but only the methods with our `#[NotOnWeekends]` attribute: ```php use Ray\Di\AbstractModule; class WeekendModule extends AbstractModule { protected function configure() { $this->bind(BillingServiceInterface::class)->to(BillingService::class); $this->bindInterceptor( $this->matcher->any(), // any class $this->matcher->annotatedWith('NotOnWeekends'), // #[NotOnWeekends] attributed method [WeekendBlocker::class] // apply WeekendBlocker interceptor ); } } $injector = new Injector(new WeekendModule); $billing = $injector->getInstance(BillingServiceInterface::class); try { echo $billing->chargeOrder(); } catch (\RuntimeException $e) { echo $e->getMessage() . "\n"; exit(1); } ``` Putting it all together, (and waiting until Saturday), we see the method is intercepted and our order is rejected: ```php RuntimeException: chargeOrder not allowed on weekends! in /apps/pizza/WeekendBlocker.php on line 14 Call Stack: 0.0022 228296 1. {main}() /apps/pizza/main.php:0 0.0054 317424 2. Ray\Aop\Weaver->chargeOrder() /apps/pizza/main.php:14 0.0054 317608 3. Ray\Aop\Weaver->__call() /libs/Ray.Aop/src/Weaver.php:14 0.0055 318384 4. Ray\Aop\ReflectiveMethodInvocation->proceed() /libs/Ray.Aop/src/Weaver.php:68 0.0056 318784 5. Ray\Aop\Sample\WeekendBlocker->invoke() /libs/Ray.Aop/src/ReflectiveMethodInvocation.php:65 ``` ## Disable interceptors To disable the interceptor, bind NullInterceptor. ```php use Ray\Aop\NullInterceptor; protected function configure() { // ... $this->bind(LoggerInterface::class)->to(NullInterceptor::class); } ``` ## Limitations Behind the scenes, method interception is implemented by generating bytecode at runtime. Ray.Di dynamically creates a subclass that applies interceptors by overriding methods. This approach imposes limits on what classes and methods can be intercepted: * Classes must be non-final * Methods must be public * Methods must be non-final * Instances must be created by Ray.Di. ## AOP Alliance The method interceptor API implemented by Ray.Di is mostly same as a public specification called [AOP Alliance in Java](http://aopalliance.sourceforge.net/). # Contextual Provider Bindings You may want to create an object using the context when binding with Provider. For example, you want to inject different connection destinations on the same DB interface. In such a case, we bind it by specifying the context (string) with `toProvider ()`. ```php $dbConfig = ['user' => $userDsn, 'job'=> $jobDsn, 'log' => $logDsn]; $this->bind()->annotatedWith('db_config')->toInstance(dbConfig); $this->bind(Connection::class)->annotatedWith('usr_db')->toProvider(DbalProvider::class, 'user'); $this->bind(Connection::class)->annotatedWith('job_db')->toProvider(DbalProvider::class, 'job'); $this->bind(Connection::class)->annotatedWith('log_db')->toProvider(DbalProvider::class, 'log'); ``` Providers are created for each context. ```php use Ray\Di\Di\Inject; use Ray\Di\Di\Named; class DbalProvider implements ProviderInterface, SetContextInterface { private $dbConfigs; public function setContext($context) { $this->context = $context; } public function __construct( private #[Named('db_config')] array $dbConfigs ){} /** * {@inheritdoc} */ public function get() { $config = $this->dbConfigs[$this->context]; $conn = DriverManager::getConnection($config); return $conn; } } ``` It is the same interface, but you can receive different connections made by `Provider`. ```php public function __construct( #[Named('user')] private readonly Connection $userDb, #[Named('job')] private readonly Connection $jobDb, #[Named('log')] private readonly Connection $logDb ) {} ``` # Injecting Providers With normal dependency injection, each type gets exactly *one instance* of each of its dependent types. The `RealBillingService` gets one `CreditCardProcessor` and one `TransactionLog`. Sometimes you want more than one instance of your dependent types. When this flexibility is necessary, Ray.Di binds a provider. Providers produce a value when the `get()` method is invoked: ```php /** * @template T */ interface ProviderInterface { /** * @return T */ public function get(); } ``` The type provided by the provider is specified by the `#[Set]` attribute. ```php class RealBillingService implements BillingServiceInterface { /** * @param ProviderInterface $processorProvider * @param ProviderInterface $transactionLogProvider */ public function __construct( #[Set(CreditCardProcessorInterface::class)] private ProviderInterface $processorProvider, #[Set(TransactionLogInterface::class)] private ProviderInterface $transactionLogProvider ) {} public function chargeOrder(PizzaOrder $order, CreditCard $creditCard): Receipt { $transactionLog = $this->transactionLogProvider->get(); $processor = $this->processorProvider->get(); /* use the processor and transaction log here */ } } ``` To support generics in static analysis, you need to set `@param` in phpdoc to `ProviderInterface` or `ProviderInterface` and so on. The type of the instance obtained by the `get()` method is specified and checked by static analysis. ## Providers for multiple instances Use providers when you need multiple instances of the same type. Suppose your application saves a summary entry and a details when a pizza charge fails. With providers, you can get a new entry whenever you need one: ```php class LogFileTransactionLog implements TransactionLogInterface { public function __construct( #[Set(TransactionLogInterface::class)] private readonly ProviderInterface $logFileProvider ) {} public function logChargeResult(ChargeResult $result): void { $summaryEntry = $this->logFileProvider->get(); $summaryEntry->setText("Charge " + (result.wasSuccessful() ? "success" : "failure")); $summaryEntry->save(); if (! $result->wasSuccessful()) { $detailEntry = $this->logFileProvider->get(); $detailEntry->setText("Failure result: " + result); $detailEntry->save(); } } } ``` ## Providers for lazy loading If you've got a dependency on a type that is particularly *expensive to produce*, you can use providers to defer that work. This is especially useful when you don't always need the dependency: ```php class LogFileTransactionLog implements TransactionLogInterface { public function __construct( #[Set(Connection::class)] private ProviderInterface $connectionProvider ) {} public function logChargeResult(ChargeResult $result) { /* only write failed charges to the database */ if (! $result->wasSuccessful()) { $connection = $connectionProvider->get(); } } ``` ## Providers for Mixing Scopes Directly injecting an object with a _narrower_ scope usually causes unintended behavior in your application. In the example below, suppose you have a singleton `ConsoleTransactionLog` that depends on the request-scoped current user. If you were to inject the user directly into the `ConsoleTransactionLog` constructor, the user would only be evaluated once for the lifetime of the application. This behavior isn't correct because the user changes from request to request. Instead, you should use a Provider. Since Providers produce values on-demand, they enable you to mix scopes safely: ```php class ConsoleTransactionLog implements TransactionLogInterface { public function __construct( #[Set(User::class)] private readonly ProviderInterface $userProvider ) {} public function logConnectException(UnreachableException $e): void { $user = $this->userProvider->get(); echo "Connection failed for " . $user . ": " . $e->getMessage(); } } ``` # Multibindings _Overview of Multibinder, MapBinder_ Multibinder is intended for plugin-type architectures. ## Multibinding Using `Multibinder` to host plugins. ### Multibinder Multibindings make it easy to support plugins in your application. Made popular by [IDEs](https://plugins.jetbrains.com/phpstorm) and [browsers](https://chrome.google.com/webstore/category/extensions), this pattern exposes APIs for extending the behaviour of an application. Neither the plugin consumer nor the plugin author need write much setup code for extensible applications with Ray.Di. Simply define an interface, bind implementations, and inject sets of implementations! Any module can create a new Multibinder to contribute bindings to a set of implementations. To illustrate, we'll use plugins to summarize ugly URIs like `http://bit.ly/1mzgW1` into something readable on Twitter. First, we define an interface that plugin authors can implement. This is usually an interface that lends itself to several implementations. For this example, we would write a different implementation for each website that we could summarize. ```php interface UriSummarizerInterface { /** * Returns a short summary of the URI, or null if this summarizer doesn't * know how to summarize the URI. */ public function summarize(Uri $uri): string; } ``` Next, we'll get our plugin authors to implement the interface. Here's an implementation that shortens Flickr photo URLs: ```php class FlickrPhotoSummarizer implements UriSummarizerInterface { public function __construct( private readonly PhotoPatternMatcherInterface $matcher ) {} public function summarize(Uri $uri): ?string { $match = $this->matcher->match($uri); if (! $match) { return null; } $id = $this->matcher->group(1); $photo = Photo::lookup($id); return $photo->getTitle(); } } } ``` The plugin author registers their implementation using a multibinder. Some plugins may bind multiple implementations, or implementations of several extension-point interfaces. ```php class FlickrPluginModule extends AbstractModule { public function configure(): void { $uriBinder = Multibinder::newInstance($this, UriSummarizerInterface::class); $uriBinder->addBinding()->to(FlickrPhotoSummarizer::class); // ...bind plugin dependencies, such as our Flickr API key } } ``` Now we can consume the services exposed by our plugins. In this case, we're summarizing tweets: ```php class TweetPrettifier { /** * @param Map $summarizers */ public function __construct( #[Set(UriSummarizerInterface::class)] private readonly Map $summarizers private readonly EmoticonImagifier $emoticonImagifier, ) {} public function prettifyTweet(String tweetMessage): Html { // split out the URIs and call prettifyUri() for each } public function prettifyUri(Uri $uri): string { // loop through the implementations, looking for one that supports this URI foreach ($this->summarizers as $summarizer) { $summary = $summarizer->summarize($uri); if ($summary != null) { return $summary; } } // no summarizer found, just return the URI itself return $uri->toString(); } } ``` _**Note:** The method `Multibinder::newInstance($module, $type)` can be confusing. This operation creates a new binder, but doesn't override any existing bindings. A binder created this way contributes to the existing Set of implementations for that type. It would create a new set only if one is not already bound._ Finally we must register the plugins themselves. The simplest mechanism to do so is to list them programatically: ```php class PrettyTweets { public function __invoke(): void { $injector = new Injector( new class extends AbstractModule { protected function configure(): void { $this->install(new TweetModule()); $this->install(new FlickrPluginModule()); $this->install(new GoogleMapsPluginModule()); $this->install(new BitlyPluginModule()); // ... any other plugins } } ); $injector->getInstance(Frontend::class)->start(); } } (new PrettyTweets)(); ``` ### MapBinder You can name the classes you add in the multibinder. ```php class FlickrPluginModule extends AbstractModule { public function configure(): void { $uriBinder = Multibinder::newInstance($this, UriSummarizerInterface::class); $uriBinder->addBinding('flickr')->to(FlickrPhotoSummarizer::class); // ...bind plugin dependencies, such as our Flickr API key } } ``` In the application, you can retrieve a `Map` injected by specifying attributes such as ``#[Set(UriSummarizer::class)]`` with the name as it was when specified by the binding. ```php class TweetPrettifier { /** * @param Map $summarizers */ public function __construct( #[Set(UriSummarizer::class)] private readonly Map $summarizers ) {} public function doSomething(): void { $flickrSummarizer = $this->summarizers['flickr']; assert($flickrSummarizer instanceof FlickrPhotoSummarizer); } } ``` ## Set binding The `setBinding()` method overrides any previous binding. ```php $uriBinder = Multibinder::newInstance($this, UriSummarizerInterface::class); $uriBinder->setBinding('flickr')->to(FlickrPhotoSummarizer::class); ``` ## Map `Map` objects are treated as generics in static analysis. If the injected interface is T, it is written as `Map`. ```php /** @param Map $summarizers **/ ``` ## Annotation Since it is not possible to annotate the argument, annotate the property to be assigned with the same name and annotate the property with `@Set`. ```php class TweetPrettifier { /** @Set(UriSummarizer::class) */ private $summarizers; /** * @param Map $summarizers */ public function __construct(Map $summarizers) { $this->summarizers = $summarizers; } } ``` # Minimize mutability Wherever possible, use constructor injection to create immutable objects. Immutable objects are simple, shareable, and can be composed. Follow this pattern to define your injectable types: ```php class RealPaymentService implements PaymentServiceInterface { public function __construct( private readonly PaymentQueue $paymentQueue, private readonly Notifier $notifier ){} ``` All fields of this class are readonly and initialized by a constructor. ## Injecting methods *Constructor injection* has some limitations: * Injected constructors may not be optional. * It cannot be used unless objects are created by Ray.Di. * Subclasses must call `parent()` with all dependencies. This makes constructor injection cumbersome, especially as the injected base class changes. *Setter injection* is most useful when you need to initialize an instance that is not constructed by Ray.Di. # Inject only direct dependencies Avoid injecting an object only as a means to get at another object. For example, don't inject a `Customer` as a means to get at an `Account`: ```php class ShowBudgets { private readonly Account $account; public function __construct(Customer $customer) { $this->account = $customer->getPurchasingAccount(); } ``` Instead, inject the dependency directly. This makes testing easier; the test case doesn't need to concern itself with the customer. Use an `Provider` class to create the binding for `Account` that uses the binding for `Customer`: ```php class CustomersModule extends AbstractModule { protected function configure() { $this->bind(Account::class)->toProvider(PurchasingAccountProvider::class); } } class PurchasingAccountProvider implements ProviderInterface { public function __construct( private readonly Customer $customer ) {} public function get(): Account { return $this->customer->getPurchasingAccount(); } } ``` By injecting the dependency directly, our code is simpler. ```php class ShowBudgets { public function __construct( private readonly Account $account ) {} ``` # Organize modules by feature, not by class type Group bindings into features. Ideally it should be possible to enable/disable an entire working feature by simply installing or not installing a single module in the injector. For example, don't just make a `FiltersModule` that has bindings for all the classes that implement `Filter` in it, and a `GraphsModule` that has all the classes that implement `Graph`, etc. Instead, try to organize modules by feature, for example an `AuthenticationModule` that authenticates requests made to your server, or a `FooBackendModule` that lets your server make requests to the Foo backend. This principle is also known as "organize modules vertically, not horizontally". # Avoid static state Static state and testability are enemies. Your tests should be fast and free of side-effects. But non-constant values held by static fields are a pain to manage. It's tricky to reliably tear down static singletons that are mocked by tests, and this interferes with other tests. Although *static state* is bad, there's nothing wrong with the static *keyword*. Static classes are okay (preferred even!) and for pure functions (sorting, math, etc.), static is just fine. # Performance boost Injectors that know all dependency bindings can compile simple PHP factory code from those bindings and provide the best performance. Injectors that don't use anonymous functions for bindings can be serialized, which can improve performance. In any case, there is no need to initialize the container for every request in production. ## Script injector `ScriptInjector` generates raw factory code for better performance and to clarify how the instance is created. ```php use Ray\Di\ScriptInjector; use Ray\Compiler\DiCompiler; use Ray\Compiler\Exception\NotCompiled; try { $injector = new ScriptInjector($tmpDir); $instance = $injector->getInstance(ListerInterface::class); } catch (NotCompiled $e) { $compiler = new DiCompiler(new ListerModule, $tmpDir); $compiler->compile(); $instance = $injector->getInstance(ListerInterface::class); } ``` Once an instance has been created, You can view the generated factory files in `$tmpDir` ## Cache injector The injector is serializable. It also boosts the performance. ```php // save $injector = new Injector(new ListerModule); $cachedInjector = serialize($injector); // load $injector = unserialize($cachedInjector); $lister = $injector->getInstance(ListerInterface::class); ``` ## CachedInjectorFactory The `CachedInjectorFactory` can be used in a hybrid of the two injectors to achieve the best performance in both development and production. The injector is able to inject singleton objects **beyond the request**, greatly increasing the speed of testing. Successive PDO connections also do not run out of connection resources in the test. See [CachedInjectorFactory](https://github.com/ray-di/Ray.Compiler/issues/75) for more information. ## Attribute Reader When not using Doctrine annotations, you can improve performance during development by using only PHP8 attribute readers. Register it as an autoloader in the `composer.json` ```json "autoload": { "files": [ "vendor/ray/aop/attribute_reader.php" ] ``` Or set in bootstrap script. ```php declare(strict_types=1); use Koriym\Attributes\AttributeReader; use Ray\ServiceLocator\ServiceLocator; ServiceLocator::setReader(new AttributeReader()); ``` ## Frameworks integration * [BEAR.Sunday](http://bearsunday.github.io/) * [CakePHP 3/4 PipingBag](https://github.com/lorenzo/piping-bag) by [@jose_zap](https://twitter.com/jose_zap) * [Yii 1](https://github.com/koriym/Ray.Dyii) * [Laravel](https://github.com/ray-di/Ray.RayDiForLaravel) # Backward Compatibility We will not break backward compatibility. Ray.Di 2.0 was first released in 2015 and since then we've been supporting the latest PHP and adding features; we may no longer support PHP that has become deprecated, but we have never broken backwards compatibility, and we plan to continue to do so. # Ray.Di Best Practices * [Minimize mutability](bp/minimize_mutability.html) * [Inject only direct dependencies](bp/inject_only_direct_dependencies.html) * [Use the Injector as little as possible (preferably only once)](bp/injecting_the_injector.html) * Avoid cyclic dependencies * [Avoid static state](bp/avoid_static_state.html) * [Modules should be fast and side-effect free](bp/modules_should_be_fast_and_side_effect_free.html) * [Avoid conditional logic in modules](bp/avoid_conditional_logic_in_modules.html) * [Don't reuse binding attributes (aka `#[Qualifiers]`)](bp/dont_reuse_annotations.html) * [Organize modules by feature, not by class type](bp/organize_modules_by_feature.html) * [Document the public bindings provided by modules](bp/document_public_bindings.html) ## Graphing Ray.Di Applications When you've written a sophisticated application, Ray.Di rich introspection API can describe the object graph in detail. The object-visual-grapher exposes this data as an easily understandable visualization. It can show the bindings and dependencies from several classes in a complex application in a unified diagram. ### Generating a .dot file Ray.Di's grapher leans heavily on [GraphViz](http://www.graphviz.org/), an open source graph visualization package. It cleanly separates graph specification from visualization and layout. To produce a graph `.dot` file for an `Injector`, you can use the following code: ```php use Ray\ObjectGrapher\ObjectGrapher; $dot = (new ObjectGrapher)(new FooModule); file_put_contents('path/to/graph.dot', $dot); ``` ### The .dot file Executing the code above produces a `.dot` file that specifies a graph. Each entry in the file represents either a node or an edge in the graph. Here's a sample `.dot` file: ```dot digraph injector { graph [rankdir=TB]; dependency_BEAR_Resource_ResourceInterface_ [style=dashed, margin=0.02, label=<
BEAR\\Resource\\ResourceInterface
>, shape=box] dependency_BEAR_Resource_FactoryInterface_ [style=dashed, margin=0.02, label=<
BEAR\\Resource\\FactoryInterface
>, shape=box] dependency_BEAR_Resource_ResourceInterface_ -> class_BEAR_Resource_Resource [style=dashed, arrowtail=none, arrowhead=onormal] dependency_BEAR_Resource_FactoryInterface_ -> class_BEAR_Resource_Factory [style=dashed, arrowtail=none, arrowhead=onormal] } ``` ### Rendering the .dot file You can then paste that code into [GraphvizOnline](https://dreampuf.github.io/GraphvizOnline/)to render it. On Linux, you can use the command-line `dot` tool to convert `.dot` files into images. ```shell dot -T png graph.dot > graph.png ``` ![graph](https://user-images.githubusercontent.com/529021/72650686-866ec100-39c4-11ea-8b49-2d86d991dc6d.png) #### Graph display Edges: * **Solid edges** represent dependencies from implementations to the types they depend on. * **Dashed edges** represent bindings from types to their implementations. * **Double arrows** indicate that the binding or dependency is to a `Provider`. Nodes: * Implementation types are given *black backgrounds*. * Implementation instances have *gray backgrounds*. ## Binding Attributes Occasionally you'll want multiple bindings for a same type. For example, you might want both a PayPal credit card processor and a Google Checkout processor. To enable this, bindings support an optional binding attribute. The attribute and type together uniquely identify a binding. This pair is called a key. ### Defining binding attributes Define qualifier attribute first. It needs to be annotated with `Qualifier` attribute. ```php use Ray\Di\Di\Qualifier; #[Attribute, Qualifier] final class PayPal { } ``` To depend on the annotated binding, apply the attribute to the injected parameter: ```php public function __construct( #[Paypal] private readonly CreditCardProcessorInterface $processor ){} ``` You can specify parameter name with qualifier. Qualifier applied all parameters without it. ```php public function __construct( #[Paypal('processor')] private readonly CreditCardProcessorInterface $processor ){} ``` Lastly we create a binding that uses the attribute. This uses the optional `annotatedWith` clause in the bind() statement: ```php $this->bind(CreditCardProcessorInterface::class) ->annotatedWith(PayPal::class) ->to(PayPalCreditCardProcessor::class); ``` ### Binding Attributes in Setters In order to make your custom `Qualifier` attribute inject dependencies by default in any method the attribute is added, you need to implement the `Ray\Di\Di\InjectInterface`: ```php use Ray\Di\Di\InjectInterface; use Ray\Di\Di\Qualifier; #[Attribute, Qualifier] final class PaymentProcessorInject implements InjectInterface { public function isOptional() { return $this->optional; } public function __construct( public readonly bool $optional = true public readonly string $type; ){} } ``` The interface requires that you implement the `isOptional()` method. It will be used to determine whether or not the injection should be performed based on whether there is a known binding for it. Now that you have created your custom injector attribute, you can use it on any method. ```php #[PaymentProcessorInject(type: 'paypal')] public setPaymentProcessor(CreditCardProcessorInterface $processor) { .... } ``` Finally, you can bind the interface to an implementation by using your new annotated information: ```php $this->bind(CreditCardProcessorInterface::class) ->annotatedWith(PaymentProcessorInject::class) ->toProvider(PaymentProcessorProvider::class); ``` The provider can now use the information supplied in the qualifier attribute in order to instantiate the most appropriate class. ## #[Named] The most common use of a Qualifier attribute is tagging arguments in a function with a certain label, the label can be used in the bindings in order to select the right class to be instantiated. For those cases, Ray.Di comes with a built-in binding attribute `#[Named]` that takes a string. ```php use Ray\Di\Di\Inject; use Ray\Di\Di\Named; public function __construct( #[Named('checkout')] private CreditCardProcessorInterface $processor ){} ``` To bind a specific name, pass that string using the `annotatedWith()` method. ```php $this->bind(CreditCardProcessorInterface::class) ->annotatedWith('checkout') ->to(CheckoutCreditCardProcessor::class); ``` Add the `#[Named]` attribute to the parameter you want to qualify. ```php use Ray\Di\Di\Inject; use Ray\Di\Di\Named; public function __construct( #[Named('checkout')] private CreditCardProcessorInterface $processor, #[Named('backup')] private CreditCardProcessorInterface $subProcessor ){} ``` ## Binding Annotation Ray.Di can be used with [doctrine/annotation](https://github.com/doctrine/annotations) for PHP 7.x. See the old [README(v2.10)](https://github.com/ray-di/Ray.Di/tree/2.10.5/README.md) for annotation code examples. To create forward-compatible annotations for attributes, see [custom annotation classes](https://github.com/kerveros12v/sacinta4/blob/e976c143b3b7d42497334e76c00fdf 38717af98e/vendor/doctrine/annotations/docs/en/custom.rst#optional-constructors-with-named-parameters). Since annotations cannot be applied to arguments, the first argument of a custom annotation should be the name of the variable. This is not necessary if the method has only one argument. ```php /** * @Paypal('processor') */ public function setCreditCardProcessor( CreditCardProcessorInterface $processor OtherDependencyInterface $dependency ){ ``` # Object Life Cycle `#[PostConstruct]` is used on methods that need to get executed after dependency injection has finalized to perform any extra initialization. ```php use Ray\Di\Di\PostConstruct; ``` ```php #[PostConstruct] public function init() { //.... } ``` # Injections _How Ray.Di initializes your objects_ The dependency injection pattern separates behaviour from dependency resolution. Rather than looking up dependencies directly or from factories, the pattern recommends that dependencies are passed in. The process of setting dependencies into an object is called *injection*. ## Constructor Injection Constructor injection combines instantiation with injection. This constructor should accept class dependencies as parameters. Most constructors will then assign the parameters to properties. You do not need `#[Inject]` attribute in constructor. ```php public function __construct(DbInterface $db) { $this->db = $db; } ``` ## Setter Injection Ray.Di can inject by methods that have the `#[Inject]` attribute. Dependencies take the form of parameters, which the injector resolves before invoking the method. Injected methods may have any number of parameters, and the method name does not impact injection. ```php use Ray\Di\Di\Inject; ``` ```php #[Inject] public function setDb(DbInterface $db) { $this->db = $db; } ``` ## Property Injection Ray.Di does not support property injection. ## Assisted Injection Also called method-call injection action injection, or Invocation injection.It is also possible to inject dependencies directly in the invoke method parameter(s). When doing this, add the dependency to the end of the arguments and add `#[Assisted]` to the parameter(s). You need `null` default for that parameter. _Note that this Assisted Injection is different from the one in Google Guice._ ```php use Ray\Di\Di\Assisted; ``` ```php public function doSomething(string $id, #[Assisted] DbInterface $db = null) { $this->db = $db; } ``` You can also provide dependency which depends on other dynamic parameter in method invocation. `MethodInvocationProvider` provides [MethodInvocation](https://github.com/ray-di/Ray.Aop/blob/2.x/src/MethodInvocation.php) object. ```php class HorizontalScaleDbProvider implements ProviderInterface { public function __construct( private readonly MethodInvocationProvider $invocationProvider ){} public function get() { $methodInvocation = $this->invocationProvider->get(); [$id] = $methodInvocation->getArguments()->getArrayCopy(); return UserDb::withId($id); // $id for database choice. } } ``` This injection done by AOP is powerful and useful for injecting objects that are only determined at method execution time, as described above. However, this injection is outside the scope of the original IOC and should only be used when really necessary. ## Optional Injections Occasionally it's convenient to use a dependency when it exists and to fall back to a default when it doesn't. Method and field injections may be optional, which causes Ray.Di to silently ignore them when the dependencies aren't available. To use optional injection, apply the `#[Inject(optional: true)]`attribute: ```php class PayPalCreditCardProcessor implements CreditCardProcessorInterface { private const SANDBOX_API_KEY = "development-use-only"; private string $apiKey = self::SANDBOX_API_KEY; #[Inject(optional: true)] public function setApiKey(#[Named('paypal-apikey')] string $apiKey): void { $this->apiKey = $apiKey; } } ```