php7, ссылки и oci привязываются по имени
Я публикую это здесь раньше 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 сообщает, что возвращенная строка содержит ЗАПИСЬ № 2270 не соответствует ожидаемому 2274.
Отслеживание кода в gdb вот что я собрал воедино:
Параметр &$ переменной 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. С другой стороны, я готов убедиться, что это ожидаемое поведение, и мне действительно нужно использовать присваивание по ссылке.
2 answers
Попробуйте патч PHP OCI8, только что загруженный в https://bugs.php.net/patch-display.php?bug_id=71148&patch=oci8-php7-bind&revision=latest
Смотрите комментарии в https://bugs.php.net/bug.php?id=71148 Были изменения в подсчете ссылок PHP 7 (для производительности PHP), которые повлияли на OCI8. Мы экспериментировали с увеличением количества ссылок внутри расширения OCI8, но это нарушило другие вещи.