Skip to content

Commit 0f657a3

Browse files
committed
move encoding conversion - do an ini pass and then go over argv
1 parent 2be5d00 commit 0f657a3

8 files changed

Lines changed: 473 additions & 45 deletions

File tree

‎main/SAPI.h‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ struct _sapi_module_struct {
288288
unsigned int (*input_filter_init)(void);
289289

290290
int (*pre_request_init)(void); /* called before activate and before the post data read - used for .user.ini */
291+
bool ini_entries_utf8; /* Windows: CLI -d values were supplied as UTF-8 */
291292
};
292293

293294
struct _sapi_post_entry {
@@ -339,6 +340,7 @@ END_EXTERN_C()
339340
NULL, /* ini_entries; */ \
340341
NULL, /* additional_functions */ \
341342
NULL, /* input_filter_init */ \
342-
NULL /* pre_request_init */
343+
NULL, /* pre_request_init */ \
344+
false /* ini_entries_utf8 */
343345

344346
#endif /* SAPI_H */

‎main/main.c‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2236,8 +2236,11 @@ zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additi
22362236
load zend extensions and register php function extensions
22372237
to be loaded later */
22382238
zend_stream_init();
2239-
php_init_config();
2239+
zend_result config_result = php_init_config();
22402240
zend_stream_shutdown();
2241+
if (config_result == FAILURE) {
2242+
return FAILURE;
2243+
}
22412244

22422245
/* Register PHP core ini entries */
22432246
zend_register_ini_entries_ex(ini_entries, module_number, MODULE_PERSISTENT);

‎main/php_ini.c‎

Lines changed: 199 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#ifdef PHP_WIN32
2626
#include "win32/php_registry.h"
2727
#include "win32/winutil.h"
28+
#include "win32/codepage.h"
2829
#endif
2930

3031
#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
307308
}
308309
/* }}} */
309310

