🐘 PHP

Trabajo con archivos en PHP: guía detallada

Что это такое и зачем? (аналогия из реальной жизни)

Представь, что ты пишешь сочинение в тетради. Ты открываешь тетрадь, читаешь, что написал вчера, дописываешь новую страницу, исправляешь ошибки, а потом закрываешь тетрадь и убираешь в рюкзак. Завтра ты снова откроешь её и увидишь всё, что написал сегодня.

В программировании работа с файлами — это то же самое. Только вместо тетради у нас файл на компьютере (например, notes.txt), а вместо ручки — PHP-код. Программа может:

  • Открыть файл (как открыть тетрадь)
  • Прочитать из него данные (как прочитать написанное)
  • Записать в него новые данные (как дописать новую страницу)
  • Закрыть файл (как закрыть тетрадь)
💡 Важно: Если не закрыть файл, он может «залипнуть» — другие программы не смогут к нему обратиться. Как если бы ты оставил тетрадь открытой на столе, и её случайно залили чаем.

Зачем это нужно? Представь, что ты делаешь сайт для школьного клуба. Тебе нужно:

  • Сохранять список участников (чтобы он не исчезал при перезагрузке страницы)
  • Читать расписание из файла
  • Записывать ошибки в лог (специальный файл для отладки)
  • Создавать конфигурационные файлы с настройками

Без работы с файлами твоя программа — как человек с амнезией: она забывает всё, что было до перезагрузки. Файлы дают ей память.

Теория: как это работает изнутри

Когда PHP работает с файлом, происходит несколько важных этапов. Давай заглянем «под капот».

Этап 1: Путь к файлу

Прежде чем открыть файл, нужно знать, где он лежит. Путь бывает:

  • Абсолютный — полный адрес от корня диска: C:xampphtdocsmyprojectdata.txt (Windows) или /var/www/html/data.txt (Linux)
  • Относительный — от текущей папки: data.txt (файл рядом со скриптом), ../data.txt (на папку выше)
⚠️ Совет: Начинающим лучше использовать относительные пути — они работают на любом компьютере, если сохранять структуру папок.

Этап 2: Режим открытия

Когда ты открываешь файл, ты должен сказать PHP, что ты собираешься с ним делать. Для этого есть специальные режимы (их называют флагами):

Режим Что делает Если файла нет Куда встаёт курсор
r Только чтение Ошибка В начало
r+ Чтение и запись Ошибка В начало
w Только запись (очищает файл!) Создаёт новый В начало
w+ Чтение и запись (очищает файл!) Создаёт новый В начало
a Только запись (добавляет в конец) Создаёт новый В конец
a+ Чтение и запись (добавляет в конец) Создаёт новый В конец
🔑 Запомни: r — read (читать), w — write (писать), a — append (добавлять). Режимы с + дают двойные возможности, но будь осторожен с w+ — он стирает всё содержимое!

Этап 3: Дескриптор файла

Когда файл открыт, PHP возвращает дескриптор — это как номерок в гардеробе. По этому номерку ты можешь обращаться к файлу: читать, писать, закрывать. Дескриптор хранится в переменной.

$handle = fopen("data.txt", "r");  // $handle — наш "номерок"

Этап 4: Буферизация

PHP не читает файл по одному байту — это было бы слишком медленно. Вместо этого он использует буфер: загружает сразу большой кусок файла в оперативную память, а потом отдаёт данные из буфера. Это как если бы ты не ходил в библиотеку за каждой книгой по одной, а взял сразу стопку.

Этап 5: Закрытие файла

Когда работа закончена, нужно сказать PHP: «Всё, я больше не работаю с этим файлом». Это освобождает ресурсы системы. Если этого не сделать, может возникнуть утечка памяти — программа будет занимать всё больше и больше места в оперативной памяти, пока сервер не «упадёт».

Синтаксис в PHP: все формы и варианты

В PHP есть два подхода к работе с файлами: классический (с функциями) и объектно-ориентированный (с классом SplFileObject). Мы рассмотрим оба.

Классический подход (функции)

Функция Что делает Пример
fopen() Открывает файл $f = fopen("file.txt", "r");
fread() Читает указанное количество байт $content = fread($f, 1024);
fgets() Читает одну строку $line = fgets($f);
fgetc() Читает один символ $char = fgetc($f);
file() Читает весь файл в массив (по строкам) $arr = file("file.txt");
file_get_contents() Читает весь файл в строку $str = file_get_contents("file.txt");
fwrite() Записывает строку в файл fwrite($f, "Привет!");
fclose() Закрывает файл fclose($f);
feof() Проверяет, достигнут ли конец файла while(!feof($f)) { ... }
filesize() Возвращает размер файла в байтах $size = filesize("file.txt");

