ghostwriter/container

Provides an extensible Dependency Injection Service Container for Automated Object Composition, Interception, and Lifetime Management.

Fund package maintenance!
ghostwriter

Installs: 4 578 050

Dependents: 20

Suggesters: 0

Security: 0

Stars: 0

Watchers: 1

Forks: 0

Open Issues: 0

pkg:composer/ghostwriter/container

6.0.1 2025-11-18 05:56 UTC

README

GitHub Sponsors Automation Supported PHP Version Downloads

Provides an extensible Dependency Injection Service Container for Automated Object Composition, Interception, and Lifetime Management.

Installation

You can install the package via composer:

composer require ghostwriter/container

Usage

Simple usage

Registering a service on the given container.

final readonly class Service
{
    public function __construct(
        private Dependency $dependency
    ) {}

    public function dependency():Dependency
    {
        return $this->dependency;
    }
}

$container = Container::getInstance();

$service = $container->get(Service::class);

assert($service instanceof Service); // true

assert($service->dependency() instanceof Dependency); // true

Automatic Service Definition Registration

Automatically register a service definition class using Composer's extra config in your composer.json file.

Important

A service definition class MUST implement Ghostwriter\Container\Interface\Service\DefinitionInterface [class].

It should look like the following:

{
    "extra": {
        "ghostwriter": {
            "container": {
                "definition": "App\\Service\\Definition"
            }
        }
    }
}

Service Definition

Registering a service definition on the container.

interface TaskInterface {}
interface TaskCollectionInterface {
    public function add(TaskInterface $task): void;
    public function count(): int;
}

final readonly class MainTask implements TaskInterface {
    public function __construct(
        private string $name
    ) {}
}

final readonly class FirstTask implements TaskInterface {
    public function __construct(
        private string $name
    ) {}
}

final class TaskCollection implements TaskCollectionInterface
{
    private array $tasks = [];
    public function add(TaskInterface $task): void
    {
        $this->tasks[] = $task;
    }
    public function count(): int
    {
        return count($this->tasks);
    }
}
final class TaskCollectionFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container): TaskCollection
    {
        return new TaskCollection();
    }
}
final class TaskCollectionExtension implements ExtensionInterface
{
    /** @param TaskCollection $service */
    public function __invoke(ContainerInterface $container, object $service): void
    {
        $service->add(new FirstTask('Task 1'));
        
        $mainTask = $container->build(TaskInterface::class, ['name' => 'Main Task']);
        
        assert($mainTask instanceof MainTask); // true
        
        $service->add($mainTask);
    }
}

final readonly class TasksServiceDefinition implements DefinitionInterface
{
    public function __invoke(ContainerInterface $container)
    {
        $container->alias(MainTask::class, TaskInterface::class);
        $container->alias(TaskCollection::class, TaskCollectionInterface::class);
        $container->extend(TaskCollection::class, TaskCollectionExtension::class);
        $container->factory(TaskCollection::class, TaskCollectionFactory::class);
    }
}

$container = Container::getInstance();

$container->define(TasksDefinition::class);

$service = $container->get(TaskCollectionInterface::class); 
assert($service instanceof TaskCollection); // true

assert($service->count() === 2); // true

Contextual Bindings

Registering a Contextual Bindings on the container.

interface ClientInterface {}

final readonly class RestClient implements ClientInterface {}

final readonly class GraphQLClient implements ClientInterface {}

final readonly class GitHub
{
    public function __construct(
        private ClientInterface $client
    ) {
    }
    public function getClient(): ClientInterface
    {
        return $this->client;
    }
}

// When GitHub::class asks for ClientInterface::class, it would receive an instance of GraphQLClient::class.
$container->bind(GitHub::class, ClientInterface::class, GraphQLClient::class);

// When any other service asks for ClientInterface::class, it would receive an instance of RestClient::class.
$container->alias(ClientInterface::class, RestClient::class);

Service Extensions

Registering a service extension on the container.

/**
 * @implements ExtensionInterface<GitHubClient>
 */
final readonly class GitHubExtension implements ExtensionInterface
{
    /**
     * @param GitHubClient $service
     */
    public function __invoke(ContainerInterface $container, object $service): void
    {
        $service->setEnterpriseUrl(
            $container->get(GitHubClient::GITHUB_HOST)
        );
    }
}

$container->alias(GitHubClientInterface::class, GitHubClient::class);
$container->extend(GitHubClientInterface::class, GitHubExtention::class);

Service Factory

Registering a service factory on the container.

final readonly class Dependency {}
final readonly class Service
{
    public function __construct(
        private Dependency $dependency
    ){}

    public function dependency():Dependency
    {
        return $this->dependency;
    }
}
final readonly class ServiceFactory {
  public function __invoke(Container $container): Service
  {
     return new Service($container->get(Dependency::class));
  }
}

$container = Container::getInstance();

$container->factory(Service::class, ServiceFactory::class);

$service = $container->get(Service::class);

assert($service instanceof Service); // true
assert($service->dependency() instanceof Dependency); // true

Testing

composer test

Changelog

Please see CHANGELOG.md for more information what has changed recently.

Security

If you discover any security related issues, please email nathanael.esayeas@protonmail.com instead of using the issue tracker.

Sponsors

[Become a GitHub Sponsor]

Credits

License

The BSD-4-Clause. Please see License File for more information.