Basic Usage
This section explains the most common ways to build a FastForward container once you understand the basic quickstart flow.
The mental model
The container()
helper accepts one or more initializers and always returns an
autowire-enabled container. Each initializer can be one of the following:
| Initializer type | What the helper does |
|---|---|
ServiceProviderInterface
|
Wraps it in ServiceProviderContainer
|
Psr\Container\ContainerInterface
|
Adds it directly to the aggregate container |
ConfigInterface
|
Wraps it in ConfigContainer
|
string
|
Instantiates the class with new
and then applies the same rules |
If you pass class names as strings, they must be instantiable without constructor arguments.
If they need runtime values, instantiate them yourself before passing them to container()
.
Step 1: Start with one provider
For most projects, the easiest entry point is a single ArrayServiceProvider
:
use FastForward\Container\ServiceProvider\ArrayServiceProvider;
use function FastForward\Container\container;
$provider = new ArrayServiceProvider([
'logger' => static fn(): Logger => new Logger('app'),
]);
$container = container($provider);
$logger = $container->get('logger');
At this point you already have:
- explicit service registration through the provider
- PSR-11 compatible
get()andhas() - autowiring for classes that can be constructed from known dependencies
Step 2: Compose more than one source
You can pass multiple providers and containers when your application is split by feature or when you need to reuse an existing PSR-11 container:
use FastForward\Container\ServiceProvider\ArrayServiceProvider;
use function FastForward\Container\container;
$catalogProvider = new ArrayServiceProvider([
ProductCatalog::class => static fn(): ProductCatalog => new ProductCatalog(),
]);
$legacyContainer = new LegacyContainer();
$container = container($catalogProvider, $legacyContainer);
When multiple initializers can resolve the same service ID, the first matching container
inside the aggregate wins. This makes container($override, $defaults)
a useful pattern
for tests and environment-specific overrides.
Step 3: Register providers through configuration
If you already use fast-forward/config
, you can declare providers inside your config object:
use FastForward\Config\ArrayConfig;
use FastForward\Container\ContainerInterface;
use FastForward\Container\ServiceProvider\ArrayServiceProvider;
use function FastForward\Container\container;
$config = new ArrayConfig([
'app' => [
'name' => 'FastForward Storefront',
],
ContainerInterface::class => [
new ArrayServiceProvider([
ApplicationName::class => static fn($container): ApplicationName => new ApplicationName(
$container->get('config.app.name'),
),
]),
],
]);
$container = container($config);
$name = $container->get('config.app.name');
$app = $container->get(ApplicationName::class);
Important detail
There are two different identifiers involved in the configuration flow:
- Inside your raw config object, you register nested providers under
FastForward\Container\ContainerInterface::class. - After the config is wrapped by
ConfigContainer, individual config values are exposed through service IDs such asconfig.app.name.
This distinction matters because new users often try to store raw config values under
config.*
keys. The config.
prefix belongs to the container view, not to the raw
configuration array.
Step 4: Use class names as shortcuts when appropriate
Class-string initializers are convenient when the class has a parameterless constructor:
$container = container(
AppProvider::class,
LegacyContainer::class,
);
If either class needs constructor arguments, instantiate it yourself instead of relying on the shortcut.
Summary
container()is the main entry point and always returns an autowire-enabled container.- You can mix providers, PSR-11 containers, config objects, and instantiable class names.
- The first matching container wins during aggregate resolution.
- Raw config keys and
config.*service IDs are related, but they are not the same thing.