From 26e424465c4d6848d19bcf041c6110ff155df840 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 9 Nov 2021 12:03:59 +0100 Subject: [PATCH] Fix bug #81598: Use C.UTF-8 as LC_CTYPE locale by default Unfortunately, libedit is locale based and does not accept UTF-8 input when the C locale is used. This patch switches the default locale to C.UTF-8 instead (if it is available). This makes libedit work and I believe it shouldn't affect behavior of single-byte locale-dependent functions that PHP otherwise uses. Closes GH-7635. --- NEWS | 4 ++++ Zend/tests/lc_ctype_inheritance.phpt | 4 ++-- Zend/zend_operators.c | 9 +++++++++ Zend/zend_operators.h | 2 ++ ext/snmp/snmp.c | 2 +- ext/standard/basic_functions.c | 1 + main/main.c | 1 + 7 files changed, 20 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index b172fe11606..9cefea074e2 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,10 @@ PHP NEWS (devnexen) . Introduced MYSQLI_IS_MARIADB. (devnexen) +- Readline: + . Fixed bug #81598 (Cannot input unicode characters in PHP 8 interactive + shell). (Nikita) + - Reflection: . Fixed bug #81681 (ReflectionEnum throwing exceptions). (cmb) diff --git a/Zend/tests/lc_ctype_inheritance.phpt b/Zend/tests/lc_ctype_inheritance.phpt index 8971ff19697..8c968f0615e 100644 --- a/Zend/tests/lc_ctype_inheritance.phpt +++ b/Zend/tests/lc_ctype_inheritance.phpt @@ -16,8 +16,8 @@ var_dump(setlocale(LC_CTYPE, "de_DE", "de-DE") !== false); var_dump(bin2hex(strtoupper("\xe4"))); var_dump(preg_match('/\w/', "\xe4")); ?> ---EXPECT-- -string(1) "C" +--EXPECTF-- +string(%d) "C%r(\.UTF-8)?%r" string(2) "e4" int(0) bool(true) diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 0a516f69190..d178b9b506f 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -2662,6 +2662,15 @@ ZEND_API void zend_update_current_locale(void) /* {{{ */ } /* }}} */ +ZEND_API void zend_reset_lc_ctype_locale(void) +{ + /* Use the C.UTF-8 locale so that readline can process UTF-8 input, while not interfering + * with single-byte locale-dependent functions used by PHP. */ + if (!setlocale(LC_CTYPE, "C.UTF-8")) { + setlocale(LC_CTYPE, "C"); + } +} + static zend_always_inline void zend_str_tolower_impl(char *dest, const char *str, size_t length) /* {{{ */ { unsigned char *p = (unsigned char*)str; unsigned char *q = (unsigned char*)dest; diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h index 5610d34dedb..2044b20b2be 100644 --- a/Zend/zend_operators.h +++ b/Zend/zend_operators.h @@ -472,6 +472,8 @@ ZEND_API zend_long ZEND_FASTCALL zend_atol(const char *str, size_t str_len); ZEND_API void zend_update_current_locale(void); +ZEND_API void zend_reset_lc_ctype_locale(void); + /* The offset in bytes between the value and type fields of a zval */ #define ZVAL_OFFSETOF_TYPE \ (offsetof(zval, u1.type_info) - offsetof(zval, value)) diff --git a/ext/snmp/snmp.c b/ext/snmp/snmp.c index f0917501751..a6ef3b92454 100644 --- a/ext/snmp/snmp.c +++ b/ext/snmp/snmp.c @@ -1999,7 +1999,7 @@ PHP_MINIT_FUNCTION(snmp) init_snmp("snmpapp"); /* net-snmp corrupts the CTYPE locale during initialization. */ - setlocale(LC_CTYPE, "C"); + zend_reset_lc_ctype_locale(); #ifdef NETSNMP_DS_LIB_DONT_PERSIST_STATE /* Prevent update of the snmpapp.conf file */ diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 73d92fd447f..23f736d2009 100755 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -528,6 +528,7 @@ PHP_RSHUTDOWN_FUNCTION(basic) /* {{{ */ * to the value in startup environment */ if (BG(locale_changed)) { setlocale(LC_ALL, "C"); + zend_reset_lc_ctype_locale(); zend_update_current_locale(); if (BG(ctype_string)) { zend_string_release_ex(BG(ctype_string), 0); diff --git a/main/main.c b/main/main.c index 7de295fe84f..a0f1a658c66 100644 --- a/main/main.c +++ b/main/main.c @@ -2087,6 +2087,7 @@ int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_mod zuf.getenv_function = sapi_getenv; zuf.resolve_path_function = php_resolve_path_for_zend; zend_startup(&zuf); + zend_reset_lc_ctype_locale(); zend_update_current_locale(); zend_observer_startup();