`, so a raw `]]>` inside one ends the section * early, turns everything after it into markup, and makes the whole document not well-formed. That * is the same harm the invisible-character strip above exists to prevent, arriving through a * character nobody enumerated (B2-09). * * It is fixed HERE, at output, rather than in the sanitizers above, and the difference matters. * Every character those strip is invalid or deceptive in any context, so removing it loses nothing. * `]]>` is ordinary text - a post explaining XML has every reason to contain it - and only breaks * one serialization. Stripping it at storage would be silent data loss on legitimate content, so * what is stored stays exactly as written and only the feed rendering is escaped. * * WordPress already does precisely this for the sibling field: get_the_content_feed() runs * str_replace( ']]>', ']]>' ) over post_content (wp-includes/feed.php). the_excerpt_rss() has no * equivalent - its only filters are convert_chars and ent2ncr, and neither touches the sequence - * so post_excerpt was unprotected where post_content was protected. This closes that asymmetry the * same way core closed it. * * The filter can only ever repair a feed, never break a working one: any excerpt containing the raw * sequence is already producing a malformed document. * * @param string $text Feed text. * @return string */ function aafm_escape_feed_cdata( $text ): string { return str_replace( ']]>', ']]>', (string) $text ); } add_filter( 'the_excerpt_rss', 'aafm_escape_feed_cdata' ); /** * Trim ASCII whitespace and the invisible or non-ASCII space characters that trim() leaves alone * (NBSP, the Unicode spaces, zero-width characters, the byte-order mark) from both ends. Works on raw * bytes with no /u flag, so invalid UTF-8 comes back unchanged instead of null, and a lone A0 byte * inside a multibyte character (the letter a-grave) is never touched. * * @param string $text Text to trim. * @return string */ function aafm_trim_unicode( string $text ): string { $space = '(?:[ \t\n\r\0\x0B\f]|\xC2\xA0|\xE1\x9A\x80|\xE2\x80[\x80-\x8D\xAF]|\xE2\x81[\x9F\xA0]|\xE3\x80\x80|\xEF\xBB\xBF)'; $out = preg_replace( '/\A' . $space . '+|' . $space . '+\z/', '', $text ); return null === $out ? trim( $text ) : $out; }