Pimple PHP: Complete Guide to a Simple DI Container
1. What is Pimple and Why Use It?
Pimple is a lightweight Dependency Injection Container for PHP, created by Fabien Potencier, the founder of the Symfony Framework. Unlike bulky DI containers, Pimple offers a minimalistic solution that you can integrate into any project without unnecessary overhead. This library implements the Service Locator pattern and provides a simple API for managing objects and their dependencies.
Pimple's main goal is to simplify dependency management in PHP applications. Instead of manually creating objects with new and passing them into constructors, you register services in the container and retrieve them by name. The container automatically resolves dependencies, creates objects once (singleton) or fresh each time (factory), and manages their lifecycle. This makes your code more modular, testable, and maintainable.
Pimple is ideal for small to medium-sized projects where you don't need the full power of Symfony DI or PHP-DI. It doesn't use annotations, requires no complex configuration, and works with PHP 5.3 and above. The library is actively used in Silex (micro-framework) and other PHP projects where simplicity and speed matter.
2. Installation
Install Pimple via Composer, the standard package manager for PHP. Run this in your terminal:
composer require pimple/pimple
After installation, include the Composer autoloader in your PHP file:
require_once __DIR__ . '/vendor/autoload.php';
If you don't have a composer.json yet, create one:
{
"require": {
"pimple/pimple": "^3.5"
}
}
3. Quick Start — Minimal Working Example
Create a file called index.php and add the following code:
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Pimple\Container;
// Create the container
$container = new Container();
// Define a simple service (closure)
$container['greeting'] = function ($c) {
return 'Hello, World!';
};
// Define a service with a parameter
$container['personal_greeting'] = function ($c) {
return function ($name) use ($c) {
return 'Hello, ' . $name . '!';
};
};
// Retrieve services
echo $container['greeting'] . "\n"; // Hello, World!
echo $container['personal_greeting']('Alice') . "\n"; // Hello, Alice!
// Define an object service
class Logger {
public function log($message) {
echo "[LOG] $message\n";
}
}
$container['logger'] = function ($c) {
return new Logger();
};
$container['logger']->log('Application started');
Run the script: php index.php. You'll see three messages printed. This example demonstrates basic service registration and retrieval from the container.
4. Key Methods and Classes
4.1. Pimple\Container::__construct()
Signature: public function __construct(array $values = [])
Description: Creates a new container instance. You can pass an array of predefined values/services.
$container = new Container([
'db.host' => 'localhost',
'db.name' => 'test'
]);
4.2. offsetSet($id, $value) (magic method via [])
Signature: public function offsetSet($id, $value): void
Description: Registers a service or value in the container. The $id is the service name, and $value can be a closure (for lazy loading) or any PHP value.
// Register a closure (service definition)
$container['mailer'] = function ($c) {
return new Mailer($c['config']['smtp_host']);
};
// Register a plain value
$container['app.name'] = 'My Awesome App';
Important: When you assign a closure to a container key, Pimple wraps it in a Pimple\Service object. The closure is only executed when you first access the service. Subsequent accesses return the same instance (singleton behavior by default).
4.3. offsetGet($id) (magic method via [])
Signature: public function offsetGet($id): mixed
Description: Retrieves a registered service or value by its identifier. If the service is a closure, it gets executed and its return value is cached for future calls.
$mailer = $container['mailer']; // Executes the closure, returns Mailer instance
$appName = $container['app.name']; // Returns 'My Awesome App'
4.4. offsetExists($id) (magic method via isset())
Signature: public function offsetExists($id): bool
Description: Checks whether a service or value with the given ID is registered in the container.
if (isset($container['mailer'])) {
// Service exists
}
4.5. offsetUnset($id) (magic method via unset())
Signature: public function offsetUnset($id): void
Description: Removes a registered service or value from the container.
unset($container['mailer']); // Removes the mailer service
4.6. factory($callable)
Signature: public function factory($callable): \Closure
Description: Wraps a closure so that it returns a new instance every time the service is accessed, instead of the default singleton behavior.
$container['new_logger'] = $container->factory(function ($c) {
return new Logger();
});
$logger1 = $container['new_logger']; // New instance
$logger2 = $container['new_logger']; // Another new instance
// $logger1 !== $logger2
4.7. protect($callable)
Signature: public function protect($callable): \Closure
Description: Stores a closure as a plain value without executing it. Use this when you want to store a callback function in the container.
$container['my_callback'] = $container->protect(function ($arg) {
return strtoupper($arg);
});
// The closure is returned as-is, not executed
$callback = $container['my_callback'];
echo $callback('hello'); // HELLO
4.8. raw($id)
Signature: public function raw($id): mixed
Description: Returns the raw closure or value stored for a service, without executing it. Useful for extending or inspecting service definitions.
$rawDefinition = $container->raw('mailer');
// $rawDefinition is the closure, not the Mailer instance
4.9. extend($id, $callable)
Signature: public function extend($id, $callable): void
Description: Extends an existing service definition. The $callable receives the original service instance and the container, and must return the modified/ extended service.
// Original service
$container['mailer'] = function ($c) {
return new Mailer();
};
// Extend it with logging
$container->extend('mailer', function ($mailer, $c) {
$mailer->setLogger($c['logger']);
return $mailer;
});
4.10. keys()
Signature: public function keys(): array
Description: Returns an array of all registered service IDs in the container.
$allKeys = $container->keys();
// ['mailer', 'logger', 'app.name', ...]
5. Practical Examples
5.1. Database Connection with Configuration
$container['config'] = [
'db.host' => 'localhost',
'db.name' => 'myapp',
'db.user' => 'root',
'db.pass' => 'secret'
];
$container['db'] = function ($c) {
$config = $c['config'];
return new PDO(
"mysql:host={$config['db.host']};dbname={$config['db.name']}",
$config['db.user'],
$config['db.pass']
);
};
// Usage
$db = $container['db'];
$stmt = $db->query('SELECT * FROM users');
5.3. Service Depending on Another Service
$container['user_repository'] = function ($c) {
return new UserRepository($c['db']);
};
$container['user_service'] = function ($c) {
return new UserService($c['user_repository']);
};
// Usage
$userService = $container['user_service'];
$users = $userService->getAllUsers();
5.4. Using Factory for Non-Singleton Services
$container['request'] = $container->factory(function ($c) {
return new Request();
});
// Each call creates a new Request object
$request1 = $container['request'];
$request2 = $container['request'];
// $request1 !== $request2
6. Best Practices and Tips
- Keep services lazy: Always define services as closures so they're only created when needed.
- Use meaningful names: Name your services clearly (e.g.,
db,mailer,logger). - Don't overuse the container: Avoid passing the entire container to your classes. Inject only the specific services they need.
- Leverage
extend(): Use it to modify services without changing their original definitions — great for plugins or modules. - Use
factory()for stateful services: If a service should not be shared (e.g., request objects), wrap it withfactory(). - Keep configuration separate: Store config values in a dedicated array or config file, not scattered across service definitions.
7. Conclusion
Pimple is a powerful yet simple DI container that brings order to dependency management in PHP projects. Its minimal API — just a handful of methods — makes it easy to learn and integrate. Whether you're building a small script or a medium-sized application, Pimple helps you write cleaner, more testable code.
Start using Pimple today to simplify your PHP development. Install it via Composer, define your services as closures, and let the container handle the rest. For more advanced needs, consider Symfony DI or PHP-DI, but for most projects, Pimple is all you need.
Also in library
Working with CSV in Java: OpenCSV, Apache Commons CSV, and Java Scanner Libraries
Working with XML in Rust: roxmltree, quick-xml, and serde-xml-rs libraries
Kotlin Application Configuration: Libraries and Approaches
Generating QR Codes in PHP: phpqrcode Library and Complete Guide
Object Serialization in Go (Golang): A Complete Guide