Skip to content
This repository was archived by the owner on Sep 15, 2025. It is now read-only.

Commit 9b726b1

Browse files
authored
Issue/390 (#397)
* Fiter items through `Result`. Fix #390 * Fix coding style * remove backslash * Advertise filtering ability
1 parent f5f06ea commit 9b726b1

File tree

7 files changed

+191
-6
lines changed

7 files changed

+191
-6
lines changed

README.md

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- Enclosure support to handle external medias like audio content
1717
- Feed logo support (RSS + Atom)
1818
- PSR compliant logging
19+
- Content filtering to fetch only the newest items
1920
- DateTime detection and conversion
2021
- A generic HTTP ClientInterface
2122
- Guzzle Client integration
@@ -49,12 +50,6 @@ Let's suppose you installed feed-io using Composer, you can use its command line
4950
./vendor/bin/feedio read http://php.net/feed.atom
5051
```
5152

52-
You can specify the number of items you want to read using the --count option. The instruction below will display the latest item :
53-
54-
```shell
55-
./vendor/bin/feedio read -c 1 http://php.net/feed.atom
56-
```
57-
5853
## reading
5954

6055
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 ) {
7671
}
7772

7873
```
74+
75+
If you need to get only the new items since the last time you've consumed the feed, use the result's `getItemsSince()` method:
76+
77+
```php
78+
// read a feed and specify the `$modifiedSince` limit to fetch only items newer than this date
79+
$result = $feedIo->read($url, $feed, $modifiedSince);
80+
81+
// iterate through new items
82+
foreach( $result->getItemsSince() as $item ) {
83+
echo $item->getTitle();
84+
}
85+
86+
```
87+
88+
You can also mix several filters to exclude items according to your needs:
89+
90+
```php
91+
// read a feed
92+
$result = $feedIo->read($url, $feed, $modifiedSince);
93+
94+
// remove items older than `$modifiedSince`
95+
$since = new FeedIo\Filter\Since($result->getModifiedSince());
96+
97+
// Your own filter
98+
$database = new Acme\Filter\Database();
99+
100+
$chain = new Chain();
101+
$chain
102+
->add($since)
103+
->add($database);
104+
105+
// iterate through new items
106+
foreach( $result->getFilteredItems($chain) as $item ) {
107+
echo $item->getTitle();
108+
}
109+
110+
```
111+
79112
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.
80113

81114
```php

src/FeedIo/Filter/Chain.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace FeedIo\Filter;
6+
7+
use FeedIo\FeedInterface;
8+
9+
class Chain
10+
{
11+
private array $filters;
12+
13+
public function add(FilterInterface $filter): void
14+
{
15+
$this->filters[] = $filter;
16+
}
17+
18+
public function filter(FeedInterface $feed): iterable
19+
{
20+
foreach ($feed as $item) {
21+
foreach ($this->filters as $filter) {
22+
if (!$filter->filter($item)) {
23+
continue 2;
24+
}
25+
}
26+
27+
yield $item;
28+
}
29+
}
30+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace FeedIo\Filter;
6+
7+
use FeedIo\Feed\ItemInterface;
8+
9+
interface FilterInterface
10+
{
11+
/**
12+
* Returns `true` if the item is to be returned.
13+
*
14+
* @param ItemInterface $item
15+
* @return bool
16+
*/
17+
public function filter(ItemInterface $item): bool;
18+
}

src/FeedIo/Filter/Since.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace FeedIo\Filter;
6+
7+
use DateTime;
8+
use FeedIo\Feed\ItemInterface;
9+
10+
class Since implements FilterInterface
11+
{
12+
private DateTime $date;
13+
14+
public function __construct(DateTime $date)
15+
{
16+
$this->date = $date;
17+
}
18+
19+
public function filter(ItemInterface $item): bool
20+
{
21+
return $item->getLastModified() >= $this->date;
22+
}
23+
}

src/FeedIo/Reader/Result.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
use DateTime;
88
use FeedIo\Adapter\ResponseInterface;
99
use FeedIo\FeedInterface;
10+
use FeedIo\Filter\Chain;
11+
use FeedIo\Filter\Since;
1012
use FeedIo\Reader\Result\UpdateStats;
1113

1214
/**
@@ -52,6 +54,19 @@ public function getFeed(): FeedInterface
5254
return $this->feed;
5355
}
5456

57+
public function getItemsSince(DateTime $since = null): iterable
58+
{
59+
$filter = new Chain();
60+
$filter->add(new Since($since ?? $this->modifiedSince));
61+
62+
return $filter->filter($this->getFeed());
63+
}
64+
65+
public function getFilteredItems(Chain $filterChain): iterable
66+
{
67+
return $filterChain->filter($this->feed);
68+
}
69+
5570
public function getModifiedSince(): ?DateTime
5671
{
5772
return $this->modifiedSince;

tests/FeedIo/Filter/ChainTest.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
namespace FeedIo\Filter;
4+
5+
use FeedIo\Feed;
6+
use FeedIo\Feed\Item;
7+
use PHPUnit\Framework\TestCase;
8+
9+
class ChainTest extends TestCase
10+
{
11+
public function testFilter()
12+
{
13+
$chain = new Chain();
14+
$chain->add(new Since(new \DateTime('-1 day')));
15+
16+
$feed = new Feed();
17+
$feed->add((new Item())->setLastModified(new \DateTime('-1 hour')));
18+
$feed->add((new Item())->setLastModified(new \DateTime('-1 day')));
19+
$feed->add((new Item())->setLastModified(new \DateTime('-1 month')));
20+
21+
$filtered = $chain->filter($feed);
22+
$this->assertEquals(2, iterator_count($filtered));
23+
}
24+
25+
public function testFancyFilter()
26+
{
27+
$chain = new Chain();
28+
$fancyFilter = $this->getMockForAbstractClass('\FeedIo\Filter\FilterInterface');
29+
$fancyFilter->expects($this->exactly(2))->method('filter')->will($this->returnCallback(function (Item $item) {
30+
return $item->getTitle() === '1 day ago';
31+
}));
32+
33+
$chain->add(new Since(new \DateTime('-1 day')));
34+
$chain->add($fancyFilter);
35+
36+
$feed = new Feed();
37+
$feed->add((new Item())->setTitle('1 hour ago')->setLastModified(new \DateTime('-1 hour')));
38+
$feed->add((new Item())->setTitle('1 day ago')->setLastModified(new \DateTime('-1 day')));
39+
$feed->add((new Item())->setTitle('1 month ago')->setLastModified(new \DateTime('-1 month')));
40+
41+
$filtered = $chain->filter($feed);
42+
$count = 0;
43+
foreach ($filtered as $item) {
44+
$count++;
45+
}
46+
$this->assertEquals(1, $count);
47+
$this->assertEquals('1 day ago', $item->getTitle());
48+
}
49+
}

tests/FeedIo/Filter/SinceTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace FeedIo\Filter;
4+
5+
use FeedIo\Feed\Item;
6+
use PHPUnit\Framework\TestCase;
7+
8+
class FilterTest extends TestCase
9+
{
10+
public function testFilter()
11+
{
12+
$filter = new Since(new \DateTime('-1 day'));
13+
$this->assertTrue($filter->filter((new Item())->setLastModified(new \DateTime('-1 hour'))));
14+
$this->assertTrue($filter->filter((new Item())->setLastModified(new \DateTime('-1 day'))));
15+
$this->assertFalse($filter->filter((new Item())->setLastModified(new \DateTime('-2 day'))));
16+
}
17+
}

0 commit comments

Comments
 (0)