jschreuder / middle-di
A typehintable dependency injection container.
Requires
- php: >=8.0
Requires (Dev)
This package is auto-updated.
Last update: 2024-12-09 02:58:54 UTC
README
A PHP Dependency Injection Container library meant to allow service and factory definitions in native PHP as to allow type-hinting, automated checks and code completion. It does this by rewriting a class on-the-fly and ensuring that service definitions will always return the same instance.
It aside from the core functionality it also allows for caching the generated code in the filesystem, thus allowing OP-cache to do its work in production. And it als includes a bit of logic for configuration to be passed in.
Conventions
All service definitions should be public methods starting with "get", and all factories should start with "new". Currently only the service defintions are enforced, additional rules include: it may only take a string name (see below) as parameter and must have a singular returntype that is either a class or an interface.
<?php use jschreuder\MiddleDi\DiCompiler; class Container { // This will be rewritten to always return the same object public function getDatabase(): PDO { return new PDO(); } // A service using the database public function getService(): Service { return new Service($this->getDatabase()); } // This will be left alone and work as defined public function newObject(): stdClass { return new stdClass(); } } // Create the compiled container $dic = (new DiCompiler(Container::class))->compile()->newInstance(); // these will all be true var_dump( $dic instanceof Container, $dic->getDatabase() === $dic->getDatabase(), $dic->getService() === $dic->getService(), $dic->newObject() !== $dic->newObject() );
Using different named instances of a service
Even though services are expected to be single instances, using multiple instances of a service is allowed when naming them. A first parameter is supported which will be treated as its name and can be used to differentiate in its configuration.
<?php class Container { public function getDatabase(): PDO { return new PDO(); } // This will return different instances for different names public function getTableService(string $name): TableService { return new TableService($name, $this->getDatabase()); } }
Make containers cached in filesystem in production
To allow op-caching to work and make things as fast as possible in production environment the compiled container can be cached to a file.
<?php use jschreuder\MiddleDi\DiCompiler; use jschreuder\MiddleDi\DiCachedCompiler; class Container {} // Create the compiled container $compiler = (new DiCachedCompiler( new DiCompiler(Container::class), new SplFileObject('path/to/cachefile.php', 'c'); ))->compile(); $dic = $compiler->newInstance();
Add configuration
There's a simple configuration helper included with the ConfigTrait
. This
adds a constructor that takes either an array or an ArrayAccess instance and
allows requesting configuration items using the config() method. It will throw
an exception when a non-existant configuration item is requested.
<?php use jschreuder\MiddleDi\DiCompiler; class Container { use jschreuder\MiddleDi\ConfigTrait; // This will be rewritten to always return the same object public function getDatabase(): PDO { return new PDO($this->config('db.dsn')); } } // Create the compiled container $config = ['dsn' => 'example:dsn']; $dic = (new DiCompiler(Container::class))->compile()->newInstance($config);