The Spiral container allows you to configure it by creating bindings between interfaces or aliases to specific implementations. You can use Bootloaders to define these bindings.
There are two ways to configure the container, either by using the Spiral\Core\BinderInterface
or
the Spiral\Core\Container
.
To bind an interface to a concrete implementation, you can use the following code snippet:
use Spiral\Core\BinderInterface;
public function boot(BinderInterface $binder): void
{
$binder->bind(
UserRepositoryInterface::class,
CycleUserRepository::class
);
}
To bind a singleton, you can use the following code snippet:
use Spiral\Core\BinderInterface;
public function boot(BinderInterface $binder): void
{
$binder->bindSingleton(
UserRepositoryInterface::class,
CycleUserRepository::class
);
}
You can also bind with specific parameters by using the Autowire
class like this:
use Spiral\Core\BinderInterface;
use Spiral\Core\Container\Autowire;
public function boot(BinderInterface $binder): void
{
$binder->bindSingleton(
UserRepositoryInterface::class,
new Autowire(CycleUserRepository::class, ['table' => 'users'])
);
}
Lastly, you can use closures to configure your class automatically by passing a closure to the bind or bindSingleton
method like this:
use Spiral\Core\BinderInterface;
public function boot(BinderInterface $binder): void
{
$binder->bindSingleton(
UserRepositoryInterface::class,
static fn() => new CycleUserRepository(table: users)
);
}
Closures also support dependencies:
use Spiral\Core\BinderInterface;
use Spiral\Core\Container\Autowire;
public function boot(BinderInterface $binder): void
{
$binder->bindSingleton(
UserRepositoryInterface::class,
static fn(UserConfig $config) => new CycleUserRepository(table: $config->getTable())
);
}
When this closure is executed, the container will automatically resolve an instance of UserConfig
and pass it as an
argument to the closure. This allows you to easily configure your classes with dependencies without having to manually
instantiate and manage them.
To check if a container has binding use:
use Spiral\Core\Container;
public function boot(Container $container): void
{
$container->has(UserRepositoryInterface::class)
}
To remove the container binding:
use Spiral\Core\BinderInterface;
public function boot(BinderInterface $binder): void
{
$binder->removeBinding(UserRepositoryInterface::class)
}
Container supports WeakReference
binding:
use Spiral\Core\Container;
use WeakReference;
public function boot(Container $container): void
{
$object = new stdClass();
$hash = \spl_object_hash($object);
$reference = WeakReference::create($object);
$container->bind('test-alias', $reference);
dump($hash === \spl_object_hash($container->get('test-alias'))); // true
unset($object);
// New object can't be created because classname has not been stored
dump($container->get('test-alias')); // null
}
Starting from version 3.8.0 of the Spiral Framework we have replaced the previous array-based structure for storing information about bindings within the container. The new approach employs Data Transfer Objects (DTO), providing a more structured and organized way to store binding information. With this change, developers now can easily configure container bindings using these objects.
To demonstrate the enhanced container functionality, here's an example showcasing the new binding configuration:
use Spiral\Core\Config\Factory;
$container->bind(LoggerInterface::class, new Factory(
callable: static function() {
return new Logger(....);
},
singleton: true
))
Here are the available binding DTOs:
This simplified DTO allows you to create a link to another binding within the container.
use Spiral\Core\Config\Alias;
$container->bind(
\DatetimeImmutable::class,
static fn() => new \DatetimeImmutable()
);
$container->bind(
'now',
new Alias(alias: \DatetimeImmutable::class)
);
In this example, we first bind the \DatetimeImmutable
class to a factory function that creates a new instance
of \DatetimeImmutable
every time it's requested. Then, we create an Alias binding named now and associate it with
the \DatetimeImmutable
class binding.
Now you can request \DatetimeImmutable
class from the container by alias $container->get('now')
The Alias also provides a second argument - singleton
. By setting singleton to true
, the aliased class becomes a
singleton. This means that whenever you request $container->get('now')
from the container, it will return the same
instance every time. On the other hand, calling $container->get(\DatetimeImmutable::class)
will return a new instance
of \DatetimeImmutable
with the current time on each request.
The Spiral\Core\Config\Autowire
binding serves as a wrapper for the Spiral\Core\Container\Autowire
class, providing
an automated way to resolve and instantiate classes by injecting their dependencies.
use Spiral\Core\Config\Autowire;
use Spiral\Core\Container\Autowire as AutowireAlias;
$container->bind(MyClass::class, new Autowire(
autowire: new AutowireAlias(MyClass::class, ['foo' => 'bar']),
singleton: true
));
The singleton
argument, set to true in this example, indicates that the container should treat MyClass
as a
singleton. When you request an instance of MyClass from the container $container->get(MyClass::class)
, it will return
the same instance every time.
The Spiral\Core\Config\Factory
binding serves as a simple factory for creating mixed types using a closure.
use Spiral\Core\Config\Factory;
$container->bind('time', new Factory(
callable: static fn() => time(),
));
Every time when you request current time from the container $container->get('time')
it will return current timestamp.
Additionally, the Factory
binding can be configured as a singleton:
$container->bind('time', new Factory(
callable: static fn() => time(),
singleton: true,
));
By setting the singleton
argument to true
. This means that whenever you request the time value from the
container $container->get('time')
, it will return the same value across multiple invocations.
The Spiral\Core\Config\DeferredFactory
binding enables you to bind deferred factory to the container using a
special array callable.
use Spiral\Core\Config\DeferredFactory;
$container->bind('some-binding', new DeferredFactory(
factory: [MyClass::class, 'handle'],
));
In the example above, we bind the key some-binding to a DeferredFactory
instance. The factory property is set
to [MyClass::class, 'handle']
. When the some-binding key is requested from the
container $container->get('some-binding')
, the DeferredFactory
will resolve an instance of MyClass
and then invoke
the handle method on that instance to produce the required value.
Additionally, binding can be configured as a singleton:
$container->bind('some-binding', new DeferredFactory(
factory: ...,
singleton: true
));
The Scalar binding is a new functionality introduced for the container. It provides a convenient way to store and retrieve static scalar values within the container. It can be useful for configuring paths, constants, or any other scalar values needed by your application.
use Spiral\Core\Config\Scalar;
$container->bind('app-path', new Scalar(value: '/var/www/my-app'));
The Shared binding allows you to bind a persistent object to a key in the container. Once the object is created, it will be reused every time the key is requested, and custom arguments cannot be provided during subsequent requests.
use Spiral\Core\Config\Shared;
$container->bind(MyClass::class, new Shared(value: new MyClass(...)));
It's important to note that with the Shared
binding, custom arguments cannot be provided during subsequent requests.
The object will be created with the initial arguments and reused as is.
It is particularly useful when you want to ensure that the same instance of an object is used throughout the application. It provides persistence and prevents the creation of multiple instances with different arguments.
An inflector allows you to manipulate an object after creating it in the container. This is particularly useful for applying common modifications or injections to objects of a specific type.
use Spiral\Core\Config\Inflector;
$container->bind(LoggerAwareInterface::class, new Inflector(
inflector: static function (LoggerAwareInterface $obj, LoggerInterface $logger): LoggerAwareInterface {
$obj->setLogger($logger);
return $obj;
}
));
In this case, any object implementing LoggerAwareInterface
will have its logger set based on the specified configuration.
The Inflector binding is a powerful tool for applying common modifications or injections consistently across objects in your application. It simplifies the process of configuring and customizing objects retrieved from the container.
The WeakReference feature allows you to work with weak reference objects within the container. Weak references are references to an object that do not prevent the object from being garbage-collected when there are no strong references to it.
se Spiral\Core\Config\WeakReference;
$obj = new MyClass();
$container->bind(MyClass::class, new WeakReference(
reference: new \WeakReference($obj)
));
$obj === $container->get(MyClass::class); // true
unset($obj);
$obj1 = $container->get(MyClass::class); // A new object will be created
$obj1 === $container->get(MyClass::class); // true
When you retrieve MyClass from the container $container->get(MyClass::class)
, the container returns the original
object because it still exists. However, when you unset the $obj
variable, removing the strong reference to the
object, it becomes eligible for garbage collection. Subsequent calls to $container->get(MyClass::class)
will create a
new instance of MyClass
because the original object has been garbage-collected.
Using weak references can be beneficial in certain scenarios where you want to have control over the object's lifecycle and allow it to be garbage-collected when there are no more strong references to it.
The framework also allows you to use "lazy singletons" which are classes that are automatically treated as singletons by the container without the need to explicitly bind them as such.
Spiral\Core\Attribute\Singleton
allows you to mark a class as a singleton. By applying this attribute to a class, you
indicate that only a single instance of the class should be created and shared across the application. This attribute
can be used as an alternative to interfaces for specifying singleton behavior.
use Spiral\Core\Attribute\Singleton;
#[Singleton]
final class UserService
{
public function store(User $user): void
{
//...
}
}
Note
Read more about container attributes in the Container - Attributes section.
Now, the container will automatically treat this class as a singleton and only create one instance of it for the entire application, regardless of how many times it is requested.
protected function index(UserService $service): void
{
dump($this->container->get(UserService::class) === $service);
}