Understanding Laminas Modular Configuration Merging
Learn how Laminas merges module configuration at bootstrap, why load order matters, and how to keep your config maintainable as the application grows.
26 Apr 2026, 15:56 UTC

Problem: Configuration grows unmanageable
As a Laminas application adds features, it is tempting to keep all settings in a single global.php file. Over time, this file becomes large and edits risk unintentionally overriding values from other parts of the system.
How Laminas merges module configuration
Each module can provide its own configuration through a getConfig() method in Module.php or a dedicated config provider. During bootstrap, Laminas walks the list of modules defined in modules.config.php (or via config autoload in Mezzio) and merges the returned arrays depth‑first. If two modules define the same leaf key, the module loaded later wins, allowing intentional overrides without editing the original source.
Worked example: Adding a service factory and a route in a module
Suppose you need a UserRepository service and a route for user profiles inside a User module.
# module/User/config/module.config.php
return [
'service_manager' => [
'factories' => [
User\\Service\\UserRepository::class => User\\Service\\Factory\\UserRepositoryFactory::class,
],
],
'router' => [
'routes' => [
'user-profile' => [
'type' => 'Segment',
'options' => [
'route' => '/user/[:id]',
'defaults' => [
'controller' => User\\Controller\\ProfileController::class,
'action' => 'index',
],
],
],
],
],
];
The module’s Module.php loads this file:
# module/User/src/Module.php
namespace User;
class Module
{
public function getConfig(): array
{
return include __DIR__ . '/../config/module.config.php';;
}
}
When the application starts, Laminas merges this array with those of other modules. The ServiceManager can then retrieve the factory, and the router can match the user-profile route.
Trade‑offs and limitations
First, the merge runs on every request unless you enable the configuration cache (config-cache.php). With many modules or deeply nested arrays, bootstrap time and memory usage can grow, affecting cold‑start performance.
Second, load order becomes an implicit dependency. If two modules both configure view_helper_manager or the same service key, the module listed later in modules.config.php wins. Adding a new module can therefore change behavior elsewhere, so documenting the intended order is essential.
Actionable steps
- Keep each module’s configuration focused and version‑controlled alongside its code.
- Enable config caching in production to avoid repeated merges.
- Review
modules.config.php(or Mezzio’s config autoload) when adding a module to verify load order. - Use
laminas-component-installerto let Composer‑installed components register their config automatically, reducing manual edits. - During development, temporarily dump the merged config (e.g.,
var_dump($app->getConfig());) to confirm that expected keys are present.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.