aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Web Development

PHP PSR-4 Autoloading: Namespace-to-Directory Mapping

PHP PSR-4 is an autoloading standard that lets you load classes from the right file based on their name alone, without ever calling require by hand. Its core idea fits in one sentence: you bind a namespace prefix to a base directory, and the remaining namespace segments map directly onto the folder path. In this article we cover the rules of PSR-4, how it is configured through composer.json, and how to fix the typical errors you hit in day-to-day development, all with real examples.

Why is autoloading needed?

In PHP's early years you had to manually include the file for every class before using it. In a medium-sized project the top of each file filled up with dozens of require_once lines, and moving a single file meant updating paths one by one. Autoloading removes that pain: the first time PHP encounters an undefined class, it triggers a registered callback (the autoloader) and loads the relevant file on the spot.

The heart of this mechanism is the spl_autoload_register() function. PSR-4 is a shared contract that defines how the autoloader registered with that function should behave. The standard was published by PHP-FIG (Framework Interop Group), and today nearly the entire modern ecosystem, including Laravel, Symfony and Guzzle, follows it. Thanks to this, different libraries can share a single autoloader, and predicting a file path from a class name becomes completely deterministic.

The core rule of PSR-4

Under PSR-4, a fully qualified class name consists of three parts: a namespace prefix, intermediate namespaces, and the class name. Let's look at an example. Say you define this mapping: the prefix App\ is bound to the src/ directory. Then:

  • class App\Mailersrc/Mailer.php
  • class App\Services\Invoicesrc/Services/Invoice.php
  • App\Http\Controllers\HomeControllersrc/Http/Controllers/HomeController.php

The mechanics are simple: the namespace prefix (here App\) is swapped for the base directory (src/); each remaining namespace separator (\) becomes a directory separator (/); and .php is appended at the end. Three points deserve attention:

  • The mapping is case-sensitive. The file for App\Mailer must be Mailer.php, not mailer.php. This matters most on Linux servers; macOS/Windows forgives it locally but it breaks in production.
  • The file name must be exactly the same as the class name inside it.
  • The prefix must correspond to a base directory; the prefix itself is not a folder name, it maps straight onto that folder.

Configuring PSR-4 with composer.json

In practice you don't write your own autoloader by hand; you use the one Composer generates. You only need to add your PSR-4 mapping to the autoload section of composer.json:

{
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}

Note that the backslash is escaped inside JSON (App\\). You can also define more than one prefix; separating application code from test code is common:

{
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    }
}

Here autoload-dev only kicks in during development; when installed for production with composer install --no-dev, the test namespace is not loaded. After adding or changing a mapping you need to regenerate the autoload map:

composer dump-autoload

This command refreshes the maps under vendor/composer/. You only need to run it when you add a new PSR-4 prefix or change the namespace structure; adding a new class to an existing namespace requires no dump, because PSR-4 infers the file from the path.

Usage: a single-line entry point

Once the mapping is ready, in your project's entry point (for example public/index.php) you only need to include the autoloader Composer generated:

require __DIR__ . '/../vendor/autoload.php';

use App\Services\Invoice;
use App\Mailer;

$invoice = new Invoice();
$mailer  = new Mailer();

Now both your own classes and every package you installed load the moment their names are referenced. The class file itself must begin with a namespace declaration; for example src/Services/Invoice.php looks like this:

<?php

namespace App\Services;

class Invoice
{
    public function total(): float
    {
        return 0.0;
    }
}

The namespace App\Services; line here must be perfectly consistent with the file's place in the PSR-4 mapping. A mismatch is the most common source of the "Class not found" error.

The difference between psr-4, classmap and files

Composer supports three different autoload strategies, and knowing when to use each makes your life easier:

  • psr-4: Maps a namespace to a directory and derives the file from the path. The default choice for modern code; adding a new class needs no dump.
  • classmap: Scans the specified folders and writes the direct file path for each class into a map. Handy for legacy code that doesn't follow the namespace convention, but it requires dump-autoload whenever a new class is added.
  • files: Unconditionally loads files containing function definitions (not classes) on every request. Typically used for global helper functions.

For most projects psr-4 alone is enough. classmap and files mostly come into play when integrating older, namespace-less libraries.

Autoload optimization in production

PSR-4 is flexible, but that flexibility carries a small cost: at runtime PHP computes the file path from the class name and checks whether the file exists on disk. In production you can speed this up by precomputing it:

composer dump-autoload --optimize

This command (short form -o) scans all PSR-4 classes and converts them into a classmap, eliminating runtime file lookups. You typically use this combination in the deploy step:

composer install --no-dev --optimize-autoloader

One caveat: an optimized classmap is static. If you add a new class file in production, the new class won't be found until you run the dump again. That is why the optimize flag is meant for deployment, not for the development environment.

Frequently Asked Questions

I'm getting a "Class not found" error, where should I look?

Check three things first: is the namespace declaration in the file consistent with the PSR-4 mapping, is the file name exactly the same as the class name including case, and is the base directory in composer.json correct. If you added a new prefix, don't forget to run composer dump-autoload. If the error only appears in production, it is almost always a case mismatch; the Linux file system is case-sensitive.

What is the difference between PSR-4 and PSR-0?

PSR-0 is the older standard and is stricter: it interprets underscores (_) in the namespace as directory separators and reflects the entire prefix into the directory structure. PSR-4 instead maps the prefix straight onto a base directory, letting you build shallower, cleaner folder structures. PSR-0 is now deprecated; always prefer PSR-4 in new projects.

Do I have to run dump-autoload for every new class?

No. Because PSR-4 derives the file from the namespace, adding a new class to an existing mapping needs no extra command; the class works right away. dump-autoload is only needed when you add a new PSR-4 prefix, use a classmap, or optimize for production.

Is the autoload setup in your PHP project a mess? If you need help with namespace organization, "class not found" errors, migrating legacy code to PSR-4, or the clean architecture of a Laravel/Symfony-based project, get in touch with me — let's put your codebase on a solid foundation together.

Bu kategorideki tüm yazılar →

Devamı için