In a PHP file declaring namespace App\Billing, how do the class names Invoice, Tax\Rate and \Invoice each resolve?
answer
- three name forms, three rules
- leading backslash means absolute
- first segment checked against imports
- unimported names get the namespace prefix
- strings in new $class are absolute
basics
~10 sInvoice becomes App\Billing\Invoice unless a use statement imports that short name; Tax\Rate becomes App\Billing\Tax\Rate unless Tax is an imported alias; \Invoice is absolute and means the global class Invoice.
solid answer
~40 sPHP names come in three forms. A **fully qualified** name starts with a backslash (`\Invoice`) and is taken literally: imports and the current namespace are ignored, so it means the global `Invoice`. A **qualified** name contains a backslash but does not start with one (`Tax\Rate`): PHP checks whether its first segment `Tax` is an imported name and substitutes the import if so; otherwise it prepends the current namespace, giving `App\Billing\Tax\Rate`. An **unqualified** class name (`Invoice`) is looked up in the file's `use` table and, if nothing matches, becomes `App\Billing\Invoice`. All of this is done at compile time, per file. Names built from strings (`new $class`, a callable string) skip the rules entirely and are always read as fully qualified.
code
php · 13 lines<?php
declare(strict_types=1);
namespace App\Billing;
class Invoice {}
$short = 'Invoice';
$full = 'App\Billing\Invoice';
var_dump(new Invoice() instanceof Invoice); // bool(true)
var_dump((new $full())::class); // string(19) "App\Billing\Invoice"
new $short(); // Error: Class "Invoice" not found (strings are absolute)go deeper
Know the three forms: Invoice, Tax\Rate and \Invoice, and that only the last one ignores the current namespace and the imports.
Walk through the rules in order: imports first, then the namespace prefix, and explain that qualified names only rewrite their first segment.
Point to the production traps: string class names in config or factories need the full name, and import tables are per file, so moving code between files changes meaning.
Frame name resolution as a compile-time contract: it keeps lookups cheap and explicit, which is why teams ban dynamic short names and let static analysis verify every reference.
## What a namespace is in PHP A **namespace** is a prefix that PHP puts in front of the names of **classes, interfaces, traits, enums, functions and constants** declared after a `namespace` statement. Variables are not namespaced. Two classes called `Invoice` can coexist as long as they live in different namespaces, for example `App\Billing\Invoice` and `Vendor\Pdf\Invoice`. A few declaration rules come up in interviews: - The `namespace` statement must be the **first statement** in the file; only a `declare(...)` statement such as `declare(strict_types=1);` may come before it. Otherwise compilation stops with "Namespace declaration statement has to be the very first statement or after any declare call in the script". - **Namespace names are case-insensitive**: `App\Billing` and `app\billing` name the same namespace. - A file may declare several namespaces, either one after another or with the **bracketed** `namespace Foo { ... }` syntax. The two syntaxes cannot be mixed in one file, and global code next to namespaced code needs the bracketed form with an unnamed `namespace { ... }` block. Real projects keep one namespace per file. - Namespaces cannot be nested with braces; `namespace App\Billing\Tax;` simply declares a longer name. ## The name forms Every class, function or constant reference in PHP source is written in one of these forms: | Form | Example | Meaning | |---|---|---| | **Unqualified** | `Invoice` | no backslash at all | | **Qualified** | `Tax\Rate` | contains a backslash, does not start with one | | **Fully qualified** | `\Invoice`, `\App\Billing\Invoice` | starts with a backslash; absolute | | **Relative** | `namespace\Invoice` | the `namespace` keyword stands for the current namespace | ## How PHP resolves a class name Inside `namespace App\Billing;` the compiler applies these rules to class-like names: 1. **Fully qualified**: drop the leading backslash and stop. `\Invoice` is the global `Invoice`; `\App\Billing\Invoice` is exactly that class. Imports never apply. 2. **Relative**: replace `namespace` with the current namespace. `namespace\Invoice` becomes `App\Billing\Invoice`. 3. **Qualified**: look up the **first segment** in the file's import table. After `use Money\Tax;`, the name `Tax\Rate` becomes `Money\Tax\Rate`. With no matching import, prepend the current namespace: `App\Billing\Tax\Rate`. 4. **Unqualified**: look up the whole name in the import table. After `use Vendor\Pdf\Invoice;`, `Invoice` means `Vendor\Pdf\Invoice`. With no matching import, prepend the current namespace: `App\Billing\Invoice`. Functions and constants follow the same rules except for the last step: an unimported, unqualified function or constant name gets a **runtime fallback to the global namespace**, which classes never get. That asymmetry is why `strlen()` works inside a namespace while `new DateTime()` fails without an import. ```php <?php declare(strict_types=1); namespace App\Billing; use Vendor\Pdf\Invoice as PdfInvoice; use Money\Tax; new Invoice(); // App\Billing\Invoice new PdfInvoice(); // Vendor\Pdf\Invoice new Tax\Rate(); // Money\Tax\Rate (first segment imported) new Report\Monthly(); // App\Billing\Report\Monthly new \Invoice(); // Invoice in the global namespace new namespace\Invoice();// App\Billing\Invoice ``` ## The same rules everywhere a class name appears The rules are not specific to `new`. Every place PHP source names a class goes through the same resolution: - `extends` and `implements` clauses, and `use` of a trait inside a class body; - parameter, return and property **type declarations**; - `instanceof` checks and `catch (...)` clauses; - static calls and constants such as `Invoice::create()` and `Invoice::VERSION`; - **attributes** such as `#[Route(...)]` and `::class` expressions. A wrong prefix therefore does not only break construction: a type declaration naming an unimported class quietly refers to a class that does not exist, and every argument fails the type check. PHP 8.0 also changed how names are tokenized. A namespaced name is now a single token, so whitespace inside it, as in `Foo \ Bar`, is no longer allowed, and reserved keywords may appear as segments, as in `App\List\Item`. ## Resolution is compile-time and per file The rewriting happens when PHP **compiles** the file. It does not check that the class exists and does not load anything; a missing class only fails later, when the code actually runs `new`, a static call or similar. Import tables belong to the file (or to one bracketed namespace block) that declares them: an included file does **not** inherit its parent's `use` statements. ## Strings are always fully qualified Names that PHP only sees at run time come from strings, and the import table no longer exists by then. So: - `$class = 'App\Billing\Invoice'; new $class;` must spell out the full name. A leading backslash is allowed but not needed. - `$class = 'Invoice'; new $class;` means the **global** `Invoice`, even inside `namespace App\Billing`. - In double-quoted strings a backslash can start an escape sequence, so single quotes or `Invoice::class` are the safer way to produce a class-name string. ## Common mistakes - Writing a leading backslash in a `use` statement and believing it changes the meaning; import names are always absolute. - Expecting an import to make a class available; it only creates a short name. - Building a class name from a short string and expecting the current namespace to be prepended.
- In PHP, inside namespace App, what does new Foo\Bar() resolve to after use Lib\Foo;?`Lib\Foo\Bar`. `Foo\Bar` is a **qualified** name, so PHP translates only its first segment through the import table. `Foo` matches the import `Lib\Foo`, so the result is `Lib\Foo\Bar`. Without that import it would become `App\Foo\Bar`. Importing a namespace this way is a common shortcut for referring to several classes under it.
- In PHP, does a use statement at the top of an included file apply to the file that included it?No. Import tables are compiled per file (and per bracketed namespace block), so an included file neither inherits the parent's `use` statements nor exports its own. Each file must import what it references by short name. The same goes for the `namespace` declaration: every file states its own.
- In PHP, what is the namespace keyword used as an operator, as in namespace\helper()?It is a **relative** name: `namespace\` is replaced by the current namespace at compile time, so inside `App\Util` the call targets `App\Util\helper()`. It is the namespace counterpart of `self` for classes. Because it is explicit, it also skips the global fallback that an unqualified `helper()` call would get.
saying these in an interview costs you the question
- An unqualified class name falls back to the global class when the namespaced one is missing
- A use statement loads or checks the imported class
- A leading backslash inside a use statement changes what gets imported
- A string like 'Invoice' passed to new picks up the current namespace
- An included file inherits the parent file's use statements
- Namespace names are case-sensitive, so App and app are different