php7, ссылки и oci_bind_by_name

Я публикую это здесь перед 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, вот что я собрал:

  • Параметр &$variable oci_bind_by_name в конечном итоге разыменовывается с помощью z/ и продолжает существовать как простая строка zval в bindp->zval (oci8_statement.c:1250). Это нормально, так как другие более простые тесты работают, пока все zvals указывают на одну и ту же строку.

  • При возврате из oci_bind_by_name $cacheRow[0] теперь является ссылкой, как и ожидалось.

  • При следующем $cacheRow[0] = '2274', когда копия $cacheRow создается во время присваивания, $cacheRow[0] в результирующей копии больше не является ссылкой, а просто zval, указывающим на исходную строку.

  • После копирования, когда выполняется назначение в новый $cacheRow[0], он просто меняет свой указатель str.

Теперь новый $cacheRow[0] указывает на другую строку, отличную от bindp->zval oci8_statement, поэтому следующий oci_execute извлечет старое связанное значение.

Я могу обойти это, убедившись, что присваивания, включающие $cache[0] (как входящие, так и исходящие), выполняются по ссылке. Это позволяет избежать проблемы, поскольку массив $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. Опять же, я хочу быть уверен, что это ожидаемое поведение, и мне действительно нужно использовать назначение по ссылке.


person dkw    schedule 09.08.2016    source источник


Ответы (2)


Попробуйте патч PHP OCI8, только что загруженный на https://bugs.php.net/patch-display.php?bug_id=71148&patch=oci8-php7-bind&revision=latest

person Christopher Jones    schedule 13.10.2016

См. комментарии в https://bugs.php.net/bug.php?id=71148 В PHP 7 произошли изменения в подсчете ссылок (для производительности PHP), которые повлияли на OCI8. Мы экспериментировали с увеличением количества ссылок внутри расширения OCI8, но это сломало другие вещи.

person Christopher Jones    schedule 17.08.2016
comment
Я не уверен, почему @tereško отредактировал мой комментарий и изменил ядро ​​PHP 7 на PHP 7. Первый правильно указал, что изменения не были в расширении OCI8. - person Christopher Jones; 13.10.2016
comment
потому что нет такой вещи, как ядро ​​php. - person tereško; 13.10.2016
comment
core был глаголом, а не существительным. - person Christopher Jones; 17.10.2016