ویژگی تصویر

متد ob_flush() در PHP

  /  PHP   /  متد ob_flush() در PHP
بنر تبلیغاتی الف
آموزش PHP

متد ob_flush() در PHP نقش مهمی در مدیریت بافر خروجی (output buffering) دارد. این تابع باعث می‌شود محتوای موجود در بافر خروجی فعلی به لایهٔ بالاتر (مثلاً webserver یا مرورگر) ارسال شود، اما به‌تنهایی تضمین‌کنندهٔ ارسال فوری به کاربر نیست. در این مقاله به مفهوم، کاربردها، محدودیت‌ها و نمونه‌های عملی ob_flush() می‌پردازیم و تفاوت آن با توابع مشابه را توضیح می‌دهیم.

چرا بافر خروجی وجود دارد؟

  • بهینه‌سازی شبکه و کاهش تعداد بسته‌ها
  • اجباری برای ساختن هدرهای HTTP قبل از محتوای اصلی
  • قابلیت بازگرداندن یا تغییر محتوا پیش از ارسال (مثلاً برای فشرده‌سازی یا تغییر محتوا)

تعریف ساده ob_flush()

ob_flush() محتوای بافر خروجی فعلی که PHP مدیریت می‌کند را تخلیه می‌کند و آن را به لایهٔ بعدی می‌فرستد. معمولاً پس از ob_flush() باید تابع flush() هم فراخوانی شود تا PHP محتوای خود را به سیستم عامل و در نهایت به وب‌سرور و مرورگر ارسال کند.

نحوۀ استفاده پایه

<?php
ob_start();
echo "مرحله 1...n";
ob_flush();
flush();
sleep(2);
echo "مرحله 2...n";
ob_flush();
flush();
ob_end_flush();
?>

در این مثال: ابتدا با ob_start() بافر جدیدی باز می‌کنیم. سپس بعد از هر echo با ob_flush() و flush() درخواست می‌کنیم محتوای فعلی فوراً ارسال شود. اما توجه کنید که موفقیت این ارسال به پیکربندی سرور، فشرده‌سازی و مرورگر نیز وابسته است.

نکات و محدودیت‌های عملی

  • فشرده‌سازی (zlib/ gzip): اگر zlib.output_compression فعال باشد، ob_flush() به‌تنهایی محتوای فشرده‌شده را آزاد نخواهد کرد. باید فشرده‌سازی غیرفعال یا از flush() پس از پایان بافر فشرده‌سازی استفاده کنید.
  • PHP-FPM و FastCGI: در تنظیمات PHP-FPM یا سرورهایی مانند Nginx ممکن است buffering سمت سرور وجود داشته باشد که مانع نمایش تدریجی شود. در این شرایط، fastcgi_buffering off; یا متد fastcgi_finish_request() مفید است.
  • مرورگرها: بعضی مرورگرها تا پر شدن یک حد مشخص (مثلاً 1024 بایت) یا دریافت تگ‌های HTML خاص، محتوا را رندر نمی‌کنند.
  • چندین سطح بافر: اگر چندین بافر تو در تو باز شده باشد، ob_flush() فقط سطح فعلی را تخلیه می‌کند. برای تخلیهٔ همه بافرها باید از حلقه استفاده کنید.

تخلیه کامل همهٔ بافرها

<?php
// Flush all output buffers
while (ob_get_level() > 0) {
    ob_end_flush();
}
flush();
?>

این کد تمامی بافرهای فعال را با ob_end_flush() می‌بندد و سپس با flush() محتوای باقی‌مانده را به سیستم ارسال می‌کند. این روش زمانی کاربرد دارد که نیاز به ارسال فوری همهٔ محتوا داریم.

مثال کاربردی: نمایش پیشرفت در طول اجرای اسکریپت

<?php
ob_start();
echo str_repeat(' ', 1024); // پر کردن بافر مرورگر برای شروع رندر
echo "<div id='progress'>0%</div>";
ob_flush(); flush();

for ($i = 1; $i <= 100; $i++) {
    echo "<script>document.getElementById('progress').innerText = '{$i}%';</script>";
    ob_flush(); flush();
    usleep(50000); // شبیه‌سازی عملیات طولانی
}

ob_end_flush();
?>

در این مثال ابتدا با repeat(‘ ‘, 1024) مقدار فضای کافی به مرورگر می‌دهیم تا رندر را آغاز کند. سپس در حلقه به‌صورت متوالی با استفاده از JavaScript و ob_flush() محتوای به‌روز رسانی شده را ارسال می‌کنیم تا نوار پیشرفت در مرورگر دیده شود. توجه کنید که این روش در برخی سرورها بدون تنظیمات اضافی کار نخواهد کرد.

Server-Sent Events (SSE) با ob_flush()

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');

for ($i = 0; $i < 5; $i++) {
    echo "data: Message {$i}nn";
    ob_flush();
    flush();
    sleep(1);
}
?>

برای SSE باید هدر مناسب را بفرستید و پس از هر پیام ob_flush() و flush() را فراخوانی کنید تا پیام‌ها بلافاصله به کلاینت برسند. باز هم هشدار دربارهٔ buffering سمت سرور وجود دارد که ممکن است نیاز به پیکربندی داشته باشد.

مقایسهٔ توابع مرتبط

تابعکارکرد
ob_start()شروع یک بافر خروجی جدید
ob_flush()تخلیهٔ محتوای بافر فعلی به لایه بالاتر
flush()درخواست ارسال داده‌ها از PHP به سیستم/وب‌سرور
ob_end_flush()بستن بافر و شبیه‌ساز ob_flush()
fastcgi_finish_request()پایان دادن به پاسخ و ادامهٔ اجرای اسکریپت بدون نگه‌داشتن کانکشن

نکات حرفه‌ای و بهینه‌سازی

  • اگر از gzip استفاده می‌کنید و نیاز به فلاش فوری دارید، بهتر است در زمان ارسال لحظه‌ای فشرده‌سازی را غیرفعال کنید یا از chunked encoding استفاده نمایید.
  • برای اجرای پس‌زمینه پس از ارسال پاسخ به کاربر، از fastcgi_finish_request() استفاده کنید تا کارها بدون گرفتن کانکشن ادامه پیدا کنند.
  • برای ارسال قطعات بزرگ (مثل دانلود فایل‌ها) از readfile() همراه با flush() در حلقه‌های مناسب استفاده کنید تا مصرف حافظه کاهش یابد.
  • در محیط CLI نیازی به ob_flush برای نمایش بلافاصله در ترمینال معمولاً نیست، اما در اسکریپت‌هایی که روی وب اجرا می‌شوند، ترکیب ob_flush() و flush() رایج است.

جمع‌بندی

ob_flush() ابزاری قدرتمند برای کنترل خروجی PHP است اما به‌تنهایی معجزه نمی‌کند. برای دریافت رفتار مورد نظر باید به مجموعه‌ای از عوامل مانند لایه‌های بافر سمت سرور، فشرده‌سازی، تنظیمات مرورگر و ترتیب فراخوانی توابع توجه کنید. در کاربردهای مانند نمایش پیشرفت، SSE یا ارسال chunked داده، ob_flush() همراه با flush() و تنظیمات سرور می‌تواند تجربهٔ بهتری برای کاربر فراهم کند.

آیا این مطلب برای شما مفید بود ؟

خیر
بله
موضوعات شما در انجمن: