Корректные имена файлов в PHP проектах

Раздел: Программирование -> Основы PHP

В PHP имена файлов напрямую влияют на возможность автозагрузки классов и общую организацию проекта. Соблюдение единых соглашений облегчает понимание кода и интеграцию с фреймворками. Рассмотрим основные правила и варианты их применения.

Основное правило: стандарт PSR-4

Современный стандарт PSR-4 предписывает, что имя файла должно в точности соответствовать имени класса с учетом регистра, а путь к файлу должен повторять пространство имен (namespace) относительно корневой директории, указанной в автозагрузке. Расширение файла - обязательно .php. Этот подход используется большинством современных фреймворков (Laravel, Symfony, Yii2) и является рекомендуемым.

Пример: Класс App\Models\User должен находиться в файле src/Models/User.php, если корневое пространство имен App привязано к директории src.

// Файл src/Models/User.php
namespace App\Models;

class User {
    // ...
}

Настройка автозагрузки через composer.json:

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

После выполнения composer dump-autoload все классы в указанной директории будут автоматически найдены по имени файла.

Как автоматически загружать классы без PSR-4?

До появления PSR-4 широко использовался стиль PEAR / Zend Framework, где имя класса содержит подчеркивания, а файл называется в точности как класс (с подчеркиваниями) и помещается в иерархию директорий, где подчеркивания заменяются на разделители путей.

Например, класс App_Models_User должен лежать в файле App/Models/User.php. Для автозагрузки используется специальный автозагрузчик:

spl_autoload_register(function ($class) {
    $file = __DIR__ . '/' . str_replace('_', '/', $class) . '.php';
    if (file_exists($file)) {
        require $file;
    }
});

Этот вариант всё ещё встречается в устаревших проектах, но не рекомендуется для новых разработок.

Как называть файлы с вспомогательными функциями?

Для файлов, не содержащих классов (например, наборы функций или конфигурационные файлы), стандарты менее строги. Часто используется kebab-case (слова через дефис) или snake_case. Например: app-helpers.php, config.php, database-connections.php. Главное - сохранять единообразие в проекте.

// Файл helpers/app-helpers.php
function formatDate(string $date): string {
    // ...
}

Как правильно именовать файлы тестов?

Тестовые классы обычно наследуют TestCase и следуют тому же PSR-4. Имя файла соответствует имени класса теста. Распространённая практика - добавлять суффикс Test к имени тестируемого класса. Например, для класса User тест будет UserTest в файле UserTest.php.

// tests/UserTest.php
use PHPUnit\Framework\TestCase;

class UserTest extends TestCase {
    // ...
}

Как отличать файлы интерфейсов и трейтов?

Для интерфейсов и трейтов нет отдельного соглашения по имени файла - оно должно совпадать с именем самого интерфейса/трейта. Однако для удобства часто добавляют суффиксы Interface и Trait в само имя типа. Файл при этом называется соответственно: LoggerInterface.php, LoggableTrait.php.

// Contracts/LoggerInterface.php
namespace App\Contracts;

interface LoggerInterface {
    public function log(string $message): void;
}

// Traits/LoggableTrait.php
namespace App\Traits;

trait LoggableTrait {
    public function log(string $message): void {
        // ...
    }
}

Типичные проблемы и ошибки

  • Регистрозависимость. На файловых системах Linux (ext4) имена файлов чувствительны к регистру, а на Windows (NTFS) - нет. Код, работающий локально на Windows, может упасть на сервере Linux, если регистр в имени класса и файла не совпадает. Решение: всегда выравнивать регистр по PSR-4 и проверять в CI/CD под Linux.
  • Несоответствие namespace и пути. Если пространство имен класса не соответствует пути в composer.json, автозагрузчик не найдёт файл. Решение: проверять раздел autoload и выполнять composer dump-autoload после изменений.
  • Дублирование классов. Два файла с именем User.php в разных неймспейсах допустимы, но если в одном файле объявлен не тот класс, автозагрузка сломается. Решение: придерживаться правила один класс - один файл.
  • Использование специальных символов. Буквы с диакритикой, пробелы, нелатинские символы в именах файлов могут вызвать проблемы на разных ОС. Решение: использовать только латинские буквы, цифры, подчеркивание и дефис.

Подробные примеры и сценарии

Пример 1: Структура проекта с PSR-4 и composer

Создадим проект с классом App\Controller\HomeController. Имя файла HomeController.php в папке src/Controller/.

Пример
// composer.json
{
    "autoload": {
        "psr-4": { "App\\": "src/" }
    }
}

// src/Controller/HomeController.php
namespace App\Controller;

class HomeController {
    public function index(): string {
        return "Home";
    }
}
Результат: после composer dump-autoload класс доступен для автозагрузки. Можно вызвать new \App\Controller\HomeController() без явного require.

Пример 2: Legacy автозагрузка с подчеркиваниями

Допустим, есть класс MyLib_DB_Connector. Файл должен называться MyLib_DB_Connector.php и помещаться в корень, где работает автозагрузчик, или в папку, где подчеркивания заменяются на разделители.

Пример
// MyLib_DB_Connector.php
class MyLib_DB_Connector {
    public function connect() { /* ... */ }
}

// автозагрузчик
spl_autoload_register(function ($class) {
    $file = __DIR__ . "/" . $class . ".php";
    if (file_exists($file)) {
        require $file;
    }
});

// использование
$connector = new MyLib_DB_Connector();
Результат: автозагрузка срабатывает, если файл MyLib_DB_Connector.php существует. Проблема: при глубокой вложенности название класса становится длинным.

Пример 3: Автозагрузка PSR-4 вручную (без Composer)

Можно реализовать свой автозагрузчик, соответствующий PSR-4:

Пример
spl_autoload_register(function ($class) {
    $prefixes = [
        "App\\" => __DIR__ . "/src/",
        "Lib\\" => __DIR__ . "/lib/",
    ];
    foreach ($prefixes as $prefix => $baseDir) {
        $len = strlen($prefix);
        if (strncmp($prefix, $class, $len) === 0) {
            $relativeClass = substr($class, $len);
            $file = $baseDir . str_replace("\\", "/", $relativeClass) . ".php";
            if (file_exists($file)) {
                require $file;
            }
        }
    }
});
Результат: для класса App\Service\Mailer будет открыт файл src/Service/Mailer.php. Этот код полезен для проектов без Composer.

Пример 4: Проблема регистра на разных ОС

Пусть на Windows класс объявлен как UserController в файле Usercontroller.php (разный регистр). Windows найдёт файл, но Linux - нет. Чтобы избежать, нужно строго соблюдать регистр:

Пример
// Неправильно:
class UserController {} // файл Usercontroller.php

// Правильно:
class UserController {} // файл UserController.php
Проверка: на Linux при загрузке класса UserController произойдет ошибка, если файл назван иначе. Решение: использовать инструменты анализа кода, например, PHPStan или проверку в CI.

Пример 5: Именование интерфейсов и трейтов с суффиксами

Создадим интерфейс CacheInterface и трейт CacheableTrait. Файлы называем соответственно.

Пример
// src/Cache/CacheInterface.php
namespace App\Cache;

interface CacheInterface {
    public function get(string $key): mixed;
}

// src/Cache/CacheableTrait.php
namespace App\Cache;

trait CacheableTrait {
    public function get(string $key): mixed {
        // реализация
    }
}
Результат: автозагрузка работает; по имени файла сразу понятно, что это интерфейс или трейт. Можно хранить их в одной папке Cache или в отдельных.

Правила именования файлов в PHP - comments

En
Php название файла (php)