Dependency Injection
IoC Container
IoC Container should be initialized before running the application. In public/index.php
, you will see:
$container = new Rise\Container();
Then the container is set up for dependency injection by autowiring. Dependencies will be injected according to the type hinting provided in the parameters of constructor.
After setting up the container, an entry point is needed to activate dependency injection.
Rise\Container::get(string $class)
is used to resolve a class from container, e.g.:
$app = $container->get(Rise\Application::class);
This will initialized the application and its dependencies.
Constructor Injection
When constructing an instance of a class, the container will resolve and inject an instance of dependency class to the constructor. Let's have an example.
class Bike {
private $distance = 0;
public function move($distance) {
$this->distance += $distance;
}
}
class Person {
public function __construct(Bike $bike) {
$this->bike = $bike;
}
public function ride($distance) {
$this->bike->move($distance);
}
}
$container = new Rise\Container();
$person = $container->get(Person::class);
$person->ride(100);
Before constructing the Person
instance, the container will first resolve the Bike
instance, then the Bike
instance will be injected when constructing
the Person
instance. User does not need to worry about the dependencies of the class, as the container will resolve them automatically.
Method Injection
Method injection will have a limited usage in this framework, it is only allowed in handlers and middlewares.
Handlers / Middlewares
In handlers or middlewares, you can inject dependencies directly in a method. e.g.:use Rise\Response;
class Handler {
public function showText(Response $response) {
$response->setHeader('Content-Type', 'text/plain')
->setBody('Some text');
}
}
Injection Rules
Global bindings
Rise\Container::bind(string $class, string $to)
can be used to bind a class or interface to another class.
interface Fruit {}
class Apple implements Fruit {}
$container = new Rise\Container();
$container->bind(Fruit::class, Apple::class);
$apple = $container->get(Fruit::class); // An instance of apple.
Specific bindings
Rise\Container::configClass(string $class, array $rules)
can be used if you want to configure some rules for dependency injection of a class.
interface Fruit {}
class Apple implements Fruit {}
class Blackberry implements Fruit {}
class Alan {
public function __construct(Fruit $fruit) {
$this->fruit = $fruit;
}
}
class Bosco {
public function __construct(Fruit $fruit) {
$this->fruit = $fruit;
}
}
$container = new Rise\Container();
// When constructing an instance of Alan, resolve an instance of
// Apple as the constructor argument.
$container->configClass(Alan::class, [
Fruit::class => Apple::class
]);
// When constructing an instance of Bosco, resolve an instance of
// Blackberry as the constructor argument.
$container->configClass(Bosco::class, [
Fruit::class => Blackberry::class
]);