在這一課裡,我們要學習如何為網頁增加智慧化處理能力。
一、基本函數
歡迎來到本教程的第三課,也是最後一課。 如果您已經學過第一課和第二課,那麼您已經掌握了MySQL和PHP的安裝及程式設計的基本知識。 下面我們要介紹PHP的一些其他函數,這些函數可能會對您有用,使您的開發過程更加簡單。 首先我們來看看標頭檔。
大家應該知道標頭檔的一些基本概念吧? 標頭檔是一個外部檔,它的內容被包含到主程式中。 方法也十分簡單:在程式檔中引用標頭檔名,這個標頭檔就會包含進來了。 在PHP中使用標頭檔,會涉及兩個函數:include()和require()。 這兩個函數差別很小,卻很重要,所以我們要認真研究一下。 require()函數工作方式與XSSI相類似;不管在程式的哪個部分使用了這個函數,只有程式一開始運行,標頭檔的內容就被作為程式本身的一部分來處理。 因此,如果您在一個條件判定語句中使用了require()函數,那麼即使這個條件即使不為真,標頭檔也會被包含進來。
而include()函數只是在執行到這一條語句時才會把標頭檔內容包含進來。 如果程式沒運行到這裡,那PHP是不會管它的。 這就意味著,您在條件判定部分使用include時,它會完全按照您希望的那樣工作。
還有,如果您用了require()函數,而您指定的標頭檔並不存在,那麼程式將會停止運行並產生錯誤。 如果您用了include(),程式會產生一個警告資訊,但是會繼續運行。 您可以親自試一下,運行下面的程式,然後把include()換成require(),再比較兩個程式運行的結果。
<html>
<body>
<?php
include("emptyfile.inc");
echo "Hello World";
?>
</body>
</html>
我喜歡把標頭檔的尾碼名起成.inc,這樣就可以把標頭檔和一般的程式區分開來。 如果您也這麼做,那麼請您修改Web伺服器軟體的設定檔,使它能夠把.inc檔也當作PHP檔來處理。 否則,駭客們也許會猜到您的標頭檔名,然後用瀏覽器把標頭檔內容以純文字格式顯示出來。 此時如果您的標頭檔中有些機密資訊(如資料庫口令等)那就糟糕了。
那麼,您用標頭檔來做什麼呢? 很簡單! 把對所有程式都通用的那些內容放到標頭檔裡。 象HTML檔頭啦,註腳啦,資料庫連接代碼啦,還有您自己定義的一些函數什麼的。 把下面的文字拷貝到一個檔中,保存為header.inc。
<?php
$db = mysql_connect("localhost", "root");
mysql_select_db("mydb",$db);
?>
<html>
<head>
<title>
<?php echo $title ?>
</title>
</head>
<body>
<center><h2><?php echo $title ?></h2></center>
進然後再創建另外一個檔,名字是footer.txt,該檔可以包含一些程式結束時用到的一些文字和標記。
現在,我們再來創建一個檔,這個檔裡面是真正的PHP程式碼。 試一下下面的代碼,當然,您要確認MySQL資料庫伺服器正在運行。
<?php
$title = "Hello World";
include("header.inc");
$result = mysql_query("SELECT * FROM employees",$db);
echo "<table border=1> ";
echo "<tr><td>名字</td><td>職位</tr> ";
while ($myrow = mysql_fetch_row($result)) {
printf("<tr><td>%s %s</td><td>%s</tr> ", $myrow[1], $myrow[2], $myrow[3]);
}
echo "</table> ";
include("footer.inc");
?>
看到發生了什麼事了嗎? 標頭檔裡的內容被合併到程式中,PHP把所有的代碼都執行了一遍。 注意在包含header.inc標頭檔之前$title是如何定義的。 在header.inc中的代碼可以訪問到它的值。 這樣,網頁的標題就被改掉了。 現在,您可以在任何程式中使用header.inc標頭檔了,您所要做的不過是在每個主程式中為$title變數取一個合適的值。
標頭檔、HTML、條件判定語句,還有迴圈語句,這些東西加在一些,您就可以用最簡練的代碼,寫出功能各異的各種複雜程式來。 在與函數同時使用時,標頭檔更能發揮它的效力,我們後面就會看到。
二、 資料校驗
想像一下這樣的情形:我們把資料庫都設計妥當了,現在請使用者輸入資訊來寫到資料庫中去。 假設您有一個欄位是要求數位類型的資訊,比如價格;而某個可愛的使用者,卻在這一欄裡輸入了文字資訊,使得您的應用程式的執行過程出現了故障。
對您在SQL語句中提供的文字類型的資料,MySQL資料庫拒不接受,並向您提出了「嚴正抗議」。
怎麼辦呢? 您要用資料校驗來防止以上狀況發生。
簡單地講,資料校驗是指我們對資料(通常是使用者經由HTML表格傳過來的)進行檢查,看看它是否遵從一定的規則。 規則可以是多種多樣的,比如某一資料元素不能為空,或者要求某一資料項目的內容必須滿足一定的要求(例如前面的例子中要求必須是數位而不是文字,或者要求電子郵件地址中一定要包含一個「@」字等等)。
資料校驗既可以在伺服器一端作,也可以在用戶端來作。 PHP是用來作伺服器一端的資料校驗的,而JavaScript或其他用戶端腳本程式設計語言則能夠提供用戶端的資料校驗功能。 本文說的是PHP,所以我們在這裡著重介紹伺服器端的校驗。 如果您想找一些現成的、在用戶端運行的資料較驗程式,那您可以去網猴程式庫看看。
暫時把資料庫放在一邊不談,我們先來說說PHP的資料校驗方法。 如果您願意(或者說,您想記錄我們要校驗的那些資料的話),您可以在前面所建的員工資料庫的里加入其他欄位,很簡單,用MySQL的ALTER 語句就行了。
有好幾個PHP功能都可以用來作資料校驗的工作,有些很簡單,有些則複雜一些。 其中strlen()是比較簡單的一個函數,它能夠告訴我們一個變數的長度。
更複雜一點兒的是ereg(),這個函數可以處理完整的常規運算式來進行複雜的校驗。 我不想就常規運算式講得太深,因為許多書都是專門寫這個問題的。 不過我會在下一頁中給出一些簡單的例子。
我們先從一個簡單的例子開始吧。 下面這個程式要檢查一個變數是否存在。
<html>
<body>
<?php
if ($submit) {
if (!$first || !$last) {
$error = "對不起,您必須填寫所有的欄目! ";
} else {
處理表格輸入內容
echo "謝謝! ";
}
}
if (!$submit || $error) {
echo $error;
?>
<P>
<form method="post" action="<?php echo $PHP_SELF ?>">
第一欄: <input type="text" name="名" value="<?php echo $first ?>"><br>
第二欄: <input type="text" name="姓" value="<?php echo $last ?>"><br>
<input type="Submit" name="submit" value="輸入資訊">
</form>
<?php
} // if結束
?>
</body>
</html>
這段程式中關鍵的地方是嵌套的條件判定語句。 第一層檢查使用者是否按了發送資料的按鈕。 如果是,程式接著檢查$first和$last兩個變數是否都存在。 那個 || 符號表示「或」,而 ! 符號表示「非」。 那一句程式用一般語言描述就是「如果$first不存在或者$last不存在,那麼就把 $error變數置成下面的值。 」
接下來,我們再進一步,檢查一段文字的長度。 這對使用者口令的檢查是很有必要的,因為您不想讓某些懶惰的使用者輸入只有一、兩個字的口令,可能會要求他們輸入六位長的口令。
我們已經講到strlen()這個函數了。 它只是簡單地返回一個數位,該數位等於被測變數中所包含的字元個數。 這裡,我修改一下上面的程式,檢查一下$first與$last的長度。
<html>
<body>
<?php
if ($submit) {
if (strlen($first) < 6 || strlen($last) < 6) {
$error = "對不起,您必須填寫所有欄目! ";
} else {
處理表格輸入內容
echo "謝謝! ";
}
}
if (!$submit || $error) {
echo $error;
?>
<P>
<form method="post" action="<?php echo $PHP_SELF ?>">
第一欄: <input type="text" name="名" value="<?php echo $first ?>"><br>
第二欄: <input type="text" name="姓" value="<?php echo $last ?>"><br>
<input type="Submit" name="submit" value="輸入資訊">
</form>
<?php
} // if結束
?>
</body>
</html>
您可以執行一下這段程式,輸入六個字或少於六個字的內容。 這種校驗很簡單,但很有效?>
三、 處理常規運算式
我們稍微講講用ereg()和eregi()兩個函數處理常規運算式。 前面我已經提過,這些函數有的很簡單,有的很複雜,看您的實際需要而定。
使用常規運算式,您可以對一個字串進行檢查,搜索其中的一些結構模式,判定這些模式是否滿足您的規定。 最普遍的用法包括檢查電子郵件地址是否有效(當然,即使這種辦法判定有效,也不能保證郵寄地址真的存在)。
我們在這裡不細究常規運算式的複雜細節了,僅僅給出幾個實例。 您可以使用上一頁中用過的表格 - 把相應的程式碼複製過來,添加到下面的程式碼片段中,就可以看到它是怎樣工作的
首先,我們要確保表格中各欄只能輸入字母。 下面的常規運算式在使用者輸入一個或多個小寫字母時判定為真,而輸入數位是不允許的:
if (!ereg("[a-Z]", $first) || !ereg("[a-Z]", $last)) {
現在我們更進一步,檢查字串的長度是否是四到六位字元長。 用[[:Alpha:]]是檢查字元是不是字母的簡單方式。 大括弧內的數位檢查字元個數。 還要說明的是,^ 和 $ 分別代表字串的開始和結束。
if (!ereg("^[[:Alpha:]]{4,6}$", $first) || !ereg("^[[:Alpha:]]{4,6}$", $last)) {
最後,我們來構造一個常規運算式,來檢驗電子郵件地址的有效性。 這種檢驗方式的效果已經引發了相當多的討論。 沒有什麼東西是十全十美的,不過我下面給出的這段程式還是十分奏效的。
我這段寶貝程式是從PHP郵件討論群組上得來的。 那可是個好去處 - 常去看看吧。 不錯,這段程式看起來是有點亂糟糟的。
if (!ereg(‘^[-!#$%&‘*+./0-9=? A-Z^_`a-z{|} ~]+‘.‘ @‘.‘ [-!#$%&‘*+/0-9=? A-Z^_`a-z{|} ~]+.‘.
‘[-!#$%&‘*+./0-9=? A-Z^_`a-z{|} ~]+$‘, $last)) {
別花太多時間來細究這段代碼了,還是先到下一頁內容吧。
四、 簡便方法
前面的常規運算式怎麼樣? 很有意思,是吧? 要是在每個需要檢查電子郵件地址的程式裡都寫上這麼一段程式,那才真叫有意思呢?! 想想看吧,得寫那麼亂七八糟的一段程式,還得寫上那麼多遍! ... 不過,當然了,還有更簡便的方法。
還記得前面我們學過的標頭檔嗎? 它能讓我們寫一段程式,像是這個電子郵件地址的檢查程式,然後把這段套裝程式含進多個程式裡面去。 這樣,我們要改寫這段程式時,只須改動一處就行了,不用修改多個檔。
但是,要做到這一點,我們必須用到函數。
我們已經用過很多次函數了。 每次我們查詢資料庫或檢查字串長度時,我們都是用函數來做的。 這些函數是PHP自帶的。 如果您是位熱心的程式師,您可以用自己編寫的函數來擴充PHP本身的功能。 但對本教程而言,這部分內容是太過高深了一點。 我們要創建的函數不是那一種,而是寫在PHP腳本程式內部的函數。
函數就是一段程式碼,我們可以把一個或多個值傳給這段代碼,然後這段代碼會處理我們傳給它的資料並返回一個值。 根據實際需要,函數可以很簡單,也可以十分複雜。 但是只要我們傳進去一個數,然後能得到一個數,您管它裡面有是複雜還是簡單呢! 這就是函數的可愛之處。
PHP裡的函數與C語言裡的函數表現差不多。 當我們定義函數時,必須指明函數需要接收什麼樣的資料。 一開始好象不太好理解為什麼它要接收資料進去,不過這樣可以防止發生一些怪異的問題。 函數之所以能做到這一點,是因為函數裡面的變數都是私有變數,也就是說,它只在該函數內部存在。
例如,您在程式中有一個變數叫$myname,如果您創建了一個函數,想讓這個函數也使用那個$myname變數(值也相同),那是不行的。 您可以在函數內部創建一個變數,名字也叫$myname,這兩個變數可以各平相處,而各自取不同的值。 不過我可不建議您這麼做! 您如果真的這麼做了,等半年後您再來修改這樣的程式時,您可能就會被弄糊塗了。
那我們現在就來創建一個函數,先來個簡單的。 我們要給它取個名字,指定它要接收什麼的變數。 在調用這個函數之前,我們還得定義這個函數。
<html>
<body>
<?php
function addnum($first, $second) {
$newnum = $first + $second;
return $newnum;
}
echo addnum(4,5);
?>
</body>
</html>
這就行了! 首先,我們創建了第一個自己的函數。 我們定義了兩個新變數,$first和$second,注意它們是怎樣被定義的。 在調用這個函數時,要給這兩個變數按它們出現的順序賦好值 - 4賦給$first,5賦給$second。 然後我們簡單地把這兩個數加在一起,返回結果。 「返回」在這裡的意思是把結果送回去。 在程式最後部分我們把數位9顯示出來。
我們再來創建一個函數,讓它對我們的資料庫應用有點説明。 一個能妥善處理錯誤的函數怎麼樣? 試試下面的程式:
<html>
<body>
<?php
function do_error($error) {
echo "噢,好象有點兒問題...<br>";
echo "系統報告的錯誤是:$error. <br>";
echo "最好是暫時關閉網站並通知系統管理員。 ";
die;
}
if (!$db = @mysql_connect("localhost","user", "password")) {
$db_error = "無法連接到MySQL資料庫";
do_error($db_error);
}
?>
</body>
</html>
在運行程式之前,試試關閉MySQL資料庫,或使用錯誤的使用者名或口令。 您會看到友好的、有用的錯誤資訊 。 細心的朋友會注意到在mysql_connect()函數之前的那個@符號。 它會抑制系統錯誤資訊,使得程式只能從do_error()函數那裡得到有關的錯誤資訊。 您還會注意到,我們可以把一個在別處定義的變數作為參數傳給函數,而不是在調用時直接賦一個值還記得我過函數使用的是私有變數吧? 這話說得不完全對。 事實上,您可以讓函數訪問到函數外面的變數。 您可能要寫一個函數,用它來查詢資料庫,然後把結果顯示在多個網頁中。 您不想每次都把資料庫連接標識都傳給函數。 在這種情況下,您可以把連接標識定義成一個全域的變數。
例如:
<html>
<body>
<?php
function db_query($sql) {
global $db;
$result = mysql_query($sql,$db);
return $result;
}
$sql = "SELECT * FROM mytable";
$result = db_query($sql);
?>
</body>
</html>
這是個很簡單的函數,但重要的是,您在調用這個函數時,不必傳遞$db變數 - 您可以通過 global這個字使得函數可以訪問到該變數。 在這條語句中您可以定義多個全域變數,各個全域變數之間用逗號隔開。
最後,您可以使用可選參數,這樣看起來您已經是真正的專家了。 這裡面關鍵的一點是,在函數中定義參數時要給它指定一個缺省值。 然後您在調用這個函數時,如果不為該參數變數指定其他值,那麼函數會自動把缺省值賦給這個變數。 如果您指定了其他值,那麼缺省值就不起作用了。
不太明白? 比方說,您在連接資料庫時,幾乎總是連接到相同的伺服器,並且使用相同的使用者名和口令。 不過有時候,您也需要連接到其他的伺服器。 看看下面的程式:
<html>
<body>
<?php
function db_connect($host = "localhost", $user="username", $pass="graeme") {
$db = mysql_connect($host, $username, $password);
return $db;
}
$old_db = db_connect();
$new_host = "site.com";
$new_db = db_connect($new_host);
?>
</body>
</html>
很「酷」是不是? 在定義函數時,函數內部用到的變數也定義好了。 第一次調用這個函數時,全部參數變數都是用的缺省值。 第二次調用時,伺服器名變了,而使用者名和口令沒有變。 真是太棒了!
想想您在什麼地方還能用到函數。 您可以用函數來作資料校驗,來完成常用的功能,等等。 在對Web網頁上顯示的文字作處理時,我用到了很多函數。 我可以一次完成對文字的檢查、解析和修改,來加入分行符號和HTML標記等。
現在,剩下的就是我要給您的一些忠告了。
五、進階技巧
談起資料庫開發,我們有很多東西要學。 如果您還沒有學習過怎樣進行資料庫設計,和怎樣在不同的平臺上可靠地運行資料庫,那麼請您趕快去找本這方面的好書來讀一讀。 這方面的能力會帶給您無法估量的好處,從長遠的眼光看,它會為您節省大量的時間與精力。
還有,認真學學MySQL。 這是一個複雜而有趣的資料庫,有很多不錯的文檔。 學學資料庫的表結構、資料類型,還有SQL。 如果您真正掌握了SQL,您可以完成相當多的實際工作。
最後,還有PHP。 您想要的一切幾乎都可以在PHP的網站上找到,包括全面的文檔、郵件討論群組的討論內容、程式碼庫,等等。 學習PHP的一個絕好的辦法是研究使用者手冊中給出的實例,並查閱網上的代碼。 網友們發表的代碼包括許多函數和類,您可以在自己的程式中直接使用,而不必自己從頭來過。 另外,如果您遇到問題,郵件討論群組是一個非常值得利用的資源。 PHP的開發人員自己也會參加郵件討論群組,還有許多經驗豐富的高手們,他們都可以説明您解決問題。