文章教程

7.3.1自定义函数的定义

9/17/2020 9:37:07 PM 人评论 次浏览

7.3 自定义函数

如果说一个WEB系统是一个加工工厂,那么一个函数可以比作是一个“加工作坊”,这个“加工作坊”接收上一个“作坊”传递过来的“原料”(其实是参数),并对这些“原料”进行加工处理产生“产品”,再把“产品”传递给下一个“作坊”。典型地,函数有一个或多个参数,函数定义了一系列的操作对这些参数进行处理,然后将处理结果返回。对于自定义函数而言,其使用过程为:程序员定义函数的参数、函数体(一系列的操作)及返回值,声明函数后对函数进行调用。

7.3.1 自定义函数的定义

在PHP中,定义自定义函数的语法格式为:

function functionName($param1, $param2, $param3,…$paramn=defaultValue){

函数体

return 返回值;

}

自定义函数的语法格式说明如下。

(1)function:定义自定义函数的关键字,关键字function大小写不敏感。

(2)functionName:自定义函数的函数名。函数名由程序员指定,在函数被调用时使用。

(3)$param:定义函数的参数,函数通过参数接收“外部”数据,从而实现对外部数据的处理。一个函数可以没有参数,也可以存在多个参数,参数之间用逗号隔开。

( 4 ) defaultValue:函数参数的默认值。必要时可以为函数中的个别参数指定默认值defaultValue,默认值通常是一个常数表达式。调用函数时,如果不给带有默认值的参数传递值,此时默认值自动赋予该参数。

figure_0135_0184自定义函数中默认值参数尽量放在参数列表的末尾,这样做的好处是默认值参数可以作为可选参数,即在函数被调用时,不必为默认值参数提供数值。

(5)函数体:函数的功能实现。函数体是在函数被调用时执行的语句块。

(6)return:如果函数有执行结果,使用return语言结构返回函数的执行结果,该执行结果可以是任意类型的数据。调用函数时,当程序运行到return语句时,立即结束此函数的执行,将执行结果作为函数的值返回给调用者,并将控制权转交给调用者。

figure_0136_0185为了便于管理自定义函数,通常将自定义函数放置到一个“专门”存放自定义函数的目录(例如functions)下,且该目录中存放的PHP文件名通常是自定义函数名。

下面的3个程序定义了3个函数:makeNine()函数、makeNineWithParams()函数和maxValue()函数。

(1)在目录“C:\wamp\www\7\”下创建“functions”目录,在该目录下创建程序makeNine.php,该程序定义了一个无参函数makeNine(),其功能是制作九九乘法表。

<?php

function makeNine(){

echo "<table border='1'>";

for ($c=1;$c<=9;$c++){

echo "<tr>";

for ($d=$c;$d<=9;$d++){

echo "<td align='right'>";

echo $c."×".$d."=".$c*$d." ";

echo "</td>";

}

echo "</tr>";

echo "<tr/><tr/>";

}

echo "</table>";

}

?>

(2)在“functions”目录下创建程序 makeNineWithParams.php,该程序定义了一个有参函数makeNineWithParams(),该函数的功能是制作九九乘法或加法表,并可以指定表格的边框宽度,边框宽度没有指定时,宽度为1。

<?php

function makeNineWithParams($method, $border=1){

echo "<table border='$border'>";

for ($c=1;$c<=9;$c++){

echo "<tr>";

for ($d=$c;$d<=9;$d++){

echo "<td align='right'>";

if($method==="+"){

echo $c."+".$d."=".($c+$d)." ";

}else if($method==="*"){

echo $c."×".$d."=".($c*$d)." ";

}

echo "</td>";

}

echo "</tr>";

echo "<tr/><tr/>";

}

echo "</table>";

}

?>

说明:makeNineWithParams()函数的参数$border 有默认值,且放在参数列表的末尾,因此$border为可选参数。

( 3 )在“functions”目录下创建程序 maxValue.php ,该程序定义了一个有返回值的函数maxValue(),该函数的功能是计算两个数的最大值。

<?php

function maxValue($a=0,$b=0){

$c = $a>$b?$a:$b;

return $c;

}

?>

说明:程序maxValue.php定义的maxValue ()函数的参数$a和$b都是可选参数。

figure_0137_0186如果将maxValue()函数名修改为max(),程序将产生错误。这是由于max()是PHP的内置函数,自定义函数名不能和内置函数名相同。

7.3.2 自定义函数的声明和调用

调用自定义函数时需要注意,应该先声明自定义函数,然后才可以在调用处使用如下方式调用自定义函数:

functionName(param1Value, param2Value,param3Value,…paramnValue)

