Managing Object Lifecycles with Laminas ServiceManager
Learn how to implement the Laminas ServiceManager to decouple object creation from business logic using factories, invokables, and proper DI patterns.
13 Jul 2026, 15:58 UTC

Solving the Dependency Bottleneck
Hard-coding class instantiations inside your business logic creates rigid code that is difficult to test and maintain. The Laminas\ServiceManager solves this by acting as a centralized Dependency Injection (DI) container. Instead of a class creating its own dependencies, the ServiceManager injects them, allowing you to swap implementations (e.g., replacing a live Mailer with a MockMailer during tests) without touching the consuming class.
Core Instantiation Strategies
The ServiceManager uses different configuration keys depending on how complex the object creation is. Choosing the wrong one leads to either unnecessary boilerplate or runtime errors.
- Invokables: Used for classes with no constructor arguments. The manager simply calls
new ClassName(). - Factories: The standard for complex objects. A factory is a class or callable that contains the logic to fetch dependencies from the container and pass them into the service's constructor.
- Abstract Factories: Fallback mechanisms that handle a group of similar services based on a naming pattern.
- Delegators: Wrappers that intercept the creation of a service to add decorators or logging before the service is returned.
Implementation Example: Factory-Based Injection
To avoid the "Service Locator" anti-pattern—where you pass the entire manager into your class—you should use a dedicated Factory. This ensures your service only knows about the specific dependencies it needs.
1. The Service and its Dependency
// src/Service/UserReportService.php
namespace App\Service;
class UserReportService {
private $dbAdapter;
public function __construct(\Laminas\Db\Adapter\AdapterInterface $dbAdapter) {
$this->dbAdapter = $dbAdapter;
}
public function generate() {
// Logic using $this->dbAdapter
}
}
2. The Factory
Run this in your project's factory directory. The factory must implement FactoryInterface.
// src/Service/UserReportServiceFactory.php
namespace App\Service;
use Interop\Container\ContainerInterface;
use Laminas\ServiceManager\Factory\FactoryInterface;
class UserReportServiceFactory implements FactoryInterface {
public function __invoke(ContainerInterface $container, $requestedName, array $options = null) {
// Retrieve the DB adapter from the container to inject into the service
$dbAdapter = $container->get(\Laminas\Db\Adapter\AdapterInterface::class);
return new UserReportService($dbAdapter);
}
}
3. The Configuration
Add this to your module configuration array (typically module.config.php). Ensure the service_manager key is at the top level.
return [
'service_manager' => [
'factories' => [
App\Service\UserReportService::class => App\Service\UserReportServiceFactory::class,
],
],
];
Performance and Stability Limits
While the ServiceManager is flexible, certain patterns can degrade application performance or cause crashes:
| Pattern | Risk | Mitigation |
|---|---|---|
| Abstract Factories | Increased lookup overhead per request. | Use explicit factories for high-traffic services. |
| Circular Dependencies | Fatal recursion error (Stack Overflow). | Refactor shared logic into a third, independent service. |
| Service Locator Injection | Hidden dependencies; impossible to unit test. | Always inject specific dependencies via constructors. |
Verification and Diagnostics
To verify the wiring is correct, you can manually test the retrieval in a development script or a test case. You will need a configured ServiceManager instance.
Check Procedure:
- Run a script that calls
$container->get(App\Service\UserReportService::class). - Verify the returned object is an instance of
UserReportService. - Use reflection or a debugger to ensure the
dbAdapterproperty is not null.
If the service is not found, check that the class name in the factories config exactly matches the string passed to get(), including the namespace.
Rollback Strategy
Because this configuration changes the state of the application's dependency graph, a failure in a factory can bring down the entire site. To roll back:
- Remove the entry from the
service_managerconfiguration array. - If using a cached configuration (common in production), clear the config cache (e.g.,
rm data/cache/module-config-cache.php) to force the manager to reload the PHP arrays.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.