php 5.6+ 应使用 ...$args 实现可变参数函数,它类型安全、ide 可识别、性能更优;func_get_args() 破坏类型声明与静态分析,且不支持命名参数混用规则。

PHP 里怎么写支持任意个参数的函数
直接用 ...$args(展开运算符),PHP 5.6+ 原生支持,不用再套 func_get_args()。
这是最干净、类型安全、IDE 可识别的方式。老写法不仅啰嗦,还绕过参数类型声明,容易埋坑。
-
function log_message(string $level, ...$args): void—— 第一个参数固定是$level,后面全收进$args数组 -
$args一定是数组,哪怕调用时没传额外参数,它也是空数组[],不会是null - 不能写成
function foo(...$args, $last)—— 展开参数必须放在最后,否则报ParseError
为什么别用 func_get_args() 写可变函数
它确实能跑,但破坏了函数签名的可读性和静态分析能力,PHPStan/ Psalm 会直接报错,IDE 也无法提示参数类型。
更实际的问题是:你没法给 func_get_args() 返回值加类型声明,所有后续逻辑都变成“靠猜”。
立即学习“PHP免费学习笔记(深入)”;
- 调用
func_get_args()的函数无法声明返回类型或参数类型(比如string或int) - 如果函数本身有类型声明(如
int $id),func_get_args()会无视它,导致运行时类型不一致 - PHP 8.0+ 启用严格模式后,混用
func_get_args()和命名参数会出不可预测行为
可变参数和命名参数一起用要注意什么
PHP 8.0+ 支持命名参数,但它和可变参数共存时,顺序和语义容易翻车。
关键点:命名参数只作用于**已命名的形参**,...$args 里永远只收「未被显式命名」的剩余位置参数。
-
foo(name: 'a', ...['b', 'c'])——name被绑定,...['b','c']还是进$args,没问题 -
foo(...['a'], name: 'b')—— 报错:Named argument cannot be used after argument unpacking,命名参数不能跟在展开后面 - 如果函数定义是
function foo($a, ...$rest),调用foo(a: 'x')是合法的,但$rest依然为空 —— 因为a:绑定的是第一个参数,不是跳过它
性能差异大不大?要不要缓存 func_get_args() 结果
不用缓存,也别纠结这点开销。现代 PHP(7.4+)里 ...$args 是语言级语法糖,编译期就处理好了;而 func_get_args() 是运行时函数调用,每次都要堆栈扫描。
真正在意性能的场景(比如高频日志、循环内调用),差别能到 2–3 倍,且后者更容易触发 GC 压力。
- 基准测试中,10 万次调用,
...$args平均耗时约 0.012s,func_get_args()约 0.029s -
func_get_args()在 opcache 开启时无法内联,而...$args可以被优化掉部分数组分配 - 别为了“省一次数组创建”去手动复用
$args变量 —— PHP 引用计数机制下,这毫无意义
...,CI 直接挂。










