本篇作為第六篇,我們來談談網頁世界中,
網頁與動態網頁互動的故事,也作為 Form 的延伸討論。
--------系列簡介--------
網站系統可說是現在最多學子與新人想要入門的一個領域,
這個原本屬於新興的領域,這幾年來也累積許多年的知識與 pattern ,
在有限的環境(stateless)與有限的伺服器端、瀏覽器資源下,
成為許多人進入程式的一塊入門鐵板(?)。
這個系列希望能夠就網站系統設計幾個門檻著手,
這是設定給初心者作為學習,給同好們作為回顧,
重新認識有關網站系統的每個環節。
昨天聊到 GET 與 POST ,稍微介紹 POST 與 GET 的差異,
並且用 php 寫了一個以 GET 資料判斷為基礎的 php 網頁。
今天接著下去,我們要順著繼續介紹 POST 的用法:
@ 昨天的內容
首先昨天的內文中我們題到 form 只要設定 method 屬性為 POST,
就可以送出 Post 呼叫,所以我們繼續承繼昨天晚上的內容作為今天的開始。
06_01_form.php
<title>表單練習</title>
<form method="POST">
<p>請輸入帳號 <input type="text" name="account" /> </p>
<p>請輸入密碼 <input type="password" name="password" /> </p>
<p><input type="submit" value="送出" /></p>
</form>
@ 所以平常到底該怎麼作登入呢?不應該再賣關子了吧?
登入,顧名思義要做帳號密碼的檢查囉,
所以我們必須要先有一些能夠通過檢查的帳號密碼,
假設筆者設定這裡有一組可以過關的帳號密碼,
是 test/1234 ,那我們該怎麼實作這樣的登入邏輯呢?
06_02_form.php
<title>表單練習</title>
<?php if(isset($_POST["account"])){
if(
$_POST["account"] == "test"
&& $_POST["password"] =="1234"
){
echo "歡迎登入, test !";
}else{
echo "帳號或密碼輸入錯誤";
}
}else{ ?>
<form method="POST">
<p>請輸入帳號 <input type="text" name="account" /> </p>
<p>請輸入密碼 <input type="password" name="password" /> </p>
<p><input type="submit" value="送出" /></p>
</form>
<?php } ?>
線上範例:
http://files.tonyq.org/iron5/06_02_form.php
當您輸入 test/1234 時它會呈現登入訊息,輸入其他輸入時,
他會判定您輸入登入錯誤呈現錯誤訊息。
@ 咦?我以為登入都需要連接到資料庫?
的確,正常狀況而言,我們會有超過一個以上的使用者,
也會有所謂的會員註冊讓使用者可以自由註冊、增加,
所以就不可能做這種直接寫死帳號密碼在程式碼裡面,進行判斷的作法。
但是本質上我們也只是透過驗證使用者輸入的資料,來驗證並將使用者登入,
有關與資料庫整合得部份,我們將在下一週(第二週)內容中介紹。
@ 為什麼我登入了以後,重新連結到那個網址,他沒有保持我的登入狀態?
如果您仔細看 06_02_form.php 這個範例,
您會發現他是使用 $_POST 的資料進行驗證,
當您送出資料時驗證,但他並未以任何形式儲存您的資訊,
所以當您再次瀏覽時,他並不知道您已經登入過。
您如果常使用一般的會員網站,可能會發現您一旦登入之後,
到您登出或者關掉瀏覽器之前,您的登入狀態一直被保存著。
@ 那應該要怎麼保存使用者的登入狀態?
在瀏覽器上保存使用者資料,有兩個儲存媒介可以用,
一個是把資料存放在瀏覽器上,稱為 cookie 。
另一個是把資料放在伺服器上,稱為 session 。
cookie 一般而言,對初學者而言不太會直接操作到他,
這是一個很難用、很複雜、很多限制、而且很不安全的一個元素。
所以我們只是帶過,請千萬不要用 cookie 存放使用者登入狀態,
不然使用者隨時可以偽造 cookie 的內容來躲過您的驗證。
(將內容加密後放到 cookie 可以改善這個問題,
但這是進階作法,在此處不多作介紹。
另外, Session 或 Cookie ,
儲存資料時都是會依據不同的使用者儲存不同資料的,
或者嚴格來說,根據電腦上的不同瀏覽器儲存資料的。
@ 我不想再聽理論了,那我到底該怎麼寫呢?
06_03_form.php
程式碼因為字數限制的關係,請直接參考
https://gist.github.com/3804422
http://files.tonyq.org/iron5/06_03_form.php
您可以先透過這個網址進行登入,之後再重新點一次上方網址重新連結,
您會發現您的登入狀態有被紀錄。
@ session_start(); ? $_SESSION ?
php 要使用 session 的話,必須先進行讀取的動作,
透過呼叫 session_start() 就可以進行這個行為。
(參考資料 http://us2.php.net/manual/en/function.session-start.php)
呼叫 session_start 後,
我們就可以自由在 session 放置任何資料。
以這個例子為例,我們以以下的語法進行登入狀態:
$_SESSION["login"] ="test" ;
表示在 session 裡面儲存 "login" 為名的資料,內容為 "test" 。
@ 為什麼要叫 login 呢?有特別含意嗎?
呃,沒有!
純粹是個人高興,事實上 session 要如何命名,
如何賦予這個變數意義,是完全取決於網站設計者的,
以筆者的經驗而言,像 "user" , "login" , "member" 等變數名稱都有人用。
不過要注意的是 Session 的內容,並不限於當前的頁面,
也會被保存同網站上的其他頁面,
也就是每一頁都可以存取這個 session 變數。
以一般而言,登入變數會是全站到處被引用的變數,
比方說,如果我設定 login 是判斷是否登入狀態的屬性,
那我每一頁需要判斷登入的頁面,都需要呼叫 login 這個屬性進行確認。
@ 既然是儲存,那它會儲存多久呢?
一般 session 大多的伺服器預設是關掉瀏覽器重新開啟後就會消失,
或者是超過三十分鐘後就會逾時。(session_timeout)
當然 session_timeout 的時間是可以對伺服器設定更長或更短的時間。
@ 登出又要怎麼作呢?
一般而言,我們會設定一個 php 頁面專門進行登出設定,
登出一般有兩種作法,這兩種作法都會清空 $_SESSION["login"],
第一種就是 $_SESSION["login"] = null; ,
將 login 資料清空,自然就無法用他判斷為登入狀態了。
但是更常被使用的是另一種作法是透過呼叫 session_destroy() ,
徹底清除該使用者存放於 session 上的所有資料。
如底下的例子
由於系統有字數限制,請使用者參考此一網址:
https://gist.github.com/3804416
這裡有筆者錄得線上操作說明影片:
http://www.youtube.com/watch?v=cXW3oYuLIx4
線上範例
http://files.tonyq.org/iron5/06_04_form.php
@ 登出跟登入只是一個屬性值的修改與判斷?會不會太隨便了?
事實上筆者一直再強調,網頁的基本原則與工具是相當簡單的,
只是因為在簡單的工具上要疊出複雜的狀態,所以才會變得複雜。XD
以登入這件事情而言,核心原理其實很單純。
@ 很多網站有 Remember me (記得我的登入) 的設計?
我們前面提過, session 只要關掉瀏覽器後就會消失,
所以下次進來就要重新登入,那所謂的"記得我"是怎麼回事呢?
基本上這個功能是會有一些安全性一律的,
這也是透過把資料紀錄在 "加密過後的" cookie ,
並且透過 cookie 可以設定過期期限的原理,來讓 cookie保存得比較久。
有關這個部份只是順帶一提,不在本次介紹的範圍內。XD
@ 既然 session 存放資料這麼好用,可不可以不要用資料庫,把所有資料都存在 session 就好?
所有的 session 的內容都會存在伺服器上的記憶體裡,
所以存越多資料就會造成更多伺服器的使用量,
當太多資料存在放 session 內造成記憶體不足時,
就無法服務更多的使用者囉!
所以要好好合理與節約的使用 session 的空間唷。
有關 session 的介紹就到這裡先暫時告一段落,
到第三週網頁功能實作時,我們會回頭提到更多有關的細節。:)
@ 有關於表單這個標籤,還有什麼我應該知道的呢?
如果您有發現的話,我們到目前為止,
送出的資料都是送給同一個頁面,
如 06_04_form.php 這個例子,(http://files.tonyq.org/iron5/06_04_form.php)
我們將接收資料的 php 程式碼寫在 06_04_form.php 裡面,
造成他有兩個狀態資料,但是其實我們也不一定只能送資料給自己唷!
@ action
form 標籤中有一個屬性叫 action ,可以指定送出的對象,
所以我們可以送資料給我們自己網站,甚至可以送給遠方的網站唷!
一般而言,為了避免資料邏輯的複雜、避免使用者重複送出表單跟很多理由,
我們會將處理資料的頁面與顯示資料區隔,
在這裡我們先簡單的從切出 form.php 與 login.php 開始,
進行 action 的練習吧。:)
由於系統限制字數,請使用者直接參考網址
https://gist.github.com/3804413
線上範例 http://files.tonyq.org/iron5/06_05_form.php
如果登入成功的話,從結果來看跟剛剛可以說是一模一樣,
但是我們已經將登入錯誤這件事情換到 login.php 去處理,
當我們登入成功後回 login.php 時,按下 f5 也沒有重新送出資料的提示,
因為我們已經完成表單的送出並進行了轉頁,
這樣也可以避免使用者一直按 f5 重新送出登入資訊。
@ 表單我們已經講了 method /action 兩個屬性,然後接下來呢?
有關表單的細節先講到這裡,我們接下來要回頭講可以輸入的東西。XD
我們說過表單由輸入元件構成的,所以我們要回頭再次討論,
有哪些輸入元件可以使用。
@ 基本元素列表與用法:
文字輸入框 <input type="text" value="預設值" name="參數名稱" />
密碼輸入框 <input type="password" value="預設值" name="參數名稱" />
長文輸入框(文字輸入區域) <textarea name="參數名稱">預設值</textarea>
以上是最簡單的,可以打字的輸入元件。
@ 下拉式選單(Select)
<select name="參數名稱">
<option value="結果一">選項一</option>
<option value="結果二">選項二</option>
<option value="結果三">選項三</option>
<option value="結果四">選項四</option>
</select>
特別注意一件事情是選項表現出的文字跟跟送出的內容可以完全不一樣,
以上例而言,當您選擇了"選項一" ,實際上表單是送出 "結果一" 給伺服器端。
對系統而言,我們呈現給使用者選擇的元素,
通常可能會含有更友善的說明文字或者能讓使用者瞭解的內容,
但是伺服器我們操作資料時,則可能是一些資料的識別值而非這些文字,
所以下拉式選單才有分為顯示文字跟輸入內容的差別。
這個概念也是相當重要的,後面介紹的 radiobox 跟 checkbox 也有這個特性。
@ 單選的 radio box
直接進範例!
請選擇類別:
<input type="radio" value="1" name="radiobox1" />類別一
<input type="radio" value="2" name="radiobox1" />類別二
<input type="radio" value="3" name="radiobox1" />類別三
<input type="radio" value="4" name="radiobox1" />類別四
線上範例:http://jsfiddle.net/Afb9L/
使用者可能會問,每個 radio input 都是獨立的,
他怎麼知道這是同一個選項,或是不同的選項呢?
答案是,如果他們的 "name" 一樣,就會視為同一個群組(group),
對 radio input 而言,同一個群組只能有一個被選擇。
@ 可多選的 checkbox
還是直接進範例!
請選擇興趣:
<input type="checkbox" value="1" name="checkbox1" />聽音樂
<input type="checkbox" value="2" name="checkbox1" />看電影
<input type="checkbox" value="3" name="checkbox1" />爬山
線上範例:http://jsfiddle.net/3zypb/
@ name 重複了?
讀者可能會記得我們曾經說過,表單送出資料時同一個 name 只能有一個資料,
那 radio/checkbox 這裡 name 有重複的情況,它會怎麼處理呢?
首先,radio input 只能擇一選取,所以它會送出被選中的 radio input 的 value。
checkbox 雖然可以多選,我們也確實會送出多筆 name 相同的資料,
但根據伺服器端實作(server side),通常我們只會取得最後一個送出的內容。
而這不是我們想要的,所以在使用 checkbox 時,要特別注意這件事情。
@ 那到底要怎麼處理呢?
這就要用到瀏覽器跟伺服器的一個秘密協議,
只要 name 的名稱後面是以 "[]" 結尾的話,
伺服器會將多個資料收集起來,成為一個複合資料唷!
如這個例子
<?php
//列印出所有取得的資料
foreach($_GET AS $key => $value){
echo "name:".$key.",value:".var_export($value)."<br />";
}
?>
<form>
請選擇興趣:
<input type="checkbox" value="1" name="checkbox1[]" />聽音樂
<input type="checkbox" value="2" name="checkbox1[]" />看電影
<input type="checkbox" value="3" name="checkbox1[]" />爬山
<input type="submit" value="submit" />
</form>
線上範例 http://files.tonyq.org/iron5/06_06_checkbox.php
然後我們可以在伺服器端在進行資料的取得,是不是很特別呢?
這也是新手入門時,常常撞牆的問題之一唷!
網頁的世界都是由許多小細節組成的,雖然我們的說明比較基本,
但是比較基本的說明才能夠確保每個重要的細節都在我們的掌握中。:)
網頁最需要的技巧並不是速成,而是一步一腳印的扎實細節。
@ 如果我只是想傳遞資料,但是不想讓使用者編輯呢?
比方說編輯留言時,我們可能需要告訴接收者,
我們現在在編輯的是那一則留言,但卻不希望使用者在畫面上看見這個識別值。
這時候我們可以使用 <input type="hidden" name="名稱" value="內容" />,
這個屬性會忠實地不呈現出任何輸入框,而是默默將資料帶給給接收的對象。
@ 還有其他的嗎?
昨天我們有提到檔案上傳,就在今天的尾巴介紹一下,
不過礙於時間有限我們就只簡要介紹,細節我們將在之後再進行更多介紹:
@ 檔案上傳:
檔案上傳過去在網頁世界中,是非常難做的一個環節,
問題不在 html ,而是對於伺服器端的限制。
讓我們慢慢道來...
@ 檔案是什麼?從伺服器端觀點。
檔案是一連串的數位串流(stream),由一堆位元組組成 (bytes),
合體之後,就可以變成有意義的資料(可能是文字資料,也可能是應用程式)。
到目前為止我們接觸到的,一個表單中通常只有文字資料,
所以 GET 方法才能透過網址進行操作。
所以當我們接觸到檔案上傳後,我們送出的資料除了純文字資料以外也就包含檔案部份,
原本的 key=value&key2=value2 這種送出資料格式,就不敷使用了。
而且更糟糕的事情是使用者可能會上傳多個檔案,
伺服器端一開始是沒有定義到會有這樣的操作,所以在這裡就變得很複雜。
於是我們就會使用一個能夠處理這種複雜資料的格式,稱之為 multipart 。
@ 什麼是 Multipart
有關於 multipart 的格式,詳情請參考 Javaworld TW 這篇討論文章。
http://www.javaworld.com.tw/jute/post/view?bid=6&id=56147&sty=1&tpg=2&age=0
簡言之,他是透過在上傳的內容中定義不同分區(part),
有些存放純文字資料,有些存放檔案資料,由伺服器端進行解析。
有些伺服器端語言,如JavaEE 或 ASP(非.Net) ,
早期並沒有辦法很容易的使用內建元素進行檔案上傳,
都需要倚賴第三方元件,就是因為這個格式真的相對複雜。
但在 Php 中並沒有這個問題,現在的 php 版本中已經能夠支援檔案上傳,
並有內建的 $_FILE 元素可以供作使用。
由於時間有限,詳細的 php 上傳範例建議參考 w3schools 這篇範例:
http://www.w3schools.com/php/php_file_upload.asp
主要的實作細節:
1.表單必須宣告 enctype="multipart/form-data" ,表示採用 multipart 格式傳送資料。
2.檔案上傳的標籤為 <input type="file" />
3.method 必須為 POST
其他以 PHP 系統而言沒什麼特別的,一切照舊,
在伺服器端檔案可以透過 $_FILE 存取,其他資訊則仍透過 $_POST/$_GET 存取。
底下是一些檔案上傳相關的概念
1.檔案上傳時很有可能會需要找地方存檔,你可以存在資料庫裡面,
也可以存在網站伺服器中的特定目錄下,視需要進行。
您可以取得使用者的檔案名稱,也可以視需要重新命名。
2.檔案上傳因為檔案可能很大,
但是網頁伺服器有預設每個使用者可使用的記憶體大小上限,
Php 預設是 2m ,如果超過的話會出現伺服器錯誤,
這個部份如果您有超過 2m 以上的上傳需求,請記得調整 php 設定。
詳細資料:http://twpug.net/modules/newbb/viewtopic.php?topic_id=1554
雖然檔案上傳實作很簡單,但檔案上傳是一個非常非常不安全與需要注意的功能,
筆者僅提供一些開發時非常嚴肅的小叮嚀:
* 一定要特別小心檢查不能讓使用者傳 php 檔案(或其他可被網站執行之檔案)上來,
最好針對上傳副檔名以白名單的方式進行檢查(只允許特定副檔名上傳)。
因為如果上傳在同一個網站,這個網站有支援 php 語法,
如果讓使用者能上傳一個 php 檔案,等於是將整個網站的權限暴露在使用者手上,
輕則砍站,重則所有資料遭竊,這點絕對不能大意。
網站伺服器主要是由副檔名判斷執行引擎,所以有關檔案上傳,
要對於網站能執行哪些應用程式,能上傳哪些內容,時時刻刻非常小心。
筆者就曾經親身看過某大學的資服處,作業的上傳系統能夠被使用者突破限制,
上傳 php 檔案並且能獲取存取權限,導致所有學生上傳的資料一覽無遺。
這也就無怪乎數年後會聽聞該系統發生大量資料集體遺失的事件。
* 要注意網站系統空間是否足夠、盡量要限制使用者上傳大小,
簡單來講,不要因為使用者上傳的圖而塞爆了。
今天的介紹大概就先到這裡,表單也是一個核心的操作,
我們之後仍然會一直頻繁的看到表單出場的,今天有沒有說明完全之處,
之後我們提到時會再進行說明與細節。
明天我們要介紹的東西會比較簡單一些,屬於網站的狀態與介紹,簡單歸簡單,
但卻是許多網站沒有仔細考慮、設計的區塊。
那麼,明天見囉;對了,也祝各位中秋節快樂。;)
checkbox 多選那邊有一張圖片是錯置的,但是目前發文系統我發表後一直無法修改文章,
出現不明原因的 "answer error",所以我把圖片補在這裡。
這是用來說明當 name 為 [] 結尾時,
checkbox 多選的功能在 php 伺服器端可以正常運作。