NightFox

NightFox

Composer, Autoloading, and Project Structure

Composer is the single biggest thing that made modern PHP feel modern to me. It isn't just a package installer: it handles autoloading, scripts, PHP version and extension requirements, and a lock file that makes every install of your app match. Even a project with zero dependencies benefits from it.

A practical composer.json

{
    "name": "example/customer-portal",
    "description": "Customer portal application",
    "type": "project",
    "require": {
        "php": "^8.4",
        "ext-json": "*",
        "ext-pdo": "*",
        "vlucas/phpdotenv": "^5.6"
    },
    "require-dev": {
        "phpunit/phpunit": "^11.0"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    },
    "scripts": {
        "test": "phpunit",
        "lint": "php -l public/index.php"
    },
    "config": {
        "sort-packages": true
    }
}
Set the PHP constraint to the supported version range for the environment you actually deploy. The example is a project policy, not a statement that every host runs that version.

Install and update mean different things

CommandUse
composer installInstall the exact dependency versions recorded in composer.lock. Use this in deployment.
composer updateResolve new versions allowed by composer.json and rewrite the lock file. Use intentionally during maintenance.
composer auditCheck installed dependency versions against known security advisories.
composer dump-autoload -oGenerate an optimized autoloader for production.

Keep the document root small

customer-portal/
├── app/
├── config/
├── migrations/
├── public/
│   ├── index.php
│   └── assets/
├── storage/
├── templates/
├── tests/
├── vendor/
├── .env
├── composer.json
└── composer.lock

Point the web server's document root at customer-portal/public, not the project folder. That way .env, logs, source code, and vendor/ simply can't be downloaded, no matter what.

Bootstrap once

<?php
declare(strict_types=1);

use Dotenv\Dotenv;

require dirname(__DIR__) . '/vendor/autoload.php';

$dotenv = Dotenv::createImmutable(dirname(__DIR__));
$dotenv->safeLoad();

set_exception_handler(static function (Throwable $exception): void {
    error_log((string) $exception);
    http_response_code(500);
    echo 'The application encountered an error.';
});

Dependency rules

  • Commit composer.json and composer.lock for applications.
  • Do not commit vendor/ unless the deployment environment genuinely requires it.
  • Review package ownership, maintenance, release history, and transitive dependencies before adoption.
  • Keep production dependencies separate from development tools.
  • Run security audits and tests before merging dependency updates.

Last Modified: October 12, 2026, 11:46 am