Сохранение содержимого средствами PHP: от основ до продвинутых техник

Раздел: Основы PHP -> Запись данных

Основные способы записи данных в PHP

Как быстро и просто записать строку в файл?

Наиболее эффективное решение - использование функции file_put_contents(). Она открывает файл, записывает данные и закрывает его за один вызов. Не требуется явно управлять указателем или ресурсом.


<?php
$filename = 'log.txt';
$data = 'Новая запись в лог';
file_put_contents($filename, $data);
?>
  

По умолчанию файл перезаписывается. Для дописывания используется флаг FILE_APPEND:


<?php
file_put_contents($filename, $data, FILE_APPEND);
?>
  

Функция возвращает количество записанных байт или false при ошибке. Полезна для протоколирования, кэширования и создания конфигураций.

Типичные ошибки:
  • Отсутствие прав на запись в директорию (Warning: failed to open stream). Решение: проверить права через is_writable() или настроить chmod.
  • Файл уже открыт другим процессом (блокировка не используется). Для одновременной записи используйте ручную блокировку через flock() или атомарную замену (см. примеры).
  • Некорректная обработка многобайтовых строк. При записи file_put_contents работает с байтами, поэтому для Unicode убедитесь, что исходная строка имеет нужную кодировку (например, UTF-8).

Как записать данные с сохранением предыдущего содержимого и возможностью блокировки?

Используйте fopen() + flock() + fwrite() + fclose(). Это даёт полный контроль над процессом.


<?php
$filename = 'data.txt';
$handle = fopen($filename, 'a'); // 'a' для дописывания, 'w' для перезаписи
if (flock($handle, LOCK_EX)) {
    fwrite($handle, "Дополнительная строка\n");
    flock($handle, LOCK_UN);
}
fclose($handle);
?>
  

Режим 'a' устанавливает указатель в конец файла. Флаг LOCK_EX предотвращает одновременную запись из разных процессов. Подходит для высоконагруженных логов и очередей.

Проблемы:
  • Забытая блокировка может привести к взаимоблокировке (deadlock). Всегда снимайте блокировку.
  • На NFS блокировка может не работать. Альтернатива - атомарная запись через временный файл.

Как записать данные в формате JSON или CSV с гарантией атомарности?

Для атомарной записи (другие процессы увидят только полностью записанный файл) используйте временный файл и rename().


<?php
$tmp = tempnam('/tmp', 'prefix_');
file_put_contents($tmp, json_encode($array, JSON_PRETTY_PRINT));
rename($tmp, 'data.json');
?>
  

Этот подход особенно полезен для конфигурационных файлов и кэша - исключает чтение частично записанных данных.

Как записать данные в нестандартные потоки, например, напрямую в буфер вывода или в удалённый файл по FTP?

PHP поддерживает обертки потоков (wrappers). Пример записи в stdout (браузер):


<?php
file_put_contents('php://output', "Строка для вывода\n");
?>
  

Запись по FTP:


<?php
$remote = 'ftp://user:pass@host/remote.txt';
file_put_contents($remote, 'Содержимое файла');
?>
  

Этот способ удобен для публикации файлов на удаленном сервере без сторонних библиотек.

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

Используйте потоковые фильтры через stream_filter_append().


<?php
$handle = fopen('output.txt', 'w');
stream_filter_append($handle, 'zlib.deflate', STREAM_FILTER_WRITE);
fwrite($handle, 'Текст для сжатия');
fclose($handle);
?>
  

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

Расширенные примеры записи данных

1. Запись многобайтового текста с явной установкой кодировки

Пример

<?php
$text = "Привет, мир! © 2025";
// Принудительно кодируем в UTF-8 (если исходная строка в Windows-1251)
$text = mb_convert_encoding($text, 'UTF-8', 'Windows-1251');
file_put_contents('example_utf8.txt', $text);
?>
Файл example_utf8.txt содержит корректный UTF-8 текст. Без преобразования символы могли отображаться как вопросительные знаки.

2. Атомарная запись с возвратом предыдущего значения

Пример

<?php
function atomicWrite($file, $data) {
    $tmp = tempnam(dirname($file), '.tmp');
    if (file_put_contents($tmp, $data) === false) {
        unlink($tmp);
        return false;
    }
    // Rename - атомарная операция на одной файловой системе
    if (rename($tmp, $file)) {
        return true;
    }
    unlink($tmp);
    return false;
}

$old = @file_get_contents('counter.txt') ?: 0;
$new = $old + 1;
if (atomicWrite('counter.txt', $new)) {
    echo "Счётчик увеличен: $new";
}
?>
Счётчик увеличен: 42

3. Запись CSV с управлением разделителями и экранированием

Пример

<?php
$data = [
    ['Имя', 'Возраст', 'Город'],
    ['Анна', 25, 'Москва'],
    ['Пётр', 30, '"Санкт-Петербург"'],
];
$handle = fopen('users.csv', 'w');
foreach ($data as $row) {
    fputcsv($handle, $row, ';', '"');
}
fclose($handle);
?>
Содержимое users.csv:
Имя;Возраст;Город
Анна;25;Москва
Пётр;30;""Санкт-Петербург""

4. Запись в буфер вывода с последующим захватом

Пример

<?php
ob_start();
echo "Строка 1\n";
echo "Строка 2\n";
$content = ob_get_clean();
file_put_contents('output.log', $content, FILE_APPEND);
?>
В файл output.log добавлено:
Строка 1
Строка 2

5. Запись через поток с пользовательским фильтром (преобразование регистра)

Пример

<?php
class UpperCaseFilter extends php_user_filter {
    public function filter($in, $out, &$consumed, $closing) {
        while ($bucket = stream_bucket_make_writeable($in)) {
            $bucket->data = strtoupper($bucket->data);
            $consumed += $bucket->datalen;
            stream_bucket_append($out, $bucket);
        }
        return PSFS_PASS_ON;
    }
}
stream_filter_register('upper', UpperCaseFilter::class);

$handle = fopen('uppercase.txt', 'w');
stream_filter_append($handle, 'upper', STREAM_FILTER_WRITE);
fwrite($handle, 'hello world');
fclose($handle);
?>
Содержимое uppercase.txt:
HELLO WORLD

Отправка содержимого PHP - comments

En
Put content php (php)