发新话题
打印

PHP与MySQL 5程序设计(第2版)

当前章节:3.11 小结

3.11 小结
虽然这一章没有后面章节的内容那么有意思,但要成为成功的PHP程序员,这一章所打下的基础有着非凡的意义,因为后面的所有功能都以此为基石。这一点很快就能看到。
下一章将主要介绍如何构造和调用函数(函数是用来完成某种特定任务的可重用代码块)。这是构造模块化、可重用PHP应用程序的第一步。
网络无限,生活无限

TOP

当前章节:4.1 调用函数

即使在简单的应用程序中,也可能存在重复的处理。对于更大规模的应用程序,这样的重复更是少不了。例如,在电子商务应用程序中,可能需要多次查询客户的概要信息,登录时、结账时以及验证送货地址时都要查询客户概要信息。但是,在整个应用程序中重复查询概要信息的过程不仅容易出错,而且维护也将成为一个恶梦。如果向客户的概要信息增加了一个新字段该怎么办?也许要查看应用程序的每一个页面,根据需要修改查询,但由此可能引入新的错误。
令人欣慰的是,在所有流行的计算机语言中,很早就引入了一个概念作为其重要组成部分之一,允许将这些重复的过程嵌入在一个命名的代码块中,然后在必要时调用这个名。这些代码块称为函数(function),如果将来要修改嵌入的过程,这就提供了一个方便的修改点,而且只需在这一处做修改,因此可以大大地减少出现编程错误的可能性,还能降低维护的开销。本章将学习PHP函数,包括如何创建和调用函数、传递输入、为调用者返回一个或多个值,以及创建和包含函数库。此外,你将了解递归(recursive)和变量(variable)函数。
4.1 调用函数
标准的PHP发行包中有1000多个标准函数,其中很多都会在本书中出现。假设函数库已经编译到安装发行包中,或者通过include()或require()语句包含了相应函数库,使得函数可用,那么通过指定函数名就可以调用函数。例如,假设希望计算5的三次方,可以如下调用PHP的pow()函数:

如果只是希望输出函数的结果,可以不把这个值赋给变量,而是直接输出,如下:

如果希望在一个更大的字符串中输出函数的结果,就需要进行拼接,如下:
网络无限,生活无限

TOP

当前章节:4.2 创建函数

4.2 创建函数
对于力图避免在编程中闭门造车、一切都重新发明的程序员来说,虽然PHP的众多函数库是一笔巨大的财富,但迟早都会需要标准包所不提供的内容,这意味着你需要创建定制函数,甚至创建整个函数库。为此,需要使用预定义的语法模式来定义一个函数,如下:

例如,考虑下面的函数,generate_footer(),它将输出一个页脚:

定义之后,就可以在任何地方调用这个函数。例如:

这将生成如下结果:


4.2.1 按值传递参数
你会经常发现向函数传递数据很有用。举一个例子来说,创建一个函数,它要确定销售税率,然后将税费增加到价格上,从而计算商品的总价:

这个函数接受两个参数,名为$price和$tax,它们将在计算中使用。尽管这些参数可能是浮点数,但由于PHP的松散类型特点,你完全可以传递任何数据类型的变量,不过输出可能会出乎意料。此外,可以根据需要定义任意多个参数;在这一点上,PHP没有任何强制限制。
定义函数之后,就可以调用这个函数了,如前例所示。例如,salestax()可以有如下调用:

当然,并非只能向函数传递静态值。也可以如下传递变量:

以这种方式传递参数时,称为按值传递或传值(passing by value)。这就意味着,函数范围内对这些值的任何改变在函数外部都会被忽略。如果希望在函数范围外也能反映出这些修改,则可以按引用传递参数,下面将介绍有关内容。
注解   需要说明的是,不要求非得在调用之前定义函数,因为PHP在执行前会把整个脚本读到引擎中。因此,可以在定义之前就调用salestax(),但并不推荐这种随便的做法。

4.2.2  按引用传递参数
在有些情况下,可能希望在函数内对参数的修改在函数范围外也能反映,按引用传递参数就可以满足这种需要。按引用传递参数(也称传引用)要在参数前加上&符号。下面是一个例子:

下面是输出:


