Article
Composer Modernization Center
Add Composer to legacy PHP projects, migrate includes to autoloading, set platform constraints, and replace abandoned packages without a big-bang rewrite.
Legacy PHP applications often boot with a forest of require_once calls, a hand-copied library folder, and no lockfile. Composer is the standard way to declare dependencies, generate autoloaders, and keep PHP platform requirements honest—but adopting it mid-life must be staged so production keeps shipping while you gain control.
This center is the Composer modernization index for ZendStudio.net. Use it when you inherited a project without composer.json, when installs fail on platform PHP, or when an abandoned package blocks a runtime upgrade. It pairs with the runtime ladders in PHP 5 to modern PHP and PHP 7 to PHP 8.
What Composer fixes in legacy apps
- Replaces ad-hoc library copies with versioned packages and a reproducible
composer.lock - Generates classmaps or PSR-4 autoload maps so new code does not need new includes
- Documents the PHP version and extensions your app actually needs via
config.platformandrequire.php - Surfaces abandoned packages and security advisories before you discover them in production
- Makes staging and production installs repeatable instead of “copy vendor from Steve’s laptop”
Adoption path (keep the site running)
- Commit a backup branch; never run destructive Composer commands only on production.
- Add a minimal
composer.jsonbeside the real application root (see Adding Composer). - Require
vendor/autoload.phponce from the front controller—keep existing includes until classes move. - Migrate hot paths from manual includes to PSR-4 or classmap (Manual includes → autoloading).
- Pin platform PHP to match staging/production (Platform constraints).
- Replace or fork abandoned packages on a schedule (Abandoned packages).
# LEGACY — bootstrap
require_once __DIR__ . '/lib/db.php';
require_once __DIR__ . '/lib/User.php';
require_once __DIR__ . '/lib/helpers.php';
require_once __DIR__ . '/vendor-manual/phpmailer/class.phpmailer.php';
# MODERN — bootstrap
declare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
# Application classes load via Composer PSR-4 / classmap.
# Keep transitional require_once only for files that are not classes yet.
Platform honesty beats ignore flags
Developer laptops on newer PHP will happily resolve packages your production 7.4/8.1 host cannot install. Set config.platform.php (and extension platform packages when needed) to the version you actually run. Validate with Composer Platform Requirement Checker and explain constraints with constraint explainer. Avoid permanent --ignore-platform-reqs—it hides the mismatch you need to fix.
# TRANSITIONAL composer.json fragment
{
"require": {
"php": ">=8.2"
},
"config": {
"platform": {
"php": "8.2.0"
}
},
"autoload": {
"psr-4": { "App\\": "src/" },
"classmap": ["legacy/lib/"]
}
}
Lockfiles, install vs update
Prefer composer install from a committed lockfile on servers. Use composer update on a branch with tests when you intentionally change versions. Treat lockfile commits as release artifacts. If you must vendor dependencies for a constrained host, document why and how rebuilds happen—do not leave mystery vendor/ trees without a recipe.
Autoloading strategies for mixed trees
- classmap — fastest transitional win for sprawling legacy directories
- PSR-4 — required for new modules; guide: PSR-4 modernization
- files — last resort for function libraries; shrink over time
Validate suspicious paths with the PSR-4 Checker and JSON shape with Composer.json Validator.
Abandoned packages and security advisories
Runtime upgrades stall when a package is abandoned or incompatible with PHP 8. Schedule replacements; do not wait for a production fatal. Run composer audit regularly and track abandoned notices from composer outdated / update output. Deep guide: Abandoned packages. Security context: PHP security modernization.
Framework-specific notes
- ZF2/ZF3 → Laminas — use the official global
laminas-migrationflow; it removesvendor/and lock as part of migration. See ZF→Laminas. - ZF1 — Composer-ize your code even if the ZF1 library stays vendored; laminas-migration is not the ZF1 path (ZF1 Composer).
- Inherited apps — add Composer before large refactors (inherited playbook).
Guides in this section
- Adding Composer to a legacy project — nested deep guide
- PSR-4 autoloading for modernization
- Platform and extension constraints
- Abandoned packages and replacements
- Manual includes → autoloading
- Incremental / strangler modernization
- Managing legacy Composer dependencies
Workbench tools
- Composer.json Validator
- PSR-4 Checker
- Composer Version Constraint Explainer
- Composer Platform Requirement Checker
- PHP Environment Compare
- Config Diff
Commands that need caution
Prefer composer install from a committed lockfile on servers. Use composer update on a branch with tests. Avoid --ignore-platform-reqs as a permanent production habit. Before large dependency moves, back up the tree and database, and validate on staging that matches production PHP and extensions. After major updates, re-run smoke tests for login, payments, and cron.
Official reference: getcomposer.org/doc.
Minimal composer.json that unblocks modernization
You do not need a perfect package layout on day one. A minimal file that autoloads first-party code and pins PHP is enough to start. Expand requires as you replace zip-dropped libraries. Validate JSON with Composer.json Validator before committing.
# TRANSITIONAL minimal shape
{
"name": "example/legacy-app",
"require": {
"php": ">=7.4"
},
"config": {
"platform": { "php": "7.4.33" }
},
"autoload": {
"classmap": ["src/", "lib/"]
}
}
Raise the platform pin when the runtime ladder moves—never leave the pin years behind production without a written reason.
CI pattern that catches platform lies
- Job A:
composer install --no-devon the production PHP version - Job B: same on the next candidate PHP
- Fail if lock cannot install without ignore flags
- Run smoke tests after install
Environment drift between laptop and server is the most common Composer failure mode in inherited apps—catch it in CI with environment compare.
When vendor commits are unavoidable
Some hosts block outbound Composer during deploy. If you must commit vendor/, document the exact Composer version and command used to generate it, and still keep composer.lock. Treat vendor commits as a packaging strategy, not as an excuse to edit third-party code in place.
Related modernization paths: incremental modernization, testing legacy PHP, and the PHP version migration center.
Related reading
- PHP 5 to Modern PHP: Complete Incremental Migration Guide A deep, production-minded path from PHP 5.x codebases to supported PHP 8.x: removed extensions, charset, PDO,…
- PHP Security Modernization for Legacy Applications Upgrade inherited PHP security practices: prepared statements, password hashing, sessions, CSRF, XSS escaping…
- PHP 7 to PHP 8 Migration Guide Deep guide to PHP 8.0 breaking changes that matter for PHP 7 applications, with upgrade tactics through suppo…
- Inheriting a Legacy PHP Application A first-30-days playbook for developers handed an unfamiliar PHP codebase: runtime truth, risk triage, and sa…
- Manual Includes to Composer Autoloading Stage a migration from require/include trees to Composer classmap and PSR-4 without deleting every include on…