Skip to content

Commit 68f1050

Browse files
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.
1 parent 10957e4 commit 68f1050

File tree

2 files changed

+101
-0
lines changed

2 files changed

+101
-0
lines changed

ext/pdo_mysql/mysql_statement.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "pdo/php_pdo_driver.h"
2828
#include "php_pdo_mysql.h"
2929
#include "php_pdo_mysql_int.h"
30+
#include "zend_interfaces.h"
3031

3132
#ifdef PDO_USE_MYSQLND
3233
# 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
490491
case IS_DOUBLE:
491492
mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, parameter, MYSQL_TYPE_DOUBLE);
492493
break;
494+
case IS_OBJECT:
495+
if(zend_class_implements_interface(Z_OBJCE_P(parameter), zend_ce_stringable)) {
496+
mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, parameter, MYSQL_TYPE_VAR_STRING);
497+
break;
498+
}
499+
ZEND_FALLTHROUGH;
493500
default:
501+
pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a scalar value or null");
494502
PDO_DBG_RETURN(0);
495503
}
496504

@@ -530,7 +538,19 @@ static int pdo_mysql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_da
530538
b->buffer = &Z_DVAL_P(parameter);
531539
PDO_DBG_RETURN(1);
532540

541+
case IS_OBJECT:
542+
if(zend_class_implements_interface(Z_OBJCE_P(parameter), zend_ce_stringable)) {
543+
convert_to_string(parameter);
544+
b->buffer_type = MYSQL_TYPE_STRING;
545+
b->buffer = Z_STRVAL_P(parameter);
546+
b->buffer_length = Z_STRLEN_P(parameter);
547+
*b->length = Z_STRLEN_P(parameter);
548+
PDO_DBG_RETURN(1);
549+
}
550+
ZEND_FALLTHROUGH;
551+
533552
default:
553+
pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a scalar value or null");
534554
PDO_DBG_RETURN(0);
535555
}
536556
#endif /* PDO_USE_MYSQLND */

ext/pdo_mysql/tests/gh13384.phpt

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
--TEST--
2+
GH-13384 Fixed GH-13167 Fixed the behavior when an inappropriate value was passed to `bindValue` and `bindParam`.
3+
--EXTENSIONS--
4+
pdo_mysql
5+
--SKIPIF--
6+
<?php
7+
require_once __DIR__ . '/inc/mysql_pdo_test.inc';
8+
MySQLPDOTest::skip();
9+
?>
10+
--FILE--
11+
<?php
12+
require_once __DIR__ . '/inc/mysql_pdo_test.inc';
13+
$db = MySQLPDOTest::factory();
14+
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
15+
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
16+
17+
$stringableObject = new class () implements Stringable {
18+
public function __toString(): string
19+
{
20+
return '555';
21+
}
22+
};
23+
24+
echo "Stringable object, value set after bindParam:\n";
25+
try {
26+
$stmt = $db->prepare('SELECT ?');
27+
$param = 'foo';
28+
$stmt->bindParam(1, $param, PDO::PARAM_STR);
29+
$param = $stringableObject;
30+
$stmt->execute();
31+
var_dump(is_object($param), $param === $stringableObject);
32+
echo "\n";
33+
} catch (Throwable $e) {
34+
echo $e->getMessage()."\n\n";
35+
}
36+
37+
echo "Stringable object, bindValue:\n";
38+
$stmt = $db->prepare('SELECT (?)');
39+
$stmt->bindValue(1, $stringableObject, PDO::PARAM_INT);
40+
$stmt->execute();
41+
var_dump($stmt->fetchAll(PDO::FETCH_ASSOC));
42+
echo "\n";
43+
44+
echo "Normal object, bindValue:\n";
45+
try {
46+
$stmt = $db->prepare('SELECT (?)');
47+
$stmt->bindValue(1, new stdClass(), PDO::PARAM_INT);
48+
$stmt->execute();
49+
} catch (Throwable $e) {
50+
echo $e->getMessage()."\n\n";
51+
}
52+
53+
echo "Array, bindParam:\n";
54+
try {
55+
$stmt = $db->prepare('SELECT (?)');
56+
$param = ['aaa'];
57+
$stmt->bindParam(1, $param, PDO::PARAM_INT);
58+
$stmt->execute();
59+
} catch (Throwable $e) {
60+
echo $e->getMessage();
61+
}
62+
?>
63+
--EXPECT--
64+
Stringable object, value set after bindParam:
65+
bool(true)
66+
bool(true)
67+
68+
Stringable object, bindValue:
69+
array(1) {
70+
[0]=>
71+
array(1) {
72+
["?"]=>
73+
string(3) "555"
74+
}
75+
}
76+
77+
Normal object, bindValue:
78+
SQLSTATE[HY105]: Invalid parameter type: Expected a scalar value or null
79+
80+
Array, bindParam:
81+
SQLSTATE[HY105]: Invalid parameter type: Expected a scalar value or null

0 commit comments

Comments
 (0)