Article

PSR-4 Autoloading During PHP Modernization

Map namespaces to directories correctly, avoid classmap-only traps, and migrate legacy class names toward PSR-4 without breaking production.

PSR-4 maps a namespace prefix to a base directory so App\Billing\Invoice resolves to src/Billing/Invoice.php. It is the default for new PHP code and the target state for modernization—but many legacy classes have no namespace and inconsistent filenames. Classmap is the bridge; PSR-4 is the destination for code you actively own.

LEGACY — unnamespaced class file
MODERN — PSR-4 class
MODERN — composer.json autoload
{
  "autoload": {
    "psr-4": {
      "App\\": "src/"
    }
  },
  "autoload-dev": {
    "psr-4": {
      "App\\Tests\\": "tests/"
    }
  }
}

Rules that break in real migrations

  • Namespace prefix and directory must agree character-for-character after the base path
  • Class name must match the filename (InvoiceHelper.php)
  • Falling back to scanning the whole project with classmap forever hides structural debt
  • Case-sensitive production filesystems fail when laptops are case-insensitive

Migration tactics

Keep old unnamespaced classes on classmap. Introduce namespaces for new modules under src/. For high-churn classes, create a namespaced wrapper that delegates to the legacy class (strangler), then swap call sites. After composer dump-autoload, prove both web and CLI entry points resolve the symbol.

Use the PSR-4 Checker on suspicious paths and the Composer.json Validator before merging autoload edits. Parent index: Composer modernization.

Related tools

Related reading