注意,$tax的值依然相同,但$cost已经改变。
4.2.3 默认参数值
可以为输入参数指定默认值,在没有提供其他值的情况下,就会把这个默认值自动赋给该参数。下面来修改前面销售税的例子,假设你的销售主要集中在俄亥俄州的弗兰克林县。可以指定$tas的默认值为5.75%,如下:

记住,也可以为$tax传递其他的税率;只有如下调用salestax()时才会使用默认值5.75%:

注意,默认参数值必须是常数表达式;而不能指定函数调用或变量等非常量值。
4.2.4 可选参数
可以指定某个参数为可选(optional)参数,为此,这些参数需要放在参数列表末尾,而且要指定其默认值为空,如下:

这样如果没有销售税,就可以如下调用salestax(),而不指定第二个参数:

这个调用会返回如下内容:


如果指定了多个可选参数,可以选择性地传递某些参数。考虑下面这个例子:

调用calculate()时,可以只传递$price和$price3,如下:

它返回如下值:


4.2.5 从函数返回值
通常情况下,只依靠函数做某些事情还不够;脚本的结果可能取决于函数的结果,也可能取决于在执行函数时对数据的修改。但是,由于变量的作用域的差异,函数体无法很容易地将信息传递给调用者,那么怎样才能完成这个任务呢?可以通过关键字return向调用者传递数据。
1. return()
return()语句可以向函数调用者返回任意确定的值,将程序控制权返回到调用者的作用域。如果return()在全局作用域内调用,将终止脚本的执行。再次修改salestax()函数,假设你不希望将计算的销售税立即显示给用户,而是将这个值返回给调用块:

还有一种方式,可以直接返回计算结果,而不需要赋给变量$total,如下:

下面是调用这个函数的例子:

2. 返回多个值
从函数中返回多个值通常很方便。例如,假设要创建一个函数,从数据库中获取用户数据,比如用户的姓名、电子邮件地址和电话号码,然后返回给调用者。完成这个任务比你想像的要简单得多,使用一个很有用的语言构造list()就可以实现。利用list()构造可以很方便地从数组中获取值,如下:

根据这个示例,你可以想到如何使用list()从函数返回这三个值:

执行此脚本将返回:


这个概念很有用,将在本书中反复使用。
4.2.6 嵌套函数
PHP支持嵌套函数(nesting function),即在函数中定义并调用其他函数。例如,完全可以在salestax()函数中定义并调用一个美元转换为英镑的函数convert_pound(),如下:

注意,PHP不限制嵌套函数的作用域。例如,salestax()之外仍可以调用convert_pound(),如下:

4.2.7 递归函数
递归函数(recursive function)即调用自身的函数,这对于程序员来说,通常有很高的实用价值,常用来将复杂的问题分解为简单的情况,反复做这种处理直到问题解决。
几乎每个入门的递归例子都会使用阶乘计算,这真是没意思。我们来做一点更实际的事情,创建一个还贷款计算器。具体地,下面的例子将使用递归来创建一个还款进度表,告诉你偿还贷款时每次支付的本金和所需的利息。递归函数amortizationTable()如代码清单4-1所列。它需要4个输入参数:$paymentNum标识还款编号,$periodicPayment表示每月总的还款额,$balance表示剩余贷款额,$monthlyInterest指定了每月的利率。这些项在另一个脚本中(名为mortgage.php)指定或确定,如代码清单4-2所列。
代码清单4-1 还贷计算器函数amortizationTable()

设置了相关变量,并完成了一些初步计算之后,代码清单4-2将调用amortizationTable()函数。因为这个函数会递归地调用自身,所以分期付款表的计算都在函数内进行,结束之后,控制权返回给调用者。
代码清单4-2 使用递归的还贷进度计算器


图4-1显示了这个例子的输出。这是一个30年期的固定贷款,贷款总额200 000.00美元,利率6.25%。为了节省篇幅,这里只列出了前10次迭代得出的还款结果。

图4-1 mortgage.php的示例输出

使用递归策略通常能大幅减少代码量,提高重用性。虽然递归函数并不总是最优的解决办法,但它对于任何语言来说都是有益的补充。
4.2.8 变量函数
PHP最吸引人的特性之一是其简洁的语法。但是,在有些情况下,使用更抽象的编程方式能够减少编写代码的许多开销。例如,考虑一种要创建很多数据访问函数的情况,比如要创建retrieveUser()、retrieveNews()和retrieveWeather()等函数,顾名思义,可以很清楚地知道这些函数各自的作用。为了触发给定的函数,可以使用URL参数和if条件语句,如下:

