PDA

View Full Version : آموزش: بافر کردن خروجی و بررسی خطای Cannot modify header



Keramatifar
یک شنبه 28 آذر 1389, 18:39 عصر
بافر کردن خروجي و خطاي Cannot modify header information

بافر کردن خروجي (Output buffering) چيست؟
اگر خروجي کد، html يا جاوا اسکريپت باشد يا از echo و print در php استفاده کرده باشيد، اين اطلاعات به مرورگر فرستاده مي شود. با استفاده از بافر کردن خروجي، اين اطلاعات ابتدا بر روي سرور ذخيره می شود تا زماني که يا اجراي اسکريپت پايان يابد يا اين که شما دستوراتی را روي محتواي بافر اجرا نماييد.
در PHP، خروجي به صورت پيش فرض بافر نمي شود، محتويات خروجي همان طور که ايجاد مي شوند، به مرورگر فرستاده مي شوند. توابع به کار گرفته شده براي بافر کردن خروجي، خروجي را بافر نموده و ضمن ارسال، امکان کنترل بر روي آن را نيز مي دهند مانند مجوز فشرده سازي محتوا. همچنين اين توابع، اجازه ايجاد ترکيبي از هدرها (headers) و محتوا را با هم مي دهند بدون اين که خطاي "Cannot modify header information - headers already sent" صادر شود.

خطاي Cannot modify header information - headers already sent
خطاي بالا زماني نمايش داده مي شود که محتوا قبل از ارسال هدر (مانند تنظيم يک کوکي، ارسال به صفحه جديد و ...) به خروجي فرستاده شود. در نتيجه، هدرها بايد قبل از ارسال هر گونه خروجي، تنظيم شوند در غير اين صورت، خطاي بالا نمايش داده مي شود.
(کوکي به عنوان يک HTTP header توسط سرور وب به مرورگر وب فرستاده مي شود و سپس بدون تغيير توسط مرورگر با ارسال هر درخواست به سمت سرور ارسال مي شود)
بهترين روش اين است که مطمئن شويم همه هدرها قبل از ارسال خروجي ها، تنظيم شده اند. گاهي اوقات ساده تر اين است که از بافر کردن خروجي استفاده کنيم که موجب حذف اين پيغام خطا خواهد شد. هنگامي که بافر کردن خروجي فعال باشد، همه هدرها فرستاده خواهند شد در حالي که محتوا يا در پايان اسکريپت ارسال خواهد شد يا هنگامي که صريحا توسط اسکريپت با دستوراتي اجرا مي شود.

چگونه بافر خروجي را فعال نماييم
براي فعال نمودن بافر خروجي، ob_start(); را در ابتداي اسکريپت php اضافه مي نماييم قبل از اين که چيزي به مرورگر ارسال شود حتي يک فاصله در غير اين صورت خطا توليد مي شود. براي فعال نمودن فشرده سازي gzip نيز، کد زیر را نیز اضافه مي نماييم.


ob_start(“ob_gzhandler”);

گرفتن محتويات بافر خروجي
امکان اين که محتويات بافر را به عنوان يک رشته بگيريم وجود دارد لذا مي توانيم آن را در يک فايل مانند فايل log ذخيره يا به عنوان يک ايميل بفرستيم. همچنين در php توابعي براي چاپ خروجي به جاي در نظر گرفتن يک رشته نيز وجود دارد.
$content = ob_get_content();: گرفتن محتوا و بازگرداندن آن در متغير $content را نشان مي دهد يعني هر آن چه که شما چاپ نموده يا به صورت ديگري به سمت مرورگر فرستاده ايد که بين اين دستور و دستور ob_start() وجود دارد، در متغير $content ذخيره خواهد شد.

$content = ob_get_clean();

گرفتن محتوا و سپس خالي نمودن بافر را نشان مي دهد

