laminas/laminas-development-mode
Laminas Development Mode provides a simple command-line toggle for switching a Laminas MVC application between production and development configurations, enabling development modules, debug settings, and config caching behavior without manual config file edits.
laminas/laminas-development-mode is a single-purpose CLI that toggles config/development.config.php on and off by copying/removing the committed config/development.config.php.dist. It is not a Laminas module and registers no config provider — it is a Composer-installed developer utility.
Install (if not already present):
composer require --dev laminas/laminas-development-mode
The Laminas MVC skeleton already ships this in require-dev plus Composer script aliases, so in most projects you simply run:
composer development-enable
composer development-status
composer development-disable
If the scripts are missing, add them to composer.json:
"scripts": {
"development-enable": "laminas-development-mode enable",
"development-disable": "laminas-development-mode disable",
"development-status": "laminas-development-mode status"
}
Older projects/docs may reference a development-mode or zf-development-mode binary; check vendor/bin/ before copying commands from a blog post.
Where to look first:
config/development.config.php.dist — the committed template and the real source of truth for what "development mode" means in your app (extra modules such as Laminas\DeveloperTools, cache flags, module paths)..gitignore — config/development.config.php should be ignored; the .dist must not be.public/index.php / config/application.config.php — to confirm how the generated file is merged into application config.composer.json — for the script aliases your team actually types.First use case: run composer development-enable, confirm config/development.config.php now exists and git status stays clean (because it is ignored), load the app and see the dev toolbar/debug output, then composer development-disable and confirm the file is gone.
Local development bootstrapping. On a fresh clone, document a single command in the README: composer install && composer development-enable. Keep genuinely local, machine-specific tweaks in config/autoload/*.local.php (ignored and never touched by this tool) rather than editing the generated development.config.php.
Treat the .dist as code. Add dev-only modules, disable config/module-map caching, and adjust module paths there. Example .dist intent:
return [
'modules' => [
'Laminas\DeveloperTools',
],
'module_listener_options' => [
'config_cache_enabled' => false,
'module_map_cache_enabled' => false,
],
];
Commit changes to the .dist, then re-run enable so the generated file picks them up.
Deploy scripts should assert the off state, not assume it. A production deploy that runs composer install --no-dev may not even have the binary. Prefer an idempotent guard:
rm -f config/development.config.php
or, when the package is available, composer development-disable. Never ship a release artifact that contains config/development.config.php.
CI matrix. Run your normal test job without development mode so you exercise production-ish config. Add one explicitly dev-mode job for things like DeveloperTools-dependent tests or to catch a broken .dist. In CI, check state deterministically with test -f config/development.config.php rather than parsing status output.
Docker Compose. For the local php service, enable dev mode in the entrypoint (or a bind-mounted generated file); for the production image, never create the file. Watch for UID mismatches: a container running as root will leave a root-owned development.config.php on the host bind mount that the host user cannot delete.
Makefile / task-runner wrappers. If your team already uses make, wrap the three Composer scripts:
dev-on: ; composer development-enable
dev-off: ; composer development-disable
dev-status: ; composer development-status
This keeps muscle memory consistent across older and newer repositories where binary names differ.
Health/diagnostic endpoints. A quick "am I in dev mode?" indicator can be implemented as file_exists('config/development.config.php') in a debug controller or an APP_ENV variable — do not couple application logic to running a shell command.
config/development.config.php. It is regenerated on enable and deleted on disable; edits are lost. Edit the .dist instead..dist, ignore the generated file. If config/development.config.php is ever accidentally committed, every developer's local dev state shows up as a diff. Add it to .gitignore immediately.config/... relative to the working directory; vendor/bin/laminas-development-mode enable invoked from elsewhere can miss the project. Composer scripts run from the root, which is why composer development-enable is the safer habit..dist is a hard failure. If the template is absent (stripped by an archive/build rule, or never created), enable cannot proceed. Verify with ls config/development.config.php*.data/cache/, enabling dev mode can appear to do nothing. Ensure the .dist disables config_cache_enabled and module_map_cache_enabled, and clear data/cache/ when in doubt.--no-dev in production removes the binary. That is fine for the normal path (the file should simply be absent), but don't write deploy scripts that require composer development-disable in a --no-dev install.test -f config/development.config.php for scripting, and composer development-status for humans.development.config.php; toggle at build time or exclude the file entirely rather than enabling in place.config/. In containers this often means files created as root; align UIDs or run the toggle as the app user.config/autoload/*.local.php. This package manages exactly one file. Local overrides and secrets belong in the ignored *.local.php files, which the tool never touches — and never put secrets in the committed .dist.data/cache), wrap the command in a Composer script or Makefile target rather than patching vendor/.vendor/bin/laminas-development-mode --help, ls -la config/development.config.php*, git check-ignore -v config/development.config.php, then confirm your entry point actually merges the generated file.How can I help you explore Laravel packages today?