对于这段代码,可以传递以下的URL:

然后,index.php文件使用$trigger来确定将执行哪个函数。虽然这样也能起作用,但很麻烦,尤其是需要很多访问函数时更是如此。还可以通过一种简短得多的方式完成同样的目标:变量函数。变量函数(variable function)是指这个函数的名也要在执行前计算,这意味着函数名直到执行时才确定。就像正常的变量一样,变量函数前面有个美元符,如下:

下面使用变量函数来修改前面的例子:

虽然有时候使用变量函数很方便,但是要记住,它们存在安全风险。最显著的问题是,攻击者可能会修改用来声明函数名的变量,以此来执行PHP的任何函数。例如,如果将前面示例中的$trigger变量设置为exec,将$rowid变量设置为rm –rf,请考虑一下会有什么结果。PHP的exec()命令将“放心地”在系统级执行其参数。而命令rm –rf将从根目录开始,递归地删除所有文件。最终的结果将是灾难性的。因此,一定要确保过滤所有用户信息;否则,你永远不知道接下来会发生什么。
网络无限,生活无限

TOP

当前章节:4.3 函数库

4.3 函数库
伟大的程序员都懒,而懒的程序员都会从重用性的角度考虑问题。函数是实现重用性的核心,通常将函数组织到函数库(library)中,可以在以后类似的应用程序中重复使用。在一个文件中简单地聚集函数定义就可以创建PHP库,如下:

保存这个库,最好使用一个能清楚地说明其用途的命名约定来命名,如taxes.library.php。然后,可以使用include()、include_once()、require()或require_once()将这个函数库插入到脚本中,这几个语句都已经在第3章介绍过。(另外,也可以使用PHP的auto_prepend配置指令自动完成文件插入。)例如,假设将这个库命名为taxes.library.php,可以如下将其包含到脚本中:

一旦包含,就可以在需要时调用函数库中的这3个函数。
网络无限,生活无限

TOP

当前章节:4.4 小结

4.4 小结
本章介绍了现代编程语言的一个基本组成部分:通过函数式编程实现重用性。在这一章中,学习了如何创建和调用函数、如何向函数块传递信息以及从函数块返回信息、嵌套函数,还了解了如何创建递归和变量函数。最后,我们介绍了如何将函数聚集在一起成为函数库,并在需要时将函数库包含到脚本中。
下一章将讨论PHP的数组功能,包括PHP丰富的数组管理功能,并介绍PHP 5新增的数组处理特性。
网络无限,生活无限

TOP

当前章节:5.1 什么是数组

程序员会花很多时间处理成组的相关数据。例如,公司中所有员工的姓名;所有美国总统及其出生日期;1900年到1975年之间的所有年份。事实上,数据集的处理如此普遍,以至于所有主流编程语言中都支持一种常见的方式,用以在代码中管理这些数据组,这一点并不奇怪。这种方式一般都以复合数据类型数组(array)为核心,它提供了一种理想的方法来存储、操作、排序和获取数据集。PHP的解决方案也不例外,它也支持数组数据类型,还支持与数组操作有关的大量行为和函数。这一章将学习PHP所支持的基于数组的所有特性和功能。
本章将介绍很多用于处理数组的函数。在此并不以字母顺序来排列这些函数,而是按如何完成下列操作的顺序来展开介绍:
  ● 输出数组;
  ● 创建数组;
  ● 测试数组;
  ● 添加和删除数组元素;
  ● 定位数组元素;
  ● 遍历数组;
  ● 确定数组大小和元素唯一性;
  ● 数组排序;
  ● 数组合并、拆分、接合和分解。
