我正在mysqli周围创建一个包装函数,以便我的应用程序不需要过度复杂的数据库处理代码。其中的一部分是使用mysqli :: bind_param()参数化SQL调用的一些代码。你可能知道,bind_param()需要引用。因为它是一个半通用的包装,我最终作出这一呼吁:我的PHP引用数组“神奇地”成为一组值......为什么?
call_user_func_array(array($stmt, 'bind_param'), $this->bindArgs);
,我得到一个错误信息:
Parameter 2 to mysqli_stmt::bind_param() expected to be a reference, value given
上面的讨论是福斯代尔那些谁也说:“你不要”在你的例子中完全不需要参考“。
我的“真实”的代码是不是有人想读一个比较复杂一点,所以我煮导致这一错误代码为以下(希望)说明性的例子:
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'));
问题似乎是,在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"
}
注意,阵列是在这里输出的上半年“正确”的状态。如果这样做是有意义的,那么我可以在那时做我的bind_param()调用,它会起作用。不幸的是,在输出的后半部分中断了。请注意数组值类型上缺少“&”。
我的参考文献发生了什么?我怎样才能防止这种情况发生?当我真的不是语言专家时,我不喜欢称之为“PHP bug”,但是这可能是一个吗?这对我来说确实很奇怪。目前我正在使用PHP 5.3.8进行测试。
编辑:
由于超过一个人所指出的,解决方法是改变setArray()参考接受它的参数:
function setArray(&$vals) {
我加入这个音符文件为什么这似乎工作。
PHP一般而言,尤其是mysqli,似乎对“参考”是什么有点奇怪的概念。注意这个例子:
$a = "foo";
$b = array(&$a);
$c = array(&$a);
var_dump($b);
var_dump($c);
首先,我敢肯定你想知道为什么我使用数组而不是标量变量 - 这是因为的var_dump()不显示是否标量的任何指示是一个参考,但它对阵列成员。
无论如何,在这一点上,$ b [0]和$ c [0]都是对$ a的引用。到现在为止还挺好。现在我们把我们的第一个扳手到作品中:
unset($a);
var_dump($b);
var_dump($c);
$ B [0],$ C [0]都仍然是同样的事情引用。如果我们改变一个,两者都会改变。但是他们提到了什么?记忆中的一些未命名的位置。当然,垃圾收集可以确保我们的数据是安全的,并且会保持这种状态,直到我们停止引用它为止。
对于我们的下一个把戏,我们这样做:
unset($b);
var_dump($c);
现在$ C [0]仅存参考我们的数据。而且,哇!神奇的是,它不再是一个“参考”。不是通过var_dump()的度量,而是通过mysqli :: bind_param()的度量。
PHP manual表示在每条数据上都有一个单独的标志'is_ref'。然而,这个测试似乎表明,“is_ref”其实就相当于“(引用计数> 1)”
为了好玩,你可以按如下修改这个玩具例子:
$a = array("foo");
$b = array(&$a[0]);
$c = array(&$a[0]);
var_dump($a);
var_dump($b);
var_dump($c);
注意所有三个数组在其成员上有参考标记,这支持'is_ref'在功能上等同于'(refcount> 1)'的想法。
为什么mysqli :: bind_param()首先会关心这种区别(或者它是call_user_func_array()......),但这似乎是我们“真正”需要确保的东西对于我们的call_user_func_array()调用中的$ this-> bindArgs的每个成员,引用计数至少为(请参阅post/question的开头部分)。最简单的方法(在这种情况下)是通过setArray()传递引用。
编辑:
对于额外的乐趣和游戏,我修改了原来的程序(这里没有显示)为它的等效离开setArray()传递的价值,并创建一个免费的额外阵列,bindArgsCopy,包含与bindArgs完全相同的东西。这意味着,是的,两个数组都包含对在第二次调用时解除分配的“临时”数据的引用。正如上面的分析预测的那样,这工作。这表明上面的分析不是var_dump()的内部工作(至少对我来说是一种解脱)的人工产物,它还表明它是引用计数的重要内容,而不是原始的“临时性”数据存储。
所以。我做了以下断言:在PHP中,为了call_user_func_array()(可能更多)的目的,说数据项是“参考”与说项目的引用计数大于或等于2相同(忽略PHP的内存优化同等数值标量)
文案注:我很想给马里奥网站信用的答案,因为他是第一个提出正确的答案,但因为他在评论中写道它,而不是一个实际的答案,我不能这样做:-(
止跌” t'setArray()'方法还需要获取数组作为参考参数?否则,您只是创建对临时数组的引用。 – mario
@mario:先生,您绝对正确,因为这确实解决了我的问题。这就引发了这个问题......为什么这样能解决问题?我希望对临时数组的引用仍然是一个引用,只是对由于引用本身而仅存在于内存中的某些内容的引用。我想我应该阅读关于PHP内存管理的内容,如果我能找到这样的文档。 –
是的,把&vals放在setArray函数中 – malletjo