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.
| Concept | What it protects | Decision rule |
|---|---|---|
| Namespace | Globally unique names | Mirror a stable domain or package boundary. |
| PSR-4 | Predictable file loading | Match namespace prefixes to source directories. |
| Lock file | Reproducible installs | Commit 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.
- Describe the package boundary boundary: its inputs, outputs, invariants, and expected failures.
- Implement the smallest happy path behind an explicit contract.
- Add validation and translate low-level failures into language the caller understands.
- Exercise the boundary with realistic data, then inspect output, logs, and resource cleanup.
- Refactor only after behavior is protected by a repeatable check.
Make the boundary visible
Guided code lab
Declare an application package
Composer loads App classes from src and exposes useful quality scripts.
{
"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.
<?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
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
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.