سرفصل‌های آموزشی
آموزش PHP
چک کردن دیتای فرم‌های HTML با دستورات شرطی در زبان PHP

چک کردن دیتای فرم‌های HTML با دستورات شرطی در زبان PHP

فرم‌ها یکی از بخش‌های لاینفک وب اپلیکیشن‌ها هستند به طوری که برای ثبت‌نام، نظردهی و کارهای به مراتب بیشتری مورد استفاده قرار می‌‌گیرند. یکی از کاربردهای دستورات شرطی if که در آموزش گذشته با آن آشنا شدید، چک کردن دیتا فرم‌های HTML است که در این آموزش پس از آشنایی با نحوهٔ ساخت یک فرم نظرسنجی، یاد خواهید گرفت که چگونه با استفاده از دستورات if else می‌توان داده‌های ارسالی توسط کاربران را مورد بررسی قرار داده و از پر بودن کلیهٔ فلیدها اطمینان حاصل کرد.

اگرچه آموزش ترفندهای HTML خارج از سرفصل‌های آموزشی این دوره است، اما به منظور درک بهتر سازوکار فرم‌ها، در ادامه اقدام به نوشتن یک فرم نظردهی کرده سپس بخش‌های مهم آن را توضیح خواهیم داد:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Simple HTML Form</title>
</head>
<body>
  <form action="handle_form.php" method="POST">
    Your name: <br>
    <input type="text" name="name"><br>

    <br> Your email: <br>
    <input type="text" name="email"><br>

    <br> Your gender: <br>
    <input type="radio" name="gender" value="male"> Male<br>
    <input type="radio" name="gender" value="female"> Female<br>

    <br> Your age: <br>
    <select name="age">
        <option value="10-20">10-20</option>
        <option value="20-30">20-30</option>
        <option value="30-40">30-40</option>
        <option value="40-50">40-50</option>
        <option value="50-60">50-60</option>
        <option value="60-70">60-70</option>
        <option value="70-80">70-80</option>
    </select>

    <br> Your comments: <br>
    <textarea name="comment" rows="15" cols="50"></textarea><br><br>
    <input type="submit" value="Submit">
  </form>
</body>
</html>

کدهای فوق را می‌توانیم در فایلی تحت عنوان form.html ذخیره سازیم.

    به خاطر داشته باشید
آنچه در نامگذاری این فرم حائز اهمیت می‌باشد این است که با توجه به اینکه هیچ نوع کد PHP داخل این فایل نیست، به سادگی می‌توانیم پسوند html. را برای آن انتخاب کنیم (البته اگر این فایل را با پسوند php. نیز ذخیره سازیم هیچ مشکلی پیش نخواهد آمد)

در اسکریپت فوق دو اتریبیوت وجود دارد که بسیار حائز اهمیت هستند که عبارتند از action و method. کاری که Value (مقدار) در نظر گرفته شده برای اتریبیوت action انجام می‌دهد این است که حاوی آدرس صفحه‌ای است که اسکریپت PHP مد نظرمان که قرار است دیتای فرم را هَندل کند در آن قرار دارد؛ اتریبیوت method هم -همان‌طور که از نامش مشخص است- متد یا روشی که قرار است این فرم هَندل شود را مشخص می‌سازد که یا می‌تواند GET باشد و یا POST.

آشنایی با تفاوت‌های متدهای GET و POST

مقدار اتریبیوت method یکی از متدهای پروتکل HTTP است که هر کدام خصوصیات و بالتبع کاربردهای خاص خود را دارند که در ادامه بیشتر پیرامون این تفاوت‌ها بحث خواهیم کرد.

متد GET دیتای ارسالی توسط فرم HTML را در قالب یکسری Name-Value (نام-مقدار) از طریق URL برای اسکریپت مد نظر ارسال می‌کند. به عنوان مثال داریم:

http://www.example.com/script.php?name=Behzad&gender=M

همان‌طور که در URL فوق مشخص است، پس از نام اسکریپت مد نظر (script.php) یک علامت ? قرار داده سپس دیتای مد نظر خود را در قالب دو جفت Name-Value که با علامت & از یکدیگر جدا شده‌اند ارسال کرده‌ایم.

