【发布时间】:2016-12-16 03:21:47
【问题描述】:
我在 php.net 之前在这里发布此内容,以便更好地了解我在 PHP 5.x 和 7.x 之间看到的行为差异。
以下代码适用于 PHP 5.x 但不适用于 7.x
$conn = oci_connect('****', '****', '****', '****');
$stmt = oci_parse($conn, 'select record# from company where record#=:1');
$cache = [];
$cacheRow[0] = '2270';
oci_bind_by_name($stmt, ":1", $cacheRow[0], 2*strlen($cacheRow[0])+32);
$cache[0] = $cacheRow;
$result = runStmt($stmt);
checkResult($result, '2270');
$cacheRow = $cache[0];
$cacheRow[0] = '2274';
$cache[0] = $cacheRow;
$result = runStmt($stmt);
checkResult($result, '2274');
runStmt() 只是 oci_execute 和 oci_fetch_array。 checkResult() 只是验证返回的行是否包含第二个参数中的值。
在 PHP 7(无论如何是 7.0.8 和 7.0.10)中,第二次调用 checkResult 报告返回的行包含 RECORD# 2270 而不是预期的 2274。
跟踪 gdb 中的代码,这是我拼凑起来的:
oci_bind_by_name 的 &$variable 参数最终被 z/ 取消引用,并在 bindp->zval (oci8_statement.c:1250) 中作为简单字符串 zval 继续存在。这没关系,因为只要所有 zval 都指向同一个字符串,其他更简单的测试就可以工作。
从 oci_bind_by_name 返回时,$cacheRow[0] 现在是预期的引用。
在下一个 $cacheRow[0] = '2274' 时,当 $cacheRow 在赋值期间复制时,结果副本中的 $cacheRow[0] 不再是引用,只是一个 zval 指向原始字符串。
在复制之后,当分配到新的 $cacheRow[0] 时,它只是改变了它的 str 指针。
现在新的 $cacheRow[0] 指向的字符串与 oci8_statement 的 bindp->zval 不同,因此下一个 oci_execute 将提取旧的绑定值。
我可以通过确保涉及 $cache[0](in-to 和 out-of)的分配是按引用来解决这个问题。这避免了这个问题,因为 $cacheRow 数组从不分离。
我也可以用纯 PHP 代码重现这个
function bbn1(&$var)
{
}
function test1()
{
$cache = [];
$cacheRow[0] = '2270';
bbn1($cacheRow[0]);
$x = $cacheRow[0];
$cache[0] = $cacheRow;
$cacheRow = $cache[0];
// Copy-on-write of $cacheRow does not preserve the reference in
// $cacheRow[0] because $cacheRow[0]'s refcount == 1
// zend_array_dup_element in zend_hash.c
$cacheRow[0] = '2274';
}
function bbn2(&$var)
{
static $cache = [];
$cache[] =& $var;
}
function test2()
{
$cache = [];
$cacheRow[0] = '2270';
bbn2($cacheRow[0]);
$x = $cacheRow[0];
$cache[0] = $cacheRow;
$cacheRow = $cache[0];
// Copy-on-write of $cacheRow preserves the reference in
// $cacheRow[0] because $cacheRow[0]'s refcount != 1
// zend_array_dup_element in zend_hash.c
$cacheRow[0] = '2274';
}
由于我可以在纯 PHP 测试中获得不同的行为,具体取决于我是否保留对传递给 bbn 的参数的引用,这让我认为,如果 oci_bind_by_name 增加其传入的 bind_var 参数的引用计数,我的原始测试将在 PHP 之间表现相同5 和 PHP 7。再说一次,我愿意相信这是预期的行为,我确实需要使用 assignment-by-ref。
【问题讨论】:
标签: php oracle php-7 oracle-call-interface oci8