第 12 堂課 - 表單規劃與設計
- 12.0: 期末報告注意事項
- 12.1: 互動式網站:表單的製作
- 12.2: type="password" 的密碼功能與 PHP 密碼比對方式
- 12.3: 使用 session:藏個變數在 server 上
- 12.4: 單選與複選的功能
- 12.5: select 與 option 的下拉式選單
- 12.6: 課後練習
12.0: 期末報告注意事項
從本週開始,每週上課前,每組的組長應該要上台說明目前的進度規範,以利期末報告的資料進行。相關的期末報告規範,如同期末報告規範章節的說明。
12.1: 互動式網站:表單的製作
就如同我們要搜尋某些資料的時候,會前往 google 網站,或者是其他相關搜尋引擎一樣,我們必須要拿出『關鍵字』, 然後輸入到搜尋引擎網站提供的條列式框框內,按下 [enter] 或搜尋之後,我們所填寫的關鍵字就會被攜帶到搜尋引擎的伺服器, 透過伺服器端自己進行搜尋任務後,將搜尋到的結果以 HTML, CSS 或其他多媒體 (如 youtube) 的方式回傳到瀏覽器上, 這就是一個可以互動的網站流程。
要達成這個流程,有幾個基本步驟是得要進行的:
- Server 要提供某種功能,讓使用者可以在瀏覽器上面輸入關鍵字,或者是『選擇』或者是『勾選』關鍵資料
- Server 在使用者按下送出後,會將使用者在上一步驟進行的資料,以變數或某種方式取得這些關鍵資料
- Server 在透過這些關鍵資料的判定後,進行某些預設的動作 (程式碼階段),然後將輸出的結果以 HTML 或某種多媒體方式,回傳給使用者的瀏覽器。 回傳的資料可能是同一個網頁,也可能是另一個新的網頁。
你會發現,幾乎都是 Server 要進行的任務,因此,這些動作我們也經常稱為伺服器端的程式語言 (Server Side Language),在台灣最常用的可能就是 PHP 以及 .NET 了。
- 整體表單的製作
若你需要透過瀏覽器上傳資料到伺服器上,那麼網頁就得要有名為 form 的表單標籤。另外,上傳的資料主要有兩種方法,分別是:
- get:利用字串方式傳送,資料量較小,沒有加密,字串長度被限制在 512bytes 內。
- post:利用封包傳送模式,將資料打包成為封包狀態再傳送出去,除具有保密性之外,還可突破 512bytes 的限制。
此外,如同上面的說明,你上傳的資料是要給哪個檔案來處理?所以,一般來說,form 的常見的屬性有這些:
<form name="表單名(可忽略)" action="要將資料上傳到server端的哪個檔案處理" method="get或post"> .... </form>
- 常見的輸入資料,使用 input 與 type="text", type="submit"
大部分傳輸的資訊,主要是透過變數格式將資料上傳的。亦即你在網頁上面設計好變數名稱,讓使用者填寫後勾選變數內容,再將資料送上去。 因此,每個設計的按鈕或填寫的資訊中,都需要有個變數名字 (variable name) 來代表用戶所勾選的資訊。
用來提供使用者輸入參數的標籤中,最常用的就是 input 這個標籤。這個標籤可用的類型非常多!在這個小節我們先拿 type="text" 來做說明。 當你使用了上述的類型,還必須要指定這個輸入資料的『變數名稱』才行。一般常見的屬性有:
- input 屬性為 type="text" 時,常見的屬性設定:
- name="var" :就是輸入變數名稱
- value="content" :你可以提供一個『預設值』喔!就是打開瀏覽器預設幫你輸入好的一些資料
- placeholder="顯示提示資訊" :跟 value 不一樣,這個值會顯示在框框內,但是當使用者輸入任何資料後,這些資訊就會消失。
如果你的 name 設定為 var,而使用者在框框內輸入 abc 時,那麼在 server 上就會有『 $var = "abc" 』的資料可用喔!
但是,資料總是需要送出吧!這時就需要 type="submit" 的使用了!
- input 屬性為 type="submit" 時,常見的屬性設定:
- value="按鈕上面顯示的文字"
- 關於頁面主導覽列與子導覽列相關的設定與 title 資料修改:
- 將 $mypage 指定為 page2 這個內容;
- 將 $mypage2 指定為 page20 這個內容;
- 將抬頭資料 (h4) 內容修改成為類似『我的第一個表單』資料
- 建立一個表單,且表單使用的上傳方式為 get ,而上傳資料處理的檔案名稱為 unit12-1-2.php。
- 建立一個名為 my1stname 的 text 類型輸入框,且顯示『你的姓氏』資料,使用 placeholder 功能,在輸入框內出現『王』
- 建立一個名為 my2ndname 的 text 類型輸入框,且顯示『你的名字』資料,使用 placeholder 功能,在輸入框內出現『小明』
- 建立 submit 按鈕,顯示文字『上傳你的資料喔』!完成之後,先直接在網址列上面輸入檔名,應該會看到類似下方的網頁圖示才對喔:
如上所示,基本上,這個表單應該是建置妥當了!不過,不對啊!我們的主導覽列倒是正確沒問題,但是子導覽列不太對勁啊!沒錯!是這樣! 所以,我們需要延伸前一個章節的設置,讓整個網站的超連結能夠是正常的。前一個章節中,我們的規劃當中,針對第 12 章以後的資料, 都先集合在 unit12-full.php 當中,而在列表當中,則是以 include/menu_form.php 當中喔!
- 關於 unit12-full.php 網頁內容的修改,大致上可以這樣做:
- 將 $mypage 指定為 page2 這個內容;
- 將 $mypage2 指定為 page20 這個內容;
- 將抬頭資料 (h4) 內容修改成為『表單、PHP 與 javascript 的學習』
- 內容增加一些說明,最後加上超連結的頁面:
<?php include ("include/menu_form.php"); ?>
- 主導覽列的修改:打開 header.php 檔案,找到『表單與 PHP』的項目,將超連結改掉:
// 原本是這樣 <a class="nav-link" href="#">表單與 PHP </a> // 要改成這樣 <a class="nav-link" href="unit12-full.php">表單與 PHP </a>
- 子導覽列的修改:同樣打開 header.php 的地方,增加子導覽列的相關載入設定值:
<?php if ( $mypage2 == 'page12' ){ include ("include/menu_unit11.php"); } elseif ($mypage2 == 'page31' ) { include ("include/menu_html.php"); } elseif ($mypage2 == 'page11' ) { include ("include/menu_unit10.php"); } elseif ($mypage2 == 'page32' ) { include ("include/menu_css.php"); } elseif ($mypage2 == 'page20' ) { include ("include/menu_form.php"); } else { include ("include/menu_main.php"); } ?>
- 超連結內容的建置:就是建立 include/menu_form.php 這個檔案囉!其實從 menu_main.php 複製出來,然後內容會有點像這樣:
<h4 class="border border-top-0 border-right-0 border-left-0">表單、PHP、javascript</h4> <nav class="navbar bg-light "> <ul class="navbar-nav" style="width: 100%; "> <li class="nav-item" style="border-bottom: 1px solid silver"><a class="nav-link p-1" href="unit12-1-1.php">例題12.1.A:第一個表單</a></li> </ul> </nav>
未來你只要更改 include/menu_form.php 這個檔案的內容與超連結,你的檔案讀寫就可以直接在整體網站當中呈現!相當簡易的! 這就是透過 PHP 以及相關程式碼,就可以讓我們更輕鬆的製作出完整的網站了!
讓我們回到 12.1.A 的例題,現在你會知道 input 的 text 類別會出現一個輸入框~另外,如果現在你直接按下 submit 的按鈕,那就會出現...錯誤啦! 因為網址列會出現 unit12-1-2.php 然後說找不到該網址。另外,在網址列你也會發現你剛剛輸入的資料喔!如果你輸入姓氏為『Tsai』, 而名字為『Der Min』後,然後按下 submit,網址列會出現:
- unit12-1-2.php?my1stname=Tsai&my2ndname=Der+Min
在網址列當中,如果你使用的是 method="get" 的表單傳送方式,那麼所有的資料都會在網址列上面顯示。而這些顯示的資料主要是在問號 (?) 之後帶入, 我們剛剛設定了 input type="text" name="my1stname" 對吧,那麼當你在該輸入框內輸入 Tsai 之後,就會帶有『 my1stname=Tsai 』的資料囉! 這樣有清楚嘛?
如果你不想要在網址列上面顯示這些怪東西,那就將 method 改為 post 就好了。老實說,建議大家使用 post 方式啦! 在網址列上面若出現密碼...那可就糗了!
- 表單資料的取得
在開始表單的資料取得之前,讓我們回到到 9 章的 XSS 跨站攻擊手法!這實在很困擾對吧!為了避免這個困擾,因此,你必須要將之前撰寫的 function.php 整合到我們的網站內。因為所有的網頁都要讀到這個檔案內容才對,因此,我們很簡單的,直接在 header.php 裡面最上方增加這個 function 即可喔! 有點類似這樣的作法:
# 在 /www/web4/include/functions.php 的內容,大概剩下這樣就好了: <?php // 可以避免你的系統被 XSS 攻擊的基礎防禦,透過 HTML 字元編碼轉碼而來 $_GET && SafeFilter($_GET); $_POST && SafeFilter($_POST); $_REQUEST && SafeFilter($_REQUEST); $_COOKIE && SafeFilter($_COOKIE); function SafeFilter (&$arr) { if (is_array($arr)) { foreach ($arr as $key => $value) { if (!is_array($value)) { if (!get_magic_quotes_gpc()) { $value = addslashes($value); } $arr[$key] = htmlspecialchars($value,ENT_QUOTES); } else { SafeFilter($arr[$key]); } } } } ?>
接下來當然是要在 header.php 裡面去載入~載入的方法很單純,因為 header.php 與 functions.php 在同一個目錄下, 所以打開 header.php 之後,在最前面這樣寫:
# 在 /www/web4/include/header.php 最前面幾行這樣寫:
<?php
include ("functions.php");
?>
<!DOCTYPE html>
<html lang="zh-Hant-TW">
<head>
<title>鳥哥的 Bootstrap 網頁學習</title>
....
只要這樣做,未來所有呼叫 header.php 的網頁,都會主動的匯入 functions.php 這個檔案喔!很棒吧!不用一個檔案一個檔案去修改了!
回到 PHP 的資料取得,基本上,伺服器要取得來自用戶傳送的表單資料,可以透過三種指令來處理,分別是這樣:
- $_GET['變數名'] 來取得 get 上傳的資料;
- $_POST['變數名'] 來取得 post 上傳的資料;
- $_REQUEST['變數名'] 來取得 get 或 post 上傳的資料 (屬於萬用型);
以前一個例題來說,我們能夠使用 $_GET['my1stname'] 來取得輸入框的值。不過,如果你已經改成 post 方式了呢?若不確定使用哪種方式, 建議就用萬用的 $_REQUEST['my1stname'] 來取得即可。此外,一般來說,我們很少會在程式碼當中直接使用 $_REQUEST['var'] 的方式來處理, 而是透過類似『 $var = $_REQUEST['var'] 』設定一個同名的變數在 PHP 檔案中,後續就能使用該變數了!
- 請先將 unit12-1-1.php 轉存為 unit12-1-2.php
- 在檔案最上方,不過必須寫在 include('include/header.php') 之後,這樣才會擁有 functions.php 內部的資料轉換的功效。 將兩個輸入框的資料抓到此檔案中 (透過 $_REQUEST)
- 之後會輸出類似『你的全名是 "Der Min Tsai" 或 "Tsai, Der Min" ,有這兩種基本的姓名寫法囉!』。 當然,unit12-1-1.php 的輸入內容會影響這個檔案的輸出才對。
- HTML5 支援的表單檢查輸入功能 - required="required"
如果你在 unit12-1-1.php 當中,保持空白然後按下送出會怎樣?當然輸出的名字部份就是一片空白!你當然可以在 unit12-1-2.php 當中透過 PHP 的 if 來處理, 不過,如果能夠在使用者送出之前就來檢查,不是更好?沒錯~但是以前要達成這樣的功能都得要透過 javascript 撰寫程式碼來判斷,但新的支援 HTML5 的瀏覽器 (對!你沒看錯!是需要瀏覽器支援的!),可以加上『 required="required" 』在 input 標籤內,達到讓用戶一定要填寫的目的! 所以,在你的 unit12-1-1.php 裡面,讓兩個 input 都加上述的功能,然後嘗試不輸入任何字串就上傳看看,你應該就會看到如下的警告囉:
不必額外撰寫 javascript 的表單檢查程式,直接加上這玩意而就好了!相當簡單吧!
- 適合表單使用的 bootstrap 4 表單樣式
既然我們都使用了 bootstrap 來設計我們的網站了,那麼,表單當然也應該要使用 bootstrap 的風格樣式才對吧?沒錯!可以! 那麼 bootstrap 4 的表單樣式怎麼處理?你可以先參考底下的連結來設計:
基本上,你應該使用 form-group 來將整個文字與 input 輸入框整合在一起,不同的說明與輸入框需要在不同的 form-group 裡面才對。 而傳送的資料,則可以使用按鈕 (btn btn-primray) 之類的類別來做成 bootstrap 的按鈕樣式。
- 將抬頭資料 (h4) 內容修改成為類似『加入 bootstrap 4 樣式的表單』資料
- 在每一個 input 與資料之間,加上底下的樣式:
<div class="form-group"> 你的姓氏: <input type="text" class="form-control" required="required" name="my1stname" placeholder="王" /> </div>
- 在最後一個傳送的按鈕,不需要加上 form-group 的類別,但是可以加上 btn btn-primary 或其他顏色的按鈕, 讓你的傳送按鈕可以變的跟 bootstrap 風格一致。
在預設的情況下,bootstrap 的 form-control 類別會將整個畫面佔好佔滿,就是等於 display:block 的樣式啦!不過,某些情況下, 你可能不想要佔滿整個版面,這時該如何是好?
- 使用不佔滿整個版面的 input 設計: form-inline
上面的範例看得出來其實使用 display: block 就可以搞定。那如果要變成內部元件呢?就是使用 inline 不就得了!? bootstrap 的表單提供了 form-inline 的類別,使用到 <xxx class="form-inline" ...> 當中,就可以讓你的 input 輸入框變成內部元件了!
- 將抬頭資料 (h4) 內容修改成為類似『加入 bootstrap 4 樣式的表單-使用 inline 的內部元件樣式』資料
- 將原本每個獨立的 div 內的類別,從 form-group 的柱狀樣式,改成 form-inline 即可。
看你喜歡怎麼樣的風格都可以!鳥哥個人覺得直接使用 form-group 的樣式看起來比較大方!如果你覺得表單資料量太寬, 設計好寬度 (width: nnnpx) 應該就能夠設計好整個版面了!
- 常見的 input 類型
除了剛剛談到的 type="text" 的類型之外,經常用來作為使用者輸入的 input 標籤類型大致上有這幾種:
- type="text" :提供一列的使用者輸入資訊
- type="password" :提供一列的使用者輸入資訊,但輸入資訊在瀏覽器上以 * 取代(不會被偷看)
- type="radio" :提供使用者選擇單選的選項
- type="checkbox" :提供使用者選擇複選的選項
- type="date" :提供萬年曆的選單功能
- type="search" :提供一個搜尋列 (可用 bootstrap 的漂亮小工具取代)
- type="hidden" :提供一個不在螢幕上出現的隱藏輸入框功能 (如果不想要給人家知道的變數,可以用這個)
- type="file" :用來製作上傳檔案的功能。
- type="submit" :用來讓使用者按下『上傳』的按鈕功能
底下幾小章節,我們就來討論討論常用的幾個 input 類型吧!
12.2: type="password" 的密碼功能與 PHP 密碼比對方式
某些時刻我們可能需要讓用戶輸入密碼的,這時就得要有這個功能才行!因為它才有辦法讓使用者輸入的密碼是藏起來的, 不會像 type="text" 一樣的文字外顯。此外,如果是需要註冊的情況之下,你有需要製作兩個密碼欄位,讓使用者輸入兩次, 然後在後台檢查兩個密碼是否相同,免得使用者自己註冊完畢後,卻發現密碼打字錯誤導致永遠無法登入啊!
- 將抬頭資料 (h4) 內容修改成為類似『使用密碼的輸入框-隱藏密碼』資料
- 更改兩個 form-group 內的的文字內容,變成『請輸入密碼』跟『重複一次密碼輸入』
- 將 input 的類型從 text 變成 password,同時給予變數名稱分別改為 pw1, pw2
- 至於 form 的 action 作用檔案請設定為 unit12-2-2.php。form 請務必使用 post (想想為什麼?)
- 第一個密碼一定要有輸入資料才能上傳
上面的第 (d) 點談到你要使用 post 方法才行,為什麼不要使用 get 呢?這是因為傳輸網址的過程中,所有的資料都會以明碼的方式寫在網址列, 因此,如果你是用 get 傳輸,那麼當你按下傳送之後,你的密碼就會被明白的顯示在網址列上面了!如果被有心人士竊取,這就不是好事囉! 所以,當然是要使用 post 才行的!這樣理解吧?
此外,type="password" 與 type="text" 幾乎沒有什麼不一樣,顯示的效果也相同,唯一不同的,就像上述的圖示, 使用者輸入資料時,會被用黑點 (某些瀏覽器會顯示 * 號) 取代,這樣就可以避免使用者輸入密碼時,被旁邊的人不小心拍照儲存了...
- 密碼的長度限制與比對:透過 strlen() 判斷變數的字元長度
有資料上傳就一定要抓下來~同時,也要比較一下 (1)密碼長度與 (2)兩筆密碼資料是否是正確才行。至於密碼長度是否足夠可以使用 PHP 的變數長度取得, 用法如下:
strlen($var) // 這個函數會顯示 $var 這個變數的字元數 if ( strlen($var) < 6 ) {...} // 代表字元數目小於 6 所需要進行的行為
- 將抬頭資料 (h4) 內容修改成為類似『取得密碼資訊,並判斷密碼是否合理』
- 設定兩個變數,名為 $pw1, $pw2,分別取得來自前一個網頁的密碼資料。同樣記得,這兩個變數資料的取得,也是必須要在 include ('include/header.php') 之後喔!
- 使用 if () {...} elseif () {...} else {...} 的語法來設計底下的任務:
- 若 $pw1 的字元個數小於 6 個 (不含 6 個),就顯示『密碼長度不足』
- 若 $pw1 與 $pw2 的內容不相同,輸出 "兩次輸入密碼不同"
- 若一切正常,則顯示『您的兩次密碼輸入為相同!』
你會發現到,其實 server 也可能記憶的是密碼的明文狀態,這樣不是很好吧!我們希望『當你忘記密碼時,除了重新設定密碼之外, 應該是沒有人會知道你的密碼明文』這樣的環境才是對的。所以,改天你問某網站,說你的密碼忘記了,可以幫你找回來嘛? 對方說 OK 沒問題時,你得要注意了...
- 透過 hash('密碼格式', '密碼明文') 產生加密的密碼
那麼如何針對密碼明文加密呢?透過 PHP 的運算函式庫即可。基本上建議直接使用 sha256 這個密碼加密機制,然後透過 hash() 函式即可。 更多加密的演算法不在本文的介紹範圍內,而且,除非是惡意網站,否則,使用 sha256 應該稍微可以滿足大家對於密碼紀錄的需求了!
$pw1 = hash('sha256', "$_REQUEST['pw1']");
這樣就可以推算出你的密碼,此時這串加密過的資料就可以放在資料庫,或者是暫時記憶到程式碼當中。由於本系資料庫的課程是在未來才會教, 因此,在底下的練習當中,我們將密碼藏在程式碼當中,當然,這個密碼是加密過的!然後,透過讓使用者輸入帳號與密碼, 來判斷使用者輸入的資訊是否為正確。
echo "<p>你輸入的明碼是:" . $pw1 . "</p>"; echo "<p>加密過後的密碼是:" . hash('sha256', $pw1) . "</p>";當你由 unit12-2-1.php 輸入正確的密碼後,應該會出現如下的資訊:
你可以將上面的那一串密碼複製到 PHP 程式碼當中,但是切記不要留下任何明碼資訊啊!即使這是我們單純用來測試的系統而已。 現在,透過這個加密過的字碼字串,我們就可以來設計一個讓使用者輸入正確帳號密碼的判斷式了。而且還是使用單一格式的方式來處理的。 意思是,任何情況下,都可以登入的意思。方法可以是透過一個名為 action 的變數,若變數內容為 login 時,就開始進行登入判斷, 否則就提供登入的帳號密碼輸入框。大致上的運作流程是這樣的:
透過上述的流程,我們還可以將註冊的程序整合到驗證的程序當中!這真是太愉快了!
- 將抬頭資料 (h4) 內容修改成為類似『驗證帳號與密碼的正確性』
- 修改輸入框格:
- 第一個輸入的方框中,將提示字串改成『請輸入你的帳號:』,將類型改為 text,將名稱改為 myuser,將 placeholder 改為『 account name 』,並且為必須 (required)
- 第二個輸入的方框中,將提示字串改為『請輸入你的密碼:』,保留 password 類型,名稱改為 mypw,placeholder 改為『 password 』, 必須是 required
- 以 type="hidden" 建立一個名為 action 的變數,且該變數的值為 login 。你需要注意的是,這個 input 的標籤, 也必須要在 form 的標籤內才行!
- form 的 action 為自己 (請用 $_SERVER['PHP_SELF']) ,使用 post 方式 (再次思考,為什麼?)
之後點選一下送出,看看出現的情況如何。然後接下來請繼續完成底下的資料處理:
- 修改 include/functions.php 這個檔案的內容,在該檔案的最底下增加這些資料:
- 透過 if ( isset ($_REQUEST['action']) ) 之類的方法,判斷是否存在這個變數?如果有的話,就使用 $action = $_REQUEST['action']; 設定好 $action ,否則就指定 $action = ""; 這樣的空字串資料。
- 判斷 $action 是否為 login ,如果 $action == 'login' 之後,就進行如下的設定:
- 增加 $okuser 與 $okpw 兩個變數,內容分別是『 teacher 』與剛剛加密過後的那串密碼亂碼,這個部份未來要以資料庫取代的!目前先這樣!
- 取得 $myuser 與 $mypw 為前頁傳過來的內容,其中 $mypw 必須使用 hsah 搭配 sha256 的方式進行編碼
- 判斷 $okuser == $myuser 以及 $okpw == $mypw ,兩者要同時成立,才算判定成功:
- 成功時,設定 $loginstate = true,且 $loginmeg = "歡迎使用者光臨"
- 如果失敗,就讓 $loginstate = false,且 $loginmsg = '登入失敗' 的模樣
- 打開登入的 unit12-2-3.php 檔案,在 form 前面增加如下的程式碼:
<article> <?php if ( isset ($loginstate) ) { echo $loginmsg ; } if ( !isset($loginstate) || $loginstate == false ) { ?> <form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>">
因為需要有登入才會有 $loginstate,因此,可以透過這樣的機制,來回覆使用者的登入資訊。至於後面的資料,則是當沒有登入行為, 或者是登入失敗時,才給予顯示註冊畫面的意思! - 接下來修改 myuser 的方框,內部增加一個 PHP 的程式碼,大致的內容是這樣的:
<?php if ( isset($myuser) ) echo "value='$myuser'"; ?>/>
根據這個判斷式,我們可以讓使用者查看到之前輸入的可能有錯的帳號資訊!使用者就不用一直重複打字~尤其是密碼輸入錯誤的朋友, 就可以省略重複輸入帳號的窘境了!
若登入正確,才會出現如下資訊
反正,無論如何,密碼還是得要加密的好!不要讓你的密碼明文被竊取啊~
12.3: 使用 session:藏個變數在 server 上
我們可以將變數藏在 Server 上面喔!為啥要這樣做?想想看,如果你通過身份驗證後,轉到其他頁面去,若其他頁面並不知道你已經通過驗證了, 那麼豈不是每一頁都需要重新驗證才能夠順利的瀏覽呢?或者是你得要將帳號與密碼藏在網頁中,一頁一頁的交待下去呢?這樣真的是很不妥當! 因為帳號密碼藏在網頁中,難保被其他人所竊取。那,我們將通過驗證的資料藏在 Server 上不就好了?這樣 server 會知道你已經驗證, 所以在這次的連線過程中,就無須再重複驗證囉!呵呵!這就是常用的功能!
- 啟動 session 功能: session_start()
啟用這個將變數藏在 server 的功能,叫做是『 session 』,而要啟動它,就得要在網頁上面加上『 session_start() 』這個函數才行! 而藏在 Server 上的變數,可以在某個網頁底下使用底下的方式來設定:
$_SESSION['var1'] = 'value1';
經過設定好之後,在這次連線過程中,你在同一個 server 的任何一個網頁內,都可以透過呼叫 $_SESSION['var1'] 來取得當初的設定值。 除非你使用底下的方式取消該設定值,否則該設定會持續存在的!
session_destroy();
- 準備好整體網站的登入頁面: login.php
我們原本在網站上面就有預留一個『登入』的這個時候,該按鈕就可以生效了!假設我們預計登入的頁面就稱呼為 login.php 好了, 所以,在右上方的那個按鈕的連結,就請連結到 login.php 去~可能我們需要這樣處理一下:
- 打開 header.php 處理右上方超連結:來到『登入』的按鈕處,將超連結寫到 login.php 這個檔案
- 打開 login.php:
- 將抬頭資料 (h4) 內容修改成為類似『使用者登入』
- 修改 $mypage 與 $mypage2 ,都是 page0 這個內容
- 修改 hidden 的 action 內容,將 value 改成 login_session 這個值。
- 將『上傳你的資料』改成『驗證你的身份』
- 將『 isset ($loginstate) ... $loginstate == false ) { 』那一段,換成這樣:
// 如果有登入訊息,就顯示登入訊息! if ( isset ($_SESSION['loginmsg']) ) { echo $_SESSION['loginmsg'] ; } // 如果沒有登入者帳號資訊,代表沒有登入過,於是開始顯示登入畫面 if ( ! isset ( $_SESSION['username']) ) {
- 打開 include/function.php :
- 將『 if ( $action == "login" ) {...} 』那一段全部複製,然後貼上在檔案最後面
- 修改 $action == "login_session" 的模樣
- 增加一個名為 $okname 的變數,內容為你的暱稱,例如『 $okname = "鳥哥老師" 』
- 將 $loginmsg 的部份,歡迎的用戶從 $myuser 修改成為 $okname
- 在驗證成功的地方,將原本的資料通通刪除,變成底下的模樣:
$_SESSION['username'] = $okname; $_SESSION['loginmsg'] = "歡迎 $okname 再次光臨"; header('Location: '.$_SERVER['REQUEST_URI']);
- 如果驗證失敗,就變成底下的模樣:
$_SESSION['loginmsg'] = "登入失敗!";
這樣就讓使用者登入了!而且,當你再次按下『登入』的按鈕時,你看不到登入的畫面!而是直接顯示已經登入的訊息! 這就是透過 SESSION 來設定連線變數的功能!要記得,目前這次的連線,所有的頁面基本上都會存在兩個 $_SESSION 變數囉!
- 根據 $_SESSION['var'] 切換按鈕功能
既然我們已經登入了,那也就是說,所有的頁面其實都會存在一個名為 $_SESSION['username'} 的 Server 變數,而且這個變數預設會維持到這次連線中斷。 那麼,我們都登入了,如上圖,右上角還是『登入』的按鈕,那就不太對勁吧!能不能讓該按鈕在未登入時顯示『登入』,在登入時顯示『登出』呢? 其實也不難,因為,我們知道,登出其實就是 session_destroy() 而已!那怎麼進行呢?製作一個名為 logout.php 的頁面來測試看看。
- 打開 header.php 處理右上方超連結:找到主導覽列右側的兩個小圖示處:
<?php if ( isset ( $_SESSION['username']) ) { ?> <ul class="navbar-nav"> <li class="nav-item"> <!-- 個別的導覽列上的按鈕 --> <a class="nav-link" href="#"><i class="fas fa-user-cog"></i> <?php echo $_SESSION['username'] ?></a> </li> <li class="nav-item"> <!-- 個別的導覽列上的按鈕 --> <a class="nav-link" href="logout.php"><i class="fas fa-sign-out-alt"></i> 登出</a> </li> </ul> <?php } else { ?> <ul class="navbar-nav"> <li class="nav-item"> <!-- 個別的導覽列上的按鈕 --> <a class="nav-link" href="#"><i class="fas fa-user-plus"></i> 註冊</a> </li> <li class="nav-item"> <!-- 個別的導覽列上的按鈕 --> <a class="nav-link" href="login.php"><i class="fas fa-sign-in-alt"></i> 登入</a> </li> </ul> <?php } ?>
相關的按鈕圖示可以前往 font awesome 網站查學看看。 - 打開 logout.php 檔案:
- 將 h4 的標題更改為類似『 使用者登出系統 』的字樣
- 如果不存在 $_SESSION['username'] 就導向『登入』畫面,若存在,才顯示登出表單:
<?php if ( ! isset ($_SESSION['username']) ) { echo "<script>window.location.replace('login.php');</script>"; } else { ?> <form method="post" action="index.php"> <p>是否確定登出系統?</p> <input type="submit" class="btn btn-primary" value="確定登出系統" /> <input type="hidden" name="action" value="logout" /> </form> <?php } ?>
- 打開 functions.php 檔案,在檔案最後面加上這一段登出的方式:
// 登出,就直接刪除 session 的方法 if ( $action == "logout" ) { session_destroy(); header('Location: index.php'); }
現在分別進行底下的動作測試看看:- 在 logout.php 裡面,真的按下『登出』,會不會回到首頁;
- 再次點選 logout.php,會去到哪個頁面?
- 再次登入,有沒有確定可以順利登入?
這個部份的登入/登出,就是一般我們會用到的會員系統裡頭最基礎的功能了!其實這邊有很多種寫法,這只是其中一種比較簡單的方式! 等到同學未來到大三學到資料庫,就會更仔細的玩一玩 PHP,屆時你會學到更多更有趣的內容!
12.4: 單選與複選的功能
某些時刻我們會考慮到使用者可能只有滑鼠或者是觸控點擊的功能,如果要讓使用者 keyin 一些資料,恐怕不太合理。 如果你的資料是已經可以列出條列清單讓用戶選擇的話,那麼就可以透過單選或者是複選的方式,來讓使用者挑選,這樣比較合理些!
- type="radio" 的單選功能
就像有時候我們去填寫問卷的時候,問卷會問我們的性別一樣,通常生理性別大部分只有一種,因此,這時候就是所謂的單選了! 單選使用的輸入框類型是 radio 這個類型!一般來說,應該會像這樣子:
<input type="radio" name="gender" value="male" /> 男生 <input type="radio" name="gender" value="female" /> 女生 <input type="radio" name="gender" value="unknown" /> 不便告知
不過,對於結果的呈現中,使用者『一定要點到那個小圓圈』實在很討厭~能否點選文字部份也能選擇呢?可以的,透過 label 即可! 例如,上面的文字可以改成這樣:
<label><input type="radio" name="gender" value="male" /> 男生</label> <label><input type="radio" name="gender" value="female" /> 女生</label> <label><input type="radio" name="gender" value="unknown" /> 不便告知</label>
如果加上 bootstrap 的樣式,則有底下兩種基本的樣式:
// 使用柱狀的情況 <div class="form-check"> <label class="form-check-label"> <input type="radio" class="form-check-input" name="optradio">Option 1 </label> </div> // 成為內部元件的情況 <div class="form-check-inline"> <label class="form-check-label"> <input type="radio" class="form-check-input" name="optradio">Option 1 </label> </div>
現在,讓我們來持續使用心理測驗題,不過,過去的心理測驗題,其實答案也都只是藏在網頁裡面,所以,老實說,厲害的傢伙透過分析網頁原始碼, 就可以全部知道答案了!我們現在則是讓使用者透過表單填寫答案後,才由 server 告知正確的答案,因此,原始網頁上面是沒有解答的! 就讓我們來玩一玩:
- 將抬頭資料 (h4) 內容修改成為類似『心理測驗 -- Server 版』
- 先對抬頭的標題與資料部份做個設定,加入 h3 以及 p 的處理,先排列好
- 以類似底下的方式做好三個選項,其中,value 的內容部份請分別填入 1, 2, 3 這三個項目,至於項目文字則依據題目說明處理。
<div class="form-check"> <label class="form-check-label"> <input type="radio" class="form-check-input" name="mycheck" value="1" /> 醜臉的百公斤大胖子 </label> </div>
- 將原本 hidden 的類型,action 保留,但是值改成 mycheck 這樣的內容。
- 最底下加一個 div,使用 alert-info 的類別,加上最小 100 像素高度、內部留白 10 像素、邊框 1px solid grey 這樣的格式。
當然啦,還沒有上傳吧!現在,開始嘗試來上傳資料~不過,這個上傳需要重新處理某些資料才行!請打開 unit12-4-1.php,然後繼續進行如下的任務:
- 在最上方的 header.php 底下,透過 isset($_POST['action']) 的存在與否,來處理底下的任務:
if ( isset ( $_POST['action'] ) ) { if ( $_POST['action'] == 'mycheck' ) { $mycheck = $_POST['mycheck']; if ( $mycheck == '1' ) { $mymsg = "<p><strong>你的勾心鬥角指數50%。</strong>.."; } } ... }
將三種數值的結果通通貼上去!以確認相關的回應資料! - 到答案說明的地方,換成這樣的資訊去顯示:
<?php if ( isset ($mymsg) ) { echo $mymsg; } else { echo "點一下來看答案啊!"; } ?>
因為會有這樣的問題,所以,一般來說,我們會透過底下的程式碼,來處理這個問題!
- 在 unit12-4-1.php 裡面,針對每一個 input,增加底下的程式碼:
<input type="radio" class="form-check-input" name="mycheck" value="1" <?php if ( $mycheck == 1 ) echo "checked='checked'"; ?> /> 醜臉的百公斤大胖子
這就是所謂的『單選』,要注意的是那個 input 裡面的『 name 』需要相同!如果不相同,代表有不同的題目的意思, 那就無法進行唯一的單選任務囉!
- type="checkbox" 的複選功能
有時候,我們需要的資料會是『複選』的模樣!就是多選題啦!不是單選而已。這時候就需要 checkbox 來處理了。只是, checkbox 的使用要很注意, 通常為了方便,我們會使用『陣列』來處理這些東西!在 php 當中,陣列的使用是這樣的:
$arrayname[] = 'value0'; $arrayname[] = 'value1';
因為 php 的陣列預設編號就是從 0 開始,如果沒有特別需求,你也可以經由底下的方式取得陣列的總數量:
count($arrayname);
現在,讓我們來設定一個陣列,讓我們收集最近常吃的午餐有哪些:
$myfood[] = '好記廣東粥'; $myfood[] = '大師父便當'; $myfood[] = '鐵路便當'; $myfood[] = '新丼飯堂'; $myfood[] = '正樽當歸鴨'; $myfood[] = '熊記牛肉麵'; $myfood[] = '郭媽媽淳牛肉'; $myfood[] = '八方雲集'; $myfood[] = '大雅便當'; $myfood[] = '忠味麵'; $myfood[] = '賣噹噹'; $myfood[] = '學餐啦'; $totalfood = count($myfood);
這樣就有一個具有 12 個午餐店的選擇了!現在,讓我們的同學們,挑挑看中午最愛吃哪幾家店這樣!
- 將抬頭資料 (h4) 內容修改成為類似『午餐的選擇 -- Server 版』
- 在 h4 底下新增一行字,說明一下你想要選擇的午餐有哪些這樣。
- 先將上述的 12 個午餐陣列資料寫入 unit12-4-2.php 的上方,在 include('include/header.php') 之後,且亦加上 $totalfood 的計算陣列的數量功能。
- 在主要內容部份,以前一章談到的 for 迴圈,搭配底下的展示碼為依據,來將你的迴圈整個展示出來:
<?php for ( $i=0; $i<$totalfood; $i++ ) { echo " <div class='form-check'> <label class='form-check-label'> <input type='checkbox' name='okfood[]' value='$i' />$myfood[$i] </label> </div> "; } ?>
很建議你也要嘗試檢視原始碼,看一下整個迴圈輸出的結果較佳!
如上所述,在 HTML 的 input 標籤中,如果是『陣列』的項目,可能得要加上中括號,亦即 name='okfood[]' 的由來。 不過,在 PHP 的環境下,要取得這個陣列的名稱,只要輸入 $_REQUEST['okfood'] 即可喔!不需要加上中括號的! 接下來,讓我們將使用者勾選的資料給顯示出來吧!
- 在 form 裡面隱藏一個 input ,設定名稱為 action 而值為 mychceck 這樣的資料。
- 在 form 的上面增加底下這一小段程式碼,讓我們選擇的『數值』可以被帶入陣列,以取得正確的便當店名:
<?php if ( $action == "mycheck" ) { echo "<div class='border' style='padding:10px'>你選擇的店家主要有:<br />"; $okfood = $_REQUEST['okfood']; foreach ( $okfood as $i ) { echo $myfood[$i] . ", "; } echo "</div>"; } ?>
這就是所謂的『複選資料』,同一個題目有很多的答案時,就是這樣處理囉!
- 透過 rand(最小,最大) 數值處理
你總是會有不知道吃什麼比較好的時候,那麼能不能透過上述的方法,讓系統自己幫我們找出要吃的店呢? 可以的!那就是透過亂數即可!
$aifood = rand(0,count($okfood)-1)
上述的程式是說,從 0 到 okfood 陣列的個數中,隨機找出一個整數,這個整數就是亂數建議的食堂囉!就是這麼簡單!
- 將抬頭的 h4 修改成『 午餐的選擇 -- Server 亂數選擇版 』
- 將剛剛 unit12-4-3.php 裡面的選擇輸出資料部份,改成底下這樣:
<?php if ( $action == "mycheck" ) { echo "<div class='border' style='padding:10px'>在你選擇的店家中,我們建議你可以前往這家店:<br />"; $okfood = $_REQUEST['okfood']; $foodnu = count($okfood); if ( $foodnu >=1 ) { $aifood = rand(0,$foodnu - 1); echo $myfood[$okfood[$aifood]]; } echo "</div>"; } ?>
如上所述,我們都沒有看到之前選擇的項目啊!其實,跟單選一樣,如果該資料是預設值,可以給予 checked='checked' 這樣的設計! 因此,你可以在上面的 unit12-4.4.php 裡頭使用 for 迴圈列印資料時,將內文做這樣的修改:
<?php for ( $i=0; $i<$totalfood; $i++ ) { $thechk = ""; if ( isset ($okfood) ) { for ( $j=0; $j < $foodnu; $j++ ) { if ( $i == $okfood[$j] ) { $thechk = "checked='checked'"; } } } echo " <div class='form-check'> <label class='form-check-label'> <input type='checkbox' name='okfood[]' $thechk value='$i' />$myfood[$i] </label> </div> "; } ?>
上述資料處理完畢之後,前往 web 界面然後點選幾個店家之後,並按下傳送資料。你應該會看到如下的畫面圖示:
12.5: select 與 option 的下拉式選單
有時候,某些『單選』的資料量太龐大,如果將所有的單選資料全部列出在同一個畫面上,似乎很混亂!而且,版面的排版也容易跑掉,醜醜的。 這個時候,使用下拉式選單來替換單選的項目,應該是還不錯的選擇。我們可以透過 select 搭配 option 來處理這樣的事情即可! 他的基本語法是這樣的:
<select class="form-control" name="myselect"> <option value="value1">value1</option> <option value="value2">value2</option> <option value="value3">value3</option> </select>
現在,假設每個使用者可以選擇一個店家,然後讓程式紀錄下來,最終如果有資料庫的話,將所有的人選擇的項目統計起來, 就可以知道大家最想去的店家排行了!不過目前僅有簡單的 PHP 程式而已。底下就讓我們來模擬這個行為吧!
- 將 h4 抬頭資料改寫為『 使用者上傳選擇的店家資料 』
- 在顯示列中的 form ,增加如下的設定:
<div class="form-group"> 訂餐者: <input class="form-control" type="text" name="username" required='required' placeholder="填寫你的姓名" <?php if ( isset($username)) echo "value='$username'"; ?> /> </div>
- 將原本的複選刪除,改成底下的程式碼:
<div class="form-group"> 選擇你的店家: <select class="form-control" name="okfood"> <?php for ( $i=0; $i<$totalfood; $i++ ) { echo "<option value='$i'>$myfood[$i]</option>"; } ?> </select> </div>
如果點選了店家的下拉式選單,會有如下的樣式喔:
接下來,讓我們們繼續來處理資料的上傳吧!要注意喔!那個 action 的變數值為 mycheck 還是請保留喔! 可以延續使用囉!
- 將資料抓取的部份,修改成底下這樣:
<?php if ( $action == "mycheck" ) { echo "<div class='border' style='padding:10px'><p>訂餐者資訊如下:</p>"; $okfood = $_REQUEST['okfood']; $username = $_REQUEST['username']; echo "訂餐者: $username <br />"; echo "選擇店家: $myfood[$okfood]"; echo "</div>"; } ?>
未來加上資料庫,配合 SQL 語法,你就可以寫動態網站囉!
12.6: 課後練習
關於本章節的課後練習部份:
- (20%) 完成今天所有的課堂上面的實做,並且所有的連結都必須在子導覽列上出現才行。
- (80%) 完成多使用者登入的功能:
- 前提:我們已經知道,登入可以從 web4/login.php 這個檔案登入處理,另外,實際登入現在已經在 web4/include/functions.php 裡頭的 login_session 裡面處理了。另外,從複選的功能裡面,我們也發現了可以具有陣列的功能存在。
- 使用陣列的方式建立三組加密的帳密,分別是(帳號/密碼/暱稱):
- teacher/Let′sgo/鳥哥老師
- student/happylearn/鳥哥學學我
- guest/donotkick/訪客用戶
- 在帳號的比對方面,你需要用類似底下的程式碼來處理了:
for ( $i=0; $i<count($okuser); $i++ ) { if ( $okuser[$i] == $myuser && $okpw[$i] == $mypw ) { ... } }
最終完成的結果中,學長可以使用 3 種身份分別測試登入,當然!你也可以再加你自己的帳號與密碼資訊喔!