【问题标题】:php7, references and oci_bind_by_namephp7,参考和 oci_bind_by_name
【发布时间】: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


    【解决方案1】:
    【解决方案2】:

    https://bugs.php.net/bug.php?id=71148中的cmets 有影响 OCI8 的 PHP 7 引用计数更改(针对 PHP 性能)。我们尝试在 OCI8 扩展中增加引用计数,但这破坏了其他东西。

    【讨论】:

    • 我不确定为什么@tereško 编辑了我的评论并将“core PHP 7”更改为“PHP 7”。前者正确地表明更改不在 OCI8 扩展中。
    • 因为没有“core php”之类的东西。
    • “core”是动词,不是名词。
    猜你喜欢
    • 1970-01-01
    • 2011-10-27
    • 2010-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多