Avatar ·

Как создать папку в PHP, если она еще не существует?

Всем привет! Я начинающий PHP-разработчик и пишу сайт на WordPress. У меня возникла проблема: я хочу сохранять загруженные пользователями изображения в отдельную папку для каждого пользователя. Например, у меня есть ID пользователя, и я хочу создать папку вида `/uploads/user_123/`. Я написал такой код: ```php $user_id = 123; $upload_dir = wp_upload_dir(); $user_folder = $upload_dir['basedir'] . '/user_' . $user_id . '/'; if (!file_exists($user_folder)) { mkdir($user_folder); } // Дальше копирую файл... move_uploaded_file($temp_file, $user_folder . '/avatar.jpg'); ``` Но проблема в том, что если папка уже существует, то `mkdir()` выдает предупреждение "Warning: mkdir(): File exists". Я понимаю, что это можно подавить с помощью `@`, но слышал, что это плохая практика. Как правильно создать папку в PHP, если она еще не существует, без ошибок и предупреждений? Может быть, есть более надежный способ, чем мой велосипед с `file_exists()`? И как быть, если нужно создать вложенные папки, например `/uploads/2023/12/`? Заранее спасибо за помощь! <<>>

Использование is_dir() и mkdir() с рекурсивным режимом

Ваш подход в целом правильный, но есть несколько улучшений, которые сделают код более надежным и элегантным. Главная проблема вашего кода — использование file_exists() вместо is_dir(). Дело в том, что file_exists() возвращает true не только для папок, но и для файлов. Если по какому-то совпадению в системе уже есть файл с именем, совпадающим с именем будущей папки, вы получите ошибку.

Правильный вариант:

<?php
$user_id = 123;
$upload_dir = wp_upload_dir();
$user_folder = $upload_dir['basedir'] . '/user_' . $user_id . '/';

if (!is_dir($user_folder)) {
    mkdir($user_folder, 0755, true); // 0755 — права доступа, true — рекурсивное создание
}

// Теперь можно безопасно копировать файл
move_uploaded_file($temp_file, $user_folder . '/avatar.jpg');
?>

Ключевые моменты:

  • is_dir() — проверяет, существует ли именно директория, а не просто файл с таким именем.
  • mkdir($path, $permissions, $recursive) — третий параметр true включает рекурсивное создание. Это значит, что если нужно создать /uploads/user_123/, а папки /uploads/ нет, она тоже будет создана автоматически.
  • Права доступа 0755 — стандартные права для веб-директорий: владелец может всё, остальные — читать и выполнять.

Как создать вложенные папки (например, по дате)

Если вам нужно создавать структуру вида /uploads/2023/12/, рекурсивный режим справится с этим одной строкой:

<?php
$year = date('Y');
$month = date('m');
$path = $upload_dir['basedir'] . '/' . $year . '/' . $month . '/';

if (!is_dir($path)) {
    mkdir($path, 0755, true);
}
?>

Дополнительные рекомендации для WordPress

В WordPress есть встроенная функция wp_mkdir_p(), которая делает то же самое, но с учетом особенностей CMS:

<?php
if (!wp_mkdir_p($user_folder)) {
    // Обработка ошибки, если папку не удалось создать
    wp_die('Не удалось создать директорию для загрузок.');
}
?>

Также рекомендую проверять, удалось ли создать папку, с помощью проверки возвращаемого значения mkdir() или wp_mkdir_p(). Если функция вернула false, значит, что-то пошло не так (нет прав на запись, превышен лимит inode и т.д.).

Почему не стоит подавлять ошибки через @?

Оператор @ подавляет все ошибки, включая критические. Если у вас закончилось место на диске или нет прав, вы не узнаете об этом и будете думать, что всё работает. Всегда лучше явно проверять условие и обрабатывать ошибки.

<<>>

Альтернативный подход: использование исключений (для современных проектов)

Если вы работаете с PHP 7+ и предпочитаете современный стиль, можно использовать исключения вместо условных операторов. Это особенно удобно в ООП-проектах:

<?php
function ensureDirectoryExists(string $path, int $permissions = 0755): void {
    if (is_dir($path)) {
        return; // Папка уже существует
    }
    
    // Пытаемся создать папку
    if (!mkdir($path, $permissions, true) && !is_dir($path)) {
        throw new \RuntimeException(sprintf(
            'Не удалось создать директорию "%s". Проверьте права доступа.',
            $path
        ));
    }
}

// Использование:
try {
    ensureDirectoryExists('/path/to/user_123/');
    // Дальнейшая работа с файлами...
} catch (\RuntimeException $e) {
    echo 'Ошибка: ' . $e->getMessage();
    // Логирование ошибки
}
?>

Плюсы этого подхода:

  • Код становится чище и читаемее.
  • Ошибки обрабатываются централизованно через try-catch.
  • Легко добавить дополнительную логику (например, уведомление администратора).

Минус: для простых скриптов может быть избыточным. Выбирайте подход под свою задачу.