Skip to content

Commit 55e8ebe

Browse files
committed
Merge branch 'PHP-8.4'
* PHP-8.4: Fix phpGH-16131: Prevent mixing PDO sub-classes with different DSN
2 parents 96f83f7 + 5892991 commit 55e8ebe

File tree

5 files changed

+84
-30
lines changed

5 files changed

+84
-30
lines changed

ext/pdo/pdo_dbh.c

+52-28
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ static char *dsn_from_uri(char *uri, char *buf, size_t buflen) /* {{{ */
219219
}
220220
/* }}} */
221221

222-
static bool create_driver_specific_pdo_object(pdo_driver_t *driver, zend_class_entry *called_scope, zval *new_object)
222+
static bool create_driver_specific_pdo_object(pdo_driver_t *driver, zend_class_entry *called_scope, zval *new_zval_object)
223223
{
224224
zend_class_entry *ce;
225225
zend_class_entry *ce_based_on_driver_name = NULL, *ce_based_on_called_object = NULL;
@@ -235,47 +235,70 @@ static bool create_driver_specific_pdo_object(pdo_driver_t *driver, zend_class_e
235235

236236
if (ce_based_on_called_object) {
237237
if (ce_based_on_driver_name) {
238-
if (instanceof_function(ce_based_on_called_object, ce_based_on_driver_name) == false) {
238+
if (!instanceof_function(ce_based_on_called_object, ce_based_on_driver_name)) {
239239
zend_throw_exception_ex(pdo_exception_ce, 0,
240-
"%s::connect() cannot be called when connecting to the \"%s\" driver, "
241-
"either %s::connect() or PDO::connect() must be called instead",
242-
ZSTR_VAL(called_scope->name), driver->driver_name, ZSTR_VAL(ce_based_on_driver_name->name));
240+
"%s::%s() cannot be used for connecting to the \"%s\" driver, "
241+
"either call %s::%s() or PDO::%s() instead",
242+
ZSTR_VAL(called_scope->name),
243+
new_zval_object ? "connect" : "__construct",
244+
driver->driver_name,
245+
ZSTR_VAL(ce_based_on_driver_name->name),
246+
new_zval_object ? "connect" : "__construct",
247+
new_zval_object ? "connect" : "__construct"
248+
);
243249
return false;
244250
}
245251

246-
/* A driver-specific implementation was instantiated via the connect() method of the appropriate driver class */
247-
object_init_ex(new_object, ce_based_on_called_object);
252+
/* A driver-specific implementation is instantiated with the appropriate driver class */
253+
if (new_zval_object) {
254+
object_init_ex(new_zval_object, called_scope);
255+
}
248256
return true;
249257
} else {
250258
zend_throw_exception_ex(pdo_exception_ce, 0,
251-
"%s::connect() cannot be called when connecting to an unknown driver, "
252-
"PDO::connect() must be called instead",
253-
ZSTR_VAL(called_scope->name));
259+
"%s::%s() cannot be used for connecting to an unknown driver, "
260+
"call PDO::%s() instead",
261+
ZSTR_VAL(called_scope->name),
262+
new_zval_object ? "connect" : "__construct",
263+
new_zval_object ? "connect" : "__construct"
264+
);
254265
return false;
255266
}
256267
}
257268

269+
/* A non-driver specific PDO subclass is instantiated via the constructor. This results in the legacy behavior. */
270+
if (called_scope != pdo_dbh_ce && new_zval_object == NULL) {
271+
return true;
272+
}
273+
258274
if (ce_based_on_driver_name) {
259275
if (called_scope != pdo_dbh_ce) {
260-
/* A driver-specific implementation was instantiated via the connect method of a wrong driver class */
276+
/* A driver-specific implementation is instantiated with a wrong driver class */
261277
zend_throw_exception_ex(pdo_exception_ce, 0,
262-
"%s::connect() cannot be called when connecting to the \"%s\" driver, "
263-
"either %s::connect() or PDO::connect() must be called instead",
264-
ZSTR_VAL(called_scope->name), driver->driver_name, ZSTR_VAL(ce_based_on_driver_name->name));
278+
"%s::%s() cannot be used for connecting to the \"%s\" driver, "
279+
"either call %s::%s() or PDO::%s() instead",
280+
ZSTR_VAL(called_scope->name),
281+
new_zval_object ? "connect" : "__construct",
282+
driver->driver_name,
283+
ZSTR_VAL(ce_based_on_driver_name->name),
284+
new_zval_object ? "connect" : "__construct",
285+
new_zval_object ? "connect" : "__construct"
286+
);
265287
return false;
266288
}
267289

268-
/* A driver-specific implementation was instantiated via PDO::__construct() */
269-
object_init_ex(new_object, ce_based_on_driver_name);
270-
} else {
290+
if (new_zval_object) {
291+
object_init_ex(new_zval_object, ce_based_on_driver_name);
292+
}
293+
} else if (new_zval_object) {
271294
/* No driver-specific implementation found */
272-
object_init_ex(new_object, called_scope);
295+
object_init_ex(new_zval_object, called_scope);
273296
}
274297

275298
return true;
276299
}
277300

