Advanced
Service Providers
Organize application services and bootstrapping
Service providers are the central place to configure and bootstrap your application services.
Quick Start
Create a Service Provider
app/Providers/AppServiceProvider.php
:
<?php
namespace App\Providers;
use Glueful\Container\Providers\BaseServiceProvider;
use Glueful\Container\Definition\FactoryDefinition;
use Glueful\Container\Definition\AliasDefinition;
final class AppServiceProvider extends BaseServiceProvider
{
public function defs(): array
{
return [
// Autowire concrete service
App\Services\OrderService::class => $this->autowire(App\Services\OrderService::class),
// Factory with config
'payment.gateway' => new FactoryDefinition(
'payment.gateway',
fn() => new App\Payments\StripePaymentGateway(config('services.stripe'))
),
// Alias interface to id
App\Contracts\PaymentGatewayInterface::class =>
new AliasDefinition(App\Contracts\PaymentGatewayInterface::class, 'payment.gateway'),
];
}
}
Register Provider
Register providers in config/serviceproviders.php
:
return [
'enabled' => [
App\Providers\AppServiceProvider::class,
],
];
Provider Model
- Providers extend
BaseServiceProvider
and return an array of service definitions indefs()
. - Use
autowire()
for concrete classes,FactoryDefinition
for factory-built services, andAliasDefinition
to map type-hints to ids. - Avoid side effects in providers; initialize on first use or via explicit boot steps (e.g., console commands, listeners) rather than a
boot()
method.
Built-in Providers
Glueful ships with several providers you can inspect for reference:
- CoreProvider — core services/aliases
- File:
src/Container/Providers/CoreProvider.php
- Aliases/services:
'logger'
(→Psr\Log\LoggerInterface
),'database'
,'request'
,'cache.store'
(via factory), query/schema builders
- File:
- RepositoryProvider — repositories factory
- File:
src/Container/Providers/RepositoryProvider.php
- Services:
Glueful\Repository\RepositoryFactory
, alias'repository'
- File:
- QueueProvider — queue system
- File:
src/Queue/ServiceProvider/QueueProvider.php
- Services:
Glueful\Queue\QueueManager
,Glueful\Queue\Failed\FailedJobProvider
,Glueful\Scheduler\JobScheduler
- File:
- HttpClientProvider — HTTP client stack
- File:
src/Http/ServiceProvider/HttpClientProvider.php
- Services:
Symfony\Contracts\HttpClient\HttpClientInterface
,Psr\Http\Client\ClientInterface
,Glueful\Http\Client
- File:
- SerializerProvider — serialization/normalization
- File:
src/Serialization/ServiceProvider/SerializerProvider.php
- File:
- SecurityProvider — auth/security helpers
- File:
src/Security/ServiceProvider/SecurityProvider.php
- File:
- TasksProvider — task scheduling/related bindings
- File:
src/Tasks/ServiceProvider/TasksProvider.php
- File:
- (PSR‑15) HttpPsr15Provider — PSR‑15 bridge config
- File:
src/Container/Providers/HttpPsr15Provider.php
- File:
Tip: Open these files to learn consistent patterns for FactoryDefinition, AliasDefinition, and autowire usage.
Common Patterns
Database Service Example
CoreProvider registers a connection as 'database'
. To expose a scoped helper:
use Glueful\Container\Providers\BaseServiceProvider;
use Glueful\Container\Definition\FactoryDefinition;
final class ReportingDbProvider extends BaseServiceProvider
{
public function defs(): array
{
return [
'db.reporting' => new FactoryDefinition(
'db.reporting',
fn(\Psr\Container\ContainerInterface $c) => $c->get('database')
),
];
}
}
Cache Service Example
Glueful registers 'cache.store'
and aliases Glueful\Cache\CacheStore::class
. To create a named cache:
use Glueful\Container\Providers\BaseServiceProvider;
use Glueful\Container\Definition\FactoryDefinition;
final class CacheServiceProvider extends BaseServiceProvider
{
public function defs(): array
{
return [
'cache.reports' => new FactoryDefinition(
'cache.reports',
fn() => \Glueful\Cache\CacheFactory::create()
),
];
}
}
Queue Service Example
Resolve the manager as needed:
$queue = app(\Glueful\Queue\QueueManager::class);
$queue->push(App\Jobs\SendEmail::class, ['userId' => $id]);
Event Service Provider
class EventServiceProvider
{
public function register(Container $container): void
{
$container->singleton(EventDispatcher::class);
}
public function boot(Container $container): void
{
$events = $container->make(EventDispatcher::class);
// Register event listeners
$events->listen(UserRegistered::class, SendWelcomeEmail::class);
$events->listen(UserRegistered::class, CreateUserProfile::class);
$events->listen(OrderCreated::class, SendOrderConfirmation::class);
$events->listen(OrderCreated::class, UpdateInventory::class);
}
}
Notification Service Provider
class NotificationServiceProvider
{
public function register(Container $container): void
{
$container->singleton('notifications', function ($app) {
$manager = new NotificationManager();
// Register channels
$manager->registerChannel('email', new EmailChannel(
$app['config']->get('mail')
));
$manager->registerChannel('sms', new SmsChannel(
$app['config']->get('sms')
));
$manager->registerChannel('push', new PushChannel(
$app['config']->get('push')
));
return $manager;
});
}
}
Custom Providers
Repository Provider
class RepositoryServiceProvider
{
public function register(Container $container): void
{
// Bind repository interfaces
$container->bind(
UserRepositoryInterface::class,
UserRepository::class
);
$container->bind(
OrderRepositoryInterface::class,
OrderRepository::class
);
$container->bind(
ProductRepositoryInterface::class,
ProductRepository::class
);
}
}
API Client Provider
class ApiServiceProvider
{
public function register(Container $container): void
{
// Stripe
$container->singleton(StripeClient::class, function ($app) {
return new StripeClient(
$app['config']->get('services.stripe.secret')
);
});
// SendGrid
$container->singleton(SendGridClient::class, function ($app) {
return new SendGridClient(
$app['config']->get('services.sendgrid.api_key')
);
});
// AWS S3
$container->singleton(S3Client::class, function ($app) {
$config = $app['config']->get('filesystems.s3');
return new S3Client([
'credentials' => [
'key' => $config['key'],
'secret' => $config['secret'],
],
'region' => $config['region'],
'version' => 'latest',
]);
});
}
}
Validation Provider
class ValidationServiceProvider
{
public function boot(Container $container): void
{
$validator = $container->make(Validator::class);
// Register custom rules
$validator->extend('phone', function ($value) {
return preg_match('/^\+?[1-9]\d{1,14}$/', $value);
});
$validator->extend('strong_password', function ($value) {
return strlen($value) >= 8
&& preg_match('/[A-Z]/', $value)
&& preg_match('/[a-z]/', $value)
&& preg_match('/[0-9]/', $value);
});
}
}
Advanced Patterns
Deferred Providers
Load providers only when needed:
class ImageServiceProvider
{
public array $provides = [
ImageProcessor::class,
'image',
];
public function register(Container $container): void
{
$container->singleton('image', function ($app) {
return new ImageProcessor(
$app['config']->get('image')
);
});
}
public function isDeferred(): bool
{
return true;
}
}
Conditional Registration
public function register(Container $container): void
{
// Only in production
if ($container['config']->get('app.env') === 'production') {
$container->singleton(ErrorTracker::class, SentryTracker::class);
}
// Only in development
if ($container['config']->get('app.debug')) {
$container->singleton(DebugBar::class);
}
}
Environment-Specific Services
public function register(Container $container): void
{
$env = $container['config']->get('app.env');
$container->singleton(PaymentGateway::class, match ($env) {
'production' => StripePaymentGateway::class,
'staging' => StripeTestGateway::class,
default => FakePaymentGateway::class,
});
}
Testing with Providers
Override Bindings in Tests
class UserServiceTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
// Replace provider bindings
app()->bind(EmailService::class, FakeEmailService::class);
app()->bind(PaymentGateway::class, FakePaymentGateway::class);
}
public function test_creates_user()
{
$service = app(UserService::class);
$user = $service->create(['email' => '[email protected]']);
$this->assertNotNull($user);
}
}
Mock Services
class OrderServiceTest extends TestCase
{
public function test_processes_order()
{
// Mock payment gateway
$gateway = $this->createMock(PaymentGateway::class);
$gateway->method('charge')->willReturn(['status' => 'success']);
app()->instance(PaymentGateway::class, $gateway);
// Test
$service = app(OrderService::class);
$order = $service->processOrder($orderData);
$this->assertEquals('completed', $order->status);
}
}
Best Practices
1. Keep Providers Focused
// ✅ Good - focused provider
class CacheServiceProvider
{
public function register(Container $container): void
{
// Only cache-related bindings
}
}
// ❌ Bad - mixed concerns
class AppServiceProvider
{
public function register(Container $container): void
{
// Cache, queue, email, payment, etc.
}
}
2. Use Boot for Side Effects
// ✅ Good - register in register(), bootstrap in boot()
public function register(Container $container): void
{
$container->singleton(EventDispatcher::class);
}
public function boot(Container $container): void
{
$events = $container->make(EventDispatcher::class);
$events->listen(...);
}
// ❌ Bad - side effects in register()
public function register(Container $container): void
{
$container->singleton(EventDispatcher::class);
$events = $container->make(EventDispatcher::class);
$events->listen(...); // Don't do this in register()
}
3. Document Provider Dependencies
/**
* Requires:
* - ConfigServiceProvider
* - DatabaseServiceProvider
*
* Provides:
* - UserRepository
* - OrderRepository
*/
class RepositoryServiceProvider
{
//...
}
4. Organize Providers by Domain
app/Providers/
├── AppServiceProvider.php # Core app services
├── DatabaseServiceProvider.php # Database
├── CacheServiceProvider.php # Caching
├── QueueServiceProvider.php # Queues
├── EventServiceProvider.php # Events
├── NotificationServiceProvider.php
├── PaymentServiceProvider.php
└── RepositoryServiceProvider.php
Troubleshooting
Services not available in register()?
- Move code to
boot()
method - Other providers may not have registered yet
Circular dependency?
- Review service dependencies
- Use lazy loading or setter injection
- Refactor to remove circular reference
Provider not loading?
- Check
config/serviceproviders.php
(andconfig/extensions.php
for extensions) - Verify namespace/class exists and is autoloaded
Bindings overwritten?
- Check provider order
- Last binding wins for same key
- Use unique service names
Next Steps
- Dependency Injection - Container basics
- Repositories - Repository pattern
- Testing - Test providers
- Configuration - Manage config