Bind vs Singleton vs Instance
Learn the differences between bind(), singleton(), and instance() methods and when to use each
bind() - Creates a NEW instance every time
• Use when you need a fresh object for each request
• Example: Request validators, temporary calculators
singleton() - Creates ONE instance for the entire request
• The same instance is reused throughout the request lifecycle
• Use for services that maintain state or are expensive to create
• Example: Database connections, cache managers, configuration loaders
instance() - Binds an EXISTING object instance
• You provide an already-created object
• Useful when you need to configure an object before binding it
• Example: Pre-configured API clients
Real-World Example:
In a reporting system, you might use bind() for report generators (each report is independent), singleton() for the database connection (reuse the same connection), and instance() for a pre-configured PDF renderer.
Code Examples
<?php
// In AppServiceProvider
public function register(): void
{
// Each time ReportGenerator is requested, create a new instance
$this->app->bind(ReportGenerator::class, function ($app) {
return new ReportGenerator(
$app->make(DatabaseConnection::class),
$app->make(PdfRenderer::class)
);
});
}
// Usage in controller:
class ReportController extends Controller
{
public function salesReport(ReportGenerator $generator)
{
return $generator->generate('sales'); // Fresh instance #1
}
public function inventoryReport(ReportGenerator $generator)
{
return $generator->generate('inventory'); // Fresh instance #2
}
}
// Each method gets a DIFFERENT instance of ReportGenerator
// Memory: Higher (multiple instances)
// Use Case: When each usage should be independent
💡 bind() creates a new instance every time it's resolved. If you resolve ReportGenerator 10 times in a request, you get 10 different objects. Good for stateless services or when you need isolation.
<?php
// In AppServiceProvider
public function register(): void
{
// Create only ONE instance per request lifecycle
$this->app->singleton(CacheManager::class, function ($app) {
return new CacheManager(
config('cache.driver'),
config('cache.prefix')
);
});
}
// Usage in controller:
class ProductController extends Controller
{
public function index(CacheManager $cache)
{
$products = $cache->remember('products', fn() => Product::all());
// $cache is instance #1
}
public function show($id, CacheManager $cache)
{
$product = $cache->remember("product.{$id}", fn() => Product::find($id));
// $cache is THE SAME instance #1 (singleton!)
}
}
// Both methods share the SAME CacheManager instance
// Memory: Lower (single instance)
// Performance: Better (no repeated initialization)
// Use Case: Database connections, cache, config loaders
💡 singleton() creates the instance once and reuses it for all subsequent resolutions within the same request. Perfect for expensive-to-create objects or services that maintain state.
<?php
// In AppServiceProvider
public function register(): void
{
// Create and configure an object BEFORE binding it
$s3Client = new S3Client([
'credentials' => [
'key' => config('aws.key'),
'secret' => config('aws.secret'),
],
'region' => config('aws.region'),
'version' => 'latest',
]);
// Additional configuration
$s3Client->setTimeout(30);
$s3Client->setRetries(3);
// Bind the already-created, pre-configured instance
$this->app->instance(S3Client::class, $s3Client);
}
// Now anywhere in your app:
class FileUploadController extends Controller
{
public function upload(Request $request, S3Client $s3)
{
// $s3 is the exact same pre-configured instance
// from the service provider
$s3->putObject([
'Bucket' => 'my-bucket',
'Key' => $request->file('image')->getClientOriginalName(),
'Body' => $request->file('image')->get(),
]);
}
}
💡 instance() lets you create and configure an object yourself, then bind it to the container. Useful when you need complex configuration that's easier to do procedurally.
<?php
// SCENARIO: Analytics Tracker
// 1. bind() - New tracker for each event (isolated)
$this->app->bind(AnalyticsTracker::class, function ($app) {
return new AnalyticsTracker();
});
// Result: Track event A, track event B = 2 different trackers
// Use when: Events should be independent
// 2. singleton() - One tracker for entire request (shared state)
$this->app->singleton(AnalyticsTracker::class, function ($app) {
return new AnalyticsTracker();
});
// Result: Track event A, track event B = same tracker (can batch events!)
// Use when: You want to collect all events and send them together
// 3. instance() - Pre-configured tracker
$tracker = new AnalyticsTracker();
$tracker->setApiKey(config('analytics.api_key'));
$tracker->setEndpoint(config('analytics.endpoint'));
$tracker->enableBatching(true);
$this->app->instance(AnalyticsTracker::class, $tracker);
// Result: Everyone gets this exact configured tracker
// Use when: Complex setup needed before binding
// Memory comparison (100 resolutions):
// bind() → 100 objects created → ~10MB
// singleton() → 1 object created → ~0.1MB
// instance() → 1 object created → ~0.1MB
💡 Quick decision guide: Use bind() for stateless services, singleton() for expensive or stateful services, and instance() when you need to pre-configure an object before binding.