Service Providers & Container Bindings
- Introduction
- Zero-Overhead Container Bindings (Recommended)
- Writing Service Providers
- Registering Providers
Introduction
In this Framework, Service Providers are completely optional and stripped of legacy architectural weight. Dynamic asset registration helpers (loadViewsFrom, loadTranslationsFrom, loadJsonTranslationsFrom, loadMigrationsFrom, and publishes) do NOT exist in the ServiceProvider base class to eliminate runtime container closure bindings and per-request filesystem scans.
Because instantiating provider classes and executing register() methods on every request adds unnecessary filesystem and boot overhead, the framework natively defers container resolution by default at the application level.
Instead of writing boilerplate DeferrableProvider classes, you can map container bindings directly within the App\Application::$bindings property or App\Application::$availableBindings. Service providers are only required when explicit boot() lifecycle hooks are needed.
Zero-Overhead Container Bindings (Recommended)
To achieve maximum performance during application boot, IoC bindings should be configured directly on the application instance rather than wrapped in Service Provider classes.
1. Available Bindings & Aliases
For core services and autoloaded singletons, define your available bindings and class aliases directly within your application class (App\Application):
$availableBindings: Maps abstract keys or interfaces to their concrete resolvers.$aliases: Registers container aliases without instantiating provider objects.
2. Explicit Binding Maps
If you have custom service bindings that would traditionally sit inside a provider’s register() method, move them to the App\Application::$bindings property as an array callable.
Because these callbacks are evaluated on-demand only when a service is explicitly requested from the container, all bindings become implicitly deferred with zero class loading cost during boot.
Writing Service Providers
If your application requires custom bootstrapping logic that must execute during startup (such as registering event listeners or queue failure handlers), you can create a Service Provider.
All service providers extend MacropaySolutions\Kernel\Support\ServiceProvider.
The Run CLI can generate a new provider via the make:provider command:
php run make:provider RiakServiceProvider
The Register Method
Within the register method, you should only bind things into the service container:
<?php
namespace App\Providers;
use App\Services\Riak\Connection;
use MacropaySolutions\Kernel\Support\ServiceProvider;
class RiakServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
$this->app->singleton(Connection::class, [\App\Factories\RiakFactory::class, 'createConnection']);
}
}
[!TIP]
If a Service Provider only contains container bindings insideregister(), delete the Service Provider entirely and move its bindings into theApp\Application::$bindingsproperty to eliminate class instantiation overhead during boot.
The Boot Method
The boot method is called after all container bindings are available. This is where cross-cutting concerns and event listeners should be registered:
<?php
namespace App\Providers;
use App\Listeners\QueueFailureListener;
use MacropaySolutions\Kernel\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
\app('queue')->failing([QueueFailureListener::class, 'handle']);
}
}
Deferred Providers
If your provider is only registering bindings in the service container or performing component registration (such as calling loadViewComponentsAs()), you should defer its registration until one of the provided services is actually needed. Deferring the loading of such a provider will improve the performance of your application because it is not loaded from the filesystem on every request.
To defer a provider you must follow the logic of MailServiceProvider from your \App\Application file and DO NOT register it in bootstrap/app.php via $app->register(…).
Implement the MacropaySolutions\Kernel\Contracts\Support\DeferrableProvider interface and define a provides(): array method. Deferred providers are skipped by Application::boot() and will never have their boot() method called automatically. If a deferred provider requires JIT setup logic upon resolution, register an $this->app->afterResolving() array callable inside its register() method.
Registering Providers
If your provider is non-deferrable, register it directly on the $app instance inside bootstrap/app.php:
$app->register(\App\Providers\AppServiceProvider::class);
If your provider has a boot method defined, execute it by passing its class name directly into the variadic $app->boot(...) method inside bootstrap/app.php:
return $app->boot(
\App\Providers\AppServiceProvider::class,
);
If a Service Provider’s register() logic has been refactored into the App\Application::$bindings array, its $app->register() line in bootstrap/app.php should be commented out or removed entirely.
[!TIP]
If a Service Provider only contains aboot()method and noregister()logic, do not call$app->register(). Simply pass the class name directly to$app->boot(...). When passing any kind of providers to$app->boot(...)the framework will Just-In-Time (JIT) instantiate it (if not loaded already), execute the boot method, and if not loaded already, discard the instance without consuming permanent memory in the loaded providers state.
[!CRITICAL] Providers that are omitted from
$app->boot(...)will NOT be booted during application startup. Only providers explicitly listed in$app->boot(...)insidebootstrap/app.phpwill be booted during application startup. Providers MUST declare an explicitboot()method if listed in the boot arguments. Thisbootmethod is not included in autowiring, so be sure to leave its definition without parameters.Not allowing providers to be booted automatically, increases the visibility on the boot logic and prevents devs from adding overhead logic on each request when not needed.