2016-08-09 4 views
3

Я размещаю это здесь перед php.net, чтобы лучше понять разницу в поведении, которую я вижу между PHP 5.x и 7.x.php7, ссылки и oci_bind_by_name

Следующий код работает в 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 сообщает, что возвращаемая строка содержит запись # 2270 не ожидаемый 2274.

Трассировка через код в GDB вот что я «в кусочки:

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

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

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

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

Теперь новый $ cacheRow [0] указывает на другую строку, чем oci8_statement-х bindp-> Zval поэтому следующий 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 увеличило refcount на его входящем параметре bind_var, мой исходный тест будет вести себя одинаково между PHP 5 и PHP 7. Затем я хочу быть уверенным, что это ожидаемое поведение, и мне действительно нужно использовать назначение по-ref.

ответ

0

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

+0

Я не знаю, почему @ tereško редактировал свой комментарий и изменил «ядро PHP 7» в «PHP 7». Первый правильно указал, что изменения не были в расширении OCI8. –

+0

, потому что нет такой вещи, как «core php». –

+0

«Ядро» было глаголом, а не существительным. –

Смежные вопросы