در واقع، دو پارامتر به نام‌های name و gender داریم که به ترتیب مقادیر Behzad و M برای آنها در نظر گرفته شده است. به طور کلی، مزایای متد GET عبارتند از:

  • پس از استفاده از این متد، می‌توان URL را در مرورگر بوکمارک کرد.
  • بدون از دست رفتن دیتا، می‌توان صفحه را رِفرش کرد.

در عین حال، این متد یکسری نقاط ضعف هم دارا است که عمده‌ترین آنها عبارتند از:

  • دیتای حجیمی را نمی‌توان با استفاده از این متد به سمت سرور ارسال کرد.
  • دیتای ارسالی به سمت سرور قابل رؤیت است.
  • زمان‌هایی که امنیت دیتای ارسالی زیاد است، این متد کارایی ندارد.

با این تفاسیر، می‌شود گفت که می‌توان از متد GET زمان‌هایی استفاده کرد که صرفاً بخواهیم یکسری داده‌ها را از دیتابیس فراخوانی کنیم و یا یکسری پارامتر را از صفحه‌ای به صفحهٔ دیگر پاس (انتقال) دهیم.

در مقابل، از متد POST زمان‌هایی می‌توانیم استفاده کنیم که:

  • حجم دیتای ارسالی بسیار زیاد است.
  • امنیت داده‌ها بالا است.

در فرم فوق، از آنجا که قصد داریم دیتای نسبتاً زیادی برای اسکریپتی که در فایل handle_form.php وجود دارد ارسال کنیم، از متد POST استفاده کرده‌ایم.

    به خاطر داشته باشید
نوشتن مقادیر اتریبیوت method هم می‌تواند با حروف کوچک باشد و هم با حروف بزرگ؛ به عبارت دیگر، هم می‌توان به صورت get/post و هم GET/POST آنها را نوشت اما روال نوشتن با حروف بزرگ است.

چیزی که در ارتباط با فرم فوق باید مد نظر قرار داد، نام یکسان برای فیلد gender است. در حقیقت، نام یکسان gender برای هر دو فیلد مربوطه این امکان را برای کاربر فراهم می‌آورد تا بتواند فقط و فقط یکی از آیتم‌ها را انتخاب کند. در حقیقت اگر بخش مربوط به انتخاب جنسیت در فرم فوق را به صورت زیر ریفکتور کنیم:

<input type="radio" name="gender_one" value="male"> Male<br>
<input type="radio" name="gender_two" value="female"> Female<br>

همان‌طور که مشاهده می‌شود، برای اتریبیوت‌های name از نام‌های مختلفی همچون gender_one و gender_two استفاده شده که این امکان را در اختیار ما قرار خواهند داد که بتوانیم در آن واحد هر دو گزینه را انتخاب کنیم.

یکی از مزیت‌های زبان PHP این است که تعامل بسیار خوبی با زبان HTML دارد و همین قضیه موجب گردیده کار با آن بسیار آسان گردد. در ادامه، برای اینکه بتوانیم دیتای فرم را با استفاده از زبان PHP هَندل کنیم، فایلی می‌سازیم تحت عنوان handle_form.php حاوی کدهای زیر:

<?php
$name = $_REQUEST['name'];
$email = $_REQUEST['email'];
$gender = $_REQUEST['gender'];
$age = $_REQUEST['age'];
$comment = $_REQUEST['comment'];

echo "Your name is: $name;<br> Your email is: $email;<br> Your gender is: $gender;<br> Your age is: $age;<br> Your comment is: $comment";

پیش از این، به طور خیلی خلاصه با مفهوم متغیرهای اصطلاحاً Super Global آشنا شدیم که یکی از آنها REQUEST_$ است که این وظیفه را دارا است تا کلیهٔ دیتای ارسال شده توسط فرم‌ها (حاوی متدهای GET و POST) را در خود ذخیره سازد.

