Although I really enjoy the concept of bindings implemented in ICanBoogie, I recognize it can be challenging when your application has thousands of dependencies. Also, as I'm implementing a message bus for ICanBoogie, I recognize the value of a dependency injection container to instantiate all the lovely message handlers, and because I really like Symfony's Dependency Injection component, I decided to use it for this first implementation, trying to create something as transparent and flexible as possible.
References
ICanBoogie favors functional programming to interfaces in many places, and uses callables for a lot of things such as controllers, event hooks, prototype methods, operations, proxy… Thus, I had the idea to create service references as callables, as they would be used without requiring any code change. They are very simple things really, they obtain the service they reference through a service provider, pass the arguments, and return the result.
The following example demonstrates how to reference a service, and invoke it with some parameters.
<?php
use function ICanBoogie\Service\ref;
$reference = ref('hello');
echo $reference("Olivier");
// Hello Olivier!
Of course, non-callable services can also be referenced, in which case the method resolve()
is
used to obtain the service:
<?php
use function ICanBoogie\Service\ref;
$reference = ref('my_non_callable_service');
$service = $reference->resolve();
$service->do_something();
# The reference forwards calls to the service, but that's a secret
$reference->do_something();
The service provider
The service provider is created during the application boot event, and acts as a proxy for the
dependency injection container, which is only created when a reference needs to be resolved, or when
the container is explicitly required through $app->container
or
ServiceProvider::defined()->container
.
Obtaining services bound to the application
Usually, ICanBoogie's components add getters to the ICanBoogie\Application
instance through the
prototype system, which means the initial request is accessed with $app->initial_request
, and
the session with $app->session
. In order to have these available to the container, an extension
automatically add definitions for them. Now the session can also be obtained with
ref('session')->resolve()
.
Defining services
Services are defined using services.yml
files in config
folders. They are collected when it's time
to create the container, just like regular configuration files.
The following example is a sample of the services defined for my blog. You'll notice the renderer
service, which is actually bound to the ICanBoogie\Application
instance:
services:
# Event
event_hook.on_rescue_exception:
class: App\Application\Handler\RescueExceptionHandler
arguments:
- "@renderer"
# Controller
controller.page:
class: App\Presentation\Controller\PageController
tl;dr
Services can be defined using Symfony's Dependency Injection component and only super sexy
services are bound to the ICanBoogie\Application
instance.
You can check icanboogie/service and icanboogie/bind-symfony-dependency-injection packages for more information on this amazing and exciting new feature.