danek/feed
Fork feed-io for (PHP >= 7.1 && PHP < 8) - 用于消费和提供JSONFeed / RSS / Atom订阅源的PHP库
Requires
- php: >=7.1
- ext-dom: *
- ext-json: *
- ext-libxml: *
- ext-mbstring: *
- guzzlehttp/guzzle: ~6.2|~7.0
- psr/log: ~1.0
This package is auto-updated.
Last update: 2024-09-21 02:28:16 UTC
README
通知
这是一个4分支的分支版本,由于需要运行最低PHP 7.1+包括后续版本,因此存在。原始库处理后续版本的PHP环境。分支的内容已被清除。它已被移除了我不使用的测试和其他工具。
这是一个用于消费和提供新闻订阅源的PHP库。它具有以下特点:
- 支持JSONFeed / Atom / RSS的读写
- 通过HTML头部自动发现订阅源
- 通过异步请求一次性读取多个订阅源
- 使用准确的缓存头部生成PSR-7响应
- 在读取订阅源时支持HTTP头部以节省网络流量
- 在读取订阅源时检测格式(RSS / Atom)
- 支持封装以处理外部媒体,如音频内容
- 支持订阅源Logo(RSS + Atom)
- PSR兼容的日志记录
- 内容过滤以获取最新条目
- 自动纠正损坏的订阅源
- 检测和转换DateTime
- 通用的HTTP ClientInterface
- Guzzle客户端集成
安装
使用Composer将feed-io添加到项目的需求中
composer require danek/feed-io
要求
feed-io需要
- php 7.1+
- psr/log 1.0
- guzzlehttp/guzzle 6.2+
Monolog不是处理feed-io日志的唯一库,您可以使用任何PSR/Log兼容库。
获取仓库
如果您想做出贡献(并欢迎您这样做)
git clone https://github.com/jDanek/feed-io.git
cd feed-io/
composer install
用法
读取
feed-io旨在读取互联网上的订阅源并发布您自己的订阅源。其主要类是FeedIo
// create a simple FeedIo instance $feedIo = \Danek\FeedIo\Factory::create()->getFeedIo(); // read a feed $result = $feedIo->read($url); // or read a feed since a certain date $result = $feedIo->readSince($url, new \DateTime('-7 days')); // get title $feedTitle = $result->getFeed()->getTitle(); // iterate through items foreach( $result->getFeed() as $item ) { echo $item->getTitle(); }
为了节省带宽,feed-io会估计下一次读取订阅源并从中获取新条目的相关时间。
$nextUpdate = $result->getNextUpdate(); echo "computed next update: {$nextUpdate->format(\DATE_ATOM)}"; // you may need to access the statistics $updateStats = $result->getUpdateStats(); echo "average interval in seconds: {$updateStats->getAverageInterval()}";
feed-io通过首先检测订阅源在过去7天内是否活跃,如果不是,则将其视为休眠。休眠订阅源的下一次更新日期设置为第二天同一时间。如果订阅源不处于休眠状态,则使用平均间隔和中间间隔,将间隔加到订阅源的最后修改日期上,并将结果与当前时间进行比较。如果结果是未来的,则返回为下一次更新时间。如果没有一个是未来的,则认为订阅源将很快更新,因此下一次更新时间是计算时刻后一小时。
请注意:休眠和即将更新的订阅源的固定延迟可以通过Result::getNextUpdate()
参数设置,有关更多详细信息,请参阅Result。
一次性异步读取多个订阅源
多亏了Guzzle,feed-io能够通过异步请求一次性获取多个订阅源。如果您想了解更多关于其工作方式的信息,可以阅读Guzzle的文档。
要使用feed-io异步请求读取源,您需要向\Danek\FeedIo\FeedIo::readAsync
发送一组\Danek\FeedIo\Async\Request
对象,并使用您自己的\Danek\FeedIo\Async\CallbackInterface
处理结果。您还可以使用\Danek\FeedIo\Async\DefaultCallback
来测试功能。
每个\Danek\FeedIo\Async\Request
都是一个您想要执行请求,它包含源URL和可选的\DateTime
来定义请求的modified-since
属性。
CallbackInterface
实例需要两个方法
/** * @param Result $result */ public function process(Result $result) : void; /** * @param Request $request * @param \Exception $exception */ public function handleError(Request $request, \Exception $exception) : void;
process()
在成功读取和解析后被调用,以便您处理结果。否则,在调用错误时将触发handleError()
。以下是一个示例:PDOCallback
源发现
一个网页可以在其头部引用一个或多个源,feed-io提供了一种发现它们的方法
$feedIo = \Danek\FeedIo\Factory::create()->getFeedIo(); $feeds = $feedIo->discover($url); foreach( $feeds as $feed ) { echo "discovered feed : {$feed}"; }
或者您可以使用feed-io的命令行
./vendor/bin/feedio discover https://a-website.org
您将在输出中获得所有发现的源。
将对象格式化为XML流
// build the feed $feed = new Danek\Danek\FeedIo\Feed; $feed->setTitle('...'); // convert it into Atom $atomString = $feedIo->toAtom($feed); // or ... $atomString = $feedIo->format($feed, 'atom');
添加样式表
$feed = new \Danek\FeedIo\FeedIo\Feed; $feed->setTitle('...'); $styleSheet = new StyleSheet('http://url-of-the-xsl-stylesheet.xsl'); $feed->setStyleSheet($styleSheet);
构建包含媒体的源
// build the feed $feed = new \Danek\FeedIo\FeedIo\Feed; $feed->setTitle('...'); $item = $feed->newItem(); // add namespaces $feed->setNS( 'itunes', //namespace 'http://www.itunes.com/dtds/podcast-1.0.dtd' //dtd for the namespace ); $feed->set('itunes,title', 'Sample Title'); //OR any other element defined in the namespace. $item->addElement('itunes:category', 'Education'); // build the media $media = new \Danek\FeedIo\Feed\Item\Media $media->setUrl('http://yourdomain.tld/medias/some-podcast.mp3'); $media->setType('audio/mpeg'); // add it to the item $item->addMedia($media); $feed->add($item);
使用源创建有效的PSR-7响应
您可以使用\Danek\FeedIo\FeedIo::getPsrResponse()
将\Danek\FeedIo\FeedInstance
直接转换为PSR-7有效响应。
$feed = new \Danek\FeedIo\Feed; // feed the beast ... $item = new \Danek\FeedIo\Feed\Item; $item->set ... $feed->add($item); $atomResponse = $feedIo->getPsrResponse($feed, 'atom'); $jsonResponse = $feedIo->getPsrResponse($feed, 'json');
使用工厂配置feed-io
在读取部分中,我们看到可以通过静态调用Factory
来获取简单的FeedIo
实例,并让它返回一个由主要依赖项组成的新的FeedIo
。问题是我们可能想要向其底层组件注入配置,例如配置Guzzle忽略SSL错误。
为此,我们将通过Factory::create()
参数注入配置,第一个是用于日志系统,第二个是用于HTTP客户端(好吧,Guzzle)。
通过工厂配置Guzzle
在上面的几行中,我们谈到了忽略SSL错误,让我们看看如何配置Guzzle来完成这项工作
$feedIo = \Danek\FeedIo\Factory::create( ['builder' => 'NullLogger'], // assuming you want feed-io to keep quiet ['builder' => 'GuzzleClient', 'config' => ['verify' => false]] )->getFeedIo();
指定“builder”非常重要,因为这个类将负责实际构建实例。
激活日志
feed-io原生支持PSR-3日志,您可以通过在工厂中选择一个'builder'来激活它
$feedIo = \Danek\FeedIo\Factory::create(['builder' => 'monolog'])->getFeedIo();
feed-io仅提供创建Monolog\Logger实例的builder。您可以编写自己的,只要Builder实现BuilderInterface即可。
不使用工厂构建FeedIo实例
要创建一个新的FeedIo实例,您只需要注入两个依赖项
- 一个实现FeedIo\Adapter\ClientInterface的HTTP客户端。它可以是对外部库(如FeedIo\Adapter\Guzzle\Client)的包装
- 一个实现Psr\Log\LoggerInterface的PSR-3日志记录器
// first dependency : the HTTP client // here we use Guzzle as a dependency for the client $guzzle = new GuzzleHttp\Client(); // Guzzle is wrapped in this adapter which is a FeedIo\Adapter\ClientInterface implementation $client = new \Danek\FeedIo\FeedIo\Adapter\Guzzle\Client($guzzle); // second dependency : a PSR-3 logger $logger = new Psr\Log\NullLogger(); // now create FeedIo's instance $feedIo = new \Danek\FeedIo\FeedIo\FeedIo($client, $logger);
另一个使用配置为将输出写入标准输出的Monolog的示例
use \Danek\FeedIo\FeedIo\FeedIo; use \Danek\FeedIo\FeedIo\Adapter\Guzzle\Client; use GuzzleHttp\Client as GuzzleClient; use Monolog\Logger; use Monolog\Handler\StreamHandler; $client = new Client(new GuzzleClient()); $logger = new Logger('default', [new StreamHandler('php://stdout')]); $feedIo = new FeedIo($client, $logger);
Guzzle配置
您可以在将其注入到FeedIo
之前配置Guzzle
use \Danek\FeedIo\FeedIo\FeedIo; use \Danek\FeedIo\FeedIo\Adapter\Guzzle\Client; use GuzzleHttp\Client as GuzzleClient; use \Psr\Log\NullLogger; // We want to timeout after 3 seconds $guzzle = new GuzzleClient(['timeout' => 3]); $client = new Client($guzzle); $logger = new NullLogger(); $feedIo = new \Danek\FeedIo\FeedIo($client, $logger);
请阅读Guzzle的文档以获取有关其配置的更多信息。
缓存中间件的使用
为了防止您的应用程序多次击中相同的源,您可以将Kevin Rob的缓存中间件注入到Guzzle的实例中
use \Danek\FeedIo\FeedIo\FeedIo; use \Danek\FeedIo\FeedIo\Adapter\Guzzle\Client; use GuzzleHttp\Client As GuzzleClient; use GuzzleHttp\HandlerStack; use Kevinrob\GuzzleCache\CacheMiddleware; use Psr\Log\NullLogger; // Create default HandlerStack $stack = HandlerStack::create(); // Add this middleware to the top with `push` $stack->push(new CacheMiddleware(), 'cache'); // Initialize the client with the handler option $guzzle = new GuzzleClient(['handler' => $stack]); $client = new Client($guzzle); $logger = new NullLogger(); $feedIo = new \Danek\FeedIo\FeedIo($client, $logger);
由于源的内容可能经常变化,缓存可能导致不期望的行为。
注入自定义Logger
只要它实现了Psr\Log\LoggerInterface
,您就可以注入任何Logger。Monolog做到了,但它是唯一的库:https://packagist.org.cn/providers/psr/log-implementation
use \Danek\FeedIo\FeedIo\FeedIo; use \Danek\FeedIo\FeedIo\Adapter\Guzzle\Client; use GuzzleHttp\Client as GuzzleClient; use Custom\Logger; $client = new Client(new GuzzleClient()); $logger = new Logger(); $feedIo = new FeedIo($client, $logger);
注入自定义HTTP客户端
警告:强烈建议使用默认的Guzzle客户端集成。
如果您真的想使用其他库来读取源,您需要创建自己的FeedIo\Adapter\ClientInterface
类来嵌入与库的交互
use \Danek\FeedIo\FeedIo\FeedIo; use Custom\Adapter\Client; use Library\Client as LibraryClient; use Psr\Log\NullLogger; $client = new Client(new LibraryClient()); $logger = new NullLogger(); $feedIo = new FeedIo($client, $logger);
工厂模式或依赖注入?
在项目的某个阶段,您必须问自己是否要使用工厂模式或者不使用它来构建FeedIo
。工厂模式主要是为了让您能够以更少的努力使用feed-io,并在很短的时间内获得初步结果。然而,它并不让您充分利用Monolog和Guzzle的所有功能,这可能会让人烦恼。依赖注入也允许您在需要时选择另一个库来处理日志。
处理缺失时区的问题
有时您必须处理缺少时区的日期的源。在某些情况下,您可能需要指定源的时区以获得准确的值,因此feed-io为此提供了一个解决方案
$feedIo->getDateTimeBuilder()->setFeedTimezone(new \DateTimeZone($feedTimezone)); $result = $feedIo->read($feedUrl); $feedIo->getDateTimeBuilder()->resetFeedTimezone();
别忘了在获取结果后重置feedTimezone
,否则所有源都会位于同一时区。