نکته‌ای که در اینجا باید توجه ویژه‌ای به آن کرد این است که مقادیر (کلیدهای) در نظر گرفته شده در REQUEST_$ دقیقاً می‌بایست با مقادیری که برای اتریبیوت name در فیلدهای فرم انتخاب کردیم، یکسان باشد. به عبارت دیگر، اگر برای فیلد مرتبط با ایمیل کاربر مقداری همچون email را برای اتریبیوت name در نظر گرفته باشیم، در اسکریپت PHP خود نیز باید به شکل زیر به آن دست یابیم:

$_REQUEST['email']

و چنانچه این کلید را با حرف اول بزرگ و به شکل Email بنویسیم، با هشداری به صورت زیر مواجه خواهیم شد:

Notice: Undefined index: Email in /var/www/sokanacademy-php-course/03/03-form/handle_form.php on line 2

حال کلیهٔ فیلدهای فرم فوق را پر کرده و روی دکمهٔ Submit کلیک می‌کنیم:

Your name is: Behzad;
Your email is: behzad.moradi@example.com;
Your gender is: male;
Your age is: 30-40;
Your comment is: This is my first comment.

می‌بینیم که توانستیم کلیهٔ مقادیر فرم را بگیریم. کاری که در اینجا انجام داده‌ایم، صرفاً پرینت کردن مقادیر روی صفحه است و این در حالی است که می‌توان این مقادیر را برای کاربر خاصی ایمیل کرد و آیا آن‌ها را در دیتابیس ذخیره ساخت که در فصول آینده بیشتر پیرامون این مباحث توضیح خواهیم داد.

    نکته

در ارتباط با تست این فرم می‌بایست دو نکته را مد نظر داشته باشیم و آن هم این که محل قرارگیری فایل handle_form.php دقیقاً‌ می‌بایست در همان پوشه‌ای باشد که فایل form.html قرار گرفته است. همچنین این فرم را باید از طریق لوکال‌هاست تست کرد و در غیر این صورت، اسکریپت PHP اجرا نخواهد شد.

اکنون قصد داریم یک بار دیگر فرم نظرسنجی فوق را اصطلاحاً Submit (ارسال) کنیم اما بدون این که هیچ‌کدام از فیلدهای این فرم را پر کنیم یا انتخاب کنیم:

Notice: Undefined index: gender in /var/www/sokanacademy-php-course/03/03-form/handle_form.php on line 4
Your name is: ;
Your email is: ;
Your gender is: ;
Your age is: 10-20;
Your comment is:

می‌بینیم که در مورد فیلد جنسیت با ارور مواجه شده و الباقی فیلدها هم خالی نمایش داده می‌شوند اما این در حالی است که به سادگی و با استفاده از دستورات شرطی if می‌توان از پر بودن کلیهٔ فیلدها اطمینان حاصل کرد. 

همان‌طور که در فایل HTML فوق مشخص است، فیلد مرتبط با سن کاربر دارای یک مقدار پیش‌فرض (۲۰-۱۰) است و قصد داریم تا در ادامه، آپشنی همچون «لطفاً یک گزینه را انتخاب نمایید» در معرض دید کاربر قرار دهیم. برای این منظور داریم:

<select name="age">
    <option value="default">Please choose an option</option>
    <option value="10-20">10-20</option>
    <option value="20-30">20-30</option>
    <option value="30-40">30-40</option>
    <option value="40-50">40-50</option>
    <option value="50-60">50-60</option>
    <option value="60-70">60-70</option>
    <option value="70-80">70-80</option>
</select>

در حقیقت، آپشنی با مقدار default و عنوان Please choose an option پیش از سایر آپشن‌ها در نظر گرفته‌ایم و این در حالی است که اگر اکنون صفحه را رِفرش کنیم، خواهیم دید که کاربر بایستی روی این فیلد کلیک کرده و یکی از گزینه‌ها را انتخاب نماید. حال برای اطمینان حاصل کردن از پر بودن کلیهٔ‌ فیلدها، ابتدا اسکریپت قرار گرفته داخل فایل handle_form.php را تکمیل کرده سپس به تفسیر آنها خواهیم پرداخت:

