程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

運行PHP

編輯:關於MYSQL數據庫
在本章的剩余部分中,將解決我們在第1章中為自己定下的目標:

  對於學分保存方案,需要編寫一個允許我們輸入、編輯測試和測驗分數的腳本。

  對於歷史同盟,需要開發一個有關美國總統的聯機測驗,使之成為交互式的,以便不做任何事情就可以為這個Web 站點的訪問者產生試題。

  我們也想允許歷史同盟成員聯機編輯它們的目錄項,使信息維持最新並減少我們自己編輯項的數量。

  每個腳本都產生多個Web 頁面,並依靠在創建頁面中嵌入的信息來在腳本的調用之間進行通信。

  輸入學生分數

  在本節中,我們將把注意力轉向學分保存方案。我們Web 站點上的這個區域的URL 是http://pit-viper.snake.Net/gp/,應該為它編寫一個簡短的主頁i n d e x . p h p,下面的頁面就正在做這件事。它包括了與第7 章編寫的score_browser 腳本的連接,因為這個腳本適合於學分保存方案。

  

  現在讓我們考慮如何設計和實現腳本score _ e n t r y. p h p,它將讓我們輸入一組新的測試或測驗分數,或者修改一組已經存在的分數。後者的性能對於處理由於生病或者其他原因缺席(或者,放棄這個想法以免輸入分數失敗)造成考試或測驗比其他學生晚的學生的分數是必要的。分數項腳本的概要是這樣的:

  1) 最初的頁面代表一系列已知的登記事件,並允許選擇一個事件或者指定應該創建的新事件。

  2) 如果選擇創建一個新事件,腳本就給出允許指定日期和事件類型的頁面。創建這個事件記錄之後,腳本重新顯示事件列表頁面來顯示這個新事件。

  3) 當選擇了事件後,腳本給出在頂部(事件ID、日期、類型)顯示事件信息的分數項頁面,後接每個學生一項的列表。對於新事件,項將是空白的。對於已存在的事件,項將顯示每個學生已存在的分數。選擇提交按鈕時,分數輸入到score 表中。

  腳本需要執行幾個不同的操作,這意味著我們需要從一個頁面到另一個頁面周而復始地傳遞狀態變量,以便腳本在每次調用時能夠知道假設要做什麼。在PHP 中很容易做到這一點,因為PHP 處理作為URL 參數傳遞的變量,並把它們轉換為與參數具有相同名稱的變量。例

  如,可以在腳本URL 的末尾對參數action 進行如下編碼:

  http://pit-viper.snake.Net/gp/score_entry.PHP?action=value

  當調用score _ e n t r y.php 時,參數action 作為變量$action 來編碼,這樣就可以直接訪問它了。這也適用於格式中的域。設想一個包括域name 和address 的表格,當客戶機傳遞表格時,Web 服務器就調用腳本訪問表格的內容。腳本能夠找出通過檢查變量$name 和$address 的值而輸入到表格中的值。對於包括許多域的表格,全部給出唯一的命名是有困難的。PHP 很容易地把數組在表格中傳入和傳出。如果使用了如x [ 0 ]、x[1] 等等的域名,則PHP 把它們作為$x 數組的元素進行編碼。可以將這些元素作為$ x [ 0 ]、$x[1] 等等來訪問。

  我們通過使用頁面中的action 參數,可以將信息從score _ e n t r y.PHP 腳本的一個調用傳送到另一個調用,並在腳本中用變量$action 檢查它的值。腳本的框架是這樣的:

  

  

  變量$action 可以取若干值,我們已在switch() 語句中測試過了(為避免在腳本中使用文字的數字,可以用PHP 的define() 構造來定義常量)。PHP switch() 語句與它在C 中相應的部分相類似。在score _ e n t r y.PHP 中,它用來確定采用什麼操作,並且調用實現這個操作的函數。

  檢查一下每次處理一個操作的函數。第一個函數d i s p l a y _ e v e n t s ( ),檢索來自MySQL的event 表的行並加以顯示。表的每一行都列出了事件ID、日期和時間類型(測試或測驗),還有編寫事件ID作為可以選擇用來修改事件分數的連接:

  

  表中的連接用$PHP_SELF 來構造。這個變量包括了腳本自己的U R L,它為腳本再次調用自己提供了一個方便的方法。然而,請注意函數開始處的global 行:

  global $PHP_SELF;

  在PHP 函數中,全局變量是不可訪問的,除非顯式地聲明要使用它們。沒有global 行,$PHP_SELF 將被看成局部變量(因為我們沒有將值賦給它,因此是空的)。在函數內部,使用global 來訪問依靠URL 參數或者作為表格域傳遞到腳本中的參數也是必需的。

  用來生成表的函數display_cell() 與第7 章編寫的同名DBI 函數相類似。PHP 版本如下:

  

  如果在display_events() 給出的表中選擇了“ New Event ”連接,則腳本通過操作SOLICIT_ EVENT進行再次調用。它引發了對solicit_event_info() 的調用,這個函數顯示了允許輸入新事件信息的表格:

  

  由solocit_event_info() 生成的表格包括輸入數據的編輯域、指定新事件是測試還是測驗的兩個單選按鈕、Submit 按鈕。當遞交表格時, ADD_EVENT 操作將調用score _ e n t r y. p h p。調用add_new_event() 函數在event 表中輸入一個新的行:

  

  在add_new_event() 中,我們使用global 訪問在新事件項表格中使用的域值( date 和type,用變量$date 和$type 訪問)。做出最低限度的安全檢查,確定數據為非空白之後,在event 表中輸入一個新記錄。輸入這個事件記錄之後,主程序將再次顯示事件列表,這樣就可以選擇新事件並開始輸入分數了。

  函數display_scores() 為給定的事件查找已存在的分數,並列出顯示他們的表格,包括學生姓名:

  

  display_scores() 用於檢索所選事件的分數信息的查詢並不是表之間的簡單連接,因為它不會為事件中沒有分數的學生選擇行。特別是,對於新的事件,連接會選擇無記錄,這就有了一個空項表格!我們使用LEFT JOIN 強迫為每個學生檢索行,無論學生是否在score表中已經有了分數。與display_scores() 用來檢索來自於MySQL的分數記錄相類似的查詢背景,已在3 . 8 . 2節“檢查表中未給出的值”中給出了介紹。那裡的查詢只選擇缺失分數,這裡的查詢只選擇特殊事件的分數。

  分數在表格中使用了有名稱的域進行編碼,如score [n],這裡的n是student_id 的值。當表格送回Web 服務器時,PHP 將這些域轉換為$score 數組的元素,我們可以訪問數組元素以恢復表格的內容。

  當完成輸入或者編輯分數,並提交給表格後,ENTER_SCORES 操作調用score _ e n t r y. p h p,並且調用函數enter_scores() 處理表格信息:

  

  學生ID 的值和相關的分數通過迭代PHP 的each 函數的$score 數組來獲得。每個分數處理如下:

  如果分數是空白的,則表明什麼也沒有輸入,但是我們還要試圖刪除這個分數,以免它以前曾經存在(也許以前我們為缺席的學生錯誤地輸入了分數)

  如果分數不是空白的,就對值進行一些根本的確認。用函數trim() 去掉前後的空格之後,如果剩余部分是空白或者整數,就接受這個結果。然而,表格值通常作為字符串來編碼,因此不能用is_long() 或者is_int() 檢查值是否為整數。即使值只包括數字,這些函數也會返回FA L S E。既然這樣,最好用模型匹配操作。如果字符串從開始到結束每個字符都是數字,則下面的測試為T R U E:

  ereg("^[0-9]+$",$str)

  如果分數檢查完畢,我們就將它加到score 表中。查詢使用REPLACE 而不用INSERT,因為我們可能替換了已存在的分數而不是輸入一個新的分數( REPLACE 在兩種情況下都適用)。

  注意score _ e n t r y.php 腳本。現在所有的分數項和編輯項都能從Web 浏覽器執行。一個明顯的缺點是:腳本沒有提供安全措施,連接到Web 服務器的任何人都可以對分數進行編輯。以後,我們用編輯歷史同盟成員項編寫的腳本來說明這個腳本所采取的簡單確認方案。也可以使用PHPLIB 程序包來提供更完善的確認。

  美國總統測驗

  歷史同盟Web 站點的目標之一就是用它給出測驗的在線版本,這類似於同盟在時事通信“美國編年史”的兒童部分發表的一些測驗。實際上我們創建了president 表,因此對基於歷史的測驗可以用它作為問題的來源。為了給出這個測驗,我們將編寫稱為pres_quiz.PHP 的腳本。

  基本的想法是隨機挑選一個總統,問一個關於他的問題,然後請求用戶回答並且察看答案是否正確。為了簡單一點,可以把主題限制為詢問總統出生在哪裡。另外一種簡單的衡量就是以多個選擇的格式給出這個問題。這對用戶來講很容易,他只需從一組選擇中挑選一個,而不用將之鍵入等待回應。這對我們來講也是容易的,因為我們不需做任何棘手的匹配字符串來檢查用戶可能鍵入的內容,而只需對用戶的選擇和我們尋找的值做一個簡單的比較。

  顯示這個測驗的腳本必須執行兩個函數。第一個,對於它最初的調用,將從p r e s i d e n t表中查閱信息來生成並顯示一個新的問題。第二個,如果腳本已經被調用是因為用戶正提交一個回答,那麼就需要檢查這些答案並給出一些反饋信息來指出它是否正確。如果正確,腳本會生成並顯示一個新的問題。如果回答不正確,將再次顯示同一問題。

  為了生成這些問題,我們將使用MySQL3.23 中出現的一個ORDER BY RAND()特性。使用這個函數就能從p r e s i d e n t表中隨機地進行行選擇。例如,為了隨機地挑選總統的姓名和出生地,查詢將執行這樣的操作:

  

  name是選擇的總統的名字,出生地是問題“總統出生在哪裡?”的正確答案,我們還需要給出一些錯誤的選擇,可用類似的查詢:

  

  從這個查詢的結果中,我們選擇了與正確答案不同的最前面的四個值。發布這個查詢並檢索結果的函數如下:

  

  為了給出測驗問題的信息,我們使用了顯示總統姓名、一組列出可能選擇的單選按鈕和一個S ub m i t按鈕的表格。這個表格需要做兩件事情:必須對客戶機給出測驗信息;當用戶提交回答時必須將信息傳送回Web 服務器,以便檢查回答是否正確。

  為了安排表格執行這些操作,我們使用了隱藏域把測驗信息包括在表格中。把域稱為name、place 和c h o i c e,它們代表總統的姓名、出生地和一組可能的選擇。使用implode() 連接值和特殊字符,這樣,這些選擇可以很容易地作為單個字符串來編碼(我們需要特殊字符,以便如果需要重新顯示問題時可以用explode() 分離字符串)。顯示表格的函數如下:

  

  函數hidden_fIEld( )為表格中的隱藏域編寫了H T M T :

  

  當用戶做出選擇並提交表格時,答案作為response 域值在發送回Web 服務器的表格內容中編碼。我們可以通過檢查變量$ name、$place 和$choice 發現name、place 和choice 域的值。這也給了我們一個方法,指出是否是第一次調用腳本,或者用戶是否給以前顯示過的表格提交了回答,如果是第一次調用則不會設置那些變量。這樣,通過檢查其中一個變量,腳本的主體就決定了應該做的事情:

  

  我們仍然需要編寫check_response() 函數來將用戶的回答與正確答案做比較。我們將正確答案在表格的place 域進行編碼,用戶的回答則在表格的response 域進行編碼,因此我們所要做的就是比較$place 和$ r e s p o n s e。在比較結果的基礎上,我們提供了一些反饋信息,之後每次都生成顯示一個新的問題,或者再次顯示相同的問題:

  

  這樣,我們就做完了。將pres_quiz.PHP 的鏈接加到歷史同盟主頁上,參觀者可以做一下這個測驗來測試他們的知識。

  歷史同盟聯機成員項的編輯

  最終的腳本e d i t _ member.PHP 允許歷史同盟成員編輯他們自己的聯機項。無論何時,成員都可以校正或者更新他們的成員信息,而不必向同盟部提交這些更改。這個性能使成員目錄總是保持最新的,而且減少了秘書的工作量。

  我們需要采取的一個防范措施就是:除了該項目的成員之外,防止任何其他人修改項目。這意味著我們需要一些安全性的表單。作為一個簡單的身份確認表單的示范,我們將使用MySQL存放每個成員的口令,並要求成員提供正確的口令以訪問腳本給出的編輯表單。該腳本操作如下:

  當初次調用時,edit_script.PHP 給出包括成員ID 和口令域的表單。

  當提交初始表單時,腳本用成員ID 作為關鍵字尋找相關的口令來搜索口令表。如果口令相符,腳本將從member 表中查找成員項,並顯示要編輯的內容。

  當提交編輯過的表單後,我們就用表單的內容更新項。

  e d i t _ member.PHP 的框架如下所示:

  

  

  當然,我們還需要一些口令,一個簡單的方法就是隨機地生成它們。下面的語句建立member _ pass表,然後,通過從隨機數中生成MD5 校驗來為每個成員創建口令。您可以讓成員們來選擇他們自己的口令,也可以調用MySQL並發布這些語句作為一種既快速又容易的方法:

  

  我們將一個特殊項加到這個表中作為編號0,它有一個用於管理的(超級用戶)口令。可以使用這個口令訪問所有想要訪問的項:

  INSERT INTO member_pass (member_id,passWord) VALUES (0,"secret");

  在創建口令表之後,您可以停止使用第7章中編寫的samp_browse 腳本,該腳本允許任何人在samp_db 數據庫中浏覽任何表的內容,其中包括member_pass 表。

  當成員輸入ID 和口令並提交該表單時, e d i t _ member.PHP 顯示該編輯的項:

  

  

  display_entry() 需要做的第一件事就是校驗口令。對於給定的成員ID,如果表單中輸入的口令與member_pass 表中存放的口令相符,或者如果它與管理口令相符(即成員0 的口令),e d i t _ member.PHP 就顯示編輯的項。口令檢查函數check_pass() 將執行一個簡單的查詢從member_pass 表中移出一條記錄:

  

  因為不能修改它,所以編輯表單作為只讀文本顯示成員ID 的值。對於正常的成員,截止日期也作為只讀文本顯示,因為不能讓成員改動它。然而,如果給出管理口令,則截止日期就成為可編輯的,允許同盟秘書為成員更新日期來重新更新他們的會員資格。

  member 表項的列由display_column() 函數顯示。它按照第三個參數值把列作為可編輯的文本或作為只讀文本加到編輯表單中:

  

  

  display_entry() 函數在格式中作為隱藏字段嵌入了member_id 和pass w o r d,因此當成員提交編輯的項時將繼續edit_script.PHP 的下一個調用。這允許自動校驗ID 的口令,而不用請求成員再次輸入(請注意,我們的簡單的確認身份的方法是以文本形式來回傳遞口令。通常這不是個好主意,但是歷史同盟不是對安全性要求很高的運作機構,因此這種方法足夠滿足要求。如果在運行金融業務,可能需要更強的安全性操作)。

  更新項的函數如下:

  

  

  首先,重新校驗口令,確定沒人發送假表單來愚弄我們,然後更新項。更新時需要注意,因為如果表單中的字段是空白的,則可能需要作為NULL 而不是作為空字符串輸入。expiration 列就是這樣的例子。NULL的成員截止日期具有特殊的含義,即“終生會員”。如果將一個空字符串插入到此列中,值轉換成“ 0 0 0 0 - 0 0 - 0 0”,則成員不再具有終生會員資格。

  為了處理這個問題,我們查找該列的元數據並檢查它是作為NULL 還是作為NOT NULL進行聲明的。該信息由函數MySQL_fetch_fIEld() 返回。不幸地是,此函數通過數值的索引查找列。在member 表中按名稱訪問列會更方便,因此我們編寫一個小函n ul l a b l e ( ),它獲取一個列名並查找相應的元數據對象:

  

  MySQL_fetch_fIEld() 函數需要包含檢查列所在表的結果集標識符。這可通過執行簡單的不返回行的SELECT 查詢來獲得。雖然該查詢返回一個空結果集,但是,對於檢索要評估member 表中列的空性能(n ul l a b i l i t y)的元數據來說,這種方法足夠了:

  SELECT * FROM member WHERE 1=0

  安裝腳本,讓成員們知道他們的口令,這樣他們就能更新自己的成員信息了。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved