From 9b726b1991f6bcff9fa8da653d70ce32448c28f2 Mon Sep 17 00:00:00 2001 From: alexdebril Date: Mon, 9 May 2022 17:48:29 +0200 Subject: [PATCH] Issue/390 (#397) * Fiter items through `Result`. Fix #390 * Fix coding style * remove backslash * Advertise filtering ability --- README.md | 45 ++++++++++++++++++++---- src/FeedIo/Filter/Chain.php | 30 ++++++++++++++++ src/FeedIo/Filter/FilterInterface.php | 18 ++++++++++ src/FeedIo/Filter/Since.php | 23 +++++++++++++ src/FeedIo/Reader/Result.php | 15 ++++++++ tests/FeedIo/Filter/ChainTest.php | 49 +++++++++++++++++++++++++++ tests/FeedIo/Filter/SinceTest.php | 17 ++++++++++ 7 files changed, 191 insertions(+), 6 deletions(-) create mode 100644 src/FeedIo/Filter/Chain.php create mode 100644 src/FeedIo/Filter/FilterInterface.php create mode 100644 src/FeedIo/Filter/Since.php create mode 100644 tests/FeedIo/Filter/ChainTest.php create mode 100644 tests/FeedIo/Filter/SinceTest.php diff --git a/README.md b/README.md index 52ccb10..a1f2f8d 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ - Enclosure support to handle external medias like audio content - Feed logo support (RSS + Atom) - PSR compliant logging +- Content filtering to fetch only the newest items - DateTime detection and conversion - A generic HTTP ClientInterface - Guzzle Client integration @@ -49,12 +50,6 @@ Let's suppose you installed feed-io using Composer, you can use its command line ./vendor/bin/feedio read http://php.net/feed.atom ``` -You can specify the number of items you want to read using the --count option. The instruction below will display the latest item : - -```shell -./vendor/bin/feedio read -c 1 http://php.net/feed.atom -``` - ## reading feed-io is designed to read feeds across the internet and to publish your own. Its main class is [FeedIo](https://github.com/alexdebril/feed-io/blob/master/src/FeedIo/FeedIo.php) : @@ -76,6 +71,44 @@ foreach( $result->getFeed() as $item ) { } ``` + +If you need to get only the new items since the last time you've consumed the feed, use the result's `getItemsSince()` method: + +```php +// read a feed and specify the `$modifiedSince` limit to fetch only items newer than this date +$result = $feedIo->read($url, $feed, $modifiedSince); + +// iterate through new items +foreach( $result->getItemsSince() as $item ) { + echo $item->getTitle(); +} + +``` + +You can also mix several filters to exclude items according to your needs: + +```php +// read a feed +$result = $feedIo->read($url, $feed, $modifiedSince); + +// remove items older than `$modifiedSince` +$since = new FeedIo\Filter\Since($result->getModifiedSince()); + +// Your own filter +$database = new Acme\Filter\Database(); + +$chain = new Chain(); +$chain + ->add($since) + ->add($database); + +// iterate through new items +foreach( $result->getFilteredItems($chain) as $item ) { + echo $item->getTitle(); +} + +``` + In order to save bandwidth, feed-io estimates the next time it will be relevant to read the feed and get new items from it. ```php diff --git a/src/FeedIo/Filter/Chain.php b/src/FeedIo/Filter/Chain.php new file mode 100644 index 0000000..a1b7bc5 --- /dev/null +++ b/src/FeedIo/Filter/Chain.php @@ -0,0 +1,30 @@ +filters[] = $filter; + } + + public function filter(FeedInterface $feed): iterable + { + foreach ($feed as $item) { + foreach ($this->filters as $filter) { + if (!$filter->filter($item)) { + continue 2; + } + } + + yield $item; + } + } +} diff --git a/src/FeedIo/Filter/FilterInterface.php b/src/FeedIo/Filter/FilterInterface.php new file mode 100644 index 0000000..cc95006 --- /dev/null +++ b/src/FeedIo/Filter/FilterInterface.php @@ -0,0 +1,18 @@ +date = $date; + } + + public function filter(ItemInterface $item): bool + { + return $item->getLastModified() >= $this->date; + } +} diff --git a/src/FeedIo/Reader/Result.php b/src/FeedIo/Reader/Result.php index 4a111cf..362a6a1 100644 --- a/src/FeedIo/Reader/Result.php +++ b/src/FeedIo/Reader/Result.php @@ -7,6 +7,8 @@ use DateTime; use FeedIo\Adapter\ResponseInterface; use FeedIo\FeedInterface; +use FeedIo\Filter\Chain; +use FeedIo\Filter\Since; use FeedIo\Reader\Result\UpdateStats; /** @@ -52,6 +54,19 @@ public function getFeed(): FeedInterface return $this->feed; } + public function getItemsSince(DateTime $since = null): iterable + { + $filter = new Chain(); + $filter->add(new Since($since ?? $this->modifiedSince)); + + return $filter->filter($this->getFeed()); + } + + public function getFilteredItems(Chain $filterChain): iterable + { + return $filterChain->filter($this->feed); + } + public function getModifiedSince(): ?DateTime { return $this->modifiedSince; diff --git a/tests/FeedIo/Filter/ChainTest.php b/tests/FeedIo/Filter/ChainTest.php new file mode 100644 index 0000000..e1b70a0 --- /dev/null +++ b/tests/FeedIo/Filter/ChainTest.php @@ -0,0 +1,49 @@ +add(new Since(new \DateTime('-1 day'))); + + $feed = new Feed(); + $feed->add((new Item())->setLastModified(new \DateTime('-1 hour'))); + $feed->add((new Item())->setLastModified(new \DateTime('-1 day'))); + $feed->add((new Item())->setLastModified(new \DateTime('-1 month'))); + + $filtered = $chain->filter($feed); + $this->assertEquals(2, iterator_count($filtered)); + } + + public function testFancyFilter() + { + $chain = new Chain(); + $fancyFilter = $this->getMockForAbstractClass('\FeedIo\Filter\FilterInterface'); + $fancyFilter->expects($this->exactly(2))->method('filter')->will($this->returnCallback(function (Item $item) { + return $item->getTitle() === '1 day ago'; + })); + + $chain->add(new Since(new \DateTime('-1 day'))); + $chain->add($fancyFilter); + + $feed = new Feed(); + $feed->add((new Item())->setTitle('1 hour ago')->setLastModified(new \DateTime('-1 hour'))); + $feed->add((new Item())->setTitle('1 day ago')->setLastModified(new \DateTime('-1 day'))); + $feed->add((new Item())->setTitle('1 month ago')->setLastModified(new \DateTime('-1 month'))); + + $filtered = $chain->filter($feed); + $count = 0; + foreach ($filtered as $item) { + $count++; + } + $this->assertEquals(1, $count); + $this->assertEquals('1 day ago', $item->getTitle()); + } +} diff --git a/tests/FeedIo/Filter/SinceTest.php b/tests/FeedIo/Filter/SinceTest.php new file mode 100644 index 0000000..90d9666 --- /dev/null +++ b/tests/FeedIo/Filter/SinceTest.php @@ -0,0 +1,17 @@ +assertTrue($filter->filter((new Item())->setLastModified(new \DateTime('-1 hour')))); + $this->assertTrue($filter->filter((new Item())->setLastModified(new \DateTime('-1 day')))); + $this->assertFalse($filter->filter((new Item())->setLastModified(new \DateTime('-2 day')))); + } +}