<?php
$name = $_REQUEST['name'];
$email = $_REQUEST['email'];
$gender = $_REQUEST['gender'];
$age = $_REQUEST['age'];
$comment = $_REQUEST['comment'];

if (empty($name)) {
    echo "Please fill in the name field <br>";
} else {
    if (empty($email)) {
        echo "Please fill in the email field <br>";
    } else {
        if (! isset($gender)) {
            echo "Please fill in the gender field <br>";
        } else {
            if ($age == 'default') {
                echo "Please fill in the age field <br>";
            } else {
                if (empty($comment)) {
                    echo "Please fill in the comment field <br>";
                } else {
                    echo "Your name is: $name;<br> Your email is: $email;<br> Your gender is: $gender;<br> Your age is: $age;<br> Your comment is: $comment";
                }
            }
        }
    }
}

اکنون یک بار بدون این‌ که هیچ‌کدام از فیلدها را پر کنیم، روی دکمهٔ Submit کلیک می‌کنیم:

Notice: Undefined index: gender in /var/www/sokanacademy-php-course/03/03-form/handle_form.php on line 4
Please fill in the name field 

می‌بینیم هشداری دریافت می‌کنیم مبنی بر این که اندیس gender نامعلوم (Undefined) است. برای رفع این مشکل، دو راه‌کار پیش رو داریم؛ راه‌کار اول، استفاده از اپراتور @ است که پیش از این به معرفی آن پرداختیم. در واقع، کاری که این اپراتور انجام می‌دهد این است که جلوی نمایش هشدارها و اخطارهایی که توسط مفسر PHP ایجاد می‌شوند را می‌گیرد (گفتیم که تا حد ممکن از این اپراتور برای چنین کاربرد‌هایی نباید استفاده کرد چرا که در پروژه‌های بزرگ ممکن است فرایند دیباگینگ پروژه را با دردسر مواجه سازد):

$gender = @$_REQUEST['gender'];

از این پس، دیگر تحت هیچ‌ عنوان اخطاری در معرض دید کاربر قرار نخواهد گرفت. به عنوان راه‌کار دوم، می‌توان از یک دستور شرطی استفاده کرد:

if (isset($_REQUEST['gender'])) {
  $gender = $_REQUEST['gender'];
}

گفتیم که فانکشن ()isset چک می‌کند ببیند که آیا یک متغیر Set (مقداردهی) شده است یا خیر؛ اگر اصطلاحاً Set شده بود، مقدار true و در غیر این صورت، مقدار false را باز می‌گرداند. هر کدام از ترفندهای فوق را که استفاده کنیم، پس از کلیک کردن روی دکمهٔ Submit بدون پر کردن هیچ کدام از فیلدها، خروجی به صورت زیر خواهد بود:

Please fill in the name field 

حال برسیم به تفسیر دستورات شرطی که پر بودن فیلدهای فرم را چک می‌کنند. در اولین دستور شرطی که در خط هشتم قرار دارد، از فانکشنی تحت عنوان ()empty استفاده کرده‌ایم که به صورت Built-in (از پیش تعریف شده) در هستهٔ زبان PHP نوشته شده است. کاری که متد ()empty انجام می‌دهد این است که چک می‌کند ببیند آیا پارامتر ورودی‌اش خالی است یا خیر؛ اگر خالی بود، مقدار true را باز می‌گرداند و در غیر این صورت، مقدار false اصطلاحاً return خواهد شد.

از آنجا که ما هیچ چیزی در فیلد مربوط به نام وارد نکرده‌ایم، مسلماً این فانکشن مقدار true را باز می‌گرداند و وارد بلوک if می‌شویم و عبارت Please fill in the name field چاپ می‌شود (لازم به ذکر است برای این که تمامی هشدارها در خطوط مجزایی در معرض دید کاربر قرار گیرند، از تگ <br> در پایان هر پیام استفاده کرده‌ایم).

اما اگر کاربر فیلد مربوط به نام را پر کرده‌ باشد، وارد بلوک else اولین دستور if می‌شویم و مجدداً با استفاده از یک بلوک if else دیگر در خط یازدهم، پر/خالی بودن فیلد مربوط به ایمیل را چک کرده‌ایم.

