Namespaces and Autoloading
Real applications have hundreds of classes, plus thousands more from libraries. Namespaces give classes unique, organised names — App\Models\User rather than just User — so they never clash. Autoloading loads each class's file automatically the first time it's used, so you never write long lists of require statements.
Declaring a namespace
A namespace declaration at the top of a file puts everything in that file into the namespace. The class's full name then includes it:
<?php namespace App\Models; class User { public function __construct(public string $name) {} } $u = new User("Ana"); echo get_class($u), "\n"; echo User::class, "\n"; echo __NAMESPACE__, "\n";
App\Models\User App\Models\User App\Models
Namespaces use backslashes as separators. By convention they're PascalCase, start with a vendor or project name (App, Acme), and mirror the folder structure.
use: importing names
From another namespace, refer to a class by its full name, or import it with use and then use the short name. as gives an alias, which resolves clashes between classes with the same short name:
<?php namespace App\Models; class User { public function __construct(public string $name) {} }
<?php namespace App\Http; class User { public function __construct(public string $ip) {} }
<?php namespace App; require __DIR__ . '/src/Models/User.php'; require __DIR__ . '/src/Http/User.php'; use App\Models\User; use App\Http\User as HttpUser; $account = new User("Ana"); $visitor = new HttpUser("203.0.113.7"); $fullName = new \App\Models\User("Ben"); // leading \ = fully qualified echo $account->name, " ", $visitor->ip, " ", $fullName->name, "\n"; echo get_class($visitor), "\n";
Ana 203.0.113.7 Ben App\Http\User
Global functions and classes
Built-in classes live in the global namespace. Inside a namespace, an unqualified class name is looked up in the current namespace, so built-in classes need a leading backslash or a use statement. Functions and constants fall back to the global ones automatically:
<?php namespace App\Services; use DateTimeImmutable; $date = new DateTimeImmutable('2026-01-01'); // via use $obj = new \ArrayObject([1, 2, 3]); // via leading backslash echo $date->format('Y'), " ", count($obj), " ", strtoupper("functions just work"), "\n"; $broken = new Exception("oops");
2026 3 FUNCTIONS JUST WORK
Fatal error: Uncaught Error: Class "App\Services\Exception" not found in main.php:11
Stack trace:
#0 {main}
thrown in main.php on line 11That last line is one of the most common namespace mistakes: Exception was looked up as App\Services\Exception. Add use Exception; or write \Exception.
Autoloading
spl_autoload_register registers a function that PHP calls whenever it meets a class it hasn't loaded yet. The function turns the class name into a file path and requires it:
<?php namespace Shop; class Product { public function __construct(public string $name, public float $price) {} }
<?php namespace Shop; class Cart { /** @var Product[] */ private array $items = []; public function add(Product $p): void { $this->items[] = $p; } public function total(): float { return array_sum(array_map(fn($p) => $p->price, $this->items)); } }
<?php spl_autoload_register(function (string $class): void { $file = __DIR__ . '/src/' . str_replace('\\', '/', $class) . '.php'; if (is_file($file)) { echo "(autoloading $class)\n"; require $file; } }); use Shop\Cart; use Shop\Product; $cart = new Cart(); $cart->add(new Product("Pen", 1.20)); $cart->add(new Product("Notebook", 3.50)); echo "Total: ", $cart->total(), "\n";
(autoloading Shop\Cart) (autoloading Shop\Product) Total: 4.7
Each file is loaded only when its class is first needed — Product wasn't loaded until new Product ran.
PSR-4 and Composer
The convention used above — namespace segments map to folders, and the class name to the file name — is standardised as PSR-4. In practice you don't write the autoloader yourself: Composer generates one from a few lines of configuration (lesson 60):
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
With that, App\Models\User is loaded from src/Models/User.php, and your entry script needs just one line: require __DIR__ . '/vendor/autoload.php';.
A typical project layout
my-app/ ├── composer.json ├── public/ │ └── index.php the only file the web server exposes ├── src/ App\ namespace (PSR-4) │ ├── Controllers/ │ ├── Models/ │ └── Services/ ├── templates/ ├── tests/ └── vendor/ installed by Composer, not committed to Git
Keeping only public/ reachable from the web means your source code, configuration and libraries can never be downloaded directly by visitors.
Grouped imports and functions
Several classes from the same namespace can be imported at once, and functions and constants can be imported with use function and use const:
<?php namespace App; use function array_map as map; use const PHP_EOL as EOL; echo implode(",", map(fn($n) => $n * 2, [1, 2, 3])), EOL;
2,4,6
Try it yourself
Put two classes in the Geometry namespace and use them from a script in another namespace.
Geometry\Circle: 7.07