-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
51 changed files
with
11,382 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
github: [phanan] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
.phpunit.cache/ | ||
.idea/ | ||
.phpunit.result.cache | ||
vendor/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} |
Oops, something went wrong.