Объектно-ориентированный подход (SplFileObject)

// Создаём объект файла
$file = new SplFileObject("data.txt", "r");

// Читаем первую строку
$firstLine = $file->fgets();

// Перебираем все строки
foreach ($file as $lineNumber => $lineContent) {
    echo "Строка $lineNumber: $lineContent";
}

// Не нужно закрывать — PHP сделает это сам, когда объект удалится
🌟 Для новичков: Начни с функций — они проще для понимания. А когда освоишься, переходи на объекты — это современный стиль.

Примеры кода — от простого к сложному

Пример 1: Чтение всего файла одной строкой

<?php
// Самый простой способ прочитать файл целиком
$content = file_get_contents("hello.txt");

// Выводим содержимое на экран
echo $content;
?>

Объяснение: file_get_contents() — это функция-«швейцарский нож». Она открывает файл, читает всё содержимое в строку и закрывает файл. Всё за один шаг. Идеально для маленьких файлов.

Пример 2: Чтение файла построчно с проверкой

<?php
// Открываем файл для чтения
$file = fopen("students.txt", "r");

// Проверяем, удалось ли открыть
if ($file === false) {
    die("Ошибка: не удалось открыть файл!");
}

// Читаем строку за строкой, пока не дойдём до конца
while (($line = fgets($file)) !== false) {
    // Убираем лишние пробелы и переносы строк
    $line = trim($line);
    echo "Ученик: " . $line . "
"; } // Обязательно закрываем файл! fclose($file); ?>

Объяснение: Здесь мы используем fopen() для открытия, fgets() для чтения одной строки и fclose() для закрытия. Цикл while продолжается, пока fgets() не вернёт false (это значит, что строки кончились). trim() убирает лишние пробелы и символы переноса строки.

Пример 3: Чтение файла в массив

<?php
// file() читает файл и возвращает массив строк
$lines = file("poem.txt");

// Проверяем, что файл прочитан успешно
if ($lines === false) {
    die("Не удалось прочитать файл!");
}

// Выводим каждую строку с номером
foreach ($lines as $index => $line) {
    echo ($index + 1) . ". " . $line . "
"; } ?>

Объяснение: file() — удобная функция, когда нужно работать с каждой строкой отдельно. Она возвращает массив, где каждый элемент — одна строка файла. Нумерация начинается с 0, поэтому мы прибавляем 1 для красоты.

Пример 4: Чтение определённого количества символов

<?php
// Открываем файл
$file = fopen("bigfile.txt", "r");

// Читаем первые 100 символов
$first100 = fread($file, 100);
echo "Первые 100 символов: " . $first100 . "
"; // Пропускаем следующие 50 символов (курсор сдвигается) fseek($file, 50, SEEK_CUR); // Читаем ещё 50 символов $next50 = fread($file, 50); echo "Следующие 50 символов: " . $next50 . "
"; // Возвращаемся в начало файла fseek($file, 0, SEEK_SET); // Читаем снова первые 10 символов $first10 = fread($file, 10); echo "Снова первые 10: " . $first10 . "
"; fclose($file); ?>

Объяснение: fread() читает указанное количество байт (символов). fseek() позволяет перемещать курсор по файлу: SEEK_SET — от начала, SEEK_CUR — от текущей позиции, SEEK_END — от конца. Это как бегунок на плёнке: ты можешь перемотать в любое место.

Пример 5: Чтение CSV-файла (табличные данные)

<?php
// Открываем CSV-файл (таблица с разделителями-запятыми)
$file = fopen("grades.csv", "r");

// Читаем заголовки столбцов (первая строка)
$headers = fgetcsv($file);
echo "Столбцы: " . implode(", ", $headers) . "
"; // Читаем остальные строки while (($row = fgetcsv($file)) !== false) { echo "Имя: " . $row[0] . ", Оценка: " . $row[1] . "
"; } fclose($file); ?>

Объяснение: fgetcsv() — специальная функция для CSV-файлов. Она читает строку и разбивает её на массив по запятым (или другому разделителю). Это очень удобно для работы с таблицами, экспортированными из Excel.

Пример 6: Чтение с обработкой ошибок (try-catch)

<?php
// Современный способ с обработкой исключений
try {
    // Попытка прочитать файл
    $content = file_get_contents("config.json");
    
    // Если файл пустой, выбрасываем исключение
    if ($content === false) {
        throw new Exception("Файл не найден или недоступен!");
    }
    
    // Преобразуем JSON в массив
    $config = json_decode($content, true);
    
    // Проверяем, удалось ли распарсить JSON
    if ($config === null) {
        throw new Exception("Ошибка в JSON-формате!");
    }
    
    echo "Настройки загружены: " . print_r($config, true);
    
} catch (Exception $e) {
    // Ловим ошибку и выводим понятное сообщение
    echo "Произошла ошибка: " . $e->getMessage();
}
?>

Объяснение: Это профессиональный стиль. Мы используем try-catch — конструкцию, которая «ловит» ошибки. Если что-то пошло не так, программа не падает, а выдаёт понятное сообщение. json_decode() превращает JSON-строку в массив PHP.

Пример 7: Чтение большого файла по частям (для экономии памяти)

<?php
// Если файл очень большой (например, 1 ГБ), нельзя читать его целиком
// Будем читать по кусочкам — по 1 КБ (1024 байта)

$file = fopen("huge_log.txt", "r");
$bufferSize = 1024;  // Размер буфера в байтах

// Читаем, пока не кончится файл
while (!feof($file)) {
    // Читаем очередной кусочек
    $chunk = fread($file, $bufferSize);
    
    // Обрабатываем кусочек (например, ищем ошибки)
    if (strpos($chunk, "ERROR") !== false) {
        echo "Найдена ошибка в куске!
"; } // Очищаем буфер, чтобы освободить память unset($chunk); } fclose($file); echo "Обработка завершена!"; ?>

Объяснение: feof() проверяет, не закончился ли файл. Мы читаем по 1 КБ за раз — это позволяет обрабатывать файлы любого размера, не загружая их целиком в память. strpos() ищет подстроку в строке. unset() удаляет переменную из памяти.

Сравнение с Python: чем похоже и чем отличается

Если ты уже знаешь Python или планируешь его учить, вот сравнение:

Операция PHP Python
Открыть файл $f = fopen("file.txt", "r"); f = open("file.txt", "r")
Прочитать всё file_get_contents("file.txt") f.read()
Прочитать строку fgets($f) f.readline()
Прочитать все строки file("file.txt") f.readlines()
Закрыть файл fclose($f) f.close()
Автоматическое закрытие Нет (нужно вызывать fclose) Есть (конструкция with)
Проверка конца файла feof($f) Цикл for line in f:

Главные отличия:

  • В PHP ты должен сам закрывать файл (fclose). Забыл — получил утечку памяти.
  • В Python есть конструкция with open(...) as f:, которая закрывает файл автоматически, даже если произошла ошибка.
  • В PHP функции для работы с файлами — это отдельные функции. В Python — это методы объекта файла.
  • В PHP есть file_get_contents() — очень удобная функция для маленьких файлов. В Python аналог — f.read().
💡 Совет: Если ты переходишь с Python на PHP, запомни главное правило: в PHP всегда проверяй, удалось ли открыть файл, и всегда закрывай его вручную.

Типичные ошибки и подводные камни

Ошибка 1: Забыл проверить, открылся ли файл

// ❌ НЕПРАВИЛЬНО
$file = fopen("secret.txt", "r");
$content = fread($file, 100);  // Если файла нет — ошибка!

// ✅ ПРАВИЛЬНО
$file = fopen("secret.txt", "r");
if ($file === false) {
    die("Файл не найден!");
}
$content = fread($file, 100);

Ошибка 2: Забыл закрыть файл

// ❌ НЕПРАВИЛЬНО
$file = fopen("log.txt", "a");
fwrite($file, "Новая запись");
// Файл остался открытым!

// ✅ ПРАВИЛЬНО
$file = fopen("log.txt", "a");
fwrite($file, "Новая запись");
fclose($file);  // Всегда закрывай!

Ошибка 3: Использование w вместо a

// ❌ НЕПРАВИЛЬНО (удалит всё содержимое!)
$file = fopen("data.txt", "w");
fwrite($file, "Новый текст");  // Старый текст исчез!

// ✅ ПРАВИЛЬНО (если нужно добавить)
$file = fopen("data.txt", "a");
fwrite($file, "Добавленный текст");  // Старый текст сохранился

Ошибка 4: Проблемы с кодировкой

// ❌ НЕПРАВИЛЬНО (русские буквы превратятся в "кракозябры")
$content = file_get_contents("russian.txt");
echo $content;  // Может отобразиться неправильно

// ✅ ПРАВИЛЬНО (указываем кодировку)
$content = file_get_contents("russian.txt");
$content = mb_convert_encoding($content, "UTF-8", "auto");
echo $content;

Ошибка 5: Чтение всего файла в память, если он огромный

// ❌ НЕПРАВИЛЬНО (если файл 2 ГБ — память закончится!)
$content = file_get_contents("huge_file.txt");

// ✅ ПРАВИЛЬНО (читаем по частям)
$file = fopen("huge_file.txt", "r");
while (!feof($file)) {
    $chunk = fread($file, 4096);
    // обрабатываем кусочек
}
fclose($file);

Ошибка 6: Неправильные пути

// ❌ НЕПРАВИЛЬНО (если файл в другой папке)
$content = file_get_contents("config.txt");  // Ищет в текущей папке

// ✅ ПРАВИЛЬНО (указываем полный или относительный путь)
$content = file_get_contents(__DIR__ . "/config/config.txt");
// __DIR__ — магическая константа, которая содержит путь к текущей папке

Лучшие практики и советы профессионалов

1. Всегда используй try-catch для критических операций

try {
    $content = file_get_contents("important.txt");
    if ($content === false) {
        throw new Exception("Не удалось прочитать файл");
    }
    // работаем с содержимым
} catch (Exception $e) {
    error_log($e->getMessage());  // Записываем ошибку в лог
    echo "Что-то пошло не так. Попробуйте позже.";
}

2. Используй __DIR__ для путей

Это гарантирует, что путь будет правильным, даже если скрипт запускается из другой папки.

$path = __DIR__ . "/data/settings.ini";
$content = file_get_contents($path);

3. Проверяй права доступа

Файл может существовать, но PHP может не иметь прав на его чтение. Используй is_readable():

if (is_readable("secret.txt")) {
    $content = file_get_contents("secret.txt");
} else {
    echo "Нет прав на чтение файла!";
}

4. Для логов используй error_log()

Не выводи ошибки на экран пользователя — записывай их в специальный файл:

error_log("Пользователь " . $user . " попытался зайти в админку");

5. Используй буферизацию для больших файлов

Если нужно отдать большой файл пользователю, не читай его в память — используй readfile():

// Отдаёт файл прямо в вывод, не загружая в память
readfile("big_video.mp4");

6. Для конфигураций используй JSON или INI

Не храни настройки в PHP-файлах — это небезопасно. Используй JSON:

$config = json_decode(file_get_contents("config.json"), true);
$dbHost = $config['database']['host'];

7. Закрывай файлы в обратном порядке

Если открываешь несколько файлов, закрывай их в обратном порядке открытия:

$f1 = fopen("a.txt", "r");
$f2 = fopen("b.txt", "r");
// работаем...
fclose($f2);  // Сначала закрываем последний
fclose($f1);  // Потом первый

Вопросы для самопроверки

  1. Вопрос: Какая функция читает весь файл в строку за один вызов?

    Ответ: file_get_contents(). Она открывает, читает и закрывает файл автоматически.

  2. Вопрос: Чем отличается режим w от a?

    Ответ: w очищает файл перед записью и начинает с начала. a не очищает файл и добавляет данные в конец.

  3. Вопрос: Что произойдёт, если попытаться открыть несуществующий файл в режиме r?

    Ответ: PHP вернёт false и выдаст предупреждение. Файл не будет создан.

  4. Вопрос: Как прочитать файл построчно?

    Ответ: Использовать fopen(), затем цикл while (($line = fgets($file)) !== false), затем fclose(). Или использовать file() для чтения в массив.

  5. Вопрос: Что такое дескриптор файла?

    Ответ: Это специальный идентификатор (число), который PHP использует для обращения к открытому файлу. Он возвращается функцией fopen().

  6. Вопрос: Почему нужно закрывать файлы?

    Ответ: Чтобы освободить системные ресурсы. Если не закрыть файл, может возникнуть утечка памяти, и другие программы не смогут работать с этим файлом.

  7. Вопрос: Как проверить, достигнут ли конец файла?

    Ответ: Функцией feof($file). Она возвращает true, если курсор находится в конце файла.

  8. Вопрос: Что делает функция fseek()?

    Ответ: Перемещает курсор в файле на указанную позицию. Можно перемещаться от начала (SEEK_SET), от текущей позиции (SEEK_CUR) или от конца (SEEK_END).

  9. Вопрос: Как прочитать CSV-файл в PHP?

    Ответ: Использовать fgetcsv($file) — она читает строку и разбивает её на массив по разделителю (по умолчанию — запятая).

  10. Вопрос: В чём преимущество SplFileObject перед обычными функциями?

    Ответ: Он использует объектно-ориентированный подход, поддерживает итерацию по строкам через foreach и автоматически закрывает файл при удалении объекта.

Итог и что изучить дальше

Мы прошли большой путь! Ты узнал:

  • Что файлы — это «память» программы, как тетрадь для записей
  • Как открывать, читать и закрывать файлы в PHP
  • Какие бывают режимы открытия и чем они отличаются
  • Как читать файлы целиком, по строкам, по символам и по кусочкам
  • Как работать с CSV и JSON
  • Как избегать типичных ошибок
  • Как писать профессиональный код с обработкой ошибок

Что