Flush: примеры (PHP)
flush: voidОписание функции flush
Функция flush() принудительно отправляет все буферизованные данные на вывод клиенту (в браузер или другой вывод). Её основная задача — преодолеть буферизацию на разных уровнях: уровне PHP, веб-сервера (например, Nginx, Apache) или прокси.
Использование актуально в сценариях, где требуется передавать данные клиенту по мере их готовности, а не одним блоком в конце выполнения скрипта. Типичные случаи применения:
- Отправка данных о долгих процессах (прогресс-бар).
- Потоковая передача больших файлов или данных.
- Создание эффекта «живого» обновления страницы (long-polling, Server-Sent Events).
- Вывод логов выполнения прямо в браузер.
Функция flush() не принимает никаких аргументов. Синтаксис вызова прост: flush(): void.
Короткие примеры использования
Поэтапный вывод чисел с задержкой.
<?php
if (ob_get_level()) ob_end_clean(); // Отключаем буферизацию вывода
header('Content-Type: text/html; charset=utf-8');
for ($i = 1; $i <= 5; $i++) {
echo "Шаг: $i<br>";
flush(); // Отправляем данные клиенту
sleep(1); // Задержка в 1 секунду
}
?>Шаг: 1 (через 1 секунду) Шаг: 2 (через 1 секунду) Шаг: 3 ... и так далее.
<?php
ob_start(); // Включаем буферизацию вывода
?>
<p>Этот текст буферизирован.</p>
<?php
flush(); // Эта попытка ничего не отправит, так как буфер не очищен
ob_flush(); // Очищаем буфер вывода
flush(); // Теперь отправляем данные клиенту
?>Этот текст буферизирован.
Похожие функции в PHP
В PHP существует несколько функций для работы с буферизацией вывода, которые могут использоваться вместе с flush() или вместо неё в зависимости от задачи.
Отправляет содержимое текущего буфера вывода (если он активен) и отключает его. В отличие от flush(), работает только с буфером PHP, а не с системными буферами. Часто используется в паре: ob_flush() очищает буфер PHP, а flush() пытается отправить данные далее.
Отправляет содержимое текущего буфера вывода и отключает буферизацию. Это комбинация ob_flush() и ob_end_clean().
Включает неявную очистку буфера. После вызова этой функции каждый оператор вывода (например, echo) автоматически выполняет очистку буфера, как если бы после него вызывался flush(). Удобно для скриптов, где данные нужно отправлять непрерывно.
<?php
ob_implicit_flush(true);
for ($i = 0; $i < 5; $i++) {
echo $i . "<br>";
sleep(1); // Данные будут отправляться после каждого echo
}
?>Типичные ошибки
Самая распространённая проблема — flush() не работает из-за буферизации на уровне веб-сервера (например, mod_gzip в Apache) или прокси-сервера. Решение часто требует настройки сервера.
<?php
// Этот код может не работать из-за буферизации сервера
for ($i = 0; $i < 5; $i++) {
echo "$i\n";
flush();
sleep(1);
}
?>(Все данные выводятся разом через 5 секунд, а не по мере поступления)
Попытка вывести данные после отправки заголовков, запрещающих вывод.
<?php
header('Content-Type: text/plain');
flush(); // Отправляем заголовки
// ... долгий процесс
sleep(5);
echo "Данные"; // Этот вывод может не отобразиться, если соединение прервано
?>Забывают отключить или очистить буфер PHP (ob_start()), из-за чего flush() не отправляет данные.
<?php
ob_start();
echo "Начало.\n";
flush(); // Данные остаются в буфере PHP, отправки не происходит
ob_end_flush(); // Только теперь данные уйдут
?>Изменения в последних версиях PHP
В PHP 8 функция flush() не претерпела изменений в своём поведении или синтаксисе. Она остаётся совместимой с предыдущими основными версиями PHP (7.x, 5.x). Основные принципы работы с буферизацией вывода сохраняются.
Изменения касаются в основном внутренней реализации и улучшения производительности ядра PHP, но не затрагивают API этой функции. Разработчикам, обновляющим код с PHP 5 на 7 или 8, не требуется вносить правки, связанные с использованием flush().
Расширенные примеры использования
Имитация отправки большого файла с выводом процентов выполнения.
<?php
header('Content-Type: text/html; charset=utf-8');
ob_implicit_flush(true);
@ini_set('zlib.output_compression', 0);
@ini_set('implicit_flush', 1);
$fileSize = 1024 * 1024 * 10; // 10 MB
$chunkSize = 1024 * 64; // 64 KB
$totalChunks = ceil($fileSize / $chunkSize);
for ($i = 0; $i < $totalChunks; $i++) {
// Имитация чтения и отправки чанка данных
echo str_repeat('.', $chunkSize / 1024); // Упрощённая имитация данных
$progress = round(($i + 1) / $totalChunks * 100);
echo "<script>document.getElementById('progress').innerHTML = '$progress%';</script>";
flush();
usleep(10000); // Небольшая задержка для наглядности
}
?>
<div id="progress">0%</div>Пример простого long-polling механизма для получения уведомлений о новых событиях.
<?php
// server.php
header('Content-Type: text/plain; charset=utf-8');
set_time_limit(0); // Отключаем ограничение времени выполнения
$lastCheck = $_GET['timestamp'] ?? time();
$maxWait = 30; // Максимальное время ожидания (секунд)
$startTime = time();
while (time() - $startTime < $maxWait) {
// Проверяем наличие новых событий (например, в БД или файле)
$hasNewEvents = checkForNewEvents($lastCheck);
if ($hasNewEvents) {
echo json_encode(['event' => 'Новое уведомление', 'timestamp' => time()]);
flush();
break;
}
sleep(1); // Пауза между проверками
}
if (!connection_aborted()) {
echo json_encode(['event' => 'timeout']);
flush();
}
function checkForNewEvents($since) {
// Логика проверки новых событий
return rand(0, 10) > 8; // Случайная имитация события
}
?>Передача данных для обновления графика на веб-странице.
<?php
// stream.php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no'); // Отключение буферизации для Nginx
ob_end_clean(); // Очистка всех буферов
while (true) {
$data = ['time' => date('H:i:s'), 'value' => rand(1, 100)];
echo "data: " . json_encode($data) . "\n\n";
flush();
if (connection_aborted()) break;
sleep(2); // Отправка данных каждые 2 секунды
}
?><!-- client.html -->
<script>
const evtSource = new EventSource("stream.php");
evtSource.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log(`Время: ${data.time}, Значение: ${data.value}`);
// Логика обновления графика...
};
</script>Альтернативы в других языках
Flush в Python
В веб-фреймворках Python, таких как Flask или Django, для потоковой передачи используется генератор или специальный потоковый ответ. Прямого аналога flush() в чистом виде нет.
# Пример с Flask и генератором
from flask import Flask, Response
import time
app = Flask(__name__)
@app.route('/stream')
def stream_data():
def generate():
for i in range(5):
yield f"Данные: {i}\n"
time.sleep(1)
return Response(generate(), content_type='text/plain')
if __name__ == '__main__':
app.run()В Node.js с фреймворком Express для потоковой передачи используется объект response. Метод write() позволяет отправлять части ответа.
const express = require('express');
const app = express();
app.get('/stream', (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/plain; charset=utf-8',
'Transfer-Encoding': 'chunked'
});
let counter = 0;
const interval = setInterval(() => {
res.write(`Чанк данных: ${counter}\n`);
counter++;
if (counter >= 5) {
clearInterval(interval);
res.end();
}
}, 1000);
});
app.listen(3000);Flush в MySQL
Прямого аналога для отправки данных клиенту в MySQL не существует. Однако для длительных операций можно использовать пошаговую выборку данных.