Article

Adding Composer to a Legacy PHP Project

Introduce composer.json, a lockfile, and vendor/autoload.php into an existing application while keeping the current include bootstrap working.

Adding Composer is usually the first reversible modernization win: you gain a dependency contract without rewriting business logic. The goal of the first pull request is not a perfect PSR-4 tree—it is a committed composer.json/composer.lock, a generated vendor/ on deploy, and a single autoload require that coexists with the old bootstrap.

Before you run Composer

  • Create a version-control branch and a filesystem/database backup of the environment you will change
  • Identify the true application root (the directory that should own composer.json, usually above the web document root)
  • Note the production PHP version and required extensions with php -v and php -m on the same SAPI you deploy
  • List third-party libraries currently copied into the tree—those become early require candidates
LEGACY — project layout
/var/www/app/
  public_html/          ← document root (index.php)
  lib/
  config/
  templates/
  third_party/phpmailer/
MODERN — Composer at app root
/var/www/app/
  composer.json
  composer.lock
  vendor/
  public/               ← document root
  src/
  config/
  templates/

Minimal first composer.json

MODERN — starter composer.json
{
  "name": "acme/legacy-app",
  "description": "Internal application",
  "type": "project",
  "require": {
    "php": ">=7.4"
  },
  "config": {
    "platform": {
      "php": "7.4.33"
    },
    "sort-packages": true
  },
  "autoload": {
    "classmap": ["lib/"]
  }
}

Set platform.php to the PHP version you actually run in staging/production so developers on newer laptops do not resolve packages you cannot install on the server. Raise the constraint when the runtime ladder moves—see platform constraints.

Wire the autoloader without deleting includes

TRANSITIONAL — front controller

install vs update

On servers and CI deploy jobs, run composer install --no-dev --optimize-autoloader from a committed lockfile. Run composer update only on a development branch when you intend to change versions. Do not treat update as a routine deploy step for a legacy app—transitive upgrades are modernization work that need tests.

Backup and rollback

Keep the previous release artifact or vendor directory available until the new deploy proves healthy. If autoload paths are wrong, the failure mode is often a flood of class-not-found fatals—revert the release rather than hot-editing vendor on the server.

Validate the JSON and platform story

Next: includes → autoloading, PSR-4. Official intro: Composer getting started.

Related tools

Related reading