fas / di
Fast and simple dependency injection
0.4.5
2021-07-17 06:27 UTC
Requires
- php: >=7.4.0
- fas/autowire: ^0.2
- fas/exportable: ^0.2
- ocramius/proxy-manager: ^2.11
- opis/closure: ^3.6
- psr/container: ^1.0 || ^2.0
- psr/log: ^1.1
Requires (Dev)
- phpmd/phpmd: ^2.10
- phpunit/phpunit: ^9
- squizlabs/php_codesniffer: ^3.6
Provides
README
Installation
composer require fas/di
Usage
Creating container
// Brand new container $container = new Container; // Load compiled container if present $container = Container::load('/tmp/container.php');
Adding dependencies
// ->set(entryName, entryName | callback | null) // (singleton by default) $container->set(LoggerInterface::class, 'some_container_entry'); // abstract factory $container->set(LoggerInterface::class, 'some_container_entry')->factory(); // lazy $container->set(LoggerInterface::class, 'some_container_entry')->lazy(LoggerInterface::class); // lazy abstract factory $container->set(LoggerInterface::class, 'some_container_entry') ->lazy(LoggerInterface::class) ->factory(); // shorthands $container->singleton(LoggerInterface::class, 'some_container_entry'); $container->factory(LoggerInterface::class, 'some_container_entry'); $container->lazy(LoggerInterface::class, 'some_container_entry'); // If entry MyLogger::class does not exist in the container, // The class MyLogger::class will be instantiated. $container->singleton(LoggerInterface::class, MyLogger::class); // Custom factory method $container->singleton(LoggerInterface::class, function () { return new MyLogger; }); // Any callable will do $container->singleton(LoggerInterface::class, [MyLoggerFactory::class, 'create']);
Abstract factories. Will be resolved on every ->get()
// ->factory(entryName, entryName | callback | null) $container->factory(MyLogger::class); // abstract factory shorthand $logger1 = $container->get(MyLogger::class); // will create new object $logger2 = $container->get(MyLogger::class); // will create new object
Lazy. Will not be resolved before used. (virtual proxy)
// ->lazy(entryName, entryName | callback | null) $container->lazy(MyLogger::class); // Lazy shorthand // make anything lazy $container->set('myentry', [MyFactory::class, 'create']) ->lazy(LoggerInterface::class);
Mix and match as you please:
$container->factory(MyLogger::class)->lazy();
equivalent to:
$container->lazy(MyLogger::class)->factory();
Performance
// Use cached virtual proxies (lazy), and write cache if missing $container->enableProxyCache('/tmp/proxies'); // Generate a class containing proper methods for registered entries. // This can be used afterwards to avoid a lot of reflection when resolving entries. $container->save('/tmp/container.php');
Recipies
Full automatic cached container and proxies
The easist way to make use of caching.
Be aware, that once a cache has been written, it has to be manually deleted in order to be renewed. This setup is usually not very useful in development.
$container = Container::load("/tmp/container.php"); if (!$container) { $container = new Container; $container->singleton(LoggerInterface::class, MyLogger::class); $container->save('/tmp/container.php'); } $container->enableProxyCache("/tmp/proxies"); // Container ready for use
Generate compiled container
This can be used in a compile script during startup of build phase.
$container = new Container; // ... populate container here $entries = $container->save("/tmp/container.php"); print "-----------------\nCompiled " . count($entries) . " entries\n-----------------\n"; print implode("\n", $entries) . "\n\n";
Generic example
Generic example using configuration, container and router.
<?php require __DIR__ . '/../vendor/autoload.php'; use App\ContainerFactory; use App\RouterFactory; use Fas\DI\Container; use Fas\Configuration\DotNotation; use Fas\Configuration\YamlLoader; use Fas\Routing\Router; use Laminas\Diactoros\ResponseFactory; use Laminas\Diactoros\ServerRequestFactory; $configFile = getenv('CONFIG_FILE') ?: '/app/config.yaml'; try { // Setup configuration, container and router $configuration = FileCache::load('/tmp/config.cache.php') ?? new DotNotation(YamlLoader::loadWithOverrides($configFile)); $container = Container::load('/tmp/container.cache.php') ?? ContainerFactory::create($configuration); $router = Router::load('/tmp/router.cache.php') ?? RouterFactory::create($container); // Handle actual request $request = ServerRequestFactory::fromGlobals(); $response = $router->handle($request); } catch (Throwable $e) { $code = $e instanceof HttpException ? $e->getCode() : 500; $response = (new ResponseFactory)->createResponse($code, $e->getMessage()); $response->getBody()->write('<pre>' . (string) $e . '</pre>'); } finally { (new SapiEmitter)->emit($response); }
Generic compiler example
Compiler example for the example above
<?php require __DIR__ . '/../vendor/autoload.php'; use App\ContainerFactory; use App\RouterFactory; use Fas\DI\Container; use Fas\Configuration\DotNotation; use Fas\Configuration\YamlLoader; use Fas\Routing\Router; use Laminas\Diactoros\ResponseFactory; use Laminas\Diactoros\ServerRequestFactory; $configFile = getenv('CONFIG_FILE') ?: '/app/config.yaml'; // Compile config $configuration = new DotNotation(YamlLoader::loadWithOverrides($configFile)); FileCache::save('/tmp/config.cache.php', $configuration); // Compile container $container = ContainerFactory::create($configuration); $entries = $container->save('/tmp/container.cache.php'); print "-----------------\nBuilt " . count($entries) . " entries\n-----------------\n" . implode("\n", $entries) . "\n-----------------\n"; // Compile routes $router = RouterFactory::create($container); $router->save('/tmp/router.cache.php');