Sharing services safely in Phalcon's DI container
Register stateless Phalcon services as shared DI instances, and avoid storing request-specific state in them.
05 Jan 2026, 20:57 UTC

Useful answer
Register stateless services—such as a database adapter, logger, or view renderer—as shared services in Phalcon\Di\Di. The container creates the object once and returns the same instance on every get() call. Do not mark request-scoped objects as shared, because the container keeps that instance alive until the container itself is destroyed.
How Phalcon resolves services
Phalcon\Di\Di is the dependency injection container provided by the Phalcon C extension. You register a service under a name, then resolve it lazily with get(). A definition can be a class name string, a closure, or an array definition. When shared is true, Di caches the created object and returns it for every later resolution of that name.
This guide assumes Phalcon 4 or 5, where the container lives under the Phalcon\Di namespace. Phalcon 3 used different class locations, so code from that version needs namespace updates before it runs under Phalcon 4 or 5.
Worked configuration
The following example creates a container, sets it as the default, registers a shared PDO database adapter, and resolves it twice to confirm identity.
<?php
use Phalcon\Di\Di;
use PDO;
// Verify the extension first: php -m | grep phalcon
$container = new Di();
// Components that call Di::getDefault() need this.
Di::setDefault($container);
$container->setShared(
'db',
function () {
return new PDO(
'mysql:host=localhost;dbname=app_db',
'app_user',
'app_password'
);
}
);
$db1 = $container->get('db');
$db2 = $container->get('db');
var_dump($db1 === $db2); // Expected check: bool(true)
Where to run: a terminal with the project's PHP interpreter. Required permissions: none beyond executing PHP and reading php.ini. Placeholders: replace the host, database name, user, and password with your own values. Expected check: the identity comparison returns true. Risk: if the phalcon extension is not installed or enabled, PHP raises a class-not-found error before the container code runs.
Limits and common mistakes
- Shared state leaks across requests. A shared service lives for the container's lifetime. Storing the current user ID, request parameters, or other mutable request data inside a shared service lets later requests reuse that stale data. Recreate the container per request, or keep request-scoped objects non-shared.
- Missing default container. Code that calls
Phalcon\Di::getDefault()fails at runtime if you never callDi::setDefault(). Set the container early in your bootstrap, before resolving services. - Namespace drift between versions. Phalcon 3 placed DI classes under different namespaces. Code written for Phalcon 3 does not load under Phalcon 4 or 5 without updates.
- Closures capture registration-time data. If a factory closure depends on request-specific input, pass that input at resolution time rather than capturing it when the service is registered.
Practical check
Confirm shared behavior directly by registering a shared closure that returns a new object, resolving it twice, and comparing the results:
$container->setShared('test', function () {
return new stdClass();
});
var_dump($container->get('test') === $container->get('test'));
Expected result: bool(true). If it returns bool(false), the service was not registered as shared, or a different container instance resolved the name.
No rollback required
Registering services changes only the in-memory container. There is no database migration, file write, or persistent configuration to roll back. If a registration causes errors, remove the setShared() call and re-run your bootstrap.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.