koded / container
A simple Dependency Injection Container with application modules support
Requires
- php: ^8.1
- psr/container: ^2
Requires (Dev)
- ext-json: *
- ext-pdo: *
- infection/infection: ^0.26
- phpbench/phpbench: ^1
- phpunit/phpunit: ^9
Provides
README
koded/container
is an OOP application bootstrapping and wiring library.
In other words, Koded\DIContainer
implements a design pattern called Dependency Inversion.
The main principle of DIP is to separate the behavior from dependency resolution.
composer require koded/container
Example
Let's look at a blog application that has
- interfaces for the database repositories and corresponding implementations
- a shared PDO instance
- a service class for the blog content fetching
- a handler class that maps the request method
use PDO; interface PostRepository { public function findBySlug(string $slug); } interface UserRepository { public function findById(int $id); } class DatabasePostRepository implements PostRepository { private $pdo; public function __construct(PDO $pdo) { $this->pdo = $pdo; } public function findBySlug(string $slug) { // $this->pdo ... } } class DatabaseUserRepository implements UserRepository { private $pdo; public function __construct(PDO $pdo) { $this->pdo = $pdo; } public function findById(int $id) { // $this->pdo ... } }
Somewhere we may have a service class that uses the dependent repositories:
class PostService { private $post, $user; public function __construct(PostRepository $post, UserRepository $user) { $this->post = $post; $this->user = $user; } // a service method that uses the post and user repository instances public function findBlogPostBySlug(string $slug) { $post = $this->post->findBySlug($slug); $user = $this->user->findById($post->userId()); // ... do something with the results, create a result data structure... } }
Then somewhere we might have a handler/controller that asks for its own dependencies:
class BlogHandler { public function get(ServerRequestInterface $request, PostService $service): ResponseInterface { $slug = slugify($request->getUri()->getPath()); $post = $service->findBlogPostBySlug($slug); // some PSR-7 compatible response object return new ServerResponse($post); } }
Wire All The Things
This is the bootstrapping / wiring application module (or container's "configuration" class) where all known dependencies are binded and shared
class BlogModule implements DIModule { public function configure(DIContainer $container): void { // bind interfaces to concrete class implementations $container->bind(PostRepository::class, DatabasePostRepository::class); $container->bind(UserRepository::class, DatabaseUserRepository::class); $container->bind(ServerRequestInterface::class, /*some PSR-7 server request class name*/); // share one PDO instance $container->singleton(PDO::class, ['sqlite:database.db']); } }
And finally in the dispatcher file, we process the request
// index.php // (resolved through an HTTP router or other means) $handler = BlogHandler::class; $method = 'get'; // by invoking the container $response = (new DIContainer(new BlogModule))([$handler, $method]); // we have a `$response` object to output the content // ex. `echo $response->getBody()->getContents();`
The container implements the __invoke() method, so the instance can be used as a function:
$container('method', ['arg1', 'arg2', ...]);
To be continued...
Code quality
vendor/bin/infection --threads=4 vendor/bin/phpbench run --report=default vendor/bin/phpunit
License
The code is distributed under the terms of The 3-Clause BSD license.