Namespaces in a Real Project
A namespace is a prefix on a name, not a folder; how to declare one, import it, alias it, and read the backslashes.
namespace Shop\Billing; use Vendor\Pdf\Invoice as PdfInvoice;A namespace is a prefix PHP attaches to the names of your classes, interfaces, traits, enums, functions and constants. It exists because two libraries may both want a class called Invoice. With namespaces one is Shop\Billing\Invoice and the other is Vendor\Pdf\Invoice, and nothing collides.
Declaring and importing
The namespace declaration is the first statement in the file, after declare(strict_types=1);. Everything defined below it carries that prefix. In another file, use Shop\Billing\Invoice; lets you write Invoice as a short name. use Shop\Billing\Invoice as Bill; gives it a second short name, which is how you handle two imports whose last part is identical.
A leading backslash means start from the root. \DateTimeImmutable is the global class, never Shop\Billing\DateTimeImmutable.
Shorthand and longhand
Three separate use lines and one group import, use Shop\Billing\{Invoice, Receipt, Refund};, are the same thing — but write both forms in the same file and PHP refuses to compile it, because a short name can only be imported once. So are Invoice::class and the string 'Shop\Billing\Invoice' — but ::class is resolved at compile time against the use statements in that file, so it keeps pointing at the right class when you change the use line, and an editor or static analyser can follow it. It does not check that the class exists, so a misspelt Invoce::class quietly produces the string Shop\Billing\Invoce instead of raising an error.
A namespace is not a directory
Nothing in PHP connects Shop\Billing to a folder named Billing. A use statement loads no file at all; it only renames. The directory layout matters because an autoloader chooses to map one onto the other, which is the next lesson.
Example
When to use it
- Publish a library whose Request class will not collide with the application's own Request.
- Import two Logger classes from different packages into one file by aliasing one of them.
- Split a growing src/ folder into Billing, Reporting and Http without renaming a single class.
- Read someone else's file and work out which package a short class name actually came from.
More examples
One namespace per file, as a project actually looks
This is the shape every real project uses: one type per file, the namespace mirroring the folder path under src/, and a use statement at the top of any file that mentions a type from elsewhere. Note that MonthlyTotal.php contains no require line. Nothing loads Invoice.php here, and the file still works, because an autoloader will resolve the name on first use. Write it this way from the start.
<?php
// src/Billing/Invoice.php
declare(strict_types=1);
namespace Shop\Billing;
final class Invoice
{
public function __construct(
public readonly string $reference,
public readonly int $cents,
) {
}
}
<?php
// src/Reporting/MonthlyTotal.php
declare(strict_types=1);
namespace Shop\Reporting;
use Shop\Billing\Invoice;
final class MonthlyTotal
{
/** @param list<Invoice> $invoices */
public function sum(array $invoices): int
{
return array_sum(array_map(static fn (Invoice $i): int => $i->cents, $invoices));
}
}Group imports, and importing functions and constants
Reach for the group form when a file imports several names from one namespace; it is purely cosmetic and the two blocks above mean exactly the same thing. The second half is the part people miss: a plain use imports only class-like names. Importing a namespaced function or constant requires use function or use const, and without them PHP looks for Shop\Http\format_money, then the global one, and reports neither exists.
<?php
declare(strict_types=1);
namespace Shop\Http;
// Longhand: one line per name. Commented out because PHP rejects a file
// that imports the same short name twice:
// Fatal error: Cannot use Shop\Billing\Invoice as Invoice
// because the name is already in use
// use Shop\Billing\Invoice;
// use Shop\Billing\Receipt;
// use Shop\Billing\Refund;
// Shorthand: the same three imports, grouped. Identical meaning.
use Shop\Billing\{Invoice, Receipt, Refund};
// Functions and constants need their own keyword.
use function Shop\Support\format_money;
use const Shop\Support\CURRENCY;
final class InvoiceController
{
public function show(Invoice $invoice): string
{
// format_money() here is Shop\Support\format_money, not a global.
return format_money($invoice->cents) . ' ' . CURRENCY;
}
}Resolving a genuine name collision with an alias
Two packages both chose the name Invoice. You cannot import both under their own short name, and PHP says so with a fatal error at compile time rather than silently preferring one. The alias is the fix, and the convention is to alias the less central of the two so the important name stays short. The alternative is to drop one use and write \Vendor\Pdf\Invoice in full every time, which is correct but noisy.
<?php
declare(strict_types=1);
namespace Shop\Checkout;
use Shop\Billing\Invoice;
use Vendor\Pdf\Invoice as PdfInvoice;
final class InvoicePrinter
{
public function render(Invoice $invoice): string
{
// Both short names are in scope and point at different classes.
$document = new PdfInvoice();
$document->addLine($invoice->reference, $invoice->cents);
return $document->toString();
}
}
// Without the alias this file cannot be written at all:
// use Shop\Billing\Invoice;
// use Vendor\Pdf\Invoice; <- Fatal error: Cannot use ... as Invoice
// because the name is already in use
Discussion