PHPackages                             sodaho/container - 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. [PSR &amp; Standards](/categories/psr-standards)
4. /
5. sodaho/container

ActiveLibrary[PSR &amp; Standards](/categories/psr-standards)

sodaho/container
================

Lightweight PSR-11 dependency injection container with autowiring and caching.

v1.0.0(3mo ago)00MITPHPPHP ^8.2CI passing

Since Mar 19Pushed 3w agoCompare

[ Source](https://github.com/SoDaHo/php-container)[ Packagist](https://packagist.org/packages/sodaho/container)[ RSS](/packages/sodaho-container/feed)WikiDiscussions main Synced 3w ago

READMEChangelogDependencies (5)Versions (2)Used By (0)

php-container
=============

[](#php-container)

Lightweight PSR-11 dependency injection container for PHP. Autowiring, caching, zero bloat.

Why This Library?
-----------------

[](#why-this-library)

**What it does:**

- PSR-11 container with constructor autowiring
- Reflection metadata caching for production performance
- HMAC-signed cache files to prevent RCE in shared hosting
- Zero dependencies beyond `psr/container`

**What it deliberately does not:**

- Attribute-based configuration
- Lazy proxies / code generation
- Compiler passes
- Tagged services

If you need those, use Symfony DI or PHP-DI.

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

[](#installation)

```
composer require sodaho/container
```

Usage
-----

[](#usage)

### Basic Autowiring

[](#basic-autowiring)

```
use Sodaho\Container\Container;

$container = new Container();

// Automatically resolves dependencies via Reflection
$controller = $container->get(UserController::class);
```

The container analyzes constructor parameters and recursively resolves all dependencies:

```
class UserController {
    public function __construct(
        private UserService $userService,  // Auto-resolved
        private Logger $logger             // Auto-resolved
    ) {}
}
```

### Manual Definitions

[](#manual-definitions)

For services that need configuration or primitives:

```
$container = new Container();

// Factory receives the container for nested resolution
$container->set(Database::class, fn(Container $c) => new Database(
    host: $_ENV['DB_HOST'],
    logger: $c->get(Logger::class)
));

// Simple values
$container->set('app.name', fn() => 'My Application');
```

### Interface Binding

[](#interface-binding)

Bind interfaces to concrete implementations:

```
$container = new Container();

// Short syntax
$container->bind(LoggerInterface::class, FileLogger::class);
$container->bind(CacheInterface::class, RedisCache::class);

// Fluent chaining
$container = Container::create()
    ->bind(LoggerInterface::class, FileLogger::class)
    ->bind(CacheInterface::class, RedisCache::class);

// Now autowiring resolves interfaces automatically
$service = $container->get(PaymentService::class);
// PaymentService receives FileLogger for LoggerInterface parameter
```

### Singleton Behavior

[](#singleton-behavior)

All resolved instances are cached (singleton pattern):

```
$container = new Container();

$logger1 = $container->get(Logger::class);
$logger2 = $container->get(Logger::class);

$logger1 === $logger2; // true - same instance
```

Caching
-------

[](#caching)

The container can cache Reflection metadata to avoid analyzing classes on every request.

### Enable via Config

[](#enable-via-config)

```
$container = new Container([
    'cacheFile' => '/var/cache/container.php',
    'cacheSignature' => $_ENV['CONTAINER_CACHE_KEY'],  // Required in production!
    'debug' => false,
]);

// At end of bootstrap/request
$container->saveCache();
```

**Security:** A signature key is **required** when caching is enabled (`debug=false`). This prevents RCE attacks via tampered cache files. See [Security](#security) section below.

### Enable via Fluent API

[](#enable-via-fluent-api)

```
$container = Container::create()
    ->setDebug(false)
    ->enableCache('/var/cache/container.php', $_ENV['CONTAINER_CACHE_KEY']);

// ... resolve services ...

$container->saveCache();
```

### Enable via Environment Variables

[](#enable-via-environment-variables)

```
CONTAINER_CACHE_FILE=/var/cache/container.php
CONTAINER_CACHE_KEY=your-secret-key-here  # Required in production!
APP_DEBUG=false
```

```
$container = new Container();  // Reads from $_ENV / getenv() automatically
```

Generate a secure key: `php -r "echo bin2hex(random_bytes(32));"`

### Configuration Priority

[](#configuration-priority)

**Priority:** `$config` array &gt; `$_ENV` &gt; `getenv()` &gt; default

The library checks `$_ENV` first (thread-safe), then falls back to `getenv()` for legacy compatibility. Use a library like [sodaho/env-loader](https://github.com/sodaho/env-loader) to load `.env` files into `$_ENV`.

### How Caching Works

[](#how-caching-works)

1. **First request:** Reflection analyzes classes, stores metadata
2. **Following requests:** Metadata loaded from cache, no Reflection needed
3. **OPcache:** Cache file is PHP code, optimized by OPcache

The cache stores "build instructions" (which dependencies each class needs), not the instances themselves.

### Cache Management

[](#cache-management)

```
// Save cache (only writes if new classes were resolved)
$container->saveCache();

// Clear cache
$container->clearCache();
```

Debug Mode
----------

[](#debug-mode)

Debug mode disables caching for development:

```
// Explicit
$container = new Container(['debug' => true]);

// Or via fluent API
$container = Container::create()->setDebug(true);

// Or automatic detection via $_ENV
// APP_DEBUG=true or APP_ENV=local/dev/development
$container = new Container();  // Debug mode auto-enabled
```

Hooks
-----

[](#hooks)

The container fires events at key points, allowing you to add logging, monitoring, or debugging without modifying your services.

### Available Events

[](#available-events)

EventWhenData`resolve`New instance created`['id' => string, 'instance' => object]``error`Exception during resolution`['id' => string, 'exception' => Throwable]``cacheHit`Class metadata found in cache`['id' => string]``cacheMiss`Class metadata not in cache`['id' => string]`### Usage

[](#usage-1)

```
$container = new Container();

// Log all resolved services
$container->on('resolve', function (array $data) {
    error_log("Resolved: {$data['id']}");
});

// Monitor cache performance
$container->on('cacheHit', fn($data) => $metrics->increment('container.cache.hit'));
$container->on('cacheMiss', fn($data) => $metrics->increment('container.cache.miss'));

// Log errors
$container->on('error', function (array $data) {
    error_log("Container error for {$data['id']}: " . $data['exception']->getMessage());
});
```

**Note:** Hooks only fire when a new instance is created. Singleton cache hits (returning an already-resolved instance) do not trigger `resolve`.

Security
--------

[](#security)

### Cache Signature Key (Required in Production)

[](#cache-signature-key-required-in-production)

When caching is enabled (`debug=false`), a signature key is **required**. This prevents Remote Code Execution (RCE) attacks via tampered cache files in shared hosting environments.

```
// Production: signature key required
$container = new Container([
    'cacheFile' => '/var/cache/container.php',
    'cacheSignature' => $_ENV['CONTAINER_CACHE_KEY'],
    'debug' => false,
]);

// Development: debug mode disables caching, no key needed
$container = new Container([
    'cacheFile' => '/var/cache/container.php',
    'debug' => true,  // No signature required
]);
```

**Why?** The cache file contains PHP code that gets executed via `require`. An attacker with write access to the cache file could inject malicious code. The HMAC-SHA256 signature ensures the file hasn't been modified.

### Exception Debug Messages

[](#exception-debug-messages)

Exceptions have two messages:

- **User message:** Safe for end users, returned by `getMessage()`
- **Debug message:** Contains technical details for logging, returned by `getDebugMessage()`

```
try {
    $container->get(SomeService::class);
} catch (ContainerException $e) {
    // Show to user
    echo $e->getMessage();  // "Cache signature key is required..."

    // Log for debugging
    error_log($e->getDebugMessage());  // "Provide key via CONTAINER_CACHE_KEY..."
}
```

Exceptions
----------

[](#exceptions)

All exceptions implement PSR-11 interfaces:

```
use Sodaho\Container\Container;
use Sodaho\Container\Exception\ContainerException;
use Sodaho\Container\Exception\NotFoundException;
use Sodaho\Container\Exception\CacheException;

try {
    $service = $container->get(SomeService::class);
} catch (NotFoundException $e) {
    // Class or service not found
} catch (CacheException $e) {
    // Cache read/write error or signature mismatch
} catch (ContainerException $e) {
    // Any other container error (not instantiable, unresolvable parameter, etc.)
}
```

ExceptionWhen`NotFoundException`Class doesn't exist or service not defined`ContainerException`Class not instantiable, unresolvable parameter, factory error, circular dependency`CacheException`Cache write failed, directory not writable, invalid signature, missing signature keyLimitations
-----------

[](#limitations)

The container is intentionally minimal. It does **not** support:

FeatureStatusAlternativeInterface binding**Supported**`bind()` methodAutowiring**Supported**Automatic via ReflectionSingleton**Supported**Default behaviorFactories**Supported**`set()` methodCaching**Supported**`enableCache()` / configUnion typesDefault onlyUse `set()` for manual definitionIntersection typesDefault onlyUse `set()` for manual definitionAttributesNot supportedUse `set()` for configurationTagged servicesNot supportedNot needed for simple DILazy proxiesNot supportedWould require code generationCompiler passesNot supportedFramework territoryRequirements
------------

[](#requirements)

- PHP ^8.2
- psr/container ^2.0

License
-------

[](#license)

MIT

Acknowledgments
---------------

[](#acknowledgments)

Parts of this project (refactoring, documentation, code review) were developed with AI assistance (Claude).

###  Health Score

37

—

LowBetter than 81% of packages

Maintenance89

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

96d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/4407ed68227862f26dfbd1fff5c4d117c89ba024bf20202280d00d8199036cfc?d=identicon)[SoDaHo](/maintainers/SoDaHo)

---

Top Contributors

[![SoDaHo](https://avatars.githubusercontent.com/u/73506118?v=4)](https://github.com/SoDaHo "SoDaHo (10 commits)")

---

Tags

autowiringcontainerdiphppsr-11phpcontainerPSR-11Autowiringdependency-injectiondi

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/sodaho-container/health.svg)

```
[![Health](https://phpackages.com/badges/sodaho-container/health.svg)](https://phpackages.com/packages/sodaho-container)
```

###  Alternatives

[devanych/di-container

Simple implementation of a PSR-11 dependency injection container

124.2k3](/packages/devanych-di-container)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