311+
#ifdef PHP_WIN32
312+
typedef struct {
313+
zval args[3];
314+
int callback_type;
315+
} php_ini_utf8_entry;
316+
317+
typedef struct {
318+
const struct php_win32_cp *cp;
319+
const struct php_win32_cp *utf8;
320+
HashTable values;
321+
zend_llist entries;
322+
bool failed;
323+
} php_ini_utf8_context;
324+
325+
static php_ini_utf8_context *php_ini_utf8;
326+
327+
static const struct php_win32_cp *php_ini_utf8_codepage(HashTable *hash)
328+
{
329+
const char *names[] = {"internal_encoding", "default_charset"};
330+
for (int i = 0; i < 2; i++) {
331+
zval *value = zend_hash_str_find(hash, names[i], strlen(names[i]));
332+
if (!value) {
333+
value = zend_hash_str_find(&configuration_hash, names[i], strlen(names[i]));
334+
}
335+
if (value && Z_TYPE_P(value) == IS_PTR) {
336+
value = Z_PTR_P(value);
337+
}
338+
if (value && Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value)) {
339+
return php_win32_cp_get_by_enc(Z_STRVAL_P(value));
340+
}
341+
}
342+
return php_win32_cp_get_by_id(CP_UTF8);
343+
}
344+
345+
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)
346+
{
347+
if (ctx->failed || Z_TYPE_P(value) != IS_STRING || from == to) {
348+
return;
349+
}
350+
size_t length, wide_len;
351+
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);
352+
char *bytes = NULL;
353+
if (wide) {
354+
bytes = php_win32_cp_conv_from_w(to->id, to->from_w_fl, wide, wide_len, &length);
355+
free(wide);
356+
}
357+
if (!bytes) {
358+
ctx->failed = true;
359+
return;
360+
}
361+
zend_string_release(Z_STR_P(value));
362+
ZVAL_STR(value, zend_string_init(bytes, length, true));
363+
free(bytes);
364+
}
365+
366+
static zval *php_ini_utf8_get_config(zend_string *name)
367+
{
368+
php_ini_utf8_context *ctx = php_ini_utf8;
369+
zval *value = zend_hash_find(&ctx->values, name);
370+
if (!value || Z_TYPE_P(value) != IS_PTR) {
371+
return value;
372+
}
373+
value = Z_PTR_P(value);
374+
if (value && Z_TYPE_P(value) == IS_STRING) {
375+
zval converted;
376+
ZVAL_STR(&converted, zend_string_copy(Z_STR_P(value)));
377+
php_ini_utf8_convert(ctx, &converted, ctx->cp, ctx->utf8);
378+
return zend_hash_update(&ctx->values, name, &converted);
379+
}
380+
return value;
381+
}
382+
383+
static char *php_ini_utf8_getenv(const char *name, size_t name_len)
384+
{
385+
wchar_t *wide_name = php_win32_cp_conv_utf8_to_w(name, name_len, PHP_WIN32_CP_IGNORE_LEN_P);
386+
if (!wide_name) {
387+
php_ini_utf8->failed = true;
388+
return estrdup("");
389+
}
390+
const wchar_t *value = _wgetenv(wide_name);
391+
free(wide_name);
392+
if (!value) {
393+
return NULL;
394+
}
395+
char *bytes = php_win32_cp_w_to_utf8(value);
396+
if (!bytes) {
397+
php_ini_utf8->failed = true;
398+
return estrdup("");
399+
}
400+
char *result = estrdup(bytes);
401+
free(bytes);
402+
return result;
403+
}
404+
405+
static void php_ini_utf8_entry_dtor(void *data)
406+
{
407+
php_ini_utf8_entry *entry = data;
408+
for (int i = 0; i < 3; i++) {
409+
zval_internal_ptr_dtor(&entry->args[i]);
410+
}
411+
}
412+
413+
static void php_ini_utf8_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callback_type, void *data)
414+
{
415+
php_ini_utf8_context *ctx = data;
416+
php_ini_utf8_entry entry;
417+
zval *args[3] = {arg1, arg2, arg3};
418+
for (int i = 0; i < 3; i++) {
419+
if (args[i]) {
420+
ZVAL_STR(&entry.args[i], zend_string_dup(Z_STR_P(args[i]), true));
421+
} else {
422+
ZVAL_UNDEF(&entry.args[i]);
423+
}
424+
}
425+
entry.callback_type = callback_type;
426+
zend_llist_add_element(&ctx->entries, &entry);
427+
/* Keep earlier -d values available for expansion, without queuing extensions twice. */
428+
if (callback_type == ZEND_INI_PARSER_ENTRY && !is_special_section
429+
&& (zend_string_equals_literal_ci(Z_STR_P(arg1), PHP_EXTENSION_TOKEN)
430+
|| zend_string_equals_literal_ci(Z_STR_P(arg1), ZEND_EXTENSION_TOKEN))) {
431+
return;
432+
}
433+
php_ini_parser_cb(arg1, arg2, arg3, callback_type, &ctx->values);
434+
}
435+
436+
static zend_result php_ini_parse_utf8(const char *entries)
437+
{
438+
php_ini_utf8_context ctx = {0};
439+
char *(*getenv_save)(const char *, size_t) = zend_getenv;
440+
zend_hash_init(&ctx.values, 8, NULL, config_zval_dtor, true);
441+
zend_llist_init(&ctx.entries, sizeof(php_ini_utf8_entry), php_ini_utf8_entry_dtor, true);
442+
ctx.cp = php_ini_utf8_codepage(&ctx.values);
443+
ctx.utf8 = php_win32_cp_get_by_id(CP_UTF8);
444+
if (!ctx.cp || !ctx.utf8) {
445+
ctx.failed = true;
446+
goto cleanup;
447+
}
448+
/* Borrow php.ini scalars; section arrays only need their structure while parsing. */
449+
zend_string *key;
450+
zval *value;
451+
ZEND_HASH_FOREACH_STR_KEY_VAL(&configuration_hash, key, value) {
452+
if (key) {
453+
zval name, borrowed;
454+
ZVAL_STR(&name, zend_string_copy(key));
455+
php_ini_utf8_convert(&ctx, &name, ctx.cp, ctx.utf8);
456+
if (Z_TYPE_P(value) == IS_ARRAY) {
457+
ZVAL_NEW_PERSISTENT_ARR(&borrowed);
458+
zend_hash_init(Z_ARRVAL(borrowed), 0, NULL, config_zval_dtor, true);
459+
} else {
460+
ZVAL_PTR(&borrowed, value);
461+
}
462+
zend_hash_update(&ctx.values, Z_STR(name), &borrowed);
463+
zend_string_release(Z_STR(name));
464+
}
465+
} ZEND_HASH_FOREACH_END();
466+
php_ini_utf8 = &ctx;
467+
zend_getenv = php_ini_utf8_getenv;
468+
/* Parse in UTF-8, then apply the values after resolving the final code page. */
469+
zend_parse_ini_string(entries, true, ZEND_INI_SCANNER_NORMAL, php_ini_utf8_parser_cb, &ctx);
470+
zend_getenv = getenv_save;
471+
php_ini_utf8 = NULL;
472+
RESET_ACTIVE_INI_HASH();
473+
ctx.cp = php_ini_utf8_codepage(&ctx.values);
474+
ctx.failed |= !ctx.cp;
475+
for (zend_llist_element *element = ctx.entries.head; element && !ctx.failed; element = element->next) {
476+
php_ini_utf8_entry *entry = (php_ini_utf8_entry *) element->data;
477+
for (int i = 0; i < 3; i++) {
478+
php_ini_utf8_convert(&ctx, &entry->args[i], ctx.utf8, ctx.cp);
479+
}
480+
if (!ctx.failed) {
481+
php_ini_parser_cb(&entry->args[0], Z_ISUNDEF(entry->args[1]) ? NULL : &entry->args[1],
482+
Z_ISUNDEF(entry->args[2]) ? NULL : &entry->args[2], entry->callback_type, &configuration_hash);
483+
}
484+
}
485+
cleanup:
486+
zend_llist_destroy(&ctx.entries);
487+
zend_hash_destroy(&ctx.values);
488+
if (ctx.failed) {
489+
php_error(E_CORE_WARNING, "Could not convert command line INI values to the configured Windows code page");
490+
return FAILURE;
491+
}
492+
return SUCCESS;
493+
}
494+
#endif
495+
310496
/* {{{ php_load_php_extension_cb */
311497
static void php_load_php_extension_cb(void *arg)
312498
{
@@ -404,7 +590,7 @@ static void append_ini_path(char *php_ini_search_path, size_t search_path_size,
404590
}
405591

406592
/* {{{ php_init_config */
407-
void php_init_config(void)
593+
zend_result php_init_config(void)
408594
{
409595
char *php_ini_file_name = NULL;
410596
char *php_ini_search_path = NULL;
@@ -716,8 +902,15 @@ void php_init_config(void)
716902
if (sapi_module.ini_entries) {
717903
/* Reset active ini section */
718904
RESET_ACTIVE_INI_HASH();
905+
906+
#ifdef PHP_WIN32
907+
if (sapi_module.ini_entries_utf8) {
908+
return php_ini_parse_utf8(sapi_module.ini_entries);
909+
}
910+
#endif
719911
zend_parse_ini_string(sapi_module.ini_entries, true, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash);
720912
}
913+
return SUCCESS;
721914
}
722915
/* }}} */
723916

@@ -876,6 +1069,11 @@ PHPAPI void php_ini_activate_per_host_config(const char *host, size_t host_len)
8761069
/* {{{ cfg_get_entry */
8771070
PHPAPI zval *cfg_get_entry_ex(zend_string *name)
8781071
{
1072+
#ifdef PHP_WIN32
1073+
if (php_ini_utf8) {
1074+
return php_ini_utf8_get_config(name);
1075+
}
1076+
#endif
8791077
return zend_hash_find(&configuration_hash, name);
8801078
}
8811079
/* }}} */

‎main/php_ini.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
BEGIN_EXTERN_C()
2121
PHPAPI void config_zval_dtor(zval *zvalue);
22-
void php_init_config(void);
22+
zend_result php_init_config(void);
2323
void php_shutdown_config(void);
2424
void php_ini_register_extensions(void);
2525
PHPAPI zval *cfg_get_entry_ex(zend_string *name);

0 commit comments

Comments
 (0)