Breaking: #110966 - Remove UTF8filesystem and systemLocale settings
See forge#110966
Description
The configuration options
$GLOBALS and
$GLOBALS have been removed.
Both options existed so that PHP string functions process UTF-8 file names
correctly. To achieve that, TYPO3 temporarily switched
LC_ to the
configured system locale around calls to
basename,
dirname,
pathinfo and
escapeshellarg.
The PHP manual still describes
basename and
dirname as
locale aware. The PHP source code shows that this no longer matters for
TYPO3:
dirnameis implemented by() zend_, which searches for the directory separator byte by byte. It does not evaluate the locale at all.dirname () basenameis implemented by() php_. Since PHP 8.1, it searches for the directory separator byte by byte as well, as long as the current locale is "ASCII compatible". PHP considers a locale ASCII compatible if it uses a single-byte encoding (such asbasename () C,POSIXor ISO-8859-1) or UTF-8. Only for other multibyte encodings, such as Shift-JIS, Big5 or GBK, PHP walks the string character by character withmblen. In these encodings, the second byte of a character can be() 0x5C, which is the directory separator on Windows. In UTF-8, all bytes of a multibyte character are0x80or higher, so they never match a directory separator.pathinfois based on these two functions.()
This was verified with PHP 8.2, 8.4 and 8.5 on glibc: all three functions
return the same, correct results for UTF-8 paths in the
C
,
C.,
en_ and
de_ locales.
escapeshellarg is different. Its implementation
php_ calls mblen for every byte and silently
skips all bytes that are invalid in the current locale. In the
C
locale of glibc, which is ASCII, every byte of
0x80
or higher is
invalid, so äöü. turns into .txt. The previous code only
prevented this if both options were configured, and EXT:indexed_search
called
escapeshellarg directly. PHP-FPM commonly runs in the
C
locale, for example when started by systemd.
\TYPO3\ and
\TYPO3\ now quote
arguments byte by byte, independent of the current locale: the argument is
wrapped in single quotes, and each single quote is replaced by
'\''
.
This is what
escapeshellarg does on POSIX systems, without dropping
bytes. On Windows,
escapeshellarg is still used. Arguments containing
null bytes are rejected with an
\Invalid. The shell
calls of EXT:indexed_search and of the install tool use these methods as well.
\TYPO3\,
\TYPO3\ and
\TYPO3\ no longer change the
locale and have been deprecated, see
Deprecation: #110966 - PathUtility basename(), dirname(), pathinfo().
File names are always stored with their Unicode characters (normalized to NFC).
The install tool checks for both options have been removed. Both options are
removed from config/system/settings.php automatically when the install
tool is accessed.
Impact
The local driver no longer transliterates non-ASCII characters of uploaded or
renamed files. Before this change, Übersicht. was stored as
Uebersicht. if
UTF8filesystem
was disabled. It is now stored
as Übersicht.. Characters that are not allowed in file names are
still replaced by underscores. Existing files are not renamed.
New installations have enabled
UTF8filesystem
by default since TYPO3
v12, so they already behave this way.
Reading one of the two options from
$GLOBALS raises
an "Undefined array key" warning.
Affected installations
- Installations that have
UTF8filesystemdisabled. This is usually the case for installations upgraded from TYPO3 v11 or earlier that never changed the option. - Installations with extensions that read one of the two options.
Migration
Remove any code that reads the two options.
To keep transliterating file names to ASCII, register a listener for
\TYPO3\:
namespace MyVendor\MyExtension\EventListener;
use TYPO3\CMS\Core\Attribute\AsEventListener;
use TYPO3\CMS\Core\Charset\CharsetConverter;
use TYPO3\CMS\Core\Resource\Event\SanitizeFileNameEvent;
final readonly class AsciiFileName
{
public function __construct(
private CharsetConverter $charsetConverter,
) {}
#[AsEventListener]
public function __invoke(SanitizeFileNameEvent $event): void
{
$fileName = $this->charsetConverter->utf8_char_mapping($event->getFileName());
$event->setFileName((string)preg_replace('/[^.\w-]/', '_', $fileName));
}
}