logo


פרק 2: קישור Perl לעמודי הרשת

יומן רישום

"ספר המבקרים" (guestbook) הוא אחד הממשקים הנפוצים והפופולרים ביותר שאתרי אינטרנט מציעים (ומקדמים). בדרך כלל, "ספר המבקרים" הוא בסה"כ אוסף של הערות, כמו אלה שניתן למצוא משורבטים בספרי המבקרים ביציאה מאתר היסטורי/לאומי כל שהוא. היעד שלנו לעומת זאת הוא למצוא שיטה פורמלית יותר לאיסוף רשימות של מבקרים- למעשה, אנו מעונינים ליצור בסיס נתונים ראשוני של נתונים אלו. לענינינו, אנו יכולים לתאר שמבקרים שנרשמים בספר, מספקים לנו ארבע פיסות מידע, שם, טלפון, דואר אלקטרוני, וכתובת כולל מיקוד (האחרון, יאפשר לנו למקם גאוגרפית את המבקרים).

איך נשמור מידע זה אחרי שהוא התקבל אצלנו? בצורה המתקדמת יש להניח שתכניסו את הנתונים לבסיס נתונים קיים, תוך שימוש בסוג טכנולוגיה כלשהוא של ניהול בסיסי נתונים כמו לדוגמה SQL. לא ניכנס לזה כאן, אך נחזור לנושא בהמשך. עבור מתחילים, נוח יותר לתכנן על לוח חלק, בצורה פשוטה של טבלה (tab-delimited table format). זוהי תצורה פשוטה, ובכל זאת ניתנת להמרה פשוטה לכל מערכת ניהול מסד-נתונים "אמיתית".

המטרה, לכן, של מערכת הרישום מבוססת ה Perl שלנו היא לקבל את טופס הרישום מעמוד האינטרנט , לאשר אותו, לעבד מה שצריך, ולאכסן את התוצאה בקובץ טקסט פשוט, שבסופו של דבר יראה לדוגמה כך:

Ella DiBella=>5551239876=>ella@suky.com=>13062
Borders McGee=>5551235432=>bmcgee@silly.net=>14850 

חוץ מהסימן <=, הטבלה שלנו תכיל למעשה תווים מופרדים-מזוהים (צורה שלא נוכל להציג בקלות במדיום זה).

כאשר נבנה את שגרת ה registerForm& , שבסופו של דבר תהפוך לחלק מהסקריפט ה register.cgi Perl , נכיר כלי חדש לתיכנות בשפת Perl: והוא פתיחה וכתיבה לקבצים מקומיים. זכרו ש CGI Perl script רץ למעשה על שרת הרשת. זה אומר שסקריפט ה Perl יכול לפתוח, לקרוא מ, ולכתוב לקבצים שנמצאים על שרת הרשת- בתנאי שהקובץ אכן קיים, ויש לקובץ את ההרשאות המתאימות להיקרא ע"י סקריפט הPerl , וזאת בתלות במערכת ההפעלה. שימו לב: לסקריפט הרץ על שרת רשת אין הרשאה לכתוב או לקרוא קבצים ממחשב המשתמש, אלא אם כן המחשב המארח את שרת הרשת הוא מחשב המשתמש.