278-
static void internal_construct(INTERNAL_FUNCTION_PARAMETERS, zend_object *object, zend_class_entry *current_scope, zval *new_zval_object)
301+
PDO_API void php_pdo_internal_construct_driver(INTERNAL_FUNCTION_PARAMETERS, zend_object *current_object, zend_class_entry *called_scope, zval *new_zval_object)
279302
{
280303
pdo_dbh_t *dbh = NULL;
281304
bool is_persistent = 0;
@@ -343,15 +366,16 @@ static void internal_construct(INTERNAL_FUNCTION_PARAMETERS, zend_object *object
343366
RETURN_THROWS();
344367
}
345368

346-
if (new_zval_object != NULL) {
347-
ZEND_ASSERT((driver->driver_name != NULL) && "PDO driver name is null");
348-
if (!create_driver_specific_pdo_object(driver, current_scope, new_zval_object)) {
349-
RETURN_THROWS();
350-
}
369+
ZEND_ASSERT((driver->driver_name != NULL) && "PDO driver name is null");
370+
371+
if (!create_driver_specific_pdo_object(driver, called_scope, new_zval_object)) {
372+
RETURN_THROWS();
373+
}
351374

375+
if (new_zval_object) {
352376
dbh = Z_PDO_DBH_P(new_zval_object);
353377
} else {
354-
dbh = php_pdo_dbh_fetch_inner(object);
378+
dbh = php_pdo_dbh_fetch_inner(current_object);
355379
}
356380

357381
/* is this supposed to be a persistent connection ? */
@@ -413,7 +437,7 @@ static void internal_construct(INTERNAL_FUNCTION_PARAMETERS, zend_object *object
413437
if (pdbh) {
414438
efree(dbh);
415439
/* switch over to the persistent one */
416-
php_pdo_dbh_fetch_object(object)->inner = pdbh;
440+
php_pdo_dbh_fetch_object(current_object)->inner = pdbh;
417441
pdbh->refcount++;
418442
dbh = pdbh;
419443
}
@@ -497,14 +521,14 @@ static void internal_construct(INTERNAL_FUNCTION_PARAMETERS, zend_object *object
497521
/* {{{ */
498522
PHP_METHOD(PDO, __construct)
499523
{
500-
internal_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, Z_OBJ(EX(This)), EX(This).value.ce, NULL);
524+
php_pdo_internal_construct_driver(INTERNAL_FUNCTION_PARAM_PASSTHRU, Z_OBJ_P(ZEND_THIS), Z_OBJCE_P(ZEND_THIS), NULL);
501525
}
502526
/* }}} */
503527

504528
/* {{{ */
505529
PHP_METHOD(PDO, connect)
506530
{
507-
internal_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, Z_OBJ(EX(This)), EX(This).value.ce, return_value);
531+
php_pdo_internal_construct_driver(INTERNAL_FUNCTION_PARAM_PASSTHRU, NULL, Z_CE_P(ZEND_THIS), return_value);
508532
}
509533
/* }}} */
510534

ext/pdo/php_pdo_driver.h

+2
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,8 @@ PDO_API void php_pdo_dbh_delref(pdo_dbh_t *dbh);
694694
PDO_API void php_pdo_free_statement(pdo_stmt_t *stmt);
695695
PDO_API void php_pdo_stmt_set_column_count(pdo_stmt_t *stmt, int new_count);
696696

697+
PDO_API void php_pdo_internal_construct_driver(INTERNAL_FUNCTION_PARAMETERS, zend_object *current_object, zend_class_entry *called_scope, zval *new_zval_object);
698+
697699
/* Normalization for fetching long param for driver attributes */
698700
PDO_API bool pdo_get_long_param(zend_long *lval, zval *value);
699701
PDO_API bool pdo_get_bool_param(bool *bval, zval *value);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
Test calling a PDO sub-class constructor with a different DSN
3+
--EXTENSIONS--
4+
pdo_pgsql
5+
pdo_sqlite
6+
--FILE--
7+
<?php
8+
9+
try {
10+
new Pdo\Pgsql('sqlite::memory:');
11+
} catch (PDOException $e) {
12+
echo $e->getMessage() . "\n";
13+
}
14+
15+
class MyPgsql extends Pdo\Pgsql
16+
{
17+
}
18+
19+
try {
20+
new MyPgsql('sqlite::memory:');
21+
} catch (PDOException $e) {
22+
echo $e->getMessage() . "\n";
23+
}
24+
25+
?>
26+
--EXPECT--
27+
Pdo\Pgsql::__construct() cannot be used for connecting to the "sqlite" driver, either call Pdo\Sqlite::__construct() or PDO::__construct() instead
28+
MyPgsql::__construct() cannot be used for connecting to the "sqlite" driver, either call Pdo\Sqlite::__construct() or PDO::__construct() instead

ext/pdo_sqlite/tests/subclasses/pdosqlite_005.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ try {
1414

1515
?>
1616
--EXPECT--
17-
Pdo\Pgsql::connect() cannot be called when connecting to the "sqlite" driver, either Pdo\Sqlite::connect() or PDO::connect() must be called instead
17+
Pdo\Pgsql::connect() cannot be used for connecting to the "sqlite" driver, either call Pdo\Sqlite::connect() or PDO::connect() instead

ext/pdo_sqlite/tests/subclasses/pdosqlite_007.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ try {
1515

1616
?>
1717
--EXPECT--
18-
MyPDO::connect() cannot be called when connecting to the "sqlite" driver, either Pdo\Sqlite::connect() or PDO::connect() must be called instead
18+
MyPDO::connect() cannot be used for connecting to the "sqlite" driver, either call Pdo\Sqlite::connect() or PDO::connect() instead

0 commit comments

Comments
 (0)