Menu

טפסים ב-PHP: עיבוד, אימות והצגה מחדש

טופס PHP שולח לעצמו עם <form method="post">, והסקריפט קורא את השדות מ-$_POST['name'] כש-$_SERVER['REQUEST_METHOD'] הוא 'POST'. למדו אימות, הודעות שגיאה ליד השדות, שמירה על ערכים שהוזנו, תיבות סימון ורשימות בחירה, ו-escape לפלט.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

טופס PHP שולח בחזרה לסקריפט שהדפיס אותו: כתבו <form method="post"> עם שדות שיש להם שם, וכש-$_SERVER['REQUEST_METHOD'] הוא 'POST', קראו כל שדה כ-$_POST['name']. בצעו escape עם htmlspecialchars() לכל דבר שאתם מדפיסים בחזרה.

הריצו את הבלוק, הקלידו שם בטופס ולחצו על Send: אותו סקריפט ירוץ שוב, הפעם כש-$_POST מלא.

שלושה דברים קורים כאן שכל מטפל בטפסים צריך:

  • בדיקת ה-method. בביקור הראשון הבקשה היא GET ואין מה לעבד. ?? 'GET' חשוב רק כשהקובץ רץ משורת הפקודה, שם REQUEST_METHOD לא מוגדר; בשרת web הוא תמיד מוגדר.
  • ?? '' על כל שדה. שדה יכול לחסור (שדה ששמו שונה, בקשה שנשלחה מסקריפט), וקריאה של מפתח חסר מדפיסה אזהרה.
  • escape בפלט. השם חוזר לדף פעמיים, ו-htmlspecialchars() מוודאת שערך כמו <b>Ada</b> מוצג כטקסט במקום להפוך ל-markup. הפרטים נמצאים בעמוד htmlspecialchars.

אימות טופס והצגת שגיאות ליד השדות

אספו שגיאות במערך שהמפתחות שלו הם שמות השדות. אם המערך ריק, הטופס תקין; אחרת הדפיסו שוב את הטופס עם כל הודעה מתחת לשדה שלה והקלט של המבקר עדיין במקומו. שלחו אותו ריק, אחר כך עם אימייל שגוי, ואחר כך עם ערכים תקינים.

novalidate מכבה את הבדיקות של הדפדפן עצמו כדי שתוכלו לראות את הבדיקות של PHP עובדות. השאירו את שתיהן בטופס אמיתי: אימות בדפדפן הוא נוחות למבקר, אימות ב-PHP הוא זה שקובע, כי כל אחד יכול לשלוח בקשה בלי הטופס שלכם. filter_var() עושה את האימייל ואת טווח המספר השלם בקריאה אחת כל אחד; עמוד filter_var מכסה את המסננים האחרים שלה.

תיבות סימון, כפתורי רדיו ו-select

שדה טקסט תמיד שולח ערך, גם ריק. שאר הפקדים לא:

  • תיבת סימון לא מסומנת לא שולחת כלום, ולכן המפתח שלה חסר ב-$_POST.
  • תיבות סימון בשם topics[] מגיעות כמערך: $_POST['topics'] הוא ['php', 'sql'].
  • קבוצת רדיו או <select> שולחות את ה-value של האפשרות שנבחרה.

כדי לשמור על הבחירה אחרי שליחה, הדפיסו checked או selected על האפשרות שמתאימה. סמנו כמה תיבות, בחרו רמה ולחצו על Save.

שתי הבדיקות שאחרי קריאת הקלט חשובות יותר ממה שהן נראות. array_intersect() משמיטה כל נושא שלא היה ברשימה שלכם, והבדיקה של is_string() יחד עם array_key_exists() מחזירה את הרמה לברירת מחדל אם מישהו שולח ערך שאף פעם לא הצעתם (או מערך, שהיה גורם ל-$levels[$level] לזרוק). select מגביל את מה שדפדפן יכול לשלוח, לא את מה שבקשה יכולה להכיל.

טפסים מרובי שלבים עם שדות נסתרים

שדה נסתר מעביר ערך משלב לשלב בלי להציג אותו. כאן שלב 1 שואל שם, שלב 2 שואל שפה ושולח את השם הלאה בשדה נסתר, ושלב 3 מציג את שניהם. עברו את שלושת השלבים.

שדות נסתרים גלויים בקוד המקור של הדף ואפשר לערוך אותם כמו כל שדה אחר, ולכן הם בסדר להעברת התשובות של המבקר עצמו בין שלבים, אבל אף פעם לא למחיר, למזהה משתמש או לתפקיד. שמרו את אלה בשרת, ב-session; ראו sessions ועוגיות.

Post/Redirect/Get: עצירת השליחה החוזרת ברענון

אחרי POST מוצלח, אם הסקריפט מדפיס דף "תודה" ישירות, לחיצה על רענון מבקשת מהדפדפן לשלוח שוב את ה-POST, וההזמנה או התגובה נשמרות פעמיים. התיקון הוא לשמור, להפנות ולהציג את התוצאה ב-GET:

