Avatar ·

Создать папку, если она не существует: правильный способ в PHP

Всем привет! Я новичок в PHP и пытаюсь сделать простую CMS на WordPress. Мне нужно, чтобы при загрузке изображений на сервер создавалась директория по дате, например /uploads/2024/03/. Но я постоянно получаю ошибку "failed to open stream: No such file or directory", если папки ещё нет.

Вот мой текущий код:

$path = '/var/www/html/uploads/' . date('Y/m');
$file = $path . '/image.jpg';

// Пытаюсь сохранить файл
file_put_contents($file, $data);

Я уже пробовал использовать mkdir($path) перед записью, но всё равно папка не создаётся. Погуглил — пишут, что нужно проверять существование. Я добавил проверку:

if (!file_exists($path)) {
    mkdir($path);
}

Но теперь ошибка "mkdir(): No such file or directory"! Что я делаю не так? Почему mkdir не может создать папку, если её нет? Может, нужно права выставлять? Или я неправильно указываю путь? Подскажите, как правильно создать вложенные папки, если их ещё не существует.

Заранее спасибо! Использую PHP 7.4 на Ubuntu.

<<>>

Правильное создание рекурсивных директорий в PHP

Ваша проблема типична для начинающих разработчиков. Ошибка возникает из-за того, что mkdir() по умолчанию не создаёт вложенные каталоги. Когда вы пытаетесь создать /uploads/2024/03/, PHP пытается создать папку 03 внутри 2024, но этой папки тоже нет — отсюда и ошибка.

Решение — использовать рекурсивный режим с помощью параметра recursive = true:

$path = '/var/www/html/uploads/' . date('Y/m');

if (!is_dir($path)) {
    mkdir($path, 0755, true);  // третий параметр = true включает рекурсию
}

$file = $path . '/image.jpg';
file_put_contents($file, $data);

Детальное объяснение

  • Первый параметр — путь к создаваемой директории.
  • Второй параметр (0755) — права доступа. 0755 — стандарт для веб-папок: владелец может всё, остальные — читать и выполнять.
  • Третий параметр (true) — рекурсивное создание всех недостающих родительских директорий.

Почему file_exists() не подходит?

file_exists() возвращает true и для файлов, и для директорий. Лучше использовать is_dir() — он проверяет именно существование папки:

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

Полный рабочий пример с обработкой ошибок

$baseDir = '/var/www/html/uploads/';
$datePath = date('Y/m');
$fullPath = $baseDir . $datePath;

// Проверяем и создаём папку
if (!is_dir($fullPath)) {
    if (!mkdir($fullPath, 0755, true)) {
        die('Не удалось создать директорию: ' . $fullPath);
    }
}

// Теперь можно сохранять файл
$file = $fullPath . '/image.jpg';
if (file_put_contents($file, $data) === false) {
    die('Ошибка записи файла');
}

echo 'Файл успешно сохранён в ' . $file;

Важные моменты для WordPress

  • В WordPress используйте wp_upload_dir() — он возвращает правильный путь к папке загрузок.
  • Права 0755 обычно достаточны, но если у вас проблемы — проверьте, что веб-сервер (www-data) имеет права на запись в родительскую директорию.
  • Для безопасности никогда не используйте права 0777 — это открывает доступ всем.

Теперь ваш код будет работать корректно: mkdir создаст сразу всю цепочку папок uploads/2024/03/, даже если ни одной из них не существует.

<<>>

Альтернативный подход: функция с проверкой и рекурсией

Если вы предпочитаете более универсальное решение, можно написать небольшую функцию-обёртку:

function ensureDirectoryExists($path, $permissions = 0755) {
    if (is_dir($path)) {
        return true;
    }
    
    // Создаём родительскую директорию рекурсивно
    $parent = dirname($path);
    if (!is_dir($parent)) {
        ensureDirectoryExists($parent, $permissions);
    }
    
    return mkdir($path, $permissions);
}

// Использование:
$path = '/var/www/html/uploads/' . date('Y/m');
if (ensureDirectoryExists($path)) {
    file_put_contents($path . '/image.jpg', $data);
} else {
    echo 'Ошибка создания директории';
}

Почему этот вариант может быть полезен?

  • Вы полностью контролируете процесс создания — можно добавить логирование ошибок.
  • Рекурсия работает как встроенная, но даёт больше гибкости (например, можно выставить разные права для разных уровней).
  • Функция возвращает true/false, что удобно для условных конструкций.

Однако для большинства случаев проще использовать mkdir($path, 0755, true) — это стандартное и надёжное решение, которое рекомендуют в документации PHP.

Совет: всегда проверяйте, что базовый путь (/var/www/html/uploads/) существует и доступен для записи веб-серверу, иначе никакая рекурсия не поможет.