Namespaces and PSR-4 Autoloading
By the end of this lesson you'll organise PHP code the way every real project does — group classes under namespace s, import them with use , understand fully-qualified names and the global \ root, and let Composer's PSR-4 autoloader load each class on demand so you never write a chain of require statements again.
Learn Namespaces and PSR-4 Autoloading in our free PHP course — an interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Php course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
1️⃣ Declaring a Namespace
A namespace is a label that groups related classes so their names never collide with code elsewhere. You declare it with namespace App\Service; as the first statement in the file. Everything defined below — classes, functions, constants — now lives under that prefix, so the class Mailer is really App\Service\Mailer . Two libraries can each ship a Mailer and never clash.
The convention is to mirror your folder structure: App\Service\Mailer sits in src/Service/Mailer.php . That mirroring is exactly what makes autoloading possible, as you'll see below.
2️⃣ Importing with use & Fully-Qualified Names
To reach a class in another namespace you can write its fully-qualified name — the whole path from the global root, with a leading backslash: \App\Service\Mailer . Writing that every time is tedious, so the use statement imports the name once, letting you write the short Mailer for the rest of the file. Remember: use only creates an alias — it does not load any file.
Both objects above are the same class — one reached by the imported short name, one by the fully-qualified path. Inside a namespace, a bare name like Mailer is resolved relative to the current namespace first, which is why the explicit leading \ matters.
3️⃣ Aliases & the Global \ Namespace
When two imports share a short name, rename one with as : use App\Service\Logger as ServiceLogger; . And remember that PHP's built-ins — DateTime , strlen , array_map — all live in the global namespace . Inside your own namespace, prefix them with a leading \ to say "look in the global root", which is also slightly faster than letting PHP fall back to it.
For classes the leading backslash is required (otherwise new DateTime() would look for App\Report\DateTime ). For functions and constants PHP has a global fallback, but writing \strlen() is explicit and avoids surprises.
4️⃣ PSR-4 Autoloading with Composer
PSR-4 is the standard that maps a namespace prefix to a base folder . You declare it in composer.json under autoload.psr-4 — for example App\ to src/ . After editing it you run composer dump-autoload to regenerate the maps. Now Composer can translate any fully-qualified class name straight into a file path.
With that mapping in place, your entry script needs just one include — vendor/autoload.php — and then you simply use classes. Composer loads each file the first time the class is referenced, in the right order, exactly once.
This is why the one-class-per-file convention exists: each file defines a single class whose name matches the file name, so App\Model\User resolves unambiguously to src/Model/User.php .
5️⃣ Why Not Just require Everything?
Before autoloaders, every file began with a wall of require lines. That approach is fragile : move a file and the paths break, and you load classes even on requests that never use them. It is order-dependent : a class that extends another must be required after its parent. And it is repetitive : the same includes copied into file after file. Autoloading replaces all of it with a single registration line and on-demand loading.
Now you try the two core skills — declaring/importing, and wiring up the autoloader.
Fill in each ___ using the 👉 hint, then run it and check it against the Output panel.
One more. Finish the PSR-4 mapping and the autoloader include so the class loads on its own.
Common Errors (and the fix)
- "Class not found" after adding a class — the file name or its folder doesn't match the namespace, or you forgot to refresh the maps. Make the path match the PSR-4 mapping exactly (case-sensitive) and run composer dump-autoload .
- "namespace declaration must be the first statement" — you put code, whitespace, or HTML before the namespace line. It must come immediately after <?php , before anything else.
- Calling new DateTime() fails inside a namespace — PHP looked for App\...\DateTime . Add the global prefix: new \DateTime() , or use DateTime; at the top.
- use didn't load my file — use is only an alias; it never includes anything. Make sure vendor/autoload.php is required and the class lives at its PSR-4 path.
- Two classes per file — PSR-4 maps one class name to one file path, so a second class in the same file will never be found. Split each class into its own file named after it.
Pro Tips
- 💡 Match folders to namespaces. Keep src/Service/Mailer.php in sync with App\Service\Mailer and the autoloader just works.
- 💡 Group your imports. Put all use statements together at the top of the file, alphabetised — most IDEs and tools do this for you.
- 💡 Optimise for production. Run composer dump-autoload -o on deploy to build a static classmap, which is faster than resolving paths at runtime.
📋 Quick Reference — Namespaces & Autoloading
Syntax / Command
What It Does
namespace App\Service;
Scope this file under App\Service (first statement)
\App\Service\Mailer
Fully-qualified name from the global root
use App\Service\Mailer;
Import a name to use its short form
use ... as Alias;
Import under a different short name
\strlen() / new \DateTime()
Force the global namespace lookup
"psr-4": { "App\\": "src/" }
Map a prefix to a base folder in composer.json
composer dump-autoload
Regenerate the autoloader maps
require 'vendor/autoload.php';
Register the autoloader (one line per app)
Frequently Asked Questions
Mini-Challenge: A Two-Class PSR-4 App
No code is filled in this time — just a brief and an outline. Build the two namespaced classes, wire them up with PSR-4, run composer dump-autoload , then check your result against the expected output in the comments. This is exactly how every real project is structured.
🎉 Lesson Complete!
- ✅ namespace App\Service; scopes a file and prevents name clashes
- ✅ A fully-qualified name starts at the global root with a leading \
- ✅ use imports a short name; as aliases it — neither loads a file
- ✅ PSR-4 maps a namespace prefix to a folder in composer.json ; composer dump-autoload refreshes it
- ✅ One require 'vendor/autoload.php' replaces every scattered require
- ✅ Keep one class per file , named after the class, matching its PSR-4 path
- ✅ Next lesson: Fibers & Async PHP — cooperative multitasking and non-blocking I/O
Practice quiz
What does the statement namespace App\Service; do at the top of a file?
- It places every class, function, and constant in that file under the App\Service namespace
- It imports the App\Service class
- It creates a new folder called App/Service
- It autoloads all files in App/Service
Answer: It places every class, function, and constant in that file under the App\Service namespace. A namespace declaration must be the first statement in a file and puts everything defined below it under that namespace, preventing name clashes.
What is a fully-qualified class name?
- A class name written in ALL CAPS
- Any class whose name is longer than ten characters
- A name written from the global root with a leading backslash, e.g. \App\Service\Mailer
- A class that has no namespace
Answer: A name written from the global root with a leading backslash, e.g. \App\Service\Mailer. A fully-qualified name starts with a leading backslash and lists the whole path from the global namespace, so it is unambiguous no matter where it is used.
Why does PHP code that calls strlen() inside a namespace sometimes use \strlen()?
- Because strlen is deprecated
- Because the leading backslash tells PHP to look in the global namespace, not the current one
- Because namespaced code cannot call functions
- Because strlen needs an argument
Answer: Because the leading backslash tells PHP to look in the global namespace, not the current one. Built-in functions live in the global namespace. A leading backslash (\strlen) forces the global lookup, which is also marginally faster than the fallback search.
What does the use statement do?
- It includes a file like require
- It defines a new namespace
- It runs a function immediately
- It imports a class, function, or constant so you can refer to it by its short name
Answer: It imports a class, function, or constant so you can refer to it by its short name. use App\Service\Mailer; lets you write Mailer instead of the full path for the rest of the file. It does not load any file by itself.
How do you create an alias for an imported class?
- use App\Service\Mailer as M;
- alias App\Service\Mailer M;
- use M = App\Service\Mailer;
- use App\Service\Mailer alias M;
Answer: use App\Service\Mailer as M;. The as keyword renames an import: use App\Service\Mailer as M; is handy when two classes share a short name.
What does PSR-4 define?
- A coding style for indentation
- A standard mapping from a namespace prefix to a base directory so classes load automatically
- A database connection format
- A way to compress files
Answer: A standard mapping from a namespace prefix to a base directory so classes load automatically. PSR-4 is the autoloading standard that maps a namespace prefix (e.g. App\) to a base folder (e.g. src/), so each class file is found from its fully-qualified name.
In composer.json, where do you declare a PSR-4 mapping?
- Under the config key
- Under the require key
- Under the scripts key
- Under the autoload key, inside a psr-4 object
Answer: Under the autoload key, inside a psr-4 object. You add it under autoload.psr-4, for example { "autoload": { "psr-4": { "App\\": "src/" } } }, mapping the App\ prefix to the src/ directory.
After editing the autoload section of composer.json, what must you run?
- php -S localhost
- composer install --no-dev
- composer dump-autoload
- composer require autoload
Answer: composer dump-autoload. composer dump-autoload regenerates the autoloader maps so Composer knows about your new namespace mapping or newly added classes.
Why is scattering require statements throughout a codebase considered bad practice?
- It is fragile and repetitive: paths break when files move, classes load even when unused, and order matters
- require cannot load classes at all
- PHP forbids more than one require per file
- require is slower than echo
Answer: It is fragile and repetitive: paths break when files move, classes load even when unused, and order matters. Manual require chains are brittle and order-dependent. An autoloader loads each class on demand, exactly once, from a single registration line.
What is the one-class-per-file convention that PSR-4 relies on?
- Each file defines exactly one class whose name matches the file name
- All classes go in a single big file
- Each class must be under 100 lines
- Every file may contain only comments
Answer: Each file defines exactly one class whose name matches the file name. PSR-4 maps a class name to a file path, so each class needs its own file named after it (User -> User.php) for the autoloader to find it.
Continue this course
- Previous: Real-Time PHP with WebSockets
- Next: Fibers & Async PHP