← PHP参数化函数:让函数活起来的参数传递机制 PHP按引用传递:让函数直接修改外部变量的机制 →

PHP按值传递:理解函数参数的默认传递机制

原创 2026-05-09 PHP 已有人查阅

在PHP里把变量传给函数时,有一个底层行为决定了函数内部对参数的修改会不会“泄漏”到函数外部。这个行为就是按值传递(Call by Value)。它是PHP默认的参数传递方式,适用于整数、浮点数、字符串、布尔值这些标量类型。

按值传递的核心规则就一句话:函数拿到的是原始变量的一个副本,不是原始变量本身。 你在函数里对这个副本做任何修改,都不会影响到外面的原始变量。理解这个规则,对写出可预测的、bug少的代码比较关键。

用生活中的例子理解按值传递

把按值传递想象成复印文件

你手里有一份原始合同。同事找你要这份合同看看,你不是把原件递给他,而是去复印机复印了一份交给他。同事拿到复印件后,可以在上面写写画画、批注修改,甚至把复印件揉成团扔掉。但不管你同事对复印件做了什么,你抽屉里的那份原件始终完好无损。

在这个比喻里:

  • 原始合同 = 函数外部的原始变量

  • 复印机复印 = PHP在传参时创建副本的动作

  • 同事 = 被调用的函数

  • 复印件 = 函数内部拿到的参数(副本)

下面用代码把这个场景还原出来:

<?php
// 原始合同(原始变量)
$originalDoc = "重要信息:机密数据";

// 同事(函数)拿到复印件后的操作
function handleDocument($copy) {
    echo "同事收到复印件:{$copy}<br/>";
    // 同事在复印件上批注
    $copy .= "——同事在此批注修改";
    echo "同事修改后的复印件:{$copy}<br/>";
    // 函数结束,复印件被丢弃
}

echo "原始文件:{$originalDoc}<br/>";
// 把原始文件的复印件传给同事
handleDocument($originalDoc);
echo "同事操作后,原始文件依然是:{$originalDoc}";
?>

输出:

原始文件:重要信息:机密数据
同事收到复印件:重要信息:机密数据
同事修改后的复印件:重要信息:机密数据——同事在此批注修改
同事操作后,原始文件依然是:重要信息:机密数据

函数内部的$copy被加上了批注,但$originalDoc纹丝不动。因为它们根本就是两份独立的数据,住在内存的不同区域里。

按值传递的底层原理:内存与作用域

从更技术的角度来看,按值传递涉及两个概念:内存分配变量作用域

内存分配方面: 当调用函数时,PHP会在内存中为函数的参数开辟一块新的空间,然后把实参的复制进去。函数内部的操作都在这个新空间上进行。函数执行完毕,这块局部内存空间被回收。原始变量占用的是另一块内存区域,从始至终没被动过。

作用域方面: PHP里每个函数调用都会创建一个独立的局部作用域。函数的参数是局部作用域里的变量,它和函数外部任何同名变量是隔离的。这种隔离是语言层面的设计,不是约定俗成的编码习惯,所以用起来比较可靠。

本节课程知识要点

关于按值传递,这几个要点值得你内化成自己的认知:

  • 标量类型默认按值传递。整数、浮点数、字符串、布尔值,传给函数时走的都是按值传递。对象和数组的行为不同,后面会专门讲。

  • 函数内修改副本不影响原变量。这是按值传递区别于按引用传递的核心特征。如果你需要函数修改原变量,要么用return把新值传出来重新赋值,要么用按引用传递(参数前加&)。

  • 每次调用都是一次新的复制。同一个函数被多次调用,每次传入同一个变量,都会各自创建一份新的副本,互不干扰。

  • 函数间的数据隔离是好事。按值传递从机制上避免了函数意外篡改外部数据,减少了副作用。在多人协作或大型项目里,这种隔离能避开很多难以追踪的bug。

多个实例加深理解

实例1:数值递增,原值不变

这是很典型的按值传递演示。

<?php
function addOne($num) {
    $num++;
    echo "函数内部:\$num = {$num}<br/>";
}

$myNumber = 5;
echo "调用前:\$myNumber = {$myNumber}<br/>";
addOne($myNumber);
echo "调用后:\$myNumber = {$myNumber}<br/>";
?>

输出:

调用前:$myNumber = 5
函数内部:$num = 6
调用后:$myNumber = 5

