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
]);