深入理解php高級技巧、面向對象與核心技術原書_第1頁
深入理解php高級技巧、面向對象與核心技術原書_第2頁
深入理解php高級技巧、面向對象與核心技術原書_第3頁
深入理解php高級技巧、面向對象與核心技術原書_第4頁
深入理解php高級技巧、面向對象與核心技術原書_第5頁
免費預覽已結束,剩余102頁可下載查看

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1PHP與初學者相比,高級PHP以理解的特性中獲益。舉例來說,盡管我們已經知道如何使用數組,但是對數組未必精通:如創(chuàng)建數據、對其進行排序等操作。或許我們已經知道如何編寫自定義函數,但是不一定知道如何使用遞歸以及靜態(tài)變量。本章將討論這類問題以及其他一些非基礎的概念,比如原的語法以及 數由于數組的強大和靈活性,使其在PHP編程中得到了廣泛的應用。對于高級應用來說,數組經常用來解決其他第一個例子將展示如何對一個數組進行排序。這是一lis, 在PHP

圖1-1數組的應用方式之sort()、ksort等相關函數。使用它們,可以對一維數組進行關鍵字排序、按值排序、逆排序$a=array('key1'=>23,'key2'=>'this'),array('key1'=>894,'key2=>'that')PHP5.4中新增了數組的縮寫語法,這為創(chuàng)建數組提供了一種簡便的方式。使用數組的縮寫語法的方式也很簡單,將調用array()函數替換為方括號即可。舉例://Old$a=array();//$b=array('this'=>//New$a=[];//$b=['this'=>這是一個簡單的二維數組(數組的元素本身也是數組key1(數值排序)或key2(字符順序排序。為了對數組進行排序,我們需要定義自己的排序函數,然后告訴PHP在調用usort()、uasort()或者uksort()的時候使用我們的自定義排序函數。它必須接收兩個參數,并且返回一個值表示哪個參數應該排面。負數或者false意味著第一個參數應該排在第二個參數前面。正數或者true則表示第二個參數應該排面。如果值為0,則舉例來說 為了對前面提到的數組根據第一個關鍵字進行排序,我們的自定義排序函數可以寫成這樣:functionasc_number_sort($x,returntrue;{return}elsereturn}}PHP中這樣使用它(1-2usort($a,PHP會把內層數組不斷地發(fā)送給這個函數,從而將其排序。如果我們想要了解細節(jié),那么可以輸出函數里被比較的數值,我們可以看到自定義排序函數是如何被調用的,如圖1-3所示。Usort()函數根據數值進行排序,但是它不保存關鍵字(對于外層數組來說。當我們使用uasort()函數時,關鍵字就會被保存。當我們使用uksort()函數時,排序將基于 圖1-2數組根據數值排序 圖1-3輸出$x['key1']和$y['key1']的如果想對前例數組的第二個關鍵字進行排序,就需要對字符串進行比較。相關代碼如圖1-4functionstring_sort($x,{return?}usort($a,1-4key2按字母進行排()在文本編輯器或集成開發(fā)環(huán)境(IDE)中創(chuàng)建一個PHP,命名為sort.php。該文件以一段HTML代碼開始,如1.1所示。<!doctype<html??<link?<?php#Script1.1-1.1這個定義了一個二維數組 <!doctype<html<metacharset="utf-<title>SortingMultidimensional<linkrel="stylesheet"<?php#Script1.1-sort.php/*Thispagecreatesamultidimensional*ofnamesand*Thearrayisthensorted*oncebynameandonceby//Createthe//Array//studentID=>array('name'=>'Name','grade'=>$students= 256=>array('name'=>'Jon','grade'=> 2=>array('name'=>'Vance','grade'=>9=>array('name'=>'Stephen','grade'=>364=>array('name'=>'Steve','grade'=>68=>array('name'=>'Rob','grade'=> //Namesortingreturnstrcasecmp($x['name'],31//Gradesorting//SortinDESCENDINGfunctiongrade_sort($x,$y)return($x['grade']<37//Printthearrayasuasort($students,echo'<h2>ArraySortedByName</h2><pre>'.print_r($students,1).'</pre>';//Sortbyuasort($students,echo'<h2>ArraySortedByGrade</h2><pre>'.print_r($students,1).'</pre>'; 可能你注意到了我在本所有的示例中使用了HTML5,即便這不會對PHP代碼產生任何影響。我還使用了一個來自HTML5Boilerplate(/)的簡單 $students=256=>array('name'=>?'grade'=>?'grade'=>?'grade'=>364=>array('name'=>?'grade'=>68=>array('name'=>?'grade'=>外層數組$students有5IDfunctionname_sort($x,{return?}trcaecmp()函數會返回一個數值—負數、0或者正數,表示這兩個字符的相似程度。回一個數,表示二個符串。果返回0則表示兩個字符串完全等。unctiongrade_srt($x,{return($x['grade]?$y['grade}這個例子類似前面介紹的示例程序,不過更加精簡。一個明顯的區(qū)別是這個例子會進行降序排序,將最好的成績排在最前面。這很容易實現:把比較操作符從大于修改為小于就可true值,這也就意味著第二個參數應該排在順序列表的前面。echo'<h2>ArrayAsIs</h2><pre>'使用print_r()函數來快速打印出數組的內容。為了提高可讀性,打印的內容將使用<pre>進行包裹uasort($students,?($students,1).因為這里使用了uasort()函數,所以關鍵字,也就是學生的ID,不會丟失,如圖1-5所示。如果只是使用了函數usort(),排序結果將會把這些關鍵字丟掉,如圖1-6所示。uasort($students,?Grade</h2><pre>'.?($students,1).保存文件為sort.php,將其放置于你的 圖1-5數組按照排 圖1-6參看1.1,如果 圖1-7數組根據成績降序排使用函數uasort將會導致關如果我們回想一下,大多數數據庫查詢都返回一個數組,如圖1-8所示。如果查詢結果只是作為一個整體發(fā)送,那么這種數組的結構并不會讓代碼變得復雜。但如果想對 圖1-8從數據庫中選擇包含多個字段的多條記錄就產生了一個數eb務列表系統(tǒng)。如果任務列表是一維的,就不會有什么難度。但是如果列表是可以嵌套的,其中每一項任務又可以有很多個步驟,那么就會得到一種樹形的結構,其中每個分支都可以具1-9所示。SubsubtaskSubsubtaskSubtaskAnotherMustDoSubtaskSubtaskCREATETABLEtaskstask_idINTUNSIGNEDNOT?parent_idINTUNSIGNEDNOT?DEFAULTdate_addedTIMESTAMPNOTNULL,pletedTIMESTAMP,PRIMARYKEY(task_id),INDEXaddedINDEXcompleted

1-9嵌套的工作列表在結構上類似task10MustDotask10MustDo20Another30MyNew42Subtask52Subtask62Subtask Subsubtask數據類型VARCHAR(100);如果需要更長的描述,可以把它定義為文本類型(texttype)。最后項都有一個parent_id一個子任務,那么它的parent_id的值就是對應的列表項的值,如圖1-10所示。如果這個列表項不是子任務,它的

1-10這個表格展示的數據和1-81-圖的中一樣。在字段task_id和parent_id之間有偽外鍵和主鍵的對應關系0。這是為了實現靈活的嵌套結構的簡單設置,但是在PHP中的處理會花費一些功夫。表。接下來的內容介紹如何在這個表中增加新任務的PHP。在本章接下來的幾個在你的文本編輯器或者集成開發(fā)環(huán)境中新建一段PHP,命名為php,以下面的HTML代碼開始,如1.2所示<!doctype<html<title>Adda<link?<?php#Script1.2-$dbc=本例中使用的是MySQL,而且在代碼中使用了改進的MySQL函數。你需要改變對應的.2 使用以下向數據庫中添加任務。利用下拉菜單可以將任務歸于其他任務執(zhí)行<!doctype<html<metacharset="utf-<title>Adda<linkrel="stylesheet"<?php#Script1.2-add_task.php*Thepagebothdisplaysandhandlesthe//Connecttothe$dbc=mysqli_connect('localhost','username','password','test'); //Sanctifytheinput...//Theparent_idmustbean$parent_id=}else$parent_id= //Escapethe$task=mysqli_real_escape_string($dbc,strip_tags($_POST['task']));$q="INSERTINTOtasks(parent_id,task)VALUES($parent_id,$r=mysqli_query($dbc,$q);//Reportontheecho'<p>Thetaskhasbeen}elseecho'<p>Thetaskcouldnotbe //Displaytheecho'<formaction="add_task.php"<legend>Adda<p>Task:<inputname="task"type="text"size="60"maxlength="100"<p>ParentTask:<selectname="parent_id"><optionvalue="0">None</option>';//Retrieveall pleted$q='SELECTtask_id,parent_id,taskFROMtasks pleted="0000-00-00?ORDERBYdate_added$r=mysqli_query($dbc,//Alsostorethetasksinanarrayforuse$tasks=//Fetchthewhile(list($task_id,$parent_id,$task)=mysqli_fetch_array($r,{ //Addtothe$tasks[]=array('task_id'=>$task_id,'parent_id'=>$parent_id,'task'=>$task); //Completetheechofunctionparent_sort($x,$y)return($x['parent_id']> usort($tasks,//Displayalltheecho'<h2>CurrentTo-Doforeach($tasksas$task)echo echo該表單有一個主文本框和一個下拉菜單,如圖1-11所示。為了檢查表單的提交情況,這個條件語句查看這個請求方法是命名為確保parent_id的值是個整數:?FILTER_VALIDATE_INT,$parent_id?}else$parent_id=

1-11HTML表}這里parent_id的值是另一個任務的task_id值。其數據來源于一個下拉菜單,這意味著它來確保這里的值是比1大的整數。如果因為某種原因,不是這種情況,那么其值將為0。這里提到的過濾擴展在PHP5.2PHP內核。如果對其不甚了解,可以參考PHP手冊。$task=?myqli_real_escape_tring()函數會處理提交的描述任務的字符串,使其對于查詢操作來說是安全的。為了防止跨站注入XS),trip_tags()函數也同時用于檢查描述任務的字符串。$q="INSERTINTO?(parent_id,task)if(mysqli_affected_rows($dbc)?1)?}elseecho'<p>Thetaskcouldnot?}}//Endofsubmissionecho'<form?<legend>Adda<p>Task:<input?type="text"??一個有效值為0,表示不隸屬于其他任何任務的任務。?taskFROMtasksWHERE?completed="0000-00-00?ORDERBYdate_added這個查詢操作返回每個未完成任務的三個信息(一旦一個任務已經完成,它的date_completed字段將會被賦予一個非零值。這里我們只選擇未完成的任務,因為向一個已經完這里的task_id和任務本身將用在下拉菜單中,parent_id將稍后用于嵌套任務。$tasks=圖1-12所示。這個數組將保存第二個列表中被使用的任務。1-12這個頁面有兩個地方包含了任務列while(list($task_id,?$task)=echo"<option??$task_id,'parent_id'}這個while1-131-13PHP代碼生成的下拉菜單的HTMLecho?value="AddThisparent_idfunctionparent_sort($x,{return($x['parent_id']?$y[}usort($tasks,這里,parent_id的作用在于區(qū)分務和子任務,所以PHP foreach($tasksas$task)echo?}echo這個循環(huán)會基于parent_id1-1所示列表的第一步。但是從圖1-12中可以看出,列表還沒有達到其應有的結構,把文件保存為add_task.php并放到我們的 如圖1-14所示

1-14針對一個存在的任務添加PHP5“悄悄地”為函數的參數增加了類型約束(typehintin)定函數中的參數是什么類型的變量。舉例來說,下面的代碼表示這個函數有一個唯一的數組類型的參數類型限制,但是標量類型如字符串和整型除外。正如你將在本書后面章節(jié)中看到的那樣,類6章中詳細講解。高級函數定PHP程經驗的程序員,也應該已經在代碼中創(chuàng)建了很多自定義函數了。但是在高級編程領域,自4遞歸靜態(tài)?以方式接收?函遞歸(recursion)就是函數自functionsomefunction()//Somecode.//Possibleother} functionlist_dir($start)$contents=scandir($start);foreach($contentsas$item){if(is_dir("$start/$item")//Use$item.}else//Use}//Endofif-}//Endof}//Endoffunction.函數的執(zhí)行操作(最后一行)調用函數list_dir('.'),設定當前 作為起點。在該函數中,scandir()函數得 的內容,然后利用foreach循環(huán)遍歷每一個條目。如果該條目,函數將被再次調用,但是使用新的作為起點參數。遞歸函數將在每個子被在使用遞歸的時候,需要注意兩件事情。第一個事情就是,像使用循環(huán)一樣,我們需要確保函數中有一個退出(ou)語句:到達某個條件,將使函數終止調用它自己。在上面的例子中,函數將在遍歷完子第二件需要注意的事情是,從服務器資源的角度來說,遞歸函數的成本是比較高的。需要銘記在心的是,每一次函數調用都是需要消耗內存和處理器的,并且第一次調用必須等到其他所有調用都結束時才會結束。遞歸的層次如果比較深,比如100次遞歸,就有可能使服務器死機。簡單地說,有時遞歸是唯一的解決方案,但是通常情況下應使用循環(huán)更加高效。在本章開頭創(chuàng)建的任務列表中,獲取和展示所有的任務項目不是很難(參見圖1-12)。然而,在add_task.php(1.2)中定義的方法并不能完全適合在圖1-1中的嵌套任務結構。在文本編輯器或者集成開發(fā)環(huán)境中新建一個PHP,并命名為view_tasks.php,并且以下面的HTML代碼開始(詳細代碼見1.3)。<!doctype<html<h2>CurrentTo-Do<?php#Script13-function{global$tasks;echo'<ol>';這個函數接收一個數組作為參數,在參數內部需要能夠數組$tasks(主數組)—我1.3 <!doctype<html<metacharset="utf-<title>View<linkrel="stylesheet"<?php#Script1.3-view_tasks.php/*Thispageshowsallexisting*Arecursivefunctionisusedtoshow*tasksasnestedlists,as//Functionfordisplayinga//Receivesoneargument:anfunction{//Needthemain$tasksglobalecho'<ol>';//Startanorderedlist.//Loopthrougheachforeach($parentas$task_id=>{//Displaytheecho//Checkfor//Callthisfunction echo'</li>';//Completethelistitem. echo'</ol>';//Closetheorderedlist. }//Endofmake_list()function.//Connecttothe$dbc=mysqli_connect('localhost','username','password','test');//Retrieveall pleted$q='SELECTtask_id,parent_id,taskFROMtasks pleted="0000-00-00?ORDERBYparent_id,date_added$r=mysqli_query($dbc,//Initializethestorage$tasks=//Loopthroughthewhile(list($task_id,$parent_id,$task)=mysqli_fetch_array($r,{//Addtothe //For//Sendthefirstarray//tothemake_list() foreach($parentas$task_id?$todo)echoforeach循環(huán)將遍歷整個數組,并在<li>中顯示每個元素if{}這是這個最重要的部分。從數據庫獲取的任務列表可能是如圖1-15所示的那樣的一個數組。對于主數組來說,關鍵字是parent_id,而元素是位于這個parent_id之下的任務集合。因此,在打印出了初始的<li>和任

圖1-15這段PHP (或者,這個任務是不是一個父任務元素?)。換句話說,在$tasks里是否有元素的關鍵字的值是這個任務的ID?如果有,當前任務就有子任務,因此這個函數需要被再次調用,以數組的這一部分(關鍵字的值是這個task_id,其值是子任務數組的元素)作為參數。foreachecho}//EndofFOREACHloop.echo'</ol>';}//Endofmake_list()$dbc=中,然后調用一次make_list()函數。?taskFROMtasksWHERE?completed="0000-00-00?ORDERBYparent_id,?這個查詢獲取每個任務項的3個信息:ID、parent_id以及任務的內容。其中的條件表示只選擇未完成的任務。得到的結果以parent_id進行排序,所以頂級任務(parent_id為0的任務)會首先返回。次要排序參數是date_added,可以讓任務按照其被添加的次序返回(假定$tasks=while(list($task_id,?$task)=$tasks[$parent_id][$task_id]?}這里的$tasks數組將保存二維數組中的每一項任務,如圖1-15所示。正如在第4步中介紹的那樣,數組最外層的關鍵字是來自數據表的parent_id的值,而最外層數據的元素是具有相應的parent_id值的任務。?在處理數組時,知道和理解相應的結構是至關重要的。如果取消這一行的注釋去1-15make_lit雖然變量$tasks是個數組,但是make_list()函數只需要被調用一次,并把第一個數組元素傳遞給它。這個元素的值是一個由parent_id為0的任務組成的數組。在這個函數中,保存文件為view_tasks.php并放到 中,然后在瀏覽器中測試,如圖1-

使用add_task.php來添加的子任務,重新在瀏覽器中測試該,如圖1-圖1-16任務頁面,以嵌套形式顯示任務列 圖1- make_list()$tasks在使用遞歸函數時 事實上,在使用任何可能會被多次調用的函數時,我們都應考慮使用靜態(tài)變量。靜態(tài)變量讓函數在多次被調用時記住變量的值,而這些變量并不是全局變量。舉例來說,make_lit()函數可以重寫一,完整的任務列表作為其第二個參數并且為可選參數,賦給一個靜態(tài)本地變量static$tasks=}make_list($tasks[0],1-17中,顯示被比較的數值并不難,但是統(tǒng)計遞歸的次數就需要使用靜態(tài)變量了。為了演示這種用法,sort.php將按照下面的步驟進行修改。像下面這樣來修改name_sort()函數(詳見1.4):functionname_sort($x,{static$count=?return?}1.4 <!doctype<html<metacharset="utf-<title>SortingMultidimensional<linkrel="stylesheet"<?php#Script1.4-sort2.php/*Thispagecreatesamultidimensional*ofnamesand*Thearrayisthensorted*oncebynameandonceby*Astaticvariablehasbeenaddedto*functionstoseehowmanytimestheyare//Createthe//Array//studentID=>array('name'=>'Name','grade'=>$students= 256=>array('name'=>'Jon','grade'=> 2=>array('name'=>'Vance','grade'=>9=>array('name'=>'Stephen','grade'=>364=>array('name'=>'Steve','grade'=>68=>array('name'=>'Rob','grade'=> //Namesortingfunctionname_sort($x,$y)//Showiterationsusingastaticstatic$count=echo"<p>Iteration$count:{$x['name']}vs.returnstrcasecmp($x['name'], //Gradesorting//SortinDESCENDING//Showiterationsusingastaticstatic$count=echo"<p>Iteration$count:{$x['grade']}vs.return($x['grade']< uasort($students,echo'<h2>ArraySortedByName</h2><pre>'.print_r($students,1).'</pre>';//Sortbyuasort($students,echo'<h2>ArraySortedByGrade</h2><pre>'.print_r($students,1).'</pre>'; 這個函數里添加了3行代碼,第一行了一個靜態(tài)變量,名稱為$count。它的初始值為1,但是這個賦值操作只在函數第一次被調用時執(zhí)行(因為它是一個靜態(tài)變量。然后下一的值。最后,變量$count的值被增加1。functiongrade_sort($x,{static$count=??}這里為grade_sort()函數添加的3行代碼和添加在name_sort()函數中的是一樣的,只是其中用于比較的關鍵字是grade而不是name。保存文件,并命名為sort2.php,放在Web 圖1-18所示。(同時我也移除了打印數組原始結構的那行代碼)為$students數組增加一些額外的元素,然后再次運行,結果如圖1-19所示圖1-18對原始的5元素數組按照進 圖1-19向主數組增加3個元后,根排序需要調用6次排序函 名字的排序需要20次遞歸調1.2.3函本部分要討論最后一個高級話題:函數(anonymousfunction,亦稱lambda)的使用。簡單地說,一個函數就是一個沒有名字的函數。創(chuàng)建函數的特性是在PHP5.354創(chuàng)建一個函數的方式和我們創(chuàng)建其他函數沒有什么不同,只是,它沒有一個名字。然而,為了方便之后這個函數例如該,這個函數的定義需要賦值給個變量。 o={echo 看上去這種做法有些不太容易理解,但是前提其實很簡單:就像賦值給一個字符串或者整數類型的變量一樣,我們只是給一個函數定義賦值。由于這個原因,在賦值語句結束時需粗略來說為了調用函數(當然在其被定義好了之圖1-20所示。圖1-20通過變量調 這種使用函數的特殊方式有其優(yōu)勢所在,但是也有一種更加明顯和簡單的方式來使用函數,我們需要討論一下。PHP中一些函數可以使用另一個函數作為其參數,舉例來說,array_map()函數接收一個函數作為其第一個參數,第二個參數是個數組,數組內的每個functionformat_names($value)//Dowhateverwith}array_map('format_names', 函數,你可以在函數參數內定義一個函數array_map(function($value)//Dowhateverwith},?由于函數是被直接使用的,因 只需要函數的定義就可以了 的時候。在接下來的步驟中,最后更新一次sort.php的代碼來使 函數。注意PHP5.3刪除當前對函數name_sort()和grade_sort()的定義代碼(1.5)。修改第一次對uasort()函數的調用,這里使用函數的方式uasort($students,function($x,$y)return?修改第二次對uasort()函數的調用,這里使用函數的方式uasort($students?保存文件為sort3.php,放到Web 121PHP5.3之前的版本,我們也可以使用create_function()函數來創(chuàng)建和函數類似的函數。

1-21最終結果沒有因為使用函數可以用做閉包,一個公認的高級概念。閉包在PHPJavaScript1.5 <!doctype<html<metacharset="utf-<title>SortingMultidimensional<linkrel="stylesheet"<?php#Script1.5-sort3.php/*Thispagecreatesamultidimensional*ofnamesand*Thearrayisthensorted*oncebynameandonceby*Thisversionusesanonymous//Createthe//Array//studentID=>array('name'=>'Name','grade'=>$students= 256=>array('name'=>'Jon','grade'=> 2=>array('name'=>'Vance','grade'=>9=>array('name'=>'Stephen','grade'=>364=>array('name'=>'Steve','grade'=>68=>array('name'=>'Rob','grade'=> uasort($students,function($x,$y)returnstrcasecmp($x['name'],32 echo'<h2>ArraySortedByName</h2><pre>'.print_r($students,1).'</pre>';//Sortbyuasort($students,function($x,$y)return($x['grade']<38 echo'<h2>ArraySortedByGrade</h2><pre>'.print_r($students,1).'</pre>'; 變量本身。也就是說,函數使用的其實是變量的一個副本。這種行為的結果就是在該函數內functionincrement($var)}$num=2;echo$num;//Still取代這種默認行為的一個方法是,將這個函數的參數傳遞方式設置為按照傳遞,而的值,而不需要使用全局變量。第二個好處是關于性能方面的。在需要傳遞的數據比較大的場景按傳意著PHP不需要去構建一份數的副本。對于字符串或數字來說,PHP不需要functionincrement(&$var)}$num=2;echo$num;//或 functionincrement($var)}$num=2;echo$num;//3原型文檔語原型文檔(heredo)是一種封裝字符串的替代方式,它不像標準的單引號或者雙引號那樣使用頻繁,但是其具有相同的功能。使用原型文檔就像是把花生和黃油抹在香蕉上一樣:原型文檔的工作方式和雙引號一樣,它包裹的內容將被打印出來,但是它還允許我們自己定義分界符。在要打印出大量的TL代碼包自身的雙引)的時候,原型文原型文檔以<<<開始,后面緊跟著分界符。分界符通常需要是全部大寫的單詞,它只能在字符串的末尾使用相同的分界符,但是這次沒有符號<<<。結束分界符必須位于單獨1-22$var=echo<<<EOTSomevar$varThisvar$that$string=<<<EODstringwith$var\necho圖1-22從結果中可以看出,使用原型文檔語法的結果和使引號的完全一使用ED和ET作為分界符是比較常見的方式(它們一般不會出現在字符串中,但是這并不是必須的。原型文檔的語法是個不錯的選擇,但是,說得更透徹些,它又是十分特殊的。如果語法不是百分之百準確無誤,甚至只為了展示原型文檔的使用,我們來重寫文件view_tasks.php,讓用戶可以對任務進行標記,如圖1-23所示。在文本編輯器或者集成開發(fā)環(huán)境中打開文件view_tasks.php(1.3)。make_list(函數中,把打印任務的相關語句修改為(如1.6所示):

1-23查看任務列表的頁面現在多了復選框,echo?1.6 最初的view_tasks.php代碼(1.3)修改為一個表單,讓用戶可以對任務進行標記,這里使用原型文檔來創(chuàng)建一些HTML標記<!doctype<html<metacharset="utf-<title>View<linkrel="stylesheet"<?php#Script1.6-view_tasks2.php/*Thispageshowsallexisting*Arecursivefunctionisusedtoshow*tasksasnestedlists,as*Taskscannowbemarkedas//Functionfordisplayinga//Receivesoneargument:anfunctionmake_list($parent)globalecho'<ol>';//Startanorderedforeach($parentas$task_id=>{//Startwithaecho<li><inputtype="checkbox"name="tasks[$task_id]"value="done">//Checkfor echo'</li>';//Completethelist}//EndofFOREACHecho'</ol>';//Closetheordered}//Endofmake_list()function.//Connecttothe$dbc=mysqli_connect('localhost','username','password','test');if(($_SERVER['REQUEST_METHOD']==&&&&&&!empty($_POST['tasks']))//Definethe$q='UPDATEtaskspleted=NOW()WHEREtask_idIN//Addeachtaskforeach($_POST['tasks']as$task_id=>$v) $q.=$task_id.', //Completethequeryand$q=substr($q,0,-2).$r=mysqli_query($dbc,//Reportontheif(mysqli_affected_rows($dbc)==count($_POST['tasks']))echo'<p>Thetask(s)havebeenmarkedas}elseecho'<p>Notalltaskscouldbemarkedas 67}//Endofsubmission//Retrieveall pleted$q='SELECTtask_id,parent_id,taskFROMtasks pleted="0000-00-00?ORDERBYparent_id,date_added$r=mysqli_query($dbc,$tasks=while(list($task_id,$parent_id,$task)=mysqli_fetch_array($r,MYSQLI_NUM))$tasks[$parent_id][$task_id]= //Makeaecho<p>Checktheboxnexttoataskandclick"Update"tomarkataskas?(it,andanysubtasks,willnolongerappearinthis<formaction="view_tasks2.php" //Completetheecho<inputname="submit"type="submit"value="Update" echo"<li><input??echo'<li><input?name="tasks['. ?&&isset($_POST['tasks'])只有在表單提交,$_POST[taks']有一個值,并且是一個非空的數組的時候,數據庫才會被更新使任務標記為已完成。哪怕只有一個復選框被選中,$_POST[tasks']也會是一個數組。$q='UPDATEtasksSET?IN?$task_id=>$v)$q.=$task_id.',}$q=substr($q,0,-2).UPDATEtasks ?NOW()WHEREtask_idIN(X,Y, pleted字段設置為當前日期和時間,這樣它就不會顯 pleted為空的任務條目。if?==count($_POST['tasks'])){?markedas}elseecho'<p>Notalltasks?bemarkedas}}//Endofsubmission在調用make_list()函數之前,先添加初始表單echo<p>Checktheboxnexttoa?andclick"Update"tomark?taskascompleted(it,and?subtasks,willnolonger?inthis<form由于make_list()函數的執(zhí)行效果,因此,只要父任務被標記為已經完成,子任務就不會在調用make_list()函數之后結束表單。echo把文件保存為view_tasks2.php,放在Web 目選中,標記為已完成,如圖1-23所示,然后提交表單,如圖1-24所示。1-24更新之后的任務列知識拓展:nowdocPHP5.3nowdoc語法。nowdoc相對于原型文檔正如單引號對于雙引號一樣。也就是說,nowdoc提供了另外一種封裝字符串的方法,但是在nowdoc里的任何變量不會被從語法上來說,nowdoc遵循和原型文檔一樣的規(guī)則,唯一的區(qū)別在于,nowdoc中的分$var=stringwith$var澄清一下,$string$ver23printf()對于大多數PHP程序員來說,只需要使用print()和echo()這兩個函數來顯示文本和變量就足夠了。但是高級程序員則偶爾會使用更為復雜的printf()函數,這個函數不僅能顯示文本,還可以設置輸出的格式。PHP手冊中對這個函數的定義如下:printf(stringformat[,?format是由直接文本變量和特殊格式參數組成的字符串。特殊格式參數由百分?符號說明符(+/–)可以強制?填充說明符表示用于右填充的字符(空格是默認的,但是對于數字來說可能需要使用0)。?對齊說明符(默認是右對齊的,使用減號(–)可以實現左對齊?一個數字,用于表示要占用的最小寬度。?精度說明符,表示浮點數應該顯示小數點后的多少位(或者字符串里的多少個字符?類型說明符,見表1-11-1類型說明字含字含b二進制f浮點c依照ASCII值的字o八進制d帶符號十進制整s字符e科學x十六進制u無符號十進制這些看起來似乎挺復雜的,但是也確實如此。我們可以從數字開始練習使用它,如圖1-25所示。printf('b:%b<br>c:%c?%d<br>f:%f,80,80,80,這是同一個數字的4種不同的表示方式。第一個格式把80顯示為二進制數字,第二個格式顯示80對應的ASCII字符(也就是大寫的P),第三個格式是個整型數,第四個格式是在此基礎上,從中選擇兩種最常用的數字類型—d和f,然后再增加一些其他格式,如圖1-26所示。printf('%0.2f<br>%+d<br>%02f?8,8,圖1-25使用四種不同的類型說明符

1-26使用printf函數設置數字的首 數字8以浮點數表示,小數點后面有兩位小數,不足的位數以0填充。然后,81235.456顯示為具有兩位小數的浮點數(結果中展示printf('Thecostof%d%sat$%0?sprintf()printf幾乎完全一樣,但是它不是顯示格式化的字符串,而

1-27混合打印出數字和字符在文本編輯器或者集成開發(fā)環(huán)境中打開add_task.php(1.2)。刪除對函數mysqli_real_escape_string()的調用(如1.7所示 定義INSERT語句的那行代碼做如下修改:$q=sprintf(INSERTINTO?(parent_id,task)??當我們使用printf()SL命令和變量的方式來創(chuàng)建查詢語句了。盡管這里的例子中開始的代碼也不算太難看,但是對于更加復雜的查詢語來照寫能”會包很多$var[index']}這樣的東西,而且還很容易產生錯誤,不方便調試。使用sprintf()函數的方法可以有效地把查詢和要使用的數據分離,同時還能夠集成函數1.7對add_task.php頁面做些小的修改(如1.1所示),展示另一種簡潔的創(chuàng)建<!doctype<html<metacharset="utf-<title>Adda<linkrel="stylesheet"<?php#Script1.7-add_task2.php/*Thispageaddstaskstothetasks* $dbc=mysqli_connect('localhost','username','password','test');$parent_id=}else$parent_id= ?mysqli_real_escape_string($dbc,$r=mysqli_query($dbc,$q); echo'<p>Thetaskhasbeen }else echo'<p>Thetaskcouldnotbe //Displaytheecho'<formaction="add_task2.php"<legend>Adda<p>Task:<inputname="task"type="text"size="60"<p>ParentTask:<selectname="parent_id"><option$q='SELECTtask_id,parent_id,taskFROMtasks pleted="0000-00-00?ORDERBYdate_added$r=mysqli_query($dbc,$tasks=while(list($task_id,$parent_id,$task)=mysqli_fetch_array($r,MYSQLI_NUM))echo"<option$tasks[]=array('task_id'=>$task_id,'parent_id'=>$parent_id,'task'=> echo把表單的actionadd_task2.phpvie_task.php頁面)都能夠以更好的方式顯示任務列表,所以沒有必要在這里包含這些代碼。保存文件為add_task2.php,將其放在中,在瀏覽器中進試,如圖1-28所示

1-28頁面的運行情況和之前沒有區(qū)printf('Thetaxrateis?vprintf()與printf()從格式參數上來說,sscanf()和fscanf()函數分別等同于printf()和sprintf()scanf()函數用于輸入,而fscanf()用于從文件數據1.5回顧和啟示◆回顧和啟在本書的新版中,每一章將以一個“回顧和啟示”小節(jié)結束。在這些小節(jié)中,看到關于本章中討論過的知識材料相關的一些問題,還會提示讀者如何通過去擴展自己的如果你對本章有任何問題,可以本書的支持 /forums/)使用什么函數去按值對一個數組進行排序?如果排序中要保留關鍵字呢?如何按關鍵字對數組進行排序?什么是函數?在PHP的哪個版本中引入了函數使用原型語法有哪些規(guī)則要遵循?和其他的替代方案相比,原型文檔的方式有什么優(yōu)勢?printf()和sprintf()1)sort.php,使用自定義函數的方式更優(yōu)雅地產生出結果。2)完成add_task.php文件,使其包含合適的錯誤處理。 使用本章中提供的建議之一,比如通過傳遞參數,或者使用靜態(tài)變量,更()如果你好奇,可以在網上搜索一些PHP中使用函數的例子view_tasks.php,使其可以顯示已經完成的和將要完成的任務列表,并以不在view_tasks.php文件中增加一個,在URL中傳遞一個參數,用于標示是所有任務都需要顯示出來,還是只顯示未完成的任務。根據傳入的參數值修改SQL查詢語句。修改文件尋找關于printf()和sprintf()函數的其他用法。提示:查看PHP手冊中關于這些函2WebPHP程序員的職業(yè)生涯通常是從專門完成一個特定功能的獨立的開始的。然后再逐漸使用越來越多的文件來建立Web應用程序,最后可能會在自己的服務器上開發(fā)站點。如果幸運的話,還可能會讓站點使用多臺不同的服務器。無論的項目有多大,在PHP程序員的日常工作中,學習開發(fā)Web應用程序的新的和改進的方式都是重要的組成部分。本章中,著重介紹初中級以上的Web應用程序的開發(fā)。從解釋如何模塊化一個開始。然后學到一些關于ApacheWeb服務器的知識,包括使用它的mod_rewrite特性,用來生成對搜索引擎優(yōu)化(SEO)規(guī)則友好的地址(URL)。以筆者的經驗來說,程序員的成長是從編寫完成比較單能的單個頁面程序開始的。一段時間之后,工作內容將會變?yōu)殚_發(fā)含有多個頁面的,并且包括模板以及狀態(tài)管理。在不斷累一經驗后,PHP程序員開始利用較少的頁面來完成和多面同樣多的工作。比如可以讓同一個既顯示表單同時又處理表單,不再使用兩個獨立的文件。或者,恰相,高級PHP程序員開始編寫一系列具單能的程序,它們分別完成特定工作這最式正對eb站點行塊化設計的前提。這里舉例說明模塊化一個站點的方式。創(chuàng)建一個偽Web站點(也就是說,組成部分進行分解、組織以及合成到一起。取代使用不同的單獨頁面的方式(比如contact.php、由于這個應用程序沒有使用數據庫,因此這里我們沒有為其創(chuàng)建一個數據庫配置文件。如果需要使用數據庫,我們可以編寫創(chuàng)建數據庫連接的文件,命名為myql.inc.php或者potgresql.incphp、db.incphp這個文件應該可以保存在 下,但是最好保存在 con?g.inc.phpDB,用于指定這個文件在服務器上的絕對路徑。 我們開發(fā)的每個Web程序都會由配置文件開始。配置文件可以有很多作用。最重要的定義??管理從根本上說,站點內的每個頁面都有可能需要的信息就應該保存在配置文件中。(這里順便提一下,對于不是大部分頁面都需要的函數,把它放在單獨的文件里,從在21在21<?php#Script2.1-2.1 配置文件是關鍵的,它定義了范圍內的常數,并且指明了如何1<?php#Script2.1-234*Filename:5*Createdby:LarryE.6*Contact:,7*Lastmodified:June5,8**Configurationfiledoesthefollowing*-Hassitesettingsinone*-StoresURLsandURIsas*-Setshowerrorswillbe #*****SETTINGS*****// ed ='a //Determinewhetherwe'reworkingonalocal//oronthereal}else //DeterminelocationoffilesandtheURLofthe//Allowfordevelopmentondifferent{//Alwaysdebugwhenrunning$debug=TRUE;//Definethe }{ *Mostimportant*The$debugvariableisusedtoseterror if($p=='thismodule')$debug= *Todebugtheentiresite,do $debug= *beforethisnext //Assumedebuggingis $debug= #*****SETTINGS***** #**************************** #*****ERRORMANAGEMENT***** //Createtheerror functionmy_error_handler($e_number,$e_message,$e_file,$e_line,{ global$debug,$contact_; //Buildtheerror $message="Anerroroccurredinscript'$e_file'online$e_line:$e_message"; //Append$e_varstothe $message.=print_r($e_vars, if($debug){//Showtheerror. echo'<divclass="error">'.$message. }{ error_log($message,1,$contact_);//Send. //Onlyprintanerrormessageiftheerrorisn'tanoticeor if(($e_number!=E_NOTICE)&&($e_number<2048)) echo'<divclass="error">Asystemerroroccurred.Weapologizefor? 105}//Endofmy_error_handler()definition.//Usemyerror#*****ERRORMANAGEMENT*****#*****************************Filename:*Createdby:LarryE.*Contact: ?*Lastmodified:June5,**Configurationfildoes?following*-Hassitesettingsin?*-StoresURLsandURIs?*-Setshowerrorswill?由于配置文件是比較通用的文件,因此它應該是程序里注釋最好的。 ? 對于實際運行的站點來說,我比較傾向于用戶在發(fā)現錯誤時能夠通過電子郵件的方式通知到我,因此我了一個變量,為每個通用的交流信息保存一個“發(fā)送到”的電子郵件地址。這個地址可以是的郵箱,但是一旦開發(fā)的站點實際運行了,這就應該是站點管理員的電子郵箱地址。$host=$local=}else$local=}我通常都會在本地服務器開發(fā),然后把完整的代碼上傳到實際的服務器。這兩種環(huán)境具有各自特定的設置。在理想情況下,配置文件應該可以根據所處的服務器環(huán)境自動為了測試站點是否運行于本地環(huán)境,我們首先檢查一下變量的前?單詞local(在中用到?IP地址的開始,192.1或者127.0,兩種情況都說明條件是否滿足,$local變量將會被賦予一個布爾值(真或者假),以便在后面的中使用。if($local)$debug=?'/path/to/html/folder/');?'?}{?? ?}我經常在自己的Web應用程序中使用3個常量。其中,常量BASE_URI在服務器上的絕對路徑。這個常量可以在中包含一個文件的時候很方便地使用絕對路徑來文件。如果你正在使用indows中的XAMPP開發(fā)環(huán)境,這個值就有可能是這樣的:xampphtdocs\ch02。常量BASE_URL是主機名和 是。最后,常量DB是包含數據庫連接信息的文件的絕對路徑。出于安全方面的考慮,這個常量最好保存在Web 可以注意到每一個常量在不同的環(huán)境下有不同的設置:一種是在測試服務器,另一種是在正式的線上服務器。如果是在測試服務器上($localTRUE),我同時還會打開$debug=}我們還使用了變量$debug$debug的值會為TRUE $debug=function?($e_number,?$e_file,$e_line,{global P允許我們定義自己的錯誤處理函數,來取代使用內置的處理機制。關于這個過程或者語的信息可以考P手冊者我一本書《PndyLorWebte:ialuckartudebook(acpte,2012)。$message="Anerroroccurred$message.=print_r($e_vars,出于調試的目的,錯誤消息中應該盡可能多地包含信息。首先,它應該發(fā)生錯誤的文件名以及行號。然后加上每個已經存在的變量的值。這可能會是大量的數據,如圖2-1所2-1當調試站點時錯誤消息的顯if($debug){//Showtheerror.echo<divclass="error">'.?$message 如圖2-1在開發(fā)一個站點的時候是非常有用的,但是對實際運行的站點來說,可能算是一個巨大的安全。這段代碼將把這些信息顯示在一個DIV中,這個有個類名為error,通過SS的設計也就說,以創(chuàng)一個殊的DIV或者不用它。}else? if(($e_number!=E_NOTICE)?($e_number<2048)){?Asystemerror?Weapologizefortheinconvnince}}//Endof$debug對于線上運行的站點來說,不應該顯示錯誤的詳細信息除非出于調試的目的暫時啟動相應頁面的調試模式,而是應該通過郵件的方式發(fā)送錯誤消息到設置的郵箱。函數error_log()的第二個參數為1時就能完成這種操作。但是對于用戶來說,需要知道站點出現了問題,因此站點會顯示一個通用的錯誤提示消息,如圖2-2所示。如果錯誤是個通知級別的錯誤,或者嚴格性錯誤值為2048,不應該有任何信息打印出來。因為這種錯誤一般不會影響頁面的操作。2-2在實際運行的站點上,錯誤消息的處理方式更加謹慎(和安全}//Endof?definition.?保存文件為con?g.inc.php,并放置在你的Web 下(在一個叫做includes的子圖2-3顯示了這個 結構。注意到這里我沒有使用一個PHP的關閉(closingPHPtag)。這在PHP內是可以接受的并且對于這種會被其他包含的文件來說,圖2-3根 (images)、包含文件(includes)以及模塊(modules)等將被用到像你的代碼結構或者文檔一樣,在開發(fā)大型的Web應用程序的時候,另外一個比較重要拆分成同的面和 。主eb檔 ,我之為html,我們應有一個子目錄至,乎有會么、一個保存類的 如使了對編的、一個函數 等。更進一,為了安全起見我比較傾于你去使用一個個性化名字來命名些文件夾。對有用戶來說,任何時候都不知道文件和文檔的名字,總是一件好事情。如果你對站點的管理模塊使用的名字為admin,從安全的角度來說,這樣不會給你帶來任何好處。TL以使用Smarty(.net)或者其他的模板系統(tǒng),但是我還是比較喜歡只使用兩個簡單的文件:一個頁頭文件包含頁面特定的內容之前的全部內容,一個頁腳文件包含頁面對于這個模板,使用HTML5WebTemplates(wwwhtml5webtemplates.co.uk)設計的HTML為了給Web站點創(chuàng)建一個模板,要獨立于所有的PHP代碼設計一個像標準的HTML頁面一樣的布局。對于這里的例子來說,正如前面提到的,使用ColourBlue設計模板,如圖2-4所示。注意為了節(jié)省本書篇幅,本例中使用的CSS文件(用于控制布局的)沒有包含在本書里,讀者可以從本書的支持( <!DOCTYPE<div這里第一個文件將包括原始的HTML(從DOCTYPE到頭部標記再到頁面主體的開始。它還包含形成瀏覽器頂部欄和右側邊欄的代碼,如圖2-4所示。書中在此將跳過大量的HTML代碼。完整的代碼請參見2.2或者直接從本書的支持站點。2-4本站將為所有的頁面使用的模<title><?phpecho?我們希望頁面的標題(出現在Web24colour_blue)能夠在逐頁的基礎上產生變化。為此,里使用一個變量來表示標題,它將通過PHP輸出。2.2 HTML模板的開始。它包含CSSPHP變//ThispagebeginstheHTMLheaderforthesite.//Checkfora$page_titleif(!isset($page_title))$page_title='DefaultPage?><!DOCTYPE8<title><?phpecho$page_title;<linkrel="stylesheet"type="text/css"href="style/style.css"title="style"<div<div<div<div<h2>Simple.Contemporary.Website<div <ul <li><a<li><a<li><a<li><a<li><a <div<div<h3>Latest<h4>NewWebsite<h5>January1st,<p>2010seestheredesignofourwebsite.Takealookaroundandletusknowwhat?think.<br/><ahref="#">Read<h3>Useful <li><ahref="#">link <li><ahref="#">link <formmethod="get"action="index.php" <inputtype="hidden"name="p"value="search"<inputclass="search"type="text"name="terms"value="Search..."<inputname="search"type="image"style="border:0;margin:00-9px?src="style/search.png"alt="Search"title="Search"<div<!--Endofheader.--HTMLPHP<?php#Script2.2-header.htmlif(!isset($page_title))為了防止有些PHP在包含頁頭文件時可能忘記設置$page_title,所以在此我們設置了一個默認的頁面標題(當然可以使用更加明確了錯誤報告,那么在頁面沒有設置$page_title時,瀏覽器的標題會變得很,如圖2-5所示。保存文件為headerhtml。文件的擴展名。有些程序員喜歡使用.inc

圖2-5要確保$page_title變量的值已經被這是個包含文件,這里我們還可以使用.inchtml作為其擴展名,同時表示它既是一個包含文 又是一個HTML文件(以便于和全部由PHP代碼構成的包含文件區(qū)分開來。把從頁面特定的內容結束到頁面末尾的內容全部到一個新的文件,如2.3<div<divCopyright©colour_blue?<ahref="??HTML5</a>|<a?check/referer">CSS</a>?<a??from?頁腳文件包含頁面的剩余格式,包括頁面的頁腳和結束HTML文檔的。保存文件為footer.html。將兩個文件都放在Web服務器的 2.3頁腳文件完成整個HTML模板<!--#Script2.3-footer.html--<div<divCopyright©colour_blue?<ahref="??HTML5</a>|<a?/css-?<ahref="??from索引文件是模塊化程序中的主要,事實上它是唯一會被加載進Web瀏覽器的頁面。這種構建方法的技術術語叫做引導文件(bootstrap?le,它通常也是在基于框架的系統(tǒng)中Web頁面。這可能涉及以???合并HTML模?按照慣例來說,引導文件不會包含任何HTML代碼,因為這些必不可少的HTML代碼在文本編輯器或者集成開發(fā)環(huán)境中新建一段PHP,并命名為index.php,2.4<?php#Script2.4-24索引頁面的控制著一切,它決定應該包含哪個模塊、需要的配置文件,并且合并HTML模板 <?php#Script2.4-index.php *Thisisthemain*Thispageincludestheconfiguration*thetemplates,andanycontent-specific8//RequiretheconfigurationfilebeforeanyPHP//Validatewhatpageto $p= $p=}else$p= //Determinewhatpagetoswitch{case$page=$page_title='AboutThiscase$page=$page_title='Contactcase$page=$page_title='Search//Defaultistoincludethemain$page=$page_title='SiteHome //Makesurethefileif(!file_exists('./modules/'.$page))$page=$page_title='SiteHome //Includetheheader//Includethecontent-specific//$pageisdeterminedfromtheaboveinclude('./modules/'.$page); //Includethefooterfiletocompletethe $p=$p=}else$p=}要顯示的內容是基于發(fā)送給這個頁面的值。當用戶單擊時,這個值會通過URL發(fā)送過來。當大部分表單提交時,這個值會通過$_POST發(fā)送。如果不是上述兩種情況之一,腳本就把$p設置為NULL。switchswitch{case$page=$page_title=每個模塊的文件名稱都是xxx.inc.php,我們用這種方式表示它既是一個PHP,同時也是一個包含文件。根據計算機處理擴展名的方式,只有最后一個擴展名是真正有效的(也就是說,如果直接運行這種文件,它會被當做PHP來處理。完成switch語句:case$page=case$page=$page_title$page=$page_title}//Endofmain每個可能的內容模塊都包含在switch語句中。出于安全的目的,default語句是很重要的。如果$p沒有值或者不是有效值(語句中指定的某個值),就會使用main.inc.php文件。這是必不可少的安全步驟,因為一些用戶在看到站點的URL形如index.php?p=contact的時候會在瀏覽器地址欄中嘗試輸入類似index.php?p=/path/to/secret/?le這樣的內容。在這種情if(!file_exists('/modules/'?$page))$page=$page_title='SiteHome}switch語句的每個條件設置的

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論