函数内部$num确实变成了6,但那是副本加1的结果。外面的$myNumber还是5,毫发无伤。

实例2:字符串拼接,原字符串不受影响

<?php
function addSuffix($str) {
    $str .= "——进阶篇";
    echo "函数内部:\$str = {$str}<br/>";
}

$myString = "PHP函数教程";
echo "调用前:\$myString = {$myString}<br/>";
addSuffix($myString);
echo "调用后:\$myString = {$myString}<br/>";
?>

输出:

调用前:$myString = PHP函数教程
函数内部:$str = PHP函数教程——进阶篇
调用后:$myString = PHP函数教程

函数内拼接了后缀,但$myString保持不变。如果你想拿到拼接后的结果,函数应该return $str,然后调用方用变量接收返回值。

实例3:多次调用,每次都是独立的副本

<?php
function doubleValue($val) {
    $val *= 2;
    return $val;
}

$value = 3;
echo "原始值:{$value}<br/>";

$result1 = doubleValue($value);
echo "第一次调用返回:{$result1},原始值仍为:{$value}<br/>";

$result2 = doubleValue($value);
echo "第二次调用返回:{$result2},原始值仍为:{$value}<br/>";
?>

输出:

原始值:3
第一次调用返回:6,原始值仍为:3
第二次调用返回:6,原始值仍为:3

两次调用doubleValue($value),每次PHP都复制了一份$value的值3传给函数。函数内部翻倍后返回6,但$value始终是3。如果想改变$value,需要写成$value = doubleValue($value);把返回值重新赋给它。

实例4:局部作用域和全局作用域的隔离

<?php
$globalNum = 100;

function tryModify($localNum) {
    $localNum += 10;
    echo "函数内部局部变量:{$localNum}<br/>";
    // 尝试访问全局变量(不加global关键字无法直接修改)
    echo "函数内部尝试读取全局变量:{$globalNum}<br/>";
}

$testNum = 50;
tryModify($testNum);
echo "函数外部testNum:{$testNum}<br/>";
echo "函数外部globalNum:{$globalNum}<br/>";
?>

输出(注意第二行):

函数内部局部变量:60
函数内部尝试读取全局变量:
函数外部testNum:50
函数外部globalNum:100

解释几个关键点:$localNum被加10变成60,但$testNum在外面仍然是50,这是按值传递的隔离效果。另外函数内部试图直接访问$globalNum,由于没有声明global $globalNum,它在函数局部作用域里是一个未初始化的新变量,所以输出为空。但外面的$globalNum依然是100,没有被影响。

个人经验分享: 在代码号学习编程的过程中,不少初学者会把“函数内直接使用外部变量”当成理所当然的事。实际上PHP的设计理念是隔离的——函数默认就是一个独立的小房间,外面的变量进不来,里面的变量出不去。按值传递就是这种隔离理念在参数入口上的体现。我建议尊重这种隔离,把函数写成“只依赖参数、只通过返回值影响外部”的纯函数形式,这样的代码可测试性和可维护性都会更好。

按值传递在哪些场景下比较重要

理解按值传递之后,它的适用场景就很自然了:

  • 数据保护场景:你有一些敏感或关键的原始数据,需要拿给多个函数做计算,但不希望任何一个函数偷偷改掉它。按值传递提供了机制层面的保障。

  • 计算转换场景:函数接收输入值,做一系列运算或格式化,生成新结果但不改变输入本身。比如计算税费、格式化日期、生成摘要等。

  • 减少副作用:在函数式编程风格里,函数被期望不产生副作用。按值传递天然支持这种风格,让代码的行为更容易被预测。

主观建议: 在日常开发中,除非你明确需要函数“就地修改外部变量”,否则坚持按值传递配合return返回结果,是比较稳妥的做法。按引用传递虽然PHP也支持,但它打破了函数内外的边界,使用不当容易引入隐蔽的bug。保持“进参数、出返回值”的单向数据流,代码的调理会更清楚。

PHP的按值传递不是让人困惑的语法细节,而是一项有意的设计。它让函数内外的数据天然隔离,保护原变量不被意外修改。理解了副本机制、作用域隔离和适用场景,你在写函数时就能更有把握地控制数据的流动方向。

← PHP参数化函数:让函数活起来的参数传递机制 PHP按引用传递:让函数直接修改外部变量的机制 →
分享笔记 (共有 篇笔记)
验证码:
微信公众号