]> granicus.if.org Git - php/commitdiff
Do not inherit LC_CTYPE locale from environment
authorNikita Popov <nikita.ppv@gmail.com>
Wed, 29 Apr 2020 09:51:50 +0000 (11:51 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Thu, 30 Apr 2020 08:22:51 +0000 (10:22 +0200)
Treatment of locales in PHP is currently inconsistent: The LC_ALL
locale is set to "C", as is standard behavior on program startup.
The LC_CTYPE locale is set to "", which will inherit it from the
environment. However, the inherited LC_CTYPE locale will only be
used in some cases, while in other cases it is necessary to perform
an explicit setlocale() call in PHP first. This is the case for
the locale-sensitive handling in the PCRE extension.

Make things consistent by *never* inheriting any locales from the
environment. LC_ALL, including LC_CTYPE will be "C" on startup.
A locale can be set or inherited through an explicit setlocale()
call, at which point the behavior will be fully consistent and
predictable.

Closes GH-5488.

UPGRADING
Zend/tests/lc_ctype_inheritance.phpt [new file with mode: 0644]
ext/snmp/snmp.c
ext/standard/basic_functions.c
ext/standard/tests/strings/strtolower-win32.phpt
ext/standard/tests/strings/strtoupper1-win32.phpt
main/main.c

index 96b9745d4ce63f6f6c68588121586e5d2ea5a01c..982ebe635cfcca2db92b50946ea85d219b85749c 100644 (file)
--- a/UPGRADING
+++ b/UPGRADING
@@ -397,6 +397,12 @@ PHP 8.0 UPGRADE NOTES
   . If the array returned by __sleep() contains non-existing properties, these
     are now silently ignored. Previously, such properties would have been
     serialized as if they had the value NULL.
+  . The default locale on startup is now always "C". No locales are inherited
+    from the environment by default. Previously, LC_ALL was set to "C", while
+    LC_CTYPE was inherited from the environment. However, some functions did not
+    respect the inherited locale without an explicit setlocale() call. An
+    explicit setlocale() call is now always required if you wish to change any
+    locale component from the default.
 
 - tidy:
   . The $use_include_path parameter, which was not used internally, has been
diff --git a/Zend/tests/lc_ctype_inheritance.phpt b/Zend/tests/lc_ctype_inheritance.phpt
new file mode 100644 (file)
index 0000000..8971ff1
--- /dev/null
@@ -0,0 +1,25 @@
+--TEST--
+Do not inherit LC_CTYPE from environment
+--SKIPIF--
+<?php
+if (!setlocale(LC_CTYPE, "de_DE", "de-DE")) die("skip requires de_DE locale");
+?>
+--ENV--
+LC_CTYPE=de_DE
+--FILE--
+<?php
+
+var_dump(setlocale(LC_CTYPE, "0"));
+var_dump(bin2hex(strtoupper("\xe4")));
+var_dump(preg_match('/\w/', "\xe4"));
+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"
+string(2) "e4"
+int(0)
+bool(true)
+string(2) "c4"
+int(1)
index 8464e74aad3755e61e6c4ceef9b0c5b1af1ec3fb..d5cf7cce04a7d9058d11a5df0f0e38962b916ff0 100644 (file)
@@ -49,6 +49,7 @@
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
+#include <locale.h>
 
 #ifndef __P
 #ifdef __GNUC__
@@ -1971,6 +1972,8 @@ PHP_MINIT_FUNCTION(snmp)
        le_snmp_session = zend_register_list_destructors_ex(php_snmp_session_destructor, NULL, PHP_SNMP_SESSION_RES_NAME, module_number);
 
        init_snmp("snmpapp");
+       /* net-snmp corrupts the CTYPE locale during initialization. */
+       setlocale(LC_CTYPE, "C");
 
 #ifdef NETSNMP_DS_LIB_DONT_PERSIST_STATE
        /* Prevent update of the snmpapp.conf file */
index 32a82aae26cbb746fd3d82c1ceb4d48090dfc4aa..5fa7be86dbe3c3ee87c431082cd81ffd6b30ea79 100755 (executable)
@@ -516,7 +516,6 @@ PHP_RSHUTDOWN_FUNCTION(basic) /* {{{ */
         * to the value in startup environment */
        if (BG(locale_changed)) {
                setlocale(LC_ALL, "C");
-               setlocale(LC_CTYPE, "");
                zend_update_current_locale();
                if (BG(locale_string)) {
                        zend_string_release_ex(BG(locale_string), 0);
index ff631754df6a89026cf0da44c630ad5580a2ccb2..2fc693641dbb38171e652a85e6e792731b65d8ba 100644 (file)
Binary files a/ext/standard/tests/strings/strtolower-win32.phpt and b/ext/standard/tests/strings/strtolower-win32.phpt differ
index 7240e5c021dc89381c0d70f4f82e4328b1a806fb..619ab51d860273686c8af0d325f206a5cdd06984 100644 (file)
Binary files a/ext/standard/tests/strings/strtoupper1-win32.phpt and b/ext/standard/tests/strings/strtoupper1-win32.phpt differ
index 3a1e92ee08ed626c102c80ed7d6215d090e631cf..dc9182e48114a247da97d65260b89e070430a013 100644 (file)
@@ -2133,7 +2133,6 @@ 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);
-       setlocale(LC_CTYPE, "");
        zend_update_current_locale();
 
 #if HAVE_TZSET