<?php
session_start();

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $errors = validate($_POST);
    if (!$errors) {
        save_comment($_POST);
        $_SESSION['flash'] = 'Comment posted.';
        header('Location: /comments.php', true, 303);
        exit;
    }
    // On errors, fall through and print the form with the messages.
}

$flash = $_SESSION['flash'] ?? null;
unset($_SESSION['flash']);

303 See Other אומר לדפדפן לעקוב אחרי ההפניה עם GET. exit אחרי header() חשוב: בלעדיו שאר הסקריפט ממשיך לרוץ. ההפניה גם חייבת לקרות לפני כל פלט, אחרת PHP מסרבת עם "headers already sent".

טפסים אמיתיים מוסיפים גם טוקן CSRF: ערך אקראי שנשמר ב-session ובשדה נסתר, ומושווה עם hash_equals() בשליחה, כך שאתר אחר לא יכול לשלוח לטופס שלכם בשם מבקר מחובר.

בדיקת האימות בלי דפדפן

שימו את הכללים בפונקציה שמקבלת מערך ומחזירה את השגיאות. הדף קורא לה עם $_POST, ואתם יכולים לקרוא לה עם כל מערך שתרצו, וזה הופך את מקרי הקצה לקלים לניסיון. הוסיפו מקרים משלכם לרשימה.

המקרה הרביעי הוא זה שאנשים שוכחים. בקשה יכולה לשלוח name[]=x, וזה הופך את $_POST['name'] למערך, והעברת מערך ל-trim() זורקת TypeError ועוצרת את הסקריפט. בדיקה של is_string() קודם הופכת קלט עוין לשגיאת אימות רגילה. ההמרה (string) בבלוקים הקודמים מונעת גם היא את ה-TypeError, אבל היא הופכת את המערך למילה Array ומדפיסה אזהרה, ולכן is_string() היא הבדיקה הנקייה יותר.

טעות נפוצה: קריאה של $_POST לפני שהטופס נשלח

סקריפט הטופס הראשון הנפוץ ביותר קורא את השדות בראש הקובץ:

<?php
$name = $_POST['name'];
echo "Hello, $name";

בביקור הראשון שום דבר לא נשלח, ולכן PHP מדפיסה:

Warning: Undefined array key "name" in /var/www/form.php on line 2

התיקון הוא הדפוס מהבלוק הראשון: עבדו את הטופס רק כשהבקשה היא POST, וקראו כל שדה עם ברירת מחדל, $_POST['name'] ?? ''. אם האזהרה מופיעה אחרי השליחה, השוו את מאפיין ה-name ב-HTML למפתח ב-PHP: name="Name" ו-$_POST['name'] הם מפתחות שונים.

שאלות נפוצות

איך שולחים טופס PHP לאותו דף?

כתבו <form method="post"> בלי action (או action=""). הדפדפן שולח בחזרה לכתובת הנוכחית, ולכן אותו סקריפט מציג את הטופס ומטפל בו: בדקו $_SERVER['REQUEST_METHOD'] === 'POST' וקראו את השדות מ-$_POST.

איך שומרים על ערכי הטופס אחרי שליחה ב-PHP?

הדפיסו את הערך שנשלח בחזרה לתוך השדה, אחרי escape: <input name="email" value="<?= htmlspecialchars($_POST['email'] ?? '') ?>">. לתיבת סימון הדפיסו checked, ולאפשרות ברשימת בחירה הדפיסו selected, כשהערך שנשלח מתאים.

איך מקבלים את הערכים של כמה תיבות סימון ב-PHP?

תנו לתיבות הסימון אותו שם שמסתיים בסוגריים מרובעים, name="topics[]". PHP שמה אז את הערכים המסומנים במערך, $_POST['topics']. תיבות לא מסומנות לא שולחות כלום, אז השתמשו ב-$_POST['topics'] ?? [] ובדקו כל ערך מול הרשימה שהצעתם.

למה רענון הדף שולח שוב את טופס ה-PHP שלי?

הבקשה האחרונה הייתה POST, ולכן הדפדפן חוזר עליה ברענון. אחרי שליחה מוצלחת, בצעו הפניה עם header('Location: /thanks.php'); exit; (תבנית Post/Redirect/Get). הרענון חוזר אז על GET לא מזיק.

האם בטוח להשתמש ב-$_POST ב-PHP?

לא, כל ערך ב-$_POST מגיע מהמבקר ויכול להיות כל דבר, כולל HTML, SQL או מערך במקום שבו ציפיתם למחרוזת. אמתו אותו בכניסה (filter_var, בדיקות אורך ורשימת היתר) ובצעו לו escape ביציאה (htmlspecialchars ל-HTML, prepared statements ל-SQL).

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל