PHP Namespaces and Autoload — PSR-4, Composer Autoloading, and File Organization
In this tutorial, you will learn about PHP Namespaces and Autoload. We cover key concepts, practical examples, and best practices to help you master this topic.
PHP namespaces organize code into logical groups preventing class name collisions, while PSR-4 autoloading with Composer automatically loads class files based on namespace-to-directory mapping without manual require statements.
Why It Matters
Without namespaces, every class in your application and its dependencies must have a globally unique name. With thousands of Composer packages available, name collisions are inevitable. Namespaces solve this by grouping classes under a vendor prefix. Autoloading eliminates the tedious require or include statements. You define a class in its file, and Composer loads it automatically when you reference it.
Real-World Use
Every modern PHP framework uses namespaces and Composer autoloading. Laravel's namespaces start with App and Illuminate. Symfony uses Symfony and App namespaces. PHPUnit uses PHPUnit namespace. When you run composer install, it generates a vendor/autoload.php file that handles all loading. DodaTech's PHP tools follow the App namespace structure with PSR-4 autoloading.
What You Will Learn
- Declaring namespaces with the namespace keyword
- Importing classes with use statements
- PSR-4 namespace-to-directory mapping
- Setting up Composer autoloading
- The spl_autoload_register function
- Best practices for namespace organization
Learning Path
flowchart LR A[Inheritance and Traits] --> B[Namespaces and Autoload
You are here] B --> C[PDO] C --> D[MVC Pattern] D --> E[DI Container] style B fill:#f90,color:#fff
What Are Namespaces?
A namespace is a container for related classes, interfaces, functions, and constants. They prevent name collisions:
<?php
// File: src/Models/User.php
namespace App\Models;
class User {
public function __construct(
public string $name,
public string $email,
) {}
}
<?php
// File: src/Security/User.php
namespace App\Security;
class User {
public function __construct(
public string $username,
public string $token,
) {}
}
These two User classes coexist because they are in different namespaces. You reference them by their fully qualified name: App\Models\User and App\Security\User.
Importing with Use
The use statement imports a class so you can reference it without the full namespace:
<?php
// Without use -- full path every time
$user = new \App\Models\User('Alice', 'alice@example.com');
$logger = new \App\Logging\FileLogger('/tmp/log.txt');
// With use -- clean references
use App\Models\User;
use App\Logging\FileLogger;
$user = new User('Alice', 'alice@example.com');
$logger = new FileLogger('/tmp/log.txt');
Aliasing
Resolve naming conflicts with aliases:
<?php
use App\Models\User as ModelUser;
use App\Security\User as SecurityUser;
$modelUser = new ModelUser('Alice', 'alice@example.com');
$securityUser = new SecurityUser('alice', 'token123');
Importing Functions and Constants
PHP 5.6 added import for functions and constants:
<?php
use function App\Helpers\formatDate;
use const App\Config\MAX_RETRIES;
echo formatDate(new DateTime());
echo MAX_RETRIES;
PSR-4 Autoloading
PSR-4 maps namespaces to directory structures. The namespace prefix maps to a base directory, and the rest maps to subdirectories:
Namespace: App\Models\User
File path: src/Models/User.php
The App\ prefix maps to the src/ directory. Everything after App\ becomes the directory path relative to src/.
Directory Structure
project/
├── src/
│ ├── Models/
│ │ ├── User.php # App\Models\User
│ │ └── Product.php # App\Models\Product
│ ├── Controllers/
│ │ └── UserController.php # App\Controllers\UserController
│ ├── Services/
│ │ └── PaymentService.php # App\Services\PaymentService
│ └── Exceptions/
│ └── ValidationException.php # App\Exceptions\ValidationException
├── public/
│ └── index.php
├── vendor/
│ └── autoload.php
└── composer.json
Composer Autoloading Configuration
In composer.json, define your namespace mapping:
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"require": {
"php": ">=8.1"
}
}
After editing composer.json, regenerate the autoloader:
composer dump-autoload
In your application entry point, require the autoloader:
<?php
// public/index.php
require __DIR__ . '/../vendor/autoload.php';
use App\Controllers\UserController;
use App\Models\User;
$controller = new UserController();
$user = new User('Alice', 'alice@example.com');
That is it. No require statements for individual files. Composer loads them automatically.
Manual Autoloading with spl_autoload_register
If you are not using Composer, you can register your own autoloader:
<?php
spl_autoload_register(function (string $class): void {
// Convert namespace to file path
$prefix = 'App\\';
$baseDir = __DIR__ . '/src/';
// Check if the class uses the prefix
if (strncmp($prefix, $class, strlen($prefix)) !== 0) {
return; // Not our namespace
}
// Remove prefix and convert to path
$relativeClass = substr($class, strlen($prefix));
$file = $baseDir . str_replace('\\', '/', $relativeClass) . '.php';
if (file_exists($file)) {
require $file;
}
});
// Now we can use classes without require
use App\Models\User;
$user = new User('Alice', 'alice@example.com');
Namespace Organization Best Practices
Vendor Prefix
Use a top-level namespace matching your organization or project:
App\ -- Application code
Vendor\ -- For reusable packages
Standard Structure
App\
├── Controllers\ -- Request handlers
├── Models\ -- Data entities
├── Services\ -- Business logic
├── Repositories\ -- Data access
├── Exceptions\ -- Custom exceptions
├── Helpers\ -- Utility functions
└── Config\ -- Configuration
Multiple Autoload Prefixes
You can define multiple PSR-4 prefixes in composer.json:
{
"autoload": {
"psr-4": {
"App\\": "src/",
"App\\Tests\\": "tests/"
}
}
}
Or for multiple source directories under the same namespace:
{
"autoload": {
"psr-4": {
"App\\": ["src/", "lib/"]
}
}
}
Classmap and Files Autoloading
Composer also supports classmap and files autoloading:
{
"autoload": {
"classmap": [
"legacy/"
],
"files": [
"src/helpers.php"
]
}
}
- Classmap: Scans directories for classes and creates a map. Good for legacy code without namespaces.
- Files: Always loads specific files. Use for global helper functions.
Common Mistakes
1. Forgetting the Leading Backslash in Fully Qualified Names
$user = new App\Models\User(); // Relative, may resolve incorrectly
$user = new \App\Models\User(); // Absolute, always correct
2. Not Running composer dump-autoload After Adding Classes
If Composer cannot find your new class, run composer dump-autoload to regenerate the autoloader.
3. Mismatched Namespace and Directory
// File: src/Models/User.php
namespace App\Model; // Wrong! Should be App\Models
The namespace must exactly match the directory structure for PSR-4.
4. Multiple Classes in One File
PHP allows multiple classes in one file, but PSR-4 expects one class per file with the filename matching the class name.
5. Case Sensitivity
On case-sensitive filesystems (Linux), src/models/User.php does not match App\Models\User. The directory and filename case must match the namespace.
Practice Questions
How does PSR-4 map namespaces to file paths? The namespace prefix maps to a base directory. The remaining namespace segments map to subdirectories. The class name maps to the filename.
What does composer dump-autoload do? It regenerates the vendor/autoload.php file with updated class mappings and directory scans.
Why use the use statement? It imports a class into the current namespace scope so you can reference it by its short name instead of the fully qualified name.
What is the difference between PSR-4 and classmap autoloading? PSR-4 maps namespaces to directories dynamically. Classmap scans directories and generates a static map. PSR-4 does not need regeneration after adding files.
Challenge: Create a project structure with src/, tests/, public/ directories, configure composer.json with PSR-4 autoloading for App and App\Tests namespaces, and create classes in each directory.
Mini Project: Application Bootstrap
Create a minimal application with namespaces and autoloading:
<?php
// composer.json
// {
// "autoload": {
// "psr-4": {
// "App\\": "src/"
// }
// }
// }
<?php
// src/Models/User.php
namespace App\Models;
class User {
public function __construct(
public readonly int $id,
public readonly string $name,
public readonly string $email,
) {}
}
<?php
// src/Repositories/UserRepository.php
namespace App\Repositories;
use App\Models\User;
class UserRepository {
/** @var User[] */
private array $users = [];
public function save(User $user): void {
$this->users[$user->id] = $user;
}
public function findById(int $id): ?User {
return $this->users[$id] ?? null;
}
public function findAll(): array {
return array_values($this->users);
}
}
<?php
// src/Services/UserService.php
namespace App\Services;
use App\Models\User;
use App\Repositories\UserRepository;
class UserService {
public function __construct(
private readonly UserRepository $repository,
) {}
public function register(string $name, string $email): User {
$id = count($this->repository->findAll()) + 1;
$user = new User($id, $name, $email);
$this->repository->save($user);
return $user;
}
}
<?php
// public/index.php
require __DIR__ . '/../vendor/autoload.php';
use App\Services\UserService;
use App\Repositories\UserRepository;
$service = new UserService(new UserRepository());
$user = $service->register('Alice', 'alice@example.com');
echo "User created: {$user->name} ({$user->email})\n";
FAQ
What is Next
Now that you understand namespaces, proceed to PDO to learn database access with prepared statements. Then read MVC Pattern to understand the architectural pattern behind most PHP frameworks.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro