PHP / Intermediate: objects, persistence, and tests / Lesson 8
Namespaces, autoloading, and Composer contracts
Separate class names from file loading and dependency resolution. Understand a PSR-4 mapping and reproducible dependency intent without installing packages or bootstrapping an application.
On this page
Lesson goals
- Resolve names and aliases deliberately.
- Explain autoload mappings and filesystem case.
- Distinguish installation from dependency updates.
A namespace names symbols; it does not locate files
Namespaces organize class-like names, functions, and constants to avoid collisions. A use declaration introduces an alias in the current file; it does not load a file or instantiate a class. Fully qualified names make the intended symbol explicit, while unqualified resolution follows PHP rules that differ for class-like names versus functions and constants. Keep imports at a readable file boundary and choose aliases only when they clarify a real collision. Do not assume that renaming a directory changes a declared namespace or that a namespace declaration automatically registers an autoloader. Those are separate contracts.
Test naming without involving a loader
A single self-contained CLI file can demonstrate a namespaced class and an alias without involving Composer. This isolates language naming from file discovery. In a project, each class would normally have its own mapped source file and a declared namespace matching the naming convention. Do not conclude from this local example that the generated vendor autoloader exists or that a case-sensitive deployment will accept a mismatched filename. A useful diagnosis asks which stage failed: name resolution, autoloader registration, mapped path selection, file loading, or declaration of the expected symbol inside the loaded file.
<?php
declare(strict_types=1);
namespace Training {
final class Course { public function title(): string { return "PDO"; } }
}
namespace {
use Training\Course as CourseRecord;
echo (new CourseRecord())->title(), PHP_EOL;
}
PDO
The class is already declared in this file. use changes the local spelling, not the filesystem or loader state.
PSR-4 is a mapping, not magic discovery
Composer can generate an autoloader from declared namespace-prefix mappings. A prefix such as App\ maps the remaining class name to a path under a configured directory, following PSR-4 conventions. File and class-name case mismatches may pass unnoticed on one filesystem and fail on another. Composer’s generated autoloader must be included through the intended entry point before the class is needed. Avoid adding many manual require statements to conceal a wrong mapping. Diagnose the declared name and expected path, then regenerate loader metadata through the project’s documented workflow when the mapping changes.
Read dependency metadata as a contract
composer.json describes intended constraints and autoload metadata, while composer.lock records a resolved dependency set for reproducible application installation. Installing from a lock and intentionally updating constraints have different purposes. A platform requirement such as PHP or an extension is also part of dependency compatibility. The example decodes a tiny metadata string only; it does not run Composer, download packages, or establish that a source file exists. Treat build scripts and plugins as executable dependencies that require the project’s trust policy, not harmless configuration to run casually while diagnosing a class name.
<?php
declare(strict_types=1);
$prefix = "App" . chr(92);
$metadata = ["autoload" => ["psr-4" => [$prefix => "src/"]]];
$json = json_encode($metadata, JSON_THROW_ON_ERROR);
$decoded = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
echo $decoded["autoload"]["psr-4"][$prefix], PHP_EOL;
src/
The output confirms the declared directory string only. No autoload file is generated or dependency installed.
Repair the first failing boundary
For class not found, inspect the exact fully qualified name, source declaration, mapped path, filename case, and whether the entry point loaded the generated autoloader. For a dependency failure, inspect the lock, runtime version, and required extensions using read-only checks before changing constraints. Do not edit vendor files as a permanent repair or copy one machine’s vendor directory as unexplained evidence of reproducibility. Keep focused language tests independent of an application container, then add a clean-entry smoke check for real autoload integration in the project’s isolated environment. Preserve the smallest failing example and its actual host.
Common errors and fixes
use is expected to include a file
- Why it happens
- Name aliasing and loading are conflated.
- How to fix it
- Register or include the intended loader separately.
- Prevent it next time
- Test symbol naming and entry-point loading independently.
A class works locally but fails on a different filesystem
- Why it happens
- Mapped class or filename case is inconsistent.
- How to fix it
- Align namespace, path, and filename with the mapping.
- Prevent it next time
- Use clean case-sensitive environment checks where relevant.
An update is run to fix an unrelated class typo
- Why it happens
- Loader diagnosis is replaced with broad dependency mutation.
- How to fix it
- Locate the first failing name/path boundary before changing dependencies.
- Prevent it next time
- Separate install, metadata generation, and intentional update workflows.
Check your understanding
1. App\Course should map under src/. What facts should a class-not-found report include?
Show worked solution
Include the full requested name, declared namespace/class, expected mapped file and exact case, and the entry point’s autoloader inclusion. Record runtime and lock context when dependencies are involved. A screenshot of an IDE import alone does not establish runtime file discovery.
2. Why should an application keep composer.lock?
Show worked solution
It records the chosen dependency resolution so installation can reproduce that set instead of selecting a new compatible set each time. It does not remove platform requirements or guarantee every host has required extensions. Updates should be intentional and tested rather than a routine response to a spelling error.
Key takeaways
- Namespace resolution, autoload mapping, and dependency installation are separate.
- Reproducibility includes both locked code and platform capabilities.