From 1f73b5d5e4589e0ffe0662b752988a19eb4c22b5 Mon Sep 17 00:00:00 2001 From: Tacsipacsi Date: Fri, 7 Feb 2025 22:18:24 +0100 Subject: [PATCH] feeds: Fix str_replace() deprecation warnings on PHP 8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Why: Both AtomFeed::formatTime() and RSSFeed::formatTime() short-circuit with null if the input is falsy. This caused deprecation warnings down the line, as the return value was later fed into str_replace(), which raises a deprecation warning on PHP 8 if it gets null. It also caused unexpected output on all PHP versions: the Mustache templates for both Atom and RSS conditionally emit the date elements ( in Atom, in RSS), but this conditional output is skipped only if the variable is null, not when it’s an empty string – which is exactly what the XML encoding returned on all PHP versions. What: Introduce a new method, xmlEncodeNullable(), which properly handles null values, and returns them as-is, without trying to encode them. This: - Avoids the deprecation warnings on PHP 8, since str_replace() is no longer called. - Makes the conditional output work: the / elements are no longer output if no date is available. - This makes the RSS output spec-compliant, as no garbage is output anymore. The RSS is optional [1]. - It doesn’t make the Atom output entirely spec-compliant, as Atom requires to be present [2], but the removal of garbage (it was a single letter Z) should still increase compatibility. [1] https://www.rssboard.org/rss-specification#hrelementsOfLtitemgt [2] https://validator.w3.org/feed/docs/atom.html#requiredEntryElements Bug: T385332 Change-Id: Iafd89c0d61baecd7c68f62b2a0764cc78cf25069 (cherry picked from commit 60c57b0fd5303e9627b7684ebac4cd369c1fe7a6) --- includes/Feed/AtomFeed.php | 2 +- includes/Feed/FeedItem.php | 9 +++++++++ includes/Feed/RSSFeed.php | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/includes/Feed/AtomFeed.php b/includes/Feed/AtomFeed.php index 453943a39f7..5f9af7af398 100644 --- a/includes/Feed/AtomFeed.php +++ b/includes/Feed/AtomFeed.php @@ -100,7 +100,7 @@ class AtomFeed extends ChannelFeed { "url" => $this->xmlEncode( $this->urlUtils->expand( $item->getUrlUnescaped(), PROTO_CURRENT ) ?? '' ), - "date" => $this->xmlEncode( $this->formatTime( $item->getDate() ) ), + "date" => $this->xmlEncodeNullable( $this->formatTime( $item->getDate() ) ), "description" => $item->getDescription(), "author" => $item->getAuthor() ]; diff --git a/includes/Feed/FeedItem.php b/includes/Feed/FeedItem.php index a67420afafc..93dd364fa15 100644 --- a/includes/Feed/FeedItem.php +++ b/includes/Feed/FeedItem.php @@ -100,6 +100,15 @@ class FeedItem { return htmlspecialchars( $string ); } + /** + * Encode $string so that it can be safely embedded in a XML document, + * returning `null` if $string was `null`. + * @since 1.44 + */ + public function xmlEncodeNullable( ?string $string ): ?string { + return $string !== null ? $this->xmlEncode( $string ) : null; + } + /** * Get the unique id of this item; already xml-encoded * diff --git a/includes/Feed/RSSFeed.php b/includes/Feed/RSSFeed.php index d19e54ca7e2..8ef86d6d580 100644 --- a/includes/Feed/RSSFeed.php +++ b/includes/Feed/RSSFeed.php @@ -78,7 +78,7 @@ class RSSFeed extends ChannelFeed { "permalink" => $item->rssIsPermalink, "uniqueID" => $item->getUniqueID(), "description" => $item->getDescription(), - "date" => $this->xmlEncode( $this->formatTime( $item->getDate() ) ), + "date" => $this->xmlEncodeNullable( $this->formatTime( $item->getDate() ) ), "author" => $item->getAuthor() ]; $comments = $item->getCommentsUnescaped();