تعليم لغة بيرل Perl و سي جي آي CGI
الدرس السادس
في الدرس السابق قمنا بعمل نموذج هتمل HTML Form يمكن لزائريك إدخال المعلومات فيه وإرسالها إلى برنامج بيرل عبر السي جي آي . و مع ذلك فقد كان هذا تدريب بسيط ، فأنت قد أرجعت المعلومات كما هي للزائر .
في هذا الدرس سوف نبني صفحة ويب أكثر صعوبة و برنامج للتعامل معها ، سوف نصمم نموذج استفهام و نرسل معلوماته و نعيد الحقول الخالية فيها إلى الزائر ليملئها ، و سوف نستعمل طريقة POST لإرسال المعلومات من النموذج إلى برنامج CGI .
أفضل طريقة لتتعلم شيئاً ما هي أن تفعله بنفسك ، دعنا نقوم بعمل نموذج استفهام بسيط و بذلك يمكننا رؤية كيف يعمل . اكتب صفحة هتمل التالية و احفظها بإسم quiz.htm :
<html dir="rtl">
<head>
<title>Quiz Form</title>
</head>
<body>
<H1 ALIGN="CENTER">A Little Quiz Form</H1>
<HR>
<H3 ALIGN="RIGHT">في كل فئة اختر ما يناسبك أو يناسب تفضيلاتك</H3>
<FORM ACTION="/cgi-bin/quiz.pl" METHOD="POST">
<table>
<tr>
<td VALIGN="top">
<B>الجنس</B><BR>
<INPUT TYPE="radio" NAME="gender" VALUE="0">ذكر<BR>
<INPUT TYPE="radio" NAME="gender" VALUE="1">أنثى<BR>
<INPUT TYPE="radio" NAME="gender" VALUE="2">غيرهما<BR>
</td>
<td VALIGN="top">
<B>العمر</B><BR>
<INPUT TYPE="radio" NAME="age" VALUE="0">12-18<BR>
<INPUT TYPE="radio" NAME="age" VALUE="1">19-25<BR>
<INPUT TYPE="radio" NAME="age" VALUE="2">26-30<BR>
<INPUT TYPE="radio" NAME="age" VALUE="3">أكبر من 30<BR>
<INPUT TYPE="radio" NAME="age" VALUE="4">شيخ قبيلة<BR>
</td>
<td VALIGN="top">
<B>فيلمك المفضل</B><BR>
<INPUT TYPE="radio" NAME="film" VALUE="0">Matirx<BR>
<INPUT TYPE="radio" NAME="film" VALUE="1">Gladiator<BR>
<INPUT TYPE="radio" NAME="film" VALUE="2">Mission Impossible2<BR>
<INPUT TYPE="radio" NAME="film" VALUE="3">الكيت كات<BR>
<INPUT TYPE="radio" NAME="film" VALUE="4">الإرهاب و الكباب<BR>
<INPUT TYPE="radio" NAME="film" VALUE="5">ليس فيهم<BR>
</td>
<td VALIGN="top">
<B>هل تعتقد أن عادل إمام قد صار كبيراً على التمثيل</B>
<INPUT TYPE="checkbox" NAME="OldAdil">
</td>
</tr>
</table>
<h3 ALIGN="RIGHT">معلومات اختيارية</h3>
<B>الإسم</B><BR>
الأول<INPUT TYPE="text" NAME="firstname" SIZE="16">
الأخير<INPUT TYPE="text" NAME="lastname" SIZE="16"><BR>
<BR>
<CENTER>
<INPUT TYPE="submit" VALUE="إرسل">
<INPUT TYPE="reset" VALUE="إمسح">
</CENTER>
</form>
</body>
</html>
المفترض أن يبدو الشكل كما في الصورة :
هذا الشكل يستعمل أزرار راديو و صندوق اختيار و صندوقين للنص ، و لذلك سيعطي برنامج بيرل مزيج جيد نوعاً ما من أنواع المداخل المختلفة ، بالإضافة إلى أنه يستعمل طريقة POST لتمرير المعلومات بدلاً من طريقة GET التي استعملناها في الدرس السابق . و كما ترى فقد حفظته في فهرس Docs في Sambar في Program files .
الآن سنقوم بعمل تعديلات بسيطة على برنامج geturl الذي كتبناه في الدرس السابق . الفرق الرئيسي بين طريقتي GET و POST هو في كيفية تمرير المعلومات ، GET تضع المعلومات في عنوان URL بالفعل و تشحنها إلى المحيط و يمكنك رؤيتها في صندوق العنوان في المتصفح ، بينما POST تضع المعلومات في مجرى مدخل برنامج بيرل ، كما لو كنت تكتب المعلومات في الداخل من لوحة المفاتيح . لذلك فإن برنامج geturl سوف يحتاج إلى تعديلين لمجاراة الاختلافات :
# Get HTML header, ender, define the page title.
require "/cgi-bin/html.pl"; # Full path.
$Title = "GEt POST Information From a URL";
# Get the length of the data.
$DataLen = $ُENV{'CONTENT_LENGTH'};
# Read the data from standard input.
read (STDIN, $QueryString, $DataLen);
# Use split to make an array of name-value pairs broken at
# the ampersand character.
@NameValuePairs = split (/&/, $QueryString);
أضف السطور السابقة الموضوع أمامها السهم إلى الملف و احذف السطرين :
# Get the query string.
$QueryString = $ENV{'QUERY_STRING'};و احفظه بإسم quiz.pl في cgi-bin بالطبع .
STDIN و POST :
لأن معلومات النموذج تأتي من المدخل الأساسي بطريقة POST فيمكنك استعمال وظيفة read لتدخلها في برنامجك . و للاختصار وظيفة read تأحذ هذا الشكل :
read (HANDLE, BUFFER, LENGTH);
حيث HANDLE هو توجيه الملف كما عرفنا . و BUFFER هو متغير عددي للإمساك بأي شيء يقرأه . و LENGTH هو عدد الرموز أو البايتات التي ستقرأ في الذاكرة المؤقتة . و لكي تحصل على قيمة HANDLE فإن بيرل يجعل هذا سهلاً ، ففي كل مرة تشغل فيها برنامج بيرل يعلن مسبقاً عن وجود مجموعة من توجيهات الملف التي تسمح لبرنامجك بالتفاعل مع نظام التشغيل و فتح و إغلاق الملفات ، و من بين هذه التوجيهات :
STDIN و هو طريق المدخل الرئيسي .
STDOUT و هو طريق المخرج الرئيسي .
و كما ترى فإن STDIN مفتوح لك بالفعل كل ما عليك هو أن تستخدمه . و أخيراً يجب عليك أن تخبر read بعدد البايتات الموجودة ليقرأها ، و لحسن الحظ فإن محيط CGI يحتوي على متغير به نفس المعلومات المطلوبة في read و هو المتغير CONTENT_LENGTH . و نتيجة لكل هذه المساعدة من بيرل و سي جي آي يصبح لديك القليل لتفعله في تعديل البرنامج ، عليك أن تحصل أولاً على طول المعلومات و هذا يتم بالسطر :
$DataLen = $ُENV{'CONTENT_LENGTH'};
ثم تستعمل هذا الطول في قراءة المعلومات من المدخل الرئيسي في متغير محلي عددي (و هو BUFFER في الصيغة العامة أو QueryString في برنامجنا) :
read (STDIN, $QueryString, $DataLen);
الآن بعد أن فهمت عمل ما أضفت من سطور ، شغل المتصفح (تأكد من تشغيل Sambar Server) و في صندوق العنوان اكتب localhost/docs/quiz.htm ثم املأ النموذج بما ترغب و اضغط زر إرسل ، و سوف تكون النتيجة في الغالب مشابهة لهذه :
لاحظ أن النموذج quiz.htm يعين قيمة عددية لكل زر راديو في كل فئة ، مثلاً فئة فيلمك المفضل بها ستة أزرار راديو كل زر له رقم من صفر إلى خمسة و بالتالي فالبرنامج يظهر لك القيمة العددية للزر الذي اخترته . يمكننا أن نعين لكل زر قيمة إسمية (سلسلة) تصف الزر ، و لكن هذا يجعلهم أكثر صعوبة في التعامل معهم ، إذاً ليس أمامنا خيار (و لا طماطم) سوف نكتب برنامجاً يعرض القيم الإسمية للأزرار ، شغل محرر النص و اكتب الآتي :
#!perl/bin
# quiz.pl
# A little perl program script to read, decode and print the names
# and values passed to it from an HTML form through CGI.# Get HTML header, ender, define the page title.
require "htmlara.pl"; # Full Path.
$Title = "تفضيلاتك الشخصية";
$MaxData = 6;
# Assign names to indexes to make them more descriptive.
($Sex, $HowOld, $FaveFilm, $OldAdil, $LastName, $FirstName) = (0..5);
# Set up a flag for the OldAdil checkbox.
$OldAdil = 1;
# Set up a series of arrays to handle the form data.
@Gender = ("ذكر", "أنثى", "غيرهما");
@Age = ("12-18", "19-25", "26-30","أكبر من 30", "شيخ قبيلة");
@Film =
(
"Matrix",
"Gladiator",
"Mission Impossible2",
"الكيت كات",
"الإرهاب و الكباب",
"ليس فيهم"
);
# Get the length of the data.
$DataLen = $ENV{'CONTENT_LENGTH'};
# Read the data from standard input.
read (STDIN, $QueryString, $DataLen);
# Use split to make an array of name-value pairs broken at
# the ampersand character. then get the values.
@NameValuePairs = split (/&/, $QueryString);
$n = 0;
foreach $NameValue (@NameValuePairs)
{
($Name, $Value[$n]) = split (/=/, $NameValue);
$n++;
}
# Set or clear $OldAdil flag based on the number of pairs
# counted in $n. The checkbox value won't be sent if it's
# not checked. Adjust indexes too.
if ($n != $MaxData)
{
$OldAdil = 0;
$FirstName--;
$LastName--;
}
# Put up an HTML header, page title and a rule.
&HTML_Header ($Title);
print "<body>\n";
print "<H1>$Title</H1>\n";
print "<HR>\n";
# Print the information in a friendly manner.
print "<H3>اسمك ";
# See if anything has been entered in the name.
if (($Value[$LastName] eq "") && ($Value[$FirstName] eq ""))
{
print "غير معروف\n";
}
else
{
print "$Value[$FirstName] $Value[$LastName]\n";
}
print "</H3>\n";
print "<H3>جنسك $Gender[$Value[$Sex]]</H3>\n";
print "<H3>عمرك $Age[$Value[$HowOld]]</H3>\n";
print "<H3>فيلمك المفضل هو ";
print "\"$Film[$Value[$FaveFilm]]\"</H3>\n";
print "<H3>أنت ";
if (!$OldAdil)
{
print "لا ";
}
print "ترى أن عادل إمام قد صار كبيراً على التمثيل </H3>\n";
# End the HTML document.
&HTML_Footer;
# End quiz.pl
احفظه بإسم quiz.pl في فهرس cgi-bin ثم شغل النموذج من المتصفح و املأه و اضغط على زر إرسل ، سترى شيئاً مثل هذا :
هذا هو أكبر برنامج نكتبه حتى الآن ، و الآن إلى الشرح :
في أول البرنامج استدعينا الملف htmlara.pl و هو نفس الملف html.pl ما عدا أن واصفة <html> فيه تدعم العربية أي تجعل اتجاه الصفحة من اليمين إلى اليسار ، و هذا مهم لكي يستطيع البرنامج أن يرسم صفحة ويب عربية ، كل ما عليك هو أن تفتح الملف html.pl و تعدل الواصفة <html> في أوله إلى :
<html dir="rtl">
و احفظه بإسم htmlara.pl .
قد تكون لاحظت أننا في هذا البرنامج قد أزلنا الأسطر التي تفك شفرة رموز URL ، و لهذا إذا كتبت في النموذج في حقلي الإسم باللغة العربية ستظهر مشفرة في النتيجة . و السبب في ذلك هو الاختصار ليس أكثر ، و لكن ينبغي عليك أن تضع هذه الشفرة في كل برامجك القادمة .
أول سطر جديد في البرنامج يعكس جزءاً هاماً من المعلومات التي تحتاجها ، و هو عدد الفئات التي تتوقع استقبالها . أي عدد فئات أزرار الراديو التي تتوقعها :
$MaxData = 6;
فأنت تبحث عن الفئات التالية :
1- الجنس . 2- العمر . 3- الفيلم المفضل .
4- رأيه في عادل إمام . 5- الإسم الأول . 6- الإسم الأخير .
و بالتالي فإن MaxData$ سوف يتم استعمالها مؤخراً في البرنامج للتأكد من الرقم الفعلي للفئات المرسلة بسبب المراوغة في صندوق الاختيار الخاص بعادل إمام . حيث إن صندوق الاختيار إما أن يكون محدد أو غير محدد ، و في حالة عدم تحديده فإنه لا يرسل أي شيء على الإطلاق و بالتالي فإن المجموعات سوف يختل ترتيبها .
بصورة عامة أنت تعرف بالفعل أن كل الفئات سيتم وضعها في مجموعة بناء على الترتيب الذي تم إرسالهم به ، و يمكنك جعل البرمجة لديك أسهل بتحديد أسماء متغيرات وصفية للفئات ، فهم سيتم إرسالهم بنفس الترتيب الذي تمت به كتابتهم في نموذج هتمل . كل فئة من الفئات تحمل رقم من صفر إلى خمسة ؛ الفئة الأولى هي الجنس و تأخذ الرقم صفر ، يليها العمر و يأخذ الرقم واحد ، و هكذا حسب ترتيبهم في نموذج الهتمل الذي كتبناه . السطر التالي يعين لكل رقم يدل على فئة إسم متغير وصفي :
($Sex, $HowOld, $FaveFilm, $OldAdil, $LastName, $FirstName) = (0..5);
و لاحظ أننا وضعنا اختصار للأرقام بدلاً من كتابة كل رقم أمام اسم المتغير الخاص به . هكذا يمكنك فهم لماذا يجب أن نتأكد من الرقم الفعلي للفئات المرسلة ، لأن صندوق الاختيار إذا لم يتم تحديده لن يرسل أي شيء و بالتالي فإن المتغير التالي عليه سيأخذ رقمه و تصبح النتيجة أن يكتب البرنامج في النهاية مثلاً أن العمر = الكيت كات .
بعد ذلك وضعنا قيمة المتغير OldAdil$ تساوي واحد ، فالطريقة الوحيدة لرؤية ما إذا كان صندوق الاختيار قد تم اختياره أو لا هو اختبار وجوده و هذا هو ما فعلناه في السطر :
$OldAdil = 1;
مــلــحــوظــة : العلم Flag في لغة البرمجة هو قيمة إما صحيحة أي تساوي واحد و إما خاطئة أي تساوي صفر ، تعرف هذه القيم بإسم قيم Boolean و هي على إسم عالم رياضيات بريطاني يسمى George Boole عاش في القرن التاسع عشر و اخترع جبراً مبنياً على المنطق الرمزي .
الجزء التالي من الشفرة سهل فهمه :
@Gender = ("ذكر", "أنثى", "غيرهما");
@Age = ("12-18", "19-25", "26-30","أكبر من 30", "شيخ قبيلة");
@Film =
(
"Matrix",
"Gladiator",
"Mission Impossible2",
"الكيت كات",
"الإرهاب و الكباب",
"ليس فيهم"
);
فأنت تريد أن تطبع أوصاف القيم التي أدخلها زائرك في النموذج لذلك فأنت تضاعف النموذج في مجموعات لكل فئة زر راديو .
و لاحظ أن السلاسل في كل مجموعة مكتوبة بنفس ترتيبها عندما كتبت في نموذج الهتمل ، هذا مهم لأن البرنامج سوف يستقبل قيم عددية من النموذج و يجب أن تتطابق الأرقام مع فهارس المجموعة و إلا فسوف تطبع المجموعة الخطأ في النهاية . (إذا كنت ترى عناصر المجموعة بأعلى غير مرتبة فذلك راجع إلى أخطاء التنسيق ، و يجب عليك أنت أن تكتب العناصر بالترتيب الصحيح في برنامجك) .
بعد ذلك يشحن البرنامج أزواج الإسم = القيمة Name = Value من المعلومات المرسلة له و لكن هناك اختلاف بسيط :
@NameValuePairs = split (/&/, $QueryString);
$n = 0;
foreach $NameValue (@NameValuePairs)
{
($Name, $Value[$n]) = split (/=/, $NameValue);
$n++;
}
هنا أسسنا متغير جديد هو n$ أعددنا قيمته تساوي صفر ، و هو عداد بيس فقط لعد رقم الفئات المرسلة إلى البرنامج و لكن كفهرس في مجموعة من كل القيم في أزواج Name = Value . أنت لا تحتاج للأسماء لأنك بالفعل تعرفهم ، و لكنك تحتاج متغير زائف لكي يمسك مكان الجانب الأيسر من الانقسام من NameValue$ . و بالتالي تكون Value[$n$] أول عنصر عددي من مجموعة Value@ على أول مرور من خلال كتلة foreach .
السطر التالي عليها يضيف n$ مع المشغل ++ لذلك فسوف تكون مساوية لواحد على المرور الثاني و 2 على الثالث و هكذا ، الآن أنت لديك مجموعة مليئة بكل القيم التي تم إرسالها من النموذج . و كذلك لديك تعداد للقيم في n$ و هذا التعداد هو كيفية تحديد ما إذا كان صندوق الاختيار الخاص بعادل إمام قد تم تحديده أو لا .
الجزء التالي يقول :
if ($n != $MaxData)
{
$OldAdil = 0;
$FirstName--;
$LastName--;
}
إنه إذا كان n$ لا يساوي MaxData$ راجع الدرس الرابع لتذكر مشغلات المقارنة فصندوق تحديد عادل إمام سوف يكون قيمته صفر أي أنه غير محدد و بالتالي سينقص FirstName$ و LastName$ واحد في الأرقام المقابلة لهم في النموذج حتى يشيرا إلى الفئة الملائمة لهم و لا يحدث خطأ ، و هذا بمشغل التناقص -- .
و أخيراً ، يكتب البرنامج المعلومات المصاغة إلى صفحة هتمل و يعرضها ، هذه شفرة قد قمنا بكتابتها من قبل ، و لكنها هنا مع استثنائين : أولهما جملة if التي تطبع الإسمين الأول و الأخير :
print "<H3>اسمك ";
# See if anything has been entered in the name.
if (($Value[$LastName] eq "") && ($Value[$FirstName] eq ""))
{
print "غير معروف\n";
}
else
{
print "$Value[$FirstName] $Value[$LastName]\n";
}
جملة if تختبر عناصر FirstName$ و LastName$ في مجموعة Value@ لكي ترى إذا كانوا خاليين (السلاسل الفارغة يشار إليها بعلامتي اقتباس لا شيء بينهما "" و علامة المقارنة eq التي تعني يساوي راجع الدرس الرابع و كل جملة موجودة بين قوسين ، و يربط بين الجملتين العلامة && التي تعني مشغل (و) (AND) المنطقي . و بالتالي يكون معنى الجملة : إذا كانت الجملة الأولى صحيحة و الثانية صحيحة إذاً نفذ الشفرة التي في الكتلة .
مــلــحــوظــة : يوجد في بيرل كذلك مشغل (أو) (OR) المنطقي و يمثله العلامة || .
الجملة التالية else تقول أنه في حالة كذب جملة أو جمل if بأعلى نفذ كتلة شفرتي . هناك تنوع else يمزج الجملتين الشرطيتين و يبدو مثل هذا :
if (هذا صحيح)
{
افعل هذا;
}
elseif (ذلك صحيح)
{
نفذ ذلك;
}
else
{
نفذ شيئاً آخر;
}
الجدير بالاهتمام في كتلة if-else أيضاً هو الطريقة التي يتم بها طبع الأسماء :
print "$Value[$FirstName] $Value[$LastName]\n";
و هذا بسبب الطريقة التي تم بها إعداد الفهارس الوصفية في أول البرنامج فيمكنك الذهاب مثلاً إلى الإسم الأخير بكتابة :
$Value[$LastName]
و لكن الأمر يصبح معقداً أكثر مع القيم الأخرى لأن النموذج شحن أرقام للإشارة إلى أزرار الراديو ، و أنت تستعمل الأرقام لالتقاط سلسلة من المجموعات التي صنعتها في بداية البرنامج . لذلك إذا كنت تريد الإشارة إلى (أنثى) التي تحمل رقم واحد في الفئة (الجنس) يمكنك كتابة :
$Gender [$Value [$sex]]=$Gender[1]="أنثى"
لاحظ أن جملة print التي تطبع فيلمك المفضل بها علامات اقتباس هاربة "\ لأننا نريد لعلامات الاقتباس أن تظهر حول اسم الفيلم في صفحة هتمل .
أخر كتلة شفرة ننظر فيها هي :
print "<H3>أنت ";
if (!$OldAdil)
{
print "لا ";
}
print "ترى أن عادل إمام قد صار كبيراً على التمثيل </H3>\n";
تذكر أن قيمة OldAdil$ قد تم إعدادها أو إزالتها اعتمادا على ما إذا كان صندوق اختيار عادل إمام قد أرسل قيمة للبرنامج أو لا . علامة التعجب في بيرل ! هي مشغل (لا)(NOT) المنطقي . و هذه الجملة تقول أنه إذا لم يكن صندوق اختيار عادل إمام محدداً اطبع كلمة (لا) قبل الجملة الأخيرة أما إذا كان محدداً فإطبع الجملة الأخيرة وحدها .
استفسار ؟ راسلني