Я размещаю это здесь перед 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.
Патч работал отлично. Благодарю. – dkw