# 函数
[toc]
## 1. 语法
函数是实现代码复用的重要方式,在所有编程语言中均如此
```php
function 函数名称(参数列表)
{
// 函数体
return 返回值;
}
```
| 序号 | 名称 | 描述 |
| ---- | ----------- | ------------------------------------------ |
| 1 | `function` | 声明函数 |
| 2 | `函数名称` | 符合 PHP 标识符命名规范,不区分大小写 |
| 2 | `参数列表` | 零个或多个接收外部传入到函数的变量 |
| 2 | `{...` | 创建出一个封闭的函数作用域 |
| 2 | `函数体` | 由零个可多个合法的 PHP 语句组成 |
| 2 | `return 值` | 将执行结果返回函数调用者[可选] |
| 2 | `...}` | 函数执行结束,如果没有`return`,则返回`null` |
示例代码: `demo1.php`
```php
# 函数语法
// 声明: function 关键字
function func1($a, $b)
{
return $a + $b;
}
// 调用: 按名调用
echo func1(10, 20);
```
---
## 2. 类型
| 序号 | 类型 | 语法 | 描述 |
| ---- | ---------- | ------------------------- | --------------------------------------- |
| 1 | 自定义函数 | `function getName(){...}` | 用户根据业务需求创建 |
| 2 | 系统函数 | `substr(), count()...` | 也叫预定义函数,不必声明直接调用 |
| 3 | 可变函数 | `$funcName();` | 函数名使用变量表示 |
| 4 | 匿名函数 | `$f = function (){...}` | 也叫"闭包"或"函数表达式",常用做回调处理 |
示例代码: `demo2.php`
```php
<?php
# 函数类型
// 1. 自定义函数
function getPrice($money, $discount)
{
return $money * $discount;
}
echo '实付金额 = ' . getPrice(5000, 0.8);
echo '<hr>';
// 2. 系统函数
$str = '钟南山团队入围2020年度国家科技奖提名';
// 仅获取前5个中文字符
echo mb_substr($str, 0, 5);
echo '<hr>';
// 3. 可变函数
// 仍以前面已声明的函数: getPrice()为例
$funcName = 'getPrice';
// 被调用的函数名称保存到一个变量中
// 这个功能很实现,在后面课程中会看到更多应用
echo '实付金额 = ' . $funcName(8000, 0.7);
echo '<hr>';
// 3. 匿名函数
$f = function ($money, $num) {
return $money * $num;
};
echo '实付金额 = ' . $f(100, 20);
echo '<br>';
// 匿名函数也叫闭包,可以继承父作用域中的变量
$discount = 0.8;
// $discount 是父作用域中的变量,这个父作用域可以是全局, 也可以是父函数
// 父作用域是全局
$getAmount = function ($money, $num) use ($discount) {
$amount = $money * $num;
return $amount > 2000 ? $amount * $discount : $amount;
};
echo '实付金额 = ' . $getAmount(120, 20);
echo '<br>';
// 父作用域是函数
$f = function ($discount) {
$getAmount = function ($money, $num) use ($discount) {
$amount = $money * $num;
return $amount > 2000 ? $amount * $discount : $amount;
};
return $getAmount;
};
echo '实付金额 = ' . $f($discount)(120, 20);
```
---
## 3. 返回值
- 函数必须要有返回值
- 函数必须是遵守**单值返回**原则
| 序号 | 场景 | 描述 |
| ---- | ---------- | --------------------------------- |
| 1 | `return` | 可以返回任何类型的值,包括函数类型 |
| 2 | 无`return` | 遇到`}`也会返回, 默认返回`null` |
- 如果需要返回多个值,可以通过以下手段
| 序号 | 返回值类型 | 描述 |
| ---- | ----------- | ------------ |
| 1 | `string` | 字符串拼接 |
| 2 | `array` | 数组 |
| 3 | `json` | JSON 字符串 |
| 4 | `serialize` | 序列化字符串 |
> json 和序列化,使用时需要进行解码操作
示例代码: `demo3.php`
```php
<?php
# 函数返回值
// 单值返回,前面已经有了许多案例,重点放在多值返回上
// 1. 通过拼装字符串实现
function demo1()
{
$status = 1;
$message = '成功';
return $status . ', ' . $message;
}
echo demo1();
// 字符串拼装返回非常适合处理大量变量与html标签的混写
// 字符串拼装返回多值有许多限制,并且对返回值的解析处理非常麻烦
echo '<hr>';
// 2. 通过数组返回多值
function demo2()
{
return ['status' => 1, 'message' => '验证成功'];
}
echo demo2()['status'] === 1 ? demo2()['message'] : '验证失败';
// 返回值如果仍在php程序中处理,这样做很方便
// 开发中, 通常返回值都是要交给前端处理的,转为JSON格式更方便
echo '<hr>';
// 3. 通过JSON格式返回多值
// json是一种通用的,轻量级数据表示格式,使用javascript对象字面量表示数据
// 几乎所有编程程序,都支持定义和解析json格式的数据,请放心使用
// 当前后端时行数据交互时, json格式几乎成了除字符串之外的唯一选择
// json本质上仍是字符串, 只不过具有一些特殊格式,需要进行解析罢了
function demo3()
{
// json_encode(): json编码函数,将数据编码为json格式字符串返回
return json_encode(['status' => 1, 'message' => '验证成功']);
}
// json_decode():json解码函数, 解析json格式字符串,默认返回对象,true表示返回数组
$data = json_decode(demo3(), true);
echo '<pre>' . print_r($data, true) . '</pre>';
echo '<hr>';
// 4. 通过序列化返回多值
// 序列化可以将程序中的变量/数据做持久化保存,可以进行传输,保存到文件中等
// 与json一样, 序列化也有序列化与反序列二种操作
function demo4()
{
return serialize(['status' => 1, 'message' => '验证成功']);
}
// 查看序列化的格式化字符串
echo demo4(), '<br>';
// 反序列化
$data = unserialize(demo4());
echo '<pre>' . print_r($data, true) . '</pre>';
```
---
## 4. 参数
- 调用者可以通过参数将数据传递到函数中
- 参数是以逗号分隔的表达式列表
- 参数按照从左到右的顺序求值
参数类型
| 序号 | 类型 | 描述 |
| ---- | -------- | -------------------- |
| 1 | 值参数 | 默认传参方式 |
| 2 | 引用参数 | 改变原始调用参数值 |
| 3 | 默认参数 | 调用时允许省略的参数 |
| 4 | 剩余参数 | 调用参数数量不确定 |
示例代码: `demo4.php`
```php
<?php
# 函数参数
// 1. 值参数: 默认
function demo1($arg)
{
return $arg *= 2;
}
$value = 100;
echo demo1($value), '<br>';
echo $value, '<br>';
echo '<hr>';
// 2. 引用参数
// 引用传参: 参数前添加地址引用符
function demo2(&$arg)
{
// 函数内部更新了调用参数,则直接影响到原值
return $arg *= 2;
}
$value = 100;
echo demo2($value), '<br>';
echo $value, '<br>';
echo '<hr>';
// 3. 默认参数
// 参数列表中,允许有必选参数和默认参数二大类, 默认参数必须排列在必选参数的后面
// 默认参数决定了函数的默认行为,如果函数默认行为满足要求,显然不传参调用更方便
// 默认参数也允许用户根据自身需求,传入自定义参数,对函数的行为进行干预
function demo3($a, $b, $opt = '+')
{
$res = 0;
switch ($opt) {
case '+':
$res = "$a + $b = " . ($a + $b);
break;
case '-':
$res = "$a - $b = " . ($a - $b);
break;
case '*':
$res = "$a * $b = " . ($a * $b);
break;
case '/':
$res = "$a / $b = " . ($a / $b);
break;
default:
$res = '非法操作符';
}
return $res;
}
// 如果用户不传第三个参数,则使用默认参数,即默认执行:+ 加法
echo demo3(10, 20), '<br>';
// 传入用户自定义操作, 如*, 乘法
echo demo3(10, 20, '*'), '<br>';
// 传入非法数据: #
echo demo3(10, 20, '#'), '<br>';
echo '<hr>';
// 4. 剩余参数
function demo4($a, $b, $c)
{
return $a + $b + $c;
}
// 计算三数之和
echo demo4(1, 2, 3), '<br>';
// 如果计算五数之和,甚至更多数的求和,难道要这样一直写下去呢?
// 其实函数的参数列表中的参数,只是占位符罢了,可以为空的
// 函数内部可以通过其它方式来获取到调用参数的
// php5.5之前,可以用以下三个函数来获取调用参数
// func_num_args(),func_get_arg(), func_get_args()
// 现在已经用不到它们了,我们可以使用一种更加优雅的方式来获取:剩余参数
// 剩余参数的概念与语法与JavaScript中是一样的, 熟悉js的同学一定不陌生
// 计算任意个数据之和
function sum(...$args)
{
print_r($args);
echo '<br>';
return array_sum($args);
}
$arr = [2, 3, 4, 6, 88, 23, 1, 24];
// ...: 剩余参数展开与收集操作符
// 调用时,执行展开操作, 传参时执行收集操作
echo sum(...$arr);
```
---
## 5. 回调函数
| 语法 | 类型 | 执行方式 | 应用场景 |
| -------- | ------------- | -------- | -------- |
| 匿名函数 | 闭包`Closure` | 异步 | 函数参数 |
> 异步执行,是指当前函数的执行并不会中断当前程序的执行流程
示例代码: `demo5.php`
```php
<?php
# 回调函数
$data = range(1, 100);
// print_r($data);
// 使用回调,可以异步的处理海量数据,并不中断当前程序
$arr1 = array_map(function ($item) {
if ($item % 2 === 0) return $item;
}, $data);
echo '<hr>';
print_r($arr1);
// 过滤掉空值
$arr2 = array_filter($arr1, function ($item) {
return $item;
});
echo '<hr>';
print_r($arr2);
```
---
## 6. 命名空间
- 使用目录来整理文档, 允许将同名文档,存储在不同的目录下面即可
- 不同目录下的同名文件,访问时必须带上的它的目录名称,以未区别
- 命名空间采用类似的思想,同名函数,只要声明在不同空间中即可
- 同样, 访问这些函数时, 也需要带上它的命名空间才可以
示例代码: `demo6.php`
```php
# 函数命名空间
// function demo1()
// {
// return __FUNCTION__;
// }
// function demo1()
// {
// return __FUNCTION__;
// }
// 报函数重复命名错误
// 可以在不同命名空间中创建同名函数
namespace ns1 {
function demo1()
{
return __FUNCTION__;
}
}
namespace ns2 {
function demo1()
{
return __FUNCTION__;
}
}
namespace {
// 带有空间的函数,调用时需要带上它的空间名称
echo \ns1\demo1();
echo '<hr>';
echo \ns2\demo1();
}
```