PHPackages                             phanan/poddle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Parsing &amp; Serialization](/categories/parsing)
4. /
5. phanan/poddle

ActiveLibrary[Parsing &amp; Serialization](/categories/parsing)

phanan/poddle
=============

Parse podcast feeds with PHP following PSP-1 Podcast RSS Standard

v1.2.3(4mo ago)1721.4k—7.1%6[1 issues](https://github.com/phanan/poddle/issues)1MITPHPPHP &gt;=8.1CI passing

Since May 18Pushed 4mo ago1 watchersCompare

[ Source](https://github.com/phanan/poddle)[ Packagist](https://packagist.org/packages/phanan/poddle)[ GitHub Sponsors](https://github.com/phanan)[ RSS](/packages/phanan-poddle/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (12)Versions (13)Used By (1)

Poddle – PHP Podcast Feed Parser [![Unit Tests](https://github.com/phanan/poddle/actions/workflows/unit.yml/badge.svg)](https://github.com/phanan/poddle/actions/workflows/unit.yml)
====================================================================================================================================================================================

[](#poddle--php-podcast-feed-parser-)

[![Poddle](./assets/banner.webp)](./assets/banner.webp)

> Effortlessly parse podcast feeds in PHP following [PSP-1 Podcast RSS Standard](https://github.com/Podcast-Standards-Project/PSP-1-Podcast-RSS-Specification).

Requirements and Installation
-----------------------------

[](#requirements-and-installation)

Poddle requires PHP 8.1 or higher. You can install the library via Composer by running the following command:

```
composer require phanan/poddle
```

Usage
-----

[](#usage)

### Parse from a URL

[](#parse-from-a-url)

To parse a podcast feed from its URL, call the `fromUrl` method with the feed URL:

```
$poddle = \PhanAn\Poddle::fromUrl('https://example.com/feed.xml');
```

This method also accepts two additional parameters:

- `timeoutInSeconds`: The number of seconds to wait while trying to connect. Defaults to 30. Note that the `max_execution_time` value in your PHP configuration may still limit the maximum timeout value.
- `client`: A PSR-7-compliant client to make the request. If not provided, Poddle will use a default client. This parameter may come in handy during testing or if you need to heavily customize the request.

### Parse from XML

[](#parse-from-xml)

If you already have the XML string, you can parse it using `Poddle::fromXml` instead:

```
$poddle = \PhanAn\Poddle::fromXml(file_read_contents('feed.xml'));
```

Upon success, both `fromUrl` and `fromXml` methods return a `Poddle` object, which you can use to access the feed's channel and episodes.

### Channel

[](#channel)

To access the podcast channel, call `getChannel` on the `Poddle` object:

```
/** @var \PhanAn\Poddle\Values\Channel $channel */
$channel = $poddle->getChannel();
```

All channel's [required elements](https://github.com/Podcast-Standards-Project/PSP-1-Podcast-RSS-Specification#required-channel-elements) per the PSP-1 standard are available as properties on the `Channel` object:

```
$channel->url; // string
$channel->atomLink; // string. Alias of $channel->url
$channel->title; // string
$channel->link; // string
$channel->description; // string
$channel->language; // string
$channel->image; // string
$channel->categories; // \PhanAn\Poddle\Values\CategoryCollection
$channel->explicit; // bool
```

All channel’s [recommended elements](https://github.com/Podcast-Standards-Project/PSP-1-Podcast-RSS-Specification#recommended-channel-elements) are available via the `metadata` property:

```
$channel->metadata; // \PhanAn\Poddle\Values\ChannelMetadata
$channel->metadata->locked; // bool
$channel->metadata->guid; // ?string
$channel->metadata->author; // ?string
$channel->metadata->copyright; // ?string
$channel->metadata->txts; // \PhanAn\Poddle\Values\TxtCollection
$channel->metadata->fundings; // \PhanAn\Poddle\Values\FundingCollection
$channel->metadata->type; // ?\PhanAn\Poddle\Values\PodcastType
$channel->metadata->complete; // bool
```

### Episodes

[](#episodes)

To access the podcast episodes, call `getEpisodes` on the `Poddle` object:

```
$episodes = $poddle->getEpisodes();
```

By default, `getEpisodes` will throw an error if any of the episodes is malformed. If you want a more forgiving behavior, pass `true` into the call to silently ignore the invalid episodes.

This method returns a [lazy collection](https://laravel.com/docs/11.x/collections#lazy-collections) of `\PhanAn\Poddle\Values\Episode` objects. You can iterate over the collection to access each episode:

```
$episodes->each(function (\PhanAn\Poddle\Values\Episode $episode) {
    // Access episode properties
});
```

All episode's [required elements](https://github.com/Podcast-Standards-Project/PSP-1-Podcast-RSS-Specification#required-item-elements) per the PSP-1 standard are available as properties on the `Episode` object:

```
$episode->title; // string
$episode->enclosure; // \PhanAn\Poddle\Values\Enclosure
$episode->guid; // \PhanAn\Poddle\Values\EpisodeGuid
```

All episode's [recommended elements](https://github.com/Podcast-Standards-Project/PSP-1-Podcast-RSS-Specification#recommended-item-elements) are available via the `metadata` property:

```
$episode->metadata; // \PhanAn\Poddle\Values\EpisodeMetadata
$episode->metadata->link; // ?string
$episode->metadata->pubDate; // ?\DateTime
$episode->metadata->description; // ?string
$episode->metadata->duration; // ?int
$episode->metadata->image; // ?string
$episode->metadata->explicit; // ?bool
$episode->metadata->transcripts; // \PhanAn\Poddle\Values\TranscriptCollection
$episode->metadata->episode; // ?int
$episode->metadata->season; // ?int
$episode->metadata->type; // ?\PhanAn\Poddle\Values\EpisodeType
$episode->metadata->block; // ?bool
```

### Other Elements and Values

[](#other-elements-and-values)

If you need to access other elements or values not covered by the PSP-1 standard, you can make use of the `$xmlReader` property on the `Poddle` object:

```
$xmlReader = $poddle->xmlReader;
```

This property is an instance of `Saloon\XmlWrangler\XmlReader` and allows you to navigate the XML document directly. For example, to access the feed's `lastBuildDate` value:

```
$poddle = \PhanAn\Poddle::fromUrl('https://example.com/feed.xml');
$poddle->xmlReader->value('rss.channel.lastBuildDate')?->sole(); // 'Thu, 02 May 2024 06:44:38 +0000'
```

For more information on how to use `XmlReader`, refer to [Saloon\\XmlWrangler documentation](https://github.com/saloonphp/xml-wrangler).

The original feed content is available via the `xml` property on the `Poddle` object:

```
$xml = $poddle->xml; // string
```

Serialization and Deserialization
---------------------------------

[](#serialization-and-deserialization)

All classes under the `PhanAn\Poddle\Values` namespace implement the [`\Illuminate\Contracts\Support\Arrayable`](https://laravel.com/api/11.x/Illuminate/Contracts/Support/Arrayable.html)and [`\Illuminate\Contracts\Support\Jsonable`](https://laravel.com/api/11.x/Illuminate/Contracts/Support/Jsonable.html) contracts, which provide two methods:

```
/**
  * Get the instance as an array. All nested objects are also converted to arrays.
  */
public function toArray(): array;

/**
  * Convert the object to its JSON representation.
  */
public function toJson($options = 0): string;
```

Additionally, classes like `Channel` and `Episode` provide `fromArray` static methods to create instances from arrays. These methods allow you to easily serialize and deserialize the objects, making it straightforward to store and retrieve the data in a database or JSON file. For instance, you can create an Eloquent [custom cast](https://laravel.com/docs/11.x/eloquent-mutators#custom-casts) in Laravel this way:

```
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use PhanAn\Poddle\Values\Channel;

class ChannelCast implements CastsAttributes
{
    public function get($model, string $key, $value, array $attributes): Channel
    {
        return Channel::fromArray(json_decode($value, true));
    }

    /** @param Channel $value */
    public function set($model, string $key, $value, array $attributes)
    {
        return $value->toJson();
    }
}
```

Then, you can use the cast in your Eloquent model:

```
use Illuminate\Database\Eloquent\Model;

class Podcast extends Model
{
    protected $casts = [
        'channel' => ChannelCast::class,
    ];
}
```

Possible Questions
------------------

[](#possible-questions)

### Why does Poddle not include element or value X from the feed?

[](#why-does-poddle-not-include-element-or-value-x-from-the-feed)

Poddle follows the PSP-1 standard, which specifies the required and recommended elements for a podcast feed. If an element or value is not part of the standard, it is not included in Poddle. However, you can still access any element or value using the `xmlReader` property as described above.

### How come `pubDate` is not a required element for episodes?

[](#how-come-pubdate-is-not-a-required-element-for-episodes)

The PSP-1 standard does not require `pubDate` for episodes, but it is a recommended element. As a result, `pubDate` is available as part of the episode's metadata as a nullable `\DateTime` object. It’s up to you to determine if the value always presents and design your system accordingly.

### Why is the episode's GUID an object instead of a string?

[](#why-is-the-episodes-guid-an-object-instead-of-a-string)

Per PSP-1 standard, an item’s `` element indeed contains a globally unique string value, but it can also have an attribute `isPermaLink` that indicates whether the GUID is a permalink. As such, the item GUID in Poddle is represented as an object with two public properties: `value` (string) and `isPermaLink` (bool). The object, however, implements the `__toString` method, so you can cast it to a string for convenience.

### Where is an episode’s media URL?

[](#where-is-an-episodes-media-url)

The media URL for an episode is available as part of the episode's [`enclosure` property](https://github.com/Podcast-Standards-Project/PSP-1-Podcast-RSS-Specification#item-enclosure).

### Why are the episodes returned as an `EpisodeCollection extends LazyCollection` object? What’s a lazy collection anyway?

[](#why-are-the-episodes-returned-as-an-episodecollection-extends-lazycollection-object-whats-a-lazy-collection-anyway)

The `LazyCollection` class leverages [PHP's generators](https://www.php.net/manual/en/language.generators.overview.php) to allow you to work with very large datasets while keeping memory usage low. Since a podcast feed can potentially contain a large number of episodes, returning a `LazyCollection` allows you to iterate over the episodes without loading them all into memory at once, speeding up the process and reducing memory consumption.

### Can you support feature X/Y/Z?

[](#can-you-support-feature-xyz)

Poddle aims to be a lightweight and efficient podcast feed parser that follows the PSP-1 standard, not a full-blown RSS/Atom parser. That said, if you have a feature request or suggestion, feel free to [open an issue](https://github.com/phanan/poddle/issues/new). Better yet, you can fork the repository, implement the feature yourself, and submit a pull request.

###  Health Score

50

—

FairBetter than 96% of packages

Maintenance76

Regular maintenance activity

Popularity39

Limited adoption so far

Community18

Small or concentrated contributor base

Maturity56

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 76.9% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~55 days

Recently: every ~111 days

Total

12

Last Release

127d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/cf42e22d8fba4d5475bd6dae13887c897bbdaa99765b209010c09b7fc1f3aef7?d=identicon)[phanan](/maintainers/phanan)

---

Top Contributors

[![phanan](https://avatars.githubusercontent.com/u/8056274?v=4)](https://github.com/phanan "phanan (20 commits)")[![panVag](https://avatars.githubusercontent.com/u/68543979?v=4)](https://github.com/panVag "panVag (2 commits)")[![bencarrr](https://avatars.githubusercontent.com/u/8208631?v=4)](https://github.com/bencarrr "bencarrr (1 commits)")[![eruraindil](https://avatars.githubusercontent.com/u/2336581?v=4)](https://github.com/eruraindil "eruraindil (1 commits)")[![veekthoven](https://avatars.githubusercontent.com/u/32249717?v=4)](https://github.com/veekthoven "veekthoven (1 commits)")[![vool](https://avatars.githubusercontent.com/u/441840?v=4)](https://github.com/vool "vool (1 commits)")

---

Tags

feedooppodcastsxmlxml-parserxmlparserfeedrsspodcastpsp-1

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/phanan-poddle/health.svg)

```
[![Health](https://phpackages.com/badges/phanan-poddle/health.svg)](https://phpackages.com/packages/phanan-poddle)
```

###  Alternatives

[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

245.2k](/packages/aedart-athenaeum)[laravie/parser

XML Document Parser for PHP

2342.1M8](/packages/laravie-parser)[torann/podcastfeed

Generate RSS feeds for podcasts in Laravel 5.

485.9k](/packages/torann-podcastfeed)[vipnytt/sitemapparser

XML Sitemap parser class compliant with the Sitemaps.org protocol.

772.2M10](/packages/vipnytt-sitemapparser)[glhd/conveyor-belt

14797.0k](/packages/glhd-conveyor-belt)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
