refactor: deprecate FeedItem constructor (#4201)

* fix: bug in prior commit

* refactor: deprecate FeedItem constructor

* test: fix
This commit is contained in:
Dag 2024-08-08 03:43:26 +02:00 committed by GitHub
parent 2a96bf19b5
commit 6afd13eb06
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 78 additions and 114 deletions

View file

@ -93,7 +93,6 @@ class DisplayAction implements ActionInterface
private function createResponse(Request $request, BridgeAbstract $bridge, string $format) private function createResponse(Request $request, BridgeAbstract $bridge, string $format)
{ {
$items = []; $items = [];
$feed = [];
try { try {
$bridge->loadConfiguration(); $bridge->loadConfiguration();
@ -113,14 +112,6 @@ class DisplayAction implements ActionInterface
$bridge->setInput($input); $bridge->setInput($input);
$bridge->collectData(); $bridge->collectData();
$items = $bridge->getItems(); $items = $bridge->getItems();
if (isset($items[0]) && is_array($items[0])) {
$feedItems = [];
foreach ($items as $item) {
$feedItems[] = FeedItem::fromArray($item);
}
$items = $feedItems;
}
$feed = $bridge->getFeed();
} catch (\Throwable $e) { } catch (\Throwable $e) {
if ($e instanceof RateLimitException) { if ($e instanceof RateLimitException) {
// These are internally generated by bridges // These are internally generated by bridges
@ -162,7 +153,7 @@ class DisplayAction implements ActionInterface
$format = $formatFactory->create($format); $format = $formatFactory->create($format);
$format->setItems($items); $format->setItems($items);
$format->setFeed($feed); $format->setFeed($bridge->getFeed());
$now = time(); $now = time();
$format->setLastModified($now); $format->setLastModified($now);
$headers = [ $headers = [
@ -172,19 +163,20 @@ class DisplayAction implements ActionInterface
return new Response($format->stringify(), 200, $headers); return new Response($format->stringify(), 200, $headers);
} }
private function createFeedItemFromException($e, BridgeAbstract $bridge): FeedItem private function createFeedItemFromException($e, BridgeAbstract $bridge): array
{ {
$item = new FeedItem(); $item = [];
// Create a unique identifier every 24 hours // Create a unique identifier every 24 hours
$uniqueIdentifier = urlencode((int)(time() / 86400)); $uniqueIdentifier = urlencode((int)(time() / 86400));
$title = sprintf('Bridge returned error %s! (%s)', $e->getCode(), $uniqueIdentifier); $title = sprintf('Bridge returned error %s! (%s)', $e->getCode(), $uniqueIdentifier);
$item->setTitle($title);
$item->setURI(get_current_url()); $item['title'] = $title;
$item->setTimestamp(time()); $item['uri'] = get_current_url();
$item['timestamp'] = time();
// Create an item identifier for feed readers e.g. "staysafetv twitch videos_19389" // Create an item identifier for feed readers e.g. "staysafetv twitch videos_19389"
$item->setUid($bridge->getName() . '_' . $uniqueIdentifier); $item['uid'] = $bridge->getName() . '_' . $uniqueIdentifier;
$content = render_template(__DIR__ . '/../templates/bridge-error.html.php', [ $content = render_template(__DIR__ . '/../templates/bridge-error.html.php', [
'error' => render_template(__DIR__ . '/../templates/exception.html.php', ['e' => $e]), 'error' => render_template(__DIR__ . '/../templates/exception.html.php', ['e' => $e]),
@ -192,7 +184,8 @@ class DisplayAction implements ActionInterface
'issueUrl' => self::createGithubIssueUrl($bridge, $e, create_sane_exception_message($e)), 'issueUrl' => self::createGithubIssueUrl($bridge, $e, create_sane_exception_message($e)),
'maintainer' => $bridge->getMaintainer(), 'maintainer' => $bridge->getMaintainer(),
]); ]);
$item->setContent($content); $item['content'] = $content;
return $item; return $item;
} }

View file

@ -129,24 +129,24 @@ class GULPProjekteBridge extends WebDriverAbstract
while (true) { while (true) {
$items = $this->getDriver()->findElements(WebDriverBy::tagName('app-project-view')); $items = $this->getDriver()->findElements(WebDriverBy::tagName('app-project-view'));
foreach ($items as $item) { foreach ($items as $item) {
$feedItem = new FeedItem(); $feedItem = [];
$heading = $item->findElement(WebDriverBy::xpath('.//app-heading-tag/h1/a')); $heading = $item->findElement(WebDriverBy::xpath('.//app-heading-tag/h1/a'));
$feedItem->setTitle($heading->getText()); $feedItem['title'] = $heading->getText();
$feedItem->setURI('https://www.gulp.de' . $heading->getAttribute('href')); $feedItem['uri'] = 'https://www.gulp.de' . $heading->getAttribute('href');
$info = $item->findElement(WebDriverBy::tagName('app-icon-info-list')); $info = $item->findElement(WebDriverBy::tagName('app-icon-info-list'));
if ($logo = $this->getLogo($item)) { if ($logo = $this->getLogo($item)) {
$feedItem->setEnclosures([$logo]); $feedItem['enclosures'] = [$logo];
} }
if (str_contains($info->getText(), 'Projektanbieter:')) { if (str_contains($info->getText(), 'Projektanbieter:')) {
$feedItem->setAuthor($info->findElement(WebDriverBy::xpath('.//li/span[2]/span'))->getText()); $feedItem['author'] = $info->findElement(WebDriverBy::xpath('.//li/span[2]/span'))->getText();
} else { } else {
// mostly "Direkt vom Auftraggeber" or "GULP Agentur" // mostly "Direkt vom Auftraggeber" or "GULP Agentur"
$feedItem->setAuthor($item->findElement(WebDriverBy::tagName('b'))->getText()); $feedItem['author'] = $item->findElement(WebDriverBy::tagName('b'))->getText();
} }
$feedItem->setContent($item->findElement(WebDriverBy::xpath('.//p[@class="description"]'))->getText()); $feedItem['content'] = $item->findElement(WebDriverBy::xpath('.//p[@class="description"]'))->getText();
$timeAgo = $item->findElement(WebDriverBy::xpath('.//small[contains(@class, "time-ago")]'))->getText(); $timeAgo = $item->findElement(WebDriverBy::xpath('.//small[contains(@class, "time-ago")]'))->getText();
$feedItem->setTimestamp($this->getTimestamp($timeAgo)); $feedItem['timestamp'] = $this->getTimestamp($timeAgo);
$this->items[] = $feedItem; $this->items[] = $feedItem;
} }

View file

@ -477,7 +477,7 @@ class NintendoBridge extends XPathAbstract
return $date->getTimestamp(); return $date->getTimestamp();
} }
protected function generateItemId(FeedItem $item) protected function generateItemId(array $item)
{ {
return $this->getCurrentCategory() . '-' . $this->lastId; return $this->getCurrentCategory() . '-' . $this->lastId;
} }

View file

@ -107,10 +107,10 @@ class RutubeBridge extends BridgeAbstract
} }
foreach ($videos as $video) { foreach ($videos as $video) {
$item = new FeedItem(); $item = [];
$item->setTitle($video->title); $item['title'] = $video->title;
$item->setURI($video->video_url); $item['uri'] = $video->video_url;
$content = '<a href="' . $item->getURI() . '">'; $content = '<a href="' . $video->video_url . '">';
$content .= '<img src="' . $video->thumbnail_url . '" />'; $content .= '<img src="' . $video->thumbnail_url . '" />';
$content .= '</a><br/>'; $content .= '</a><br/>';
$content .= nl2br( $content .= nl2br(
@ -122,9 +122,10 @@ class RutubeBridge extends BridgeAbstract
$video->description . ' ' $video->description . ' '
) )
); );
$item->setTimestamp($video->created_ts); $item['timestamp'] = $video->created_ts;
$item->setAuthor($video->author->name); $item['author'] = $video->author->name;
$item->setContent($content); $item['content'] = $content;
$this->items[] = $item; $this->items[] = $item;
} }
} }

View file

@ -41,16 +41,20 @@ class ScalableCapitalBlogBridge extends WebDriverAbstract
$items = $this->getDriver()->findElements(WebDriverBy::xpath('//div[contains(@class, "articles")]//div[@class="items"]//div[contains(@class, "item")]')); $items = $this->getDriver()->findElements(WebDriverBy::xpath('//div[contains(@class, "articles")]//div[@class="items"]//div[contains(@class, "item")]'));
foreach ($items as $item) { foreach ($items as $item) {
$feedItem = new FeedItem(); $feedItem = [];
$feedItem['enclosures'] = ['https://de.scalable.capital' . $item->findElement(WebDriverBy::tagName('img'))->getAttribute('src')];
$feedItem->setEnclosures(['https://de.scalable.capital' . $item->findElement(WebDriverBy::tagName('img'))->getAttribute('src')]);
$heading = $item->findElement(WebDriverBy::tagName('a')); $heading = $item->findElement(WebDriverBy::tagName('a'));
$feedItem->setTitle($heading->getText()); $feedItem['title'] = $heading->getText();
$feedItem->setURI('https://de.scalable.capital' . $heading->getAttribute('href'));
$feedItem->setContent($item->findElement(WebDriverBy::xpath('.//div[@class="summary"]'))->getText()); $feedItem['uri'] = 'https://de.scalable.capital' . $heading->getAttribute('href');
$feedItem['content'] = $item->findElement(WebDriverBy::xpath('.//div[@class="summary"]'))->getText();
$date = $item->findElement(WebDriverBy::xpath('.//div[@class="published-date"]'))->getText(); $date = $item->findElement(WebDriverBy::xpath('.//div[@class="published-date"]'))->getText();
$feedItem->setTimestamp($this->formatItemTimestamp($date)); $feedItem['timestamp'] = $this->formatItemTimestamp($date);
$feedItem->setAuthor($item->findElement(WebDriverBy::xpath('.//div[@class="author"]'))->getText());
$feedItem['author'] = $item->findElement(WebDriverBy::xpath('.//div[@class="author"]'))->getText();
$this->items[] = $feedItem; $this->items[] = $feedItem;
} }

View file

@ -255,7 +255,7 @@ class Vk2Bridge extends BridgeAbstract
if (!$ownerId) { if (!$ownerId) {
$ownerId = $post['owner_id']; $ownerId = $post['owner_id'];
} }
$item = new FeedItem(); $item = [];
$content = $this->generateContentFromPost($post); $content = $this->generateContentFromPost($post);
if (isset($post['copy_history'])) { if (isset($post['copy_history'])) {
if ($this->getInput('hide_reposts')) { if ($this->getInput('hide_reposts')) {
@ -277,11 +277,11 @@ class Vk2Bridge extends BridgeAbstract
$content .= '):</p>'; $content .= '):</p>';
$content .= $this->generateContentFromPost($originalPost); $content .= $this->generateContentFromPost($originalPost);
} }
$item->setContent($content); $item['content'] = $content;
$item->setTimestamp($post['date']); $item['timestamp'] = $post['date'];
$item->setAuthor($this->ownerNames[$post['from_id']]); $item['author'] = $this->ownerNames[$post['from_id']];
$item->setTitle($this->getTitle(strip_tags($content))); $item['title'] = $this->getTitle(strip_tags($content));
$item->setURI($this->getPostURI($post)); $item['uri'] = $this->getPostURI($post);
$this->items[] = $item; $this->items[] = $item;
} }

View file

@ -12,10 +12,6 @@ class FeedItem
protected ?string $uid = null; protected ?string $uid = null;
protected array $misc = []; protected array $misc = [];
public function __construct()
{
}
public static function fromArray(array $itemArray): self public static function fromArray(array $itemArray): self
{ {
$item = new self(); $item = new self();
@ -25,6 +21,10 @@ class FeedItem
return $item; return $item;
} }
private function __construct()
{
}
public function __set($name, $value) public function __set($name, $value)
{ {
switch ($name) { switch ($name) {
@ -89,18 +89,6 @@ class FeedItem
return $this->uri; return $this->uri;
} }
/**
* Set URI to the full article.
*
* Use {@see FeedItem::getURI()} to get the URI.
*
* _Note_: Removes whitespace from the beginning and end of the URI.
*
* _Remarks_: Uses the attribute "href" or "src" if the provided URI is an
* object of simple_html_dom_node.
*
* @param simple_html_dom_node|object|string $uri URI to the full article.
*/
public function setURI($uri) public function setURI($uri)
{ {
$this->uri = null; // Clear previous data $this->uri = null; // Clear previous data

View file

@ -6,11 +6,11 @@ abstract class FormatAbstract
const MIME_TYPE = 'text/plain'; const MIME_TYPE = 'text/plain';
protected string $charset = 'UTF-8';
protected array $items = [];
protected int $lastModified;
protected array $feed = []; protected array $feed = [];
protected array $items = [];
protected string $charset = 'UTF-8';
protected int $lastModified;
abstract public function stringify(); abstract public function stringify();
@ -30,12 +30,11 @@ abstract class FormatAbstract
return $this->feed; return $this->feed;
} }
/**
* @param FeedItem[] $items
*/
public function setItems(array $items): void public function setItems(array $items): void
{ {
$this->items = $items; foreach ($items as $item) {
$this->items[] = FeedItem::fromArray($item);
}
} }
/** /**

View file

@ -422,9 +422,18 @@ abstract class XPathAbstract extends BridgeAbstract
} }
foreach ($entries as $entry) { foreach ($entries as $entry) {
$item = new FeedItem(); $item = [];
foreach (['title', 'content', 'uri', 'author', 'timestamp', 'enclosures', 'categories'] as $param) { $parameters = [
$expression = $this->getParam($param); 'title',
'content',
'uri',
'author',
'timestamp',
'enclosures',
'categories',
];
foreach ($parameters as $parameter) {
$expression = $this->getParam($parameter);
if ('' === $expression) { if ('' === $expression) {
continue; continue;
} }
@ -438,21 +447,21 @@ abstract class XPathAbstract extends BridgeAbstract
continue; continue;
} }
if ('categories' === $param && $typedResult instanceof \DOMNodeList) { if ('categories' === $parameter && $typedResult instanceof \DOMNodeList) {
$value = []; $value = [];
foreach ($typedResult as $domNode) { foreach ($typedResult as $domNode) {
$value[] = $this->getItemValueOrNodeValue($domNode, false); $value[] = $this->getItemValueOrNodeValue($domNode, false);
} }
} else { } else {
$value = $this->getItemValueOrNodeValue($typedResult, 'content' === $param); $value = $this->getItemValueOrNodeValue($typedResult, 'content' === $parameter);
} }
$item->__set($param, $this->formatParamValue($param, $value)); $item[$parameter] = $this->formatParamValue($parameter, $value);
} }
$itemId = $this->generateItemId($item); $itemId = $this->generateItemId($item);
if (null !== $itemId) { if (null !== $itemId) {
$item->setUid($itemId); $item['uid'] = $itemId;
} }
$this->items[] = $item; $this->items[] = $item;
@ -646,10 +655,9 @@ abstract class XPathAbstract extends BridgeAbstract
/** /**
* Allows overriding default mechanism determining items Uid's * Allows overriding default mechanism determining items Uid's
* *
* @param FeedItem $item
* @return string|null * @return string|null
*/ */
protected function generateItemId(FeedItem $item) protected function generateItemId(array $item)
{ {
return null; return null;
} }

View file

@ -4,45 +4,16 @@ declare(strict_types=1);
namespace RssBridge\Tests; namespace RssBridge\Tests;
use FeedItem;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
class FeedItemTest extends TestCase class FeedItemTest extends TestCase
{ {
public function test() public function test()
{ {
$item = new FeedItem(); $item = [
$item->setTitle('hello'); 'title' => 'kek',
$this->assertSame('hello', $item->getTitle()); ];
$feedItem = \FeedItem::fromArray($item);
$item = FeedItem::fromArray(['title' => 'hello2']); $this->assertSame('kek', $feedItem->getTitle());
$this->assertSame('hello2', $item->getTitle());
$item = new FeedItem();
$item->setAuthor('123');
$this->assertSame('123', $item->getAuthor());
$item = new FeedItem();
$item->title = 'aa';
$this->assertSame('aa', $item->title);
$this->assertSame('aa', $item->getTitle());
}
public function testTimestamp()
{
$item = new FeedItem();
$item->setTimestamp(5);
$this->assertSame(5, $item->getTimestamp());
$item->setTimestamp('5');
$this->assertSame(5, $item->getTimestamp());
$item->setTimestamp('1970-01-01 18:00:00');
$this->assertSame(64800, $item->getTimestamp());
$item->setTimestamp('1st jan last year');
// This will fail at 2025-01-01 hehe
$this->assertSame(1672531200, $item->getTimestamp());
} }
} }

View file

@ -39,7 +39,7 @@ abstract class BaseFormatTest extends TestCase
$items = []; $items = [];
foreach ($data['items'] as $item) { foreach ($data['items'] as $item) {
$items[] = \FeedItem::fromArray($item); $items[] = ($item);
} }
return (object)[ return (object)[