如果要为将来的某些问题寻求可行的解决方案,而且需要参考本章的内容,函数的这种分类方式可能要比按字母排列有用得多。不过,在开始之前,有必要先花些时间正式地给出数组的定义,并回顾PHP中有关处理这个重要数据类型的一些基本概念。
5.1 什么是数组
传统上把数组(array)定义为一组有某种共同特性的元素,如相似性(车模、棒球队、水果类型等)和类型(例如所有元素都是字符串或整数),每个元素由一个特殊的标识符来区分,这称为键(key)。上面这句话中提到了传统上一词,因为现在可以摒弃这种定义,数组结构中可以包括完全无关的元素。PHP则更进一步,数组中的元素甚至可以不属于同一种类型。例如,一个数组可能包含州名、邮政编码、考试成绩或扑克牌等元素。
每个实体包含两个项:前面提到的键(key)和值(value)。可以通过查询键来获取其相应的“值”。这些键可以是数值(numerical)键或关联(associative)键。数值键与值没有真正的联系,它们只是值在数组中的位置。例如,一个数组中包含按字母顺序排列的美国州名,键0表示"Alabama"(阿拉巴马州),键49表示"Wyoming"(怀俄明州)。使用PHP语法,该数组如下:

使用数值索引,可以如下引用第1个州:

注解   PHP的数值索引数组以位置0起始,而不是1。

与此不同的是,关联键与值有一定关系,而不是值在数组中的位置。使用数值索引值不可行时,以关联的方式来映射数组会特别方便。例如,你可能希望创建一个将州缩写映射到州名的数组,如OH/Ohio、PA/Pennsylvania和NY/New York。使用PHP语法,该数组如下:

可以如下引用“Ohio”:

只由原子实体组成的数组称为一维(single-dimensional)数组。还可以创建包含数组的数组,这称为多维数组(multidimensional arrays)。例如,可以使用一个多维数组存储美国各州的信息。使用PHP语法,该数组如下:

然后可以如下引用Ohio的人口:

这将返回以下值:


除了提供创建和填充数组的方法之外,PHP还必须提供遍历数组的方法。通过本章可以了解到,PHP提供了很多遍历数组的方法。无论使用哪一种方法,要记住,它们都依赖于一种称为数组指针(array pointer)的特性。数组指针就如同书签,告诉你正在检查的数组位置。你并不是直接操作数组指针,而是使用内置的语言特性或函数来遍历数组。但是,理解这个基本概念很有用。
网络无限,生活无限

TOP

当前章节:5.2 输出数组

5.2 输出数组
我们还没有学习PHP中如何创建数组,在此之前了解如何输出数组可能意义不大,但本章中大量地使用了print_r()函数,甚至一般的开发过程中这个函数都会贯穿始终,所以,这一章首先提到这一点会很有好处。
print_r()

print_r()函数接受一个输入,可以是任意的variable,并将其内容发送给标准输出,成功时返回TRUE,否则返回FALSE。这本身并不新奇,但它在显示数组(及对象)前能将其内容组织为可读性很强的格式,考虑到这一点,你就会对它刮目相看了。例如,假设希望查看一个关联数组的内容,这个数组由州及其相应的州府组成。可以如下调用print_r():

这将返回如下结果:

return是一个可选参数,可以用来修改函数的行为,使得将输出返回给调用者,而不是发送到标准输出。因此,如果希望返回以上$states数组的内容,只需将return设置为TRUE:

作为一个显示示例结果的简单方法,这个函数将在本章反复使用。
提示   print_r()函数不是输出数组的唯一方法,而只是提供了一种很便利的方式。也可以使用循环条件(如while或for)来输出数组;事实上,很多应用程序特性的实现都需要使用这种循环。本章及后面章节中将会反复使用这种方法。
网络无限,生活无限

TOP

当前章节:5.3 创建数组

5.3 创建数组
与其他很多语言的数组实现方式不同,PHP不需要在创建数组时指定其大小。事实上,因为PHP是一种松散类型的语言,所以甚至不需要在使用数组前先行声明。尽管没有限制,PHP仍提供了正式和非正式的数组声明方法。两个方法各有优点,都值得学习。本节将分别讨论这两种方法,首先来介绍非正式的方法。
要引用PHP数组中的各个元素,可以用一对中括号来指示。因为数组没有大小限制,所以只需建立引用就可以创建数组,例如:

然后,可以如下显示数组$states的第一个元素:

接下来,可以为数组索引映射新值,从而添加其他的值,如下:

有趣的是,如果认为索引值是数值索引而且是递增的,还可以在创建时省略索引值:

用这种方式创建关联数组也同样很简单,只不过必须一直使用关联索引引用(即不可省略)。下面的示例创建了一个数组,它将美国州名映射到其加入联邦的日期:

