یکی از بهترین لحظات برنامه نویسی زمانی است که راه حل های طولانی و خسته کننده جای خود را به راه حل های میانبر و کوتاه میدهند. اما برای یافتن چنین راه حل هایی نیاز به دانش و مهارتی بیش از اصول و مفاهیم اولیه زبان برنامه نویسی وجود دارد. همه برنامه نویسان با ایجاد توابع و فراخوانی آنها آشنا هستند، اما تنها تعدادی از آنها نوعی از توابع که به آنها تابع پس فراخوان (callback) گفته میشود را به خوبی میشناسند. این توابع ابزارهایی هستند که بسیاری از راه حل های پیچیده و دشوار را به کدهای ساده و چند خطی تبدیل میسازند.
یک تابع پس فراخوان درست همانند توابع دیگر، توسط برنامهنویس تعریف میشود. اما به طور مستقیم در برنامه فراخوانی نمیشود. طبق تعریف، تابع پس فراخوان تنها زمانی فراخوانی میشود که یک اتفاق معین در حین اجرای برنامه روی بدهد. این اتفاق ممکن است از سوی توابع خود PHP روی دهد. برای مثال، در هنگام تجزیه کدهای XML، هر بار که تابع تجزیه کننده به یک تگ برسد تابع پس فراخوان تعریف شده برای آن را اجرا مینماید. در ادامه چند نمونه از کاربردهای توابع پس فراخوان را با یکدیگر میبینیم:
مرتب کردن آرایه چند بعدی
فرض کنید کلاس درسی دارید و دانش آموزان کلاس و نمرات آنها را در یک آرایه به شکل زیر ذخیره کردهاید:
اکنون میخواهید این آرایه را بر اساس نمرات این دانش آموزان از بیشتر به کمتر مرتب سازید. متأسفانه توابع مرتب سازی PHP همگی برای توابع تک بعدی ساخته شده اند و برای توابع دو بعدی و بیشتر هیچ تابع مستقیمیوجود ندارد. شما ممکن است بخواهید یک تابع مرتب سازی بر اساس الگوریتمهای موجود ایجاد کنید اما خیلی ساده تر از این میتوانید از ترکیب تابع usort و یک تابع پس فراخوان استفاده نمایید. اول تابع usort را به این شکل فرا میخوانیم:
متغیر $students که آرایه دانش آموزان است، اما منظور از “cmp” تابعی به همین نام است که در برنامه تعریف شده است. طرز کار تابع usort به این صورت است که درست مانند تابع sort با مقایسه هر یک از عناصر با هر یک از عناصر دیگر آرایه را مرتب میسازد، اما این بار به جای استفاده از یک قاعده یا ترتیب کلی، از نتیجه ای که تابع پس فراخوان تعریف شده برای آن (در مثال ما تابع cmp) برمیگرداند استفاده میکند. در راهنمای تابع usort آمده است که تابع پس فراخوان باید دو آرگومان بپذیرد. این دو آرگومان دو مقدار از آرایه هستند که usort میخواهد جای آنها را در آرایه مرتب شده نسبت به هم تعیین نماید. تابع پس فراخوان باید یک عدد صحیح منفی، صفر، و یا مثبت را برگرداند. اگر مقدار برگردانده شده منفی باشد یعنی آرگومان اول از آرگومان دوم کوچکتر است، اگر صفر باشد یعنی دو آرگومان برابرند، و اگر مثبت باشد یعنی آرگومان اول از آرگومان دوم بزرگتر است. با این اطلاعات میخواهیم تابع cmp مورد نیاز را بنویسیم:
خط echo اول برای این است که طرز کار تابع را ببینیم. پس از آزمایش تابع آن را پاک خواهیم کرد. در خطوط بعدی دو آرگومان $a و $b را با هم مقایسه میکنیم و یکی از مقادیر صفر، منفی یک، و یا مثبت یک را برمیگردانیم. اگر اکنون برنامه را اجرا میکنیم این خروجی حاصل میشود:
این خطوط توسط دستور echo در تابع cmp تولید شدهاند و طرز کار تابع usort را به روشنی نمایش میدهند. خط echo مذکور را پاک کرده و این خط را برای نمایش تابع مرتب شده به انتهای برنامه میافزاییم :
نتیجه تولید شده مطابق انتظار است:
حال بیایید کمیتابع cmp نوشته شده را دستکاری کنیم. من سعی میکنم کوتاه ترین تابع ممکن را بنویسم. خیلی ساده تصور میکنم که اگر نمره دانش آموز مربوط به آرگومان اول را از نمره دانش آموز مربوط به آرگومان دوم را کم کنم همان نتیجه به دست خواهد آمد:
میبینید که من این تابع را در یک خط نوشتم. اما راستش را بخواهید این کار را با منظور خاصی انجام دادم. دلیل چنین کاری این بود که من میخواهم تابع create_function را به شما معرفی کنم. این تابع کارش ساختن یک تابع دیگر است. اگر تابع eval را دیده باشید میدانید که PHP توانایی اجرای کدهای ذخیره شده در یک رشته را دارد. از این رو شما میتوانید حتی کدهای ذخیره شده در یک پایگاه داده را هم اجرا نمایید، هر چند که از نظر امنیتی این کار اصلاً صحیح نیست. حالا با تابع create_function میتوانید از روی دو رشته یک تابع جدید در برنامه داشته باشید. رشته اول که آرگومان اول این تابع است لیست آرگومانهای تابعی است که میخواهیم آن را تعریف کنیم. رشته دوم هم کد داخل تابع جدید است. از این رو تعریف تابع cmp کوتاه شده خود را میتوانیم به این شکل هم بنویسیم:
اما این تابع دیگر اسم ندارد و به اصطلاح ناشناس است. بنابراین آن را مستقیم به خورد تابع usort میدهیم:
اکنون موفق شدیم کد مرتب سازی یک آرایه دو بعدی را در یک خط بنویسیم. فکر کنید اگر میخواستیم از روش های رایج استفاده کنیم چقدر زمان میبرد و با چه مشکلاتی روبرو میشدیم؟
جایگزین سازی پیچیده
یکی از توابع بسیار مهم و کاربردی PHP تابع str_replace است. این تابع یک رشته را بررسی کرده و هر بار وقوع یک یا چند زیر رشته را با یک یا چند زیر رشته دیگر جایگزین میسازد:
اما این تابع فقط به درد زیر رشته های محض (literal) میخورد و انعطاف پذیری بسیار کمیدارد. یک تابع دیگر که preg_replace است که از عبارات باقاعده استفاده میکند. تابع برآیند این تابع که مورد نظر ما است تابع preg_replace_callback است که میخواهیم یکی دو نمونه از کاربردهای آن را با هم امتحان نماییم. این تابع تطابق های یک عبارت باقاعده را در رشته ای که به آن میدهیم پیدا کرده و با یک رشته دیگر جایگزین میسازد با این تفاوت که رشته جایگزین را از تابع پس فراخوان خود تأمین میسازد.
فرض کنید رشته ای داریم که شامل یک جدول HTML است. ردیف های این جدول با خصیصه bgcolor پس زمینه خاکستری شدهاند به این صورت :
ما میخواهیم این کد HTML را طوری دستکاری کنیم که یکی در میان رنگ زمینه ردیف ها سفید و زرد شود. ممکن است فکر کنید که چرا این کار را زمان تولید کد HTML جدول انجام نداده ایم؟ جواب این است که ممکن است کد HTML توسط برنامه ما تولید نشده باشد و ما فقط توانایی تغییر در آن را داشته باشیم. برای تغییر در رنگ پس زمینه ها به چنین کدی احتیاج داریم:
حالا یا باید ‘callback_function’ را تعریف کنیم یا آن را با create_function بسازیم. ابتدا از روش اول استفاده میکنیم. تابعی مینویسیم که یکی در میان مقدار سفید یا زرد را برگرداند. برای این کار از یک متغیر ایستا (static) استفاده میکنیم که در هر بار اجرا مقدار قبلی اش را حفظ مینماید:
شبیه به راه حل قبل میتوانیم همه چیز را در یک خط خلاصه کنیم:
میانگیری خروجی
توابع مرتبط با میانگیری خروجی (output buffering) هم از جمله توابعی هستند که با توابع پس فراخوان سر و کار پیدا دارند. اگر تابع ob_start() را فراخوانی کنید، پس از آن تا زمانی که پردازش پایان نپذیرد یا خودتان نخواهید هیچ خروجی از برنامه ارسال نخواهد شد. در این بین شما میتوانید خروجی برنامه را جمع آوری کرده و قبل از نمایش آن را از زیر دست یک تابع پس فراخوان عبور دهید.
برای نمونه فرض کنید که متغیر $language حاوی اسم زبان کنونی سایت شما است. شما میخواهید تصاویر صفحه شما به جای شاخه images از شاخه images/farsi/ یا images/english/ خوانده شوند اما نمیخواهید دو قالب متفاوت داشته باشید. برای این کار ابتدای برنامه توسط تابع ob_start میانگیری خروجی را فعال میسازید:
تابع output_filter چنین تابعی خواهد بود:
یک مثال عملی تر تبدیل همه اعداد موجود در صفحه به اعداد فارسی است. اگر بخواهید این کار را با یک تابع تبدیل انجام دهید باید در پس هر محاسبه ای یک بار این تابع را فراخوانی کنید و سختی فراخوانی مکرر آن را بپذیرید. ضمن اینکه در نهایت با برنامه ای مغشوش و به هم ریخته مواجه خواهید شد. اگر برنامه را خودتان هم ننوشته باشید که دیگر بد از بدتر میشود. اما خیلی ساده میتوانید با میانگیری کل خروجی برنامه این کار را به سادگی انجام دهید. برای تبدیل ارقام لاتین به معادل یونیکد فارسی آنها از تابع preg_replace_callback بهره میگیریم:
عدد صفر فارسی در HTML معادل کد ٠ است، عدد یک معادل ١ است، و الی آخر. تابع ما همه این ارقام را پیدا کرده و با معادل یونیکد فارسی آنها جایگزین می سازد. همانطور که مشاهده میکنید در این کد در یک تابع پس فراخوان یعنی تابع output_filter از یک تابع پس فراخوان دیگر که توسط create_function ساخته شده است، استفاده کرده ایم. بنابراین در این زمینه محدودیتی نداریم.
توابع پس فراخوان افق های جدیدی در حل مسائل مختلف پیش روی ما باز میکنند. توابع دیگری هم در زبان PHP هستند که از این توابع سود میجویند. از قبیل این توابع array_map، array_walk، array_filter هستند که توانایی های زیادی در زمینه پردازش داده های موجود در آرایه ها به ما ارائه میدهند. برای شروع به برنامه نویسی با توابع پس فراخوان راهنمای PHP را باز کرده و به سراغ آنها بروید.
رضا ولی نژاد
یک تابع پس فراخوان درست همانند توابع دیگر، توسط برنامهنویس تعریف میشود. اما به طور مستقیم در برنامه فراخوانی نمیشود. طبق تعریف، تابع پس فراخوان تنها زمانی فراخوانی میشود که یک اتفاق معین در حین اجرای برنامه روی بدهد. این اتفاق ممکن است از سوی توابع خود PHP روی دهد. برای مثال، در هنگام تجزیه کدهای XML، هر بار که تابع تجزیه کننده به یک تگ برسد تابع پس فراخوان تعریف شده برای آن را اجرا مینماید. در ادامه چند نمونه از کاربردهای توابع پس فراخوان را با یکدیگر میبینیم:
مرتب کردن آرایه چند بعدی
فرض کنید کلاس درسی دارید و دانش آموزان کلاس و نمرات آنها را در یک آرایه به شکل زیر ذخیره کردهاید:
PHP:
$students = array(
array(‘name’=>‘Hamid’, ‘score’=>18),
array(‘name’=>‘Saeed’, ‘score’=>20),
array(‘name’=>‘Majid’, ‘score’=>17),
array(‘name’=>‘Habib’, ‘score’=>19)
);
PHP:
usort($students, “cmp”);
PHP:
function cmp($a, $b) {
echo “Comparing {$a[name]} ({$a[score]}) with {$b[name]} ({$b[score]})<br>”;
if ($a[score] == $b[score]) {
return 0;
}
return ($a[score] > $b[score]) ? -1 : 1;
}
کد:
Comparing Saeed (20) with Hamid (18)
Comparing Habib (19) with Saeed (20)
Comparing Majid (17) with Saeed (20)
Comparing Hamid (18) with Saeed (20)
Comparing Majid (17) with Hamid (18)
Comparing Habib (19) with Majid (17)
Comparing Hamid (18) with Habib (19)
PHP:
foreach($students as $student) echo “{$student[name]} score is {$student[score]}<br>”;
کد:
Saeed score is 20
Habib score is 19
Hamid score is 18
Majid score is 17
PHP:
function cmp($a, $b) { return $b[score] - $a[score]; }
PHP:
create_function('$a, $b', 'return $b[score] - $a[score];')
PHP:
usort($students, create_function('$a, $b', 'return $b[score] - a[score];'));
جایگزین سازی پیچیده
یکی از توابع بسیار مهم و کاربردی PHP تابع str_replace است. این تابع یک رشته را بررسی کرده و هر بار وقوع یک یا چند زیر رشته را با یک یا چند زیر رشته دیگر جایگزین میسازد:
PHP:
$nospam = str_replace(array(‘@’, ‘.’), array(‘[at]’, ‘[dot]’), ‘[email protected]’);
فرض کنید رشته ای داریم که شامل یک جدول HTML است. ردیف های این جدول با خصیصه bgcolor پس زمینه خاکستری شدهاند به این صورت :
کد:
<tr bgcolor=”gray”>
PHP:
$table_code = preg_replace_callback(‘@gray’, ‘callback_function’, $table_code);
PHP:
function callback_function($match) {
static $run_time = 0;
$run_time++;
return ($run_time % 2) ? “white” : “yellow”;
}
PHP:
$table_code = preg_replace_callback(‘@gray@’, create_function(‘$match’, ‘static $run_time = 0; return (++$run_time % 2) ? “white” :”yellow”;’, $table_code);
میانگیری خروجی
توابع مرتبط با میانگیری خروجی (output buffering) هم از جمله توابعی هستند که با توابع پس فراخوان سر و کار پیدا دارند. اگر تابع ob_start() را فراخوانی کنید، پس از آن تا زمانی که پردازش پایان نپذیرد یا خودتان نخواهید هیچ خروجی از برنامه ارسال نخواهد شد. در این بین شما میتوانید خروجی برنامه را جمع آوری کرده و قبل از نمایش آن را از زیر دست یک تابع پس فراخوان عبور دهید.
برای نمونه فرض کنید که متغیر $language حاوی اسم زبان کنونی سایت شما است. شما میخواهید تصاویر صفحه شما به جای شاخه images از شاخه images/farsi/ یا images/english/ خوانده شوند اما نمیخواهید دو قالب متفاوت داشته باشید. برای این کار ابتدای برنامه توسط تابع ob_start میانگیری خروجی را فعال میسازید:
PHP:
ob_start(‘output_filter’);
PHP:
function output_filter($buffer) {
global $language;
return str_replace("images/", "images/{$language}/", $buffer);
}
PHP:
function output_filter($buffer) {
return preg_replace_callback('@[0-9]@', create_function('$match', 'return "&#" . ($match[0]+1632) . ";";'), $buffer);
}
توابع پس فراخوان افق های جدیدی در حل مسائل مختلف پیش روی ما باز میکنند. توابع دیگری هم در زبان PHP هستند که از این توابع سود میجویند. از قبیل این توابع array_map، array_walk، array_filter هستند که توانایی های زیادی در زمینه پردازش داده های موجود در آرایه ها به ما ارائه میدهند. برای شروع به برنامه نویسی با توابع پس فراخوان راهنمای PHP را باز کرده و به سراغ آنها بروید.
رضا ولی نژاد
آخرین ویرایش: