PHP Namespaces and Composer

Organise a PHP project with namespaces, load classes automatically with PSR-4 autoloading, and install third-party packages with Composer.

Lesson 21 of 86Advanced17 min read

Small PHP projects start with a few files joined together by require. As the project grows, that approach struggles. You end up with long lists of require_once lines at the top of every file, and sooner or later two pieces of code both want a class called User or a function called format().

Modern PHP solves both problems. Namespaces give every class a full name, like a folder path, so App\Models\User and Vendor\Auth\User can live side by side. Autoloading loads each class file automatically the first time it's used, so the require lists disappear.

Composer, PHP's package manager, ties it together. It sets up autoloading for your own code and downloads thousands of well-tested libraries, for sending email, handling dates, reading spreadsheets or building whole websites, with a single command. Every modern PHP framework is installed and run through Composer.

Namespaces

Declare a namespace at the very top of a file, after <?php:

PHP
<?php
namespace App\Models;

class User { /* ... */ }

The class's full name is now App\Models\User. Namespaces use backslashes and usually match the folder structure: src/Models/User.php. Built-in classes such as DateTimeImmutable and PDO live in the global namespace; inside a namespaced file, write them with a leading backslash, \PDO, or import them with use.

use and aliases

use imports a class so you can refer to it by its short name:

PHP
use App\Models\User;
use App\Services\Mailer as AppMailer;
use DateTimeImmutable;

$user = new User();
$mailer = new AppMailer();
  • use statements go after the namespace line and before any code.
  • as gives an alias, useful when two imported classes share a short name.
  • use only creates a shortcut; it doesn't load the file. Loading is autoloading's job.

Composer and PSR-4 autoloading

Install Composer from getcomposer.org, then in your project folder run composer init, or create composer.json yourself:

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

This PSR-4 rule says: classes whose names start with App\ live in the src folder, with the rest of the name as the path. So App\Models\User is loaded from src/Models/User.php. Run composer dump-autoload, then put one line at the start of your entry file, such as index.php:

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

From then on, every class loads itself the first time it's used.

Installing packages

composer require downloads a package and everything it depends on into the vendor folder, and records it in composer.json:

CommandDoes
composer require vlucas/phpdotenvAdd a package
composer require --dev phpunit/phpunitAdd a package only needed for development
composer installInstall exactly the versions in composer.lock
composer updateUpdate packages within the allowed versions
composer outdatedList packages with newer versions

Packages are listed on packagist.org. Commit composer.json and composer.lock to Git, but add vendor/ to .gitignore: anyone can rebuild it with composer install.

PHP Composer Syntax

Namespaces and Composer

<?php
namespace App\Services;

use App\Models\User;
use DateTimeImmutable as Date;

require __DIR__ . '/../vendor/autoload.php';
composer init
composer require vendor/package
composer require --dev phpunit/phpunit
composer install
composer dump-autoload

PHP Composer Summary

  • A namespace gives classes a full name like App\Models\User, avoiding clashes.
  • use imports a class by its short name, with as for aliases; built-in classes are in the global namespace.
  • PSR-4 in composer.json maps a namespace prefix to a folder; one class per file.
  • require 'vendor/autoload.php' once, and classes load automatically.
  • composer require adds packages; commit composer.json and composer.lock, not vendor/.

PHP Composer Examples

Each example shows the code, the output it produces and what each part does.

Example 1

A small project with namespaces

Text
my-shop/
  composer.json
  public/index.php
  src/Models/Product.php
  src/Services/PriceFormatter.php
  vendor/              (created by Composer)
PHP
<?php
// src/Models/Product.php
namespace App\Models;

class Product {
    public function __construct(
        public readonly string $name,
        public readonly float $price,
    ) {}
}
PHP
<?php
// src/Services/PriceFormatter.php
namespace App\Services;

use App\Models\Product;

class PriceFormatter {
    public function format(Product $p): string {
        return $p->name . ": £" . number_format($p->price, 2);
    }
}
Output
App\Models\Product        -> src/Models/Product.php
App\Services\PriceFormatter -> src/Services/PriceFormatter.php
Line-by-line explanation
  1. Line 3In both files, the namespace matches the folder: App\Models is src/Models and App\Services is src/Services.
  2. Line 5In Product.php, the class name matches the file name exactly. In PriceFormatter.php, line 5 imports Product so its short name works.
Example 2

The entry file and the autoloader

PHP
<?php
// public/index.php
require __DIR__ . '/../vendor/autoload.php';

use App\Models\Product;
use App\Services\PriceFormatter;

$items = [new Product('Backpack', 34.99), new Product('Pen', 1.2)];
$formatter = new PriceFormatter();

