为何说PHP引用是个坑,要慎用(2)

别的语言的工程师,他们阅读别的语言编码规范,会看到建议在处理大的数据结构或者字串时,使用指针来减小对内存的消耗以提高运行效率。这些工程师误将此概念理解到『引用』上,然而『指针』与『引用』是完全不同的技术模型。PHP 解析器与其他语言不同,在 PHP 中,我们使用『写时复制(copy-on-write)』模型。

在『写时复制』模型里,赋值和函数传参不会触发 复制 动作,你可以理解为多个不同的变量指向同一个『变量容器』,只有当『写』动作发生时,才会触发复制动作。这意味着,即使变量看起来像是『复制』的,本质上却不是。所以当传参一个巨大的变量给某个函数时,并不会对性能造成多大影响。不过此时如果你使用引用传参的话,引用传参会关闭『写时复制』机制,这会导致接下来那些没有使用引用的变量传参会被立刻复制一份。这也不是世界末日,你也可以在所有地方都引用就行了嘛。事实并非如此:PHP 的内部机制依赖于『写时复制』模型,存在很多你无法修改的内部函数传参。
我曾在某处看到过类似下面这样的代码:

<?php function foo(&$data) { for ($i = 0; $i < strlen($data); $i++) { do_something($data{$i}); } } $string = "... looooong string with lots of data ....."; foo(string); ?>

显然,上面这段代码的第一个问题是:在循环中调用 strlen() 而不是使用已经计算好的长度。也就是说调用一次 strlen($data) 就可以了的,但是他却调用了很多次。 不同于 C 这类语言, 一般来说,PHP 的字符串都自带了长度,因此也不用进行长度的计算。所以就 strlen() 而言,这还不算太糟糕。 但现在另一个问题是,案例中的这个开发者为了节省时间,传递了一个引用作为参数以显示自己的聪明。 然而,strlen() 期望得到的是一个副本。『写时复制』不能用于引用,因此 $data 将会在 strlen() 调用时被复制,strlen() 将会做一个绝对简单的操作 —— 事实上 strlen() 本来就是 PHP 里最简单的函数之一 —— 紧接着该副本就会被直接销毁。

如果没有使用引用,也就没必要进行复制操作,代码执行也会更快。而且就算 strlen() 支持引用,你也不会因此获得更多好处。

总的来说:

除了 PHP4 的遗留问题,不要在面向对象(OO)中使用引用。

不要使用引用来提升性能。

使用引用来完成事情的第三个问题是:通过参数的引用来返回数据所导致的糟糕的 API 设计。这个问题还是因为那个开发者没有意识到『PHP 就是 PHP 而不是其他语言』所导致的。

在 PHP 中,同一个函数可以返回不同数据类型。—— 因此,你可以在函数执行成功时返回一个字符串,而在失败时返回一个布尔值 false,PHP 也允许返回复杂的结构类型,比如数组和对象。所以在需要返回很多东西的时候,可以将他们打包在一起。另外,异常也是函数返回的一种方式。

使用引用是一件不好的事情,除了引用本身不好,并且还会使性能下降这个事实外,使用引用这种方式会使得代码难以维护。像下面这段代码的函数调用:

do_something($var);

你希望 $var 发生改变吗?—— 当然不会。然而,如果 do_something() 传递的参数是引用,它就可能会改变。

这类 API 的另一个问题是:函数不能链式调用,因而你总会遇到必须使用临时变量的场景。链式调用可能会使可读性降低,但是在许多场景下,链式调用使得代码更加简洁。

关于引用的糟糕的设计决定,我个人最喜欢的一个例子是 PHP 自带的 sort() 函数。sort() 使用一个数组作为引用参数,然后通过引用返回一个排好序的数组。 像常规那样通过值返回一个排好序的数组可能还更好些。当然,这么做是由于历史的原因:sort() 比『写时复制』更早出现。『写时复制』产生于 PHP4,而 sort() 则更早,它早在 PHP 还是作为一种在 Web 上做起事来很方便的东西,而不是真正的成为自己的语言的时候就存在了。

总之: 在 PHP 中,引用是不好的。 不要使用引用。 它们只会惹事生非,另外,不要对使用引用来提升引擎抱有希望。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

您可能感兴趣的文章:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/83668a0f698da6940c6492fe41793248.html