2.8 函数
在很多编程语言中都有函数这个概念。函数将为解决某一问题而编写的代码组织在一起,使得在解决同一个问题时,可以重复利用这些代码。本节将介绍PHP中函数的概念、构建和调用函数等内容。
2.8.1 PHP中函数的概念
在数学知识里,函数是由参数的定义域和在这个参数定义域上的某种规则组成的。当选定某一参数时,函数的值也是唯一确定的。例如有这样一个数学函数f(x)=2x+3,那么就有f(1)=5、f(3)=9,这里的1、3都是函数f的参数,而5、9都是这些参数对应的函数f的值。
PHP语言中的函数和数学中函数的概念很相似,只不过,PHP中的函数不仅仅是做一些数学运算,而是要完成更多、更复杂的功能。
在程序设计中,经常将一些常用的功能模块编写成函数,放在公用函数库中,供程序或其他文件使用。函数就像一些小程序,用它们可以组成更大的程序。函数之间也可以相互调用,进而完成更复杂的功能,但它们之间是相互独立的,互不隶属。
从使用角度来看,PHP的函数可以分为两种,即PHP的预定义函数和用户自定义的函数。用户可以在自己的程序或PHP文件中直接使用预定义函数,PHP提供过了大量功能丰富的预定义函数供PHP开发人员使用,极大地提高了开发效率。用户自定义的函数,是开发人员专门用来解决特定需求的功能模块。
2.8.2 定义函数和调用函数
PHP使用如下语法定义一个函数。
function func_name(param_list) {statement}
其中,function是PHP的保留关键字,表示开始定义一个函数。func_name是函数名,由开发人员自行指定。函数名以字母或下划线开始,后跟任意字母、数字或下划线。函数名后的一对括号,用来存放函数的参数param_list,如果所定义的函数不需要传入参数,括号内留空,但不能没有括号。最后花括号括住的语句statement叫做函数体,它可以是单条语句,也可以是多条语句,这些语句完成函数所要实现的功能。如下代码演示了如何定义一个函数。
function say_hello() { $name = "Jack"; echo "Hello,".$name; }
这段代码很简单,它定义了一个名叫say_hello()的函数,该函数没有参数,所以函数名后面的括号内为空。函数体有两条语句:第1句是赋值语句,将某个人名赋给变量$name;第2句输出一个含这个人名的字符串。实际上,该函数的功能就是用echo语句输出一条问候语。
提示 函数的参数根据需要,可以有多个。
一个定义好的函数需要在程序其他地方使用,才能发挥它的功能。调用一个函数的最简单的用法如下。
function_name();
其中,function_name是所要调用的函数的名称。代码2-31演示了如何在程序中调用上面定义的函数say_hello()。
代码2-31 函数的调用2-31.php
01 <?php 02 function say_hello() 03 { 04 $name = "Jack"; 05 echo "Hello,".$name; 06 } 07 08 say_hello(); // 在这里调用上面定义的函数say_hello 09 ?>
图2-26 函数的定义和调用
【代码解析】 代码第02~06行首先定义了一个函数say_hello(),但此时不会执行函数中的echo语句,因为此时只是函数的定义,计算机不需要执行任何实际代码。这段代码的第08行使用say_hello()来调用函数,此时,程序转入函数内部执行,这时执行echo语句,输出一条问候语。该程序的执行结果如图2-26所示。
另外,一个PHP文件中,函数定义的位置和调用函数的位置一般不需要确定的前后书写关系。也就是说,函数的定义可以在PHP文件的任何位置,调用也可以在PHP文件的任何位置,函数的定义不一定必须在函数调用的前面书写。例如代码2-31中函数的调用也可以写在最前面,而在最后定义函数say_hello()。
2.8.3 函数的参数和函数的返回值
2.8.2小节的函数say_hello()只能向“Jack”显示问候语,如果想向更多人显示问候语,该怎么做呢?这就是函数参数的问题。在大多数情况下,调用函数都会有数据的传递,这就是前面讲到的函数参数。将参数传递给函数,函数根据不同的参数完成不同的功能,或有不同的输出。
在函数say_hello()的例子中,如果想要向更多的人显示问候语,可以传递一个参数给函数say_hello(),这个参数就是不同的人名,在函数体内输出这个变量即可。代码2-32演示了带有参数的函数的用法。
代码2-32 调用带有参数的函数2-32.php
01 <?php 02 function say_hello($some_name) 03 { 04 echo "Hello,".$some_name; 05 echo "<br/>"; 06 echo "<br/>"; 07 } 08 09 say_hello("Jenny"); // 这里使用参数“Jenny ”调用函数say_hello 10 say_hello("Harry"); // 这里使用参数“Harry ”调用函数say_hello 11 say_hello("Ema"); // 这里使用参数“Ema ”调用函数say_hello 12 ?>
图2-27 调用带有参数的函数
【代码解析】代码第02~07行定义了带有一个参数的函数say_hello($some_name),接着3次调用该函数,但每次传给函数参数的值不一样。第一次调用时,传给函数的值是“Jenny”,因此函数参数$some_name的值即为“Jenny”,这时就会输出“Hello,Jenny”。同样的道理,其他调用会显示对其他人名的问候语。程序执行结果如图2-27所示。
PHP的函数的参数可以使用默认值。代码2-33演示了函数默认参数的使用。
代码2-33 函数的默认参数2-33.php
01 <?php 02 function say_hello($some_name = "Jack") 03 { 04 echo "Hello,".$some_name; 05 echo "<br/>"; 06 echo "<br/>"; 07 } 08 09 say_hello(); // 不使用任何参数调用函数say_hello 时,函数将使用函数定义的默认参数“Jack ” 10 say_hello("Jenny"); // 使用参数“Jenny ”调用函数say_hello 11 say_hello("Harry"); // 使用参数“Harry ”调用函数say_hello 12 say_hello("Ema"); // 使用参数“Ema ”调用函数say_hello 13 ?>
图2-28 使用函数的默认参数
【代码解析】该程序对函数say_hello()做了一点小的改动,在函数定义时,第02行为函数的参数$some_name赋了默认值“Jack”。这样当程序中调用该函数,但又没有传值给函数参数时,函数就会在函数体内使用这个默认值“Jack”,如果调用时传值给函数参数,那么个这个默认值就不再起作用,而是在函数体内使用用户传入的值。程序首先使用say_hello()调用该函数,如果没有传入任何值给函数,那么这时函数体执行后,就会输出“Hello,Jack”。程序的整个执行结果如图2-28所示。
在调用一个函数之后,有时希望能得到一个确定的值,这就是函数的返回值。PHP函数中使用return语句取得函数的返回值,return语句会将函数中一个确定的值带回到调用函数的地方。示例代码2-34演示了如何获得函数的返回值。
代码2-34 函数的返回值2-34.php
01 <?php 02 define(PI,3.14); 03 04 for($r = 3; $r<=8; $r++) // 从3 到8 循环 05 { 06 $s = get_circle_area($r); // 调用函数get_circle_area() 07 echo "r=$r, area=$s"; 08 echo "<br/>"; 09 echo "<br/>"; 10 } 11 12 function get_circle_area($radius) // 定义函数get_circle_area() ,参数是圆的半径 13 { 14 $area = PI * $radius * $radius; // 求圆的面积 15 return $area; // 返回圆的面积 16 } 17 ?>
图2-29 函数的返回值
【代码解析】代码第12~16行定义了一个计算圆面积的函数get_circle_area(),并在函数体最后将计算结果用return语句返回。在for循环里多次调用函数get_circle_area(),每次调用,函数get_circle_area()都会将计算结果返回赋给变量$s,接着输出圆的半径及其对应的圆的面积。程序的执行结果如图2-29所示。
2.8.4 PHP函数的传值与传址
2.8.3小节讲述的向函数传入参数是按传值方式传入的。传值的含义是指,在函数体内会生成一个传入值的复制,在函数内部对参数的修改不会影响到传入的值。因为开发的需要,有时希望在函数内部能够修改传入的值,这时就需要对函数参数使用传址方式。传址的含义是指,在函数体内真实引用传入的值,这意味着在函数体内使用的函数参数和传入的值是同一个,而不单单是传入值的一个复制。这时,在函数内部修改参数的值,同时也就修改了传入的值。
在PHP中,要想在函数参数中传址,需要定义函数时在参数前加上符号&。代码2-35演示了PHP函数如何使用传址方式传递参数。
代码2-35 传址方式传递函数参数2-35.php
01 <?php 02 $i = 100; 03 04 function func(&$n) 05 { 06 $n = $n+100; // 因为传址传入变量$i ,所以这里的变量$n 引用的就是$i 本身,此句等价于$i = $i+100 07 } 08 09 echo " 调用函数func 前:\$i=$i"; 10 echo "<br/>"; 11 echo "<br/>"; 12 13 func($i); // 将$i 传入函数func ,因为是传址方式,所以此时函数内的变量$n 就是变量$i 14 echo " 调用函数func 后:\$i=$i"; 15 ?>
【代码解析】代码第04~07行定义了函数func(),该函数接收一个传址方式的参数。执行函数体内的第06行语句“$n=$n+100;”会使变量$i的值同时发生改变。程序的执行结果如图2-30所示,从中可以看出,变量$i的值原来为100,因为经过参数传址方式的调用,在函数func()中使用的就是变量$i本身,所以变量$i的值被修改为200。
为了将传值方式和传址方式进行对比,将上述函数定义改为“function func($n)”,即按传值方式传递参数给函数func(),看看结果有什么不同。请看代码2-36。
代码2-36 传值方式传递函数参数2-36.php
01 <?php 02 $i = 100; 03 04 function func($n) // 定义函数func() 05 { 06 $n = $n+100; 07 } 08 09 echo " 调用函数func 前:\$i=$i"; 10 echo "<br/>"; 11 echo "<br/>"; 12 13 func($i); // 调用函数func() 14 echo " 调用函数func 后:\$i=$i.<br/>"; 15 ?>
程序的执行结果如图2-31所示。
【代码解析】从这个执行结果中可以看出,因为是传值方式传递给函数参数,在函数体内使用的是变量$i的一个复制,并不是$i本身,所以尽管在函数体内执行了加法运算,但并没有影响到变量$i的原始值。
图2-30 传址方式传递函数参数
图2-31 传值方式传递函数参数
2.8.5 变量作用域
变量作用域就是变量的有效范围。对于大多数PHP变量,作用域只能有一个。但是,在用户自定义函数中存在一个单独的局部函数范围。在一个函数内部定义的变量是局部变量,它只在本函数内有效,它的作用域就是当前函数之内。这就是说,一个在函数外部定义的变量不会在函数内部起作用;反之亦然。请看示例代码2-37。
代码2-37 变量作用域演示程序(1)2-37.php
01 <?php 02 $var = "some text"; 03 04 function test() // 定义函数test() 05 { 06 echo $var; 07 } 08 09 test(); // 调用函数test() 10 ?>
【代码解析】代码原意是想通过调用函数test()来输出变量$var的值,但通过浏览器没有看到任何输出内容。这是因为两个$var变量的作用域是不同的,第1个$var是一个全局变量,虽然它被赋值,但它的作用域(或者说它的有效范围)并不在函数test()内。这一点和C、Java等语言完全不同。在C等语言中,全局变量在函数中自动生效,除非被局部变量覆盖。第2个$var是局部变量,echo语句引用的是第2个$var(其作用域是局部的),它没有被定义(或者说没有被赋值),因此代码2-37不会在浏览器中显示出任何字符串内容。对上述代码稍作改动,读者可以更清楚地看到两个变量的有效范围。请看示例代码2-38。
代码2-38 变量作用域演示程序(2)2-38.php
01 <?php 02 $var = "some text"; 03 04 function test() // 定义函数test() 05 { 06 $var = "some text in function"; 07 echo ' 这是局部变量$var :'.$var; 08 } 09 10 echo ' 这是全局变量$var :'.$var; 11 echo '<br/>'; 12 echo '<br/>'; 13 test(); // 调用函数test() 14 ?>
代码2-38的执行结果如图2-32所示。
图2-32 变量的作用域
【代码解析】从这个执行结果可以看出,在函数test()外部定义的变量$var不会在函数内部生效。在函数test()中输出的变量$var是在其内部定义的,而在函数test()外部定义的全局变量$var并不会在函数内部生效。
如果希望在函数内部引用全局变量,需要在函数内部使用global关键字。PHP中global关键字的作用是在函数内部声明某个变量为全局变量,从而在函数内部使用该变量。代码2-39演示了global关键字的作用。
代码2-39 在函数内部使用global关键字2-39.php
01 <?php 02 $a = 1997; 03 $b = 1998; 04 05 function sum() // 定义函数sum() 06 { 07 global $a,$b; 08 $b = $a + $b; 09 } 10 11 echo '$a='.$a; 12 echo '<br/>'; 13 echo '$b='.$b; 14 echo '<br/>'; 15 echo '<br/>'; 16 17 sum(); // 调用函数sum() 18 echo '$a + $b = '.$b; 19 ?>
【代码解析】代码第02~03行首先定义了两个全局变量$a和$b,第05~09行定义了求和函数sum(),该函数内使用global关键字引用全局变量求和,并将求和结果赋值给变量$b,如代码第07行和第08行所示。这段代码的执行结果如图2-33所示。如果将代码2-39中的第07行注释掉,然后再次执行该程序,可以看到全局变量$a和$b没有在函数sum()内生效,其执行结果如图2-34所示。
图2-33 在程序中使用global关键字
图2-34 在程序中未使用global关键字