---
title: Namespaces
source: 'https://academia.sh/en/courses/php-object-model/namespaces'
course: 'Object-Oriented PHP and Standards'
language: en
updated: '2026-10-21T05:03:35+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Reaches a class through a fully qualified name or an alias using namespace, use and as'
    - 'Measures that two same-named classes are breaking in the global namespace and stay in place in separate namespaces'
    - 'Shows that an unqualified function call falls back to the global function and that a leading backslash prevents it'
---

# Namespaces

namespace, use and aliases with fully qualified names; two same-named classes kept apart by a namespace, while the same collision in code without namespaces stops the program; an unqualified function call falling back to the global function.

In the Type Declarations lesson the change came from inside the definition: a type was added to a parameter and the old call paid for it. In this lesson the change comes from outside the definition. Someone else adds a class that carries the same name as our class. The question that runs through the course does not change: **where does this change reach?** The answer is one of four: **in place**, **at the seam**, **breaking**, **implicit**.

M09/K02 looked for the place where a name is bound: in the declaration, at the point of use, or in the value. A namespace adds a prefix to the place where that binding is made. This lesson measures how many files are edited when the binding changes, and what the unedited code experiences.

## Namespaces, use and the Fully Qualified Name

A namespace is a scope that gives names a prefix. A class in a file opened with `namespace Shop;` carries the name `Shop\Record`, not `Record`. This long prefixed form is called the **fully qualified name**. `use` makes a fully qualified name referable by its short name inside the file; `as` gives an alias to one of two classes that share the same short name.

```php
<?php
// names.php — namespace, fully qualified name and alias
namespace Shop {
    class Record {}
}
namespace External {
    class Record {}
}
namespace {
    use Shop\Record;
    use External\Record as ExternalRecord;

    echo Record::class, "\n";
    echo ExternalRecord::class, "\n";
    echo \External\Record::class, "\n";
    echo get_class(new ExternalRecord()), "\n";
}
```

```
Shop\Record
External\Record
External\Record
External\Record
```

The two classes share a short name but have different fully qualified names. The name `Record` was bound to `Shop\Record`, the alias `ExternalRecord` to `External\Record`; the third line, which starts with a backslash, writes the full name without any `use`. `get_class` returns the real name of the object in fully qualified form. A namespace is not a folder but a name prefix: it does not say in which file the class lives.

An unqualified class name written inside a namespace is taken to belong to that namespace. PHP's own classes live in the global namespace, so while a namespace is open they are reached with the full name or with `use`.

```php
<?php
// root.php — inside a namespace an unqualified class name does not fall back to the global one
namespace Shop;

echo Exception::class, "\n";
echo \Exception::class, "\n";

try {
    new Exception('x');
} catch (\Error $error) {
    echo get_class($error), "\n";
}
echo get_class(new \Exception('x')), "\n";
```

```
Shop\Exception
Exception
Error
Exception
```

When `Exception` was written, PHP read it as `Shop\Exception`; since no such class exists, `new` threw an `Error` and `catch` caught it. The name with a leading backslash selected the global class. The rule for classes is this: a class name that cannot be found in the namespace does **not** fall back to the global one. The rule for functions is different, and that difference is exactly the bounding measurement of this lesson.

## Name Resolution: Unqualified, Qualified, Fully Qualified

PHP sees a name in three forms, and each is resolved differently. An **unqualified** name contains no backslash (`Money`). A **qualified** name contains one but does not start with one (`Sub\Money`). A **fully qualified** name starts from the global namespace and begins with a backslash (`\Money`). The resolution order is short. A fully qualified name stays as it is. An unqualified name is first checked against the imports made with `use`; if it was not imported, it is appended to the current namespace. If the first part of a qualified name is a `use` alias, that part is replaced with the name the alias points to; otherwise the name is again written under the current namespace. The `namespace\Money` form explicitly selects the current namespace.

This resolution happens at compile time and does not require the class to exist. `::class` gives only the resolved name, so in the driver below the results of five forms can be seen side by side without defining any class.

