php7,引用和oci_bind_by_name

时间:2016-08-09 22:42:38

标签: php oracle php-7 oci oci8

我在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的& $变量参数最终被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)的赋值是by-reference来解决这个问题。这避免了这个问题,因为$ 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.然后,我愿意确信这是预期的行为,我确实需要使用ref-by-ref。

2 个答案:

答案 0 :(得分:1)

答案 1 :(得分:0)

查看https://bugs.php.net/bug.php?id=71148中的评论 有影响OCI8的PHP 7引用计数更改(对于PHP性能)。我们尝试在OCI8扩展中碰撞引用计数,但这打破了其他事情。