PHPNamespaces

Namespaces

A namespace is a naming container for classes, functions, and constants, similar in spirit to a folder for files. Without namespaces, every class in a PHP project — and every class in every third-party library it depends on — shares one single global naming pool. Once a project pulls in a handful of libraries, it becomes almost guaranteed that two of them will each define a class called something generic like Logger, Request, or Collection, and PHP has no way to tell those apart without namespaces. A namespace lets both classes exist under the same short name while remaining completely distinct to PHP, because their full identity includes the namespace they live in.

Declaring a namespace

A namespace is declared with the namespace keyword, and it must be the very first statement in the file — nothing except a leading comment or the opening <?php tag is allowed before it. Everything declared in that file (classes, functions, constants) now belongs to that namespace.

src/Billing/Invoice.php

PHP
<?php

namespace App\Billing;

class Invoice
{
    public function __construct(public float $amount)
    {
    }

    public function summary(): string
    {
        return "Invoice for \${$this->amount}";
    }
}

The class declared here isn't really named Invoice as far as PHP is concerned — its full, unambiguous name is App\Billing\Invoice. Anywhere outside this namespace, referring to plain Invoice will not find it; you need either the full name or a use import, covered next.

Importing with use, and aliasing

Typing the full namespaced name every time would be tedious, so PHP provides use statements near the top of a file (right after the namespace line, if there is one) to import a name and refer to it by its short form for the rest of the file. If two imported names would collide, or you simply want a clearer local name, use ... as ... renames the import for that file only.

Importing and aliasing with use

PHP
<?php

namespace App\Reports;

use App\Billing\Invoice;
use App\Billing\Logger as BillingLogger;
use App\Audit\Logger as AuditLogger;

$invoice = new Invoice(150.00);
echo $invoice->summary(), "\n";

$billingLog = new BillingLogger();
$auditLog = new AuditLogger();

Invoice resolves to App\Billing\Invoice because of the first use line. The two Logger classes — one from App\Billing, one from App\Audit — would collide under the same short name Logger, so each is imported under a distinct alias, BillingLogger and AuditLogger. Without the as clause, importing both in the same file would be a fatal "cannot use ... because the name is already in use" error.

Fully-qualified vs relative names

A name that starts with a leading backslash, like \App\Billing\Invoice, is a fully-qualified name: it is resolved from the absolute global root no matter what namespace the current file is in. A name without the leading backslash is relative — PHP resolves it against the current namespace first (unless it was imported with use, or it's a recognized global like strlen() or Exception). This distinction mostly matters when you're referring to something from inside a namespaced file without importing it first.

Fully-qualified access without a use import

PHP
<?php

namespace App\Reports;

// No "use" statement for Invoice here.
$invoice = new \App\Billing\Invoice(75.50);
echo $invoice->summary(), "\n";

// PHP's built-in classes and functions are always reachable
// from the global namespace without a leading backslash needed
// in most cases, but it's unambiguous either way:
$now = new \DateTime();
echo $now->format('Y') . "\n";

Leaving off the leading backslash and just writing App\Billing\Invoice from inside the App\Reports namespace would actually make PHP look for App\Reports\App\Billing\Invoice, which doesn't exist — relative class names are appended to the current namespace, not resolved from the root. This is a common source of confusion when moving a class-reference line between files that live in different namespaces; a use import at the top of the file sidesteps the whole issue by resolving the name once, explicitly.

How namespaces avoid real collisions

Picture a project that depends on two logging packages: one from a vendor called Acme and one it wrote in-house. Both happen to define a class named Logger. Before namespaces existed, PHP simply could not have two classes both named Logger loaded at once — the second class Logger declaration would be a fatal "cannot redeclare" error. With namespaces, this is a complete non-issue.

Acme/Logging/Logger.php — a vendor package's Logger

PHP
<?php

namespace Acme\Logging;

class Logger
{
    public function log(string $message): void
    {
        echo "[Acme] {$message}\n";
    }
}

App/Logging/Logger.php — a completely different Logger

PHP
<?php

namespace App\Logging;

class Logger
{
    public function log(string $message): void
    {
        echo "[App] {$message}\n";
    }
}

Using both Loggers in the same script

PHP
<?php

use Acme\Logging\Logger as VendorLogger;
use App\Logging\Logger as AppLogger;

(new VendorLogger())->log('vendor event');
(new AppLogger())->log('app event');

Both classes are named Logger and both would have collided in the global namespace, but Acme\Logging\Logger and App\Logging\Logger are entirely distinct identities as far as PHP is concerned. This is exactly why namespaces matter more as a project grows: it lets library authors pick short, obvious names without needing to worry about clashing with every other library on Packagist.

A quick look ahead: namespaces and file paths

In real projects, namespaces don't float independently of the filesystem — they typically follow a convention called PSR-4, which maps a namespace prefix directly onto a directory. For instance, a class named App\Services\Mailer conventionally lives in a file at src/Services/Mailer.php, where App\ maps to the project's src/ directory. Following this convention consistently is what allows tools (and Composer specifically) to find and load the right file automatically just from seeing a namespaced class name used in code — no manual require needed. That mechanism, autoloading, is its own topic worth covering separately.

The global namespace still exists
Any file (or part of a file) with no `namespace` declaration lives in the global namespace, written as a bare leading backslash `\` when referenced explicitly. PHP's built-in classes like `Exception`, `DateTime`, and `ArrayObject` live there, which is why they're reachable from inside any namespace without an import — PHP falls back to the global namespace automatically for names it can't resolve any other way, but only for classes (not for functions or constants, where the fallback behaves slightly differently).
  • The namespace App\Billing; declaration must be the first statement in a file and applies to everything declared below it.

  • A use App\Billing\Invoice; statement imports a name for short-form use in the current file only.

  • use ... as ... renames an import, which is how two same-named classes from different namespaces can be used side by side.

  • A leading backslash before a name is a fully-qualified reference resolved from the global root; a name without one is resolved relative to the current namespace.

  • Namespaces let two libraries each define a class with the same short name without a fatal "cannot redeclare" collision.

  • By convention (PSR-4), a namespace prefix maps to a directory, e.g. App\Services\Mailer living at src/Services/Mailer.php.

Tip
Pick one project-wide root namespace early (commonly `App`) and keep its directory structure mirroring the namespace structure exactly, even for small projects. It costs almost nothing up front, and it means that the moment you add a real autoloader, every class is found automatically with zero extra configuration.