```php
<?php
// resolution.php — five ways of writing a name and their resolved names
namespace Shop\Order;

use Shop\Tools as A;

echo 'unqualified     : ', Money::class, "\n";
echo 'qualified       : ', Sub\Money::class, "\n";
echo 'aliased         : ', A\Money::class, "\n";
echo 'fully qualified : ', \Money::class, "\n";
echo 'namespace\ prefix: ', namespace\Money::class, "\n";
```

```
unqualified     : Shop\Order\Money
qualified       : Shop\Order\Sub\Money
aliased         : Shop\Tools\Money
fully qualified : Money
namespace\ prefix: Shop\Order\Money
```

Four of the five lines went to the current namespace or to the place the alias points to; only the name starting with a backslash went to the global namespace. This also says what happens when the code is moved to another namespace: unqualified and qualified names move together with the namespace of the file that carries them, while fully qualified names stay where they are. For classes the resolution ends here. For functions and constants there is one more step: an unqualified name that cannot be found in the current namespace falls back to the global one. The bounding measurement measures the consequence of this difference.

## Grouped use, use function and use const

A file can contain several `use` statements, and names that come from the same namespace can be gathered on one line with curly braces. Plain `use` is enough for classes; functions and constants need separate keywords: `use function` and `use const`. Plain `use` does not import a function or a constant.

```php
<?php
// imports.php — grouped use, use function and use const
namespace Shop\Tools {
    const VAT = 20;
    function taxed(int $price): int { return $price + intdiv($price * VAT, 100); }
    class Round {}
    class Money {}
}
namespace {
    define('GENERAL', 'global');
}
namespace App {
    use Shop\Tools\{Round, Money as Amount};
    use function Shop\Tools\taxed;
    use const Shop\Tools\VAT;

    echo taxed(100), ' ', VAT, "\n";
    echo Round::class, ' ', Amount::class, "\n";
    echo GENERAL, "\n";
}
```

```
120 20
Shop\Tools\Round Shop\Tools\Money
global
```

The first line shows two imports: the function `taxed` and the constant `VAT` were used without writing the namespace. On the second line the grouped `use` imported two classes in one statement, and the class `Money` was given the alias `Amount` inside the group. The third line shows that a global constant can be read from inside a namespace without any `use`: constants fall back to the global namespace just as functions do. Classes do not.

## Where the namespace Declaration Goes, and Independence from the Directory

The `namespace` declaration must be the first statement of the file; only `declare` may come before it. Even an `echo` written before it stops the file at compile time. The driver below tries this in a separate PHP process and prints only the exit code. The same driver shows a second thing as well: the class in the `Shop\Model` namespace is defined in a file inside the `deep/inner` directory, which has nothing to do with its name, and it still works.

```php
<?php
// place.php — namespace must be the first statement; the directory does not determine the namespace
$dir = 'measure/place/deep/inner';
mkdir($dir, 0777, true);
file_put_contents("$dir/class.php", "<?php\nnamespace Shop\\Model;\nclass Product {}\n");
file_put_contents('measure/place/late.php', "<?php\necho 'before';\nnamespace Shop;\n");

require "$dir/class.php";
$file = (new ReflectionClass('Shop\Model\Product'))->getFileName();
echo \Shop\Model\Product::class, ' ', basename($file), "\n";

exec('php -d display_errors=0 measure/place/late.php 2>/dev/null', $lines, $code);
echo "namespace not first: exit=$code\n";
```

```
Shop\Model\Product class.php
namespace not first: exit=255
```

The first line proves independence from the directory: the class name is `Shop\Model\Product`, while its file is `measure/place/deep/inner/class.php`. PHP does not match the two; a namespace is only a prefix declared inside the file. The second line shows that the process ended with 255 because the `namespace` declaration came after the `echo`. The mapping between a namespace and a directory is the job not of the language but of a convention. The next lesson sets up that convention: the autoloader takes on the task of matching the namespace prefix to a directory.

## The Measurement Setup

