Создать папку в PHP, если её нет: эволюция одного вопроса
Если вы когда-нибудь писали скрипт, который загружает файлы, сохраняет кэш или генерирует отчёты, то наверняка сталкивались с ситуацией: «А папки-то нет!». Ошибка, которая валит весь скрипт, а пользователь видит белый экран или (что ещё хуже) непонятную ошибку PHP.
Вопрос «Create a folder if it doesn't already exist» на Stack Overflow собирает тысячи просмотров. И неспроста. Казалось бы, что сложного? Но дьявол, как всегда, в деталях: права доступа, рекурсия, обработка исключений, кодировка имени папки. В 2025 году, когда проекты становятся всё сложнее, а требования к безопасности — жёстче, правильное создание директорий — это база, которую обязан знать каждый PHP-разработчик.
В этой статье мы разберём 5 лучших практик создания папки в PHP, если она не существует. От простого решения для новичков до промышленного кода с защитой от race condition. Поехали!
Почему просто mkdir() — это плохо? (история одной ошибки)
В начале пути многие пишут так:
<?php
mkdir('/path/to/my/folder');
?>
И получают:
- Warning: mkdir(): No such file or directory — если родительская папка не существует.
- Warning: mkdir(): File exists — если папка уже есть.
Оба предупреждения — это не просто «жёлтый текст». В production-режиме они могут превратиться в фатальные ошибки, если включён строгий режим отчёта ошибок. А ещё это дыра в безопасности: злоумышленник может создать папку с тем же именем раньше вашего скрипта (race condition).
Поэтому современный подход — это всегда проверка, рекурсия и контроль прав.
Метод 1: Классика с is_dir() и mkdir() (для новичков)
Самый простой и понятный способ — сначала проверить, существует ли папка, а потом создать её.
<?php
$folder = '/var/www/uploads/images';
if (!is_dir($folder)) {
mkdir($folder, 0755, true);
echo "Папка создана!";
} else {
echo "Папка уже существует.";
}
?>
Что тут важно:
is_dir()— возвращает true, если путь существует и является директорией.mkdir($folder, 0755, true)— третий параметрtrueвключает рекурсивное создание. Если родительской папки нет, PHP создаст всю цепочку (/var/www/uploads/images→ создастuploads, потомimages).0755— права доступа: владелец может всё, остальные — читать и выполнять.
Минус: этот код не защищён от race condition. Если два запроса одновременно проверят is_dir() и оба попытаются создать папку, второй вызовет предупреждение.
Метод 2: Промышленный подход с @mkdir() и проверкой ошибок
Опытные разработчики часто используют оператор подавления ошибок @ и затем явно проверяют результат.
<?php
$folder = '/var/www/uploads/images';
if (!is_dir($folder)) {
// Подавляем warning, если папка уже существует
if (!@mkdir($folder, 0755, true) && !is_dir($folder)) {
throw new \RuntimeException('Не удалось создать папку: ' . $folder);
}
}
?>
Почему это лучше?
- Мы подавляем warning, но не игнорируем ошибку — если создать не удалось, выбрасывается исключение.
- Двойная проверка
!is_dir($folder)послеmkdir()защищает от race condition: если другой процесс создал папку между нашей проверкой и вызовомmkdir(), мы не упадём.
Внимание: оператор @ снижает производительность, так как PHP всё равно генерирует ошибку, но скрывает её. В высоконагруженных проектах лучше использовать метод 3.
Метод 3: Современный подход с исключениями (PHP 8+)
С выходом PHP 8.0 появилась возможность использовать try-catch с mkdir() без подавления ошибок. Просто включите строгую типизацию и используйте \Throwable.
<?php
declare(strict_types=1);
function createFolder(string $path, int $permissions = 0755): bool
{
if (is_dir($path)) {
return true;
}
try {
if (!mkdir($path, $permissions, true) && !is_dir($path)) {
throw new \RuntimeException('Не удалось создать директорию');
}
return true;
} catch (\Throwable $e) {
// Логируем ошибку
error_log('Ошибка создания папки: ' . $e->getMessage());
return false;
}
}
// Использование
if (createFolder('/var/www/uploads/images')) {
echo "Готово!";
} else {
echo "Ошибка!";
}
?>
Преимущества:
- Чистый код без
@. - Исключения можно обрабатывать централизованно.
- Функция возвращает
bool— удобно для условных конструкций.
Метод 4: Рекурсивное создание с безопасными правами
Иногда нужно создать глубокую вложенность папок, но при этом задать разные права для разных уровней. Например, корневая папка должна быть 0755, а вложенная — 0777 (для загрузки файлов).
<?php
function createDirectoryRecursive(string $path, int $permissions = 0755): void
{
$parent = dirname($path);
// Сначала создаём родительскую папку, если её нет
if (!is_dir($parent)) {
createDirectoryRecursive($parent, $permissions);
}
// Теперь создаём текущую папку
if (!is_dir($path)) {
if (!mkdir($path, $permissions) && !is_dir($path)) {
throw new \RuntimeException("Не удалось создать: $path");
}
// Принудительно устанавливаем права (на случай umask)
chmod($path, $permissions);
}
}
// Пример: создаём /var/www/uploads/2025/01/15
createDirectoryRecursive('/var/www/uploads/2025/01/15', 0777);
?>
Важный нюанс: mkdir() с флагом true создаёт все папки, но права доступа применяются только к последней. Если вам нужно, чтобы все папки имели одинаковые права, используйте рекурсию вручную или после создания выполните chmod().