下面要讨论的array()函数在创建数组方面也有同样的功能,但更显正式。
1. array()

array()函数接受0个或多个元素作为输入,返回一个包含这些输入元素的数组。下面是一个使用array()创建索引数组的例子:

还可以使用array()创建一个关联数组,如下:

2. list()

list()函数与array()类似,只是它可以在一次操作中从一个数组内提取多个值,同时为多个变量赋值。从数据库或文件中提取信息时,这种构造尤其有用。例如,假设你希望格式化并输出从一个文本文件中读取的信息。文件的每一行都包含用户信息,如姓名、职业和喜爱的颜色,每一项用竖线分隔。典型的一行如下所示:

可以通过一个简单的循环使用list()来读取每一行,将各部分数据赋给变量,按照需要格式化并输出数据。下面显示了如何使用list()同时为多个变量赋值:

然后会读取每一行,并如下格式化:


回顾一下这个例子,list()依靠函数explode()将每一行分解为三个元素,这里,explode()使用竖线作为元素分隔符。(explode()函数将在第9章中正式介绍。)然后,这些元素分别赋给了$name、$occupation和$color。至此,余下的只是如何格式化以及显示到浏览器。
3. range()

range()函数是一个快速创建数组的简单方法,并会使用lowhigh范围的整数值填充数组。这个函数将返回一个包含此范围内所有整数的数组。
作为一个例子,假设需要一个数组,其中包含骰子中所有可能出现的值:

可选的step参数为确定范围内成员之间的增长幅度(步长)提供了一种简便方法。例如,如果希望有一个包含0到20之间所有偶数的数组,就可以使用步长值为2:

range()函数还可以用于字符序列。例如,假设希望创建一个包含字母A到F的数组:
网络无限,生活无限

TOP

5.4 测试数组

在应用程序中使用数组时,有时需要知道某个特定变量是否为一个数组。内置函数is_array()可以用来完成这个工作。
is_array()

is_array()函数确定variable是否为数组,如果是则返回TRUE,否则返回FALSE。注意,即使数组只包含一个值,也将被认为是一个数组。示例如下:

下面是结果:
网络无限,生活无限

TOP

当前章节:5.5 增加和删除数组元素

5.5 增加和删除数组元素
PHP为扩大和缩小数组提供了一些函数。对于那些希望模仿各种队列实现(FIFO、LIFO等)的程序员来说,这些函数可以提供便利。顾名思义,从这些函数的函数名(push、pop、shift和unshift)就清楚地反映出其作用。
注解   传统的队列是一种数据结构,删除元素与加入元素的顺序相同,就称为先进先出,或FIFO。相反,栈是另外一种数据结构,其中删除元素的顺序与加入时的顺序相反,这称为后进先出,或LIFO。

1. $arrayname[ ]
这不是一个函数,而是一个语言特性。只通过赋值就能增加数组元素,例如:

对于数值索引,还可以如下追加一个新元素:

但是,有时候需要一种更复杂的方式来增加数组元素(以及删除数组元素,这种功能无法用上述增加元素的方式来实现)。本节后面将介绍这些函数。
2. array_push()

array_push()函数将variable增加到target_array的末尾,成功时返回TRUE,否则返回FALSE。可以为此函数传递多个变量作为输入参数,同时向数组压入多个变量(元素)。如下例所示:

3. array_pop()

array_pop()函数返回target_array的最后一个元素,并在结束后重置数组的指针。示例如下:

4. array_shift()

array_shift()函数类似于array_pop(),只是它返回target_array的第一个数组元素,而非最后一个。其结果是,如果使用的是数值键,则所有相应的值都会下移,而使用关联键的数组不受影响。示例如下:

与array_pop()一样,array_shift()也会在结束时重置指针。
5. array_unshift()

array_unshift()函数与array_push()相似,只是它将元素增加到数组头,而不是尾。所有已有的数值键都会相应地修改,反映出在数组中的新位置,而关联键不受影响。示例如下:

6. array_pad()

array_pad()函数会修改target数组,将其大小增加到length指定的长度。这是通过在数组中填充由pad_value指定的值实现的。如果pad_value是正数,将填充到数组的右侧(后面);如果为负,则将填充到左侧(前面)。如果length等于或小于当前大小,将不做任何操作。示例如下:
网络无限,生活无限

TOP

发新话题