ZetCode

PHP Composer and Autoloading

last modified October 22, 2026

In this article, we show how to use Composer to install dependencies and how to load our own classes automatically with PSR-4 autoloading.

Composer is the standard dependency manager for PHP. It reads a composer.json file and downloads the required packages from Packagist into a vendor directory.

Composer also generates vendor/autoload.php. Including that single file makes every class of the installed packages — and of our own project — available without a long list of require statements.

Main Features

These are the files and concepts we work with in this article.

The project layout

Our example project has two classes in namespaced directories under src, a file with helper functions, and an entry script.

app/
├── composer.json
├── index.php
└── src/
    ├── helpers.php
    ├── Model/
    │   └── User.php
    └── Service/
        └── Greeter.php

composer.json

The composer.json file is the configuration of the project. It is placed in the project root directory.

composer.json
{
    "name": "zetcode/demo",
    "description": "Composer autoloading demo",
    "type": "project",
    "require": {
        "php": ">=8.2",
        "nesbot/carbon": "^3.15"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        },
        "files": [
            "src/helpers.php"
        ]
    }
}

The require section lists the dependencies; here only the minimum PHP version. The autoload section tells Composer how to find our classes.

The PSR-4 rule maps the namespace prefix App\ to the src/ directory. A class App\Service\Greeter is expected in src/Service/Greeter.php.

The files entry loads helpers.php on every request. This is how functions, which cannot be autoloaded, are made available.

The classes

Each class lives in its own file whose path follows the PSR-4 rule.

src/Model/User.php
<?php

declare(strict_types=1);

namespace App\Model;

class User
{
    public function __construct(
        public readonly string $name,
        public readonly string $email,
    ) {}
}

The namespace mirrors the directory path below src/, and the file is named after the class.

This one-to-one convention is what lets the autoloader find a class without any configuration per class.

src/Service/Greeter.php
<?php

declare(strict_types=1);

namespace App\Service;

use App\Model\User;

class Greeter
{
    public function greet(User $user): string
    {
        return "Hello, {$user->name} <{$user->email}>";
    }
}

The Greeter refers to User by its fully qualified name through a use statement. Neither file includes the other.

src/helpers.php
<?php

declare(strict_types=1);

function dd_line(string $text): void
{
    echo str_repeat('-', strlen($text)), "\n", $text, "\n";
}

A plain function file, loaded through the files autoload entry.

Using the autoloader

The entry script includes the autoloader and uses the classes and the helper function.

index.php
<?php

declare(strict_types=1);

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

use App\Model\User;
use App\Service\Greeter;

$user = new User('Alice', 'alice@example.com');
$greeter = new Greeter();

dd_line($greeter->greet($user));

The only require is the one for vendor/autoload.php.

When new User is evaluated, PHP asks the autoloader for App\Model\User. The autoloader turns the name into a path and includes the file.

Whenever the autoload section of composer.json changes, regenerate the autoloader with composer dump-autoload.

$ composer dump-autoload
Generating autoload files
Generated autoload files
$ php index.php
--------------------------------
Hello, Alice <alice@example.com>

How the mapping is stored

Composer writes the PSR-4 rules into vendor/composer/autoload_psr4.php.

Looking at it shows there is no magic involved: the autoloader just tries the prefix directory plus the rest of the class name.

$ tail -6 vendor/composer/autoload_psr4.php
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);

return array(
    'App\\' => array($baseDir . '/src'),
);

For production, use composer dump-autoload -o (or --classmap-authoritative). It builds a class map of every class up front, so PHP does not touch the file system to search for classes.

$ composer dump-autoload -o
Generating optimized autoload files
Generated optimized autoload files containing 3 classes

Installing a package

The composer require command downloads a package and adds it to composer.json. It also updates composer.lock and regenerates the autoloader.

$ composer require nesbot/carbon
Using version ^3.15 for nesbot/carbon
use_pkg.php
<?php

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

use Carbon\Carbon;

echo Carbon::parse('2026-10-22')->addDays(10)->toDateString(), "\n";

Packages are used through the same vendor/autoload.php as our own code.

The lock file guarantees that every developer and the production server get the same versions after composer install.

The composer update command moves to newer versions allowed by the version constraint. The constraint ^3.15 means at least 3.15 and below 4.0.

$ php use_pkg.php
2026-11-01

Useful commands

The following table lists the Composer commands used most often.

CommandPurpose
composer initCreate a composer.json interactively.
composer installInstall the versions recorded in composer.lock.
composer updateUpdate packages within their version constraints.
composer require --dev phpunit/phpunitAdd a development-only dependency.
composer install --no-dev -oProduction install without dev packages and with an optimized autoloader.
composer remove vendor/packageRemove a dependency.
composer outdatedList packages with newer versions.
composer auditCheck installed packages for known vulnerabilities.

Commit composer.json and composer.lock to version control, but not the vendor directory.

Summary

In this article, we have created a composer.json with a PSR-4 autoload rule, loaded our classes through vendor/autoload.php, and installed a third-party package.

Author

My name is Jan Bodnar and I am a passionate programmer with many years of programming experience. I have been writing programming articles since 2007. So far, I have written over 1400 articles and 8 e-books. I have over eight years of experience in teaching programming.

List all PHP tutorials.