mirror of
https://github.com/php/php-src.git
synced 2024-12-12 03:15:29 +08:00
Merge branch 'PHP-8.3'
* PHP-8.3: ext/pdo: Fix various PDORow bugs
This commit is contained in:
commit
5c7a0abe55
@ -2254,74 +2254,84 @@ zend_object_iterator *pdo_stmt_iter_get(zend_class_entry *ce, zval *object, int
|
||||
/* }}} */
|
||||
|
||||
/* {{{ overloaded handlers for PDORow class (used by PDO_FETCH_LAZY) */
|
||||
static zval *row_read_column_name(pdo_stmt_t *stmt, zend_string *name, zval *rv)
|
||||
{
|
||||
/* TODO: replace this with a hash of available column names to column numbers */
|
||||
for (int colno = 0; colno < stmt->column_count; colno++) {
|
||||
if (zend_string_equals(stmt->columns[colno].name, name)) {
|
||||
fetch_value(stmt, rv, colno, NULL);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static zval *row_read_column_number(pdo_stmt_t *stmt, zend_long column, zval *rv)
|
||||
{
|
||||
if (column >= 0 && column < stmt->column_count) {
|
||||
fetch_value(stmt, rv, column, NULL);
|
||||
return rv;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static zval *row_prop_read(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv)
|
||||
{
|
||||
pdo_row_t *row = (pdo_row_t *)object;
|
||||
pdo_stmt_t *stmt = row->stmt;
|
||||
int colno = -1;
|
||||
zend_long lval;
|
||||
zval *retval;
|
||||
ZEND_ASSERT(stmt);
|
||||
|
||||
ZVAL_NULL(rv);
|
||||
if (zend_string_equals_literal(name, "queryString")) {
|
||||
return zend_std_read_property(&stmt->std, name, type, cache_slot, rv);
|
||||
} else if (is_numeric_string(ZSTR_VAL(name), ZSTR_LEN(name), &lval, NULL, 0) == IS_LONG) {
|
||||
if (lval >= 0 && lval < stmt->column_count) {
|
||||
fetch_value(stmt, rv, lval, NULL);
|
||||
}
|
||||
} else if (is_numeric_str_function(name, &lval, /* dval */ NULL) == IS_LONG) {
|
||||
retval = row_read_column_number(stmt, lval, rv);
|
||||
} else {
|
||||
/* TODO: replace this with a hash of available column names to column
|
||||
* numbers */
|
||||
for (colno = 0; colno < stmt->column_count; colno++) {
|
||||
if (zend_string_equals(stmt->columns[colno].name, name)) {
|
||||
fetch_value(stmt, rv, colno, NULL);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
retval = row_read_column_name(stmt, name, rv);
|
||||
}
|
||||
|
||||
return rv;
|
||||
if (UNEXPECTED(!retval)) {
|
||||
// TODO throw an error on master
|
||||
//if (type != BP_VAR_IS) {
|
||||
// if (is_numeric) {
|
||||
// zend_value_error("Invalid column index");
|
||||
// } else {
|
||||
// zend_throw_error(NULL, "No column named \"%s\" exists", ZSTR_VAL(name));
|
||||
// }
|
||||
//}
|
||||
//return &EG(uninitialized_zval);
|
||||
ZVAL_NULL(rv);
|
||||
return rv;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static zval *row_dim_read(zend_object *object, zval *member, int type, zval *rv)
|
||||
static zval *row_dim_read(zend_object *object, zval *offset, int type, zval *rv)
|
||||
{
|
||||
pdo_row_t *row = (pdo_row_t *)object;
|
||||
pdo_stmt_t *stmt = row->stmt;
|
||||
int colno = -1;
|
||||
zend_long lval;
|
||||
ZEND_ASSERT(stmt);
|
||||
|
||||
ZVAL_NULL(rv);
|
||||
if (Z_TYPE_P(member) == IS_LONG) {
|
||||
if (Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count) {
|
||||
fetch_value(stmt, rv, Z_LVAL_P(member), NULL);
|
||||
}
|
||||
} else if (Z_TYPE_P(member) == IS_STRING
|
||||
&& is_numeric_string(Z_STRVAL_P(member), Z_STRLEN_P(member), &lval, NULL, 0) == IS_LONG) {
|
||||
if (lval >= 0 && lval < stmt->column_count) {
|
||||
fetch_value(stmt, rv, lval, NULL);
|
||||
}
|
||||
} else {
|
||||
if (!try_convert_to_string(member)) {
|
||||
return &EG(uninitialized_zval);
|
||||
}
|
||||
|
||||
if (zend_string_equals_literal(Z_STR_P(member), "queryString")) {
|
||||
return zend_std_read_property(&stmt->std, Z_STR_P(member), type, NULL, rv);
|
||||
}
|
||||
|
||||
/* TODO: replace this with a hash of available column names to column
|
||||
* numbers */
|
||||
for (colno = 0; colno < stmt->column_count; colno++) {
|
||||
if (zend_string_equals(stmt->columns[colno].name, Z_STR_P(member))) {
|
||||
fetch_value(stmt, rv, colno, NULL);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
if (UNEXPECTED(!offset)) {
|
||||
zend_throw_error(NULL, "Cannot append to PDORow offset");
|
||||
return NULL;
|
||||
}
|
||||
if (Z_TYPE_P(offset) == IS_LONG) {
|
||||
pdo_row_t *row = (pdo_row_t *)object;
|
||||
pdo_stmt_t *stmt = row->stmt;
|
||||
ZEND_ASSERT(stmt);
|
||||
|
||||
return rv;
|
||||
ZVAL_NULL(rv);
|
||||
if (Z_LVAL_P(offset) >= 0 && Z_LVAL_P(offset) < stmt->column_count) {
|
||||
fetch_value(stmt, rv, Z_LVAL_P(offset), NULL);
|
||||
}
|
||||
return rv;
|
||||
} else {
|
||||
zend_string *member = zval_try_get_string(offset);
|
||||
if (!member) {
|
||||
return NULL;
|
||||
}
|
||||
zval *result = row_prop_read(object, member, type, NULL, rv);
|
||||
zend_string_release_ex(member, false);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
static zval *row_prop_write(zend_object *object, zend_string *name, zval *value, void **cache_slot)
|
||||
@ -2332,75 +2342,67 @@ static zval *row_prop_write(zend_object *object, zend_string *name, zval *value,
|
||||
|
||||
static void row_dim_write(zend_object *object, zval *member, zval *value)
|
||||
{
|
||||
zend_throw_error(NULL, "Cannot write to PDORow offset");
|
||||
if (!member) {
|
||||
zend_throw_error(NULL, "Cannot append to PDORow offset");
|
||||
} else {
|
||||
zend_throw_error(NULL, "Cannot write to PDORow offset");
|
||||
}
|
||||
}
|
||||
|
||||
static int row_prop_exists(zend_object *object, zend_string *name, int check_empty, void **cache_slot)
|
||||
{
|
||||
pdo_row_t *row = (pdo_row_t *)object;
|
||||
pdo_stmt_t *stmt = row->stmt;
|
||||
int colno = -1;
|
||||
zend_long lval;
|
||||
zval tmp_val;
|
||||
zval *retval = NULL;
|
||||
ZEND_ASSERT(stmt);
|
||||
|
||||
if (is_numeric_string(ZSTR_VAL(name), ZSTR_LEN(name), &lval, NULL, 0) == IS_LONG) {
|
||||
return lval >=0 && lval < stmt->column_count;
|
||||
if (is_numeric_str_function(name, &lval, /* dval */ NULL) == IS_LONG) {
|
||||
retval = row_read_column_number(stmt, lval, &tmp_val);
|
||||
} else {
|
||||
retval = row_read_column_name(stmt, name, &tmp_val);
|
||||
}
|
||||
|
||||
/* TODO: replace this with a hash of available column names to column
|
||||
* numbers */
|
||||
for (colno = 0; colno < stmt->column_count; colno++) {
|
||||
if (zend_string_equals(stmt->columns[colno].name, name)) {
|
||||
int res;
|
||||
zval val;
|
||||
|
||||
fetch_value(stmt, &val, colno, NULL);
|
||||
res = check_empty ? i_zend_is_true(&val) : Z_TYPE(val) != IS_NULL;
|
||||
zval_ptr_dtor_nogc(&val);
|
||||
|
||||
return res;
|
||||
}
|
||||
if (!retval) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
ZEND_ASSERT(retval == &tmp_val);
|
||||
int res = check_empty ? i_zend_is_true(retval) : Z_TYPE(tmp_val) != IS_NULL;
|
||||
zval_ptr_dtor_nogc(retval);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int row_dim_exists(zend_object *object, zval *member, int check_empty)
|
||||
static int row_dim_exists(zend_object *object, zval *offset, int check_empty)
|
||||
{
|
||||
pdo_row_t *row = (pdo_row_t *)object;
|
||||
pdo_stmt_t *stmt = row->stmt;
|
||||
int colno = -1;
|
||||
zend_long lval;
|
||||
ZEND_ASSERT(stmt);
|
||||
if (Z_TYPE_P(offset) == IS_LONG) {
|
||||
pdo_row_t *row = (pdo_row_t *)object;
|
||||
pdo_stmt_t *stmt = row->stmt;
|
||||
ZEND_ASSERT(stmt);
|
||||
zend_long column = Z_LVAL_P(offset);
|
||||
|
||||
if (Z_TYPE_P(member) == IS_LONG) {
|
||||
return Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count;
|
||||
} else if (Z_TYPE_P(member) == IS_STRING) {
|
||||
if (is_numeric_string(Z_STRVAL_P(member), Z_STRLEN_P(member), &lval, NULL, 0) == IS_LONG) {
|
||||
return lval >=0 && lval < stmt->column_count;
|
||||
if (!check_empty) {
|
||||
return column >= 0 && column < stmt->column_count;
|
||||
}
|
||||
|
||||
zval tmp_val;
|
||||
zval *retval = row_read_column_number(stmt, column, &tmp_val);
|
||||
if (!retval) {
|
||||
return false;
|
||||
}
|
||||
ZEND_ASSERT(retval == &tmp_val);
|
||||
int res = check_empty ? i_zend_is_true(retval) : Z_TYPE(tmp_val) != IS_NULL;
|
||||
zval_ptr_dtor_nogc(retval);
|
||||
return res;
|
||||
} else {
|
||||
if (!try_convert_to_string(member)) {
|
||||
zend_string *member = zval_try_get_string(offset);
|
||||
if (!member) {
|
||||
return 0;
|
||||
}
|
||||
int result = row_prop_exists(object, member, check_empty, NULL);
|
||||
zend_string_release_ex(member, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* TODO: replace this with a hash of available column names to column
|
||||
* numbers */
|
||||
for (colno = 0; colno < stmt->column_count; colno++) {
|
||||
if (zend_string_equals(stmt->columns[colno].name, Z_STR_P(member))) {
|
||||
int res;
|
||||
zval val;
|
||||
|
||||
fetch_value(stmt, &val, colno, NULL);
|
||||
res = check_empty ? i_zend_is_true(&val) : Z_TYPE(val) != IS_NULL;
|
||||
zval_ptr_dtor_nogc(&val);
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void row_prop_delete(zend_object *object, zend_string *offset, void **cache_slot)
|
||||
|
262
ext/pdo/tests/pdo_035.phpt
Normal file
262
ext/pdo/tests/pdo_035.phpt
Normal file
@ -0,0 +1,262 @@
|
||||
--TEST--
|
||||
PDO Common: PDORow + get_parent_class()
|
||||
--EXTENSIONS--
|
||||
pdo
|
||||
--SKIPIF--
|
||||
<?php
|
||||
$dir = getenv('REDIR_TEST_DIR');
|
||||
if (false == $dir) die('skip no driver');
|
||||
require_once $dir . 'pdo_test.inc';
|
||||
PDOTest::skip();
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
if (getenv('REDIR_TEST_DIR') === false) putenv('REDIR_TEST_DIR='.__DIR__ . '/../../pdo/tests/');
|
||||
require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc';
|
||||
$db = PDOTest::factory();
|
||||
|
||||
const TABLE_NAME = 'test_pdo_35_pdo_row';
|
||||
$db->exec('CREATE TABLE ' . TABLE_NAME .' (id int, name varchar(10))');
|
||||
$db->exec('INSERT INTO ' . TABLE_NAME .' VALUES (23, \'0\')');
|
||||
|
||||
$stmt = $db->prepare('SELECT id, name FROM ' . TABLE_NAME);
|
||||
$stmt->execute();
|
||||
$result = $stmt->fetch(PDO::FETCH_LAZY);
|
||||
|
||||
var_dump($result);
|
||||
var_dump(get_parent_class($result));
|
||||
|
||||
foreach ([0, "0", "id", "name", 1] as $offset) {
|
||||
echo 'Offset: ', var_export($offset), PHP_EOL;
|
||||
$offsetRef = &$offset;
|
||||
|
||||
echo 'Dimension:', PHP_EOL;
|
||||
echo 'Isset:', PHP_EOL;
|
||||
var_dump(isset($result[$offset]));
|
||||
var_dump(isset($result[$offsetRef]));
|
||||
echo 'Empty:', PHP_EOL;
|
||||
var_dump(empty($result[$offset]));
|
||||
var_dump(empty($result[$offsetRef]));
|
||||
echo 'Null coalesce:', PHP_EOL;
|
||||
var_dump($result[$offset] ?? "default");
|
||||
var_dump($result[$offsetRef] ?? "default");
|
||||
echo 'Read:', PHP_EOL;
|
||||
var_dump($result[$offset]);
|
||||
var_dump($result[$offsetRef]);
|
||||
echo 'Property:', PHP_EOL;
|
||||
echo 'Isset:', PHP_EOL;
|
||||
var_dump(isset($result->{$offset}));
|
||||
var_dump(isset($result->{$offsetRef}));
|
||||
echo 'Empty:', PHP_EOL;
|
||||
var_dump(empty($result->{$offset}));
|
||||
var_dump(empty($result->{$offsetRef}));
|
||||
echo 'Null coalesce:', PHP_EOL;
|
||||
var_dump($result->{$offset} ?? "default");
|
||||
var_dump($result->{$offsetRef} ?? "default");
|
||||
echo 'Read:', PHP_EOL;
|
||||
var_dump($result->{$offset});
|
||||
var_dump($result->{$offsetRef});
|
||||
}
|
||||
|
||||
echo 'Errors:', PHP_EOL;
|
||||
try {
|
||||
$result[0] = 1;
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
$result[] = 1;
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
$refResult = &$result[0];
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
$refResult = &$result[];
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
unset($result[0]);
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
$result->foo = 1;
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
unset($result->foo);
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--CLEAN--
|
||||
<?php
|
||||
if (getenv('REDIR_TEST_DIR') === false) putenv('REDIR_TEST_DIR='.__DIR__ . '/../../pdo/tests/');
|
||||
require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc';
|
||||
$db = PDOTest::factory();
|
||||
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
|
||||
|
||||
const TABLE_NAME = 'test_pdo_35_pdo_row';
|
||||
$db->exec("DROP TABLE " . TABLE_NAME);
|
||||
?>
|
||||
--EXPECTF--
|
||||
object(PDORow)#3 (3) {
|
||||
["queryString"]=>
|
||||
string(40) "SELECT id, name FROM test_pdo_35_pdo_row"
|
||||
["id"]=>
|
||||
string(2) "23"
|
||||
["name"]=>
|
||||
string(1) "0"
|
||||
}
|
||||
bool(false)
|
||||
Offset: 0
|
||||
Dimension:
|
||||
Isset:
|
||||
bool(true)
|
||||
bool(true)
|
||||
Empty:
|
||||
bool(false)
|
||||
bool(false)
|
||||
Null coalesce:
|
||||
string(2) "23"
|
||||
string(2) "23"
|
||||
Read:
|
||||
string(2) "23"
|
||||
string(2) "23"
|
||||
Property:
|
||||
Isset:
|
||||
bool(true)
|
||||
bool(true)
|
||||
Empty:
|
||||
bool(false)
|
||||
bool(false)
|
||||
Null coalesce:
|
||||
string(2) "23"
|
||||
string(2) "23"
|
||||
Read:
|
||||
string(2) "23"
|
||||
string(2) "23"
|
||||
Offset: '0'
|
||||
Dimension:
|
||||
Isset:
|
||||
bool(true)
|
||||
bool(true)
|
||||
Empty:
|
||||
bool(false)
|
||||
bool(false)
|
||||
Null coalesce:
|
||||
string(2) "23"
|
||||
string(2) "23"
|
||||
Read:
|
||||
string(2) "23"
|
||||
string(2) "23"
|
||||
Property:
|
||||
Isset:
|
||||
bool(true)
|
||||
bool(true)
|
||||
Empty:
|
||||
bool(false)
|
||||
bool(false)
|
||||
Null coalesce:
|
||||
string(2) "23"
|
||||
string(2) "23"
|
||||
Read:
|
||||
string(2) "23"
|
||||
string(2) "23"
|
||||
Offset: 'id'
|
||||
Dimension:
|
||||
Isset:
|
||||
bool(true)
|
||||
bool(true)
|
||||
Empty:
|
||||
bool(false)
|
||||
bool(false)
|
||||
Null coalesce:
|
||||
string(2) "23"
|
||||
string(2) "23"
|
||||
Read:
|
||||
string(2) "23"
|
||||
string(2) "23"
|
||||
Property:
|
||||
Isset:
|
||||
bool(true)
|
||||
bool(true)
|
||||
Empty:
|
||||
bool(false)
|
||||
bool(false)
|
||||
Null coalesce:
|
||||
string(2) "23"
|
||||
string(2) "23"
|
||||
Read:
|
||||
string(2) "23"
|
||||
string(2) "23"
|
||||
Offset: 'name'
|
||||
Dimension:
|
||||
Isset:
|
||||
bool(true)
|
||||
bool(true)
|
||||
Empty:
|
||||
bool(true)
|
||||
bool(true)
|
||||
Null coalesce:
|
||||
string(1) "0"
|
||||
string(1) "0"
|
||||
Read:
|
||||
string(1) "0"
|
||||
string(1) "0"
|
||||
Property:
|
||||
Isset:
|
||||
bool(true)
|
||||
bool(true)
|
||||
Empty:
|
||||
bool(true)
|
||||
bool(true)
|
||||
Null coalesce:
|
||||
string(1) "0"
|
||||
string(1) "0"
|
||||
Read:
|
||||
string(1) "0"
|
||||
string(1) "0"
|
||||
Offset: 1
|
||||
Dimension:
|
||||
Isset:
|
||||
bool(true)
|
||||
bool(true)
|
||||
Empty:
|
||||
bool(true)
|
||||
bool(true)
|
||||
Null coalesce:
|
||||
string(1) "0"
|
||||
string(1) "0"
|
||||
Read:
|
||||
string(1) "0"
|
||||
string(1) "0"
|
||||
Property:
|
||||
Isset:
|
||||
bool(true)
|
||||
bool(true)
|
||||
Empty:
|
||||
bool(true)
|
||||
bool(true)
|
||||
Null coalesce:
|
||||
string(1) "0"
|
||||
string(1) "0"
|
||||
Read:
|
||||
string(1) "0"
|
||||
string(1) "0"
|
||||
Errors:
|
||||
Cannot write to PDORow offset
|
||||
Cannot append to PDORow offset
|
||||
|
||||
Notice: Indirect modification of overloaded element of PDORow has no effect in %s on line %d
|
||||
Cannot append to PDORow offset
|
||||
Cannot unset PDORow offset
|
||||
Cannot write to PDORow property
|
||||
Cannot unset PDORow property
|
@ -1,46 +0,0 @@
|
||||
--TEST--
|
||||
PDO Common: PDORow + get_parent_class()
|
||||
--EXTENSIONS--
|
||||
pdo_sqlite
|
||||
--FILE--
|
||||
<?php
|
||||
$db = new PDO('sqlite::memory:');
|
||||
$db->exec('CREATE TABLE test_pdo_035 (id int)');
|
||||
$db->exec('INSERT INTO test_pdo_035 VALUES (23)');
|
||||
|
||||
$stmt = $db->prepare('SELECT id FROM test_pdo_035');
|
||||
$stmt->execute();
|
||||
$result = $stmt->fetch(PDO::FETCH_LAZY);
|
||||
|
||||
echo get_class($result), "\n";
|
||||
var_dump(get_parent_class($result));
|
||||
|
||||
try {
|
||||
$result->foo = 1;
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
$result[0] = 1;
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
unset($result->foo);
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
unset($result[0]);
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
PDORow
|
||||
bool(false)
|
||||
Cannot write to PDORow property
|
||||
Cannot write to PDORow offset
|
||||
Cannot unset PDORow property
|
||||
Cannot unset PDORow offset
|
Loading…
Reference in New Issue
Block a user