重头学PHP-3: 函数编程基础

254 阅读6分钟

PHP-3: 函数编程基础

1. 语法

函数是实现代码复用的重要方式,在所有编程语言中均如此

function 函数名称(类型: 参数列表): 返回值类型
{
    // 函数体
    return 返回值;
}
序号 名称 描述
1 function 声明函数
2 函数名称 符合 PHP 标识符命名规范,不区分大小写
2 参数列表 零个或多个接收外部传入到函数的变量
2 {... 创建出一个封闭的函数作用域
2 函数体 由零个可多个合法的 PHP 语句组成
2 return 值 将执行结果返回函数调用者[可选]
2 ...} 函数执行结束,如果没有return,则返回null

示例代码: demo1.php

# 函数语法

// 声明: function 关键字
function func1(int $a, int $b) : int
{
    return $a + $b;
}

// 调用: 按名调用
echo func1(10, 20);

2. 类型

序号 类型 语法 描述
1 自定义函数 function getName(){...} 用户根据业务需求创建
2 系统函数 substr(), count()... 也叫预定义函数,不必声明直接调用
3 可变函数 $funcName(); 函数名使用变量表示
4 匿名函数 $f = function (){...} 也叫"闭包"或"函数表达式",常用做回调处理

示例代码: demo2.php


# 函数类型

// 1. 自定义函数
function getPrice(float $money, float $discount): float
{
    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 (float $money, int $num): float {
    return $money * $num;
};
echo '实付金额 = ' . $f(100, 20);
echo '<br>';

// 匿名函数也叫闭包,可以继承父作用域中的变量
$discount = 0.8;
// $discount 是父作用域中的变量,这个父作用域可以是全局, 也可以是父函数
// 父作用域是全局
$getAmount = function (float $money, int $num) use ($discount): float {
    $amount = $money * $num;
    return $amount > 2000 ? $amount * $discount  : $amount;
};

echo '实付金额 = ' . $getAmount(120, 20);
echo '<br>';

// 父作用域是函数
$f = function (float $discount) {
    $getAmount = function (float $money, int $num) use ($discount): float {
        $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
# 函数返回值

// 单值返回,前面已经有了许多案例,重点放在多值返回上

// 1. 通过拼装字符串实现

function demo1(): string
{
    $status = 1;
    $message = '成功';
    return $status . ', ' . $message;
}

echo demo1();
// 字符串拼装返回非常适合处理大量变量与html标签的混写
// 字符串拼装返回多值有许多限制,并且对返回值的解析处理非常麻烦

echo '<hr>';

// 2. 通过数组返回多值
function demo2(): array
{
    return ['status' => 1, 'message' => '验证成功'];
}

echo demo2()['status'] === 1 ? demo2()['message'] : '验证失败';
// 返回值如果仍在php程序中处理,这样做很方便
// 开发中, 通常返回值都是要交给前端处理的,转为JSON格式更方便

echo '<hr>';



// 3. 通过JSON格式返回多值
// json是一种通用的,轻量级数据表示格式,使用javascript对象字面量表示数据
// 几乎所有编程程序,都支持定义和解析json格式的数据,请放心使用
// 当前后端时行数据交互时, json格式几乎成了除字符串之外的唯一选择
// json本质上仍是字符串, 只不过具有一些特殊格式,需要进行解析罢了

function demo3(): string
{
    // 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(): string
{
    return serialize(['status' => 1, 'message' => '验证成功']);
}
// 查看序列化的格式化字符串
echo demo4(), '<br>';
// 反序列化
$data = unserialize(demo4());
echo '<pre>' . print_r($data, true) . '</pre>';

echo '<hr>';

// 5. 返回null

function demo5(int $n): ?int
{
    if ($n < 10) return  $n * 2;
    // 如果允许返回null空, 必须显式的指出来,否则报错
    return null;
    // 可以简写
    // return ($n < 10) ?  $n * 2 : null;
}

echo demo5(15);

4. 参数

  • 调用者可以通过参数将数据传递到函数中
  • 参数是以逗号分隔的表达式列表
  • 参数按照从左到右的顺序求值

参数类型

序号 类型 描述
1 值参数 默认传参方式
2 引用参数 改变原始调用参数值
3 默认参数 调用时允许省略的参数
4 剩余参数 调用参数数量不确定

示例代码: demo4.php

# 函数参数

// 1. 值参数: 默认
function demo1(float $arg): float
{
    return $arg *= 2;
}

$value = 100;
echo demo1($value), '<br>';
echo $value, '<br>';

echo '<hr>';

// 2. 引用参数
// 引用传参: 参数前添加地址引用符
function demo2(int &$arg): float
{
    // 函数内部更新了调用参数,则直接影响到原值
    return $arg *= 2;
}

$value = 100;
echo demo2($value), '<br>';
echo $value, '<br>';

echo '<hr>';

// 3. 默认参数
// 参数列表中,允许有必选参数和默认参数二大类, 默认参数必须排列在必选参数的后面
// 默认参数决定了函数的默认行为,如果函数默认行为满足要求,显然不传参调用更方便
// 默认参数也允许用户根据自身需求,传入自定义参数,对函数的行为进行干预
// 注意, 执行正确返回数值,错误返回字符串类型, 所以不要限制函数的返回类型
function demo3(float $a, float $b, string $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(float $a, float $b, float $c): float
{
    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(float ...$args): float
{
    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

# 回调函数

$data = range(1, 100);
// print_r($data);

// 使用回调,可以异步的处理海量数据,并不中断当前程序
$arr1 = array_map(function (int $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

# 函数命名空间

// function demo1(): string
// {
//     return __FUNCTION__;
// }

// function demo1(): string
// {
//     return __FUNCTION__;
// }

// 报函数重复命名错误

// 可以在不同命名空间中创建同名函数
namespace ns1 {
    function demo1(): string
    {
        return __FUNCTION__;
    }
}


namespace ns2 {
    function demo1(): string
    {
        return __FUNCTION__;
    }
}

namespace {
    // 带有空间的函数,调用时需要带上它的空间名称
    echo \ns1\demo1();

    echo '<hr>';

    echo \ns2\demo1();
}