Namespaces and use
In the previous lesson files became books on shelves. The more books there are, the more often titles repeat: a library will have five books called "Basics". The shelf tells them apart: "Basics" from the Chemistry shelf and "Basics" from the Music shelf are different books.
Classes work the same way. Your project declares a class User, and a package you downloaded declares a User too. PHP won't accept two classes with one name: at the second declaration the script stops with a fatal error, because the name is already taken.
That is why classes have shelves. A namespace is a prefix PHP adds to the name of every class declared in a file.
The full class name
A namespace is declared by the first statement after <?php: the word namespace and the shelf's name. The parts of the name are separated with a backslash \:
<?php
namespace App\Models;
class User
{
public function __construct(public string $name) {}
}
$u = new User('Anna');
echo User::class, "\n"; // App\Models\User
echo get_class($u), "\n"; // App\Models\UserLet's go through it line by line:
namespace App\Models;puts the prefixApp\Models\on everything declared in this file.- That is why
class Userdeclares a class with the full nameApp\Models\User. - Inside the same namespace you can refer to the class by its short name:
new User('Anna'). User::classandget_class($u)show the class's real name—the full one.
The short name User is only a convenience for writing. PHP works with the full name everywhere, so App\Models\User and Shop\Auth\User are two different classes, and they don't get in each other's way.
Namespace names are written in PascalCase. The first part is usually the name of the project or the company: App, Monolog, Symfony.
use: a short name
From another namespace you refer to a class by its full name: new \App\Models\User('Anna'). A backslash at the start means "look from the root, not on the current shelf". Writing the full name on every line is tedious, so at the top of the file you put use—a bookmark with a short name:
<?php
namespace App\Http;
use App\Models\User;
use App\Models\Order as Purchase;
echo User::class, "\n"; // App\Models\User
echo Purchase::class, "\n"; // App\Models\OrderWhat happens here:
use App\Models\User;says that in this fileUsermeansApp\Models\User;- the word
asgives the bookmark another name:PurchasemeansApp\Models\Order; - bookmarks work only in their own file, and another file needs its own.
A name with as is chosen when two classes from different namespaces have the same name: use Shop\Auth\User as AuthUser;.
Here the picture parts ways with PHP. A real bookmark goes into a book that stands on the shelf. But use does not pull in a file and does not check whether the class exists.
This program has no User or Order classes at all, and the names were printed anyway. The line new User() in it would end with the error Class "App\Models\User" not found.
Functions and constants are different: if the current namespace has none by that name, PHP takes the global one. That is why strlen, count and PHP_INT_MAX work inside namespace App without any use.
Built-in classes: \Exception
PHP's built-in classes—Exception, RuntimeException, InvalidArgumentException—live in the global namespace, at the root. Inside namespace App the short name Exception means App\Exception: PHP looks for the class on the current shelf and does not look into the root.
This is especially sneaky in catch. PHP does not check whether the class in a catch exists; it only compares the thrown object with it. A block with a class that doesn't exist never fires, and there is no error at all:
<?php
namespace App;
try {
throw new \InvalidArgumentException('empty name');
} catch (InvalidArgumentException $e) {
echo 'first', "\n";
} catch (\InvalidArgumentException $e) {
echo 'second: ', $e->getMessage(), "\n"; // second: empty name
}Let's go through it step by step:
throwthrows an object of the global class\InvalidArgumentException.- The first
catchexpectsApp\InvalidArgumentException. No such class exists, and the block is silently skipped. - The second
catchnames the global class with\and catches the exception.
There are two ways to fix the first catch: write \InvalidArgumentException, or put the line use InvalidArgumentException; after namespace. Why catch (Exception $e) inside a namespace catches nothing is a common interview question.
Common mistake
A built-in class was created without the backslash:
<?php
namespace App;
throw new Exception('empty name'); // ✗Fatal error: Uncaught Error: Class "App\Exception" not foundUnlike catch, new requires the class to exist. PHP looks for App\Exception, doesn't find it and throws an Error. The fix is throw new \Exception('empty name'); or the line use Exception; after namespace.
Key points
namespace App\Models;adds a prefix to every class in the file: the full class name isApp\Models\User.use App\Models\User;is a bookmark with a short name for one file; it does not pull in the class's file.- Inside a namespace a class name without
\is looked up on the current shelf, so built-in classes are written as\Exceptionor imported withuse. - A
catchwith a class that doesn't exist silently never fires, whilenewwith such a class throws anError.