pmjones / caplet
A minimal PSR-11 compliant autowiring dependency injection container.
Requires
- php: ^8.0
- psr/container: ^2.0
Requires (Dev)
- pds/composer-script-names: ^1.0
- pds/skeleton: ^1.0
- phpstan/phpstan: ^1.0
- phpunit/phpunit: ^9.0
Provides
- psr/container-implementation: 2.0.0
README
Caplet is a minimal autowiring dependency injection container to handle basic constructor injection and object factories.
Getting Started
Instantiate Caplet like so:
use Caplet\Caplet; $caplet = new Caplet();
You can then call one of these PSR-11 methods:
-
get(string $class) : object
to get a shared object instance of$class
. -
has(string $class) : bool
to see if an instance is available. (This means either a class definition exists; or, in the case of an interface, the interface definition exists and has afactory()
entry -- see below for thefactory()
method.)
Caplet offers this non-PSR-11 method:
new(string $class) : object
to get a new object instance of$class
. (This method is not part of PSR-11.)
Configuration
Configure non-object constructor arguments by passing an array with the
structure $config['ClassName']['parameterName']
at Caplet construction
time. For example, given the following class ...
namespace Foo; class Bar { public function __construct( protected string $bar, protected string $baz ) { } }
... you would configure the arguments for its parameters like so:
use Caplet\Caplet; use Foo\Bar; $caplet = new Caplet([ Bar::class => [ 'bar' => 'bar-value', 'baz' => 'baz-value', ]; ]); $bar = $caplet->get(Bar::class);
Alternatively, extend Caplet and override __construct()
to accept your own
environment or configuration values, then call the parent::__construct()
with
the $config['ClassName']['parameterName']
structure.
namespace Project; use Caplet\Caplet; class ProjectCaplet extends Caplet { public function __construct(array $env) { parent::__construct([ Foo::class => [ 'bar' => $env['BAR_VALUE'], 'baz' => $env['BAZ_VALUE'], ], ]); } }
Factories
Extending Caplet also allows you to call the protected factory()
method
inside the constructor to define the object-creation logic for a given type.
This allows you to specify concrete classes for instantiation in place of
abstracts or interfaces. For example:
namespace Project; use Caplet\Caplet; use Project\Log\Logger; use Psr\Log\LoggerInterface; class ProjectCaplet extends Caplet { public function __construct( string $bar, string $baz, ) { parent::__construct([ Foo::class => [ 'bar' => 'bar-value', 'baz' => 'baz-value', ], ]); $this->factory( LoggerInterface::class, fn (Caplet $caplet) => $caplet->get(Logger::class) ); } }
As seen above, the callable factory logic must have the signature
function (Caplet $caplet)
, and may specify a return type.
Constructor Parameter Resolution
Caplet will attempt to resolve constructor parameters in this order:
- First, use an argument from $config, if one is available.
- Next, try to
get()
an object of the parameter type. - Last, use the default parameter value, if one is defined.
If none of these work, Caplet will throw Exception\NotInstantiated, with a previous exception of Exception\NotResolved.