PHP 函数(上):自定义函数和内置函数

自定义函数

函数是现代高级编程语言的基本配备,PHP 也不例外。一个典型的 PHP 函数通过 function 关键字进行声明,然后紧跟着是函数名和通过圆括号声明的参数列表,再通过花括号定义函数体 —— 我们可以在函数体中定义具体的业务逻辑,最后通过 return 语句返回函数返回值(可选)。

编写自定义函数

我们新建一个 php_learning/function/test.php 文件存放测试代码,然后定义一个 add 函数执行加法计算:

<?php

/**
 * 计算两数相加之和
 * @param $a
 * @param $b
 * @return mixed
 */
function add($a, $b) {
    $sum = $a + $b;
    return $sum;
}

在这个函数中,funtion 函数声明之上的部分是该函数的代码注释(多行注释,包含函数的功能、参数和返回值),函数名是 add,支持传入两个参数 $a$b,在函数体中,对这两个参数进行求和并赋值给 $sum,最后返回这个 $sum 该函数调用者。

调用自定义函数

然后我们可以这样调用这个函数:

$a = 1;
$b = 2;
$c = add($a, $b);
echo "$a + $b = $c" . PHP_EOL;

上述代码的执行结果是:

-w697

提高代码复用性

显然,我们可以通过函数来封装特定业务逻辑,提高代码的复用性,如果没有这个自定义函数的话,如果要求和,就需要做多次运算:

$n1 = 1;
$n2 = 3;
$s1 = $n1 + $n2;

$n3 = 8.0;
$n4 = 9.1;
$s2 = $n3 + $n4;

而有了 add 函数之后,就可以直接调用该函数执行加法运算:

$n1 = 1;
$n2 = 3;
//$s1 = $n1 + $n2;
$s1 = add($n1, $n2);

$n3 = 8.0;
$n4 = 9.1;
//$s2 = $n3 + $n4;
$s2 = add($n3, $n4);

自动类型转化

并且在 PHP 中,还可以自动对数据类型进行转化:

$n5 = '1';
$n6 = 2;
$s3 = add($n5, $n6);
echo "$n5 + $n6 = $s3" . PHP_EOL;

上述代码的执行结果是:

1 + 2 = 3

当然这种类型转化也不是总能奏效,比如我们将调用代码改写如下:

$n5 = '学院君';
$n6 = 2;
$s3 = add($n5, $n6);
echo "$n5 + $n6 = $s3" . PHP_EOL;

此时通过命令行执行代码就会报错,因为 $n5 不能转化为有效的数值:

-w865

声明参数和返回值类型

从 PHP 7 开始,支持对传入参数和返回值声明数据类型:

/**
 * 计算两数相加之和
 * @param int $a
 * @param int $b
 * @return int
 */
function add(int $a, int $b): int {
    $sum = $a + $b;
    return $sum;
}

上述代码限定只能传入整型参数,并且函数返回值也是整型(通过 : + int 设置,其他类型依此类推)。

这样一来,就只能传入整型数据到 add 函数了,如果传入浮点型或字符串类型数据:

$n3 = 8.0;
$n4 = 9.1;
$s2 = add($n3, $n4);

$n5 = '学院君';
$n6 = 2;
$s3 = add($n5, $n6);

都会报错,报错信息如下:

-w1385

提示参数类型只支持整型。

不提供返回值

最后,我们也可以在函数中不提供返回值,比如可以将上述 add 函数改写如下:

/**
 * 计算两数相加之和
 * @param int $a
 * @param int $b
 */
function add(int $a, int $b) {
    $sum = $a + $b;
    echo "$a + $b = $sum" . PHP_EOL;
}

这完全取决于业务的需要。

值传递和引用传递

函数参数默认以值传递方式进行传递,也就是说,我们传递到函数内部的实际上是变量值的拷贝,而不是变量本身,还是以 add 函数为例,如果我们要实现类似 $a += $b 这种方式的求和,可以这么做:

function add(int $a, int $b): int {
    $a += $b;
    return $a;
}

在这段代码中,看似我们在函数体中运行 $a += $b 修改了 $a 的值,但是由于参数传递默认是值拷贝,这个赋值作用域仅限于函数体内部,在函数外部并没有真正修改 $a 的值,所以需要通过 return 语句返回 $a 才能在外部获取求和后 $a 的值,我们可以编写测试代码如下:

$a = 1;
$b = 2;
$c = add($a, $b);
printf("\$a = %d\n", $a);
printf("\$c = %d\n", $c);

上述代码的执行结果如下:

$a = 1
$c = 3

可以看到 $a 的值确实没有变化,因为传递进函数的仅仅是 $a 的值拷贝而已,当然这个结果还可以从另一个角度解释,那就是形参(形式参数)和实参(实际参数),函数签名中的 $a$b 仅仅是形参而已,外面定义的变量 $a$b 才是实参,为了便于标识,我们将外部调用的代码调整如下:

$m = 1;
$n = 2;
$c = add($m, $n);
printf("\$m = %d\n", $m);
printf("\$c = %d\n", $c);

