Wednesday, August 25, 2010

foreach 赋值引用 + refcount 引发的一个问题

第一个问题,封装了 mysqli_stmt 的bind_param方法,为了实现 $stmt->execute(array(typelist, var1, var2, ...)); 的操作。
<?php
  $values[0] = $info[0];
  foreach ($info[1] as $k=>$v) {
   $fields[] = "`{$k}`=?";
   $values[] = & $v;
  }

这个操作的结果显然是错误的。于是有了个下边的测试:
<?php
$array = array(
1,2,3,4,5
);

$to0 = array();
foreach ($array as $k=>$v) {
 $to0[$k] = & $v;
}
var_dump($to0);


$to1 = array();
foreach ($array as $k=>$v) {
 $to1[$k] = & $v;
 unset($v);
}
var_dump($to1);


$to2 = array();
foreach ($array as $k=>&$v) {
 $to2[$k] = & $v;
}
var_dump($to2);

/*
Output:

G:\wwwroot\Test>php reference3.php
array(5) {
  [0]=>
  &int(5)
  [1]=>
  &int(5)
  [2]=>
  &int(5)
  [3]=>
  &int(5)
  [4]=>
  &int(5)
}
array(5) {
  [0]=>
  &int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
  [3]=>
  int(4)
  [4]=>
  int(5)
}
array(5) {
  [0]=>
  &int(1)
  [1]=>
  &int(2)
  [2]=>
  &int(3)
  [3]=>
  &int(4)
  [4]=>
  &int(5)
}

*/


第一个问题结束
---------------------------------------------------------

第二个问题,
最上边代码后来的版本是这样的:
<?php
  $values[0] = $info[0];
  foreach ($info[1] as $k=>&$v) {
   $fields[] = "`{$k}`=?";
   $values[] = & $v;
  }
  unset($info);

  $stmt = $db->prepare($query);
  $ret = $stmt->execute($values);

结果传递多于一个字段时,execute报错。
假设foreach 循环结束后,$values 内容是:
array(5) {
  [0]=>
  &int(1)
  [1]=>
  &int(2)
  [2]=>
  &int(3)
  [3]=>
  &int(4)
  [4]=>
  &int(5)
}
而在execute()的时候,却变成了
array(5) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
  [3]=>
  int(4)
  [4]=>
  &int(5)
}

除了最后一个元素外,其他的引用都消失了。。。
在几个点放上debug_zval_dump,一看便明白了。

foreach 循环结束的时候,$values 数组涉及到引用的元素,除了最后一个 refcount 为3,其余均为2. 因为对于其他的元素,$info 和 $values 两个数组的元素都对目标进行了引用,所以refcount为2,而最后一个,因为 foreach 的临时变量 $v 还对这个有引用,所以 refcount 是3.
当执行了 unset($info) 的操作后,所有的refcount均 -1,于是变成了 11112。
这个时候var_dump,自然会发现只有最后一个元素还处于引用的状态。

No comments:

Post a Comment