Most dependency injection containers work through public configuration, are intended for use at the application level, and use "stringly"-typed retrieval methods.
Capsule, on the other hand, is intended for use at the library, module, component, or package level; has no public methods for configuration; and encourages typehinted retrieval methods.
This means that you
composer require capsule/di ~1.0
into your package, extend the Container
for your library or module,
and __construct()
your component objects inside that extended container. You
then add typehinted public methods for object retrieval.
The following container provides a shared instance of a hypothetical data mapper using a shared instance of PDO.
<?php
class MyCapsule extends \Capsule\Di\Container
{
public function __construct(array $env = [])
{
$this->setEnv($env);
$this->provide(PDO::CLASS)->args(
$this->env('DB_DSN'),
$this->env('DB_USERNAME'),
$this->env('DB_PASSWORD')
);
$this->provide(MyDataMapper::CLASS)->args(
$this->service(PDO::CLASS)
);
}
public function getMyDataMapper() : MyDataMapper
{
return $this->serviceInstance(MyDataMapper::CLASS);
}
}
class MyDataMapper
{
public function __construct(PDO $pdo) { ... }
}
$capsule = new MyCapsule([
'DB_DSN' => 'mysql:host=localhost;dbname=mydb',
'DB_USERNAME' => 'my_user',
'DB_PASSWORD' => 'my_pass',
]);
$mapper = $capsule->getMyDataMapper(); // instanceof MyDataMapper
Call these methods within __construct()
to configure the container. Note that
they are all protected; they cannot be called from outside the container.
Returns a Config object (see below) so you can set the default constructor arguments, post-instantiation method calls, and instantiation factory for a particular class. All object instantiations will use this configuration by default.
<?php
public function __construct()
{
// ...
$this->default(Foo::CLASS)->args(
'foo',
'bar',
'baz',
);
}
Use this to provide a shared instance of a class that will be reused after it is instantiated.
By default, the provide()
method presumes the service $id
is a class name,
and the shared instance is registered under that class name.
The returned LazyNew object extends Config, so you can further configure the object prior to its instantiation, overriding the class defaults.
<?php
public function __construct()
{
// ...
// uses default arguments
$this->provide(Foo::CLASS);
// overrides default arguments
$this->provide(Bar::CLASS)->args(
'zim',
'dib',
'gir',
);
}
Use this to provide a shared instance of a class that will be reused after it is
instantiated. The shared instance is registered under the $id
, which may be
any class name or any other identifying string.
If you pass a $lazy
argument to provide()
, the $lazy
will be used to
create the service instance, in which case the $id
may or may not match the
class of the lazy-loaded instance. When passing an explicit $lazy
, provide()
will return null
.
Use this to specify that a dependency should be a shared service instance
registered using provide()
. (The service instance may not be defined yet.)
<?php
public function __construct()
{
// ...
$this->default(Foo::CLASS)->args(
$this->service(Bar::CLASS)
);
}
Use this to specify that a dependency should be the result of a method call to a shared service instance.
<?php
public function __construct()
{
// ...
// provide a MapperLocator
$this->provide(MapperLocator::CLASS);
// for the Wiki application service, get the WikiMapper
// out of the MapperLocator
$this->default(WikiService::CLASS)->args(
$this->serviceCall(MapperLocator::CLASS, 'get', WikiMapper::CLASS)
);
}
Use this to specify that a dependency should be a new instance of a class, not a shared instance.
<?php
public function __construct()
{
// ...
$this->default(Service::CLASS)->args(
$this->new(SupportingObject::CLASS)
);
}
The returned LazyNew object extends Config, so you can further configure the object prior to its instantiation, overriding the class defaults.
Use this to specify that a dependency should be the results of invoking a
callable, whether an instance method, static method, or function. (The PHP
keywords include
and require
are also supported.)
<?php
public function __construct()
{
// ...
$this->default(Service::CLASS)->args(
$this->call('include', '/path/to/file.php')
);
}
This returns a Closure that makes a call to a Capsule container method. It is useful for providing new-instance and service-instance callables to other containers, locators, registries, and factories. It will not be invoked as a lazy-loaded dependency at instantiation time.
<?php
public function __construct()
{
// ...
// presuming the the MapperLocator takes an array of callable factories
// as its first argument
$this->default(MapperLocator::CLASS)->args(
[
$this->closure('newInstance', ThreadMapper::CLASS),
$this->closure('newInstance', AuthorMapper::CLASS),
$this->closure('newInstance', ReplyMapper::CLASS),
]
);
}
Returns the value of the container-specific $env[$key]
property (if set), then
the value of getenv($id)
(if not false
), and then null
.
<?php
public function __construct(array $env = [])
{
$this->setEnv($env);
// ...
$this->provide(PDO::CLASS)->args(
$this->env('DB_DSN'),
$this->env('DB_USERNAME'),
$this->env('DB_PASSWORD')
);
}
The above will use $env['DB_DSN']
(et al.) if they are passed to the
constructor, and fall back to getenv('DB_DSN')
if they are not.
To set or replace all container-specific environment values, call
setEnv(array $env)
.
To merge new values with existing container-specific environment values, call
addEnv(array $env)
.
Use this to alias a class $from
its original name $to
another name. All
requests for $from
will receive an instance of $to
.
This can be useful for specifying default implementations for interfaces.
<?php
public function __construct()
{
$this->alias('FooInterface', 'FooImplementation');
$instance = $this->newInstance('FooInterface'); // instanceof FooImplementation
}
These methods return an actual instance of the requested class, thus invoking all the lazy-loading configurations for that class and its dependency.
These are protected methods, and should be called from typehinted methods on your extended container class to provide objects from your library, package, module, or component.
This returns a new instance of the specified class, with additional arguments
that override the default for that class. Multiple calls to newInstance()
return different new instances.
<?php
class MyCapsule extends \Capsule\Di\Container
{
public function newPdo() : PDO
{
return $this->newInstance(PDO::CLASS);
}
}
This returns a shared service instance from the Capsule. Multiple calls to
serviceInstance()
return the same instance.
<?php
class MyCapsule extends \Capsule\Di\Container
{
public function getPdo() : PDO
{
return $this->serviceInstance(PDO::CLASS);
}
}
These fluent methods are available on the return of default()
, new()
, and
provide()
. They allow you to further configure the class specified by those
methods.
Sets the constructor arguments for the class. These are sequential; lazy-loaded
dependencies will be invoked when the object is actually created. Multiple
calls to args()
will reset and replace the previous constructor arguments.
To reset the args, call resetArgs()
.
Adds one post-instantiation method call on the object. This is good for setter
injection, and for post-instantiation setup methods. Multiple calls to call()
will add to the previous calls.
To reset the calls, use resetCalls()
.
Sets a custom factory callable to use for instantiating the object. This factory
will be used in place of the Capsule factory. The $args
values will
be passed to the custom factory when it is called, and the $calls
will be made
on the object returned from the custom factory.
To reset the factory so that Capsule factory is used, call resetFactory()
.
If you do not define the default()
configuration for a class, the Capsule
will reflect on the requested class instance and examine its constructor
arguments. The Capsule will then populate each argument with:
provide()
-ed; or,Non-class typehints cannot be configured automatically.
PSR-11 is a "stringly"-typed interface. The Capsule container does not implement it by default, but you can do so easily.
<?php
class Psr11Capsule
extends \Capsule\Di\Container
implements \Psr\Container\ContainerInterface
{
public function get($id)
{
return $this->getRegistry()->get($id);
}
public function has($id)
{
return $this->getRegistry()->has($id);
}
}
TBD.
TBD.
Note that you can wrap it in a closure or other lazy-loader so that it does not get created until it is called.