这样,函数 add 中的 $a$b 是形参,$m$n 是实参就更好理解了,当我们调用函数时,实际执行了如下将实参赋值给形参的工作:

$a = $m;
$b = $n;

$a 后续的赋值和修改和 $m 没有任何关系。

如果我们想要形参 $a 的赋值和修改与实参 $m 关联起来,可不可以做到呢?当然可以,这就需要引入引用传递的概念 —— 上面的实现传递的是值拷贝,我们把实参的指针赋值给形参,这样,修改形参的值就等同于修改实参值了,因为操作的是同一个内存地址中的值,在 PHP 中,不支持指针的概念,可以通过引用来替代,引用和指针一样,都是通过 & 获取,按照这个逻辑我们修改上述 add 方法实现如下:

function add(int &$a, int $b) {
    $a += $b;
}

如果要实现引用传递,需要显式通过 &$a 进行声明,这样一来,就不需要设置返回值,对变量 $a 的修改会直接同步到外部传入的实参上:

$m = 1;
$n = 2;
add($m, $n);
printf("\$m = %d\n", $m);

上述代码的执行结果是:

$m = 3

引用传递传递的是变量指针值(内存地址)的拷贝,所以本质上说,仍然是一种值拷贝。对于基本数据类型,包括字符串、数值、布尔类型、数组而言,引用传递的时候需要显式通过 & 进行标识,而如果传递的对象这种复合类型的时候,由于默认就是引用类型,所以不需要加上 & 标识,后面我们介绍类与对象的时候会专门介绍。

内置函数

除了自定义函数之外,PHP 还提供了丰富的内置函数。我们前面已经使用过很多,比如定义常量的 define、格式化输出的 printf、以及打印变量的 var_dump 和打印数组的 print_r 等。下面我们介绍一些常见的操作基本变量的内置函数。

字符串函数

PHP 所有内置的字符串函数都可以在这里查询:https://www.php.net/manual/zh/ref.strings.php。通过这些函数可以满足所有日常对字符串的操作需求,比如大小写、获取字符串长度等:

// 字符串函数
$str = "Hello, PHP!";
printf("字符串长度: %d\n", strlen($str));
printf("大写格式: %s\n", strtoupper($str));
printf("小写格式: %s\n", strtolower($str));

上述代码的执行结果如下:

-w698

关于字符串函数的查询和使用就简单介绍到这里,下面来看数组函数。

数组函数

PHP 所有内置的数组函数都可以在这里查询:https://www.php.net/manual/zh/ref.array.php。通过这些函数可以满足所有日常对数组的操作需求,比如排序、获取最大/小值、获取数组大小、打印数组等:

// 数组函数
$arr = [1, 3, 8, 7, 6];
sort($arr);  // 升序
print_r($arr);
rsort($arr); // 降序
print_r($arr);
printf("数组最大值:%d\n", max($arr));
printf("数组最小值:%d\n", min($arr));

其中 sort 对数组进行升序排序,rsort 对数组进行降序排序,上述代码的执行结果如下:

-w382

你可以通过数组函数链接查看 PHP 内置数组函数的使用,这里就简单介绍到这里。

数学函数

另外,PHP 还内置了很多数学计算函数,你可以在这里查询所有这些函数列表:https://www.php.net/manual/zh/ref.math.php,比如计算正弦、余弦、正切、余切、指数、平方根、进制转化、生成随机数等:

// 数学函数
$num = 100;
$n1 = pow(100, 2); // 100的2次方
$n2 = sqrt(100);   // 100的平方根
$n3 = decbin(100);  // 转化为二进制
$n4 = mt_rand(0, 100);  // 生成0-100之间的随机数

文件系统函数

在 PHP 中,我们可以通过内置的文件系统函数与本地操作系统的文件系统进行交互,比如文件的创建、写入、读取、关闭、删除等,下面是一些基本示例:

// 文件系统函数
file_put_contents('test1.txt', '你好,学院君');  // 快速写入内容到文件 test.txt(不存在则自动创建)
$content = file_get_contents('test1.txt');  // 从文件 test.txt 中读取内容
var_dump($content);

$file = fopen('test2.txt', 'w');   // 以写入模式打开文件 test2.txt,不存在则创建自动创建
fwrite($file, '你好,');   // 通过 fwrite 写入内容到文件
fwrite($file, '学院君!');  // 继续写入
fclose($file);  // 关闭这个文件句柄

$file = fopen('test2.txt', 'r');  // 只读模式打开 test2.txt 文件
$content = '';
while (!feof($file)) {    // 还没有到文件末尾,则继续读取
    $content .= fread($file, 1024);   // 通过 fread 读取指定字节内容
}
fclose($file);
var_dump($content);

// 删除上述文件
unlink('test1.txt');
unlink('test2.txt');

当然还有很多其他 PHP 内置函数,这里就不一一列举了。

上一篇: PHP 控制结构

下一篇: PHP 函数(下):匿名函数和作用域