说明如下。

(1)functionName:调用自定义函数的函数名,函数名大小写不敏感。

(2)paramValue:传递给函数的参数值。注意参数值的顺序和自定义函数参数的顺序需保持一致。当函数的定义和函数的调用位于不同的PHP文件时,需要使用include(或include_once)或require(或 require_once)语言结构引用函数定义所在的 PHP 文件,这个过程称为函数的声明。当函数的定义和函数的调用位于同一个PHP文件时,此时无须函数的声明即可直接调用自定义函数。例如如下程序call.php实现了对函数makeNine()、maxValue()和makeNineWithParams()的调用,程序call.php中使用“include_once("相对路径");”语句引用了函数定义所在的程序文件。

<?php

//函数的声明

include_once("functions/makeNine.php");

include_once("functions/makeNineWithParams.php");

include_once("functions/maxValue.php");

//函数的调用

makeNine();

echo "<hr/>";

makeNineWithParams('+');

echo "<hr/>";

makeNineWithParams('*',2);

echo "<hr/>";

echo maxValue();

echo "<hr/>";

echo maxValue(200,100);

?>

7.3.3 自定义函数的参数赋值

和变量赋值方法相同,自定义函数的参数赋值有两种方法:传值赋值和传地址赋值。

1.传值赋值

默认情况下,自定义函数的参数是按传值赋值的方式为函数参数赋值,即将一个值的“拷贝”赋值给函数的参数(例如程序byValue.php)。

程序byValue.php代码如下,该程序的运行结果如图7-6所示。

figure_0138_0187
图7-6 自定义函数的参数传值赋值

<?php

function addAge($value){

$value = $value + 1;

echo $value;

}

$age = 18;

addAge($age); //输出:19

echo "<br/>";

echo $age; //输出:18

?>

程序byValue.php运行过程中的内存分配图如图7-7所示。

figure_0138_0188
图7-7 变量在内存中的动态分配

程序byValue.php运行过程说明如下。

(1)函数只有被调用时,才占用WEB服务器的CPU资源和内存资源,否则函数仅仅是保存到外存空间(即硬盘)的一个PHP文件。

(2)程序执行语句“$age = 18;”,PHP预处理器为程序分配了第一个内存空间,这个过程称为过程1。

(3)程序执行到语句“addAge($age);”,此时自定义函数 addAge()被调用,PHP 预处理器为函数的参数$value分配了内存空间,这个过程称为过程2。由于PHP采用的是“写时拷贝”的原理,并没有为参数值分配新的内存空间。

(4)当$value的值发生变化时,PHP预处理器为$value的值分配新的内存空间,这个过程称为过程3。

(5)函数体执行完毕后,意味着函数调用的结束。PHP预处理器回收函数调用期间分配的所有内存,这个过程为过程4。

(6)所有程序执行完毕,内存又恢复到初始状态,这个过程为过程5。

从图7-7可以看出,使用传值赋值时,函数参数$value在内存中的生存周期是从函数addAge()被调用到函数addAge()运行完毕结束调用的这段时间,$value的作用域为函数内有效。读者可以自己分析下面的程序byValue2.php的内存分配图,推断byValue2.php程序的运行结果。

<?php

function addAge($age){

$age = $age + 1;

echo $age;

}

$age = 18;

addAge($age);

echo "<br/>";

echo $age;

?>

说明:由于生存周期和作用域的不同,程序byValue2.php中函数的参数$age和程序中的变量$age是两个不同的变量。

2.传地址赋值

自定义函数的参数也可使用传地址赋值,即将一个变量的“引用”传递给函数的参数。和变量传地址赋值的方式一样,在函数的参数名前追加一个“&”符实现传地址赋值(例如程序byReference.php)。

程序byReference.php代码如下,该程序运行结果如图7-8所示。

figure_0139_0189
图7-8 自定义函数的参数传地址赋值

<?php

function addAge(&$value){

$value = $value + 1;

echo $value;

}

$age = 18;

addAge($age); //输出:19

echo "<br/>";

echo $age; //输出:19

?>

程序byReference.php运行过程中的内存分配图如图7-9所示。

figure_0139_0190
图7-9 变量在内存中的动态分配

程序byReference.php运行过程部分说明如下。

(1)程序执行到语句“addAge(&$age);”时,此时自定义函数addAge()被调用,PHP预处理器为函数的参数$value 分配了内存空间,这个过程称为过程 2。由于这里是传地址赋值,函数参数$value和程序byReference.php的变量$age指向同一个变量值18。

(2)程序执行到函数体内的语句“$value = $value + 1;”时,$value的值变为19,此时变量$age的值也跟着发生了变化,这个过程称为过程3。

