Fixed GH-13167 Fixed the behavior of bindValue and bindParam. (#13384)

Fixed to generate an error when a non-scalar value is passed in
`PDO_PARAM_EVT_EXEC_PRE` of `pdo_mysql_stmt_param_hook` unless
it is a `Stringable` object.
This commit is contained in:
Saki Takamachi 2024-02-18 20:10:21 +09:00 committed by GitHub
parent 10957e498c
commit 68f10504de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 101 additions and 0 deletions

View file

@ -27,6 +27,7 @@
#include "pdo/php_pdo_driver.h"
#include "php_pdo_mysql.h"
#include "php_pdo_mysql_int.h"
#include "zend_interfaces.h"
#ifdef PDO_USE_MYSQLND
# define pdo_mysql_stmt_execute_prepared(stmt) pdo_mysql_stmt_execute_prepared_mysqlnd(stmt)
@ -490,7 +491,14 @@ static int pdo_mysql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_da
case IS_DOUBLE:
mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, parameter, MYSQL_TYPE_DOUBLE);
break;
case IS_OBJECT:
if(zend_class_implements_interface(Z_OBJCE_P(parameter), zend_ce_stringable)) {
mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, parameter, MYSQL_TYPE_VAR_STRING);
break;
}
ZEND_FALLTHROUGH;
default:
pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a scalar value or null");
PDO_DBG_RETURN(0);
}
@ -530,7 +538,19 @@ static int pdo_mysql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_da
b->buffer = &Z_DVAL_P(parameter);
PDO_DBG_RETURN(1);
case IS_OBJECT:
if(zend_class_implements_interface(Z_OBJCE_P(parameter), zend_ce_stringable)) {
convert_to_string(parameter);
b->buffer_type = MYSQL_TYPE_STRING;
b->buffer = Z_STRVAL_P(parameter);
b->buffer_length = Z_STRLEN_P(parameter);
*b->length = Z_STRLEN_P(parameter);
PDO_DBG_RETURN(1);
}
ZEND_FALLTHROUGH;
default:
pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a scalar value or null");
PDO_DBG_RETURN(0);
}
#endif /* PDO_USE_MYSQLND */

View file

@ -0,0 +1,81 @@
--TEST--
GH-13384 Fixed GH-13167 Fixed the behavior when an inappropriate value was passed to `bindValue` and `bindParam`.
--EXTENSIONS--
pdo_mysql
--SKIPIF--
<?php
require_once __DIR__ . '/inc/mysql_pdo_test.inc';
MySQLPDOTest::skip();
?>
--FILE--
<?php
require_once __DIR__ . '/inc/mysql_pdo_test.inc';
$db = MySQLPDOTest::factory();
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$stringableObject = new class () implements Stringable {
public function __toString(): string
{
return '555';
}
};
echo "Stringable object, value set after bindParam:\n";
try {
$stmt = $db->prepare('SELECT ?');
$param = 'foo';
$stmt->bindParam(1, $param, PDO::PARAM_STR);
$param = $stringableObject;
$stmt->execute();
var_dump(is_object($param), $param === $stringableObject);
echo "\n";
} catch (Throwable $e) {
echo $e->getMessage()."\n\n";
}
echo "Stringable object, bindValue:\n";
$stmt = $db->prepare('SELECT (?)');
$stmt->bindValue(1, $stringableObject, PDO::PARAM_INT);
$stmt->execute();
var_dump($stmt->fetchAll(PDO::FETCH_ASSOC));
echo "\n";
echo "Normal object, bindValue:\n";
try {
$stmt = $db->prepare('SELECT (?)');
$stmt->bindValue(1, new stdClass(), PDO::PARAM_INT);
$stmt->execute();
} catch (Throwable $e) {
echo $e->getMessage()."\n\n";
}
echo "Array, bindParam:\n";
try {
$stmt = $db->prepare('SELECT (?)');
$param = ['aaa'];
$stmt->bindParam(1, $param, PDO::PARAM_INT);
$stmt->execute();
} catch (Throwable $e) {
echo $e->getMessage();
}
?>
--EXPECT--
Stringable object, value set after bindParam:
bool(true)
bool(true)
Stringable object, bindValue:
array(1) {
[0]=>
array(1) {
["?"]=>
string(3) "555"
}
}
Normal object, bindValue:
SQLSTATE[HY105]: Invalid parameter type: Expected a scalar value or null
Array, bindParam:
SQLSTATE[HY105]: Invalid parameter type: Expected a scalar value or null