On this page

PHP 函数

用户自定义函数

一个函数可由以下的语法来定义:

示例 #1 展示函数用途的伪代码


<?php
function foo($arg_1, $arg_2, /* ..., */ $arg_n)
{
    echo "Example function.\n";
    return $retval;
}
?>

任何有效的 PHP 代码都有可能出现在函数内部,甚至包括其它函数和 定义。

函数名和 PHP 中的其它标识符命名规则相同。有效的函数名以字母或下划线打头,后面跟字母,数字或下划线。可以用正则表达式表示为: ^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$


函数的参数

通过参数列表可以传递信息到函数,即以逗号作为分隔符的表达式列表。函数在实际调用之前,值参数是从左向右求值的(及早求值)。

PHP 支持按值传递参数(默认),通过引用传递参数 以及 默认参数。也支持

[可变长度参数列表](https://www.php.net/manual/zh/functions.arguments.php#functions.variable-arg-list) 和
[命名参数](https://www.php.net/manual/zh/functions.arguments.php#functions.named-arguments)。

示例 #1 向函数传递数组


<?php
function takes_array($input)
{
    echo "$input[0] + $input[1] = ", $input[0]+$input[1];
}
?>

从 PHP 8.0.0 开始,函数参数列表可以包含一个尾部的逗号,这个逗号将被忽略。这在参数列表较长或包含较长的变量名的情况下特别有用,这样可以方便地垂直列出参数。

示例 #2 函数参数使用尾部逗号


<?php
function takes_many_args(
    $first_arg,
    $second_arg,
    $a_very_long_argument_name,
    $arg_with_default = 5,
    $again = 'a default string', // 在 8.0.0 之前,这个尾部的逗号是不允许的。
)
{
    // ...
}
?>

通过引用传递参数

默认情况下,函数参数通过值传递(因而即使在函数内部改变参数的值,它并不会改变函数外部的值)。如果希望允许函数修改它的参数值,必须通过引用传递参数。

如果想要函数的一个参数总是通过引用传递,可以在函数定义中该参数的前面加上符号 &:

示例 #3 用引用传递函数参数


<?php
function add_some_extra(&$string)
{
    $string .= 'and something extra.';
}
$str = 'This is a string, ';
add_some_extra($str);
echo $str;    // 输出 'This is a string, and something extra.'
?>

默认参数的值

函数可以使用类似分配变量的语法定义参数的默认值。仅当参数未指定时才使用默认值;需要注意的是传递

 **`null`**

不会分配默认值。

示例 #4 在函数中使用默认参数


<?php
function makecoffee($type = "cappuccino")
{
    return "Making a cup of $type.\n";
}
echo makecoffee();
echo makecoffee(null);
echo makecoffee("espresso");
?>

以上示例会输出:

Making a cup of cappuccino.
Making a cup of .
Making a cup of espresso.

默认参数值可以是标量值、array、特殊类型 null,以及从 PHP 8.1.0 开始,使用 new ClassName() 语法的对象。

示例 #5 使用非标量类型作为默认参数


<?php
function makecoffee($types = array("cappuccino"), $coffeeMaker = NULL)
{
    $device = is_null($coffeeMaker) ? "hands" : $coffeeMaker;
    return "Making a cup of ".join(", ", $types)." with $device.\n";
}
echo makecoffee();
echo makecoffee(array("cappuccino", "lavazza"), "teapot");?>

示例 #6 使用对象作为默认值(自 PHP 8.1.0 起)


<?php
class DefaultCoffeeMaker {
    public function brew() {
        return 'Making coffee.';
    }
}
class FancyCoffeeMaker {
    public function brew() {
        return 'Crafting a beautiful coffee just for you.';
    }
}
function makecoffee($coffeeMaker = new DefaultCoffeeMaker)
{
    return $coffeeMaker->brew();
}
echo makecoffee();
echo makecoffee(new FancyCoffeeMaker);
?>

默认值必须是常量表达式,不能是诸如变量,类成员,或者函数调用等。 注意任何可选参数都应在强制参数之后指定,否则可选参数不能在调用时省略。考虑以下示例:

示例 #7 函数默认参数的不正确用法


<?php
function makeyogurt($container = "bowl", $flavour)
{
    return "Making a $container of $flavour yogurt.\n";
}
 
echo makeyogurt("raspberry"); // "raspberry" 是 $container, 不是 $flavour
?>

以上示例会输出:

Fatal error: Uncaught ArgumentCountError: Too few arguments
 to function makeyogurt(), 1 passed in example.php on line 42
 现在,比较上面的例子和这个例子:

示例 #8 函数默认参数正确的用法


<?php
function makeyogurt($flavour, $container = "bowl")
{
    return "Making a $container of $flavour yogurt.\n";
}
 
echo makeyogurt("raspberry"); // "raspberry" 是 $flavour
?>

以上示例会输出:

Making a bowl of raspberry yogurt.

自 PHP 8.0.0 起,命名参数可用于跳过多个可选参数。

示例 #9 函数默认参数正确的用法


<?php
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 8.0.0 起,弃用在可选参数之后声明强制参数。这通常可以通过删除默认值来解决,因为它永远不会被使用。唯一的例外是

 `Type $param = null` 类型的参数,其中默认 **`null`** 使得该类型可以隐式为
 null。这种做法依然允许,但是推荐使用显式[可为 null 类型](https://www.php.net/manual/zh/language.types.declarations.php#language.types.declarations.nullable)代替。
 

示例 #10 强制参数后声明可选参数


<?php
 function foo($a = [], $b) {} // 默认不使用;自 PHP 8.0.0 起弃用
 function foo($a, $b) {}      // 功能相同,无弃用通知

 function bar(A $a = null, $b) {} // 仍然允许;但 $a 强制但可以为 null
 function bar(?A $a, $b) {}       // 推荐
 ?>

注意:

自 PHP 7.1.0 起,省略未指定默认值的参数会原因引发

  [ArgumentCountError](https://www.php.net/manual/zh/class.argumentcounterror.php);在此之前的版本会引发警告。

注意:

 传引用的参数也可以有默认值。

可变数量的参数列表

 PHP 在用户自定义函数中支持可变数量的参数列表。由
 `...` 语法实现。

注意:

还可以使用以下函数来获取可变参数

  [func_num_args()](https://www.php.net/manual/zh/function.func-num-args.php)、
  [func_get_arg()](https://www.php.net/manual/zh/function.func-get-arg.php) 和
  [func_get_args()](https://www.php.net/manual/zh/function.func-get-args.php),不建议使用此方式,请使用
  `...` 来替代。

包含 ... 的参数,会转换为指定参数变量的一个 array:

示例 #11 使用 ... 来访问变量参数


<?php
function sum(...$numbers) {
    $acc = 0;
    foreach ($numbers as $n) {
        $acc += $n;
    }
    return $acc;
}

echo sum(1, 2, 3, 4);
?>

以上示例会输出:

10

也可以使用 ... 语法来传递 array 或

 [Traversable](https://www.php.net/manual/zh/class.traversable.php)
 做为参数到函数中:
 

示例 #12 使用 ... 来传递参数


<?php
function add($a, $b) {
    return $a + $b;
}

echo add(...[1, 2])."\n";

$a = [1, 2];
echo add(...$a);
?>

以上示例会输出:

3
3

你可以在

 `...` 前指定正常的位置参数。在这种情况下,只有不符合位置参数的尾部参数才会被添加到
 `...` 生成的数组中。

你也可以在

 `...` 标记前添加一个
 [类型声明](https://www.php.net/manual/zh/language.types.declarations.php)。如果存在这种情况,那么
 `...` 捕获的所有参数都必须匹配参数类型。
 

示例 #13 输入提示的变量参数


<?php
function total_intervals($unit, DateInterval ...$intervals) {
    $time = 0;
    foreach ($intervals as $interval) {
        $time += $interval->$unit;
    }
    return $time;
}

$a = new DateInterval('P1D');
$b = new DateInterval('P2D');
echo total_intervals('d', $a, $b).' days';

// 这将会失败,因为 null 不是 DateInterval 对象。
echo total_intervals('d', null);
?>

以上示例会输出:

3 days
Catchable fatal error: Argument 2 passed to total_intervals() must be an instance of DateInterval, null given, called in - on line 14 and defined in - on line 2

最后,你还可以给参数传递

 [引用变量](https://www.php.net/manual/zh/functions.arguments.php#functions.arguments.by-reference),通过在
  `...` 前加上一个 (`&`) 符号来实现。

旧版本的 PHP

不需要特殊的语法来声明一个函数是可变的;但是访问函数的参数必须使用

  [func_num_args()](https://www.php.net/manual/zh/function.func-num-args.php), [func_get_arg()](https://www.php.net/manual/zh/function.func-get-arg.php)
  和 [func_get_args()](https://www.php.net/manual/zh/function.func-get-args.php) 函数。
 
  上面的第一个例子在早期 PHP 版本中的实现如下:
  

示例 #14 在 PHP 早期版本中访问可变参数


<?php
function sum() {
    $acc = 0;
    foreach (func_get_args() as $n) {
        $acc += $n;
    }
    return $acc;
}

echo sum(1, 2, 3, 4);
?>

以上示例会输出:

10

命名参数

PHP 8.0.0 开始引入了命名参数作为现有位置参数的扩展。命名参数允许根据参数名而不是参数位置向函数传参。这使得参数的含义自成体系,参数与顺序无关,并允许任意跳过默认值。

命名参数通过在参数名前加上冒号来传递。允许使用保留关键字作为参数名。参数名必须是一个标识符,不允许动态指定。

示例 #15 命名参数的语法


<?php
myFunction(paramName: $value);
array_foobar(array: $value);

// 不支持。
function_name($variableStoringParamName: $value);
?>

示例 #16 通过位置传参与命名参数的对比


<?php
// 使用顺序传递参数:
array_fill(0, 100, 50);

// 使用命名参数:
array_fill(start_index: 0, count: 100, value: 50);
?>
 指定参数的传递顺序并不重要。

示例 #17 参数顺序不同的示例(同上例)


<?php
array_fill(value: 50, count: 100, start_index: 0);
?>
 命名参数也可以与位置参数相结合使用。此种情况下,命名参数必须在位置参数之后。也可以只指定一个函数的部分可选参数,而不考虑它们的顺序。

示例 #18 命名参数与位置参数结合使用


<?php
htmlspecialchars($string, double_encode: false);
// 等价于
htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, 'UTF-8', false);
?>
 传递多个相同参数将会导致 Error 异常。

示例 #19 传递多个相同参数将会导致抛出 Error


<?php
function foo($param) { ... }

foo(param: 1, param: 2);
// 错误:命名参数 $param 覆盖了之前的参数
foo(1, param: 2);
// 错误:命名参数 $param 覆盖了之前的参数
?>
 自 PHP 8.1.0 起,可以在解包参数后面使用命名参数。命名参数不能覆盖已解包的参数。

示例 #20 解包后使用命名参数


<?php
function foo($a, $b, $c = 3, $d = 4) {
  return $a + $b + $c + $d;
}

var_dump(foo(...[1, 2], d: 40)); // 46
var_dump(foo(...['b' => 2, 'a' => 1], d: 40)); // 46

var_dump(foo(...[1, 2], b: 20)); // Fatal error。命名参数 $b 覆盖之前的参数

返回值

值通过使用可选的返回语句返回。可以返回包括数组和对象的任意类型。返回语句会立即中止函数的运行,并且将控制权交回调用该函数的代码行。

注意:

如果省略了return,则返回值为 null

return 的使用

示例 #1 return 的使用


<?php
function square($num)
{
    return $num * $num;
}
echo square(4);   // 输出 '16'。
?>
 函数不能返回多个值,但可以通过返回一个数组来得到类似的效果。

示例 #2 返回一个数组以得到多个返回值


<?php
function small_numbers()
{
    return [0, 1, 2];
}
// 使用短数组语法将数组中的值赋给一组变量
[$zero, $one, $two] = small_numbers();

// 在 7.1.0 之前,唯一相等的选择是使用 list() 结构
list($zero, $one, $two) = small_numbers();
?>
 从函数返回一个引用,必须在函数声明和指派返回值给一个变量时都使用引用运算符 &:

示例 #3 从函数返回一个引用


<?php
function &returns_reference()
{
    return $someref;
}

$newref =& returns_reference();
?>

可变函数

PHP 支持可变函数的概念。这意味着如果一个变量名后有圆括号,PHP 将寻找与变量的值同名的函数,并且尝试执行它。可变函数可以用来实现包括回调函数,函数表在内的一些用途。

可变函数不能用于例如

[echo](https://www.php.net/manual/zh/function.echo.php),[print](https://www.php.net/manual/zh/function.print.php),[unset()](https://www.php.net/manual/zh/function.unset.php),[isset()](https://www.php.net/manual/zh/function.isset.php),[empty()](https://www.php.net/manual/zh/function.empty.php),[include](https://www.php.net/manual/zh/function.include.php),[require](https://www.php.net/manual/zh/function.require.php)
以及类似的语言结构。需要使用自己的包装函数来将这些结构用作可变函数。

示例 #1 可变函数示例


<?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()
?>

内部(内置)函数

PHP 有很多标准的函数和结构。还有一些函数需要和特定地 PHP扩展模块一起编译,否则在使用它们的时候就会得到一个致命的“未定义函数”错误。


匿名函数

匿名函数(Anonymous functions),也叫闭包函数(closures),允许临时创建一个没有指定名称的函数。最经常用作回调函数 callable参数的值。当然,也有其它应用的情况。

匿名函数目前是通过 Closure 类来实现的。

示例 #1 匿名函数示例


<?php
echo preg_replace_callback('~-([a-z])~', function ($match) {
    return strtoupper($match[1]);
}, 'hello-world');
// 输出 helloWorld
?>

闭包函数也可以作为变量的值来使用。PHP 会自动把此种表达式转换成内置类

[Closure](https://www.php.net/manual/zh/class.closure.php) 的对象实例。把一个 closure
对象赋值给一个变量的方式与普通变量赋值的语法是一样的,最后也要加上分号:

示例 #2 匿名函数变量赋值示例


<?php
$greet = function($name) {
    printf("Hello %s\r\n", $name);
};

$greet('World');
$greet('PHP');
?>

闭包可以从父作用域中继承变量。

任何此类变量都应该用 `use` 语言结构传递进去。
PHP 7.1 起,不能传入此类变量:  [superglobals](https://www.php.net/manual/zh/language.variables.predefined.php)、 $this 或者和参数重名。
返回类型声明必须放在 `use` 子句的 后面 。

示例 #3 从父作用域继承变量


<?php
$message = 'hello';

// 没有 "use"
$example = function () {
    var_dump($message);
};
$example();

// 继承 $message
$example = function () use ($message) {
    var_dump($message);
};
$example();

// 当函数被定义而不是被调用的时候继承变量的值
$message = 'world';
$example();

// 重置 message
$message = 'hello';

// 通过引用继承
$example = function () use (&$message) {
    var_dump($message);
};
$example();

// 父级作用域改变的值反映在函数调用中
$message = 'world';
$example();

// 闭包函数也可以接受常规参数
$example = function ($arg) use ($message) {
    var_dump($arg . ' ' . $message);
};
$example("hello");

// 返回类型在 use 子句的后面
$example = function () use ($message): string {
    return "hello $message";
};
var_dump($example());
?>

以上示例的输出类似于:

Notice: Undefined variable: message in /example.php on line 6
NULL
string(5) "hello"
string(5) "hello"
string(5) "hello"
string(5) "world"
string(11) "hello world"
string(11) "hello world"

箭头函数

箭头函数是 PHP 7.4 的新语法,是一种更简洁的

[匿名函数](https://www.php.net/manual/zh/functions.anonymous.php) 写法。

匿名函数和箭头函数都是

[Closure](https://www.php.net/manual/zh/class.closure.php) 类的实现。

箭头函数的基本语法为

`fn (argument_list) => expr`。

箭头函数支持与 匿名函数

相同的功能,只是其父作用域的变量总是自动的。

当表达式中使用的变量是在父作用域中定义的,它将被隐式地按值捕获。在下面的例子中,函数

$fn1 和 $fn2 的行为是一样的。

示例 #1 箭头函数自动捕捉变量的值


<?php

$y = 1;

$fn1 = fn($x) => $x + $y;
// 相当于通过 value 使用 $y:
$fn2 = function ($x) use ($y) {
    return $x + $y;
};

var_export($fn1(3));
?>

以上示例会输出:

4
在箭头函数嵌套的情况下同样有效。

示例 #2 箭头函数自动捕捉变量的值,即使在嵌套的情况下


<?php

$z = 1;
$fn = fn($x) => fn($y) => $x * $y + $z;
// 输出 51
var_export($fn(5)(10));
?>

和匿名函数一样,箭头函数语法同样允许标准的函数声明,包括参数和返回类型、缺省值、变量,以及通过引用传递和返回。以下都是箭头函数的有效例子。

示例 #3 合法的箭头函数例子


<?php

fn(array $x) => $x;
static fn(): int => $x;
fn($x = 42) => $x;
fn(&$x) => $x;
fn&($x) => $x;
fn($x, ...$rest) => $rest;

?>

箭头函数会自动绑定上下文变量,这相当于对箭头函数内部使用的每一个变量 $x 执行了一个

`use($x)`。这意味着不可能修改外部作用域的任何值,若要实现对值的修改,可以使用
[匿名函数](https://www.php.net/manual/zh/functions.anonymous.php) 来替代。

示例 #4 来自外部范围的值不能在箭头函数内修改


<?php

$x = 1;
$fn = fn() => $x++; // 不会影响 x 的值
$fn();
var_export($x);  // 输出 1