ارسال محتويات بافر خروجي به مرورگر (flush)
بافر مي تواند در هر زماني به طور صريح با استفاده از توابع زير خالي و به سمت مرورگر ارسال شود.
تابع ob_flush()، محتواي بافر را براي مرورگر، ارسال و آن را خالي مي نمايد. در ضمن عمل بافرينگ در صورت خروجي بيشتر، ادامه مي يابد.
تابع ob_end_flush()، مانند تابع بالا عمل نموده با اين تفاوت که عمل بافرينگ را ادامه نمي دهد. در صورت وجود محتواي بيشتر، مستقيما به خروجي فرستاده مي شود.
تابع ob_get_flush(): مانند تابع ob_end_flush() عمل نموده محتوا را به صورت رشته بر مي گرداند.

خالي نمودن بافر خروجي
براي خالي نمودن محتويات جاري بافر خروجي از ob_end_clean() استفاده مي نماييم.
تابع ob_clean(): اگر بخواهيد هر آن چه در بافر خروجي ذخيره شده را خالي نموده تا براي مرورگر کلاينت ارسال نشود و يا يک صفحه جديد بسازيد، از اين تابع استفاده نماييد. اين تابع، مانند تابع ob_flush()، عمل بافرينگ را ادامه مي دهد.
تابع ob_end_clean(): مانند تابع ob_clean() عمل نموده با اين تفاوت که عمل بافرينگ را ادامه نمي دهد. يعني بافر را خالي و سپس بافرينگ را متوقف مي سازد.
مثال: تغيير مقدار يک قسمت از صفحه:

<?php

ob_start('changeValue');

?>

<html>

<body>

<p>Hello world!</p>

</body>

</html>

<?php

ob_end_flush();

function changeValue ($buffer)
{

$buffer = str_replace('Hello', 'Hi', $buffer);

return $buffer;

}

?>
منبع:
http://keramatifar.ir/ShowTopic.php?id=%2057

Mehrdad-p70
چهارشنبه 16 فروردین 1391, 07:56 صبح
سپاس به خاطر مطلب مفیدتون
در مورد تابع ob_start چندتا سوال دارم
اول اینکه پارامترهایی که میگیره به چه دردی می خوره البته http://ir2.php.net/manual/en/function.ob-start.php را خواندم ولی متوجه نشدم

دوم اینکه تو همون صفحه نوشته :


Warning
Some web servers (e.g. Apache) change the working directory of a script when calling the callback function. You can change it back by e.g. chdir(dirname($_SERVER['SCRIPT_FILENAME'])) in the callback function.

که من منظورش را نفهمیدم!!!
ممنون میشم توضیح بدید

