Using Phalcon's Dependency Injection Container for Shared Services
Learn how to register shared services in Phalcon's DI container to reuse database connections and avoid common pitfalls like missing default DI in CLI or circular dependencies.
29 Aug 2025, 14:46 UTC

To avoid creating a new database connection on every request, register the adapter as a shared service in Phalcon's DI container. This ensures the same instance is reused throughout the request lifecycle, preventing excess connections and preserving stateful objects.
How the DI Container Works
The Phalcon\\Di\\Di class acts as a service locator. Services are registered with either set() (transient) or setShared() (singleton). When a service is first requested via get(), the associated closure is executed and the resulting object is stored for future requests if it was registered as shared.
Registrations are typically closures that receive the DI container itself, allowing lazy instantiation and access to other services if needed.
Worked Example: Shared DB and Crypt Services
The following bootstrap code registers a MySQL adapter and an encryption service as shared. It belongs in your public/index.php or similar entry point.
setShared('db', function () {
return new Mysql([
'host' => 'your-db-host',
'username' => 'your-db-user',
'password' => 'your-db-password',
'dbname' => 'your-db-name',
'charset' => 'utf8',
]);
});
// Shared crypt service
$di->setShared('crypt', function () {
$crypt = new Crypt();
$crypt->setKey('your-random-secret-key-32-bytes-long');
return $crypt;
});
// Make this DI the default for the application
Di::setDefault($di);
?>
Controllers can now access these services as properties thanks to Phalcon's magic methods.
db resolves the shared MySQL adapter
$users = $this->db->fetchAll('SELECT id, name FROM users WHERE active = ?', [1]);
// $this->crypt resolves the shared encryption service
$token = $this->crypt->encrypt('session-token');
$this->view->setVars(['users' => $users, 'token' => $token]);
}
}
?>
Limits and Common Mistakes
1. Confusing set() with setShared()
Using set() for a database adapter creates a new PDO connection each time $this->db is accessed. This can quickly exhaust MySQL's max_connections limit. Reserve set() for stateless objects like helpers or factories.
2. Missing Default DI in CLI Scripts
When running Phalcon tasks from the command line, the default DI is not automatically set. Forgetting to call Di::setDefault($di) in your CLI bootstrap leads to Phalcon\\Di\\Exception: DependencyInjector is not set errors. Always duplicate the DI setup used by your web bootstrap.
3. Circular Dependencies
If service A's closure calls $this->get('B') and service B's closure calls $this->get('A'), resolution recurses until PHP exhausts memory or hits the nesting limit. Design services hierarchically: low‑level services (db, logger) should never depend on higher‑level ones (controllers, models).
4. No Constructor Auto‑wiring
Phalcon does not inspect constructor arguments to inject dependencies automatically. Every required service must be explicitly registered or manually instantiated inside the closure. Attempting to access an unregistered service throws Phalcon\\Di\\Exception: Service 'x' was not found.
Verification
To confirm that a shared service returns the same instance, add a temporary debug snippet after the DI setup:
get('db');
$db2 = $di->get('db');
var_dump(spl_object_hash($db1) === spl_object_hash($db2)); // should bool(true)
?>
Check the output; if it shows false, the service was registered with set() instead of setShared(). For CLI scripts, run the same snippet in your task bootstrap and ensure no exception is thrown.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.