خروجی بافر یا همان Output Buffering در php یکی از قابلیت های مهم برای کنترل خروجی اسکریپت php است . کنترل خروجی بر اساس زمان ارسال خروجی به مرورگر , ترتیب خروجی ها و غیره .. . انجام می شود .
به طور پیش فرض , تمام داده های مشخص شده در در دستورات چاپی یا محتویات HTML در اسکریپت PHP به طور تکه تکه به مرورگر ارسال می شود .
اما با استفاده از Output Buffering در php این داده ها در یک متغییر در حافظه بافر ذخیره خواهند شد و در نهایت به صورت یکجا یا همان یک تکه به مرورگر ارسال می شوند.
این قابلیت باعث جلوگیری از خطاهایی مثل header already sent در هنگام استفاده از توابعی مثل session_start()
که در مقاله آموزش کار با SESSION ها در php توضیح دادم می شود
Buffer یا بافر چیست ؟
بافر یک قسمت کوچک از حافظه برای ذخیره سازی موقت داده ها است .
پیکربندی Output Buffering در php
برای کنترل خروجی های یک اسکریپت php توسط بافر , نیاز داریم که Output Buffering را با استفاده از هرکدام از روش های زیر فعال کنیم .
- فعال کردن Output Buffering در php از طریق فایل php.ini
- فعال کردن Output Buffering از طریق توابع PHP
فعال کردن Output Buffering در php از طریق فایل php.ini
سه دستور پیکربندی زیر برای کنترل خروجی بافر استفاده می شوند
Output_buffering
– به صورت پیش فرض۰
است . نیاز داریم که با مقدار ON قابلیت Output Buffering را برای تمام اسکریپت های php فعال کنیمOutput_handler
– مقدار پیش فرض ,NULL
است .نیاز داریم که یک تابع از پیش ساخته مرجع برای ارسال خروجی به آن تابع مشخص کنیمImplicit_flush
– قرار دادن مقدارON
برای این دستور , باعث خواهد شد که اسکریپت php مقدار خروجی را به ازای هر دستور چاپی در php یکبار flush یا ارسال کند .
هر دو دستور اول در هرکدام از فایل های php.ini , .htacces , httpd.conf یا user.ini می تواند ست شود . و دستور پیکربندی آخر لیست بالا می تواند به هر طریق حتی با تابع ini_set()
ست شود .
فعال کردن Output Buffering از طریق توابع PHP
با ست کردن تنظیمات بالا , نتیجه بر روی تمام اسکریپت های php که بر روی سرور اجرا می شوند , اثر خواهد گذاشت . اما , فعال کردن Output Buffering در php بر روی یک فایل یا پروژه خاص بهتر از آن است که بخواهیم به صورت سراسری تنظیمات را اعمال کنیم .
بنابراین , php شامل توابعی برای کنترل Output Buffering است .این توابع با توضیحات کوتاه در زیر لیست شده اند .
- تابع
Ob_start()
– این تابع برای هر دیتای ذخیره شده یک بافر می سازد . این دیتا ها در صورت استفاده از دستور های print یا echo یا هر دستور چاپی به اسکریپت php پاس داده خواهند شد. - تابع
ob_flush()
,ob_end_flush()
– این توابع برای ارسال خروجی بافر برای نمایش در مروگر استفاده می شوند . تفاوت این توابع اینجاست که ,ob_flush()
بعد از ارسال بافر , مقدار بافر ذخیره شده را پاک نمی کند . ولی با یکبار اجرای ob_end_flush() , اگر نیاز داشتیم باید دوباره یک بافر جدید بسازیم . - تابع
ob_clean()
,ob_end_clean()
– این توابع برای پاک کردن داده های ذخیره شده در بافر استفاده می شوند .
مثال Output Buffering در php
با استفاده از توابع بالا قصد داریم یک مثال را با هم ببینیم.
1 2 3 4 5 6 7 8 9 10 11 12 |
<?php ob_start(); print "PHP Output Buffering: level-1<br/>"; ob_end_flush(); ob_start(); print "PHP Output Buffering: level-2<br/>"; ob_flush(); print "Store into recently created buffer on level-2<br/>"; ob_end_flush(); ?> |
در اسکریپت php بالا , در اولین خط با استفاده از تابع ob_start()
یک بافر ساختیم . و بعد , داده مشخص شده در تابع print
در بافر ذخیره شد . پس از آن , تابع ob_end_flush()
را به ترتیب برای ارسال و پاکسازی داده های بافر اجرا کردیم
بعد از آن , دوباره تابع ob_start()
را برای ساخت یک بافر با محتوای داده های دستور print
ساختیم . اما این دفعه به جای تابع ob_end_flush()
, تابع ob_flush()
را استفاده کردیم . بنابراین فقط مقدار بافر را برای نمایش ارسال کردیم ولی آن را از بافر حذف نکردیم . بنابراین , سومین دستور print داده ذخیره شده در بافر قبلی را نیز ذخیره می کند.
توابع کنترل Output Buffering در php
به جز توابع اولیه و پایه بالا , php چندین تابع دیگر برای این extension را نیز پشتیبانی می کند . این توابع برای پیاده سازی قابلیت های زیر کاربرد دارد .
- خواندن داده ها از خروجی بافر ها
- ساختن پشته (stack) برای خروجی بافر
- فشرده سازی داده ذخیره شده در بافر
- URL rewiting
حالا اجازه بدید دسته بندی های بالا را به ترتیب بررسی کنیم
خواندن داده ها از خروجی بافر ها
با استفاده از تابع Output Buffering در php , می توانیم اطلاعاتی مثل محتوای بافر , طول داده بافر شده و غیره .. را بدست آوریم.
ob_get_contents()
– این تابع محتوای خروجی بافر را برگشت می دهدob_get_length()
– این تابع طول داده ذخیره شده در بافر را return می کند.ob_get_level()
– برای دریافت سطح یا همان level خروجی بافر به کار می رود. وقتی تعداد بافرهای ما زیاد می شود این بافرها به سطح بندی می شوند که با این level ها می توانیم به راحتی به آنها دسترسی پیدا کنیم.ob_get_status()
– برای دریافت آرایه از وضعیت بافر های سطح بالا یا تمام بافر ها با هر سطح استفاده می شود.ob_get_flush()
– این تابع کار هر دو تابعob_get_contents()
وob_end_flush()
را یکجا انجام میدهد !ob_get_clean()
– مشابه مورد بالا تاثیر هر دو تابعob_get_contents()
وob_end_flush()
همزمان نشان می دهد .
ساختن پشته (stack) برای خروجی بافر
می توانیم از مثال قبلی برای نحوه ارسال بافر ها به stack یا همان پشته استفاده کنیم .
1 2 3 4 5 6 7 8 9 |
<?php ob_start(); print "PHP Output Buffering: level-1<br/>"; ob_start(); print "PHP Output Buffering: level-2<br/>"; ob_end_flush(); ?> |
stack یکی از سه بخش تخصص یافته به یک برنامه در حافظه RAM است .ساختمان داده های stack به صورت LIFO عمل می کند.(Last in First Out)
این نوع تکنیک به این معناست که آخرین دیتای ورودی اول از همه خارج می شود .(روش LIFO و FIFO در حسابداری موجودی کالا نیز استفاده می شود.)
اگر یک بافر را با فراخوانی تابع ob_start()
بسازیم , پس از آن هر بافر ساخته شده به حافظه stack در بالای بافر قبلی قرار می گیرد . برای مثال :
فشرده سازی داده ذخیره شده در بافر
در php , خروجی دیتای بافر شده با مشخص کردن مقدار ob_gzhandler
به عنوان آرگومان تابع ob_start()
فشرده سازی خواهد شد . ob_gzhandler()
یک تابع از پیش ساخته (in-built) در php است که به عنوان تابع بازگشتی (callback) استفاده کردیم. با کمک این تابع بازگشتی , php داده فشرده شده را به مرورگر ارسال خواهد کرد.
طبیعتا کمی زمان برای فشرده کردن داده خروجی صرف خواهد شد , این زمان توسط مروگر برای دانلود دیتای فشرده شده جبران خواهد شد .
URL Rewriting
دو تابع برای کنترل Output Buffering در php هنگام کار با URL Rewritng وجود دارد که به صورت زیر است .
output_add_rewrite_var()
– اسم و مقدار را به صورت جفت در آرگومان می پذیرد .این اسم و مقادیر ها به صورت query string در یک url اضافه خواهند شد و سپس یک فیلد خالی برای این جفت ها افزوده می شود .output_reset_rewrite_varts()
– این تابع تمام مقادیر URL Rewriting را پاک می کند .
نکته :
Php به طور خودکار قادر است عملیات ارسال داده های خروجی بافر را تا زمان رسیدن به پایان خط php انجام دهد حتی اگر هیچ تابع output bufferingی مشخص نشده باشد !
با استفاده از Output Buffering در php , می توانید جلوی خطاهایی که مربوط به ارسال داده قبل از اجرای کدهای session یا setcookie()
می شود , را بگیرد.
بافرهای تو در تو (nested)
بافرها در PHP می توانند بصورت تو در تو (Nested) باشند. بنابراین زمانی که یکی از بافرها فعال است, یک ob_start()
دیگر یک بافر جدید را فعال می کند.
بنابراین هر دو تابع ob_end_flush()
و ob_flush()
در واقع هیچ بافری را به خروجی (output) ارسال نمی کنند, اما به بافر والد (parent) بله. و همچنین فقط زمانی هیچ بافر والدی نداریم که محتویات به مرورگر ارسال شده باشد.
بنابراین بسیار مهم است که عملیات بافر را غیرفعال کنیم حتی اگر یک استثنا (exception) رخ دهد.
1 2 3 4 5 6 |
ob_start(); try { $foo->render(); } finally { //finally exists since PHP 5.5 ob_end_clean(); // or ob_end_flush() } |
مزایای استفاده از Output Buffering در php این است که
- چونکه تمام اطلاعات و مقادیر html به جای اینکه به صورت تکه تکه به مروگر ارسال شود , در یک متغیر ذخیره و به صورت یکجا ارسال می شود که در نهایت باعث افزایش سرعت درخواست ها و لود صفحات وب می شود .
- باعت جلوگیری از خطا های header already sent که مربوط به استفاده از سیشن و کوکی ها است می شود .
سعی کردم یکبار برای همیشه و به طور کامل مقاله ای بنویسیم که تمام مباحث Output Buffering در php را پوشش دهم و احساس می کنم این هدف هم نایل شد .
هر گونه مشکلی در پیاده سازی کدها داشتید ، از قسمت نظرات اقدام کنید . سریعا ، پاسخگوی سوالات شما هستیم .
موفق و پیروز باشید.
بسیار عالی و قابل فهم توضیح دادین خیلی ممنون
اقای شفیعی کد من تاثیر داره در سرعت ؟ اگر ایراد داره تصحیحش میکنید واسم؟ یا اشکالشو واسم بیان میکنید؟ ایا در تابع باید استفاده کنم؟
function sanitize (string $val) : string {
return trim(strtoupper($val));
}
function setName (string $fname , string $lname) : string|array {
ob_start();
$result = array_map(‘sanitize’ , [$fname , $lname]);
ob_end_clean();
return $result;
}
var_dump(setName(‘komeil’ , ‘ebrahimi’));
سلام. نه کد خیلی هم مناسب و اوکیه و مشکلی نداره
سلام خسته نباشید ممنون از مقاله واقعا کاربردی تون . من تکه کدی که در صفحه مقاله (ساخت سیستم کش در php) گذاشتید به طور کامل در سورس سایتم قرار دادم اما این خطا را به من داده :
Warning: mkdir(): No such file or directory in /footer.php on line 103
Warning: fopen(): Filename cannot be empty in /footer.php on line 106
Warning: fwrite() expects parameter 1 to be resource, bool given in /footer.php on line 107
Warning: fclose() expects parameter 1 to be resource, bool given in /footer.php
میشه لطف کنید راهنمایی کنید چرا این وارنیگ ها رو به من داده .
یک مورد دیگه داخل هاست های معمولی هم میتونن از این قطعه کد استفاده کنند .شرمنده میکنید اگر دوباره کمکم کنید.
سلام. خوشحالیم که مفید واقع شده.
این خطاها به این معنیه که آدرس دهی بدرستی انجام نشده و فایل و فولدر رو نمیتونه بخونه
بله در هاست معمولی هم میشه استفاده کرد
سلام وقت بخیر استاد شفیعی من از داخل صفحه (ساخت سیستم کش در php) یک سوالی از خدمتتون داشتم شما زحمت کشیدید در قسمت سورس کد (cache_ext = .html$) قرار دادید سوال من اینجاست مگر ما نمیخوایم کل فایل php کش شود و این کد رو بالای header.php و پایین footer.php قرار نمی دیم پس چرا ما از html. در قسمت cache_ext$ استفاده میکنیم من فکر میکردم باید از php. در قسمت cache_ext$ استفاده میکردیم البته شاید سوال من خنده دار به نظر برسه چون من یک تازه کار هستم . لطف میکنید اگر راهنماییم کنید مننون از کمکی که میکنید.
سلام. ممنون.
نه هدف ما کش کردن خروجی ای هست که توسط کد php تولید میشه نه خود کدهای php
سلام خسته نباشید ممنون از مطالبی که گذاشتید که تقریبا توی سایت های دیگه نیستش . یک سوالی از خدمتتون داشتم توی مقاله ساخت سیستم کش در php شما زحمت کشید نحوه استفاده از بافر درphp توضیح دادید و کد های اماده اون رو هم قرار دادید من طبق گفته شما کدها رو قرار دادم ولی وقتی از مرورگر chrome لایت هوس میگیرم میبینم perforemance من اصلا فرقی نکره بهتر هم نشده میشه راهنمایی کنید چرا تغییر نمیکنه فکر کنم output buffering باید روی performance تاثیر بزاره درسته لطف کنید کمکم کنید تشکر از شما.
سلام ممنون.
بار اول که فرقی نمیکنه چون هنوز کش نکرده. بعد بار اول که فایل کش شما ساخته شد دیگه اون صفحه خیلی سریعتر لود میشه. البته در اسکریپت بسیار کوچیک و حتی در لوکال این تفاوت احساس نمیشه و باید در هاست واقعی این موارد رو تست کنید چون بین شما و سرور یه delay و access time وجود داره و در صورت کش شدن این زمان به حداقل خود میرسه
اقای شفیعی ممنون از اینکه برای من وقت با ارزشتون رو گزاشتید دو تا کوتاه دیگه هم از خدمت شما داشتم سوالم این بود که میتونیم از Output Buffering کنار رول های کش کردن در فایل .htaccess به کار برد و ایا این که Output Buffering به تنهایی باعث افزایش سرعت میشود .یک سوال دیگه هم از خدمتتون داشتم من سایت خودم با لاراول نوشته شده اما از همین کد میشه برای کسایی که سایت هاشون ووردپرسی هست هم استفاده شود. ممنون از راهنماییتون.
خواهیش میکنم.
بله میتونید در کنار htaccess ازش استفاده کنید. خیر به تنهایی نه چون اگه سرور مجازی داشته باشید یه سری ابزارهای لینوکسی هم در کنار کد برنامه نویسی وجود دارند که به این کار کمک می کنند.
بله در وردپرس هم میتونید استفاده کنید البته افزونه هاشو اگه نصب کنید بهتره چون بهینه تر هستند و امکانات بیشتری در اختیارتون میزارن مثل wp rocket
سلام قالب وودمارت سرعت افزودن به سبد خرید موقه ای که به اجکس میره ۵ ثانیه است دوتاکد براش ست کردم که در js و در functions.php سرعت به ۲ ثانیه میرسه ولی دکمه های – + که توی بنر اجکس هست کار میکنه در خود اجکس ولی در هدر روی ایکون سبد خرید دیگه شمارهاش کم و زیاد نمیشه میشه ببیند مشکل این کد از چیه
Try to add the following PHP code snippet to the child theme functions.php file to fix this
add_filter( ‘woocommerce_add_to_cart_fragments’, ‘woodmart_cart_data’, 30 );
function woodmart_cart_data( $array ) {
ob_start();
woodmart_cart_count();
$count = ob_get_clean();
ob_start();
woodmart_cart_subtotal();
$subtotal = ob_get_clean();
$array[‘span.wd-cart-number_wd’] = $count;
$array[‘span.wd-cart-subtotal_wd’] = $subtotal;
return $array;
}
and put the following code to the Custom JS in Theme Settings
jQuery( document.body ).on( ‘added_to_cart removed_from_cart’, function( e, fragments ){
if ( fragments ) {
jQuery.each( fragments, function( key, value ) {
jQuery( key.replace(‘_wd’,”) ).replaceWith( value );
});
jQuery( document.body ).trigger( ‘wc_fragments_loaded’ );
}
});
درود بر شما من به یک مشکلی بر خوردم من توسط کوعری استرینگ ریدایرکت میکنم تو یکی از فایل هام مشکل headers already sent by دارم جاییکه وقتی روی دکمه دیلیت کلیک میشه کاربر به لینک dashbord.php?folder=menu&file=delete وارد میشود عملایت حذف هم انجام میشود در اخر در همان فایل delete.php ریدایرکت میشود به همان صفحه dashbord اما با ارور مواجه میشود از متد ob_start(); نیز استفاده کردم به طور کل مسیر یابی دچار مشکل میوشد یعنی کوعری استرینگ را اصلا شناسایی نمیکند not found میزند نمیدانم کجای کار ایراد دارد تنظیمات wamp server هم روی دیفالت است
سلام اقای شفیعی ممنون مرجع اولیه ام همیشه سایت شماست
بنده توی ربات تلگرام در اول فایل Php اینها رو گذاشتم ایا باعث کندی سرعت ربات میشه؟
flush();
ob_start();
ob_implicit_flush(1);
header(‘Content-Type: text/html; charset=utf-8’);
ممنون از شما
سلام. لطف دارید.
نه مشکلی پیش نمیاد و روی سرعت تاثیری نداره و به نظر نیاز هم نیست همان header رو ست کنید کافیه
موفق باشید.
سلام آقای شفیعی
من با ارور warning: Cannot modify header information – headers already sent by (output started at
مواجه میشم تو اسکریپت، چطوری این خطا رو رفع کنم، لینک فایل رو اینجا قرار میدم ببینید. لاین خطا ها هم تو زیر مشخص کردم
template.php:144
routing.php on line 141
http://s8.picofile.com/file/8358482968/help.zip.html
یاشا
سلام. در خط اول یعنی بعد از تگ php بلافاصله تابع ob_start() را استفاده کنید این خطا حل میشه.
موفق باشید.
سلام، آقای شفیعی متوجه نشدم !
سلام. بسیار سادست. اول فایل های php خودتون قبل از همه کدها این تابع ob_start() رو کپی پیست کنید همین.
اول فایل های php شما اگر php?> است باید بعد کپی تابع بالا به اینصورت باشه
php?>
ob_start()
بعد اینکارخطای موردنطر رفع میشه.
موفق باشید.
ممنون، با این تابع مشکل ام تا حدودی حل شد، و دیگه ارور بالا نمایش داده نمیشه.من از اسکریپت درگاه واسط استفاده میکنم، وقتی لینک پرداخت ایجاد میکنم و آدرس پرداخت ناموفق رو خالی میزارم … وارد صفحه پرداخت میشم و از پرداخت انصراف میدم به جای اینکه صفحه پیشفرض سایت رو نشون بده و بگه پرداخت انجام نشد یا شخص از خرید انصراف داده، لینک پرداخت موفق رو نشون میده، و در ضمن اگه آدرس پرداخت ناموفق رو خالی نذارم و آدرسی وارد کنم به اون صفحه هدایت میشه و لینک دانلود موفق رو نشون نمیده برعکس بالا، ممنون میشم در این باره منو راهنمایی کنید (البته اگه منظورمو درست رسونده باشم)
سلام
خب چه دلیلی داره صفحه پرداخت نا موفق رو ست نمی کنید. اگر میخوایین به صفحه اصلی برگرده که آدرس دامنه خودتون رو وارد کنید.
همچنین اگر این ایراد هست مطمینا یا کد وضعیت رو درست اعتبارسنجی نمی کنید یا ایراد از خود سیستم درگاه واسط است که باید اطلاع بدید حل کنند.
سلام، یاشا آقای شفیعی
ممنون که جواب کاربران رو دقیق و شفاف میدید، و همینطور مرسی بابت سایت خوبتون
سلام. خواهش می کنم. خوشحالیم که مفید واقع شده.
موفق و پیروز باشید.
با سلام
بر اساس تجربه ،یک سورس مشخص روی هاست های اشتراکی مختلف. ممکنه خطای
already headers sent رو بدهند. یعنی یک هاست خطا میده اما هاست دیگه خطا نمیده.
در حالی که سورس عوض نشده. تنها نکته هم استفاده از دستور header برای ریدایرکت هست.
مانند header(“Location:index.php?go=listmaker”);
آیا استفاده از این ob_start() در تمام صفحاتی که از header استفاده می کنند بدون flush یا End مشکلی بوجود نمیاره؟
سلام. این خطا بستگی به این دارد در هاست مورد نظر فعال شده باشه یا خیر که بهتره در فایل موردنظر با کد php این موارد رو پیش فرض فعال کنید
چون هاست اشتراکی هست ، آیا استفاده از این ob_start() در تمام صفحاتی که از header استفاده می کنند بدون flush یا End مشکلی بوجود نمیاره؟
خیر مشکلی پیش نمیاد
سلام
ممنون از مقاله بسیار خوب شما.
یک سوال وقتی ob_stsrt در ابتدای فایل php قرار بگیره که این فایل شامل دستورات شرطی و فراخوانی های get هست که هر کدام دارای فرم ها و… هستند مشکلی بابت ذخیره سازی پیش نمی آید؟
ممنون
سلام. خوشحالیم که مفید واقع شده.
نه مشکلی پیش نمیاره
البته این هم یادم رفت بگم که برای افزونه های وردپرس این کار پیشنهاد می شود یا خیر.
باتشکر
خود وردپرس توابعی برای کش کردن داره که از اونا استفاده کنید
https://developer.wordpress.org/reference/functions/wp_cache_set
سلام بر شما. چجوری میشه برای این تابع اوت پوت بافر ایجاد کرد؟
$post_link=get_field(‘postlink’);
function urlsite ($post_link){
$reg_exUrl = “/(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+/”;
preg_match($reg_exUrl, $post_link, $url);
return $url[0];
}
$res= urlsite ($post_link);
سلام. زمانی میتونید از output buffering استفاده کنید که در خروجی یه چیزی چاپ بشه.
اینجا شما
return
کردید که بعد از چاپ کردن مقدارها باید از$res= ob_get_clean();
استفاده کنید. البته قبل از هر چیزی باید مطمین باشید انجینش روشن باشه (ob_start()
)یک مثال سادشو هنگام نمایش محتوای پست
the_content
ببینید :موفق باشید.
کامل تر از این توی منابع فارسی پیدا نکردم. دستتون درد نکنه. مثل همیشه عالی بود.
سلام. خوشحالم که مفید واقع شده. موفق و پیروز باشید.
سلام حسین جان
اگر لطف کنید و در مورد بافرهای تودرتو یا nested بنویسید ممنون میشم من هرقدر در موردش مطالب انگلیسی میخوانم متوجه نمیشم چی میگن!!!
سلام .
بله حتما این موضوع به عنوان ویرایش به مقاله اضافه و از طریق ایمیل به شما اطلاع داده می شود .
موفق و پیروز باشید