Ric*_*shi 11 php arrays mysqli reference object
我正在围绕mysqli创建一个包装函数,这样我的应用程序就不必过于复杂了数据库处理代码.部分原因是使用mysqli :: bind_param()参数化SQL调用的一些代码.你可能知道bind_param()需要引用.因为它是一个半通用的包装器,所以我最终打了这个电话:
call_user_func_array(array($stmt, 'bind_param'), $this->bindArgs);
Run Code Online (Sandbox Code Playgroud)
我收到一条错误消息:
Parameter 2 to mysqli_stmt::bind_param() expected to be a reference, value given
Run Code Online (Sandbox Code Playgroud)
上面的讨论是为了那些会说"你的例子中根本不需要引用"的人.
我的"真实"代码比任何人想读的都复杂一点,所以我把导致这个错误的代码煮成了以下(希望)说明性示例:
class myclass {
private $myarray = array();
function setArray($vals) {
foreach ($vals as $key => &$value) {
$this->myarray[] =& $value;
}
$this->dumpArray();
}
function dumpArray() {
var_dump($this->myarray);
}
}
function myfunc($vals) {
$obj = new myclass;
$obj->setArray($vals);
$obj->dumpArray();
}
myfunc(array('key1' => 'val1',
'key2' => 'val2'));
Run Code Online (Sandbox Code Playgroud)
问题似乎是,在myfunc()中,在调用setArray()和调用dumpArray()之间,$ obj-> myarray中的所有元素都不再是引用而是变为值.通过查看输出可以很容易地看出这一点:
array(2) {
[0]=>
&string(4) "val1"
[1]=>
&string(4) "val2"
}
array(2) {
[0]=>
string(4) "val1"
[1]=>
string(4) "val2"
}
Run Code Online (Sandbox Code Playgroud)
请注意,此处输出的前半部分中的数组处于"正确"状态.如果这样做是有意义的,那么我可以在那时调用bind_param(),它会起作用.不幸的是,在输出的后半部分出现了问题.注意数组值类型上缺少"&".
我的推荐怎么了?我怎样才能防止这种情况发生?当我真的不是语言专家时,我讨厌称"PHP bug",但这可能是一个吗?这对我来说似乎很奇怪.我目前正在使用PHP 5.3.8进行测试.
编辑:
正如不止一个人指出的那样,修复方法是更改setArray()以通过引用接受其参数:
function setArray(&$vals) {
Run Code Online (Sandbox Code Playgroud)
我正在添加此注释以记录为什么这似乎有效.
PHP,特别是mysqli,似乎对"引用"的概念略显奇怪.观察这个例子:
$a = "foo";
$b = array(&$a);
$c = array(&$a);
var_dump($b);
var_dump($c);
Run Code Online (Sandbox Code Playgroud)
首先,我确定你想知道为什么我使用数组而不是标量变量 - 这是因为var_dump()没有显示标量是否是引用的任何指示,但它对数组成员有用.
无论如何,在这一点上,$ b [0]和$ c [0]都是对$ a的引用.到现在为止还挺好.现在我们把第一把扳手投入工作:
unset($a);
var_dump($b);
var_dump($c);
Run Code Online (Sandbox Code Playgroud)
$ b [0]和$ c [0]仍然是对同一事物的引用.如果我们换一个,两者都会改变.但是他们提到了什么?内存中有一些未命名的位置.当然,垃圾收集确保我们的数据是安全的,并且将保持不变,直到我们停止引用它为止.
对于我们的下一个技巧,我们这样做:
unset($b);
var_dump($c);
Run Code Online (Sandbox Code Playgroud)
现在$ c [0]是我们数据的唯一剩余参考.而且,哇!奇迹般地,它不再是"参考".不是通过var_dump()的度量,而是通过mysqli :: bind_param()的度量.
在PHP手册说,有一个独立的标志,对每一块数据的"is_ref".但是,此测试似乎表明'is_ref'实际上相当于'(refcount> 1)'
为了好玩,您可以修改此玩具示例,如下所示:
$a = array("foo");
$b = array(&$a[0]);
$c = array(&$a[0]);
var_dump($a);
var_dump($b);
var_dump($c);
Run Code Online (Sandbox Code Playgroud)
请注意,所有三个数组都在其成员上有引用标记,这支持了'is_ref'在功能上等同于'(refcount> 1)'的想法.
超出我的原因,mysqli :: bind_param()首先关心这种区别(或者也许是call_user_func_array()......无论如何),但看起来我们"真正"需要确保的是参考对于call_user_func_array()调用中$ this-> bindArgs的每个成员,count至少为2(请参阅帖子/问题的开头).最简单的方法(在这种情况下)是使setArray()传递引用.
编辑:
为了获得额外的乐趣和游戏,我修改了原始程序(此处未显示)以使其等效于setArray()传值,并创建一个无偿的额外数组bindArgsCopy,其中包含与bindArgs完全相同的内容.这意味着,是的,两个数组都包含对"临时"数据的引用,这些数据在第二次调用时被解除分配.正如上面的分析所预测的,这是有效的.这表明上面的分析不是var_dump()内部工作的一个工件(至少对我来说是一种解脱),它也证明了重要的引用计数,而不是原始的"临时性".数据存储.
所以.我做了以下断言:在PHP中,出于call_user_func_array()(可能更多)的目的,说数据项是"引用"与说项目的引用计数大于或等于2是一回事(忽略PHP对等值标量的内部内存优化)
Administrivia注意:我很乐意给马里奥提供答案,因为他是第一个提出正确答案的人,但是因为他在评论中写了这个,而不是一个真正的答案,我不能这样做: - (
传递数组作为参考:
function setArray(&$vals) {
foreach ($vals as $key => &$value) {
$this->myarray[] =& $value;
}
$this->dumpArray();
}
Run Code Online (Sandbox Code Playgroud)
我的猜测(在一些细节中可能是错误的,但希望大多数情况下是正确的)至于为什么这会使你的代码按预期工作,当你作为一个值传递时,一切都很酷dumpArray(),setArray()因为参考调用内部到$vals里面的数组setArray()仍然存在.但是当控制返回时,myfunc()那个临时变量就像所有对它的引用一样消失了.因此,在为内存释放内存之前,PHP会尽可能地将数组更改为字符串值而不是引用.但是,如果你将它作为引用myfunc()从那时传递,则setArray()使用对控件返回时生效的数组的引用myfunc().
| 归档时间: |
|
| 查看次数: |
3105 次 |
| 最近记录: |