sub registerForm
 { unless (open (LOGFILE,">>/var/logs/registerlog")) 
    {print "Failed to open Registration Log!";exit}
   flock(LOGFILE, LOCK_EX);

בואו נתחיל את לכתוב את ה registerForm& ע"י פתיחה של קובץ טקסט שעליו נוסיף את מידע הרישום: יתכן והפעולה שעשינו בגוש קוד אינה ברורה כל כך. באופן עקרוני, אנו מנסים לפתוח את הקובץ "/var/logs/registerlog" - מובן, שאת הנתיב המדויק ושם הקובץ, תבחרו על פי מערכת ההפעלה בה אתם משתמשים. הסימן "גדול מ" שחוזר פעמים (<<) מציין שהקובץ צריך להפתח במצב append (הוספה). במצב זה, כל מידע שיכתב לקובץ יתווסף למידע שכבר קיים בו, אם הקובץ לא קיים, הוא יווצר. שימו לב: "גדול מ" בודד (<) פותח את הקובץ במצב "create" (צור) או במצב overwrite, שימחוק כל קובץ קיים בשם זה, ויתחיל אותו מחדש. הזהרו!!!.

הפקודה ()open יוצרת filehandle (מזהה יחודי) בשם LOGFILE. אנו יכולים לחשוב על המזהה כמשהו שדומה מאוד בתפקודו למשתנה, אבל הוא מתיחס לקובץ מסוים והמצב שבו הוא מופעל (זכרו, אנו משתמשים ב /var/logs/registerlog במצב append ). אם הפקודה ()open נכשלת בנסיון לפתוח את הקובץ במצב המסוים (הוא עשוי לא להיות קיים, או לא מחזיק בהרשאות המתאימות לכתיבה או קריאה) יוחזר ערך שקר. זוהי הסיבה שהקריאה ל ()open עטופה בתוך משפט unless: אם יהיה כשלון בפתיחת הקובץ, יופעל המשפט המטפל בכישלון, שידפיס הודעת שגיאה, ויצא מהתוכנית ("exit").

בהנחה שהקובץ נפתח בהצלחה עבור "הוספה", אנו רואים עתה משפט מוזר. קריאה לפונקצית ה ()flock. אם אתם מפתחים בסביבת UNIX, פקודה זו חשובה מאוד,אולם חלק ממערכות הפעלה האחרות כלל לא תומכות בה, לכן תצטרכו להשמיט שורה זו לדוגמא בחלונות. בקיצור,()flock נועלת את הקובץ שנפתח כך שתהליך אחר לא יוכל לגעת בו, עד שיבוצע unlock על הקובץ. ברשת זה ממש חיוני, כאשר מספר רב של משתמשים יכולים לשלוח טופס רישום באותו זמן ממש-תיווצר אנדרלמוסיה שלמה אם כל אחד ינסה לכתוב ולקורא באותו זמן. שימוש ב ()flock גורם לכל מי שנרשמים להכנס למין תור, תוך שמירה על ההגינות בגישה לקובץ הפתוח. שימו לב: כמה מערכות UNIX עלולות שלא לתמוך בפרמטר lock_ex , במקרה כזה תצטרכו להחליף אותו בערך 2 , בצורה ( flock(FILEHANDLE,2 .

עתה, הניחו שאספנו זה מכבר את הטופס לתוך ארבעת המשתנים שלנו: $username, $userphone, $usermail, $userZIP. העברת מידע זה לקובץ החדש שלנו קלה למדי:

print LOGFILE "$username\t$userphone\t$usermail\t$userZIP\n";
unless (close (LOGFILE)) 
 { print "Failed to close Registration Log!";exit}
print "<h2>Thank you for your registration. ".
      "Click <a href=\"home.html\">here</a>".
      "to return to the home page.";
}

הפקודהprint מפנה את הפלט למזהה LOGFILE, המצביע, כמובן, לקובץ שפתחנו ל"הוספה". הפלט עצמו מכיל בפשטות את כל משתני המידע מופרדים ע"י טאבים (ה t\ בשפת Perl ). החלק האחרון של המידע מלווה בתו של שורה חדשה, הידוע גם כ enter (התו n\ ).

אחרי שהדפסנו לתוך קובץ ה log אנו סוגרים את הקובץ, שגורם גם לנעילה (lock) להשתחרר. במקרה שהפקודה ()close תכשל, דבר שהוא נדיר מאוד במקרה שהקובץ נפתח בהצלחה, תופיע הודעת שגיאה והתהליך יופסק. אחרת, הפקודה print תוציא הודעת HTML המודה למשתמש על הרשמתו, והשגרה תסגר ע"י סוגריים מסולסלים.

עכשיו אנו מוכנים לשלב את הפיסות למבנה סופי ושלם של הסקריפט register.cgi. שימו לב להוספה אחרונה-ההצהרה "use Fcntl;" שהוספנו בתחילת הסקריפט. הצהרה זו היא לעיתים דרישה מוקדמת לשימוש בפקודה ()flock , בכל אופן חלק מההתקנות של Perl עשויות שלא לתמוך במודול Fcntl - אם תקבל הודעת שגיאה מחק שורה זו.

register.cgi (סופי)

#!/usr/bin/perl
use CGI;
use Fcntl;

#create an instance of the CGI object 
$cgiobject = new CGI;
			
#grab the values submitted by the user
$userphone=$cgiobject->param("userphone");

#output HTML header to web browser
print $cgiobject->header;

#test form validation, output error if necessary 
#otherwise proceed to registration log
if ( &validateForm )
 { &registerForm }
else
 { &output_fail }

# subroutine which validates form fields and 
#returns a true or false result
sub validateForm
 { $failedFields="";
   $formValid=1; 
   #validate phone number   
   $fieldValid=$userphone=~/^\D*\d{3}?\D*\d{3}?\D*\d{4}?\D*$/;
   if ($fieldValid)
    { $userphone=~s/\D//g }
   else
    { $failedFields.="Telephone Number,";
      $formValid=0 }   
   
   #validate user name
   $fieldValid=$username=~/^[a-zA-Z]+/;
   unless ($fieldValid)
    { $failedFields.="User Name,";
      $formValid=0 }

   #validate ZIP code
   $fieldValid=$userZIP=~/^\d{5}(-\d{4})?$/;
   unless ($fieldValid)
    { $failedFields.="ZIP Code,";
      $formValid=0 }

   
   return $formValid
 }

#subroutine which outputs failure message 
#if form does not validate
sub output_fail
 { chop($failedFields); 
    $resultPage="<html><head>".
		"<title>Uh-Oh: Registration Problem</title>".
		"</head><body bgcolor=\"white\">".
		"<h2>Sadly, there seems to be a problem with your ".
		"form submission. Specifically, the following ".
		"mandatory fields were filled in improperly:</h2>".
		"<Br><h3>$failedFields</h3>".
		"<Br>Please go back and try again.".
		"</body></html>";
   print $resultPage;
 }


#subroutine which saves the form data to a log file
sub registerForm
 { unless (open (LOGFILE,">>/var/logs/registerlog")) 
    {print "Failed to open Registration Log!";exit}
   flock(LOGFILE, LOCK_EX);
   print LOGFILE "$username\t$userphone\t$usermail\t$userZIP\n";
   unless (close (LOGFILE)) 
    { print "Failed to close Registration Log!";exit}
   print "<h2>Thank you for your registration. ".
         "Click <a href=\"home.html\">here</a>".
         "to return to the home page.";
 }

בדיקת טפסים
תוכן עניינים
מבט על משתנים סביבתיים

אודות
תוכן עניינים
פרק 1: ה Perl שאתם צריכים לדעת
פרק 2: קישור Perl לעמודי הרשת
התאמת מחרוזות ב Perl ביטויים רגולריים  
CGI ותכנות מונחה עצמים ב Perl  
הערות בקשר להרצת CGI  
CGI ותכנות מונחה עצמים ב Perl: קלט  
CGI ותכנות מונחה עצמים ב Perl: פלט  
בדיקת טפסים  
יומן רישום  
מבט על משתנים סביבתיים  
מעקף: הפניה מחדש (redirection)  
סיכום  
פרק 3: שמירת מצב
פרק 4: HTML בחטף ותבניות (Templates) רשת
פרק 5: עיבוד וניתוח של עמודי רשת
פרק 6: להשתעשע עם בסיסי נתונים מקוונים:אקסס
פרק 7: המודל MySQL
פרק 8: להשתעשע בבסיסי נתונים - GUFE - החזית הכללית והשימושית
פרק 9: המילניום - ניהול זמן ותאריך
פרק 10: ניהול רשימות והאשים (Hashs)
פרק 11: הפניה להפניה
פרק 12: הכרות עם mod_perl