Skip to content

Commit

Permalink
feat: v1 features
Browse files Browse the repository at this point in the history
  • Loading branch information
phanan committed May 18, 2024
1 parent 2febfca commit 26893ba
Show file tree
Hide file tree
Showing 51 changed files with 11,382 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[*]
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2

[{*.php,*.xml,*.xml.dist,*.rss}]
indent_size = 4
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
github: [phanan]
35 changes: 35 additions & 0 deletions .github/workflows/unit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Unit Tests
on:
pull_request:
branches:
- main
push:
branches:
- main
workflow_dispatch:
branches:
- main
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
php-version: [ 8.1, 8.2 ]
fail-fast: false
steps:
- uses: actions/checkout@v4
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
tools: composer:v2
- name: Install PHP dependencies
uses: ramsey/composer-install@v3
with:
composer-options: --prefer-dist
- name: Run code style checker
run: composer cs
- name: Run static analysis
run: composer analyze -- --no-progress
- name: Run tests
run: composer test
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.phpunit.cache/
.idea/
.phpunit.result.cache
vendor/
188 changes: 188 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
# 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](./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

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

```bash
composer require phanan/poddle
```

## Usage

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

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

It is possible to configure the timeout value for the request by passing an integer as the second parameter for `Poddle::fromUrl`.
For total control, you can make the request yourself (for example using an HTTP client) and pass the response body to `Poddle::fromXml` instead:

```php
use Illuminate\Support\Facades\Http;

$poddle = \PhanAn\Poddle::fromXml(Http::timeout(60)
->withoutVerifying()
->get('https://example.com/feed.xml')
->body()
);
```

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

### Channel

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

```php
/** @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:

```php
$channel->title; // string
$channel->link; // string
$channel->description; // string
$channel->language; // string
$channel->image; // string
$channel->categories; // \PhanAn\Poddle\Values\CategoryCollection<\PhanAn\Poddle\Values\Category>
$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:

```php
$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<\PhanAn\Poddle\Values\Txt>
$channel->metadata->fundings; // \PhanAn\Poddle\Values\FundingCollection<\PhanAn\Poddle\Values\Funding>
$channel->metadata->type; // ?\PhanAn\Poddle\Values\PodcastType
$channel->metadata->complete; // bool
```

### Episodes

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

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

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:

```php
$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:

```php
$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:

```php
$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<\PhanAn\Poddle\Values\Transcript>
$episode->metadata->episode; // ?int
$episode->metadata->season; // ?int
$episode->metadata->type; // ?\PhanAn\Poddle\Values\EpisodeType
$episode->metadata->block; // ?bool
```

### 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:

```php
$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:

```php
$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:

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

## 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:

```php
/**
* 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:

```php
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:

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

class Podcast extends Model
{
protected $casts = [
'channel' => ChannelCast::class,
];
}
```
Binary file added assets/banner.webp
Binary file not shown.
59 changes: 59 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"name": "phanan/poddle",
"description": "Parse podcast feeds with PHP following PSP-1 Podcast RSS Standard",
"authors": [
{
"name": "Phan An",
"email": "[email protected]"
}
],
"keywords": [
"podcast",
"xml",
"rss",
"feed",
"parser",
"psp-1"
],
"license": "MIT",
"type": "library",
"require": {
"php": ">=8.1",
"saloonphp/xml-wrangler": "^1.2",
"illuminate/collections": "^10.48",
"illuminate/http": "^10.48",
"illuminate/support": "^10.48",
"guzzlehttp/guzzle": "^7.8"
},
"require-dev": {
"phpunit/phpunit": ">=10.5",
"laravel/pint": "^1.15",
"larastan/larastan": "^2.9",
"orchestra/testbench": "*",
"laravel/tinker": "^2.9"
},
"scripts": {
"test": "phpunit tests",
"cs": "pint --test",
"cs:fix": "pint",
"analyze": "phpstan analyse"
},
"post-install-cmd": [
"composer dump-autoload"
],
"autoload": {
"psr-4": {
"PhanAn\\Poddle\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"config": {
"preferred-install": "dist",
"optimize-autoloader": true
},
"minimum-stability": "stable"
}
Loading

0 comments on commit 26893ba

Please sign in to comment.