深入理解php高级技巧、面向对象与核心技术原书_第1页
深入理解php高级技巧、面向对象与核心技术原书_第2页
深入理解php高级技巧、面向对象与核心技术原书_第3页
深入理解php高级技巧、面向对象与核心技术原书_第4页
深入理解php高级技巧、面向对象与核心技术原书_第5页
已阅读5页,还剩102页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1PHP与初学者相比,高级PHP以理解的特性中获益。举例来说,尽管我们已经知道如何使用数组,但是对数组未必精通:如创建数据、对其进行排序等操作。或许我们已经知道如何编写自定义函数,但是不一定知道如何使用递归以及静态变量。本章将讨论这类问题以及其他一些非基础的概念,比如原的语法以及 数由于数组的强大和灵活性,使其在PHP编程中得到了广泛的应用。对于高级应用来说,数组经常用来解决其他第一个例子将展示如何对一个数组进行排序。这是一lis, 在PHP

图1-1数组的应用方式之sort()、ksort等相关函数。使用它们,可以对一维数组进行关键字排序、按值排序、逆排序$a=array('key1'=>23,'key2'=>'this'),array('key1'=>894,'key2=>'that')PHP5.4中新增了数组的缩写语法,这为创建数组提供了一种简便的方式。使用数组的缩写语法的方式也很简单,将调用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会把内层数组不断地发送给这个函数,从而将其排序。如果我们想要了解细节,那么可以输出函数里被比较的数值,我们可以看到自定义排序函数是如何被调用的,如图1-3所示。Usort()函数根据数值进行排序,但是它不保存关键字(对于外层数组来说。当我们使用uasort()函数时,关键字就会被保存。当我们使用uksort()函数时,排序将基于 图1-2数组根据数值排序 图1-3输出$x['key1']和$y['key1']的如果想对前例数组的第二个关键字进行排序,就需要对字符串进行比较。相关代码如图1-4functionstring_sort($x,{return�}usort($a,1-4key2按字母进行排()在文本编辑器或集成开发环境(IDE)中创建一个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}这个例子类似前面介绍的示例程序,不过更加精简。一个明显的区别是这个例子会进行降序排序,将最好的成绩排在最前面。这很容易实现:把比较操作符从大于修改为小于就可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所示。如果查询结果只是作为一个整体发送,那么这种数组的结构并不会让代码变得复杂。但如果想对 图1-8从数据库中选择包含多个字段的多条记录就产生了一个数eb务列表系统。如果任务列表是一维的,就不会有什么难度。但是如果列表是可以嵌套的,其中每一项任务又可以有很多个步骤,那么就会得到一种树形的结构,其中每个分支都可以具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。在本章接下来的几个在你的文本编辑器或者集成开发环境中新建一段PHP,命名为php,以下面的HTML代码开始,如1.2所示<!doctype<html<title>Adda<link�<?php#Script1.2-$dbc=本例中使用的是MySQL,而且在代码中使用了改进的MySQL函数。你需要改变对应的.2 使用以下向数据库中添加任务。利用下拉菜单可以将任务归于其他任务执行<!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的作用在于区分务和子任务,所以PHP foreach($tasksas$task)echo�}echo这个循环会基于parent_id1-1所示列表的第一步。但是从图1-12中可以看出,列表还没有达到其应有的结构,把文件保存为add_task.php并放到我们的 如图1-14所示

1-14针对一个存在的任务添加PHP5“悄悄地”为函数的参数增加了类型约束(typehintin)定函数中的参数是什么类型的变量。举例来说,下面的代码表示这个函数有一个唯一的数组类型的参数类型限制,但是标量类型如字符串和整型除外。正如你将在本书后面章节中看到的那样,类6章中详细讲解。高级函数定PHP程经验的程序员,也应该已经在代码中创建了很多自定义函数了。但是在高级编程领域,自4递归静态❏以方式接收❏函递归(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.函数的执行操作(最后一行)调用函数list_dir('.'),设定当前 作为起点。在该函数中,scandir()函数得 的内容,然后利用foreach循环遍历每一个条目。如果该条目,函数将被再次调用,但是使用新的作为起点参数。递归函数将在每个子被在使用递归的时候,需要注意两件事情。第一个事情就是,像使用循环一样,我们需要确保函数中有一个退出(ou)语句:到达某个条件,将使函数终止调用它自己。在上面的例子中,函数将在遍历完子第二件需要注意的事情是,从服务器资源的角度来说,递归函数的成本是比较高的。需要铭记在心的是,每一次函数调用都是需要消耗内存和处理器的,并且第一次调用必须等到其他所有调用都结束时才会结束。递归的层次如果比较深,比如100次递归,就有可能使服务器死机。简单地说,有时递归是唯一的解决方案,但是通常情况下应使用循环更加高效。在本章开头创建的任务列表中,获取和展示所有的任务项目不是很难(参见图1-12)。然而,在add_task.php(1.2)中定义的方法并不能完全适合在图1-1中的嵌套任务结构。在文本编辑器或者集成开发环境中新建一个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循环将遍历整个数组,并在<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在使用递归函数时 事实上,在使用任何可能会被多次调用的函数时,我们都应考虑使用静态变量。静态变量让函数在多次被调用时记住变量的值,而这些变量并不是全局变量。举例来说,make_lit()函数可以重写一,完整的任务列表作为其第二个参数并且为可选参数,赋给一个静态本地变量static$tasks=}make_list($tasks[0],1-17中,显示被比较的数值并不难,但是统计递归的次数就需要使用静态变量了。为了演示这种用法,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行代码,第一行了一个静态变量,名称为$count。它的初始值为1,但是这个赋值操作只在函数第一次被调用时执行(因为它是一个静态变量。然后下一的值。最后,变量$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)的使用。简单地说,一个函数就是一个没有名字的函数。创建函数的特性是在PHP5.354创建一个函数的方式和我们创建其他函数没有什么不同,只是,它没有一个名字。然而,为了方便之后这个函数例如该,这个函数的定义需要赋值给个变量。 o={echo 看上去这种做法有些不太容易理解,但是前提其实很简单:就像赋值给一个字符串或者整数类型的变量一样,我们只是给一个函数定义赋值。由于这个原因,在赋值语句结束时需粗略来说为了调用函数(当然在其被定义好了之图1-20所示。图1-20通过变量调 这种使用函数的特殊方式有其优势所在,但是也有一种更加明显和简单的方式来使用函数,我们需要讨论一下。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()函数来创建和函数类似的函数。

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所示。在文本编辑器或者集成开发环境中打开文件view_tasks.php(1.3)。make_list(函数中,把打印任务的相关语句修改为(如1.6所示):

1-23查看任务列表的页面现在多了复选框,echo�1.6 最初的view_tasks.php代码(1.3)修改为一个表单,让用户可以对任务进行标记,这里使用原型文档来创建一些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()函数的执行效果,因此,只要父任务被标记为已经完成,子任务就不会在调用make_list()函数之后结束表单。echo把文件保存为view_tasks2.php,放在Web 目选中,标记为已完成,如图1-23所示,然后提交表单,如图1-24所示。1-24更新之后的任务列知识拓展:nowdocPHP5.3nowdoc语法。nowdoc相对于原型文档正如单引号对于双引号一样。也就是说,nowdoc提供了另外一种封装字符串的方法,但是在nowdoc里的任何变量不会被从语法上来说,nowdoc遵循和原型文档一样的规则,唯一的区别在于,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混合打印出数字和字符在文本编辑器或者集成开发环境中打开add_task.php(1.2)。删除对函数mysqli_real_escape_string()的调用(如1.7所示 定义INSERT语句的那行代码做如下修改:$q=sprintf(INSERTINTO�(parent_id,task)��当我们使用printf()SL命令和变量的方式来创建查询语句了。尽管这里的例子中开始的代码也不算太难看,但是对于更加复杂的查询语来照写能”会包很多$var[index']}这样的东西,而且还很容易产生错误,不方便调试。使用sprintf()函数的方法可以有效地把查询和要使用的数据分离,同时还能够集成函数1.7对add_task.php页面做些小的修改(如1.1所示),展示另一种简洁的创建<!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页面的运行情况和之前没有区printf('Thetaxrateis�vprintf()与printf()从格式参数上来说,sscanf()和fscanf()函数分别等同于printf()和sprintf()scanf()函数用于输入,而fscanf()用于从文件数据1.5回顾和启示◆回顾和启在本书的新版中,每一章将以一个“回顾和启示”小节结束。在这些小节中,看到关于本章中讨论过的知识材料相关的一些问题,还会提示读者如何通过去扩展自己的如果你对本章有任何问题,可以本书的支持 /forums/)使用什么函数去按值对一个数组进行排序?如果排序中要保留关键字呢?如何按关键字对数组进行排序?什么是函数?在PHP的哪个版本中引入了函数使用原型语法有哪些规则要遵循?和其他的替代方案相比,原型文档的方式有什么优势?printf()和sprintf()1)sort.php,使用自定义函数的方式更优雅地产生出结果。2)完成add_task.php文件,使其包含合适的错误处理。 使用本章中提供的建议之一,比如通过传递参数,或者使用静态变量,更()如果你好奇,可以在网上搜索一些PHP中使用函数的例子view_tasks.php,使其可以显示已经完成的和将要完成的任务列表,并以不在view_tasks.php文件中增加一个,在URL中传递一个参数,用于标示是所有任务都需要显示出来,还是只显示未完成的任务。根据传入的参数值修改SQL查询语句。修改文件寻找关于printf()和sprintf()函数的其他用法。提示:查看PHP手册中关于这些函2WebPHP程序员的职业生涯通常是从专门完成一个特定功能的独立的开始的。然后再逐渐使用越来越多的文件来建立Web应用程序,最后可能会在自己的服务器上开发站点。如果幸运的话,还可能会让站点使用多台不同的服务器。无论的项目有多大,在PHP程序员的日常工作中,学习开发Web应用程序的新的和改进的方式都是重要的组成部分。本章中,着重介绍初中级以上的Web应用程序的开发。从解释如何模块化一个开始。然后学到一些关于ApacheWeb服务器的知识,包括使用它的mod_rewrite特性,用来生成对搜索引擎优化(SEO)规则友好的地址(URL)。以笔者的经验来说,程序员的成长是从编写完成比较单能的单个页面程序开始的。一段时间之后,工作内容将会变为开发含有多个页面的,并且包括模板以及状态管理。在不断累一经验后,PHP程序员开始利用较少的页面来完成和多面同样多的工作。比如可以让同一个既显示表单同时又处理表单,不再使用两个独立的文件。或者,恰相,高级PHP程序员开始编写一系列具单能的程序,它们分别完成特定工作这最式正对eb站点行块化设计的前提。这里举例说明模块化一个站点的方式。创建一个伪Web站点(也就是说,组成部分进行分解、组织以及合成到一起。取代使用不同的单独页面的方式(比如contact.php、由于这个应用程序没有使用数据库,因此这里我们没有为其创建一个数据库配置文件。如果需要使用数据库,我们可以编写创建数据库连接的文件,命名为myql.inc.php或者potgresql.incphp、db.incphp这个文件应该可以保存在 下,但是最好保存在 config.inc.phpDB,用于指定这个文件在服务器上的绝对路径。 我们开发的每个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�由于配置文件是比较通用的文件,因此它应该是程序里注释最好的。 � 对于实际运行的站点来说,我比较倾向于用户在发现错误时能够通过电子邮件的方式通知到我,因此我了一个变量,为每个通用的交流信息保存一个“发送到”的电子邮件地址。这个地址可以是的邮箱,但是一旦开发的站点实际运行了,这就应该是站点管理员的电子邮箱地址。$host=$local=}else$local=}我通常都会在本地服务器开发,然后把完整的代码上传到实际的服务器。这两种环境具有各自特定的设置。在理想情况下,配置文件应该可以根据所处的服务器环境自动为了测试站点是否运行于本地环境,我们首先检查一下变量的前❏单词local(在中用到❏IP地址的开始,192.1或者127.0,两种情况都说明条件是否满足,$local变量将会被赋予一个布尔值(真或者假),以便在后面的中使用。if($local)$debug=�'/path/to/html/folder/');�'�}{�� �}我经常在自己的Web应用程序中使用3个常量。其中,常量BASE_URI在服务器上的绝对路径。这个常量可以在中包含一个文件的时候很方便地使用绝对路径来文件。如果你正在使用indows中的XAMPP开发环境,这个值就有可能是这样的:xampphtdocs\ch02。常量BASE_URL是主机名和 是。最后,常量DB是包含数据库连接信息的文件的绝对路径。出于安全方面的考虑,这个常量最好保存在Web 可以注意到每一个常量在不同的环境下有不同的设置:一种是在测试服务器,另一种是在正式的线上服务器。如果是在测试服务器上($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,出于调试的目的,错误消息中应该尽可能多地包含信息。首先,它应该发生错误的文件名以及行号。然后加上每个已经存在的变量的值。这可能会是大量的数据,如图2-1所2-1当调试站点时错误消息的显if($debug){//Showtheerror.echo<divclass="error">'.�$message 如图2-1在开发一个站点的时候是非常有用的,但是对实际运行的站点来说,可能算是一个巨大的安全。这段代码将把这些信息显示在一个DIV中,这个有个类名为error,通过SS的设计也就说,以创一个殊的DIV或者不用它。}else� if(($e_number!=E_NOTICE)�($e_number<2048)){�Asystemerror�Weapologizefortheinconvnince}}//Endof$debug对于线上运行的站点来说,不应该显示错误的详细信息除非出于调试的目的暂时启动相应页面的调试模式,而是应该通过邮件的方式发送错误消息到设置的邮箱。函数error_log()的第二个参数为1时就能完成这种操作。但是对于用户来说,需要知道站点出现了问题,因此站点会显示一个通用的错误提示消息,如图2-2所示。如果错误是个通知级别的错误,或者严格性错误值为2048,不应该有任何信息打印出来。因为这种错误一般不会影响页面的操作。2-2在实际运行的站点上,错误消息的处理方式更加谨慎(和安全}//Endof�definition.�保存文件为config.inc.php,并放置在你的Web 下(在一个叫做includes的子图2-3显示了这个 结构。注意到这里我没有使用一个PHP的关闭(closingPHPtag)。这在PHP内是可以接受的并且对于这种会被其他包含的文件来说,图2-3根 (images)、包含文件(includes)以及模块(modules)等将被用到像你的代码结构或者文档一样,在开发大型的Web应用程序的时候,另外一个比较重要拆分成同的面和 。主eb档 ,我之为html,我们应有一个子目录至,乎有会么、一个保存类的 如使了对编的、一个函数 等。更进一,为了安全起见我比较倾于你去使用一个个性化名字来命名些文件夹。对有用户来说,任何时候都不知道文件和文档的名字,总是一件好事情。如果你对站点的管理模块使用的名字为admin,从安全的角度来说,这样不会给你带来任何好处。TL以使用Smarty(.net)或者其他的模板系统,但是我还是比较喜欢只使用两个简单的文件:一个页头文件包含页面特定的内容之前的全部内容,一个页脚文件包含页面对于这个模板,使用HTML5WebTemplates(wwwhtml5webtemplates.co.uk)设计的HTML为了给Web站点创建一个模板,要独立于所有的PHP代码设计一个像标准的HTML页面一样的布局。对于这里的例子来说,正如前面提到的,使用ColourBlue设计模板,如图2-4所示。注意为了节省本书篇幅,本例中使用的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代码构成的包含文件区分开来。把从页面特定的内容结束到页面末尾的内容全部到一个新的文件,如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浏览器的页面。这种构建方法的技术术语叫做引导文件(bootstrapfile,它通常也是在基于框架的系统中Web页面。这可能涉及以❏❏❏合并HTML模❏按照惯例来说,引导文件不会包含任何HTML代码,因为这些必不可少的HTML代码在文本编辑器或者集成开发环境中新建一段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=}要显示的内容是基于发送给这个页面的值。当用户单击时,这个值会通过URL发送过来。当大部分表单提交时,这个值会通过$_POST发送。如果不是上述两种情况之一,脚本就把$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/file这样的内容。在这种情if(!file_exists('/modules/'�$page))$page=$page_title='SiteHome}switch语句的每个条件设置的

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论