mouf/picotainer
Picotainer is a tiny (24 lines) dependency injection container for PHP, inspired by Pimple and compatible with container-interop/PSR-11. Define entries as closures, support delegate lookup, and retrieve services with a minimalist API.
composer require mouf/picotainer
use Mouf\Picotainer\Picotainer;
$container = new Picotainer([
'logger' => function () {
return new \Monolog\Logger('name');
},
'database' => function () {
return new \Illuminate\Database\Connection();
}
]);
class UserService {
private $logger;
private $database;
public function __construct($logger, $database) {
$this->logger = $logger;
$this->database = $database;
}
}
$userService = $container->get('userService', function ($c) {
return new UserService($c->get('logger'), $c->get('database'));
});
Picotainer class: Core container logic (24 lines).get() method: Primary entry point for fetching dependencies.ContainerInterface for recursive dependency resolution.$container = new Picotainer([
'userRepository' => function ($c) {
return new UserRepository($c->get('dbConnection'));
},
'dbConnection' => function () {
return new PDO('mysql:host=localhost;dbname=test');
}
]);
$userRepo = $container->get('userRepository');
// Laravel's container as delegate
$laravelContainer = app();
$picotainer = new Picotainer([
'picotainerService' => function ($c) {
// $c here is the Laravel container
return new PicotainerService($c->make('auth'));
}
], $laravelContainer);
$container = new Picotainer([
'appName' => function () {
return 'MyApp';
},
'debugMode' => function () {
return env('APP_DEBUG', false);
}
]);
// In a service provider
public function register() {
$this->app->singleton('picotainer', function () {
$picotainer = new Picotainer([
'picotainerLogger' => function ($c) {
return new \Monolog\Logger('picotainer');
}
], $this->app); // Delegate to Laravel's container
return $picotainer;
});
}
// Usage in a command
public function handle() {
$logger = app('picotainer')->get('picotainerLogger');
$logger->info('Running Picotainer service...');
}
singleton() method: Manually manage singletons via closures:
$container = new Picotainer([
'config' => function () {
return new Config(); // Singleton instance
}
]);
bindIf(): Use conditional logic inside closures:
$container = new Picotainer([
'cache' => function () {
return env('CACHE_DRIVER') === 'redis'
? new RedisCache()
: new FileCache();
}
]);
public function testUserService()
{
$container = new Picotainer([
'userRepository' => function () {
return $this->createMock(UserRepository::class);
}
]);
$service = $container->get('userService');
// Test logic...
}
class CustomPicotainer extends Picotainer {
public function singleton($id, Closure $closure) {
$this[$id] = function () use ($closure) {
static $instance;
return $instance ?? ($instance = $closure($this));
};
return $this;
}
}
No Built-in Singleton Management
get() with the same key may create duplicate instances.Picotainer:
$container = new Picotainer([
'config' => function () {
static $instance;
return $instance ?? ($instance = new Config());
}
]);
Delegate Lookup Ambiguity
$container parameter in closures refers to the delegate container, not Picotainer itself. This can cause confusion when accessing Picotainer-specific entries.No has() Method for Non-Existent Keys
has() on a non-existent key throws an exception (fixed in v1.0.1 but may still cause confusion).isset($container[$key]) or wrap get() in a try-catch.Lack of Laravel-Specific Features
tag(), contextualBinding(), and other Laravel container features.No Automatic Wiring
$container = new Picotainer([
'userService' => function ($c) {
return new UserService(
$c->get('userRepository'),
$c->get('logger')
);
}
]);
Inspect Container Contents
print_r($container->get('key')); // Debug values
var_dump($container->has('key')); // Check existence
Override Dependencies for Testing
$container = new Picotainer([
'userRepository' => function () {
return $this->createMock(UserRepository::class);
}
]);
Handle Circular Dependencies
A depends on B, which depends on A) will cause infinite recursion.Custom Container Classes
Extend Picotainer to add Laravel-like methods:
class LaravelPicotainer extends Picotainer {
public function singleton($id, Closure $closure) {
$this[$id] = function () use ($closure) {
static $instance;
return $instance ?? ($instance = $closure($this));
};
return $this;
}
}
PSR-11 Adapter for Laravel Create a wrapper to use Picotainer as Laravel’s container:
class PicotainerAdapter implements ContainerInterface {
protected $picotainer;
public function __construct(Picotainer $picotainer) {
$this->picotainer = $picotainer;
}
public function get($id) {
return $this->picotainer->get($id);
}
public function has($id) {
return isset($this->picotainer[$id]);
}
}
Dynamic Binding Use closures to dynamically bind services:
$container = new Picotainer([
'cache' => function () {
return Cache::store(config('cache.default'));
}
]);
Closure Scope
use to capture variables:
$userId = 1;
$container = new Picotainer([
'user' => function () use ($userId) {
return User::find($userId);
}
]);
Delegate Lookup Overrides
get() method for its own keys.Performance Considerations
How can I help you explore Laravel packages today?