The measurement is done with `reach.php`. The file carries the `measure()` function; `measure()` builds the same program before and after the change, runs both, and asks for the reach in order: if the later run exits with a non-zero code the reach is **breaking**, if the output differs it is **implicit**, if the edited file is in the seam list it is **at the seam**, and if none of these holds it is **in place**. There are two conditions: the earlier version must exit with 0, and the consumer file `main.php` is not edited. The code blocks in the lessons below only mention the file; the driver of this lesson pulls it in with `require_once`. The file is only written to disk, without an output block.

```php
<?php
// reach.php — helpers that measure where a change reaches

function write_files(string $dir, array $files): void
{
    foreach ($files as $name => $content) {
        if ($content === null) {
            continue; // null: the file was deleted
        }
        $path = "$dir/$name";
        if (!is_dir(dirname($path))) {
            mkdir(dirname($path), 0777, true);
        }
        file_put_contents($path, $content);
    }
}

function read_files(string $dir): array
{
    $files = [];
    $walker = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS)
    );
    foreach ($walker as $file) {
        $name = substr($file->getPathname(), strlen($dir) + 1);
        $files[$name] = file_get_contents($file->getPathname());
    }
    return $files;
}

function run(string $dir): array
{
    $command = 'cd ' . escapeshellarg($dir) . ' && php -d display_errors=0 main.php 2>/dev/null';
    exec($command, $lines, $code);
    return [$code, implode("\n", $lines)];
}

function measure(string $name, array $base, array $change, array $seams = []): string
{
    if (array_key_exists('main.php', $change)) {
        throw new LogicException("$name: the consumer is not edited");
    }
    write_files("measure/$name/before", $base);
    write_files("measure/$name/after", array_merge($base, $change));
    [$codeBefore, $outputBefore] = run("measure/$name/before");
    [$codeAfter, $outputAfter] = run("measure/$name/after");
    if ($codeBefore !== 0) {
        throw new LogicException("$name: the baseline did not run");
    }
    $reach = match (true) {
        $codeAfter !== 0 => 'breaking',
        $outputBefore !== $outputAfter => 'implicit',
        array_intersect(array_keys($change), $seams) !== [] => 'at-seam',
        default => 'in-place',
    };
    printf(
        "%-18s files=%d  exit=%-3d output %-7s -> %s\n",
        $name,
        count($change),
        $codeAfter,
        $outputBefore === $outputAfter ? 'same' : 'changed',
        $reach
    );
    return $reach;
}
```

## The Collision: Code With and Without a Namespace

In the `namespace` measurement, `ours.php` defines the class `Record` in the `App` namespace, and its `source()` method returns `app`. The external code adds to `external.php` a `Record` of the same name inside its own namespace, `External`. The consumer calls our class with `use App\Record;`. The counterpart is `global-name`: both classes stand in the global namespace, without any namespace.

```php
<?php
// namespace.php — a same-named class with a namespace and without one
require_once 'reach.php';

// 8. External code adds a class with the same name as ours, in its own namespace
$reaches[] = measure('namespace', [
    'ours.php' => <<<'PHP'
        <?php
        namespace App;
        class Record
        {
            public function source(): string { return 'app'; }
        }
        PHP,
    'external.php' => <<<'PHP'
        <?php
        namespace External;
        class Log {}
        PHP,
    'main.php' => <<<'PHP'
        <?php
        use App\Record;
        require 'ours.php';
        require 'external.php';
        echo (new Record())->source(), "\n";
        PHP,
], [
    'external.php' => <<<'PHP'
        <?php
        namespace External;
        class Log {}
        class Record
        {
            public function source(): string { return 'external'; }
        }
        PHP,
]);

$reaches[] = measure('global-name', [
    'ours.php' => "<?php\nclass Record\n{\n    public function source(): string { return 'app'; }\n}\n",
    'external.php' => "<?php\nclass Log {}\n",
    'main.php' => "<?php\nrequire 'ours.php';\nrequire 'external.php';\necho (new Record())->source(), \"\\n\";\n",
], [
    'external.php' => "<?php\nclass Log {}\nclass Record\n{\n    public function source(): string { return 'external'; }\n}\n",
]);

echo run('measure/namespace/before')[1], ' -> ', run('measure/namespace/after')[1], "\n";
```

```
namespace          files=1  exit=0   output same    -> in-place
global-name        files=1  exit=255 output changed -> breaking
app -> app
```

