Phase 3 · Object-Oriented & Modern PHPModule 17~46 min read

Namespaces, Autoloading & Composer

Organize professional projects with namespaces, PSR-4 autoloading, Composer packages, and reproducible dependencies.

What you'll learn

Organize PHP as an installable, reproducible project. Namespaces prevent collisions, PSR-4 maps names to files, and Composer records dependencies so every environment runs the same code.

By the end of this lesson, you'll be able to:

  • Map namespaces with PSR-4
  • Install and load packages through Composer
  • Use version constraints and composer.lock correctly

Core mental model

Professional PHP is less about memorizing APIs and more about choosing a clear boundary for each responsibility. Use this table as a decision guide while reading the examples.

ConceptWhat it protectsDecision rule
NamespaceGlobally unique namesMirror a stable domain or package boundary.
PSR-4Predictable file loadingMatch namespace prefixes to source directories.
Lock fileReproducible installsCommit it for applications; CI uses composer install.

Professional workflow

Build the feature in small, verifiable steps. Each step leaves the system in a state you can test.

  1. Describe the package boundary boundary: its inputs, outputs, invariants, and expected failures.
  2. Implement the smallest happy path behind an explicit contract.
  3. Add validation and translate low-level failures into language the caller understands.
  4. Exercise the boundary with realistic data, then inspect output, logs, and resource cleanup.
  5. Refactor only after behavior is protected by a repeatable check.

Make the boundary visible

Name inputs, outputs, side effects, and failure cases before adding framework or infrastructure code. That habit keeps advanced PHP understandable as the application grows.

Guided code lab

Declare an application package

Composer loads App classes from src and exposes useful quality scripts.

composer.json
{
  "name": "academy/course-app",
  "require": { "php": "^8.3", "psr/log": "^3.0" },
  "autoload": { "psr-4": { "App\\": "src/" } },
  "autoload-dev": { "psr-4": { "Tests\\": "tests/" } },
  "scripts": { "test": "phpunit", "analyse": "phpstan analyse" }
}

Load a namespaced service

After composer dump-autoload, the class name deterministically resolves to its file.

public/index.php
<?php
declare(strict_types=1);
require dirname(__DIR__) . '/vendor/autoload.php';

use App\Enrollment\EnrollmentService;
use Psr\Log\LoggerInterface;

function bootstrap(LoggerInterface $logger): EnrollmentService
{
    return new EnrollmentService($logger);
}

Production practice

Contract

Treat composer.json as the package contract and avoid application code in the global namespace.

Verification

Run composer validate, composer install, the test script, and an optimized autoload build in CI.

Operations

Audit dependencies, review lock-file changes, and keep platform PHP extensions explicit.

Common failure mode

Running composer update during deployment changes versions at the riskiest moment. Resolve versions earlier and deploy the lock file.

Independent workshop

Convert a multi-file manual-require project to PSR-4 and add one small third-party package.

Your finished workshop must include:

  • Valid composer.json and committed lock file
  • Namespaced src and tests trees
  • Documented install, test, and analysis commands

Definition of done

Run the happy path and at least two failure paths, explain one design tradeoff in a short README, and leave the code formatted and ready for review.

Recap & quick check

Key takeaways

  • Namespaces identify code; autoloading locates it.
  • PSR-4 replaces chains of manual require calls.
  • composer.lock makes builds repeatable.
  • Dependency upgrades deserve review and tests.

Quick check

1. Which command respects locked application versions?

2. What does PSR-4 map?

3. When should an app commit composer.lock?

Keep the workshop: later phases deliberately build on these boundaries, so today's small example can become part of your portfolio architecture.