From f9b0c646bcf185e2caf1c02342571d99428bac76 Mon Sep 17 00:00:00 2001 From: Saki Takamachi Date: Sun, 28 Apr 2024 16:16:58 +0900 Subject: [PATCH 1/3] Use the new FCC API --- ext/pdo_sqlite/pdo_sqlite.c | 16 ++--- ext/pdo_sqlite/php_pdo_sqlite_int.h | 13 ++-- ext/pdo_sqlite/sqlite_driver.c | 104 +++++++++++++++------------- 3 files changed, 62 insertions(+), 71 deletions(-) diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c index 55bfc621291e..487fa6b4c35d 100644 --- a/ext/pdo_sqlite/pdo_sqlite.c +++ b/ext/pdo_sqlite/pdo_sqlite.c @@ -335,25 +335,18 @@ PHP_METHOD(PdoSqlite, openBlob) static int php_sqlite_collation_callback(void *context, int string1_len, const void *string1, int string2_len, const void *string2) { - int ret; + int ret = 0; zval zargs[2]; zval retval; struct pdo_sqlite_collation *collation = (struct pdo_sqlite_collation*) context; - collation->fc.fci.size = sizeof(collation->fc.fci); - ZVAL_COPY_VALUE(&collation->fc.fci.function_name, &collation->callback); - collation->fc.fci.object = NULL; - collation->fc.fci.retval = &retval; - // Prepare the arguments. ZVAL_STRINGL(&zargs[0], (char *) string1, string1_len); ZVAL_STRINGL(&zargs[1], (char *) string2, string2_len); - collation->fc.fci.param_count = 2; - collation->fc.fci.params = zargs; - if ((ret = zend_call_function(&collation->fc.fci, &collation->fc.fcc)) == FAILURE) { - php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); - } else if (!Z_ISUNDEF(retval)) { + zend_call_known_fcc(&collation->callback, &retval, /* argc */ 2, zargs, /* named_params */ NULL); + + if (!Z_ISUNDEF(retval)) { if (Z_TYPE(retval) != IS_LONG) { zend_string *func_name = get_active_function_or_method_name(); zend_type_error("%s(): Return value of the callback must be of type int, %s returned", @@ -361,7 +354,6 @@ static int php_sqlite_collation_callback(void *context, int string1_len, const v zend_string_release(func_name); return FAILURE; } - ret = 0; if (Z_LVAL(retval) > 0) { ret = 1; } else if (Z_LVAL(retval) < 0) { diff --git a/ext/pdo_sqlite/php_pdo_sqlite_int.h b/ext/pdo_sqlite/php_pdo_sqlite_int.h index ae42a03e8a80..054e1fbae262 100644 --- a/ext/pdo_sqlite/php_pdo_sqlite_int.h +++ b/ext/pdo_sqlite/php_pdo_sqlite_int.h @@ -26,28 +26,23 @@ typedef struct { char *errmsg; } pdo_sqlite_error_info; -struct pdo_sqlite_fci { - zend_fcall_info fci; - zend_fcall_info_cache fcc; -}; - struct pdo_sqlite_func { struct pdo_sqlite_func *next; - zval func, step, fini; int argc; const char *funcname; /* accelerated callback references */ - struct pdo_sqlite_fci afunc, astep, afini; + zend_fcall_info_cache func; + zend_fcall_info_cache step; + zend_fcall_info_cache fini; }; struct pdo_sqlite_collation { struct pdo_sqlite_collation *next; const char *name; - zval callback; - struct pdo_sqlite_fci fc; + zend_fcall_info_cache callback; }; typedef struct { diff --git a/ext/pdo_sqlite/sqlite_driver.c b/ext/pdo_sqlite/sqlite_driver.c index a5875f84f830..6c331eb126f7 100644 --- a/ext/pdo_sqlite/sqlite_driver.c +++ b/ext/pdo_sqlite/sqlite_driver.c @@ -112,14 +112,14 @@ static void pdo_sqlite_cleanup_callbacks(pdo_sqlite_db_handle *H) } efree((char*)func->funcname); - if (!Z_ISUNDEF(func->func)) { - zval_ptr_dtor(&func->func); + if (ZEND_FCC_INITIALIZED(func->func)) { + zend_fcc_dtor(&func->func); } - if (!Z_ISUNDEF(func->step)) { - zval_ptr_dtor(&func->step); + if (ZEND_FCC_INITIALIZED(func->step)) { + zend_fcc_dtor(&func->step); } - if (!Z_ISUNDEF(func->fini)) { - zval_ptr_dtor(&func->fini); + if (ZEND_FCC_INITIALIZED(func->fini)) { + zend_fcc_dtor(&func->fini); } efree(func); } @@ -139,8 +139,8 @@ static void pdo_sqlite_cleanup_callbacks(pdo_sqlite_db_handle *H) } efree((char*)collation->name); - if (!Z_ISUNDEF(collation->callback)) { - zval_ptr_dtor(&collation->callback); + if (ZEND_FCC_INITIALIZED(collation->callback)) { + zend_fcc_dtor(&collation->callback); } efree(collation); } @@ -309,12 +309,12 @@ typedef struct { zend_long row; } aggregate_context; -static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, int argc, sqlite3_value **argv, sqlite3_context *context, int is_agg) +static int do_callback(zend_fcall_info_cache *fcc, int argc, sqlite3_value **argv, sqlite3_context *context, int is_agg) { zval *zargs = NULL; zval retval; int i; - int ret; + int ret = SUCCESS; int fake_argc; aggregate_context *agg_context = NULL; @@ -324,14 +324,7 @@ static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, int argc, sqlite3_va fake_argc = argc + is_agg; - fc->fci.size = sizeof(fc->fci); - ZVAL_COPY_VALUE(&fc->fci.function_name, cb); - fc->fci.object = NULL; - fc->fci.retval = &retval; - fc->fci.param_count = fake_argc; - /* build up the params */ - if (fake_argc) { zargs = safe_emalloc(fake_argc, sizeof(zval), 0); } @@ -372,11 +365,7 @@ static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, int argc, sqlite3_va } } - fc->fci.params = zargs; - - if ((ret = zend_call_function(&fc->fci, &fc->fcc)) == FAILURE) { - php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); - } + zend_call_known_fcc(fcc, &retval, fake_argc, zargs, /* named_params */ NULL); /* clean up the params */ if (zargs) { @@ -445,41 +434,33 @@ static void php_sqlite3_func_step_callback(sqlite3_context *context, int argc, s { struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); - do_callback(&func->astep, &func->step, argc, argv, context, 1); + do_callback(&func->step, argc, argv, context, 1); } static void php_sqlite3_func_final_callback(sqlite3_context *context) { struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); - do_callback(&func->afini, &func->fini, 0, NULL, context, 1); + do_callback(&func->fini, 0, NULL, context, 1); } static int php_sqlite3_collation_callback(void *context, int string1_len, const void *string1, int string2_len, const void *string2) { - int ret; + int ret = 0; zval zargs[2]; zval retval; struct pdo_sqlite_collation *collation = (struct pdo_sqlite_collation*) context; - collation->fc.fci.size = sizeof(collation->fc.fci); - ZVAL_COPY_VALUE(&collation->fc.fci.function_name, &collation->callback); - collation->fc.fci.object = NULL; - collation->fc.fci.retval = &retval; - /* Prepare the arguments. */ ZVAL_STRINGL(&zargs[0], (char *) string1, string1_len); ZVAL_STRINGL(&zargs[1], (char *) string2, string2_len); - collation->fc.fci.param_count = 2; - collation->fc.fci.params = zargs; - if ((ret = zend_call_function(&collation->fc.fci, &collation->fc.fcc)) == FAILURE) { - php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); - } else if (!Z_ISUNDEF(retval)) { + zend_call_known_fcc(&collation->callback, &retval, /* argc */ 2, zargs, /* named_params */ NULL); + + if (!Z_ISUNDEF(retval)) { if (Z_TYPE(retval) != IS_LONG) { convert_to_long(&retval); } - ret = 0; if (Z_LVAL(retval) > 0) { ret = 1; } else if (Z_LVAL(retval) < 0) { @@ -498,7 +479,7 @@ static void php_sqlite3_func_callback(sqlite3_context *context, int argc, sqlite { struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); - do_callback(&func->afunc, &func->func, argc, argv, context, 0); + do_callback(&func->func, argc, argv, context, 0); } void pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAMETERS) @@ -516,7 +497,7 @@ void pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAMETERS) ZEND_PARSE_PARAMETERS_START(2, 4) Z_PARAM_STRING(func_name, func_name_len) - Z_PARAM_FUNC(fci, fcc) + Z_PARAM_FUNC_NO_TRAMPOLINE_FREE(fci, fcc) Z_PARAM_OPTIONAL Z_PARAM_LONG(argc) Z_PARAM_LONG(flags) @@ -533,7 +514,7 @@ void pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAMETERS) if (ret == SQLITE_OK) { func->funcname = estrdup(func_name); - ZVAL_COPY(&func->func, &fci.function_name); + zend_fcc_dup(&func->func, &fcc); func->argc = argc; @@ -544,6 +525,7 @@ void pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAMETERS) } efree(func); + zend_release_fcall_info_cache(&fcc); RETURN_FALSE; } @@ -566,14 +548,18 @@ void pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAMETERS) pdo_dbh_t *dbh; pdo_sqlite_db_handle *H; int ret; + bool is_throw = false; + + step_fcc.function_handler = NULL; + fini_fcc.function_handler = NULL; ZEND_PARSE_PARAMETERS_START(3, 4) Z_PARAM_STRING(func_name, func_name_len) - Z_PARAM_FUNC(step_fci, step_fcc) - Z_PARAM_FUNC(fini_fci, fini_fcc) + Z_PARAM_FUNC_NO_TRAMPOLINE_FREE(step_fci, step_fcc) + Z_PARAM_FUNC_NO_TRAMPOLINE_FREE(fini_fci, fini_fcc) Z_PARAM_OPTIONAL Z_PARAM_LONG(argc) - ZEND_PARSE_PARAMETERS_END(); + ZEND_PARSE_PARAMETERS_END_EX(is_throw = true; goto error;); dbh = Z_PDO_DBH_P(ZEND_THIS); PDO_CONSTRUCT_CHECK; @@ -587,9 +573,9 @@ void pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAMETERS) if (ret == SQLITE_OK) { func->funcname = estrdup(func_name); - ZVAL_COPY(&func->step, &step_fci.function_name); + zend_fcc_dup(&func->step, &step_fcc); - ZVAL_COPY(&func->fini, &fini_fci.function_name); + zend_fcc_dup(&func->fini, &fini_fcc); func->argc = argc; @@ -600,6 +586,14 @@ void pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAMETERS) } efree(func); + +error: + zend_release_fcall_info_cache(&step_fcc); + zend_release_fcall_info_cache(&fini_fcc); + + if (is_throw) { + RETURN_THROWS(); + } RETURN_FALSE; } @@ -641,7 +635,7 @@ void pdo_sqlite_create_collation_internal(INTERNAL_FUNCTION_PARAMETERS, pdo_sqli ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STRING(collation_name, collation_name_len) - Z_PARAM_FUNC(fci, fcc) + Z_PARAM_FUNC_NO_TRAMPOLINE_FREE(fci, fcc) ZEND_PARSE_PARAMETERS_END(); dbh = Z_PDO_DBH_P(ZEND_THIS); @@ -655,7 +649,7 @@ void pdo_sqlite_create_collation_internal(INTERNAL_FUNCTION_PARAMETERS, pdo_sqli if (ret == SQLITE_OK) { collation->name = estrdup(collation_name); - ZVAL_COPY(&collation->callback, &fci.function_name); + zend_fcc_dup(&collation->callback, &fcc); collation->next = H->collations; H->collations = collation; @@ -663,6 +657,8 @@ void pdo_sqlite_create_collation_internal(INTERNAL_FUNCTION_PARAMETERS, pdo_sqli RETURN_TRUE; } + zend_release_fcall_info_cache(&fcc); + if (UNEXPECTED(EG(exception))) { RETURN_THROWS(); } @@ -706,15 +702,23 @@ static void pdo_sqlite_get_gc(pdo_dbh_t *dbh, zend_get_gc_buffer *gc_buffer) struct pdo_sqlite_func *func = H->funcs; while (func) { - zend_get_gc_buffer_add_zval(gc_buffer, &func->func); - zend_get_gc_buffer_add_zval(gc_buffer, &func->step); - zend_get_gc_buffer_add_zval(gc_buffer, &func->fini); + if (ZEND_FCC_INITIALIZED(func->func)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &func->func); + } + if (ZEND_FCC_INITIALIZED(func->step)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &func->step); + } + if (ZEND_FCC_INITIALIZED(func->fini)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &func->fini); + } func = func->next; } struct pdo_sqlite_collation *collation = H->collations; while (collation) { - zend_get_gc_buffer_add_zval(gc_buffer, &collation->callback); + if (ZEND_FCC_INITIALIZED(collation->callback)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &collation->callback); + } collation = collation->next; } } From 82c2bac5f892978c2ad59b3dac627b60e0aa119f Mon Sep 17 00:00:00 2001 From: Saki Takamachi Date: Wed, 1 May 2024 20:30:49 +0900 Subject: [PATCH 2/3] Address comments --- ext/pdo_sqlite/sqlite_driver.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/ext/pdo_sqlite/sqlite_driver.c b/ext/pdo_sqlite/sqlite_driver.c index 6c331eb126f7..99882388a59d 100644 --- a/ext/pdo_sqlite/sqlite_driver.c +++ b/ext/pdo_sqlite/sqlite_driver.c @@ -485,8 +485,8 @@ static void php_sqlite3_func_callback(sqlite3_context *context, int argc, sqlite void pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAMETERS) { struct pdo_sqlite_func *func; - zend_fcall_info fci; - zend_fcall_info_cache fcc; + zend_fcall_info fci = empty_fcall_info; + zend_fcall_info_cache fcc = empty_fcall_info_cache; char *func_name; size_t func_name_len; zend_long argc = -1; @@ -501,7 +501,7 @@ void pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAMETERS) Z_PARAM_OPTIONAL Z_PARAM_LONG(argc) Z_PARAM_LONG(flags) - ZEND_PARSE_PARAMETERS_END(); + ZEND_PARSE_PARAMETERS_END_EX(goto error;); dbh = Z_PDO_DBH_P(ZEND_THIS); PDO_CONSTRUCT_CHECK; @@ -525,6 +525,8 @@ void pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAMETERS) } efree(func); + +error: zend_release_fcall_info_cache(&fcc); RETURN_FALSE; } @@ -540,18 +542,16 @@ PHP_METHOD(PDO_SQLite_Ext, sqliteCreateFunction) void pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAMETERS) { struct pdo_sqlite_func *func; - zend_fcall_info step_fci, fini_fci; - zend_fcall_info_cache step_fcc, fini_fcc; + zend_fcall_info step_fci = empty_fcall_info; + zend_fcall_info fini_fci = empty_fcall_info; + zend_fcall_info_cache step_fcc = empty_fcall_info_cache; + zend_fcall_info_cache fini_fcc = empty_fcall_info_cache; char *func_name; size_t func_name_len; zend_long argc = -1; pdo_dbh_t *dbh; pdo_sqlite_db_handle *H; int ret; - bool is_throw = false; - - step_fcc.function_handler = NULL; - fini_fcc.function_handler = NULL; ZEND_PARSE_PARAMETERS_START(3, 4) Z_PARAM_STRING(func_name, func_name_len) @@ -559,7 +559,7 @@ void pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAMETERS) Z_PARAM_FUNC_NO_TRAMPOLINE_FREE(fini_fci, fini_fcc) Z_PARAM_OPTIONAL Z_PARAM_LONG(argc) - ZEND_PARSE_PARAMETERS_END_EX(is_throw = true; goto error;); + ZEND_PARSE_PARAMETERS_END_EX(goto error;); dbh = Z_PDO_DBH_P(ZEND_THIS); PDO_CONSTRUCT_CHECK; @@ -574,7 +574,6 @@ void pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAMETERS) func->funcname = estrdup(func_name); zend_fcc_dup(&func->step, &step_fcc); - zend_fcc_dup(&func->fini, &fini_fcc); func->argc = argc; @@ -590,10 +589,6 @@ void pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAMETERS) error: zend_release_fcall_info_cache(&step_fcc); zend_release_fcall_info_cache(&fini_fcc); - - if (is_throw) { - RETURN_THROWS(); - } RETURN_FALSE; } @@ -625,8 +620,8 @@ PHP_METHOD(PDO_SQLite_Ext, sqliteCreateAggregate) void pdo_sqlite_create_collation_internal(INTERNAL_FUNCTION_PARAMETERS, pdo_sqlite_create_collation_callback callback) { struct pdo_sqlite_collation *collation; - zend_fcall_info fci; - zend_fcall_info_cache fcc; + zend_fcall_info fci = empty_fcall_info; + zend_fcall_info_cache fcc = empty_fcall_info_cache; char *collation_name; size_t collation_name_len; pdo_dbh_t *dbh; From 0ad72727fd65c0b1bf5e9de7e7fbfec4e274c138 Mon Sep 17 00:00:00 2001 From: Saki Takamachi Date: Wed, 1 May 2024 20:47:52 +0900 Subject: [PATCH 3/3] added tests --- .../pdo_sqlite_createafunction_arg_error.phpt | 49 +++++++++++++++ .../pdo_sqlite_createaggregate_arg_error.phpt | 63 +++++++++++++++++++ .../pdo_sqlite_createcollation_arg_error.phpt | 35 +++++++++++ 3 files changed, 147 insertions(+) create mode 100644 ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createafunction_arg_error.phpt create mode 100644 ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate_arg_error.phpt create mode 100644 ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_arg_error.phpt diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createafunction_arg_error.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createafunction_arg_error.phpt new file mode 100644 index 000000000000..9cdd689f74aa --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createafunction_arg_error.phpt @@ -0,0 +1,49 @@ +--TEST-- +Test PdoSqlite::createFunction() arguments error +--EXTENSIONS-- +pdo_sqlite +--FILE-- +createFunction(null, [new TrampolineTest(), 'strtoupper']); +} catch (Throwable $e) { + echo $e->getMessage() . "\n"; +} + +try { + $db->createFunction('strtoupper', null); +} catch (Throwable $e) { + echo $e->getMessage() . "\n"; +} + +try { + $db->createFunction('strtoupper', [new TrampolineTest(), 'strtoupper'], null); +} catch (Throwable $e) { + echo $e->getMessage() . "\n"; +} + +try { + $db->createFunction('strtoupper', [new TrampolineTest(), 'strtoupper'], 1, null); +} catch (Throwable $e) { + echo $e->getMessage() . "\n"; +} + +echo 'done!'; +?> +--EXPECT-- +PdoSqlite::createFunction(): Argument #1 ($function_name) must be of type string, null given +PdoSqlite::createFunction(): Argument #2 ($callback) must be a valid callback, no array or string given +PdoSqlite::createFunction(): Argument #3 ($num_args) must be of type int, null given +PdoSqlite::createFunction(): Argument #4 ($flags) must be of type int, null given +done! diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate_arg_error.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate_arg_error.phpt new file mode 100644 index 000000000000..8c0f3fedd963 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate_arg_error.phpt @@ -0,0 +1,63 @@ +--TEST-- +Test PdoSqlite::createAggregate() arguments error +--EXTENSIONS-- +pdo_sqlite +--FILE-- +createAggregate(null, [new TrampolineTest(), 'step'], null, 1); +} catch (Throwable $e) { + echo $e->getMessage() . "\n"; +} + +try { + $db->createAggregate(null, null, [new TrampolineTest(), 'step'], 1); +} catch (Throwable $e) { + echo $e->getMessage() . "\n"; +} + +try { + $db->createAggregate(null, [new TrampolineTest(), 'step'], [new TrampolineTest(), 'step'], 1); +} catch (Throwable $e) { + echo $e->getMessage() . "\n"; +} + +try { + $db->createAggregate('S', null, [new TrampolineTest(), 'finalize'], 1); +} catch (Throwable $e) { + echo $e->getMessage() . "\n"; +} + +try { + $db->createAggregate('S', [new TrampolineTest(), 'step'], null, 1); +} catch (Throwable $e) { + echo $e->getMessage() . "\n"; +} + +try { + $db->createAggregate('S', [new TrampolineTest(), 'step'], [new TrampolineTest(), 'finalize'], null); +} catch (Throwable $e) { + echo $e->getMessage() . "\n"; +} + +echo 'done!'; +?> +--EXPECT-- +PdoSqlite::createAggregate(): Argument #1 ($name) must be of type string, null given +PdoSqlite::createAggregate(): Argument #1 ($name) must be of type string, null given +PdoSqlite::createAggregate(): Argument #1 ($name) must be of type string, null given +PdoSqlite::createAggregate(): Argument #2 ($step) must be a valid callback, no array or string given +PdoSqlite::createAggregate(): Argument #3 ($finalize) must be a valid callback, no array or string given +PdoSqlite::createAggregate(): Argument #4 ($numArgs) must be of type int, null given +done! diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_arg_error.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_arg_error.phpt new file mode 100644 index 000000000000..c5be063efc7a --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_arg_error.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test PdoSqlite::createCollation() arguments error +--EXTENSIONS-- +pdo_sqlite +--FILE-- +createCollation(null, [new TrampolineTest(), 'NAT']); +} catch (Throwable $e) { + echo $e->getMessage() . "\n"; +} + +try { + $db->createCollation('NAT', null); +} catch (Throwable $e) { + echo $e->getMessage() . "\n"; +} + +echo 'done!'; +?> +--EXPECT-- +PdoSqlite::createCollation(): Argument #1 ($name) must be of type string, null given +PdoSqlite::createCollation(): Argument #2 ($callback) must be a valid callback, no array or string given +done!