blackbonjour/service-manager

Small, lightweight and factory-biased service manager based on the PSR-11 standard.

1.6.0 2025-05-25 17:44 UTC

This package is auto-updated.

Last update: 2025-05-25 17:47:25 UTC


README

Small, lightweight and factory-biased service manager based on the PSR-11 standard.

Latest Stable Version License

Table of Contents

Installation

You can install the package via composer:

composer require blackbonjour/service-manager

Basic Usage

use BlackBonjour\ServiceManager\ServiceManager;

// Create a new service manager
$serviceManager = new ServiceManager();

// Add a service directly
$serviceManager->addService('config', [
    'db' => [
        'host' => 'localhost',
        'user' => 'root',
        'password' => 'secret',
    ],
]);

// Retrieve a service
$config = $serviceManager->get('config');

Configuration

The ServiceManager constructor accepts several configuration arrays:

$serviceManager = new ServiceManager(
    services: [],          // Pre-created service instances
    factories: [],         // Factories for creating services
    abstractFactories: [], // Abstract factories for dynamic service creation
    invokables: [],        // Classes that can be instantiated directly
    aliases: [],           // Alternative names for services
);

Services

Services are pre-created instances that are stored in the container:

// Via constructor
$serviceManager = new ServiceManager([
    'config' => ['debug' => true],
    'logger' => new Logger(),
]);

// Via method
$serviceManager->addService('config', ['debug' => true]);
$serviceManager->addService('logger', new Logger());

Factories

Factories are responsible for creating service instances. They can be:

  1. Classes implementing FactoryInterface
  2. Callable objects
  3. Class strings that resolve to one of the above
// Using a factory class
$serviceManager->addFactory(Database::class, DatabaseFactory::class);

// Using a callable
$serviceManager->addFactory('logger', function($container, $requestedName) {
    return new Logger();
});

// Via constructor
$serviceManager = new ServiceManager(
    factories: [
        Database::class => DatabaseFactory::class,
        'logger' => function($container, $requestedName) {
            return new Logger();
        },
    ]
);

Abstract Factories

Abstract factories are used when a service is not explicitly defined. They determine if they can create a requested service:

// Add an abstract factory
$serviceManager->addAbstractFactory(new DynamicFactory());

// Or via constructor
$serviceManager = new ServiceManager(
    abstractFactories: [
        new DynamicFactory(),
        ReflectionFactory::class,
    ]
);

Invokables

Invokables are classes that can be instantiated directly without a factory:

// Add an invokable
$serviceManager->addInvokable(stdClass::class);

// Via constructor
$serviceManager = new ServiceManager(
    invokables: [
        stdClass::class,
        SomeClass::class,
    ]
);

Aliases

Aliases provide alternative names for services:

// Add an alias
$serviceManager->addAlias('configuration', 'config');

// Via constructor
$serviceManager = new ServiceManager(
    aliases: [
        'configuration' => 'config',
        'db' => Database::class,
    ]
);

Advanced Usage

Creating Services with Options

You can create services with additional options:

// Define a factory that accepts options
$serviceManager->addFactory('database', function($container, $requestedName, $options = null) {
    return new Database(
        $options['host'] ?? 'localhost',
        $options['user'] ?? 'root',
        $options['password'] ?? 'secret'
    );
});

// Create the service with options
$db = $serviceManager->createService('database', [
    'host' => 'db.example.com',
    'user' => 'admin',
    'password' => 'password123'
]);

Using Array Access

The ServiceManager implements ArrayAccess, allowing you to use array syntax:

// Add a service
$serviceManager['config'] = ['debug' => true];

// Check if a service exists
if (isset($serviceManager['config'])) {
    // Service exists
}

// Get a service
$config = $serviceManager['config'];

// Remove a service
unset($serviceManager['config']);

Removing Services

You can remove services from the container:

$serviceManager->removeService('config');
// or
unset($serviceManager['config']);

Abstract Factories

DynamicFactory

The DynamicFactory looks for a factory class with the same name as the requested service plus "Factory":

// Add the DynamicFactory
$serviceManager->addAbstractFactory(new DynamicFactory());

// Now if you request MyService, it will look for MyServiceFactory
$service = $serviceManager->get(MyService::class);

ReflectionFactory

The ReflectionFactory uses PHP's Reflection API to automatically instantiate classes and resolve their dependencies:

// Add the ReflectionFactory
$serviceManager->addAbstractFactory(new ReflectionFactory());

// Now you can get any class that has constructor dependencies registered in the container
$service = $serviceManager->get(MyService::class);

API Reference

ServiceManager

The main container class implementing PSR-11's ContainerInterface.

Methods:

  • __construct(array $services = [], array $factories = [], array $abstractFactories = [], array $invokables = [], array $aliases = [])
  • addAbstractFactory(AbstractFactoryInterface|string $abstractFactory): void
  • addAlias(string $alias, string $id): void
  • addFactory(string $id, FactoryInterface|callable|string $factory): void
  • addInvokable(string $id): void
  • addService(string $id, mixed $service): void
  • createService(string $id, ?array $options = null): mixed
  • get(string $id): mixed
  • has(string $id): bool
  • removeService(string $id): void

FactoryInterface

Interface for factories that create services.

Methods:

  • __invoke(ContainerInterface $container, string $service, ?array $options = null)

AbstractFactoryInterface

Interface for abstract factories that can dynamically determine if they can create a service.

Methods:

  • canCreate(ContainerInterface $container, string $service): bool
  • __invoke(ContainerInterface $container, string $service, ?array $options = null) (inherited from FactoryInterface)

InvokableFactory

A factory for creating instances of classes without dependencies.

Methods:

  • __invoke(ContainerInterface $container, string $service, ?array $options = null)