(3)函数体执行完毕后,意味着函数的调用结束。PHP预处理器回收函数调用期间分配的所有内存,这个过程为过程4。

(4)所有程序执行完毕,内存又恢复到初始状态,这个过程为过程5。

从图7-9可以看出,使用传地址赋值时,函数参数$value在内存中的生存周期是从函数addAge()被调用到函数addAge()运行完毕结束调用的这段时间,$value的作用域为函数内有效。读者可以自己分析下面的程序byReference2.php的内存分配图,推断byReference2.php程序的运行结果。

<?php

function addAge(&$age){

$age = $age + 1;

echo $age;

}

$age = 18;

addAge($age);

echo "<br/>";

echo $age;

?>

说明:由于生存周期和作用域的不同,程序byReference2.php中函数的参数$age和程序中的变量$age是两个不同的变量。

通过对比程序byValue.php和程序byReference.php的运行结果可以得知,函数调用时,若使用传值赋值方式为函数参数赋值,函数无法修改函数体外的变量值;若使用传地址赋值方式为函数参数赋值,函数可以修改函数体外的变量值。但不管使用传值赋值还是传地址赋值,函数参数(或函数体内变量)的生存周期是函数运行期间,函数参数(或函数体内变量)的作用域为函数体内有效。若要延长函数体内变量的生存周期,需使用关键字static;若要扩大函数体内变量的作用域,需使用关键字global。

figure_0140_0191使用传地址赋值方式时,传递给函数的值不能是常量,否则程序将产生 Fatal error错误终止程序的运行(例如程序byConstant.php)。

程序byConstant.php代码如下,该程序的运行结果如图7-10所示。

figure_0140_0192
图7-10 自定义函数的参数传地址赋值

<?php

function addAge(&$age){

$age = $age + 1;

echo $age;

}

$age = 18;

addAge(20); //给出Fatal error 错误信息

?>

细心的读者可能会有一个疑问:调用函数过程中,在使用传值赋值的方式为函数参数赋值时,能不能将一个变量的引用(例如&$age)传递给函数?例如程序 byValue3.php 如下,该程序的运行结果如图7-11所示。

figure_0141_0193
图7-11 自定义函数的参数传值赋值

<?php

function addAge($value){

$value = $value + 1;

echo $value;

}

$age = 18;

addAge(&$age);  //输出:19

echo "<br/>";

echo $age;   //输出:19

?>

从图7-11中可以看出,PHP鼓励在函数的定义中指定哪些参数应该用引用传递,不建议直接给函数传递一个引用参数。当然这仅仅是一种建议,通过修改 php.ini 配置文件的选项allow_call_time_pass_reference(默认值为Off)决定是否开启函数调用时强制参数按照引用传递。

7.3.4 变量的作用域和global关键字

变量的作用域决定了PHP程序在何地能访问到该变量,根据变量的作用域可将变量分为全局变量和局部变量。变量的作用域取决于变量在PHP程序中的位置。

(1)在函数内定义的变量(包括函数的参数)为局部变量,局部变量在调用函数结束后被自动回收。

(2)在函数外定义的变量为全局变量,声明后的全局变量可以被PHP程序中所有语句访问(函数内的PHP语句除外),当程序执行到程序末尾的时候,全局变量才被自动回收。全局变量也可应用于 include语句和require语句所引用的PHP程序文件。

例如程序 byValue2.php,该程序的运行结果如图7-12所示,程序运行过程中的内存动态分配图如图7-13所示。

figure_0141_0194
图7-12 局部变量和全局示例程序运行结果
figure_0141_0195
图7-13 变量在内存中的动态分配

如果函数中的PHP语句要访问全局变量,需要在函数内定义的变量名前加关键字global,此时函数内局部变量变为全局变量。例如程序global.php如下,该程序的内存动态分配图如图7-14所示。

figure_0142_0196
图7-14 全局变量和局部变量在内存中的动态分配

<?php

function addAge($age){

global $age;

$age = $age + 1;

echo $age;

}

$age = 18;

addAge($age); //输出:19

echo "<br/>";

echo $age; //输出:19

?>

程序global.php运行过程部分说明如下。

(1)程序执行到语句“addAge($age);”时,此时自定义函数addAge()被调用,PHP预处理器为程序global.php创建一个局部变量$age,该过程为过程2。

(2)程序执行到语句“global $age;”时,将局部变量$age声明为全局变量,此后函数内的变量$age和函数外的变量$age为同一个变量,该过程为过程3。

(3)当程序执行到语句“$age = $age + 1;”时,将全局变量$age的值修改为19,该过程为过程4。

