Tom Gould

Products

RSS to JSON

A small PHP library and command-line tool that turns RSS, Atom and media feeds into clean JSON, and fetches remote feeds safely.

Source on GitHub Package on Packagist

RSS to JSON is for PHP developers who need to read feeds and pass them on as JSON: to a JavaScript front end, an API response or a queue. It reads a feed from a URL, a local file or a string, and gives back a plain array you can pass straight to json_encode().

What it does

It handles RSS 0.9x and 2.0, RSS 1.0 (RDF), Atom 1.0 and extensions such as Media RSS, Dublin Core and content:encoded. Other XML is parsed generically. The output follows a few fixed rules, so code that reads it doesn’t have to guess:

  • the result is always ['channel' => [...]], for Atom too
  • items are always a list under channel['item'], even when there’s only one, and that covers Atom entries
  • an element with only text becomes a string, and repeated elements become a list
  • attributes go under @attributes, and text next to attributes or child elements under @value
  • prefixed elements keep their prefix, such as media:thumbnail or dc:creator
  • CDATA becomes plain text

For example, this feed:

<rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/">
  <channel>
    <title>Example Feed</title>
    <link>https://example.com/</link>
    <item>
      <title>Fish &amp; chips</title>
      <guid isPermaLink="false">post-1</guid>
      <category>Food</category>
      <category>UK</category>
      <media:thumbnail url="https://example.com/1.jpg" width="640"/>
    </item>
  </channel>
</rss>

becomes:

{
    "channel": {
        "title": "Example Feed",
        "link": "https://example.com/",
        "item": [
            {
                "media:thumbnail": {
                    "@attributes": {
                        "url": "https://example.com/1.jpg",
                        "width": "640"
                    }
                },
                "title": "Fish & chips",
                "guid": {
                    "@attributes": {
                        "isPermaLink": "false"
                    },
                    "@value": "post-1"
                },
                "category": [
                    "Food",
                    "UK"
                ]
            }
        ]
    }
}

A command-line tool prints any feed as pretty-printed JSON, or the reason it failed.

How it’s built

The library needs PHP 7.4 or later with the cURL, libxml and SimpleXML extensions, and has no other dependencies. Every input goes through one loading path, so the same protections apply whether the XML came from a URL, a file or a string.

Feeds are untrusted input, and version 2.1 hardened how they’re read. XML is loaded with network access off and without entity substitution, so external entities and DTDs aren’t loaded. Any & that doesn’t start one of XML’s own entities is escaped before parsing, so a declared entity comes out as literal text.

Fetching only allows http and https, for the URL and every redirect. It stops after five redirects, times out after ten seconds, refuses responses over 20 MiB and only accepts a 2xx status with an XML content type. All of these can be changed.

For URLs that come from someone you don’t trust, there’s an opt-in block_private_ips option. It resolves the host of the URL and of every redirect before connecting. It refuses private, loopback, link-local, carrier-grade NAT, multicast and reserved addresses, including cloud metadata addresses and IPv4 addresses inside IPv6. Then it pins the connection to the address it checked, so DNS rebinding can’t switch it afterwards.

PHPUnit covers the parser and the fetcher, with the remote tests running against PHP’s built-in web server on 127.0.0.1. PHPStan checks the code at its strictest level. GitHub Actions runs both.

How to use it

composer require tomgould/rsstojson
use TomGould\RSSToJson\RSSToJson;

$parser = new RSSToJson(['block_private_ips' => true]);
$feed = $parser->parseUrl('https://example.com/feed.xml');

if ($feed === null) {
    echo $parser->getLastError(), PHP_EOL;
} else {
    echo json_encode($feed, JSON_PRETTY_PRINT);
}

Or from the command line:

vendor/bin/rsstojson https://example.com/feed.xml