در ارتباط با متغیر gender$، باتوجه به این که این متغیر از جنسی نیست که بتواند پر/خالی باشد (در واقع این فیلد از نوع Radio است)، برای چک کردن آن از فانکشن ()isset به همراه اپراتور ! استفاده کرده‌ایم. به عبارت دیگر، دستور داده‌ایم که اگر این متغیر مقداردهی نشده بود، وارد بلوک مرتبط با if شویم.

در ادامه، به متغیر age$ می‌رسیم. نکته‌ای که در ارتباط با Value (مقدار) این متغیر وجود دارد این است که اگر آن را با تابع ()empty چک کنیم، این تابع همواره مقدار false را نمایش خواهد داد (به عبارت دیگر، همواره نشان خواهد داد که این فیلد مقداردهی شده است). دلیل اینچنین مسئله‌ای این است که ما به عنوان مقدار اولین آپشن این فیلد، استرینگ default را مورد استفاده قرار داده‌ایم.

در چنین مواقعی، بایستی از اپراتور مقایسه‌ای == استفاده کنیم. همان‌طور که ملاحظه می‌شود، در خط هفدهم گفته‌ایم اگر مقدار متغیر age$ برابر با استرینگ default بود، ارور نمایش داده شود و در غیر این صورت، به مرحلهٔ بعد برویم.

در نهایت، پر/خالی بودن متغیر comment$ را هم در خط بیستم چک کرده و اگر کاربر متنی وارد فیلد مربوطه کرده باشد، وارد بلوک else مرتبط خواهیم شد و مقادیر کلیهٔ متغیرها چاپ خواهند شد:

Your name is: Behzad;
Your email is: behzad.moradi@example.com;
Your gender is: male;
Your age is: 30-40;
Your comment is: This is my first comment.

به نظر می‌رسد که با ذکر مثال فوق، با کاربرد دستورات شرطی در ارتباط با چک کردن فیلدهای فرم‌های HTML آشنا شده باشید. آنچه در این مثال حائز اهمیت می‌باشد این است که این اسکریپت یک آسیب‌پذیری جدی دارد. به عبارت دیگر، اگر کاربری پیدا شود و با استفاده از ابزار Inspect Element مرورگر اقدام به تغییر کدهای HTML کرده و مثلاً مقداری همچون oops را برای یکی از فیلدهای مرتبط با جنسیت یا سن در نظر بگیرد چه‌طور؟

<select name="age">
    <option value="oops">Please choose an option</option>
    <option value="10-20">10-20</option>
    <option value="20-30">20-30</option>
    <option value="30-40">30-40</option>
    <option value="40-50">40-50</option>
    <option value="50-60">50-60</option>
    <option value="60-70">60-70</option>
    <option value="70-80">70-80</option>
</select>

می‌بینیم که فرد هکر در اولین آپشن به جای استرینگ default از استرینگی همچون oops استفاده کرده است و اگر کلیهٔ فیلدهای فرم را پر کرده و فیلد مرتبط با سن را دست‌نخوره باقی بگذاریم، فرم اصطلاحاً Valid (معتبر) بوده و اسکریپت ما بدون نمایش دادن هیچ‌ هشداری تا پایان اجرا می‌گردد:

Your name is: Behzad;
Your email is: behzad.moradi@example.com;
Your gender is: male;
Your age is: oops;
Your comment is: This is my first comment.

در واقع، ما در دستور شرطی مرتبط با متغیر age$ صرفاً گفته‌ایم که اگر مقدار این متغیر برابر با استرینگ default بود به کاربر هشدار داده شود (این قضیه برای فیلد مرتبط با مشخص کردن جنسیت هم کاملاً صادق است).

در چنین مواقعی، با استفاده از دستورات شرطی مختلف و همچنین یکسری فانکشن‌های شخصی‌سازی شده حاوی الگوریتم‌هایی به منظور ارسال دیتای صحیح به سمت سرور،‌ می‌توان جلوی این‌گونه رفتارهای کاربران (هکرها) را گرفت.

online-support-icon