PHP 函数
函数是 PHP 编程中的重要组成部分,通过函数可以更好地组织代码,提高代码的质量和效率。
用户自定义函数
在PHP中,可以通过特定的语法来创建自己的函数。以下是一个简单的函数定义示例:
function 示例函数($参数1, $参数2, /* ..., */ $参数n) {
echo "这是一个示例函数。\n";
return $返回值;
}
在函数体内,可以放置任何有效的PHP代码,甚至是其他的函数或类定义。
函数命名规则
函数名应遵循PHP标识符的一般命名规则:以字母或下划线开头,并且可以包含字母、数字和下划线。这可以通过正则表达式^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$
来表示。
命名建议
建议参考用户空间命名指南来选择合适的函数名称。
函数定义时机
通常,函数不必在调用之前定义。但是,如果函数是在条件语句中定义的,则必须确保在调用之前已经定义了该函数。例如:
$创建函数 = true;
// 不能在这里调用示例函数,因为它还未定义
通用函数();
if ($创建函数) {
function 示例函数() {
echo "直到程序执行到这里,我才存在。\n";
}
}
// 如果$创建函数为true,则可以安全调用示例函数
if ($创建函数) 示例函数();
function 通用函数() {
echo "我在程序启动时就存在。\n";
}
函数中的函数
可以在一个函数内部定义另一个函数,但这个内部函数只有在外部函数被调用时才存在。例如:
function 外部函数() {
function 内部函数() {
echo "我只在外部函数被调用时才存在。\n";
}
}
// 直到外部函数被调用之前,无法调用内部函数
外部函数();
// 此时可以调用内部函数,因为外部函数已经被执行过
内部函数();
作用域
PHP中的函数和类具有全局作用域,可以在一个函数内部定义而在外部调用,反之亦然。
函数重载与取消定义
PHP不支持函数重载(即不允许存在同名但参数不同的多个函数),也无法取消定义或重新定义一个已经声明的函数。
参数处理
PHP支持可变数量的参数和默认参数。可以使用func_num_args()
、func_get_arg()
和func_get_args()
等内置函数来处理传递给函数的参数。
递归函数
递归函数是指函数可以直接或间接地调用自身。虽然PHP支持递归函数,但应避免递归调用层数过多(超过100-200层),以防栈溢出导致脚本终止。无限递归被视为编程错误。例如:
function 递归($a) {
if ($a < 20) {
echo "$a\n";
递归($a + 1);
}
}
函数的参数
可以通过参数列表来给函数传递数据。这些参数就像是函数的一系列输入,它们之间用逗号隔开。当函数被调用时,如果参数是值参数的话,它们会按照从左到右的顺序进行计算(也就是立即求值)。
在 PHP 中,传递参数有以下几种方式:
- 按值传递参数(默认方式)
- 按引用传递参数
- 使用带有默认值的参数
- 还支持可变数量的参数列表和命名参数
以下例子,展示如何向函数传递一个数组:
function processArray($array) {
echo $array[0] . " + " . $array[1] . " = " . ($array[0] + $array[1]);
}
从 PHP 8.0.0 版本开始,在定义函数时可以在参数列表的最后添加一个额外的逗号,这个逗号会被忽略。这个特性对于长参数列表或具有长名称的变量尤其有用,因为它允许你将参数垂直排列,提高代码的可读性。
以下例子,展示了如何利用尾部逗号来定义一个接受多个参数的函数:
function processMultipleArgs(
$firstParam,
$secondParam,
$veryLongParameterName,
$paramWithDefault = 5,
$anotherParam = 'default string',
) {
// 函数体
}
注意,在 PHP 8.0.0 之前,在参数列表的末尾添加逗号是不被允许的。
通过引用传递参数
PHP 中,默认情况下,当你把一个变量传递给函数时,实际上是传递了该变量的一个副本。意味着你在函数内部改变了这个参数的值,原始变量的值不会受到影响。如果你想让函数能够直接修改传递给它的变量,那么你需要通过“引用”来传递这个变量。
要通过引用传递一个参数,你可以在函数定义中的参数前加上 &
符号。这样,函数内部对这个参数的任何修改都会反映到原始变量上。
以下例子,演示了如何通过引用传递参数:
function appendExtra(&$text) {
$text .= 'and something extra.';
}
$myText = 'This is a string, ';
appendExtra($myText);
echo $myText; // 输出 'This is a string, and something extra.'
默认参数的值
在 PHP 中,你可以为函数参数设置默认值。如果在函数调用时没有提供某个参数,就会使用这个默认值。但是,如果传递了 null
,则不会使用默认值。
示例
function makeCoffee($type = "cappuccino") {
return "Making a cup of $type.\n";
}
echo makeCoffee(); // 输出: Making a cup of cappuccino.
echo makeCoffee(null); // 输出: Making a cup of .
echo makeCoffee("espresso"); // 输出: Making a cup of espresso.
默认参数可以是标量值、数组、null
,以及从 PHP 8.1.0 开始还可以是使用 new ClassName()
语法创建的对象。
非标量类型的默认参数
function makeCoffee($types = ["cappuccino"], $coffeeMaker = null) {
$device = is_null($coffeeMaker) ? "hands" : $coffeeMaker;
return "Making a cup of " . implode(", ", $types) . " with $device.\n";
}
echo makeCoffee(); // 输出: Making a cup of cappuccino with hands.
echo makeCoffee(["cappuccino", "lavazza"], "teapot"); // 输出: Making a cup of cappuccino, lavazza with teapot.
对象作为默认值
从 PHP 8.1.0 开始,你可以使用对象作为默认参数值。
class DefaultCoffeeMaker {
public function brew() {
return "Making coffee.\n";
}
}
class FancyCoffeeMaker {
public function brew() {
return "Crafting a beautiful coffee just for you.\n";
}
}
function makeCoffee($coffeeMaker = new DefaultCoffeeMaker()) {
return $coffeeMaker->brew();
}
echo makeCoffee(); // 输出: Making coffee.
echo makeCoffee(new FancyCoffeeMaker()); // 输出: Crafting a beautiful coffee just for you.
参数顺序
所有可选参数必须放在强制参数之后。否则,如果省略了中间的可选参数,会导致错误。
错误的参数顺序
function makeYogurt($container = "bowl", $flavour) {
return "Making a $container of $flavour yogurt.\n";
}
echo makeYogurt("raspberry"); // 这里会引发错误
正确的参数顺序
function makeYogurt($flavour, $container = "bowl") {
return "Making a $container of $flavour yogurt.\n";
}
echo makeYogurt("raspberry"); // 输出: Making a bowl of raspberry yogurt.
命名参数
自 PHP 8.0.0 起,你可以使用命名参数来跳过中间的可选参数。
function makeYogurt($container = "bowl", $flavour = "raspberry", $style = "Greek") {
return "Making a $container of $flavour $style yogurt.\n";
}
echo makeYogurt(style: "natural"); // 输出: Making a bowl of raspberry natural yogurt.
可变数量的参数列表
PHP 允许你在自定义函数中接收可变数量的参数。通过在参数列表中使用 ...
语法来实现。
当你在函数参数中使用 ...
时,所有传递给该参数的值将被收集到一个数组中。
示例
function sum(...$numbers) {
$total = 0;
foreach ($numbers as $number) {
$total += $number;
}
return $total;
}
echo sum(1, 2, 3, 4); // 输出: 10
命名参数
从 PHP 8.0.0 开始,PHP 引入了命名参数作为位置参数的扩展。命名参数允许你根据参数名而不是参数位置来传递参数。这使得参数的含义更加明确,并且可以跳过那些有默认值的参数。
语法
命名参数通过在参数名前加上冒号 :
来传递。这样可以让你在调用函数时明确指定每个参数的含义,并且不需要按照严格的顺序传递参数。
示例
function myFunction($paramName, $anotherParam = "default") {
echo "paramName: $paramName, anotherParam: $anotherParam\n";
}
// 位置参数调用
myFunction("value1", "value2"); // 输出: paramName: value1, anotherParam: value2
// 命名参数调用
myFunction(anotherParam: "value2", paramName: "value1"); // 输出: paramName: value1, anotherParam: value2
// 跳过有默认值的参数
myFunction(paramName: "value1"); // 输出: paramName: value1, anotherParam: default
注意事项
- 参数名必须是一个有效的标识符,不能动态指定。
- 可以使用保留关键字作为参数名。
- 如果有默认值的参数在中间,则可以在调用时跳过它们。
返回值
在 PHP 中,函数可以通过 return
语句返回任意类型的值,包括数组和对象。return
语句会立即终止函数的执行,并将控制权交回调用该函数的代码行。
注意
- 如果省略了
return
语句,则函数默认返回null
。 - 函数不能直接返回多个独立的值,但可以通过返回一个数组或对象来达到类似的效果。
示例
function square($num) {
return $num * $num;
}
echo square(4); // 输出 '16'
在这个例子中:
square
函数接收一个参数$num
,并返回它的平方。- 调用
square(4)
会返回16
,并输出结果。
返回多个值
虽然函数不能直接返回多个独立的值,但你可以通过返回一个数组来实现类似的效果:
function getCoordinates() {
return [40.7128, -74.0060]; // 纬度和经度
}
list($latitude, $longitude) = getCoordinates();
echo "Latitude: $latitude, Longitude: $longitude"; // 输出 'Latitude: 40.7128, Longitude: -74.006'
可变函数
在 PHP 中,可变函数是指通过变量名来调用函数的能力。你可以将一个函数名存储在一个变量中,并通过该变量来调用函数。
可变函数可以用于多种场景,特别是当需要动态决定调用哪个函数时。常见的用途包括:
- 回调函数:在处理事件驱动编程或需要异步操作时,可以将函数名作为参数传递给另一个函数。
- 函数表:可以创建一个函数列表或字典,根据条件选择不同的函数来执行。
- 插件系统:在构建插件系统时,可以根据插件提供的函数名动态调用不同的功能。
示例
function foo() {
echo "In foo()<br />\n";
}
function bar($arg = '') {
echo "In bar(); argument was '$arg'.<br />\n";
}
// 使用 echo 的包装函数
function echoit($string) {
echo $string;
}
$func = 'foo';
$func(); // 调用 foo()
$func = 'bar';
$func('test'); // 调用 bar()
$func = 'echoit';
$func('test'); // 调用 echoit()
注意事项
可变函数不能用于某些语言结构,如 echo
、print
、unset()
、isset()
、empty()
、include
和 require
等。为了使用这些结构作为可变函数,你需要创建一个包装函数。
调用对象的方法
也可以使用可变函数的语法来调用一个对象的方法:
class MyClass {
public function myMethod($arg) {
echo "In myMethod(); argument was '$arg'.<br />\n";
}
}
$object = new MyClass();
$method = 'myMethod';
$object->$method('test'); // 调用 myMethod()
内部(内置)函数
PHP 提供了许多标准的内置函数。这些函数分为两类:
- 核心函数:随每个 PHP 版本自带的核心函数,如字符串处理和变量操作函数。
- 扩展函数:需要特定的 PHP 扩展模块支持的函数。如果没有编译时启用相应的扩展,使用这些函数会导致 “未定义函数” 的致命错误。
如何查看已加载的扩展
通过调用 phpinfo()
或 get_loaded_extensions()
来查看 PHP 已加载的扩展库。
示例
- 要使用
imagecreatetruecolor()
函数,需要在编译 PHP 时加上 GD 图像库的支持。 - 要使用
mysqli_connect()
函数,需要在编译 PHP 时加上 MySQLi 扩展的支持。
查看函数原型
PHP 手册中详细介绍了如何阅读和理解函数的原型。了解函数的返回值类型以及它是否直接修改传递的参数非常重要。
- 例如,
str_replace()
返回修改后的字符串。 - 而
usort()
直接修改传递的数组。
注意事项
- 如果传递给函数的参数类型不匹配,函数的行为可能是不确定的。从 PHP 8.0.0 起,这种情况下会抛出
TypeError
异常。 - 在严格模式下,传递
null
给声明为非null
类型的参数会导致TypeError
。
强制模式下的内置函数
从 PHP 8.1.0 起,传递 null
到声明为非 null
类型的内置函数参数被弃用,并且在严格模式下会发出弃用通知。
示例
var_dump(strlen(null)); // 输出 int(0),但在 PHP 8.1.0 起会发出弃用通知
var_dump(str_contains("foobar", null)); // 输出 bool(true),但在 PHP 8.1.0 起会发出弃用通知
匿名函数
匿名函数(Anonymous functions),也称为闭包函数(closures),允许我们创建一个没有名称的临时函数。匿名函数常用于回调函数中,即作为 callable
参数的值。除此之外,它们还有其他用途。
在 PHP 中,匿名函数是通过 Closure
类来实现的。
匿名函数的使用
echo preg_replace_callback('~-([a-z])~', function ($match) {
return strtoupper($match[1]);
}, 'hello-world');
// 输出: helloWorld
我们还可以将匿名函数赋值给一个变量。PHP 会自动将这种函数表达式转换为 Closure
类的实例。把一个匿名函数赋值给变量的语法与普通变量赋值的语法相同,结尾也要加上分号。
将匿名函数赋值给变量
$greet = function($name) {
printf("Hello %s\r\n", $name);
};
$greet('World');
$greet('PHP');
箭头函数
箭头函数是 PHP 7.4 中引入的新语法,提供了一种更简洁的方式来编写匿名函数。箭头函数和传统的匿名函数都是 Closure
类的实现。
语法
箭头函数的基本语法如下:
fn (argument_list) => expr
这里的 argument_list
是参数列表,expr
是返回的表达式。
特点
- 简洁性:箭头函数的语法比传统匿名函数更简洁。
- 自动捕获变量:箭头函数会自动捕获父作用域中的变量,并且默认是按值捕获。
示例
箭头函数自动捕获变量的值
$y = 1;
$fn1 = fn($x) => $x + $y;
// 相当于通过 value 使用 $y:
$fn2 = function ($x) use ($y) {
return $x + $y;
};
var_export($fn1(3)); // 输出 4
嵌套箭头函数
$z = 2;
$outerFn = fn($x) => {
$innerFn = fn($y) => $x + $y + $z;
return $innerFn(3);
};
var_export($outerFn(1)); // 输出 6
First class 可调用语法
从 PHP 8.1.0 开始,引入了 First Class 可调用语法,这是一种从 callable
创建匿名函数的新方法。这种语法的优势在于它可以进行静态分析,并且可以使用可调用对象的作用域。
语法
CallableExpr(...)
语法用于从可调用对象创建 Closure
。CallableExpr
接受任何可以直接调用的表达式。
示例
简单的 First Class 可调用语法
class Foo {
public function method() {}
public static function staticmethod() {}
public function __invoke() {}
}
$obj = new Foo();
$classStr = 'Foo';
$methodStr = 'method';
$staticmethodStr = 'staticmethod';
// 使用 First Class 可调用语法
$f1 = strlen(...);
$f2 = $obj(...); // 可调用对象
$f3 = $obj->method(...);
$f4 = $obj->$methodStr(...);
$f5 = Foo::staticmethod(...);
$f6 = $classStr::$staticmethodStr(...);
// 传统的 callable 语法
$f7 = 'strlen'(...);
$f8 = [$obj, 'method'](...);
$f9 = [Foo::class, 'staticmethod'](...);
...
是语法的一部分,表示传递给闭包的参数。