diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index 239b8d6a99bef..13180e29ee865 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -2077,6 +2077,23 @@ static zend_function *dbstmt_method_get(zend_object **object_pp, zend_string *me return fbc; } +static HashTable *dbstmt_get_gc(zend_object *object, zval **gc_data, int *gc_count) +{ + pdo_stmt_t *stmt = php_pdo_stmt_fetch_object(object); + *gc_data = &stmt->fetch.into; + *gc_count = 1; + + /** + * If there are no dynamic properties and the default property is 1 (that is, there is only one property + * of string that does not participate in GC), there is no need to call zend_std_get_properties(). + */ + if (object->properties == NULL && object->ce->default_properties_count <= 1) { + return NULL; + } else { + return zend_std_get_properties(object); + } +} + zend_object_handlers pdo_dbstmt_object_handlers; zend_object_handlers pdo_row_object_handlers; @@ -2495,6 +2512,7 @@ void pdo_stmt_init(void) pdo_dbstmt_object_handlers.get_method = dbstmt_method_get; pdo_dbstmt_object_handlers.compare = zend_objects_not_comparable; pdo_dbstmt_object_handlers.clone_obj = NULL; + pdo_dbstmt_object_handlers.get_gc = dbstmt_get_gc; pdo_row_ce = register_class_PDORow(); pdo_row_ce->create_object = pdo_row_new; diff --git a/ext/pdo/tests/gh16703.phpt b/ext/pdo/tests/gh16703.phpt new file mode 100644 index 0000000000000..e5b4b8640389e --- /dev/null +++ b/ext/pdo/tests/gh16703.phpt @@ -0,0 +1,48 @@ +--TEST-- +GH-16703: Memory leak of setFetchMode() +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- + PDO::CASE_LOWER, + PDO::ATTR_STATEMENT_CLASS => [TestStmt::class], + ], +); + +$db->exec('CREATE TABLE gh16703 (name varchar(255))'); +$db->exec("INSERT INTO gh16703 (name) VALUES ('test_name')"); + +$stmt = $db->query('SELECT name FROM gh16703'); +$t = $stmt; +$stmt->setFetchMode(PDO::FETCH_INTO, $stmt); +$stmt->fetch(); +echo "done!\n"; +?> +--CLEAN-- +exec('DROP TABLE gh16703'); +?> +--EXPECT-- +done!