global关键字用法的注意事项如下。

xiao_1 不能使用global定义函数的参数。

xiao_1 在函数内使用global定义全局变量的同时,不能使用赋值语句给该变量赋值。

xiao_1 global可以一次性地定义多个全局变量,例如“global $a,$b;”。

xiao_1 在函数内使用global语句定义全局变量时,若程序中已经存在该全局变量,则直接“拿来”使用,否则将创建该全局变量。

xiao_1 经global定义的全局变量,PHP会将该变量的定义放到$_GLOBALS数组中,数组的键为该全局变量的变量名,数组的值为该全局变量的变量值。

figure_0142_0197常量作用域不同于变量的作用域,常量在其定义处开始到程序运行结束期间(包括被调用函数的PHP语句执行期间)一直有效。

7.3.5 变量的生存周期和static关键字

函数体内定义的变量生存周期是短暂的:每一次函数调用的开始到这一次函数调用的结束。有时希望函数体内的变量能够从这次调用一直存活到下次调用,此时需要在该变量前加上 static关键字。static关键字一般在函数定义中使用,用于修饰局部变量。例如程序static.php如下,该程序运行过程中的内存分配图如图7-15所示。

figure_0143_0198
图7-15 静态变量在内存中的动态分配

<?php

function plus(){

static $sum = 0;

$sum++;

echo $sum;

echo "<br/>";

}

plus(); //输出:1

plus(); //输出:2

?>

程序static.php运行过程说明如下。

(1)程序第一次执行到语句“plus();”,开始调用plus()函数,然后执行函数体内的第一条语句“static $sum = 0;”。此时内存创建了一个$sum的静态变量,该过程为过程1。

(2)程序执行到语句“$sum++;”时,该静态变量的值加1,该过程为过程2。

(3)函数第一次调用结束后,由于$sum变量为静态变量,因此该变量将一直存在于内存中,该过程为过程3。

(4)第二次执行语句“plus();”,再次调用plus()函数时,由于内存中已经存在静态变量$sum,程序将跳过语句“static $sum = 0;”,此时静态变量$sum能够保持前一次的值,不再进行初始化,该过程为过程4。

(5)程序执行到语句“$sum++;”时,该静态变量的值加1,该过程为过程5。

(6)所有代码执行结束后,内存中的所有变量被回收,该过程为过程6。

static关键字用法的注意事项如下。

xiao_1 static主要用于修饰函数体内的变量,不能使用static定义函数的参数。

xiao_1 静态变量只在PHP程序的当前执行中有效,如果刷新了页面,一切又将从头开始。

xiao_1 经static修饰的变量一般要进行初始化。

xiao_1 static可以一次性地定义多个全局变量,例如“static $a,$b;”。

例如如下程序trColor.php使用静态变量制作一个表格,表格中的每一行颜色交替以示醒目。

<?php

function trColor() {

static $color;

if($color=="#FE2E9A"){

$color = "#E6E6E6";

}else{

$color = "#FE2E9A";

}

return($color);

}

?>

<table border=1>

<?php

for ($i=0;$i<10;$i++){

$color = trColor();

echo "<tr bgcolor='$color'><td>第".$i."行</td></tr>";

}

?>

</table>

程序trColor.php的运行结果如图7-16所示。

figure_0144_0199
图7-16 静态变量的应用

借助静态变量可以实现递归函数。递归函数是一种调用自身的函数,为了防止递归函数无休止地“调用”自身,必须为递归函数提供一个函数出口,这个出口可以使用静态变量实现。例如程序recursion.php如下。

<?php

function recursion()

{

static $count = 0;

$count++;

echo $count." ";

if ($count < 10) {

recursion();

}

echo $count." ";

$count--;

}

recursion();

?>

程序recursion.php的运行结果如图7-17所示。

figure_0145_0200
图7-17 静态变量的应用

7.3.6 变量函数

变量函数类似于可变变量,变量函数的函数名为变量。使用变量函数可以实现通过改变变量的值的方法调用不同的函数。变量函数的调用方法如下:

$varName(param1Value, param2Value,param3Value,…paramnValue)

例如可以将程序call.php的代码修改为如下代码。

<?php

//函数的声明

include_once("functions/makeNine.php");

include_once("functions/makeNineWithParams.php");

include_once("functions/maxValue.php");

//函数的调用

$functionName = "makeNine";

$functionName();

echo "<hr/>";

$functionName = "makeNineWithParams";

$functionName('+');

echo "<hr/>";

makeNineWithParams('*',2);

echo "<hr/>";

$functionName = "maxValue";

echo $functionName();

echo "<hr/>";

echo $functionName(200,100);

?>

教程类别