Skip to content

Composer, autoloading, and PHP standards

Status: Complete. Last reviewed 2026-08-28.

Composer is both a dependency resolver and an installation/build tool. It turns declared constraints plus repository and platform metadata into a selected dependency graph, records that resolution in a lock file, installs package code, generates autoload metadata, and may execute third-party plugins or scripts. Reproducibility, compatibility, and supply-chain safety depend on treating each responsibility explicitly.

composer.json declares direct package requirements, PHP/extensions and other platform requirements, repositories, stability policy, autoloading, scripts, and configuration. Each package contributes transitive constraints. Composer’s solver searches for one graph satisfying them all; a conflict is evidence that the requested constraints cannot coexist, not a reason to copy random vendor files into the project.

Version operators express ranges. A caret normally allows changes that do not modify the leftmost non-zero component; tilde and explicit ranges encode different upper bounds. They describe what the consumer permits, not proof that the publisher follows semantic versioning or that every permitted release is behaviorally compatible. Prefer constraints justified by tested compatibility and inspect why a version is selected or blocked with Composer’s diagnostic commands.

Minimum stability and per-package stability flags affect which releases are eligible. Broad dev constraints can introduce moving commits and non-repeatable expectations. A production application should normally resolve deliberate tagged versions and review the resulting transitive change set.

composer update runs dependency resolution against current metadata and writes selected package versions and relevant metadata to composer.lock; it then installs that resolution unless told otherwise. composer install uses the committed lock when present and fails or warns when the manifest and lock are inconsistent according to current Composer rules.

Applications should commit composer.lock. CI and deployment should run install, normally with production-appropriate flags, so all environments consume the reviewed graph rather than independently selecting today’s newest permitted packages. Deleting the lock or running unrestricted update during deployment turns a release into an unreviewed dependency upgrade.

Library packages publish constraints for their consumers. Their lock file does not constrain what downstream applications resolve, so libraries need CI against supported low/high or representative dependency ranges. A library may keep a lock for its own contributor tooling, but must not mistake that for consumer compatibility coverage.

The lock improves reproducibility of package selection and distribution metadata; it does not freeze the OS, PHP build, extensions, external services, Composer binary, or all generated artifacts. Build immutable release artifacts in a controlled environment and verify platform requirements in the runtime image.

Platform requirements are part of compatibility

Section titled “Platform requirements are part of compatibility”

Composer represents PHP, extensions, libraries, and Composer APIs as platform packages. Resolution sees the platform on which Composer runs unless a platform configuration simulates another target. Simulating PHP can help resolve for production, but it does not install missing extensions or prove the target actually satisfies them.

--ignore-platform-reqs bypasses a safety boundary. It may be useful for a constrained diagnostic operation, but production use can create a graph that cannot execute. Run composer check-platform-reqs in the built runtime, not only the build environment. Account for CLI-versus-FPM extension and INI differences.

PSR-4 maps a namespace prefix to one or more base directories, then maps the remaining case-sensitive namespace/class segments to subdirectories and a .php filename. It covers classes, interfaces, traits, enums, and similar symbols under the specification’s class term. It does not eagerly load every file, enforce one class per file as an engine rule, validate class contents, or guarantee the expected symbol is actually declared after inclusion.

Composer combines package mappings into its generated autoloader. Classmap rules scan files and record symbol-to-file mappings; files autoload entries are included eagerly and can create global side effects, ordering conflicts, or function-definition collisions. autoload-dev keeps root development/test mappings out of consumer and production autoloading.

Case mismatches may work on a case-insensitive developer filesystem and fail on Linux. Diagnose with the fully qualified class name, generated mapping, installed path/case, optimized mode, and deployment artifact before changing namespaces blindly.

Optimized classmap generation resolves known classes quickly and is a normal production optimization. Authoritative classmaps additionally treat anything absent from the map as nonexistent, avoiding fallback filesystem checks but breaking applications that generate discoverable classes at runtime. APCu autoload caching can cache found and missing lookups without the same authoritative restriction, at the cost of its extension/memory/lifecycle requirements.

Development favors flexible PSR-4 discovery; immutable production favors generated optimized metadata. The deploy must regenerate autoload files after manifest or class changes. OPcache can cache generated classmap PHP files, but OPcache and Composer autoloading solve different layers: one retains compiled scripts, the other locates which script defines a symbol.

Composer plugins extend Composer itself; scripts run commands or PHP callbacks on lifecycle events. They execute with the privileges and credentials of the Composer process. Installing or updating an untrusted package graph is therefore code execution, not merely downloading text.

Review allow-plugins, scripts, installer types, repositories, package provenance, and lock diffs. Avoid running Composer as root, isolate dependency operations in controlled CI/build environments, and use --no-plugins --no-scripts when inspecting genuinely untrusted manifests—while recognizing the resulting install may not be a functional application build.

Run current Composer security/advisory policy and composer audit, review abandoned packages, and define ownership for remediation. An audit is only one feed and does not prove absence of vulnerabilities, malicious maintainer changes, compromised distribution, unsafe scripts, or application misuse. Dependency reduction, locked review, artifact provenance, minimal credentials, and rapid update capability all matter.

PHP-FIG PSRs define narrow contracts between packages. High-value examples include PSR-3 logging, PSR-4 autoloading, PSR-7 HTTP messages, PSR-11 containers, PSR-12 style, PSR-15 server middleware, and PSR-18 HTTP clients. Implementing one does not imply implementing another, and framework adapters may expose richer APIs around them.

Use a PSR when interchangeability at that seam is valuable. Do not wrap every Laravel facility merely to claim standard compliance. A PSR-11 container interface intentionally exposes service lookup; constructor injection remains an application design choice. PSR-7 immutability describes message mutation methods, not the immutability of arbitrary stream contents or external I/O.

  1. Change constraints deliberately and resolve in a development branch.
  2. Review direct and transitive lock changes, scripts/plugins, advisories, licenses, and platform effects.
  3. Run unit/integration/static checks against the resolved graph and supported platforms.
  4. Build with locked install, production autoload optimization, and least privilege.
  5. Verify runtime platform requirements and artifact contents.
  6. Deploy immutably, retain rollback/roll-forward artifacts, and monitor dependency-related behavior.
  • Current: Composer 2, committed application locks, controlled plugin permission, audit policy, optimized production autoloading, and explicit platform verification are expected on PHP 8.5.
  • Common: PHP 8.2–8.4 estates often contain mature constraints and framework discovery scripts; upgrades must account for those executable hooks.
  • Legacy: committed vendor trees, PSR-0, custom autoloaders, global PEAR-style dependencies, unconstrained dev branches, and deployment-time update require staged replacement with artifact and compatibility tests.