` mit dem Code direkt darin, ohne ``. WordPress, * der Block-Editor und die gaengigen Highlighter erwarten dagegen * `
`.
 *
 * Die verwalteten Bereiche fuer Hub und Quellen kommen jeweils in einen
 * core/html-Block. Ohne ihn wickelt der Block-Editor beim Speichern jeden
 * Marker in einen eigenen Absatz (`

`); der * Bereich zwischen den Markern laege dann quer zu den Absatzgrenzen, und der * naechste Hub-Patch schriebe eine Liste in einen Absatz. Den Inhalt eines * core/html-Blocks speichert der Editor dagegen byte-genau. Im Browser * nachgewiesen am 2026-10-20 (WordPress 7.1). */ final class HtmlNormalizer { /** * Die verwalteten Bereiche: Start- und Endmarker. * * @var array */ private const MANAGED_REGIONS = array( array( '', '' ), array( '', '' ), ); /** * Wandelt den Body fuer den Import um. * * Laeuft nur beim Import. Der Server baut spaetere Patches gegen den * zurueckgemeldeten Body, also gegen genau diese Form. * * @param string $html Gelieferter Body. * @return string */ public static function for_import( string $html ): string { $converted = preg_replace_callback( '#]*)?>(.*?)
#is', array( self::class, 'convert_pre' ), $html ); $html = null === $converted ? $html : $converted; foreach ( self::MANAGED_REGIONS as $markers ) { $html = self::wrap_region( $html, $markers[0], $markers[1] ); } return $html; } /** * Setzt einen verwalteten Bereich in einen core/html-Block. * * Nur wenn beide Marker genau einmal und in der richtigen Reihenfolge * vorkommen; sonst bleibt der Body, wie er ist. Das Format ist genau das, * was der Editor selbst schreibt — ein Zeilenumbruch nach dem Oeffner, einer * vor dem Schliesser, Bloecke durch eine Leerzeile getrennt —, damit ein * Speichern im Editor den Bereich nicht veraendert. * * @param string $html Body. * @param string $start Startmarker. * @param string $end Endmarker. * @return string */ private static function wrap_region( string $html, string $start, string $end ): string { if ( 1 !== substr_count( $html, $start ) || 1 !== substr_count( $html, $end ) ) { return $html; } $from = (int) strpos( $html, $start ); $to = (int) strpos( $html, $end ) + strlen( $end ); if ( $to <= $from ) { return $html; } $before = rtrim( substr( $html, 0, $from ) ); $region = substr( $html, $from, $to - $from ); $after = ltrim( substr( $html, $to ) ); $block = "\n" . $region . "\n"; return implode( "\n\n", array_filter( array( $before, $block, $after ), 'strlen' ) ); } /** * Wandelt einen einzelnen pre-Block um. * * @param array $parts Treffer: [0] ganzer Block, [1] Attribute, [2] Inhalt. * @return string */ private static function convert_pre( array $parts ): string { $attributes = $parts[1] ?? ''; $inner = $parts[2] ?? ''; // Steckt der Code schon in , gibt es nichts zu tun. if ( 1 === preg_match( '#^\s*]#i', $inner ) ) { return $parts[0]; } $language = ''; if ( 1 === preg_match( '#data-highlighter=(["\'])([a-z0-9_+\#.-]*)\1#i', $attributes, $language_match ) ) { $language = strtolower( $language_match[2] ); } $code_attributes = '' !== $language ? ' class="language-' . esc_attr( $language ) . '"' : ''; return '
' . $inner . '
'; } }