PHPackages                             joby/smol-context - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. joby/smol-context

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

joby/smol-context
=================

A lightweight DI container for PHP for global, low-config service and object registration. Use it to resolve and inject services, objects, and config values anywhere. You can execute callables with injected dependencies, instantiate objects with dependencies, and even include files using a unique DocBlock injection system.

v1.1.1(2mo ago)036MITPHPPHP &gt;=8.1CI passing

Since Jan 18Pushed 2mo agoCompare

[ Source](https://github.com/joby-lol/smol-context)[ Packagist](https://packagist.org/packages/joby/smol-context)[ RSS](/packages/joby-smol-context/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (3)Dependencies (4)Versions (4)Used By (0)

smolContext
===========

[](#smolcontext)

A lightweight dependency injection container with config integration and docblock-driven file inclusion.

Installation
------------

[](#installation)

```
composer require joby/smol-context
```

About
-----

[](#about)

smolContext provides a simple, static dependency injection container for PHP. Services are registered and automatically instantiated with their dependencies when retrieved.

**Key features:**

- **Static API**: Access the container globally via `Context` without passing it around
- **Automatic dependency resolution**: Constructor and callable parameters are injected automatically
- **Explicit registration**: Services register only under specified class names for predictable behavior
- **Config integration**: Inject config values alongside objects using `#[ConfigValue]`
- **Docblock file inclusion**: Include PHP files with variables injected from docblock annotations
- **Context stack**: Push/pop container scopes for testing, sub-requests, or rollback workflows

Basic Usage
-----------

[](#basic-usage)

### Registering and Retrieving Services

[](#registering-and-retrieving-services)

```
use Joby\Smol\Context\Context;

// Register a class (lazy-loaded on first get)
Context::register(App\UserService::class);

// Register a concrete instance
Context::register(new App\Logger());

// Get registered services
$users = Context::get(App\UserService::class);
$logger = Context::get(App\Logger::class);

// Services are cached - same instance every time
assert($logger === Context::get(App\Logger::class));
```

### Registration Aliasing

[](#registration-aliasing)

By default, services are registered only under their exact class name. This explicit behavior prevents surprises and makes dependencies clear. However, you can optionally register a service under additional class names using the `also` parameter.

```
use Joby\Smol\Context\Context;

// Default: Only retrievable by exact class name
Context::register(App\Services\MysqlDatabase::class);
Context::get(App\Services\MysqlDatabase::class); // ✓ Works
Context::get(App\Contracts\DatabaseInterface::class); // ✗ Throws exception

// Register under a specific interface
Context::register(
    App\Services\MysqlDatabase::class,
    also: App\Contracts\DatabaseInterface::class
);
Context::get(App\Services\MysqlDatabase::class); // ✓ Works
Context::get(App\Contracts\DatabaseInterface::class); // ✓ Works (same instance)

// Register under multiple interfaces/classes
Context::register(
    App\Services\RedisCache::class,
    also: [
        App\Contracts\CacheInterface::class,
        App\Contracts\Storage::class
    ]
);
Context::get(App\Services\RedisCache::class); // ✓ Works
Context::get(App\Contracts\CacheInterface::class); // ✓ Works (same instance)
Context::get(App\Contracts\Storage::class); // ✓ Works (same instance)

// Register under ALL parent classes and interfaces
Context::register(
    App\Services\FileLogger::class,
    also: true
);
// Now retrievable by FileLogger, Logger, LoggerInterface, etc.
```

**When to use `also`:**

- `false` (default): For concrete implementations you'll reference directly
- `string`: When injecting via a specific interface or parent class (such as replacing something with a child class)
- `array`: When the same instance should satisfy multiple contracts
- `true`: For widely-used services accessed via various type hints (use sparingly)

### Creating Transient Objects

[](#creating-transient-objects)

Build objects without caching them in the container:

```
// Each call creates a new instance
$parser1 = Context::new(App\Parser::class);
$parser2 = Context::new(App\Parser::class);

assert($parser1 !== $parser2);
```

### Checking for Services

[](#checking-for-services)

```
if (Context::has(App\UserService::class)) {
    $users = Context::get(App\UserService::class);
}
```

Executing Callables with Injection
----------------------------------

[](#executing-callables-with-injection)

Execute callables with automatic parameter injection:

```
use Joby\Smol\Context\Context;

Context::register(App\UserService::class);
Context::register(App\Logger::class);

$result = Context::execute(
    function (App\UserService $users, App\Logger $logger): string {
        $logger->log('Processing...');
        return $users->process();
    }
);
```

Type-hinted object parameters are automatically resolved from the container.

Config Integration
------------------

[](#config-integration)

Every container includes a config service (backed by `joby/smol-config`). Inject config values using the `#[ConfigValue]` attribute:

```
use Joby\Smol\Context\Context;
use Joby\Smol\Context\ConfigValue;
use Joby\Smol\Config\Sources\ArraySource;

// Add config source
$runtime = new ArraySource();
$runtime['name'] = 'My Application';
$runtime['host'] = 'localhost';
Context::container()->config->addSource('app', $runtime);
Context::container()->config->addSource('db', $runtime);

// Inject config values into callables
$result = Context::execute(
    function (
        #[ConfigValue('app/name')] string $appName,
        #[ConfigValue('db/host')] string $dbHost,
    ): string {
        return "{$appName} @ {$dbHost}";
    }
);
```

### Mixing Config and Object Injection

[](#mixing-config-and-object-injection)

```
use Joby\Smol\Config\Sources\ArraySource;

Context::register(App\Logger::class);

$config = new ArraySource();
$config['debug'] = true;
Context::container()->config->addSource('app', $config);

Context::execute(
    function (
        App\Logger $logger,
        #[ConfigValue('app/debug')] bool $debug,
    ): void {
        if ($debug) {
            $logger->enableDebugMode();
        }
    }
);
```

Including Files with Docblock Injection
---------------------------------------

[](#including-files-with-docblock-injection)

Include PHP files with variables injected from docblock annotations. This is useful for templates, scripts, or configuration files that need access to services.

### The Include File

[](#the-include-file)

Create a file with dependencies declared in its opening docblock (`report.php`):

```
