【PHP Telegram Bot】Day17 - 基础(6):函式与作用域

函式就像是一个小程序一样,把多个指令包装在一块,用简单的方式就能使用

函式主要有两种:

// 非匿名
function sum($a, $b) {
    return $a + $b;
}
echo sum(1, 2); // 3

// 匿名
$sum = function ($a, $b) {
    return $a + $b;
};
echo $sum(1, 2); // 3

函式通常是非匿名方式定义的,名称可以自己决定,建议动词开头取名,取成这个函式的功能,让人一眼就知道怎麽用这个函式

匿名函式则比较少见,你可以发现它本身是没有名字的,有名字的是装着它的变数


一般函式 Function

函式的参数可以设定预设值,并且在呼叫时可以不填

function sum($a, $b = 2) { // b 预设为 2
    return $a + $b;
}
echo sum(1); // 3

可以限制参数的型别 int float array...,不可使用 null

function sum(int $a, int $b) {
    return $a + $b;
}
echo sum(1, 2); // 3
echo sum(null, 2); // error

在型别前面加上 ? 表示参数可以是 null

function sum(?int $a, int $b) {
    return ($a ?? 1) + $b;
}
echo sum(null, 2); // 3

返回值也可以设定型别,void 表示不返回东西

function sum($a, $b): float {
    return $a + $b;
}
var_dump(sum(1, 2)); // float(3)

可以使用 ... 让函式拥有可变长度参数列表

function sum($a, $b, ...$c) {
    print_r($c);
    // [0] => 3
    // [1] => 4
    // [2] => 5
    return $a + $b + array_sum($c);
}
echo sum(1, 2, 3, 4, 5); // 15

函式不能有多个返回值

function sum($a, $b) {
    return $a, $b; // syntax error
}
sum(1, 2); 

但是可以用阵列包起来,再用阵列取值的方式拿出来

function sum($a, $b) {
    return [$a, $b];
}
[$x, $y] = sum(1, 2);
echo $x, "\n"; // 1
echo $y, "\n"; // 2

使用函式时可以利用阵列解包填入参数

function sum($a, $b) {
    return $a + $b;
}
$x = [1, 2];
echo sum(...$x); // 3

匿名函式 Anonymous Function

一般函式的功能它都有,它还有一个额外的关键字 use,让它可以使用外面的变数,但是是复制一份进去,并不是原本的那个

$x = 1;
$addX = function () use ($x) {
    $x++;
    echo $x, "\n"; // 2
};
$addX();
echo $x, "\n"; // 1

匿名函式可以做为函式的参数 Callback Function

function callback(callable $a) {
    $a();
}
$x = function () {
    echo 'Hello, World!';
};
callback($x); // Hello, World!

匿名函式也可以做为函式的返回值,制作闭包 Closure

function makeClosure($a) {
    return function ($b) use ($a) {
        return $a . ', ' . $b . '!';
    };
}
$closure = makeClosure('Hello'); // 把 Hello 关在里面
echo $closure('World'); // Hello, World!

另外还有一种简化的匿名函式:

箭头函式 Arrow Function

会自动返回结果,不用打 return

不需要 use 就能使用外面的变数,但一样是复制过後的

$x = 1;
$add = fn () => $x += 1;

echo $add(), "\n"; // 2
echo $x, "\n"; // 1

作用域 Scope

在函式中是不能存取外面的变数的,这个特性可以防止意外的改动

$x = 1;
function readX() {
    echo $x; // error
}
readX();

常数没有这个限制,因为原本就改不了

const X = 1;
function readX() {
    echo X; // 1
}
readX();

加一行 global 的话就能够修改外面的变数(建议不要这样做)

$x = 1;
function readX() {
    global $x;
    echo $x; // 1
}
readX();

建议将变数透过参数传入使用,这样虽然会无法修改外面的变数,但可以让程序比较乾净

$x = 1;
function readX($a) {
    echo ++$a, "\n"; // 2
}
readX($x);
echo $x; // 1

可以透过传参考 Call By Reference,在参数前加 &,使得两个变数指向同一个记忆体位置

$x = 1;
function readX(&$a) {
    echo ++$a, "\n"; // 2
}
readX($x);
echo $x; // 2

返回值也可以设定成传参考,函式名称前要加 &,不论是在定义还是使用时

$x = 1;
function &readX(&$a) {
    echo ++$a, "\n"; // 2
    return $a;
}
$i = &readX($x);
echo $x, "\n"; // 2
echo $i, "\n"; // 2
$i++;
echo $x, "\n"; // 3
echo $i, "\n"; // 3

匿名函式的 use 中的参数也可以传参考

$x = 1;
$addX = function () use (&$x) {
    $x++;
};
$addX();
echo $x; // 2

其他特性

函式中函式 Functions Within Function

函式可以在函式里定义,而且不论在哪里定义,都可以在全域使用

但是要执行函式(createSum)才会定义函式(sum)

function createSum() {
    function sum($a, $b) {
        return $a + $b;
    }
}
echo sum(1, 2); // error
createSum();
echo sum(1, 2); // 3

递回函式 Recursive Function

函式可以呼叫自己,例如制作 f(x) = x + f(x - 1)

function sum($a) {
    if ($a <= 0) {
        return 0;
    }
    return $a + sum($a - 1);
}
echo sum(10); // 55

变数函式 Variable Function

如果变数後面附加了括号,PHP 会找与值相同名称的函式,并尝试执行它

function sum($a, $b) {
    return $a + $b;
}
$a = 'sum';
echo $a(1, 2);

关於函式的内建函式 Function Handling Function

函式 说明 用法
function_exists() 检查函式是否存在 function_exists($fn_name)
get_defined_functions() 查看所有已定义函式 get_defined_functions()
function createSum() {
    function sum($a, $b) {
        return $a + $b;
    }
}
var_dump(function_exists('sum')); // bool(false)
createSum();
var_dump(function_exists('sum')); // bool(true)

其他内建函式:https://www.php.net/manual/en/ref.funchand.php


<<:  [Day22] - 介绍 LitElement 如何使用

>>:  OpenWRT安装与设定

Day11:Swift 基础语法 —Array

前言 上一篇文章讲到 Dictionary, 今天讲另一个值的集合 - Array。 Array 和...

[机派X] Day 15 - 把你扳直!来校正飞行控制器吧!

引言 今天是机派X系列文章的第十五天。 昨天刚组装好无人机,今天要为飞行控制器做初始设定以及校正。 ...

[Day_6]资料型别、变数与运算子 - 练习题

这边会给大家一些练习题, 以及参考解答, 过程可能会与大家有些不尽相同, 还请各位多多包涵, 我会先...

[Day24] CH11:刘姥姥逛物件导向的世界——抽象、介面

今天要来介绍这个主题最後一个单元了,废话不多说就直接进入正题吧! 抽象(Abstract)类别与方法...

会员管理网站实作篇 - (以律师谘询平台为例子) part 1

前言 最後四篇篇幅我们以实作一个会员网站为例子,想做这个主题原因在於会员网站在 WP 中算是个少见的...