Mysql入門系列:PHP基礎(chǔ)
PHP 的基本功能就是解釋一個腳本,來生成發(fā)送到客戶機的Web 頁面。具有代表性的是,腳本包括逐字發(fā)送到客戶機的HTML 和作為程序執(zhí)行的PHP 代碼的混合編碼。無論代碼生成什么樣的輸出,都會發(fā)送到客戶機,因此客戶機永遠(yuǎn)不會看到代碼,它只能看結(jié)果的輸出。;;;;當(dāng)PHP 開始讀取文件時,假設(shè)文件內(nèi)容表示文字的H T M L,則它僅僅拷貝在那里找到的輸出內(nèi)容。當(dāng)PHP 解釋程序遇到一個特殊的打開標(biāo)記時,就從HTML 模式切換到PHP 代碼模式,而作為要執(zhí)行的PHP 代碼也開始解釋文件。代碼的結(jié)尾由另一個特殊的標(biāo)記指出,解釋程序在這個位置從代碼模式切換回HTML 模式。這就允許將靜態(tài)的文本( HTML 部分)與動態(tài)產(chǎn)生的結(jié)果( PHP 代碼部分的輸出)相混合,產(chǎn)生依賴于調(diào)用環(huán)境變化的頁面。例如,可以使用PHP 腳本來處理表格的結(jié)果,在這個格式中,用戶已經(jīng)輸入了數(shù)據(jù)庫搜索的參數(shù)。由于格式填入內(nèi)容的不同,所以每次搜索的參數(shù)可能也不同,因此當(dāng)腳本執(zhí)行搜索的時候,每個作為結(jié)果的頁面將反映不同的搜索。;;;;讓我們通過一個非常簡單的PHP 腳本看一看它是如何工作的:;;;;<HTML>;;;;<BODY>;;;;hello,world;;;;</body>;;;;</HTML>;;;;這個腳本并不很有趣,因為它不包括PHP 代碼!因此您會問:那它有什么好處?這個問題的回答是:它有時有助于建立包括想要生成頁面的HTML 框架的腳本,然后再加入PHP代碼。這是非常有效的,PHP 解釋程序用于它是沒有問題的。;;;;為了在腳本中包括PHP 代碼,您可從用兩個特殊標(biāo)記(腳本開始處的‘ < ? p h p’和腳本結(jié)束處的‘? >’)把它與周圍的文本區(qū)分開來。當(dāng)PHP 解釋程序遇到開始的‘< ? p h p’標(biāo)記時,就從HTML 模式切換到PHP 模式,并解釋它找到的任何PHP 代碼,直到看見結(jié)束的‘ ? >’標(biāo)記為止。它產(chǎn)生的所有輸出解釋并替換了兩個標(biāo)記之間的腳本。將前面的實例再重新編寫一下,它包括了少量的PHP 代碼,如下所示:;;;;此時,代碼部分是很小的,由單行組成。當(dāng)解釋代碼時,產(chǎn)生了輸出“ hello, world,它作為輸出部分發(fā)送到客戶機瀏覽器。這樣,這個腳本產(chǎn)生的Web 頁面與前面實例產(chǎn)生的Web頁面一樣,前面實例的腳本完全由HTML 組成。;;;;可以使用PHP 代碼產(chǎn)生Web 頁面的任何部分。我們已經(jīng)看到了一個特別的實例,在那里整個腳本都由文字的HTML 組成,而不包括PHP 代碼。另一個特別的實例是整個腳本都是PHP 代碼而不包括文字的H T M L:;;;;這說明PHP 在如何產(chǎn)生輸出方面有很大的靈活性。但PHP 也留下一個問題,那就是確定如何組合HTML 和PHP 代碼才是合適的。不必把所有代碼都放在一個地方, PHP 在這方面也很靈活。只要您高興,就可以通過腳本在HTML 和PHP 代碼模式之間進行轉(zhuǎn)換。PHP 腳本標(biāo)記;;;;除了本章實例中使用的標(biāo)記之外, PHP還支持其他的腳本標(biāo)記。您可以在其他人編寫的PHP 代碼中看到它們,或者可以自己使用這些標(biāo)記。PHP 識別四種標(biāo)記風(fēng)格:;;;;缺省標(biāo)記風(fēng)格。這是PHP 配置為缺省時使用的風(fēng)格:;;;;<?php print ("hello,worldn");?>;;;;簡潔開標(biāo)記風(fēng)格。這個風(fēng)格除了開標(biāo)記較簡潔外,其他與缺省風(fēng)格相類似:;;;;<? print ("hello,worldn");?>;;;;兼容ASP 的風(fēng)格。這個風(fēng)格在Active Server Page 環(huán)境內(nèi)部是通用的:;;;;<% print ("hello,worldn");%>;;;;<SCRIPT> 標(biāo)記風(fēng)格。如果使用與其他標(biāo)記風(fēng)格不同的HTML 編輯器,這個風(fēng)格是有用的。當(dāng)然,它比較冗長,但是當(dāng)您使用其他標(biāo)記風(fēng)格時,編輯器可能不處理PHP 代碼,這時您將發(fā)現(xiàn)它是必需的:;;;;<SCRIPT LANGUAGE="php">print ("hello,worldn");</SCRIPT>;;;;缺省時,簡潔開標(biāo)記風(fēng)格和兼容ASP 風(fēng)格無效。請參閱附錄H 有關(guān)開啟它們的說明。獨立的PHP 腳本;;;;當(dāng)處理命令解釋程序腳本或Perl 腳本時,可以編寫從命令行調(diào)用的獨立的PHP 腳本。這里有一個實例:;;;;#! /usr/local/bin/php -q;;;;<?php print ("hello,worldn");?>;;;;前面的腳本可命名為h e l l o . p h p,利用chmod +x 使之成為可執(zhí)行的,并從命令解釋程序中調(diào)用:;;;;% hello.php;;;;hello,world;;;;本章中我們不編寫任何獨立的腳本。這里編寫的所有實例都期望Web 服務(wù)器調(diào)用它們來生成Web 頁面。;;;;下一個腳本有一些實質(zhì)性的內(nèi)容,但仍然相當(dāng)簡短。它說明了如何較容易地從PHP 訪問MySQL數(shù)據(jù)庫,并在Web 頁面中使用查詢結(jié)果。此腳本在第5 章表達(dá)得很簡短,它形成了歷史同盟Web 站點主頁的基礎(chǔ)。在我們繼續(xù)往下做的時候,應(yīng)該使腳本更精巧一些,但直到現(xiàn)在為止,它所做的只是顯示簡短的歡迎消息和當(dāng)前同盟會員資格的計數(shù):;;;;歡迎消息就是一個靜態(tài)文本,因此它作為文字的HTML 是最容易編寫的。另一方面,會員資格的計算是動態(tài)的,而且隨時會更改,因此必須在它不工作時通過查詢samp_db 數(shù)據(jù)庫中的member 表來確定。;;;;在開和閉腳本標(biāo)記之間的代碼文本執(zhí)行一個簡單的任務(wù)。首先,它打開了與MySQL服務(wù)器的連接,并使samp_db 數(shù)據(jù)庫成為缺省數(shù)據(jù)庫。然后,它發(fā)送一個到服務(wù)器的查詢來確定此刻歷史同盟有多少成員(我們將它作為member 表中的行數(shù))。查詢結(jié)果將作為包括會員資格計數(shù)消息的一部分顯示出來,然后再做處理。;;;;在處理過程中,如果任何一點發(fā)生錯誤,該腳本都會簡單地退出。它由于太簡單而不產(chǎn)生錯誤輸出,因此把訪問該Web 站點的人們搞糊涂了(如果依靠PHP 代碼生成整個Web 頁面,當(dāng)因錯誤而退出,且不產(chǎn)生任何輸出時,可能會使訪問頁面的人非常惱火,因為有些瀏覽器將顯示“這個頁面沒有數(shù)據(jù)的對話框)。;;;;讓我們把該腳本分成幾段,看看它是如何工作的。第一步是用mysql_pconnect() 連接到服務(wù)器:;;;;上一篇; ; ; 目錄; ; ; 下一篇=@mysql_pconnect ("pit.viper.snake.net","paul","secret");;;;or exit ();;;;;mysql_pconnect() 把主機名、用戶名和口令作為參數(shù)。連接建立成功,則返回連接標(biāo)識符,如果發(fā)生錯誤,則返回FA L S E。如果連接失敗,腳本立刻調(diào)用exit() 來結(jié)束腳本,并且不再產(chǎn)生輸出。;;;;在mysql_pconnect() 調(diào)用前的‘@’字符是什么意思呢?就是“請關(guān)閉的意思。有些PHP 函數(shù)在失敗時除了返回狀態(tài)代碼之外還寫出錯誤消息。在mysql_pconnect() 情況下,失敗的連接導(dǎo)致了下面的消息出現(xiàn)在發(fā)送到客戶機瀏覽器的Web 頁面上:;;;;Warning:MySQLConnection Failed:Access denied for user:;;;;'paul0pit-viper.snake.net' (Using password:YES);;;;這樣很難看,參觀我們站點的人們可能不知道它是如何造成的和該怎樣處理它。把‘ @’字符放到mysql_pconnect() 調(diào)用的前面,就可以取消這個錯誤消息,以使我們能在返回值的基礎(chǔ)上自己選擇如何處理錯誤。對于該腳本,如果發(fā)生錯誤,最好根本不產(chǎn)生屬于會員資格計數(shù)的輸出。這樣,頁面將只包含歡迎消息。;;;;任何PHP 函數(shù)都可以使用‘@’,但以筆者的經(jīng)驗來說,初始的mysql_pconnect() 調(diào)用是最可能失敗的一個。因此,本章的例子禁止來自該函數(shù)中的消息。;;;;將名稱和口令嵌入到所有人都能看到的腳本中,可能會使您緊張不安。是這樣的,名稱和口令出現(xiàn)在發(fā)送到客戶機的Web 頁面上是對的,因為該腳本的內(nèi)容由其輸出的結(jié)果替換掉了。然而,如果Web 服務(wù)器不知何故配置不當(dāng),沒有識別出腳本需要由PHP 代碼來處理,它就會將腳本以純文本發(fā)送出去,且會暴露連接參數(shù)。在8 . 2 . 1節(jié)“使用函數(shù)和include 文件中,將簡要地處理這類情況。;;;;mysql_pconnect() 返回的連接標(biāo)識符可以傳遞到PHP API 中的幾個與MySQL相關(guān)的調(diào)用中。然而,對于這樣的調(diào)用,標(biāo)識符總是可選擇的。例如,可以使用下列格式之一來調(diào)用mysql_ s e l e c t _ db ( ):;;;;mysql_select_db ($db_name,上一篇; ; ; 目錄; ; ; 下一篇);;;;;mysql_select_db ($db_name);;;;;mysql_pconnect() 與mysql_connect() 的對比函數(shù)mysql_pconnect() 與函數(shù)mysql_connect() 相似,都具有主機名、用戶名和口令參數(shù),并返回連接標(biāo)識符或FALSE 來說明連接是否成功。兩個調(diào)用之間的不同在于:mysql_pconnect() 建立了一個持久的連接,而mysql_connect() 建立了一個非持久的連接。與非持久連接不同,持久連接在腳本終止時不關(guān)閉。如果另外一個PHP 腳本隨后由同一個Apache 子處理執(zhí)行,并用同樣的參數(shù)調(diào)用mysql_ p c o n n e c t ( ),將重新使用這個連接。這比關(guān)閉后再建立連接的效率要高。;;;;如果忽略了從一些相關(guān)MySQL的PHP 調(diào)用中得到的連接參數(shù),調(diào)用就會使用最近打開的連接。這樣,如果腳本只打開單個連接,那么在任何MySQL調(diào)用中永遠(yuǎn)不必指定連接參數(shù)——即連接是缺省的。這就是C 或DBI API 與MySQL程序設(shè)計的極大不同之處,因為它們沒有這樣的缺省。;;;;筆者使用上一篇; ; ; 目錄; ; ; 下一篇 變量在簡單的主頁上編寫了下面的連接代碼,使mysql_pconnect() 返回哪種類型的值更加清晰:;;;;上一篇; ; ; 目錄; ; ; 下一篇=@mysql_pconnect("pit-viper.snake.net","paul","secret");;;;or exit();;;;;然而,實際上,我們在腳本的其他地方都沒有使用$ l i n k,因此代碼可更簡單地寫成:;;;;@mysql_pconnect("pit.viper.snake.net","paul","secret");;;;or exit();;;;;假設(shè)連接建立成功,則下一步是選擇一個數(shù)據(jù)庫:;;;;mysql_select_db("samp_db");;;;or exit();;;;;如果mysql_select_db 失敗,我們將會默默地退出。如果我們能夠連接到服務(wù)器,并且數(shù)據(jù)庫存在,那么似乎不可能發(fā)生錯誤,但是仍然要嚴(yán)格地檢查問題并采取相應(yīng)的行動。選擇數(shù)據(jù)庫之后,可將查詢發(fā)送到服務(wù)器,提取結(jié)果,加以顯示,然后釋放結(jié)果集:;;;;$result=mysql_query("SELECT COUNT(*) FROM member;;;;or exit();;;;;if ($row = mysql_fetch_array ($result))echo "<p>The League currently has ". $row[0] . "members";;;;;mysql_free_result($result);;;;;mysql_query() 函數(shù)將查詢發(fā)送到服務(wù)器中去執(zhí)行。查詢不用分號或者‘ g’終止。如果查詢非法或因為某些原因不能執(zhí)行,則mysql_query() 返回FA L S E,否則返回一個結(jié)果集標(biāo)識符。該標(biāo)識符是我們能用來獲得有關(guān)結(jié)果集信息的值。對于查詢,該結(jié)果集由表示會員資格計數(shù)的單列值的單行組成。為得到這個值,我們可以把結(jié)果集標(biāo)識符傳給mysql_ f e t c h _ r o w ( )來獲取行,將此行賦給變量$ r o w,并以$row[0] 形式訪問第一個元素(只有一個元素時也是這樣)。;;;;當(dāng)處理完結(jié)果集時,將結(jié)果集傳遞給mysql_free_result() 進行釋放。實際上這種調(diào)用在我們的腳本中是不必要的,因為當(dāng)腳本結(jié)束時, PHP 會自動地釋放所有活動的結(jié)果集。;;;;mysql_free_result() 有助于執(zhí)行大型查詢或大量查詢的腳本。它防止大量內(nèi)存的使用。;;;;為了使用腳本,需要在某處安裝它。本章將采用這樣的約定:美國歷史同盟在Apache 文檔樹的最高一級中有自己的目錄,稱為us h l,因此主頁面腳本作為該樹的ushl/index.php 進行安裝。我們也將為學(xué)分保存方案開發(fā)腳本,因此給出目錄g p。如果Web 站點主機是pit-viper.snake.net,那么這兩個目錄中的頁面將有如下開頭的URL:;;;;http://pit-viper.snake.net/ushl/;;;;http://pit-viper.snake.net/gp/;;;;例如,每個目錄的主頁面都可稱為i n d e x . p h p,并以如下方式進行訪問:;;;;http://pit-viper.snake.net/ushl/index.php;;;;http://pit-viper.snake.net/gp/index.php使用函數(shù)和include 文件;;;;PHP 腳本與DBI 腳本的不同之處在于, PHP 腳本位于Web 服務(wù)器文檔樹的內(nèi)部,而DBI 腳本位于cgi-bin 目錄中,這個目錄在文檔樹的外部。這就提出了一個安全性問題:服務(wù)器配置不當(dāng)?shù)腻e誤可能導(dǎo)致位于文檔樹內(nèi)部的頁面會以純文本方式泄露給客戶機。這意味著建立與MySQL服務(wù)器連接的用戶名和口令如果在PHP 腳本而非DBI 腳本中使用,則將處于暴露給外界的很高的危險之中。;;;;PHP 中的變量;;;;在PHP中,可以通過簡單地使用變量而使它們存在。主頁腳本使用了三個變量:$ l i n k、$result 和$ r o w,沒有一個變量會在所有地方都聲明(聲明變量的地方有上下文,如在函數(shù)內(nèi)部引用全局變量時,我們隨后會談到這個問題)。;;;;變量由美元符號(‘$’)為開頭的標(biāo)識符表示。無論它表示什么類型的值都是正確的,盡管對于數(shù)組和對象要添加一些額外內(nèi)容來訪問值的單個元素。如果變量$x 表示單個值,例如,數(shù)字或字符串,則可以寫成$x 來訪問它。如果$x 表示有數(shù)字索引的數(shù)組,則可以寫成$ x [ 0 ]、$x[1] 等等來訪問它的元素。如果$x 表示有關(guān)聯(lián)索引的數(shù)組,如“ y e l l o w或者“l a rg e,則可以寫成$ x [“y e l l o w]、$ x [“l a rg e] 來訪問它的元素。;;;;PHP 數(shù)組可以同時擁有數(shù)字索引的元素和相關(guān)索引的元素。例如, $x[1] 和 $ x [“ l a rg e] 都能作為同一數(shù)組的元素。如果$x 代表一個對象,則可寫成$ x ->property_name 來訪問它所具有的屬性。例如, $x->yellow 和$ x - > l a rge 都是$x 的屬性。作為屬性名,數(shù)字是不合法的,因此$x->1在PHP 中是不合法的。;;;;初始的歷史同盟主頁腳本也存在這個問題,因為它包括MySQL用戶名和口令的直接值。讓我們用兩個PHP 性能:函數(shù)和include 文件,把這些連接參數(shù)移到腳本外面。我們將編寫函數(shù)samp_db _ c o n n e c t ( )來建立這個連接,并把函數(shù)放到一個include 文件中—不是主腳本部分的,但可以從腳本中引用的文件。這種方法的優(yōu)點如下:;;;;編寫連接建立代碼比較容易。不需要寫出所有參數(shù),就可以在連接后用samp_db _connect() 選擇數(shù)據(jù)庫,使一個函數(shù)可以進行兩個PHP 函數(shù)的工作。由于你可以將精力集中于腳本的獨特標(biāo)記,而不必為連接建立代碼分心,因此也使得腳本更加易于理解。;;;;可從腳本中訪問include 文件,但可移到Apache 文檔樹的外面。這使它的內(nèi)容對于客戶機來說是不可訪問的,即使Web 服務(wù)器配置不當(dāng),連接參數(shù)也不會暴露給它們。使用include 文件,對于隱藏不想由Web 服務(wù)器發(fā)送到站點的任何類型的敏感信息都是個良策。;;;;雖然如此,但這并不意味著用戶名和口令在任何意義上說都是安全的。如果沒有采取預(yù)防措施,在Web 服務(wù)器主機上注冊的用戶,能夠直接讀取include 文件。請參閱7 . 4 . 3節(jié)“從Web 腳本連接到MySQL服務(wù)器關(guān)于安裝DBI 配置文件所描述的預(yù)防措施,它們用于保護口令和用戶名不受其他用戶侵害。對PHP 的include 文件也要應(yīng)用同樣的防范措施。;;;;PHP 語言上的影響;;;;如果有C 程序設(shè)計經(jīng)驗,則可能注意到:腳本中許多語法的結(jié)構(gòu)與C 程序設(shè)計中的非常類似。實際上, PHP 語法很大程度上來自于C,因此這種相似處并不是巧合。如果有些C 的背景知識,就可以將它的許多內(nèi)容轉(zhuǎn)換到PHP。事實上,如果不能確信如何用PHP 編寫表達(dá)式或控制結(jié)構(gòu),則可以試用C 中編寫它們的方法,這很可能也是正確的。雖然PHP 的基本部分主要在C 中,但也包含了使用Java 和Perl 的成分。可以在注釋語法中查看它,在那里允許以下形式:;;;;# Perl-style comment from '#' to end of line;;;;//c++ or Java-style comment from '//' to end of line;;;;//* C-style comment between slash-star to star-slash */;;;;Perl 的其他相似性包括‘ .’字符串連接操作符(包括‘ . =’作為額外的連接),變量引用和轉(zhuǎn)義序列的方法是在雙引號內(nèi)而非單引號內(nèi)解釋的。;;;;include 文件可以由多個腳本使用。這提高了代碼的可重用性,使代碼更加可維護。同時也允許對訪問這個文件的每個腳本不費力地做出全局性的更改。例如,如果我們將數(shù)據(jù)庫samp_db 從pit-viper 移動到b o a,則不必更改一簇單個腳本,而只要更改包含samp_db_connect() 函數(shù)的include 文件中mysql_pconnect() 調(diào)用的主機名參數(shù)即可。;;;;為了使用include 文件,必須有存放它們的地方,而且必須使PHP 找到它們。如果系統(tǒng)已經(jīng)有了這樣一個位置,則可以使用。如果沒有,則使用下面的過程建立一個include 文件的位置:;;;;1) 創(chuàng)建一個目錄來存放PHP 的include 文件。該目錄不能位于Web 服務(wù)器文檔樹內(nèi)部!筆者使用/usr/local/apache/php 的PHP include 目錄,它與我的文檔樹在同一層次上(/ us r / l o c a l / a p a c h e / h t d o c s),而不是在其內(nèi)部。;;;;2) 通過完整的路徑名或者告訴PHP 在搜索時尋找哪個目錄來引用include 文件。后者的方法更方便些,因為如果我們使用了文件的基名, PHP 就會找到它( PHP include 文件與C頭文件有些類似,其中包括的PHP 將在多個目錄中搜尋include 文件,就像C 預(yù)處理程序在多個目錄中搜尋C 頭文件一樣)。為了告訴PHP 去哪里查看,修改PHP 初始化文件(系統(tǒng)上的/ us r / l o c a l / l i b / p h p 3 . i n i)來改變include_path 的值。如果它沒有值,可以將它設(shè)置為新的包含路徑的完整路徑名:;;;;include_path="current_value:/usr/local/apache/php";;;;如果include_path 已經(jīng)有值了,則把新的目錄加到那個值中:;;;;include_path="current_value:/usr/local/apache/php";;;;3) 創(chuàng)建想使用的include 文件并將它放到i n c l ud e目錄中。文件應(yīng)該有一些有特點的名稱,為了這個目的,這里我們使用samp_db . i n c。它的內(nèi)容將在下面列出。對于我們這里開發(fā)的腳本,當(dāng)連接到MySQL服務(wù)器上時,會一直使用samp_db 數(shù)據(jù)庫,因此連接函數(shù)samp_db_connect() 也可以為我們選擇samp_db 數(shù)據(jù)庫。如果連接成功并選擇了這個數(shù)據(jù)庫,這個函數(shù)就返回一個連接標(biāo)識符;如果發(fā)生錯誤,則返回FA L S E。發(fā)生錯誤時將不打印消息,并且允許調(diào)用者靜靜地退出,或者在環(huán)境允許時再打印消息。;;;;觀察一下,samp_db.inc 文件的內(nèi)容由‘< ? p h p’和‘? >’括在一起。這是因為PHP 是在HTML 模式中開始讀取文件的。如果沒有這些標(biāo)記PHP 會把文件以純文本發(fā)送出去,而不是作為PHP 代碼解釋。如果想在文件中包含文字的HTML 是很好的選擇。但是,如果文件包含PHP 代碼,就必須在腳本標(biāo)記內(nèi)部封閉代碼。;;;;4) 使用下面的行從腳本中引用文件:;;;;include("samp_db.inc");;;;;當(dāng)PHP 看到這一行時,就搜尋文件并讀取內(nèi)容。對于腳本的下列部分,文件中的任何事物都變成可訪問的。;;;;在建立了我們的include 文件samp_db.inc 之后,就可以修改歷史同盟主頁來引用i n c l ud e文件,并通過調(diào)用samp_db_connect() 函數(shù)連接到MySQL服務(wù)器上:;;;;include( ) 與require( ) 的對比;;;;PHP 的require() 性能與include() 相類似。不同之處在于,對include() 來說,在include() 執(zhí)行時文件每次都要進行讀取和評估;而對于require() 來說,文件只處理一次(實際上,文件內(nèi)容替換了require() 語句)。這意味著如果有包含這些指令之一的代碼和可能執(zhí)行多次的代碼,則使用require() 的效率比較高。另一方面,如果每次執(zhí)行代碼時想讀取不同的文件,或者有通過一組文件迭代的循環(huán),就使用i n c l ud e ( ),因為可以給想要包括的文件名設(shè)置一個變量,當(dāng)參數(shù)為include() 時使用這個變量。;;;;samp_db.inc 文件對其他函數(shù)也是有用的,我們可以將它作為各種其他事物的儲藏庫。實際上,還可以再創(chuàng)建兩個函數(shù)放入到文件中。我們編寫的每個腳本在頁面的開頭都會產(chǎn)生一組相當(dāng)醒目的HTML 標(biāo)記,而另一組在結(jié)尾。不必在每個腳本中將它們逐字地寫出,我們可以通過編寫函數(shù)html_begin() 和html_end() 來做這些事。函數(shù)html_begin() 能夠提取幾個指定了頁面標(biāo)題和頭的參數(shù)。兩個函數(shù)的代碼如下:;;;;然后我們可以修改歷史同盟主頁來使用這兩個新函數(shù),如下所示:;;;;請注意代碼被分成了兩塊,兩塊代碼之間出現(xiàn)了歡迎消息的文字HTML 文本。;;;;產(chǎn)生頁面開始和最后部分的函數(shù)用法給了我們一個重要的能力。如果想改變使用這些函數(shù)的頁面頭和尾的外觀,可以在函數(shù)中包含一些代碼。使用它們的每個腳本也都將自動地受到影響。例如,您可以把消息“ Copyright USHL放在每個歷史同盟頁面的底部。頁面尾部函數(shù)html_end() 會很容易地做到這一點。一個簡單的查詢頁面;;;;已經(jīng)嵌入到歷史同盟主頁中的腳本運行了一個只返回單個行的查詢。下一個腳本介紹了如何處理多行的結(jié)果集。它獲取并顯示了member 表中的內(nèi)容。這就是第7 章開發(fā)的dump_members DBI 腳本的PHP 等價物,因此稱它為d um p _ member s . p h p。它與DBI 版本的不同之處在于,它希望在Web 環(huán)境中使用而不是在命令行中使用。由于這個原因,它需要產(chǎn)生HTML 輸出而不是簡單地寫出制表符分隔的文本。為了使行和列漂亮地排列,我們將以HTML 表形式編寫會員資格記錄。腳本如下:;;;;這個腳本使用了die() 函數(shù)來打印消息,如果發(fā)生錯誤則退出( die() 函數(shù)與exit() 函數(shù)相類似,但是它在退出之前打印消息)。與我們在歷史同盟主頁中使用的靜靜地退出方法相比,這是一種不同的錯誤處理方法。在dump_membes.php 中,我們希望看到一個特殊的結(jié)果,因此打印錯誤消息來說明發(fā)生的問題是有道理的。;;;;腳本可以安裝在ushl 目錄并用http://pit-viper.snake.net/ushl/dump_members.php 訪問。可以在歷史同盟主頁的新腳本中增加一個連接,以便人們了解它:處理查詢結(jié)果;;;;在這一節(jié)中,我們將更細(xì)致地檢查如何執(zhí)行MySQL查詢并處理結(jié)果集。在PHP 中,所有的查詢都通過調(diào)用mysql_query() 函數(shù)來發(fā)布,這個函數(shù)提取查詢字符串和連接標(biāo)識符作為參數(shù)。連接標(biāo)識符是可選的,因此可以用下面任意一種形式調(diào)用mysql_query():;;;;$result=mysql_query($query,上一篇; ; ; 目錄; ; ; 下一篇); # use explicit connection;;;;$result=mysql_query($query); # use default connection;;;;對于不返回行的查詢(非SELECT 的查詢,如D E L E T E、INSERT、REPLACE 和UPDATE),mysql_query() 返回TRUE 或者FALSE 說明查詢成功或者失敗。為了獲得成功的查詢,可以調(diào)用mysql_ a ffected_rows() 找出有多少行被改變(可能是刪除、插入、替換或者更新)。;;;;對于SELECT 語句,mysql_query() 返回結(jié)果集標(biāo)識符或者FALSE 說明查詢是成功或是失敗。為了獲得成功的查詢,使用結(jié)果集標(biāo)識符可以獲得更多的有關(guān)結(jié)果集的信息。例如,可以找出結(jié)果集有多少行或列,或者提取這個結(jié)果集內(nèi)部包括的行。;;;;當(dāng)mysql_query() 返回FA L S E(也就是零)時,意味著查詢失敗—換句話說就是發(fā)生一些錯誤而不能執(zhí)行查詢。查詢失敗可能有下面幾個原因:;;;;查詢可能是畸形的并包含語法錯誤。;;;;查詢依照語法可能是正確的,但是在語義上卻是無意義的,例如要從不包含列的表中選擇列時。;;;;沒有充分的權(quán)利執(zhí)行查詢。;;;;由于網(wǎng)絡(luò)問題,可能已經(jīng)連接不到MySQL服務(wù)器主機。;;;;在以上每種情況中(還有其他情況),mysql_query() 都返回FA L S E。如果要想知道錯誤的詳細(xì)原因,就調(diào)用mysql_error() 或者mysql_errno() 獲得錯誤消息字符串或者數(shù)字錯誤代碼(請參閱8 . 2 . 4節(jié)“處理錯誤一節(jié))。;;;;考慮mysql_query() 造成的兩種最常見的錯誤,返回值是行計數(shù),或者它包含查詢返回的數(shù)據(jù),兩者都是錯誤的。;;;;1. 處理不返回結(jié)果集的查詢;;;;下面的代碼使用DELETE 說明了如何處理不返回任何行的查詢:;;;;$result=mysql_query("DELETE FROM member WHERE member_id=149");;;;;if (!$result)print ("query failedn");;;;;elseprintf("number of rows deleted:%dn",mysql_affected_rows());;;;;如果有一個ID 號為149 的成員,MySQL就刪除記錄,且mysql_query() 返回T R U E。如果沒有這樣的成員呢?這種情況下mysql_query() 仍然返回T R U E!這使將精力耗在誤解mysql_query() 的返回值是行計數(shù)的人們非常驚訝。兩種情況都返回T R U E,是因為不管實際上是否刪除了一些行,查詢都是合法的。由查詢作用的行的數(shù)目則是完全不同的事。若要在查詢成功之后提取值,可調(diào)用mysql_ a ff e c t e d _ r o w s ( )。;;;;2. 處理返回結(jié)果集的查詢;;;;下面的實例提供了SELECT 查詢處理的大致概況:;;;;不要假設(shè)mysql_query() 會成功;;;;在PHP 郵件發(fā)送清單中,新的PHP 用戶會共同詢問:執(zhí)行腳本時為什么會發(fā)生下面的錯誤消息:;;;;Warning:0 is not a MySQLresult index in file on line n;;;;這個消息將為零的結(jié)果集標(biāo)識符的值傳遞給了期望有效結(jié)果集的一些函數(shù)(如提取行的函數(shù))。這意味著早時的mysql_query() 調(diào)用返回了零值—就是FA L S E。換句話說就是mysql_query() 失敗,并且在由其他函數(shù)使用它之前,腳本編寫程序?qū)z查返回值并不煩惱。在使用mysql_query() 時,要一直檢測返回值。;;;;如果查詢失敗,結(jié)果將為FA L S E,對這個結(jié)果我們只是打印一條消息(取決于環(huán)境,其他對錯誤的反應(yīng)可能更合適)。如果查詢成功,則mysql_query() 返回結(jié)果集標(biāo)識符。這個返回值在許多方法中是有用的(但不是作為行計數(shù)!)。結(jié)果集標(biāo)識符可用于下列目標(biāo):;;;;將它傳遞給mysql_ n um _ r o w s ( ),來確定結(jié)果集中的行數(shù)。;;;;將它傳遞給mysql_ n um _ f i e l d s ( ),來確定結(jié)果集中的列數(shù)。;;;;將它傳遞給提取行的例程,來提取結(jié)果集的連續(xù)行。這個實例使用了mysql_ f e t c h _r o w ( ),但是還有其他的選擇,我們馬上就會看到它們。;;;;將它傳遞給mysql_ f r e e _ r e s ul t ( ),來釋放結(jié)果集并允許PHP 處理一些與之相關(guān)的源文件。;;;;在mysql_query() 成功地執(zhí)行S E L E C T查詢之后(請參見表8 - 1),PHP 為檢索結(jié)果集提供了幾個提取行的函數(shù)。當(dāng)不再有行時,每個函數(shù)都得到一個結(jié)果集標(biāo)識符作為參數(shù)并返回FA L S E。表8-1PHP 行提取函數(shù)
最基本的調(diào)用是mysql_ f e t c h _ r o w ( ),它返回結(jié)果集的下一行作為一個數(shù)組。數(shù)組的元素通過從0 到mysql_ n um _ f i e l d s ( )-1范圍內(nèi)的數(shù)字索引訪問。下面的實例說明了如何在每一行都提取和打印值的簡單循環(huán)中使用mysql_ f e t c h _ r o w ( ):;;;;變量$row 是一個數(shù)組。可用$row[$i] 訪問它, $ i在這里是數(shù)字列索引。如果熟悉PHP count() 函數(shù),可以試著用它而不要用mysql_num_fields() 來確定每一行的列數(shù)。count() 只計算這個數(shù)組中已設(shè)置值的元素的數(shù)量, PHP 不是與NULL 列值相對應(yīng)的元素設(shè)置值。count ( ) 對返回列數(shù)的度量是不可靠的,因為那不是它想要的。它還用于另外兩種提取行的函數(shù)。 ;;;;第二個提取行的函數(shù)mysql_fetch_array() 在表8 - 1中列出,它與mysql_fetch_row() 相類似,但是由數(shù)字索引和相關(guān)索引返回的數(shù)組元素都是可靠的。換句話說,可以通過數(shù)字或名稱訪問元素:;;;;由mysql_fetch_array() 返回的信息是mysql_fetch_row() 返回的信息的擴展集。除此之外,兩個函數(shù)之間的不同性能是可以忽略不記的,調(diào)用mysql_ f e t c h _ a r r a y ( )可以無特殊性能損耗。;;;;第三個提取行的函數(shù)mysql_ f e t c h _ o b j e c t ( ),返回結(jié)果集的下一行作為對象,這意味著用$row->col_name 語法訪問行的元素。例如,如果從President 表中檢索last_name 和first_name 值,可以用$row->last_name 和$row->first_name 訪問那些列:;;;;在查詢結(jié)果中測試NULL 值;;;;可以使用isset() 函數(shù)測試SELECT 查詢返回的列值是否為NULL。例如,如果行包含在$row 數(shù)組中,那么如果$row[$i] 對應(yīng)于NULL 值,則isset($row[$i]) 就為FA L S E,如果$row[$i] 為非NULL 值,則isset($row[$i]) 就為T R U E。相關(guān)的函數(shù)是e m p t y ( ),但是對于NULL 和空字符串,empty() 返回的結(jié)果都是一樣的,因此作為NULL 值測試而言,這個函數(shù)是無用的。;;;;如果查詢包括計算的列怎么辦?例如,發(fā)布一個作為表達(dá)式結(jié)果計算的返回值的查詢:;;;;SELECT CONCAT(first_name," ",lsat_last_name)FROM president;;;;這樣編寫的查詢不適于使用mysql_ f e t c h _ o b j e c t ( )。選擇的列名本身就是表達(dá)式,它不是合法的屬性名。然而,可以通過給列賦予一個別名來提供合法的名稱。下面的查詢將列的別名命名為f ul l _ name,如果用mysql_fetch_object() 提取結(jié)果,就允許它以$row->full_name 形式來訪問:;;;;SELECT CONCAT(first_name," ",lsat_last_name) AS full_name FROM president處理錯誤;;;;PHP提供了三種處理錯誤的方法:;;;;■ 用‘@’取消錯誤消息。可以對一些顯示消息的函數(shù)使用‘ @’。當(dāng)我們調(diào)用mysql_pconnect() 阻止來自于函數(shù)的錯誤消息不在發(fā)送到客戶機的頁面上出現(xiàn)時,就已經(jīng)在做這一點了。;;;;使用error_reporting() 函數(shù)。這個函數(shù)按下列級別將錯誤報告打開或者關(guān)閉:
錯誤級別 錯誤報告類型1 正常函數(shù)錯誤2 正常警告4 分析程序錯誤8 請注意為了控制錯誤報告,可調(diào)用error_reporting() 函數(shù),且參數(shù)為想要激活的級別的總和。關(guān)閉級別1和級別2警告應(yīng)該完全能夠取消來源于MySQL的消息:;;;;error_reporting(4+8);;;;;你可能不想關(guān)閉有關(guān)分析錯誤的級別4警告,如果關(guān)掉了,可能要有一段艱難的時間用來調(diào)試它對腳本造成的改動!級別8警告常常被忽略,但有時它指出腳本中應(yīng)該注意的問題,因此您可能也想把它激活。還有16和3 2錯誤級別,它們都來自于PHP 核心發(fā)動機,而非函數(shù),因此通常情況下不必考慮它們。;;;;使用mysql_error() 和mysql_er r n o ( )。這些函數(shù)報告了MySQL服務(wù)器返回的錯誤信息。它們與相應(yīng)的C API 調(diào)用相類似。mysql_error() 以字符串形式返回錯誤信息(如果不發(fā)生錯誤就返回空字符串)。mysql_errno() 返回一個錯誤數(shù)字(如果不發(fā)生錯誤就返回0)。兩個函數(shù)都有指定與MySQL服務(wù)器連接的連接標(biāo)識符參數(shù),在返回狀態(tài)的連接上都返回最近調(diào)用的MySQL函數(shù)的錯誤信息。連接標(biāo)識符是可選的,如果缺失,就使用最近打開的連接。例如,可以這樣報告mysql_query() 的錯誤:;;;;mysql_error() 和mysql_errno() 的PHP 版本在一個重要方面與C API 中對應(yīng)的部分不同。在C 中,即使試圖連接服務(wù)器失敗,也會得到錯誤信息。相反, PHP 調(diào)用直到連接建立成功,才返回有用的連接信息。換句話說,如果連接失敗,就不能用mysql_error() 和mysql_er r n o ( ) 報告失敗原因。如果要報告連接失敗的特殊原因而不是普通原因,則必須做特殊的考慮。請參閱附錄H“PHP API 參考,其中詳細(xì)介紹了如何去做。;;;;當(dāng)檢測到錯誤時,本章的腳本打印了相當(dāng)普通的錯誤信息,如“查詢失敗。然而在開發(fā)腳本時,您會發(fā)現(xiàn):加入一個mysql_error() 調(diào)用對幫您發(fā)現(xiàn)錯誤的特殊原因是很有用的。引用問題;;;;在PHP 中構(gòu)造查詢字符串時,知道引用問題是必要的,就像在C 和Perl 中一樣。雖然函數(shù)名在各種語言中是不同的,但處理引用問題的方法卻是類似的。假設(shè)正在構(gòu)造一個,將新的記錄插入到表中的查詢,可以在值的前后加上引號插入到字符串列中:;;;;這里的問題是引用值的本身還包含著引號(“O’M a l l e y),如果將查詢發(fā)送到MySQL服務(wù)器會導(dǎo)致語法錯誤。在C 中我們調(diào)用mysql_escape_string() 解決這個問題。在Perl DBI腳本中則使用quote( ) 。PHP 有一個addslashes( ) 函數(shù)可以完成同樣的事情。例如,調(diào)用a d d s l a s h e s(“O’ Malley)返回“O’ Malley 值。將前面的實例做如下編寫來解決引用問題:;;;;DBI quote( ) 方法是把前后的引號加到字符串中。addslashes( ) 則不是,因此我們?nèi)孕柙诓樵冏址幸迦胫档闹車鷮⒛切┮栵@式地指定出來。;;;;當(dāng)編寫信息出現(xiàn)在Web 頁面上時也將發(fā)生引用問題。如果正在編寫一個將作為HTML 或U R L的部分出現(xiàn)的字符串,而且這個字符串包含HTML 或URL 內(nèi)部的特殊字符,最好將它編碼。PHP 函數(shù)htmlspecialchars( ) 和urlencode( ) 可以做到這點,它們與C G I . p mescapeHTML( ) 和escape( ) 方法相類似。
相關(guān)文章:
1. Nginx+php配置文件及原理解析2. windows服務(wù)器使用IIS時thinkphp搜索中文無效問題3. 用PHP+MySql編寫聊天室4. PHP laravel實現(xiàn)基本路由配置詳解5. PHP5.0正式發(fā)布 不完全兼容PHP4 新增多項功能6. php的mysql性能優(yōu)化7. PHP輸出控制功能在簡繁體轉(zhuǎn)換中的應(yīng)用 8. PHP 實現(xiàn) WebSocket 協(xié)議原理與應(yīng)用詳解9. Thinkphp5文件包含漏洞解析10. Thinkphp5分頁后攜帶參數(shù)跳轉(zhuǎn)傳遞功能實現(xiàn)
