From ef8db599264eba9e866342ef0f60a7529e7cebb4 Mon Sep 17 00:00:00 2001 From: Marc Date: Thu, 24 Sep 2026 18:26:21 +0200 Subject: [PATCH 01/13] fix: don't force-overwrite argv if do_php_cli is called by embedder --- sapi/cli/php_cli.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c index 9cd1f5a8c158..3d21eb046ede 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -1333,12 +1333,16 @@ PHP_CLI_API int do_php_cli(int argc, char *argv[]) #if defined(PHP_WIN32) php_win32_cp_cli_setup(); orig_cp = (php_win32_cp_get_orig())->id; - /* Ignore the delivered argv and argc, read from W API. This place - might be too late though, but this is the earliest place ATW - we can access the internal charset information from PHP. */ - argv_wide = CommandLineToArgvW(GetCommandLineW(), &num_args); - PHP_WIN32_CP_W_TO_ANY_ARRAY(argv_wide, num_args, argv, argc) - using_wide_argv = 1; + /* Embedders supply their own arguments, we mustn't replace them with + * the ones by the host process command line. */ + if (argv_save == __argv) { + /* Ignore the delivered argv and argc, read from W API. This place + might be too late though, but this is the earliest place ATW + we can access the internal charset information from PHP. */ + argv_wide = CommandLineToArgvW(GetCommandLineW(), &num_args); + PHP_WIN32_CP_W_TO_ANY_ARRAY(argv_wide, num_args, argv, argc) + using_wide_argv = 1; + } SetConsoleCtrlHandler(php_cli_win32_ctrl_handler, TRUE); #endif From e520f2e57ad95f669119be96ff4b2e4ea1119573 Mon Sep 17 00:00:00 2001 From: Marc Date: Sat, 26 Sep 2026 18:15:32 +0200 Subject: [PATCH 02/13] require custom do_php_cli() argv to be passed as utf8 --- UPGRADING.INTERNALS | 2 +- sapi/cli/cli.h | 1 + sapi/cli/php_cli.c | 28 ++++++++++++++++++++++++---- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 8bbdc5caabe5..eeed7212f85b 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -366,4 +366,4 @@ PHP 8.6 INTERNALS UPGRADE NOTES __thread storage on ZTS builds. - php-cli functionality is now available in embed builds via the do_php_cli() - function. + function. On Windows, custom argv must use UTF-8. diff --git a/sapi/cli/cli.h b/sapi/cli/cli.h index 9502a6726fe4..a3f241e84669 100644 --- a/sapi/cli/cli.h +++ b/sapi/cli/cli.h @@ -55,6 +55,7 @@ typedef struct php_cli_server_context { } php_cli_server_context; /* this performs full cli-SAPI boot, loads modules, sets up TSRM and co. */ +/* On Windows, custom argv strings must be UTF-8. */ extern PHP_CLI_API int do_php_cli(int argc, char *argv[]); #endif /* CLI_H */ diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c index 3d21eb046ede..39ec89c3095a 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -1187,9 +1187,8 @@ PHP_CLI_API int do_php_cli(int argc, char *argv[]) { #if defined(PHP_WIN32) int num_args; - wchar_t **argv_wide; + wchar_t **argv_wide = NULL; char **argv_save = argv; - BOOL using_wide_argv = 0; #endif int c; @@ -1341,7 +1340,26 @@ PHP_CLI_API int do_php_cli(int argc, char *argv[]) we can access the internal charset information from PHP. */ argv_wide = CommandLineToArgvW(GetCommandLineW(), &num_args); PHP_WIN32_CP_W_TO_ANY_ARRAY(argv_wide, num_args, argv, argc) - using_wide_argv = 1; + } else if (!php_win32_cp_use_unicode()) { + /* Custom arguments are UTF-8, regardless of PHP's configured encoding. */ + char **converted_argv = calloc((size_t) argc + 1, sizeof(char *)); + if (!converted_argv) { + exit_status = 1; + goto out; + } + argv = converted_argv; + for (int i = 0; i < argc; i++) { + wchar_t *arg = php_win32_cp_utf8_to_w(argv_save[i]); + if (arg) { + argv[i] = php_win32_cp_w_to_any(arg); + free(arg); + } + if (!argv[i]) { + fprintf(stderr, "Could not convert command line argument %d.\n", i); + exit_status = 1; + goto out; + } + } } SetConsoleCtrlHandler(php_cli_win32_ctrl_handler, TRUE); @@ -1381,8 +1399,10 @@ PHP_CLI_API int do_php_cli(int argc, char *argv[]) #if defined(PHP_WIN32) (void)php_win32_cp_cli_restore(); - if (using_wide_argv) { + if (argv != argv_save) { PHP_WIN32_CP_FREE_ARRAY(argv, argc); + } + if (argv_wide) { LocalFree(argv_wide); } argv = argv_save; From 65efafbcdd1be6b13505a07ab274b1210a32e1f3 Mon Sep 17 00:00:00 2001 From: Marc Date: Sat, 26 Sep 2026 18:28:32 +0200 Subject: [PATCH 03/13] create embed test, verify argv always passed as valid utf8 --- sapi/cli/php_cli.c | 12 ++++++++++ sapi/embed/Makefile.frag.w32 | 8 +++++++ sapi/embed/config.w32 | 2 ++ sapi/embed/tests/cli_argv.c | 26 +++++++++++++++++++++ sapi/embed/tests/cli_argv.phpt | 42 ++++++++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+) create mode 100644 sapi/embed/Makefile.frag.w32 create mode 100644 sapi/embed/tests/cli_argv.c create mode 100644 sapi/embed/tests/cli_argv.phpt diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c index 39ec89c3095a..fb6e63ea4fdc 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -1207,6 +1207,18 @@ PHP_CLI_API int do_php_cli(int argc, char *argv[]) */ argv = save_ps_args(argc, argv); +#ifdef PHP_WIN32 + if (argv_save != __argv) { + for (int i = 0; i < argc; i++) { + if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, argv[i], -1, NULL, 0)) { + fprintf(stderr, "Invalid UTF-8 in command line argument %d.\n", i); + cleanup_ps_args(argv); + return 1; + } + } + } +#endif + #if defined(PHP_WIN32) && !defined(PHP_CLI_WIN32_NO_CONSOLE) php_win32_console_fileno_set_vt100(STDOUT_FILENO, TRUE); php_win32_console_fileno_set_vt100(STDERR_FILENO, TRUE); diff --git a/sapi/embed/Makefile.frag.w32 b/sapi/embed/Makefile.frag.w32 new file mode 100644 index 000000000000..0fee41a07483 --- /dev/null +++ b/sapi/embed/Makefile.frag.w32 @@ -0,0 +1,8 @@ +test_helpers: $(BUILD_DIR)\php_embed_cli_test.exe + +$(BUILD_DIR)\sapi\embed\cli_argv.obj: $(PHP_SRC_DIR)\sapi\embed\tests\cli_argv.c + cd $(PHP_SRC_DIR) + $(CC) $(CFLAGS_EMBED_OBJ) $(CFLAGS) $(CFLAGS_BD_SAPI_EMBED) /Fo$@ /c $(PHP_SRC_DIR)\sapi\embed\tests\cli_argv.c + +$(BUILD_DIR)\php_embed_cli_test.exe: $(BUILD_DIR)\sapi\embed\cli_argv.obj $(BUILD_DIR)\$(EMBED_LIB) + "$(LINK)" /nologo $(BUILD_DIR)\sapi\embed\cli_argv.obj $(BUILD_DIR)\$(EMBED_LIB) /out:$@ $(LDFLAGS) $(LDFLAGS_EMBED) /stack:67108864 diff --git a/sapi/embed/config.w32 b/sapi/embed/config.w32 index 3ac4668bce8c..6fb5345ac492 100644 --- a/sapi/embed/config.w32 +++ b/sapi/embed/config.w32 @@ -10,4 +10,6 @@ if (PHP_EMBED != "no") { ADD_FLAG("LIBS_EMBED", "ws2_32.lib"); ADD_FLAG("LIBS_EMBED", "shell32.lib"); PHP_INSTALL_HEADERS("sapi/embed", "php_embed.h"); + DEFINE('EMBED_LIB', 'php' + PHP_VERSION + 'embed.lib'); + ADD_MAKEFILE_FRAGMENT(); } diff --git a/sapi/embed/tests/cli_argv.c b/sapi/embed/tests/cli_argv.c new file mode 100644 index 000000000000..0700011d975b --- /dev/null +++ b/sapi/embed/tests/cli_argv.c @@ -0,0 +1,26 @@ +#include "php.h" +#include "sapi/cli/cli.h" + +int main(int argc, char *argv[]) +{ + char *php_argv[] = { + "embedded-php", "-n", "-d", "default_charset=UTF-8", "-r", + "echo json_encode([$argc, array_map('bin2hex', array_slice($argv, 1))]), PHP_EOL; exit(23);", + "--", "caf\xc3\xa9", "argument with spaces", "", "\xf0\x9f\x98\x80", NULL + }; + if (argc > 1 && strcmp(argv[1], "default-charset") == 0) { + php_argv[3] = "default_charset=Windows-1252"; + php_argv[10] = "\xe2\x82\xac"; + } else if (argc > 1 && strcmp(argv[1], "internal-encoding") == 0) { + php_argv[3] = "internal_encoding=Windows-1252"; + php_argv[10] = "\xe2\x82\xac"; + } + if (argc > 2 && strcmp(argv[2], "file") == 0) { + php_argv[4] = "-f"; + php_argv[5] = "cli_argv_caf\xc3\xa9.php"; + } else if (argc > 2 && strcmp(argv[2], "invalid-utf8") == 0) { + /* Latin-1 cafe with an accented e, which is not valid UTF-8. */ + php_argv[7] = "caf\xe9"; + } + return do_php_cli((int) (sizeof(php_argv) / sizeof(*php_argv)) - 1, php_argv); +} diff --git a/sapi/embed/tests/cli_argv.phpt b/sapi/embed/tests/cli_argv.phpt new file mode 100644 index 000000000000..f24f265f3090 --- /dev/null +++ b/sapi/embed/tests/cli_argv.phpt @@ -0,0 +1,42 @@ +--TEST-- +do_php_cli() validates and converts custom UTF-8 argv on Windows +--SKIPIF-- + +--FILE-- +&1', $status); + var_dump($status); +} +chdir(__DIR__); +file_put_contents("cli_argv_caf\xc3\xa9.php", ' +--CLEAN-- + +--EXPECT-- +[5,["636166c3a9","617267756d656e74207769746820737061636573","","f09f9880"]] +int(23) +Invalid UTF-8 in command line argument 7. +int(1) +[5,["636166e9","617267756d656e74207769746820737061636573","","80"]] +int(23) +Invalid UTF-8 in command line argument 7. +int(1) +[5,["636166e9","617267756d656e74207769746820737061636573","","80"]] +int(23) +Invalid UTF-8 in command line argument 7. +int(1) +Unicode filename works +int(0) From 2be5d00dcf478334b31e62dec33beed6c0d0e243 Mon Sep 17 00:00:00 2001 From: Marc Date: Mon, 5 Oct 2026 15:11:20 +0200 Subject: [PATCH 04/13] reorder --- sapi/cli/php_cli.c | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c index fb6e63ea4fdc..49477e14bf17 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -1346,32 +1346,34 @@ PHP_CLI_API int do_php_cli(int argc, char *argv[]) orig_cp = (php_win32_cp_get_orig())->id; /* Embedders supply their own arguments, we mustn't replace them with * the ones by the host process command line. */ - if (argv_save == __argv) { + if (argv_save != __argv) { + if (!php_win32_cp_use_unicode()) { + /* Custom arguments must be passed as UTF-8, convert to PHP's internal encvodingg. */ + char **converted_argv = calloc((size_t) argc + 1, sizeof(char *)); + if (!converted_argv) { + exit_status = 1; + goto out; + } + argv = converted_argv; + for (int i = 0; i < argc; i++) { + wchar_t *arg = php_win32_cp_utf8_to_w(argv_save[i]); + if (arg) { + argv[i] = php_win32_cp_w_to_any(arg); + free(arg); + } + if (!argv[i]) { + fprintf(stderr, "Could not convert command line argument %d.\n", i); + exit_status = 1; + goto out; + } + } + } + } else { /* Ignore the delivered argv and argc, read from W API. This place might be too late though, but this is the earliest place ATW we can access the internal charset information from PHP. */ argv_wide = CommandLineToArgvW(GetCommandLineW(), &num_args); PHP_WIN32_CP_W_TO_ANY_ARRAY(argv_wide, num_args, argv, argc) - } else if (!php_win32_cp_use_unicode()) { - /* Custom arguments are UTF-8, regardless of PHP's configured encoding. */ - char **converted_argv = calloc((size_t) argc + 1, sizeof(char *)); - if (!converted_argv) { - exit_status = 1; - goto out; - } - argv = converted_argv; - for (int i = 0; i < argc; i++) { - wchar_t *arg = php_win32_cp_utf8_to_w(argv_save[i]); - if (arg) { - argv[i] = php_win32_cp_w_to_any(arg); - free(arg); - } - if (!argv[i]) { - fprintf(stderr, "Could not convert command line argument %d.\n", i); - exit_status = 1; - goto out; - } - } } SetConsoleCtrlHandler(php_cli_win32_ctrl_handler, TRUE); From 0f657a3c9eaa55e1e0571333c8836b7cc87ff94d Mon Sep 17 00:00:00 2001 From: Marc Date: Tue, 6 Oct 2026 15:14:34 +0200 Subject: [PATCH 05/13] move encoding conversion - do an ini pass and then go over argv --- main/SAPI.h | 4 +- main/main.c | 5 +- main/php_ini.c | 200 ++++++++++++++++++++++++++++- main/php_ini.h | 2 +- sapi/cli/php_cli.c | 108 ++++++++++------ sapi/embed/tests/cli_argv.c | 49 ++++++- sapi/embed/tests/cli_argv.phpt | 39 +++++- sapi/embed/tests/cli_ini_utf8.phpt | 111 ++++++++++++++++ 8 files changed, 473 insertions(+), 45 deletions(-) create mode 100644 sapi/embed/tests/cli_ini_utf8.phpt diff --git a/main/SAPI.h b/main/SAPI.h index 2621b9184d1b..3052aff2b721 100644 --- a/main/SAPI.h +++ b/main/SAPI.h @@ -288,6 +288,7 @@ struct _sapi_module_struct { unsigned int (*input_filter_init)(void); int (*pre_request_init)(void); /* called before activate and before the post data read - used for .user.ini */ + bool ini_entries_utf8; /* Windows: CLI -d values were supplied as UTF-8 */ }; struct _sapi_post_entry { @@ -339,6 +340,7 @@ END_EXTERN_C() NULL, /* ini_entries; */ \ NULL, /* additional_functions */ \ NULL, /* input_filter_init */ \ - NULL /* pre_request_init */ + NULL, /* pre_request_init */ \ + false /* ini_entries_utf8 */ #endif /* SAPI_H */ diff --git a/main/main.c b/main/main.c index 0539220de362..834dc884393f 100644 --- a/main/main.c +++ b/main/main.c @@ -2236,8 +2236,11 @@ zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additi load zend extensions and register php function extensions to be loaded later */ zend_stream_init(); - php_init_config(); + zend_result config_result = php_init_config(); zend_stream_shutdown(); + if (config_result == FAILURE) { + return FAILURE; + } /* Register PHP core ini entries */ zend_register_ini_entries_ex(ini_entries, module_number, MODULE_PERSISTENT); diff --git a/main/php_ini.c b/main/php_ini.c index a3b33da65bab..31a234b2f632 100644 --- a/main/php_ini.c +++ b/main/php_ini.c @@ -25,6 +25,7 @@ #ifdef PHP_WIN32 #include "win32/php_registry.h" #include "win32/winutil.h" +#include "win32/codepage.h" #endif #if defined(HAVE_SCANDIR) && defined(HAVE_ALPHASORT) && defined(HAVE_DIRENT_H) @@ -307,6 +308,191 @@ static void php_ini_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callback_t } /* }}} */ +#ifdef PHP_WIN32 +typedef struct { + zval args[3]; + int callback_type; +} php_ini_utf8_entry; + +typedef struct { + const struct php_win32_cp *cp; + const struct php_win32_cp *utf8; + HashTable values; + zend_llist entries; + bool failed; +} php_ini_utf8_context; + +static php_ini_utf8_context *php_ini_utf8; + +static const struct php_win32_cp *php_ini_utf8_codepage(HashTable *hash) +{ + const char *names[] = {"internal_encoding", "default_charset"}; + for (int i = 0; i < 2; i++) { + zval *value = zend_hash_str_find(hash, names[i], strlen(names[i])); + if (!value) { + value = zend_hash_str_find(&configuration_hash, names[i], strlen(names[i])); + } + if (value && Z_TYPE_P(value) == IS_PTR) { + value = Z_PTR_P(value); + } + if (value && Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value)) { + return php_win32_cp_get_by_enc(Z_STRVAL_P(value)); + } + } + return php_win32_cp_get_by_id(CP_UTF8); +} + +static void php_ini_utf8_convert(php_ini_utf8_context *ctx, zval *value, const struct php_win32_cp *from, const struct php_win32_cp *to) +{ + if (ctx->failed || Z_TYPE_P(value) != IS_STRING || from == to) { + return; + } + size_t length, wide_len; + wchar_t *wide = php_win32_cp_conv_to_w(from->id, from->to_w_fl, Z_STRVAL_P(value), Z_STRLEN_P(value), &wide_len); + char *bytes = NULL; + if (wide) { + bytes = php_win32_cp_conv_from_w(to->id, to->from_w_fl, wide, wide_len, &length); + free(wide); + } + if (!bytes) { + ctx->failed = true; + return; + } + zend_string_release(Z_STR_P(value)); + ZVAL_STR(value, zend_string_init(bytes, length, true)); + free(bytes); +} + +static zval *php_ini_utf8_get_config(zend_string *name) +{ + php_ini_utf8_context *ctx = php_ini_utf8; + zval *value = zend_hash_find(&ctx->values, name); + if (!value || Z_TYPE_P(value) != IS_PTR) { + return value; + } + value = Z_PTR_P(value); + if (value && Z_TYPE_P(value) == IS_STRING) { + zval converted; + ZVAL_STR(&converted, zend_string_copy(Z_STR_P(value))); + php_ini_utf8_convert(ctx, &converted, ctx->cp, ctx->utf8); + return zend_hash_update(&ctx->values, name, &converted); + } + return value; +} + +static char *php_ini_utf8_getenv(const char *name, size_t name_len) +{ + wchar_t *wide_name = php_win32_cp_conv_utf8_to_w(name, name_len, PHP_WIN32_CP_IGNORE_LEN_P); + if (!wide_name) { + php_ini_utf8->failed = true; + return estrdup(""); + } + const wchar_t *value = _wgetenv(wide_name); + free(wide_name); + if (!value) { + return NULL; + } + char *bytes = php_win32_cp_w_to_utf8(value); + if (!bytes) { + php_ini_utf8->failed = true; + return estrdup(""); + } + char *result = estrdup(bytes); + free(bytes); + return result; +} + +static void php_ini_utf8_entry_dtor(void *data) +{ + php_ini_utf8_entry *entry = data; + for (int i = 0; i < 3; i++) { + zval_internal_ptr_dtor(&entry->args[i]); + } +} + +static void php_ini_utf8_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callback_type, void *data) +{ + php_ini_utf8_context *ctx = data; + php_ini_utf8_entry entry; + zval *args[3] = {arg1, arg2, arg3}; + for (int i = 0; i < 3; i++) { + if (args[i]) { + ZVAL_STR(&entry.args[i], zend_string_dup(Z_STR_P(args[i]), true)); + } else { + ZVAL_UNDEF(&entry.args[i]); + } + } + entry.callback_type = callback_type; + zend_llist_add_element(&ctx->entries, &entry); + /* Keep earlier -d values available for expansion, without queuing extensions twice. */ + if (callback_type == ZEND_INI_PARSER_ENTRY && !is_special_section + && (zend_string_equals_literal_ci(Z_STR_P(arg1), PHP_EXTENSION_TOKEN) + || zend_string_equals_literal_ci(Z_STR_P(arg1), ZEND_EXTENSION_TOKEN))) { + return; + } + php_ini_parser_cb(arg1, arg2, arg3, callback_type, &ctx->values); +} + +static zend_result php_ini_parse_utf8(const char *entries) +{ + php_ini_utf8_context ctx = {0}; + char *(*getenv_save)(const char *, size_t) = zend_getenv; + zend_hash_init(&ctx.values, 8, NULL, config_zval_dtor, true); + zend_llist_init(&ctx.entries, sizeof(php_ini_utf8_entry), php_ini_utf8_entry_dtor, true); + ctx.cp = php_ini_utf8_codepage(&ctx.values); + ctx.utf8 = php_win32_cp_get_by_id(CP_UTF8); + if (!ctx.cp || !ctx.utf8) { + ctx.failed = true; + goto cleanup; + } + /* Borrow php.ini scalars; section arrays only need their structure while parsing. */ + zend_string *key; + zval *value; + ZEND_HASH_FOREACH_STR_KEY_VAL(&configuration_hash, key, value) { + if (key) { + zval name, borrowed; + ZVAL_STR(&name, zend_string_copy(key)); + php_ini_utf8_convert(&ctx, &name, ctx.cp, ctx.utf8); + if (Z_TYPE_P(value) == IS_ARRAY) { + ZVAL_NEW_PERSISTENT_ARR(&borrowed); + zend_hash_init(Z_ARRVAL(borrowed), 0, NULL, config_zval_dtor, true); + } else { + ZVAL_PTR(&borrowed, value); + } + zend_hash_update(&ctx.values, Z_STR(name), &borrowed); + zend_string_release(Z_STR(name)); + } + } ZEND_HASH_FOREACH_END(); + php_ini_utf8 = &ctx; + zend_getenv = php_ini_utf8_getenv; + /* Parse in UTF-8, then apply the values after resolving the final code page. */ + zend_parse_ini_string(entries, true, ZEND_INI_SCANNER_NORMAL, php_ini_utf8_parser_cb, &ctx); + zend_getenv = getenv_save; + php_ini_utf8 = NULL; + RESET_ACTIVE_INI_HASH(); + ctx.cp = php_ini_utf8_codepage(&ctx.values); + ctx.failed |= !ctx.cp; + for (zend_llist_element *element = ctx.entries.head; element && !ctx.failed; element = element->next) { + php_ini_utf8_entry *entry = (php_ini_utf8_entry *) element->data; + for (int i = 0; i < 3; i++) { + php_ini_utf8_convert(&ctx, &entry->args[i], ctx.utf8, ctx.cp); + } + if (!ctx.failed) { + php_ini_parser_cb(&entry->args[0], Z_ISUNDEF(entry->args[1]) ? NULL : &entry->args[1], + Z_ISUNDEF(entry->args[2]) ? NULL : &entry->args[2], entry->callback_type, &configuration_hash); + } + } +cleanup: + zend_llist_destroy(&ctx.entries); + zend_hash_destroy(&ctx.values); + if (ctx.failed) { + php_error(E_CORE_WARNING, "Could not convert command line INI values to the configured Windows code page"); + return FAILURE; + } + return SUCCESS; +} +#endif + /* {{{ php_load_php_extension_cb */ static void php_load_php_extension_cb(void *arg) { @@ -404,7 +590,7 @@ static void append_ini_path(char *php_ini_search_path, size_t search_path_size, } /* {{{ php_init_config */ -void php_init_config(void) +zend_result php_init_config(void) { char *php_ini_file_name = NULL; char *php_ini_search_path = NULL; @@ -716,8 +902,15 @@ void php_init_config(void) if (sapi_module.ini_entries) { /* Reset active ini section */ RESET_ACTIVE_INI_HASH(); + +#ifdef PHP_WIN32 + if (sapi_module.ini_entries_utf8) { + return php_ini_parse_utf8(sapi_module.ini_entries); + } +#endif zend_parse_ini_string(sapi_module.ini_entries, true, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash); } + return SUCCESS; } /* }}} */ @@ -876,6 +1069,11 @@ PHPAPI void php_ini_activate_per_host_config(const char *host, size_t host_len) /* {{{ cfg_get_entry */ PHPAPI zval *cfg_get_entry_ex(zend_string *name) { +#ifdef PHP_WIN32 + if (php_ini_utf8) { + return php_ini_utf8_get_config(name); + } +#endif return zend_hash_find(&configuration_hash, name); } /* }}} */ diff --git a/main/php_ini.h b/main/php_ini.h index ffc2c36186c9..6d485444483f 100644 --- a/main/php_ini.h +++ b/main/php_ini.h @@ -19,7 +19,7 @@ BEGIN_EXTERN_C() PHPAPI void config_zval_dtor(zval *zvalue); -void php_init_config(void); +zend_result php_init_config(void); void php_shutdown_config(void); void php_ini_register_extensions(void); PHPAPI zval *cfg_get_entry_ex(zend_string *name); diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c index 49477e14bf17..3f94201d1c06 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -1182,13 +1182,36 @@ static int do_cli(int argc, char **argv) /* {{{ */ } /* }}} */ +#ifdef PHP_WIN32 +static char **php_cli_utf8_command_line(int *argc) +{ + wchar_t **wide = CommandLineToArgvW(GetCommandLineW(), argc); + char **args = wide ? calloc((size_t) *argc + 1, sizeof(char *)) : NULL; + + if (args) { + for (int i = 0; i < *argc; i++) { + args[i] = php_win32_cp_w_to_utf8(wide[i]); + if (!args[i]) { + PHP_WIN32_CP_FREE_ARRAY(args, *argc); + args = NULL; + break; + } + } + } + if (wide) { + LocalFree(wide); + } + return args; +} +#endif + /* {{{ do_php_cli */ PHP_CLI_API int do_php_cli(int argc, char *argv[]) { #if defined(PHP_WIN32) - int num_args; - wchar_t **argv_wide = NULL; - char **argv_save = argv; + char **native_argv = NULL; + char **argv_save; + BOOL using_converted_argv = 0; #endif int c; @@ -1201,6 +1224,17 @@ PHP_CLI_API int do_php_cli(int argc, char *argv[]) int ini_ignore = 0; sapi_module_struct *sapi_module_ptr = &cli_sapi_module; +#ifdef PHP_WIN32 + if (argv == __argv) { + native_argv = php_cli_utf8_command_line(&argc); + if (!native_argv) { + return 1; + } + argv = native_argv; + } + argv_save = argv; +#endif + /* * Do not move this initialization. It needs to happen before argv is used * in any way. @@ -1208,13 +1242,14 @@ PHP_CLI_API int do_php_cli(int argc, char *argv[]) argv = save_ps_args(argc, argv); #ifdef PHP_WIN32 - if (argv_save != __argv) { - for (int i = 0; i < argc; i++) { - if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, argv[i], -1, NULL, 0)) { - fprintf(stderr, "Invalid UTF-8 in command line argument %d.\n", i); - cleanup_ps_args(argv); - return 1; + for (int i = 0; i < argc; i++) { + if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, argv[i], -1, NULL, 0)) { + fprintf(stderr, "Invalid UTF-8 in command line argument %d.\n", i); + cleanup_ps_args(argv); + if (native_argv) { + PHP_WIN32_CP_FREE_ARRAY(native_argv, argc); } + return 1; } } #endif @@ -1313,6 +1348,9 @@ PHP_CLI_API int do_php_cli(int argc, char *argv[]) exit_loop: sapi_module_ptr->ini_defaults = sapi_cli_ini_defaults; +#ifdef PHP_WIN32 + sapi_module_ptr->ini_entries_utf8 = true; +#endif sapi_module_ptr->php_ini_path_override = ini_path_override; sapi_module_ptr->phpinfo_as_text = 1; sapi_module_ptr->php_ini_ignore_cwd = 1; @@ -1344,36 +1382,26 @@ PHP_CLI_API int do_php_cli(int argc, char *argv[]) #if defined(PHP_WIN32) php_win32_cp_cli_setup(); orig_cp = (php_win32_cp_get_orig())->id; - /* Embedders supply their own arguments, we mustn't replace them with - * the ones by the host process command line. */ - if (argv_save != __argv) { - if (!php_win32_cp_use_unicode()) { - /* Custom arguments must be passed as UTF-8, convert to PHP's internal encvodingg. */ - char **converted_argv = calloc((size_t) argc + 1, sizeof(char *)); - if (!converted_argv) { + if (!php_win32_cp_use_unicode()) { + char **converted_argv = calloc((size_t) argc + 1, sizeof(char *)); + if (!converted_argv) { + exit_status = 1; + goto out; + } + argv = converted_argv; + using_converted_argv = 1; + for (int i = 0; i < argc; i++) { + wchar_t *wide = php_win32_cp_utf8_to_w(argv_save[i]); + if (wide) { + argv[i] = php_win32_cp_w_to_any(wide); + free(wide); + } + if (!argv[i]) { + fprintf(stderr, "Could not convert command line argument %d.\n", i); exit_status = 1; goto out; } - argv = converted_argv; - for (int i = 0; i < argc; i++) { - wchar_t *arg = php_win32_cp_utf8_to_w(argv_save[i]); - if (arg) { - argv[i] = php_win32_cp_w_to_any(arg); - free(arg); - } - if (!argv[i]) { - fprintf(stderr, "Could not convert command line argument %d.\n", i); - exit_status = 1; - goto out; - } - } } - } else { - /* Ignore the delivered argv and argc, read from W API. This place - might be too late though, but this is the earliest place ATW - we can access the internal charset information from PHP. */ - argv_wide = CommandLineToArgvW(GetCommandLineW(), &num_args); - PHP_WIN32_CP_W_TO_ANY_ARRAY(argv_wide, num_args, argv, argc) } SetConsoleCtrlHandler(php_cli_win32_ctrl_handler, TRUE); @@ -1413,12 +1441,9 @@ PHP_CLI_API int do_php_cli(int argc, char *argv[]) #if defined(PHP_WIN32) (void)php_win32_cp_cli_restore(); - if (argv != argv_save) { + if (using_converted_argv) { PHP_WIN32_CP_FREE_ARRAY(argv, argc); } - if (argv_wide) { - LocalFree(argv_wide); - } argv = argv_save; #endif /* @@ -1426,6 +1451,11 @@ PHP_CLI_API int do_php_cli(int argc, char *argv[]) * exiting. */ cleanup_ps_args(argv); +#ifdef PHP_WIN32 + if (native_argv) { + PHP_WIN32_CP_FREE_ARRAY(native_argv, argc); + } +#endif return exit_status; } /* }}} */ diff --git a/sapi/embed/tests/cli_argv.c b/sapi/embed/tests/cli_argv.c index 0700011d975b..9034fbcb4c7b 100644 --- a/sapi/embed/tests/cli_argv.c +++ b/sapi/embed/tests/cli_argv.c @@ -3,11 +3,58 @@ int main(int argc, char *argv[]) { + char *prepend_argv[] = { + "embedded-php", "-n", "-d", "auto_prepend_file=cli_argv_caf\xc3\xa9.php", + "-d", "default_charset=Windows-1252", "-f", "cli_argv_main.php", NULL + }; + char *prepend_ini_argv[] = { + "embedded-php", "-c", "cli_argv_test.ini", "-d", + "auto_prepend_file=cli_argv_caf\xc3\xa9.php", "-f", "cli_argv_main.php", NULL + }; + char *ini_expansion_argv[] = { + "embedded-php", "-n", "-d", "prefix=cli_ini_caf\xc3\xa9", + "-d", "caf\xc3\xa9=cli_ini_caf\xc3\xa9", "-d", "auto_prepend_file=${prefix}.php", + "-d", "include_path=${caf\xc3\xa9}", "-d", "user_agent=${CLI_INI_UTF8}", + "-d", "alias=Windows-1252", "-d", "default_charset=${alias}", "-f", "cli_ini_utf8.php", NULL + }; + char *ini_file_expansion_argv[] = { + "embedded-php", "-c", "cli_ini_utf8.ini", "-d", "auto_prepend_file=${prefix}${suffix}.php", + "-d", "include_path=cli_ini_caf\xc3\xa9${suffix}", "-d", "user_agent=${caf\xc3\xa9}", + "-f", "cli_ini_utf8.php", NULL + }; + char *ini_cp932_argv[] = { + "embedded-php", "-n", "-d", "auto_prepend_file=cli_ini_cp932.php", + "-d", "include_path=\xe3\x82\xbd\\main.php", "-d", "token=tail", + "-d", "user_agent=\xe3\x82\xbd${token}", "-d", "default_charset=CP932", "-f", "cli_ini_utf8.php", NULL + }; + char *ini_section_argv[] = { + "embedded-php", "-c", "cli_ini_utf8.ini", "-d", "before=1\n[PATH=collision]\ndefault_charset=UTF-8", + "-d", "auto_prepend_file=${prefix}.php", "-d", "include_path=${prefix}", + "-d", "user_agent=${CLI_INI_UTF8}", "-f", "cli_ini_utf8.php", NULL + }; char *php_argv[] = { "embedded-php", "-n", "-d", "default_charset=UTF-8", "-r", - "echo json_encode([$argc, array_map('bin2hex', array_slice($argv, 1))]), PHP_EOL; exit(23);", + "echo json_encode([$argc, array_map('bin2hex', array_slice($argv, 1))]), PHP_EOL; echo sapi_windows_cp_get(), PHP_EOL; exit(23);", "--", "caf\xc3\xa9", "argument with spaces", "", "\xf0\x9f\x98\x80", NULL }; + if (argc > 1 && strcmp(argv[1], "prepend") == 0) { + return do_php_cli((int) (sizeof(prepend_argv) / sizeof(*prepend_argv)) - 1, prepend_argv); + } + if (argc > 1 && strcmp(argv[1], "prepend-ini") == 0) { + return do_php_cli((int) (sizeof(prepend_ini_argv) / sizeof(*prepend_ini_argv)) - 1, prepend_ini_argv); + } + if (argc > 1 && strcmp(argv[1], "ini-expansion") == 0) { + return do_php_cli((int) (sizeof(ini_expansion_argv) / sizeof(*ini_expansion_argv)) - 1, ini_expansion_argv); + } + if (argc > 1 && strcmp(argv[1], "ini-file-expansion") == 0) { + return do_php_cli((int) (sizeof(ini_file_expansion_argv) / sizeof(*ini_file_expansion_argv)) - 1, ini_file_expansion_argv); + } + if (argc > 1 && strcmp(argv[1], "ini-cp932") == 0) { + return do_php_cli((int) (sizeof(ini_cp932_argv) / sizeof(*ini_cp932_argv)) - 1, ini_cp932_argv); + } + if (argc > 1 && strcmp(argv[1], "ini-section") == 0) { + return do_php_cli((int) (sizeof(ini_section_argv) / sizeof(*ini_section_argv)) - 1, ini_section_argv); + } if (argc > 1 && strcmp(argv[1], "default-charset") == 0) { php_argv[3] = "default_charset=Windows-1252"; php_argv[10] = "\xe2\x82\xac"; diff --git a/sapi/embed/tests/cli_argv.phpt b/sapi/embed/tests/cli_argv.phpt index f24f265f3090..65bb8cd05c3c 100644 --- a/sapi/embed/tests/cli_argv.phpt +++ b/sapi/embed/tests/cli_argv.phpt @@ -1,5 +1,5 @@ --TEST-- -do_php_cli() validates and converts custom UTF-8 argv on Windows +Windows CLI and embed argv use the configured code page after UTF-8 option parsing --SKIPIF-- --CLEAN-- --EXPECT-- [5,["636166c3a9","617267756d656e74207769746820737061636573","","f09f9880"]] +65001 int(23) Invalid UTF-8 in command line argument 7. int(1) [5,["636166e9","617267756d656e74207769746820737061636573","","80"]] +1252 int(23) Invalid UTF-8 in command line argument 7. int(1) [5,["636166e9","617267756d656e74207769746820737061636573","","80"]] +1252 int(23) Invalid UTF-8 in command line argument 7. int(1) Unicode filename works int(0) +Unicode filename works +Main script works +1252 +636c695f617267765f636166e92e706870 +int(0) +Unicode filename works +Main script works +1252 +636c695f617267765f636166e92e706870 +int(0) +Unicode filename works +Main script works +65001 +636c695f617267765f636166c3a92e706870 +int(0) +Unicode filename works +Main script works +1252 +636c695f617267765f636166e92e706870 +int(0) diff --git a/sapi/embed/tests/cli_ini_utf8.phpt b/sapi/embed/tests/cli_ini_utf8.phpt new file mode 100644 index 000000000000..dd9f4fe32f13 --- /dev/null +++ b/sapi/embed/tests/cli_ini_utf8.phpt @@ -0,0 +1,111 @@ +--TEST-- +Windows CLI INI expansion preserves UTF-8 until the final code page is selected +--SKIPIF-- + +--FILE-- + [ + '-n', '-d', "prefix=cli_ini_caf\xc3\xa9", '-d', "caf\xc3\xa9=cli_ini_caf\xc3\xa9", + '-d', 'auto_prepend_file=${prefix}.php', '-d', 'include_path=${café}', + '-d', 'user_agent=${CLI_INI_UTF8}', '-d', 'alias=Windows-1252', + '-d', 'default_charset=${alias}', '-f', 'cli_ini_utf8.php', + ], + 'ini-file-expansion' => [ + '-c', 'cli_ini_utf8.ini', '-d', 'auto_prepend_file=${prefix}${suffix}.php', + '-d', "include_path=cli_ini_caf\xc3\xa9" . '${suffix}', '-d', 'user_agent=${café}', + '-f', 'cli_ini_utf8.php', + ], + 'ini-cp932' => [ + '-n', '-d', 'auto_prepend_file=cli_ini_cp932.php', + '-d', 'include_path=ソ\main.php', '-d', 'token=tail', + '-d', 'user_agent=ソ${token}', '-d', 'default_charset=CP932', '-f', 'cli_ini_utf8.php', + ], + 'ini-section' => [ + '-c', 'cli_ini_utf8.ini', '-d', "before=1\n[PATH=collision]\ndefault_charset=UTF-8", + '-d', 'auto_prepend_file=${prefix}.php', '-d', 'include_path=${prefix}', + '-d', 'user_agent=${CLI_INI_UTF8}', '-f', 'cli_ini_utf8.php', + ], +]; +foreach ($cases as $mode => $options) { + passthru("$host $mode", $status); + var_dump($status); + $process = proc_open([PHP_BINARY, ...$options], [1 => STDOUT, 2 => STDERR], $pipes); + var_dump(proc_close($process)); +} +?> +--CLEAN-- + +--EXPECT-- +Prepend works +1252 +636c695f696e695f636166e92e706870 +636c695f696e695f636166e9 +636166e9 +int(0) +Prepend works +1252 +636c695f696e695f636166e92e706870 +636c695f696e695f636166e9 +636166e9 +int(0) +Prepend works +1252 +636c695f696e695f636166e9e92e706870 +636c695f696e695f636166e9e9 +636c695f696e695f636166e9e9 +int(0) +Prepend works +1252 +636c695f696e695f636166e9e92e706870 +636c695f696e695f636166e9e9 +636c695f696e695f636166e9e9 +int(0) +Prepend works +932 +636c695f696e695f63703933322e706870 +835c5c6d61696e2e706870 +835c7461696c +int(0) +Prepend works +932 +636c695f696e695f63703933322e706870 +835c5c6d61696e2e706870 +835c7461696c +int(0) +Prepend works +65001 +636c695f696e695f636166c3a92e706870 +636c695f696e695f636166c3a9 +636166c3a9 +int(0) +Prepend works +65001 +636c695f696e695f636166c3a92e706870 +636c695f696e695f636166c3a9 +636166c3a9 +int(0) From 3b8b6fa6017c6830db4f98f5c524de2904e7df26 Mon Sep 17 00:00:00 2001 From: Marc Date: Tue, 6 Oct 2026 15:19:15 +0200 Subject: [PATCH 06/13] fix hover --- sapi/cli/cli.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sapi/cli/cli.h b/sapi/cli/cli.h index a3f241e84669..a2ee8475d9db 100644 --- a/sapi/cli/cli.h +++ b/sapi/cli/cli.h @@ -54,8 +54,8 @@ typedef struct php_cli_server_context { php_cli_mode mode; } php_cli_server_context; -/* this performs full cli-SAPI boot, loads modules, sets up TSRM and co. */ -/* On Windows, custom argv strings must be UTF-8. */ +/** This performs full cli-SAPI boot, loads modules, sets up TSRM and co. + * On Windows, custom argv strings must be UTF-8. */ extern PHP_CLI_API int do_php_cli(int argc, char *argv[]); #endif /* CLI_H */ From dc17926fdefb21e7f2f28487b15a249711941f66 Mon Sep 17 00:00:00 2001 From: Marc Date: Tue, 6 Oct 2026 16:20:58 +0200 Subject: [PATCH 07/13] fix double quoting splitting --- main/main.c | 2 +- main/php_ini.c | 5 +--- sapi/cli/php_cli.c | 23 +++++++++---------- .../tests/windows_ini_quoted_argument.phpt | 20 ++++++++++++++++ sapi/embed/tests/cli_argv.c | 3 +++ sapi/embed/tests/cli_argv.phpt | 14 ++++++++++- 6 files changed, 49 insertions(+), 18 deletions(-) create mode 100644 sapi/cli/tests/windows_ini_quoted_argument.phpt diff --git a/main/main.c b/main/main.c index 834dc884393f..594626470a50 100644 --- a/main/main.c +++ b/main/main.c @@ -644,7 +644,7 @@ static PHP_INI_MH(OnUpdateInternalEncoding) } if (new_value) { #ifdef PHP_WIN32 - php_win32_cp_do_update(ZSTR_VAL(new_value)); + php_win32_cp_do_update(NULL); #endif } return SUCCESS; diff --git a/main/php_ini.c b/main/php_ini.c index 31a234b2f632..357d0b0fe551 100644 --- a/main/php_ini.c +++ b/main/php_ini.c @@ -329,9 +329,6 @@ static const struct php_win32_cp *php_ini_utf8_codepage(HashTable *hash) const char *names[] = {"internal_encoding", "default_charset"}; for (int i = 0; i < 2; i++) { zval *value = zend_hash_str_find(hash, names[i], strlen(names[i])); - if (!value) { - value = zend_hash_str_find(&configuration_hash, names[i], strlen(names[i])); - } if (value && Z_TYPE_P(value) == IS_PTR) { value = Z_PTR_P(value); } @@ -439,7 +436,7 @@ static zend_result php_ini_parse_utf8(const char *entries) char *(*getenv_save)(const char *, size_t) = zend_getenv; zend_hash_init(&ctx.values, 8, NULL, config_zval_dtor, true); zend_llist_init(&ctx.entries, sizeof(php_ini_utf8_entry), php_ini_utf8_entry_dtor, true); - ctx.cp = php_ini_utf8_codepage(&ctx.values); + ctx.cp = php_ini_utf8_codepage(&configuration_hash); ctx.utf8 = php_win32_cp_get_by_id(CP_UTF8); if (!ctx.cp || !ctx.utf8) { ctx.failed = true; diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c index 3f94201d1c06..2abab3adcd39 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -34,7 +34,7 @@ #include "win32/signal.h" #include "win32/console.h" #include -#include +#include #endif #ifdef HAVE_SYS_TIME_H #include @@ -1185,12 +1185,15 @@ static int do_cli(int argc, char **argv) /* {{{ */ #ifdef PHP_WIN32 static char **php_cli_utf8_command_line(int *argc) { - wchar_t **wide = CommandLineToArgvW(GetCommandLineW(), argc); - char **args = wide ? calloc((size_t) *argc + 1, sizeof(char *)) : NULL; + if (!__wargv && _configure_wide_argv(_crt_argv_unexpanded_arguments)) { + return NULL; + } + *argc = __argc; + char **args = calloc((size_t) *argc + 1, sizeof(char *)); if (args) { for (int i = 0; i < *argc; i++) { - args[i] = php_win32_cp_w_to_utf8(wide[i]); + args[i] = php_win32_cp_w_to_utf8(__wargv[i]); if (!args[i]) { PHP_WIN32_CP_FREE_ARRAY(args, *argc); args = NULL; @@ -1198,9 +1201,6 @@ static char **php_cli_utf8_command_line(int *argc) } } } - if (wide) { - LocalFree(wide); - } return args; } #endif @@ -1210,8 +1210,8 @@ PHP_CLI_API int do_php_cli(int argc, char *argv[]) { #if defined(PHP_WIN32) char **native_argv = NULL; + char **converted_argv = NULL; char **argv_save; - BOOL using_converted_argv = 0; #endif int c; @@ -1383,13 +1383,12 @@ PHP_CLI_API int do_php_cli(int argc, char *argv[]) php_win32_cp_cli_setup(); orig_cp = (php_win32_cp_get_orig())->id; if (!php_win32_cp_use_unicode()) { - char **converted_argv = calloc((size_t) argc + 1, sizeof(char *)); + converted_argv = calloc((size_t) argc + 1, sizeof(char *)); if (!converted_argv) { exit_status = 1; goto out; } argv = converted_argv; - using_converted_argv = 1; for (int i = 0; i < argc; i++) { wchar_t *wide = php_win32_cp_utf8_to_w(argv_save[i]); if (wide) { @@ -1441,8 +1440,8 @@ PHP_CLI_API int do_php_cli(int argc, char *argv[]) #if defined(PHP_WIN32) (void)php_win32_cp_cli_restore(); - if (using_converted_argv) { - PHP_WIN32_CP_FREE_ARRAY(argv, argc); + if (converted_argv) { + PHP_WIN32_CP_FREE_ARRAY(converted_argv, argc); } argv = argv_save; #endif diff --git a/sapi/cli/tests/windows_ini_quoted_argument.phpt b/sapi/cli/tests/windows_ini_quoted_argument.phpt new file mode 100644 index 000000000000..e5648c16f75f --- /dev/null +++ b/sapi/cli/tests/windows_ini_quoted_argument.phpt @@ -0,0 +1,20 @@ +--TEST-- +Windows CLI INI options preserve CRT doubled-quote escaping +--SKIPIF-- + +--FILE-- + ['pipe', 'w'], 2 => ['redirect', 1]], $pipes, + null, null, ['bypass_shell' => true]); +echo stream_get_contents($pipes[1]); +fclose($pipes[1]); +var_dump(proc_close($process)); +?> +--EXPECT-- +68656c6c6f20776f726c64 +int(0) diff --git a/sapi/embed/tests/cli_argv.c b/sapi/embed/tests/cli_argv.c index 9034fbcb4c7b..40fb718f771b 100644 --- a/sapi/embed/tests/cli_argv.c +++ b/sapi/embed/tests/cli_argv.c @@ -61,6 +61,9 @@ int main(int argc, char *argv[]) } else if (argc > 1 && strcmp(argv[1], "internal-encoding") == 0) { php_argv[3] = "internal_encoding=Windows-1252"; php_argv[10] = "\xe2\x82\xac"; + } else if (argc > 1 && strcmp(argv[1], "empty-internal-encoding") == 0) { + php_argv[3] = "default_charset=Windows-1252\ninternal_encoding="; + php_argv[10] = "\xe2\x82\xac"; } if (argc > 2 && strcmp(argv[2], "file") == 0) { php_argv[4] = "-f"; diff --git a/sapi/embed/tests/cli_argv.phpt b/sapi/embed/tests/cli_argv.phpt index 65bb8cd05c3c..426c81c9b840 100644 --- a/sapi/embed/tests/cli_argv.phpt +++ b/sapi/embed/tests/cli_argv.phpt @@ -10,7 +10,7 @@ if (!file_exists(dirname(PHP_BINARY) . '/php_embed_cli_test.exe')) { --FILE-- &1', $status); @@ -32,6 +32,8 @@ passthru("$native -n -d $prepend -f cli_argv_main.php", $status); var_dump($status); passthru("$native -n -d default_charset=Windows-1252 -d $prepend -f cli_argv_main.php", $status); var_dump($status); +passthru("$native -n -d default_charset=Windows-1252 -d internal_encoding= -d $prepend -f cli_argv_main.php", $status); +var_dump($status); ?> --CLEAN-- Date: Tue, 6 Oct 2026 18:28:40 +0200 Subject: [PATCH 08/13] move test args to test files --- sapi/embed/tests/cli_argv.c | 95 ++++++++---------------------- sapi/embed/tests/cli_argv.phpt | 60 +++++++++++-------- sapi/embed/tests/cli_ini_utf8.phpt | 8 +-- 3 files changed, 62 insertions(+), 101 deletions(-) diff --git a/sapi/embed/tests/cli_argv.c b/sapi/embed/tests/cli_argv.c index 40fb718f771b..d40103962a9d 100644 --- a/sapi/embed/tests/cli_argv.c +++ b/sapi/embed/tests/cli_argv.c @@ -1,76 +1,29 @@ #include "php.h" #include "sapi/cli/cli.h" +#include "win32/codepage.h" -int main(int argc, char *argv[]) +int wmain(int argc, wchar_t *argv[]) { - char *prepend_argv[] = { - "embedded-php", "-n", "-d", "auto_prepend_file=cli_argv_caf\xc3\xa9.php", - "-d", "default_charset=Windows-1252", "-f", "cli_argv_main.php", NULL - }; - char *prepend_ini_argv[] = { - "embedded-php", "-c", "cli_argv_test.ini", "-d", - "auto_prepend_file=cli_argv_caf\xc3\xa9.php", "-f", "cli_argv_main.php", NULL - }; - char *ini_expansion_argv[] = { - "embedded-php", "-n", "-d", "prefix=cli_ini_caf\xc3\xa9", - "-d", "caf\xc3\xa9=cli_ini_caf\xc3\xa9", "-d", "auto_prepend_file=${prefix}.php", - "-d", "include_path=${caf\xc3\xa9}", "-d", "user_agent=${CLI_INI_UTF8}", - "-d", "alias=Windows-1252", "-d", "default_charset=${alias}", "-f", "cli_ini_utf8.php", NULL - }; - char *ini_file_expansion_argv[] = { - "embedded-php", "-c", "cli_ini_utf8.ini", "-d", "auto_prepend_file=${prefix}${suffix}.php", - "-d", "include_path=cli_ini_caf\xc3\xa9${suffix}", "-d", "user_agent=${caf\xc3\xa9}", - "-f", "cli_ini_utf8.php", NULL - }; - char *ini_cp932_argv[] = { - "embedded-php", "-n", "-d", "auto_prepend_file=cli_ini_cp932.php", - "-d", "include_path=\xe3\x82\xbd\\main.php", "-d", "token=tail", - "-d", "user_agent=\xe3\x82\xbd${token}", "-d", "default_charset=CP932", "-f", "cli_ini_utf8.php", NULL - }; - char *ini_section_argv[] = { - "embedded-php", "-c", "cli_ini_utf8.ini", "-d", "before=1\n[PATH=collision]\ndefault_charset=UTF-8", - "-d", "auto_prepend_file=${prefix}.php", "-d", "include_path=${prefix}", - "-d", "user_agent=${CLI_INI_UTF8}", "-f", "cli_ini_utf8.php", NULL - }; - char *php_argv[] = { - "embedded-php", "-n", "-d", "default_charset=UTF-8", "-r", - "echo json_encode([$argc, array_map('bin2hex', array_slice($argv, 1))]), PHP_EOL; echo sapi_windows_cp_get(), PHP_EOL; exit(23);", - "--", "caf\xc3\xa9", "argument with spaces", "", "\xf0\x9f\x98\x80", NULL - }; - if (argc > 1 && strcmp(argv[1], "prepend") == 0) { - return do_php_cli((int) (sizeof(prepend_argv) / sizeof(*prepend_argv)) - 1, prepend_argv); - } - if (argc > 1 && strcmp(argv[1], "prepend-ini") == 0) { - return do_php_cli((int) (sizeof(prepend_ini_argv) / sizeof(*prepend_ini_argv)) - 1, prepend_ini_argv); - } - if (argc > 1 && strcmp(argv[1], "ini-expansion") == 0) { - return do_php_cli((int) (sizeof(ini_expansion_argv) / sizeof(*ini_expansion_argv)) - 1, ini_expansion_argv); - } - if (argc > 1 && strcmp(argv[1], "ini-file-expansion") == 0) { - return do_php_cli((int) (sizeof(ini_file_expansion_argv) / sizeof(*ini_file_expansion_argv)) - 1, ini_file_expansion_argv); - } - if (argc > 1 && strcmp(argv[1], "ini-cp932") == 0) { - return do_php_cli((int) (sizeof(ini_cp932_argv) / sizeof(*ini_cp932_argv)) - 1, ini_cp932_argv); - } - if (argc > 1 && strcmp(argv[1], "ini-section") == 0) { - return do_php_cli((int) (sizeof(ini_section_argv) / sizeof(*ini_section_argv)) - 1, ini_section_argv); - } - if (argc > 1 && strcmp(argv[1], "default-charset") == 0) { - php_argv[3] = "default_charset=Windows-1252"; - php_argv[10] = "\xe2\x82\xac"; - } else if (argc > 1 && strcmp(argv[1], "internal-encoding") == 0) { - php_argv[3] = "internal_encoding=Windows-1252"; - php_argv[10] = "\xe2\x82\xac"; - } else if (argc > 1 && strcmp(argv[1], "empty-internal-encoding") == 0) { - php_argv[3] = "default_charset=Windows-1252\ninternal_encoding="; - php_argv[10] = "\xe2\x82\xac"; - } - if (argc > 2 && strcmp(argv[2], "file") == 0) { - php_argv[4] = "-f"; - php_argv[5] = "cli_argv_caf\xc3\xa9.php"; - } else if (argc > 2 && strcmp(argv[2], "invalid-utf8") == 0) { - /* Latin-1 cafe with an accented e, which is not valid UTF-8. */ - php_argv[7] = "caf\xe9"; - } - return do_php_cli((int) (sizeof(php_argv) / sizeof(*php_argv)) - 1, php_argv); + if (argc < 2 || wcscmp(argv[1], L"--") != 0) { + return 1; + } + argc--; + char **args = calloc((size_t) argc + 1, sizeof(char *)); + if (!args) { + return 1; + } + /* Omit the host's separator so the supplied argv differs from the process argv. */ + for (int i = 0; i < argc; i++) { + args[i] = php_win32_cp_w_to_utf8(argv[i ? i + 1 : 0]); + if (!args[i]) { + PHP_WIN32_CP_FREE_ARRAY(args, argc); + return 1; + } + } + if (getenv("PHP_EMBED_TEST_INVALID_UTF8") && argc > 1 && args[argc - 1][0]) { + args[argc - 1][0] = '\xff'; + } + int status = do_php_cli(argc, args); + PHP_WIN32_CP_FREE_ARRAY(args, argc); + return status; } diff --git a/sapi/embed/tests/cli_argv.phpt b/sapi/embed/tests/cli_argv.phpt index 426c81c9b840..46b50642634b 100644 --- a/sapi/embed/tests/cli_argv.phpt +++ b/sapi/embed/tests/cli_argv.phpt @@ -10,30 +10,40 @@ if (!file_exists(dirname(PHP_BINARY) . '/php_embed_cli_test.exe')) { --FILE-- &1', $status); - var_dump($status); +function run_cli(array $command, bool $invalid_utf8 = false): void { + $env = $invalid_utf8 ? ['PHP_EMBED_TEST_INVALID_UTF8' => '1'] + getenv() : null; + $process = proc_open($command, [1 => STDOUT, 2 => STDOUT], $pipes, null, $env); + var_dump(proc_close($process)); } +$code = <<<'PHP' +echo json_encode([$argc, array_map('bin2hex', array_slice($argv, 1))]), PHP_EOL; +echo sapi_windows_cp_get(), PHP_EOL; +exit(23); +PHP; +foreach ([ + ['default_charset=UTF-8', "\xf0\x9f\x98\x80"], + ['default_charset=Windows-1252', "\xe2\x82\xac"], + ['internal_encoding=Windows-1252', "\xe2\x82\xac"], +] as [$setting, $last_arg]) { + $options = ['-n', '-d', $setting, '-r', $code, '--', "caf\xc3\xa9", 'argument with spaces', '', $last_arg]; + run_cli([$host, '--', ...$options]); + run_cli([$host, '--', ...$options], true); +} +run_cli([$host, '--', '-n', '-d', 'default_charset=Windows-1252', '-d', 'internal_encoding=', + '-d', "user_agent=caf\xc3\xa9", '-r', + "echo sapi_windows_cp_get(), PHP_EOL; echo bin2hex(ini_get('user_agent')), PHP_EOL;"]); chdir(__DIR__); file_put_contents("cli_argv_caf\xc3\xa9.php", ' --CLEAN-- $options) { - passthru("$host $mode", $status); - var_dump($status); +foreach ($cases as $options) { + $process = proc_open([$host, '--', ...$options], [1 => STDOUT, 2 => STDERR], $pipes); + var_dump(proc_close($process)); $process = proc_open([PHP_BINARY, ...$options], [1 => STDOUT, 2 => STDERR], $pipes); var_dump(proc_close($process)); } From dcd7d0b8a0e5c8fcda6e170b2907aa25f85410d2 Mon Sep 17 00:00:00 2001 From: Marc Date: Tue, 6 Oct 2026 19:36:34 +0200 Subject: [PATCH 09/13] fix failing utf 7 test --- main/php_ini.c | 23 ++++++++++++++++++++--- sapi/cli/tests/windows_ini_utf7.phpt | 17 +++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 sapi/cli/tests/windows_ini_utf7.phpt diff --git a/main/php_ini.c b/main/php_ini.c index 357d0b0fe551..fab2d7b692a5 100644 --- a/main/php_ini.c +++ b/main/php_ini.c @@ -339,13 +339,25 @@ static const struct php_win32_cp *php_ini_utf8_codepage(HashTable *hash) return php_win32_cp_get_by_id(CP_UTF8); } +static bool php_ini_utf8_extension_path(zend_string *name) +{ + return zend_string_equals_literal_ci(name, PHP_EXTENSION_TOKEN) + || zend_string_equals_literal_ci(name, ZEND_EXTENSION_TOKEN) + || zend_string_equals_literal(name, "extension_dir"); +} + static void php_ini_utf8_convert(php_ini_utf8_context *ctx, zval *value, const struct php_win32_cp *from, const struct php_win32_cp *to) { if (ctx->failed || Z_TYPE_P(value) != IS_STRING || from == to) { return; } + wchar_t *wide = php_win32_cp_conv_ascii_to_w(Z_STRVAL_P(value), Z_STRLEN_P(value), PHP_WIN32_CP_IGNORE_LEN_P); + if (wide) { + free(wide); + return; + } size_t length, wide_len; - wchar_t *wide = php_win32_cp_conv_to_w(from->id, from->to_w_fl, Z_STRVAL_P(value), Z_STRLEN_P(value), &wide_len); + wide = php_win32_cp_conv_to_w(from->id, from->to_w_fl, Z_STRVAL_P(value), Z_STRLEN_P(value), &wide_len); char *bytes = NULL; if (wide) { bytes = php_win32_cp_conv_from_w(to->id, to->from_w_fl, wide, wide_len, &length); @@ -371,7 +383,9 @@ static zval *php_ini_utf8_get_config(zend_string *name) if (value && Z_TYPE_P(value) == IS_STRING) { zval converted; ZVAL_STR(&converted, zend_string_copy(Z_STR_P(value))); - php_ini_utf8_convert(ctx, &converted, ctx->cp, ctx->utf8); + const struct php_win32_cp *cp = php_ini_utf8_extension_path(name) + ? php_win32_cp_get_by_id(CP_ACP) : ctx->cp; + php_ini_utf8_convert(ctx, &converted, cp, ctx->utf8); return zend_hash_update(&ctx->values, name, &converted); } return value; @@ -471,8 +485,11 @@ static zend_result php_ini_parse_utf8(const char *entries) ctx.failed |= !ctx.cp; for (zend_llist_element *element = ctx.entries.head; element && !ctx.failed; element = element->next) { php_ini_utf8_entry *entry = (php_ini_utf8_entry *) element->data; + const struct php_win32_cp *value_cp = entry->callback_type == ZEND_INI_PARSER_ENTRY + && php_ini_utf8_extension_path(Z_STR(entry->args[0])) + ? php_win32_cp_get_by_id(CP_ACP) : ctx.cp; for (int i = 0; i < 3; i++) { - php_ini_utf8_convert(&ctx, &entry->args[i], ctx.utf8, ctx.cp); + php_ini_utf8_convert(&ctx, &entry->args[i], ctx.utf8, i == 1 ? value_cp : ctx.cp); } if (!ctx.failed) { php_ini_parser_cb(&entry->args[0], Z_ISUNDEF(entry->args[1]) ? NULL : &entry->args[1], diff --git a/sapi/cli/tests/windows_ini_utf7.phpt b/sapi/cli/tests/windows_ini_utf7.phpt new file mode 100644 index 000000000000..6045da7858ed --- /dev/null +++ b/sapi/cli/tests/windows_ini_utf7.phpt @@ -0,0 +1,17 @@ +--TEST-- +Windows CLI INI conversion preserves ASCII names and values with UTF-7 +--SKIPIF-- + +--INI-- +internal_encoding=UTF-7 +user_agent="foo+bar" +--FILE-- + +--EXPECT-- +string(7) "foo+bar" +int(65000) From 5dd155143b8b66ce8620e7c7d390bbeaadf71063 Mon Sep 17 00:00:00 2001 From: Marc Date: Wed, 7 Oct 2026 08:33:57 +0200 Subject: [PATCH 10/13] move var declarations, remove first ifdef --- sapi/cli/php_cli.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c index 2abab3adcd39..6143d34b44ef 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -1208,12 +1208,6 @@ static char **php_cli_utf8_command_line(int *argc) /* {{{ do_php_cli */ PHP_CLI_API int do_php_cli(int argc, char *argv[]) { -#if defined(PHP_WIN32) - char **native_argv = NULL; - char **converted_argv = NULL; - char **argv_save; -#endif - int c; int exit_status = SUCCESS; int module_started = 0, sapi_started = 0; @@ -1225,6 +1219,10 @@ PHP_CLI_API int do_php_cli(int argc, char *argv[]) sapi_module_struct *sapi_module_ptr = &cli_sapi_module; #ifdef PHP_WIN32 + char **native_argv = NULL; + char **converted_argv = NULL; + char **argv_save; + if (argv == __argv) { native_argv = php_cli_utf8_command_line(&argc); if (!native_argv) { From c6f4dd713adfc2afd51ac5ea787a194dc95fdac1 Mon Sep 17 00:00:00 2001 From: Marc Date: Wed, 7 Oct 2026 09:17:15 +0200 Subject: [PATCH 11/13] move codepage stuff --- sapi/cli/php_cli.c | 43 ++++++------------------------------------- win32/codepage.h | 43 ++++++++++++++++++++++++++++++------------- 2 files changed, 36 insertions(+), 50 deletions(-) diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c index 6143d34b44ef..cdf65bc6f063 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -1182,29 +1182,6 @@ static int do_cli(int argc, char **argv) /* {{{ */ } /* }}} */ -#ifdef PHP_WIN32 -static char **php_cli_utf8_command_line(int *argc) -{ - if (!__wargv && _configure_wide_argv(_crt_argv_unexpanded_arguments)) { - return NULL; - } - *argc = __argc; - char **args = calloc((size_t) *argc + 1, sizeof(char *)); - - if (args) { - for (int i = 0; i < *argc; i++) { - args[i] = php_win32_cp_w_to_utf8(__wargv[i]); - if (!args[i]) { - PHP_WIN32_CP_FREE_ARRAY(args, *argc); - args = NULL; - break; - } - } - } - return args; -} -#endif - /* {{{ do_php_cli */ PHP_CLI_API int do_php_cli(int argc, char *argv[]) { @@ -1224,7 +1201,10 @@ PHP_CLI_API int do_php_cli(int argc, char *argv[]) char **argv_save; if (argv == __argv) { - native_argv = php_cli_utf8_command_line(&argc); + if (!__wargv && _configure_wide_argv(_crt_argv_unexpanded_arguments)) { + return 1; + } + PHP_WIN32_CP_CONVERT_ARRAY(__wargv, __argc, native_argv, argc, php_win32_cp_w_to_utf8) if (!native_argv) { return 1; } @@ -1381,24 +1361,13 @@ PHP_CLI_API int do_php_cli(int argc, char *argv[]) php_win32_cp_cli_setup(); orig_cp = (php_win32_cp_get_orig())->id; if (!php_win32_cp_use_unicode()) { - converted_argv = calloc((size_t) argc + 1, sizeof(char *)); + PHP_WIN32_CP_CONVERT_ARRAY(argv_save, argc, converted_argv, argc, php_win32_cp_utf8_to_any) if (!converted_argv) { + fprintf(stderr, "Could not convert command line arguments.\n"); exit_status = 1; goto out; } argv = converted_argv; - for (int i = 0; i < argc; i++) { - wchar_t *wide = php_win32_cp_utf8_to_w(argv_save[i]); - if (wide) { - argv[i] = php_win32_cp_w_to_any(wide); - free(wide); - } - if (!argv[i]) { - fprintf(stderr, "Could not convert command line argument %d.\n", i); - exit_status = 1; - goto out; - } - } } SetConsoleCtrlHandler(php_cli_win32_ctrl_handler, TRUE); diff --git a/win32/codepage.h b/win32/codepage.h index 779589b54c90..c980a599aedc 100644 --- a/win32/codepage.h +++ b/win32/codepage.h @@ -121,19 +121,6 @@ zend_always_inline static char *php_win32_cp_conv_w_to_any(const wchar_t* in, si }/*}}}*/ #define php_win32_cp_w_to_any(in) php_win32_cp_conv_w_to_any(in, PHP_WIN32_CP_IGNORE_LEN, PHP_WIN32_CP_IGNORE_LEN_P) -#define PHP_WIN32_CP_W_TO_ANY_ARRAY(aw, aw_len, aa, aa_len) do { \ - int i; \ - aa_len = aw_len; \ - aa = (char **) malloc(aw_len * sizeof(char *)); \ - if (!aa) { \ - break; \ - } \ - for (i = 0; i < aw_len; i++) { \ - aa[i] = php_win32_cp_w_to_any(aw[i]); \ - } \ -} while (0); - - #define PHP_WIN32_CP_FREE_ARRAY(a, a_len) do { \ int i; \ for (i = 0; i < a_len; i++) { \ @@ -142,6 +129,36 @@ zend_always_inline static char *php_win32_cp_conv_w_to_any(const wchar_t* in, si free(a); \ } while (0); +zend_always_inline static char *php_win32_cp_utf8_to_any(const char *in) +{ + wchar_t *wide = php_win32_cp_utf8_to_w(in); + if (!wide) { + return NULL; + } + char *result = php_win32_cp_w_to_any(wide); + free(wide); + return result; +} + +#define PHP_WIN32_CP_CONVERT_ARRAY(src, src_len, dst, dst_len, convert) do { \ + (dst_len) = (src_len); \ + (dst) = calloc((size_t) (dst_len) + 1, sizeof(char *)); \ + if (!(dst)) { \ + break; \ + } \ + for (int _php_cp_i = 0; _php_cp_i < (dst_len); _php_cp_i++) { \ + (dst)[_php_cp_i] = convert((src)[_php_cp_i]); \ + if (!(dst)[_php_cp_i]) { \ + PHP_WIN32_CP_FREE_ARRAY((dst), (dst_len)) \ + (dst) = NULL; \ + break; \ + } \ + } \ +} while (0); + +#define PHP_WIN32_CP_W_TO_ANY_ARRAY(aw, aw_len, aa, aa_len) \ + PHP_WIN32_CP_CONVERT_ARRAY(aw, aw_len, aa, aa_len, php_win32_cp_w_to_any) + #ifdef __cplusplus } #endif From d62e523f16e4e8d847fdb39a1600b7d05f9789ec Mon Sep 17 00:00:00 2001 From: Marc Date: Wed, 7 Oct 2026 10:14:45 +0200 Subject: [PATCH 12/13] only for !native_arg --- sapi/cli/php_cli.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c index cdf65bc6f063..1f9846c85d1c 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -1220,14 +1220,13 @@ PHP_CLI_API int do_php_cli(int argc, char *argv[]) argv = save_ps_args(argc, argv); #ifdef PHP_WIN32 - for (int i = 0; i < argc; i++) { - if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, argv[i], -1, NULL, 0)) { - fprintf(stderr, "Invalid UTF-8 in command line argument %d.\n", i); - cleanup_ps_args(argv); - if (native_argv) { - PHP_WIN32_CP_FREE_ARRAY(native_argv, argc); + if (!native_argv) { + for (int i = 0; i < argc; i++) { + if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, argv[i], -1, NULL, 0)) { + fprintf(stderr, "Invalid UTF-8 in command line argument %d.\n", i); + cleanup_ps_args(argv); + return 1; } - return 1; } } #endif From 8fc948b8888821e4a494b91d48488d62d31c2abf Mon Sep 17 00:00:00 2001 From: Marc Date: Wed, 7 Oct 2026 16:39:41 +0200 Subject: [PATCH 13/13] don't change standard sapi module --- main/SAPI.h | 4 +--- main/php_ini.c | 3 ++- sapi/cli/php_cli.c | 3 --- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/main/SAPI.h b/main/SAPI.h index 3052aff2b721..2621b9184d1b 100644 --- a/main/SAPI.h +++ b/main/SAPI.h @@ -288,7 +288,6 @@ struct _sapi_module_struct { unsigned int (*input_filter_init)(void); int (*pre_request_init)(void); /* called before activate and before the post data read - used for .user.ini */ - bool ini_entries_utf8; /* Windows: CLI -d values were supplied as UTF-8 */ }; struct _sapi_post_entry { @@ -340,7 +339,6 @@ END_EXTERN_C() NULL, /* ini_entries; */ \ NULL, /* additional_functions */ \ NULL, /* input_filter_init */ \ - NULL, /* pre_request_init */ \ - false /* ini_entries_utf8 */ + NULL /* pre_request_init */ #endif /* SAPI_H */ diff --git a/main/php_ini.c b/main/php_ini.c index fab2d7b692a5..b45f3261db79 100644 --- a/main/php_ini.c +++ b/main/php_ini.c @@ -26,6 +26,7 @@ #include "win32/php_registry.h" #include "win32/winutil.h" #include "win32/codepage.h" +#include "win32/console.h" #endif #if defined(HAVE_SCANDIR) && defined(HAVE_ALPHASORT) && defined(HAVE_DIRENT_H) @@ -918,7 +919,7 @@ zend_result php_init_config(void) RESET_ACTIVE_INI_HASH(); #ifdef PHP_WIN32 - if (sapi_module.ini_entries_utf8) { + if (php_win32_console_is_cli_sapi()) { return php_ini_parse_utf8(sapi_module.ini_entries); } #endif diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c index 1f9846c85d1c..4ea30ff82a64 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -1325,9 +1325,6 @@ PHP_CLI_API int do_php_cli(int argc, char *argv[]) exit_loop: sapi_module_ptr->ini_defaults = sapi_cli_ini_defaults; -#ifdef PHP_WIN32 - sapi_module_ptr->ini_entries_utf8 = true; -#endif sapi_module_ptr->php_ini_path_override = ini_path_override; sapi_module_ptr->phpinfo_as_text = 1; sapi_module_ptr->php_ini_ignore_cwd = 1;