Kodokon kodokon.com

Файлы: чтение, запись, загрузка, JSON

Читай и записывай файлы, веди журнал событий, безопасно принимай загруженные файлы и храни данные в JSON.

10 мин · 3 вопросов

Открыть этот урок в Kodokon

Чтобы прочитать файл, хватит одной функции: file_get_contents() возвращает всё содержимое строкой, file() разбивает его на массив строк. Перед чтением проверь доступ через is_readable(): неверный путь вернул бы false вместе с предупреждением.

PHP
<?php

declare(strict_types=1);

$path = __DIR__ . '/notes.txt';

if (!is_readable($path)) {
    exit("File not found or unreadable.");
}

$content = (string) file_get_contents($path);
echo nl2br(htmlspecialchars($content, ENT_QUOTES));

$lines = file(
    $path,
    FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES
);
echo count($lines) . " non-empty line(s)";
Читаем один и тот же файл целиком, а затем построчно.

Для записи file_put_contents() по умолчанию заменяет всё содержимое. Два флага меняют дело: FILE_APPEND дописывает в конец вместо перезаписи, LOCK_EX ставит исключительную блокировку, чтобы два одновременных запроса не перемешали свои записи. Вот и рецепт минимального журнала приложения.

PHP
<?php

declare(strict_types=1);

$logFile = __DIR__ . '/app.log';

$line = sprintf(
    "[%s] New order from %s\n",
    date('Y-m-d H:i:s'),
    'alice@example.com'
);

file_put_contents(
    $logFile,
    $line,
    FILE_APPEND | LOCK_EX
);

echo "Event logged.";
Надёжный журнал приложения в несколько строк.

Загрузка файлов требует особой осторожности. Форма должна объявить enctype="multipart/form-data", после чего PHP описывает полученный файл в $_FILES. Проверяй всё: код ошибки, размер и настоящий тип, определённый через finfo - никогда не расширение и не тип, заявленный браузером. И наконец, перемещать временный файл должна только move_uploaded_file().

PHP
<?php

declare(strict_types=1);

$maxSize = 2 * 1024 * 1024;
$file = $_FILES['avatar'] ?? null;

if ($file === null || $file['error'] !== UPLOAD_ERR_OK) {
    exit("No valid file received.");
}

if ($file['size'] > $maxSize) {
    exit("File too large (2 MB max).");
}

$info = new finfo(FILEINFO_MIME_TYPE);
$mime = $info->file($file['tmp_name']);
$allowed = ['image/jpeg' => 'jpg', 'image/png' => 'png'];

if (!isset($allowed[$mime])) {
    exit("Format not allowed.");
}

$name = bin2hex(random_bytes(8));
$target = __DIR__ . '/uploads/' . $name
    . '.' . $allowed[$mime];

move_uploaded_file($file['tmp_name'], $target);
echo "Image saved.";
upload.php: размер, настоящий тип и имя файла под контролем.

Формат JSON - естественный мост между PHP и остальным миром: json_encode() сериализует массив, json_decode() делает обратное. Передавай true вторым аргументом, чтобы получать ассоциативные массивы, и флаг JSON_THROW_ON_ERROR, чтобы битый JSON превращался в понятное исключение, а не в молчаливый null.

PHP
<?php

declare(strict_types=1);

$path = __DIR__ . '/tasks.json';

$json = is_readable($path)
    ? (string) file_get_contents($path)
    : '[]';

$tasks = json_decode(
    $json,
    true,
    512,
    JSON_THROW_ON_ERROR
);

$tasks[] = [
    'title' => 'Review the lesson',
    'done' => false,
];

file_put_contents(
    $path,
    json_encode(
        $tasks,
        JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE
    ),
    LOCK_EX
);

echo count($tasks) . " task(s) saved";
Мини-хранилище данных: прочитать, изменить, перезаписать.

Проверка знаний

Убедись, что запомнил ключевые моменты этого урока.

  1. Что делает флаг FILE_APPEND, переданный в file_put_contents()?
    • Он дописывает содержимое в конец файла вместо перезаписи
    • Он создаёт родительскую папку, если её нет
    • Он блокирует файл на время записи
  2. Почему нельзя полагаться на $_FILES['avatar']['type'] при проверке загруженного файла?
    • Этот ключ существует только на некоторых серверах
    • Он работает только для изображений
    • Это значение присылает браузер, и его можно подделать; настоящий тип нужно определять через finfo
  3. Что меняет второй аргумент true в json_decode($json, true)?
    • Декодирование выбрасывает исключение при ошибке
    • Объекты JSON становятся ассоциативными массивами PHP вместо объектов stdClass
    • Ключи переводятся в верхний регистр