feeds: Fix str_replace() deprecation warnings on PHP 8

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
(<updated> in Atom, <pubDate> 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 <updated>/<pubDate> 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 <pubDate> is optional [1].
  - It doesn’t make the Atom output entirely spec-compliant, as Atom
    requires <updated> 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)
This commit is contained in:
Tacsipacsi 2025-02-07 22:18:24 +01:00 committed by Reedy
parent f43da897e5
commit 1f73b5d5e4
3 changed files with 11 additions and 2 deletions

View file

@ -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()
];

View file

@ -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
*

View file

@ -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();