MMSHFE
چهارشنبه 16 فروردین 1391, 12:08 عصر
دوست عزیز، این هشدار داره میگه برخی از سرورها (مثل Apache) موقع صدا زدن یک تابع به روش CallBack (همین روشی که اسم تابع رو برای ob_start موقع فراخوانی اون بصورت رشته میفرستیم)، مسیر کاری رو تغییر میدن (البته درصورتی که اون تابع توی فایلی باشه که اون فایل توی مسیری متفاوت با اسکریپت ما قرار داره و اون فایل رو include کردیم). شما میتونید دوباره مسیر رو با اجرای دستور زیر:
;((['chdir(dirname($_SERVER['SCRIPT_FILENAME
به مسیر اجرای اسکریپت برگردونید. برای اینکه بهتر متوجه بشین، بگذارین مثال بزنم. فرض کنید ما یک فایل functions.php داریم که توی مسیر inc/ ذخیره شده و تابع زیر رو توی اون تعریف کردیم:


function test($path) {
$result = '';
$result .= $path.':'.PHP_EOL;
$result .= '<pre>'.PHP_EOL;
$files = scandir($path);
foreach($files as $file) {
$result .= $file.PHP_EOL;
}
$result .= '</pre>'.PHP_EOL;
return $result;
}

خوب همونطور که میدونید، اگه ما فایل functions.php رو با include به هر اسکریپتی ضمیمه کنیم، میتونیم از تابع test استفاده کنیم. حالا فرض کنید ما این دستورات رو توی فایل index.php نوشتیم که توی مسیر / یعنی ریشه سایتمون قرار داره:


require_once 'inc/functions.php';
ob_start('test');
echo 'images';
ob_end_flush();

خوب انتظار داریم با این کار، فهرست فایلهای توی پوشه images/ یعنی پوشه images در فهرست ریشه سایت به نمایش در بیاد ولی به جای این کار، یک پیغام خطا میگیریم که میگه پوشه images پیدا نشد! علت این مسئله اینه که وقتی ما فایل test رو از داخل فایل functions.php به روش CallBack صدا زدیم، بخاطر اینکه فایل functions.php توی مسیر inc/ قرار داشته، مسیر کاری به inc/ تغییر میکنه و لذا دنبال پوشه images توی مسیر inc/ یعنی درحقیقت پوشه inc/images/ میگرده که وجود نداره. برای رفع این مشکل، باید تابع test رو اینطوری تغییر بدیم:


function test() {
chdir(dirname($_SERVER['SCRIPT_FILENAME']));
$result = '';
$result .= $path.':'.PHP_EOL;
$result .= '<pre>'.PHP_EOL;
$files = scandir($path);
foreach($files as $file) {
$result .= $file.PHP_EOL;
}
$result .= '</pre>'.PHP_EOL;
return $result;
}

تا قبل از اینکه دنبال پوشه images بگرده، اول مسیر کاری رو به مسیری که اسکریپت در حال اجرا (یعنی index.php) توش قرار داره تغییر بده و بعد بقیه تابع اجرا بشه.
امیدوارم خوب توضیح داده باشم.
-----
درمورد پارامترهای تابع ob_start هم باید بگم اولیش اسم یک تابع هست که باید قابل فراخوانی به روش CallBack باشه. فقط توابعی میتونن به این روش صدا زده بشن که یک پارامتر رشته ای بعنوان ورودی بگیرن و یک مقدار هم return کنن. البته توابع CallBack میتونن پارامتر دوم هم داشته باشن که بطور خودکار با یکی از مقادیر بیتی PHP_OUTPUT_HANDLER_START (شروع بافرینگ) یا PHP_OUTPUT_HANDLER_CONT (ادامه بافرینگ قبلی) یا PHP_OUTPUT_HANDLER_END (پایان بافرینگ) برحسب شرایط سرور و محل صدا زدن بافر خروجی، مقداردهی میشه. مثلاً با کمک این متغیر میتونید توی تابع یکسری کارها رو در اولین اجرای بافرینگ انجام بدین، یکسری کارها رو در ادامه بافرینگ (مثلاً بعد از فراخوانی ob_flush) و یکسری کارها رو هم در پایان بافرینگ (مثلاً موقع صدازدن ob_end_flush).
مقداری که این تابع return میکنه یا یک رشته هست که مشخص میکنه چی باید برای مرورگر ارسال بشه، یا false که باعث میشه محتوای اصلی بافر ارسال بشه.
پارامتر دوم ob_start اندازه بافر رو برحسب بایت مشخص میکنه و درنتیجه هرموقع محتوای بافر به اندازه مربوطه رسید، برای مرورگر ارسال میشه و بافر خالی میشه. اگه صفر بگذارین، یعنی نامحدود و فقط وقتی خودتون با کمک توابع مربوطه خواستین، محتوای بافر ارسال میشه. قبل از نسخه 5.4.0 زبان PHP هم مقدار 1 معادل 4096 بایت (4 کیلوبایت) بود که از نسخه 5.4.0 حذف شده و باید همون 4096 رو بگذارین.
پارامتر آخر هم اگه false باشه، محتوای بافر تا پایان اسکریپت خالی نمیشه. درنتیجه فراخوانی توابع flush و clean یک E_NOTICE (یادآوری) تولید میکنه که میگه محتوا خالی نشد! اگر هم این پارامتر رو نگذارین یا هر مقداری غیر از false داشته باشه، بافر به روش معمولی کار میکنه.
خروجی ob_start هم یا true هست یا false که مشخص میکنه PHP تونسته بافر خروجی رو فعال کنه یا نه؟
-----
موفق باشید.