foreach ($items as $item) {
    echo $formatter->format($item) . "\n";
}
echo get_class($formatter) . "\n";
Output
Backpack: £34.99
Pen: £1.20
App\Services\PriceFormatter
Line-by-line explanation
  1. Line 3The one and only require: Composer's autoloader.
  2. Lines 5 to 6Import the classes by their full names.
  3. Lines 8 to 9The first time Product and PriceFormatter are used, the autoloader finds and loads their files.
  4. Line 14get_class shows the full, namespaced name.
Example 3

Installing and using a package

Terminal
composer require nesbot/carbon
PHP
<?php
require __DIR__ . '/vendor/autoload.php';

use Carbon\CarbonImmutable;

$posted = CarbonImmutable::parse('2024-07-01 09:00');
$now = CarbonImmutable::parse('2024-07-05 09:00');
echo "Posted " . $posted->diffForHumans($now) . "\n";
echo "Deadline: " . $now->addWeekdays(3)->format('l j F') . "\n";
Output
Using version ^3.8 for nesbot/carbon
./composer.json has been updated
Package operations: 4 installs, 0 updates, 0 removals
Generating autoload files

Posted 4 days before
Deadline: Wednesday 10 July
Line-by-line explanation
  1. Line 1composer require downloads Carbon, a popular date library, plus its dependencies. Your version numbers may differ.
  2. Line 4The package's classes live in its own namespace, Carbon.
  3. Lines 8 to 9Ready-made helpers like diffForHumans save writing your own date code.

Common PHP Composer Mistakes

Code before the namespace

Don't
<?php
echo "Loading";
namespace App\Models;        // Fatal error: namespace must come first
Do
<?php
namespace App\Models;

Built-in classes inside a namespace

Don't
namespace App\Services;
$pdo = new PDO($dsn);        // looks for App\Services\PDO
Do
namespace App\Services;
use PDO;
$pdo = new PDO($dsn);

Committing the vendor folder

Don't
git add vendor/              # thousands of files that Composer can rebuild
Do
# .gitignore
/vendor/
# commit composer.json and composer.lock instead

PHP Composer in Real Projects

Composer in the PHP world

Laravel, Symfony, WordPress plugins and nearly every modern PHP library are installed with Composer, and Packagist hosts hundreds of thousands of packages. Hosting platforms run composer install --no-dev when deploying, so production servers get exactly the tested versions recorded in composer.lock, without the development tools.

PHP Composer FAQ

What is the difference between composer install and composer update?

composer install installs the exact versions recorded in composer.lock, so everyone on the project and the production server gets identical packages. composer update looks for newer versions allowed by composer.json, installs them and rewrites the lock file. Run update deliberately, review the changes and commit the new lock file.

Why does PHP say "Class not found" with Composer?

Check four things: the entry file requires vendor/autoload.php; the namespace matches the PSR-4 prefix in composer.json; the folder and file names match the namespace and class name exactly, including capital letters; and you ran composer dump-autoload after changing the autoload settings.

How do I use a function or constant from another namespace?

Import it with use function or use const: use function App\Helpers\formatPrice; lets you call formatPrice() directly. Without an import, write the fully qualified name with a leading backslash, \App\Helpers\formatPrice(). Composer's files autoload setting loads files of functions, since PSR-4 only covers classes.

Do I need Composer for a small PHP project?

Not strictly, but it quickly pays off. Even with no packages, Composer's autoloader removes require lists, and as soon as you need a library, such as a mailer or a PDF generator, it handles downloading, versions and dependencies for you.

Should vendor be uploaded to the server?

Not by copying it from your computer. Deploy composer.json and composer.lock, then run composer install --no-dev --optimize-autoloader on the server or in your build pipeline. That installs exactly the locked versions without development tools and builds a faster autoloader.

PHP Composer Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Complete the line that loads Composer's autoloader.

require __DIR__ . '/vendor/';
Multiple choiceeasy

What is a namespace for?

Multiple choicemedium

With PSR-4 mapping "App\\" to "src/", where does App\Services\Mailer live?

Multiple choicemedium

Which command adds a package needed only during development?

Multiple choicehard

Why commit composer.lock?

PHP Composer Quiz

5 questions to check what you learned.

  1. Which keyword imports a class by its short name?

    • use
    • import
    • require
    • include
  2. Where must the namespace declaration go?

    • Anywhere in the file
    • At the top of the file, before any other code
    • At the end of the file
    • Inside the class
  3. Should the vendor folder be committed to Git?

    • No, it's rebuilt with composer install
    • Yes, always
    • Only the autoload.php file
    • Only on Windows
  4. What does use App\Services\Mailer as AppMailer; do?

    • Renames the file
    • Creates a new class
    • Imports the class under the alias AppMailer
    • Loads the file immediately
  5. What does autoloading do?

    • Loads every class when the app starts
    • Updates packages
    • Compiles PHP code
    • Loads a class's file automatically the first time the class is used