You know the rule from PSR-4 Autoloading Basics: a namespace prefix maps to a base folder. Here is how to set it up with Composer, step by step.
1. The folder layout
my-app/
├── composer.json
├── public/
│ └── index.php
└── src/
├── Service/
│ └── Greeter.php
└── Model/
└── User.php
2. composer.json
{
"name": "demo/my-app",
"require": {
"php": "^8.2"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
The key "App\\" is the namespace prefix (with the backslash doubled for JSON). The value "src/" is the folder it maps to.
3. The classes
<?php
declare(strict_types=1);
namespace App\Service;
use App\Model\User;
final class Greeter
{
public function greet(User $user): string
{
return "Hello, " . $user->name();
}
}
<?php
declare(strict_types=1);
namespace App\Model;
final class User
{
public function __construct(private string $name) {}
public function name(): string
{
return $this->name;
}
}
4. Generate the autoloader
composer dump-autoload
(Or composer install, which does it too.)
5. Use the classes
<?php
declare(strict_types=1);
require __DIR__ . "/../vendor/autoload.php";
use App\Model\User;
use App\Service\Greeter;
echo (new Greeter())->greet(new User("Ana"));
Hello, Ana
There is not a single require for a class. Composer’s autoloader finds both classes from their names.
Multiple prefixes
{
"autoload": {
"psr-4": {
"App\\": "src/",
"Database\\Seeders\\": "database/seeders/"
}
}
}
Tests: autoload-dev
Test classes should not be loaded in production. Put them in autoload-dev:
{
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
}
}
Fixing “class not found”
| Symptom | Check |
|---|---|
Class "App\Service\Greeter" not found | Does the file src/Service/Greeter.php exist, with the exact capitalisation? |
| Works on Windows, fails on Linux | A capital letter differs between the class and the file name |
| New namespace not found | Did you add it to composer.json and run composer dump-autoload? |
| Wrong namespace line in the file | The namespace must equal the prefix plus the folder path |
A quick way to see what Composer knows:
composer dump-autoload -o
php -r 'require "vendor/autoload.php"; var_dump(class_exists("App\\Service\\Greeter"));'
Checking a mapping
<?php
function expectedFile(string $class): string
{
$relative = substr($class, strlen("App\\"));
return "src/" . str_replace("\\", "/", $relative) . ".php";
}
echo expectedFile("App\\Model\\User");
src/Model/User.php
If the class file is not at the expected path, the autoloader cannot find it.
Why this matters in Laravel
"App\\": "app/" in composer.json. When you add a class in app/Services/, it works at once, because of exactly this PSR-4 mapping.