php7、参考文献和 oci_bind_by_name
php7, references and oci_bind_by_name
I'm posting this here before php.net to maybe get a better understanding of the difference in behavior that I'm seeing between PHP 5.x and 7.x.
The following code works in PHP 5.x but not 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() just oci_execute and oci_fetch_array. checkResult() just verifies that the row returned contains the value in the second parameter.
In PHP 7 (7.0.8 and 7.0.10 anyway) the second call to checkResult reports that the row returned contains the RECORD# 2270 not the expected 2274.
Tracing through the code in gdb here's what I've pieced together:
The &$variable parameter of oci_bind_by_name ends up be dereferenced by z/ and lives on as a simple string zval in bindp->zval (oci8_statement.c:1250). This is ok, as other simpler tests work as long as all the zvals are pointing at the same string.
On return from oci_bind_by_name $cacheRow[0] is now a reference as expected.
On the next $cacheRow[0] = '2274' when the copy of $cacheRow is made during the assignment, $cacheRow[0] in the resulting copy is no longer a reference, just a zval pointing to the original string.
After the copy when the assignment into the new $cacheRow[0] is made it just changes its str pointer.
Now the new $cacheRow[0] is pointing to a different string than oci8_statement's bindp->zval so the next oci_execute will pull the old bound value.
I can work around this by ensuring that the assignments involving $cache[0] (both in-to and out-of) are by-reference. This avoids the issue because the $cacheRow array is never separated.
I can also reproduce this in pure PHP code
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';
}
Since I can get different behaviors in the pure PHP tests depending on if I keep a reference to the passed parameter to bbn this makes me think that if oci_bind_by_name increased the refcount on its incoming bind_var parameter my original test would behave identically between PHP 5 and PHP 7. Then again, I'm willing to be convinced that this is expected behavior and I really do need to use assignment-by-ref.
查看 https://bugs.php.net/bug.php?id=71148 中的评论
有 PHP 7 个引用计数更改(针对 PHP 性能)影响了 OCI8。我们尝试在 OCI8 扩展中增加引用计数,但这破坏了其他东西。
试试刚刚上传到 https://bugs.php.net/patch-display.php?bug_id=71148&patch=oci8-php7-bind&revision=latest
的 PHP OCI8 补丁
I'm posting this here before php.net to maybe get a better understanding of the difference in behavior that I'm seeing between PHP 5.x and 7.x.
The following code works in PHP 5.x but not 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() just oci_execute and oci_fetch_array. checkResult() just verifies that the row returned contains the value in the second parameter.
In PHP 7 (7.0.8 and 7.0.10 anyway) the second call to checkResult reports that the row returned contains the RECORD# 2270 not the expected 2274.
Tracing through the code in gdb here's what I've pieced together:
The &$variable parameter of oci_bind_by_name ends up be dereferenced by z/ and lives on as a simple string zval in bindp->zval (oci8_statement.c:1250). This is ok, as other simpler tests work as long as all the zvals are pointing at the same string.
On return from oci_bind_by_name $cacheRow[0] is now a reference as expected.
On the next $cacheRow[0] = '2274' when the copy of $cacheRow is made during the assignment, $cacheRow[0] in the resulting copy is no longer a reference, just a zval pointing to the original string.
After the copy when the assignment into the new $cacheRow[0] is made it just changes its str pointer.
Now the new $cacheRow[0] is pointing to a different string than oci8_statement's bindp->zval so the next oci_execute will pull the old bound value.
I can work around this by ensuring that the assignments involving $cache[0] (both in-to and out-of) are by-reference. This avoids the issue because the $cacheRow array is never separated.
I can also reproduce this in pure PHP code
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';
}
Since I can get different behaviors in the pure PHP tests depending on if I keep a reference to the passed parameter to bbn this makes me think that if oci_bind_by_name increased the refcount on its incoming bind_var parameter my original test would behave identically between PHP 5 and PHP 7. Then again, I'm willing to be convinced that this is expected behavior and I really do need to use assignment-by-ref.
查看 https://bugs.php.net/bug.php?id=71148 中的评论 有 PHP 7 个引用计数更改(针对 PHP 性能)影响了 OCI8。我们尝试在 OCI8 扩展中增加引用计数,但这破坏了其他东西。
试试刚刚上传到 https://bugs.php.net/patch-display.php?bug_id=71148&patch=oci8-php7-bind&revision=latest
的 PHP OCI8 补丁