'), die RTE-Persistenz * escapte das & erneut. Die Importer schreiben seit 2026-10-28 literal * (ENT_NOQUOTES); dieses Skript repariert den Bestand um EINE * Escaping-Stufe: &apos; -> ' usw. * * Plain PDO (Muster migrations/check_bg.php). * * Usage: php migrations/fix_double_entities.php (Scan/Report) * php migrations/fix_double_entities.php --apply */ error_reporting(E_ALL); ini_set('display_errors', '1'); // Stufe 1: doppelt kodierte Entities um eine Stufe reduzieren. // Stufe 2 (Befund 2026-10-28: Scan fand KEINE Doubles in der DB - die DB // traegt ' EINFACH kodiert, erst die Render-Pipeline escapet das & // erneut): Apostroph-Entities literal machen, dann kann nichts mehr doppeln. const PATTERNS = ['&apos;', '&quot;', '&#039;', '&amp;', '&nbsp;', ''', ''']; const REPLACEMENTS = [''', '"', ''', '&', ' ', "'", "'"]; $apply = in_array('--apply', $argv, true); $root = null; $dir = __DIR__; for ($i = 0; $i < 5; $i++) { if (is_file($dir . '/config/system/settings.php')) { $root = $dir; break; } $dir = dirname($dir); } if ($root === null) { exit("Could not locate project root above " . __DIR__ . "\n"); } $settings = require $root . '/config/system/settings.php'; $db = $settings['DB']['Connections']['Default']; $pdo = new PDO( sprintf('mysql:host=%s;port=%s;dbname=%s;charset=utf8mb4', $db['host'] ?? 'localhost', $db['port'] ?? 3306, $db['dbname'] ?? ''), $db['user'] ?? '', $db['password'] ?? '', [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC] ); echo ($apply ? 'APPLY' : 'SCAN') . "\n"; $like = implode(' OR ', array_map(static fn(string $p): string => "bodytext LIKE '%" . $p . "%'", PATTERNS)); $rows = $pdo->query( "SELECT c.uid, c.pid, p.slug, c.CType, c.bodytext FROM tt_content c LEFT JOIN pages p ON p.uid = c.pid WHERE c.deleted = 0 AND ($like) ORDER BY p.slug, c.uid" )->fetchAll(); $fixed = 0; foreach ($rows as $row) { $new = str_replace(PATTERNS, REPLACEMENTS, (string)$row['bodytext']); $hits = []; foreach (PATTERNS as $pattern) { $n = substr_count((string)$row['bodytext'], $pattern); if ($n > 0) { $hits[] = $pattern . 'x' . $n; } } printf("#%-6d %-28s %-45s %s\n", $row['uid'], $row['CType'], (string)$row['slug'], implode(' ', $hits)); if ($apply) { $pdo->prepare('UPDATE tt_content SET bodytext = ?, tstamp = UNIX_TIMESTAMP() WHERE uid = ?') ->execute([$new, (int)$row['uid']]); $fixed++; } } echo "\n" . count($rows) . " Element(e) betroffen" . ($apply ? ", $fixed repariert. Danach: vendor/bin/typo3 cache:flush" : ". Mit --apply reparieren.") . "\n"; if (!$apply && $rows !== []) { echo "HINWEIS: Ersetzt wird genau EINE Escaping-Stufe (&apos; -> ' usw.).\n"; }