The first line is `namespace`: the external code brought the same name, one file changed, the exit code was 0, and the output was `app` before and stayed `app`. Because an empty seam list was given, the reach is **in place**: the change stayed inside `external.php` and did not reach the consumer. The namespace confined the collision inside the definition.

The second line is the counterpart: when the same single-file addition was made without a namespace, the program stopped and exited with 255. The reach is **breaking**. The difference is not in the number of files; in both cases one file changed. The difference is in where the name lives.

For a namespace to do its job, the name of the namespace itself must not collide either. This is why the first segment of a namespace is usually a name that belongs to the project or to the team that writes it, and the subsegments follow the directory structure: `Shop\Model`, `Shop\Service`. When two separate libraries choose the same first segment, the namespace stops being a protection. Choosing the prefix is a matter of convention, not something the language checks.

**SP1.** This lesson treats the namespace only as a matter of name prefixes. How files are found and loaded belongs to the Autoloading lesson; here every class is pulled in by hand with `require`.

## The Bounding Measurement

A namespace protected the classes, but it does not protect everything. For an unqualified **function** call, PHP first looks in the namespace where the call stands, and if the function is not there it falls back to the global function. In the driver below, the consumer calls `strlen` inside `namespace App;`. The `helper.php` it includes at first carries only `greet()`; in the change, a `strlen` function also enters the same namespace. In the second measurement the call is written as `\strlen`.

```php
<?php
// function.php — an unqualified function call falls back to the global function
require_once 'reach.php';

$helper = <<<'PHP'
    <?php
    namespace App;
    function greet(): string { return 'hello'; }
    PHP;
$extended = $helper . "\n" . 'function strlen(string $text): int { return 0; }' . "\n";
$main = "<?php\nnamespace App;\nrequire 'helper.php';\necho %s('apple'), \"\\n\";\n";

measure('function-fallback', ['helper.php' => $helper, 'main.php' => sprintf($main, 'strlen')], ['helper.php' => $extended]);
measure('root-call', ['helper.php' => $helper, 'main.php' => sprintf($main, '\strlen')], ['helper.php' => $extended]);

echo run('measure/function-fallback/before')[1], ' -> ', run('measure/function-fallback/after')[1], "\n";
```

```
function-fallback  files=1  exit=0   output changed -> implicit
root-call          files=1  exit=0   output same    -> in-place
5 -> 0
```

The first line is `function-fallback`: the call `strlen('apple')` found the function `App\strlen` and printed `0` instead of `5`. The program did not stop and the exit code stayed 0; the output changed, so the reach is **implicit**. The consumer was never edited, and the change reached it all the same: an unqualified call does not say which `strlen` is meant. The second line is `root-call`: when the same call was written as `\strlen`, PHP chose the global function without looking at the namespace, and the reach stayed **in place**. A leading backslash explicitly selects the global namespace.

A namespace protected against a class collision, but it did not protect a function call on its own. The same mechanism either protects or does not, depending on how the call is written. What is measured is, once again, not the definition itself but what the consumer touches.

## Summary

- The reach of this lesson is **in place**. In the `namespace` measurement, external code brought a same-named class in its own namespace, one file changed, and the output stayed `app`.
- Its counterpart `global-name` is **breaking**: the same addition without a namespace stopped the program with exit code 255. One file changed in both cases; the difference was the place where the name lives.
- `namespace` gives names a prefix, `use` makes a fully qualified name referable by its short name, and `as` gives an alias to colliding short names.
- The bounding measurement: an unqualified function call falls back to the global one. The `strlen` call aimed at `App\strlen` went `5` → `0` and the reach was **implicit**; with `\strlen` it was **in place**.
- A namespace is not a folder but a name prefix: it does not say in which file the class lives. Unqualified, qualified and fully qualified names are resolved separately; `use function` and `use const` import a function and a constant; the `namespace` declaration is the first statement of the file.

## Next Step

In this lesson, finding the class was done by hand with `require`. If a class file moves to another root directory, how many files change, and is the consumer affected? The Autoloading lesson answers this question by measuring the loader as a single seam.
