mirror of
https://github.com/RSS-Bridge/rss-bridge.git
synced 2025-03-14 20:21:14 +03:00
refactor (#3708)
This commit is contained in:
parent
e1b911fc1f
commit
cd30c25b08
14 changed files with 282 additions and 884 deletions
|
@ -266,7 +266,7 @@ https://alice:cat@rss-bridge.org/bridge01/?action=display&bridge=FabriceBellardB
|
|||
|
||||
### How to create a new output format
|
||||
|
||||
[Create a new format](https://rss-bridge.github.io/rss-bridge/Format_API/index.html).
|
||||
See `formats/PlaintextFormat.php` for an example.
|
||||
|
||||
### How to run unit tests and linter
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ class DisplayAction implements ActionInterface
|
|||
return $response;
|
||||
}
|
||||
|
||||
private function createResponse(array $request, BridgeAbstract $bridge, FormatInterface $format)
|
||||
private function createResponse(array $request, BridgeAbstract $bridge, FormatAbstract $format)
|
||||
{
|
||||
$items = [];
|
||||
$infos = [];
|
||||
|
@ -108,15 +108,15 @@ class DisplayAction implements ActionInterface
|
|||
if (isset($items[0]) && is_array($items[0])) {
|
||||
$feedItems = [];
|
||||
foreach ($items as $item) {
|
||||
$feedItems[] = new FeedItem($item);
|
||||
$feedItems[] = FeedItem::fromArray($item);
|
||||
}
|
||||
$items = $feedItems;
|
||||
}
|
||||
$infos = [
|
||||
'name' => $bridge->getName(),
|
||||
'uri' => $bridge->getURI(),
|
||||
'donationUri' => $bridge->getDonationURI(),
|
||||
'icon' => $bridge->getIcon()
|
||||
'name' => $bridge->getName(),
|
||||
'uri' => $bridge->getURI(),
|
||||
'donationUri' => $bridge->getDonationURI(),
|
||||
'icon' => $bridge->getIcon()
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
if ($e instanceof HttpException) {
|
||||
|
@ -167,8 +167,8 @@ class DisplayAction implements ActionInterface
|
|||
|
||||
// Create a unique identifier every 24 hours
|
||||
$uniqueIdentifier = urlencode((int)(time() / 86400));
|
||||
$itemTitle = sprintf('Bridge returned error %s! (%s)', $e->getCode(), $uniqueIdentifier);
|
||||
$item->setTitle($itemTitle);
|
||||
$title = sprintf('Bridge returned error %s! (%s)', $e->getCode(), $uniqueIdentifier);
|
||||
$item->setTitle($title);
|
||||
$item->setURI(get_current_url());
|
||||
$item->setTimestamp(time());
|
||||
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
Create a new file in the `formats/` folder (see [Folder structure](../04_For_Developers/03_Folder_structure.md)).
|
||||
|
||||
The file must be named according to following specification:
|
||||
|
||||
* It starts with the type
|
||||
* The file name must end with 'Format'
|
||||
* The file type must be PHP, written in small letters (seriously!) ".php"
|
||||
|
||||
**Examples:**
|
||||
|
||||
Type | Filename
|
||||
-----|---------
|
||||
Atom | AtomFormat.php
|
||||
Html | HtmlFormat.php
|
||||
|
||||
The file must start with the PHP tags and end with an empty line. The closing tag `?>` is [omitted](http://php.net/basic-syntax.instruction-separation).
|
||||
|
||||
Example:
|
||||
|
||||
```PHP
|
||||
<?PHP
|
||||
// PHP code here
|
||||
// This line is empty (just imagine it!)
|
||||
```
|
|
@ -1,132 +0,0 @@
|
|||
The `FormatInterface` interface defines functions that need to be implemented by all formats:
|
||||
|
||||
* [display](#the-display-function)
|
||||
* [stringify](#the-stringify-function)
|
||||
* [setItems](#the-setitems-function)
|
||||
* [getItems](#the-getitems-function)
|
||||
* [setCharset](#the-setcharset-function)
|
||||
* [getCharset](#the-getcharset-function)
|
||||
* [setExtraInfos](#the-setextrainfos-function)
|
||||
* [getExtraInfos](#the-getextrainfos-function)
|
||||
* [getMimeType](#the-getmimetype-function)
|
||||
|
||||
Find a [template](#template) at the end of this file
|
||||
|
||||
# Functions
|
||||
|
||||
## The `stringify` function
|
||||
|
||||
The `stringify` function returns the items received by [`setItems`](#the-setitem-function) as string.
|
||||
|
||||
```PHP
|
||||
stringify(): string
|
||||
```
|
||||
|
||||
## The `setItems` function
|
||||
|
||||
The `setItems` function receives an array of items generated by the bridge and must return the object instance. Each item represents an entry in the feed. For more information refer to the [collectData](../05_Bridge_API/02_BridgeAbstract.md#collectdata) function.
|
||||
|
||||
```PHP
|
||||
setItems(array $items): self
|
||||
```
|
||||
|
||||
## The `getItems` function
|
||||
|
||||
The `getItems` function returns the items previously set by the [`setItems`](#the-setitems-function) function. If no items where set previously this function returns an error.
|
||||
|
||||
```PHP
|
||||
getItems(): array
|
||||
```
|
||||
|
||||
## The `setCharset` function
|
||||
|
||||
The `setCharset` function receives the character set value as string and returns the object instance.
|
||||
|
||||
```PHP
|
||||
setCharset(string): self
|
||||
```
|
||||
|
||||
## The `getCharset` function
|
||||
|
||||
The `getCharset` function returns the character set value.
|
||||
|
||||
```PHP
|
||||
getCharset(): string
|
||||
```
|
||||
|
||||
## The `setExtraInfos` function
|
||||
|
||||
The `setExtraInfos` function receives an array of elements with additional information to generate format outputs and must return the object instance.
|
||||
|
||||
```PHP
|
||||
setExtraInfos(array $infos): self
|
||||
```
|
||||
|
||||
Currently supported information are:
|
||||
|
||||
Name | Description
|
||||
-----|------------
|
||||
`name` | Defines the name as generated by the bridge
|
||||
`uri` | Defines the URI of the feed as generated by the bridge
|
||||
|
||||
## The `getExtraInfos` function
|
||||
|
||||
The `getExtraInfos` function returns the information previously set via the [`setExtraInfos`](#the-setextrainfos-function) function.
|
||||
|
||||
```PHP
|
||||
getExtraInfos(): array
|
||||
```
|
||||
|
||||
## The `getMimeType` function
|
||||
|
||||
The `getMimeType` function returns the expected [MIME type](https://en.wikipedia.org/wiki/Media_type#Common_examples) of the format's output.
|
||||
|
||||
```PHP
|
||||
parse_mime_type(): string
|
||||
```
|
||||
|
||||
# Template
|
||||
|
||||
This is a bare minimum template for a format:
|
||||
|
||||
```PHP
|
||||
<?php
|
||||
class MyTypeFormat implements FormatInterface {
|
||||
private $items;
|
||||
private $charset;
|
||||
private $extraInfos;
|
||||
|
||||
public function stringify(){
|
||||
// Implement your code here
|
||||
return ''; // Return items as string
|
||||
}
|
||||
|
||||
public function setItems(array $items){
|
||||
$this->items = $items;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getItems(){
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
public function setCharset($charset){
|
||||
$this->charset = $charset;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCharset(){
|
||||
return $this->charset;
|
||||
}
|
||||
|
||||
public function setExtraInfos(array $infos){
|
||||
$this->extraInfos = $infos;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getExtraInfos(){
|
||||
return $this->extraInfos;
|
||||
}
|
||||
}
|
||||
// Imaginary empty line!
|
||||
```
|
|
@ -1,9 +0,0 @@
|
|||
A Format is a class that allows RSS-Bridge to turn items from a bridge into an RSS-feed format.
|
||||
It is developed in a PHP file located in the `formats/` folder
|
||||
[Folder structure](../04_For_Developers/03_Folder_structure.md)
|
||||
and either implements the
|
||||
[FormatInterface](../08_Format_API/02_FormatInterface.md)
|
||||
interface or extends the FormatAbstract class.
|
||||
|
||||
For more information about how to create a new _Format_, read
|
||||
[How to create a new Format?](./01_How_to_create_a_new_format.md)
|
|
@ -1,28 +1,9 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of RSS-Bridge, a PHP project capable of generating RSS and
|
||||
* Atom feeds for websites that don't have one.
|
||||
*
|
||||
* For the full license information, please view the UNLICENSE file distributed
|
||||
* with this source code.
|
||||
*
|
||||
* @package Core
|
||||
* @license http://unlicense.org/ UNLICENSE
|
||||
* @link https://github.com/rss-bridge/rss-bridge
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface for action objects.
|
||||
*/
|
||||
interface ActionInterface
|
||||
{
|
||||
/**
|
||||
* Execute the action.
|
||||
*
|
||||
* Note: This function directly outputs data to the user.
|
||||
*
|
||||
* @return ?string
|
||||
* @return string|Response
|
||||
*/
|
||||
public function execute(array $request);
|
||||
}
|
||||
|
|
742
lib/FeedItem.php
742
lib/FeedItem.php
|
@ -1,511 +1,30 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of RSS-Bridge, a PHP project capable of generating RSS and
|
||||
* Atom feeds for websites that don't have one.
|
||||
*
|
||||
* For the full license information, please view the UNLICENSE file distributed
|
||||
* with this source code.
|
||||
*
|
||||
* @package Core
|
||||
* @license http://unlicense.org/ UNLICENSE
|
||||
* @link https://github.com/rss-bridge/rss-bridge
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a simple feed item for transformation into various feed formats.
|
||||
*
|
||||
* This class represents a feed item. A feed item is an entity that can be
|
||||
* transformed into various feed formats. It holds a set of pre-defined
|
||||
* properties:
|
||||
*
|
||||
* - **URI**: URI to the full article (i.e. "https://...")
|
||||
* - **Title**: The title
|
||||
* - **Timestamp**: A timestamp of when the item was first released
|
||||
* - **Author**: Name of the author
|
||||
* - **Content**: Body of the feed, as text or HTML
|
||||
* - **Enclosures**: A list of links to media objects (images, videos, etc...)
|
||||
* - **Categories**: A list of category names or tags to categorize the item
|
||||
*
|
||||
* _Note_: A feed item can have any number of additional parameters, all of which
|
||||
* may or may not be transformed to the selected output format.
|
||||
*
|
||||
* _Remarks_: This class supports legacy items via {@see FeedItem::__construct()}
|
||||
* (i.e. `$feedItem = \FeedItem($item);`). Support for legacy items may be removed
|
||||
* in future versions of RSS-Bridge.
|
||||
*/
|
||||
class FeedItem
|
||||
{
|
||||
/** @var string|null URI to the full article */
|
||||
protected $uri = null;
|
||||
protected ?string $uri = null;
|
||||
protected ?string $title = null;
|
||||
protected ?int $timestamp = null;
|
||||
protected ?string $author = null;
|
||||
protected ?string $content = null;
|
||||
protected array $enclosures = [];
|
||||
protected array $categories = [];
|
||||
protected ?string $uid = null;
|
||||
protected array $misc = [];
|
||||
|
||||
/** @var string|null Title of the item */
|
||||
protected $title = null;
|
||||
|
||||
/** @var int|null Timestamp of when the item was first released */
|
||||
protected $timestamp = null;
|
||||
|
||||
/** @var string|null Name of the author */
|
||||
protected $author = null;
|
||||
|
||||
/** @var string|null Body of the feed */
|
||||
protected $content = null;
|
||||
|
||||
/** @var array List of links to media objects */
|
||||
protected $enclosures = [];
|
||||
|
||||
/** @var array List of category names or tags */
|
||||
protected $categories = [];
|
||||
|
||||
/** @var string Unique ID for the current item */
|
||||
protected $uid = null;
|
||||
|
||||
/** @var array Associative list of additional parameters */
|
||||
protected $misc = []; // Custom parameters
|
||||
|
||||
/**
|
||||
* Create object from legacy item.
|
||||
*
|
||||
* The provided array must be an associative array of key-value-pairs, where
|
||||
* keys may correspond to any of the properties of this class.
|
||||
*
|
||||
* Example use:
|
||||
*
|
||||
* ```PHP
|
||||
* <?php
|
||||
* $item = array();
|
||||
*
|
||||
* $item['uri'] = 'https://www.github.com/rss-bridge/rss-bridge/';
|
||||
* $item['title'] = 'Title';
|
||||
* $item['timestamp'] = strtotime('now');
|
||||
* $item['author'] = 'Unknown author';
|
||||
* $item['content'] = 'Hello World!';
|
||||
* $item['enclosures'] = array('https://github.com/favicon.ico');
|
||||
* $item['categories'] = array('php', 'rss-bridge', 'awesome');
|
||||
*
|
||||
* $feedItem = new \FeedItem($item);
|
||||
*
|
||||
* ```
|
||||
*
|
||||
* The result of the code above is the same as the code below:
|
||||
*
|
||||
* ```PHP
|
||||
* <?php
|
||||
* $feedItem = \FeedItem();
|
||||
*
|
||||
* $feedItem->uri = 'https://www.github.com/rss-bridge/rss-bridge/';
|
||||
* $feedItem->title = 'Title';
|
||||
* $feedItem->timestamp = strtotime('now');
|
||||
* $feedItem->autor = 'Unknown author';
|
||||
* $feedItem->content = 'Hello World!';
|
||||
* $feedItem->enclosures = array('https://github.com/favicon.ico');
|
||||
* $feedItem->categories = array('php', 'rss-bridge', 'awesome');
|
||||
* ```
|
||||
*
|
||||
* @param array $item (optional) A legacy item (empty: no legacy support).
|
||||
* @return object A new object of this class
|
||||
*/
|
||||
public function __construct($item = [])
|
||||
public function __construct()
|
||||
{
|
||||
if (!is_array($item)) {
|
||||
Debug::log('Item must be an array!');
|
||||
}
|
||||
|
||||
foreach ($item as $key => $value) {
|
||||
$this->__set($key, $value);
|
||||
}
|
||||
|
||||
public static function fromArray(array $itemArray): self
|
||||
{
|
||||
$item = new self();
|
||||
foreach ($itemArray as $key => $value) {
|
||||
$item->__set($key, $value);
|
||||
}
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current URI.
|
||||
*
|
||||
* Use {@see FeedItem::setURI()} to set the URI.
|
||||
*
|
||||
* @return string|null The URI or null if it hasn't been set.
|
||||
*/
|
||||
public function getURI()
|
||||
{
|
||||
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 object|string $uri URI to the full article.
|
||||
* @return self
|
||||
*/
|
||||
public function setURI($uri)
|
||||
{
|
||||
$this->uri = null; // Clear previous data
|
||||
|
||||
if ($uri instanceof simple_html_dom_node) {
|
||||
if ($uri->hasAttribute('href')) { // Anchor
|
||||
$uri = $uri->href;
|
||||
} elseif ($uri->hasAttribute('src')) { // Image
|
||||
$uri = $uri->src;
|
||||
} else {
|
||||
Debug::log('The item provided as URI is unknown!');
|
||||
}
|
||||
}
|
||||
if (!is_string($uri)) {
|
||||
Debug::log(sprintf('Expected $uri to be string but got %s', gettype($uri)));
|
||||
return $this;
|
||||
}
|
||||
$uri = trim($uri);
|
||||
// Intentionally doing a weak url validation here because FILTER_VALIDATE_URL is too strict
|
||||
if (!preg_match('#^https?://#i', $uri)) {
|
||||
Debug::log(sprintf('Not a valid url: "%s"', $uri));
|
||||
return $this;
|
||||
}
|
||||
$this->uri = $uri;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current title.
|
||||
*
|
||||
* Use {@see FeedItem::setTitle()} to set the title.
|
||||
*
|
||||
* @return string|null The current title or null if it hasn't been set.
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set title.
|
||||
*
|
||||
* Use {@see FeedItem::getTitle()} to get the title.
|
||||
*
|
||||
* _Note_: Removes whitespace from beginning and end of the title.
|
||||
*
|
||||
* @param string $title The title
|
||||
* @return self
|
||||
*/
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = null; // Clear previous data
|
||||
|
||||
if (!is_string($title)) {
|
||||
Debug::log('Title must be a string!');
|
||||
} else {
|
||||
$this->title = truncate(trim($title));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current timestamp.
|
||||
*
|
||||
* Use {@see FeedItem::setTimestamp()} to set the timestamp.
|
||||
*
|
||||
* @return int|null The current timestamp or null if it hasn't been set.
|
||||
*/
|
||||
public function getTimestamp()
|
||||
{
|
||||
return $this->timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set timestamp of first release.
|
||||
*
|
||||
* _Note_: The timestamp should represent the number of seconds since
|
||||
* January 1 1970 00:00:00 GMT (Unix time).
|
||||
*
|
||||
* _Remarks_: If the provided timestamp is a string (not numeric), this
|
||||
* function automatically attempts to parse the string using
|
||||
* [strtotime](http://php.net/manual/en/function.strtotime.php)
|
||||
*
|
||||
* @link http://php.net/manual/en/function.strtotime.php strtotime (PHP)
|
||||
* @link https://en.wikipedia.org/wiki/Unix_time Unix time (Wikipedia)
|
||||
*
|
||||
* @param string|int $timestamp A timestamp of when the item was first released
|
||||
* @return self
|
||||
*/
|
||||
public function setTimestamp($timestamp)
|
||||
{
|
||||
$this->timestamp = null; // Clear previous data
|
||||
|
||||
if (
|
||||
!is_numeric($timestamp)
|
||||
&& !$timestamp = strtotime($timestamp)
|
||||
) {
|
||||
Debug::log('Unable to parse timestamp!');
|
||||
}
|
||||
|
||||
if ($timestamp <= 0) {
|
||||
Debug::log('Timestamp must be greater than zero!');
|
||||
} else {
|
||||
$this->timestamp = $timestamp;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current author name.
|
||||
*
|
||||
* Use {@see FeedItem::setAuthor()} to set the author.
|
||||
*
|
||||
* @return string|null The author or null if it hasn't been set.
|
||||
*/
|
||||
public function getAuthor()
|
||||
{
|
||||
return $this->author;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the author name.
|
||||
*
|
||||
* Use {@see FeedItem::getAuthor()} to get the author.
|
||||
*
|
||||
* @param string $author The author name.
|
||||
* @return self
|
||||
*/
|
||||
public function setAuthor($author)
|
||||
{
|
||||
$this->author = null; // Clear previous data
|
||||
|
||||
if (!is_string($author)) {
|
||||
Debug::log('Author must be a string!');
|
||||
} else {
|
||||
$this->author = $author;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get item content.
|
||||
*
|
||||
* Use {@see FeedItem::setContent()} to set the item content.
|
||||
*
|
||||
* @return string|null The item content or null if it hasn't been set.
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set item content.
|
||||
*
|
||||
* Note: This function casts objects of type simple_html_dom and
|
||||
* simple_html_dom_node to string.
|
||||
*
|
||||
* Use {@see FeedItem::getContent()} to get the current item content.
|
||||
*
|
||||
* @param string|object $content The item content as text or simple_html_dom object.
|
||||
* @return self
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
$this->content = null; // Clear previous data
|
||||
|
||||
if (
|
||||
$content instanceof simple_html_dom
|
||||
|| $content instanceof simple_html_dom_node
|
||||
) {
|
||||
$content = (string)$content;
|
||||
}
|
||||
|
||||
if (is_string($content)) {
|
||||
$this->content = $content;
|
||||
} else {
|
||||
Debug::log(sprintf('Feed content must be a string but got %s', gettype($content)));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get item enclosures.
|
||||
*
|
||||
* Use {@see FeedItem::setEnclosures()} to set feed enclosures.
|
||||
*
|
||||
* @return array Enclosures as array of enclosure URIs.
|
||||
*/
|
||||
public function getEnclosures()
|
||||
{
|
||||
return $this->enclosures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set item enclosures.
|
||||
*
|
||||
* Use {@see FeedItem::getEnclosures()} to get the current item enclosures.
|
||||
*
|
||||
* @param array $enclosures Array of enclosures, where each element links to
|
||||
* one enclosure.
|
||||
* @return self
|
||||
*/
|
||||
public function setEnclosures($enclosures)
|
||||
{
|
||||
$this->enclosures = [];
|
||||
|
||||
if (is_array($enclosures)) {
|
||||
foreach ($enclosures as $enclosure) {
|
||||
if (
|
||||
!filter_var(
|
||||
$enclosure,
|
||||
FILTER_VALIDATE_URL,
|
||||
FILTER_FLAG_PATH_REQUIRED
|
||||
)
|
||||
) {
|
||||
Debug::log('Each enclosure must contain a scheme, host and path!');
|
||||
} elseif (!in_array($enclosure, $this->enclosures)) {
|
||||
$this->enclosures[] = $enclosure;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Debug::log('Enclosures must be an array!');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get item categories.
|
||||
*
|
||||
* Use {@see FeedItem::setCategories()} to set item categories.
|
||||
*
|
||||
* @param array The item categories.
|
||||
*/
|
||||
public function getCategories()
|
||||
{
|
||||
return $this->categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set item categories.
|
||||
*
|
||||
* Use {@see FeedItem::getCategories()} to get the current item categories.
|
||||
*
|
||||
* @param array $categories Array of categories, where each element defines
|
||||
* a single category name.
|
||||
* @return self
|
||||
*/
|
||||
public function setCategories($categories)
|
||||
{
|
||||
$this->categories = [];
|
||||
|
||||
if (is_array($categories)) {
|
||||
foreach ($categories as $category) {
|
||||
if (!is_string($category)) {
|
||||
Debug::log('Category must be a string!');
|
||||
} else {
|
||||
$this->categories[] = $category;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Debug::log('Categories must be an array!');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get unique id
|
||||
*
|
||||
* Use {@see FeedItem::setUid()} to set the unique id.
|
||||
*
|
||||
* @param string The unique id.
|
||||
*/
|
||||
public function getUid()
|
||||
{
|
||||
return $this->uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set unique id.
|
||||
*
|
||||
* Use {@see FeedItem::getUid()} to get the unique id.
|
||||
*
|
||||
* @param string $uid A string that uniquely identifies the current item
|
||||
* @return self
|
||||
*/
|
||||
public function setUid($uid)
|
||||
{
|
||||
$this->uid = null; // Clear previous data
|
||||
|
||||
if (!is_string($uid)) {
|
||||
Debug::log('Unique id must be a string!');
|
||||
} elseif (preg_match('/^[a-f0-9]{40}$/', $uid)) {
|
||||
// keep id if it already is a SHA-1 hash
|
||||
$this->uid = $uid;
|
||||
} else {
|
||||
$this->uid = sha1($uid);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add miscellaneous elements to the item.
|
||||
*
|
||||
* @param string $key Name of the element.
|
||||
* @param mixed $value Value of the element.
|
||||
* @return self
|
||||
*/
|
||||
public function addMisc($key, $value)
|
||||
{
|
||||
if (!is_string($key)) {
|
||||
Debug::log('Key must be a string!');
|
||||
} elseif (in_array($key, get_object_vars($this))) {
|
||||
Debug::log('Key must be unique!');
|
||||
} else {
|
||||
$this->misc[$key] = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform current object to array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return array_merge(
|
||||
[
|
||||
'uri' => $this->uri,
|
||||
'title' => $this->title,
|
||||
'timestamp' => $this->timestamp,
|
||||
'author' => $this->author,
|
||||
'content' => $this->content,
|
||||
'enclosures' => $this->enclosures,
|
||||
'categories' => $this->categories,
|
||||
'uid' => $this->uid,
|
||||
],
|
||||
$this->misc
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set item property
|
||||
*
|
||||
* Allows simple assignment to parameters. This method is slower, but easier
|
||||
* to implement in some cases:
|
||||
*
|
||||
* ```PHP
|
||||
* $item = new \FeedItem();
|
||||
* $item->content = 'Hello World!';
|
||||
* $item->my_id = 42;
|
||||
* ```
|
||||
*
|
||||
* @param string $name Property name
|
||||
* @param mixed $value Property value
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
switch ($name) {
|
||||
|
@ -538,15 +57,6 @@ class FeedItem
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get item property
|
||||
*
|
||||
* Allows simple assignment to parameters. This method is slower, but easier
|
||||
* to implement in some cases.
|
||||
*
|
||||
* @param string $name Property name
|
||||
* @return mixed Property value
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
switch ($name) {
|
||||
|
@ -573,4 +83,220 @@ class FeedItem
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function getURI(): ?string
|
||||
{
|
||||
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)
|
||||
{
|
||||
$this->uri = null; // Clear previous data
|
||||
|
||||
if ($uri instanceof simple_html_dom_node) {
|
||||
if ($uri->hasAttribute('href')) { // Anchor
|
||||
$uri = $uri->href;
|
||||
} elseif ($uri->hasAttribute('src')) { // Image
|
||||
$uri = $uri->src;
|
||||
} else {
|
||||
Debug::log('The item provided as URI is unknown!');
|
||||
}
|
||||
}
|
||||
if (!is_string($uri)) {
|
||||
Debug::log(sprintf('Expected $uri to be string but got %s', gettype($uri)));
|
||||
return;
|
||||
}
|
||||
$uri = trim($uri);
|
||||
// Intentionally doing a weak url validation here because FILTER_VALIDATE_URL is too strict
|
||||
if (!preg_match('#^https?://#i', $uri)) {
|
||||
Debug::log(sprintf('Not a valid url: "%s"', $uri));
|
||||
return;
|
||||
}
|
||||
$this->uri = $uri;
|
||||
}
|
||||
|
||||
public function getTitle(): ?string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = null;
|
||||
if (!is_string($title)) {
|
||||
Debug::log('Title must be a string!');
|
||||
} else {
|
||||
$this->title = truncate(trim($title));
|
||||
}
|
||||
}
|
||||
|
||||
public function getTimestamp(): ?int
|
||||
{
|
||||
return $this->timestamp;
|
||||
}
|
||||
|
||||
public function setTimestamp($timestamp)
|
||||
{
|
||||
$this->timestamp = null;
|
||||
if (
|
||||
!is_numeric($timestamp)
|
||||
&& !$timestamp = strtotime($timestamp)
|
||||
) {
|
||||
Debug::log('Unable to parse timestamp!');
|
||||
}
|
||||
if ($timestamp <= 0) {
|
||||
Debug::log('Timestamp must be greater than zero!');
|
||||
} else {
|
||||
$this->timestamp = $timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
public function getAuthor(): ?string
|
||||
{
|
||||
return $this->author;
|
||||
}
|
||||
|
||||
public function setAuthor($author)
|
||||
{
|
||||
$this->author = null;
|
||||
if (!is_string($author)) {
|
||||
Debug::log('Author must be a string!');
|
||||
} else {
|
||||
$this->author = $author;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getContent(): ?string
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|object $content The item content as text or simple_html_dom object.
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
$this->content = null;
|
||||
if (
|
||||
$content instanceof simple_html_dom
|
||||
|| $content instanceof simple_html_dom_node
|
||||
) {
|
||||
$content = (string) $content;
|
||||
}
|
||||
if (is_string($content)) {
|
||||
$this->content = $content;
|
||||
} else {
|
||||
Debug::log(sprintf('Feed content must be a string but got %s', gettype($content)));
|
||||
}
|
||||
}
|
||||
|
||||
public function getEnclosures(): array
|
||||
{
|
||||
return $this->enclosures;
|
||||
}
|
||||
|
||||
public function setEnclosures($enclosures)
|
||||
{
|
||||
$this->enclosures = [];
|
||||
|
||||
if (!is_array($enclosures)) {
|
||||
Debug::log('Enclosures must be an array!');
|
||||
return;
|
||||
}
|
||||
foreach ($enclosures as $enclosure) {
|
||||
if (
|
||||
!filter_var(
|
||||
$enclosure,
|
||||
FILTER_VALIDATE_URL,
|
||||
FILTER_FLAG_PATH_REQUIRED
|
||||
)
|
||||
) {
|
||||
Debug::log('Each enclosure must contain a scheme, host and path!');
|
||||
} elseif (!in_array($enclosure, $this->enclosures)) {
|
||||
$this->enclosures[] = $enclosure;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getCategories(): array
|
||||
{
|
||||
return $this->categories;
|
||||
}
|
||||
|
||||
public function setCategories($categories)
|
||||
{
|
||||
$this->categories = [];
|
||||
|
||||
if (!is_array($categories)) {
|
||||
Debug::log('Categories must be an array!');
|
||||
return;
|
||||
}
|
||||
foreach ($categories as $category) {
|
||||
if (is_string($category)) {
|
||||
$this->categories[] = $category;
|
||||
} else {
|
||||
Debug::log('Category must be a string!');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getUid(): ?string
|
||||
{
|
||||
return $this->uid;
|
||||
}
|
||||
|
||||
public function setUid($uid)
|
||||
{
|
||||
$this->uid = null;
|
||||
if (!is_string($uid)) {
|
||||
Debug::log('Unique id must be a string!');
|
||||
} elseif (preg_match('/^[a-f0-9]{40}$/', $uid)) {
|
||||
// keep id if it already is SHA-1 hash
|
||||
$this->uid = $uid;
|
||||
} else {
|
||||
$this->uid = sha1($uid);
|
||||
}
|
||||
}
|
||||
|
||||
public function addMisc($name, $value)
|
||||
{
|
||||
if (!is_string($name)) {
|
||||
Debug::log('Key must be a string!');
|
||||
} elseif (in_array($name, get_object_vars($this))) {
|
||||
Debug::log('Key must be unique!');
|
||||
} else {
|
||||
$this->misc[$name] = $value;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return array_merge(
|
||||
[
|
||||
'uri' => $this->uri,
|
||||
'title' => $this->title,
|
||||
'timestamp' => $this->timestamp,
|
||||
'author' => $this->author,
|
||||
'content' => $this->content,
|
||||
'enclosures' => $this->enclosures,
|
||||
'categories' => $this->categories,
|
||||
'uid' => $this->uid,
|
||||
],
|
||||
$this->misc
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,132 +1,70 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of RSS-Bridge, a PHP project capable of generating RSS and
|
||||
* Atom feeds for websites that don't have one.
|
||||
*
|
||||
* For the full license information, please view the UNLICENSE file distributed
|
||||
* with this source code.
|
||||
*
|
||||
* @package Core
|
||||
* @license https://unlicense.org/ UNLICENSE
|
||||
* @link https://github.com/rss-bridge/rss-bridge
|
||||
*/
|
||||
|
||||
/**
|
||||
* An abstract class for format implementations
|
||||
*
|
||||
* This class implements {@see FormatInterface}
|
||||
*/
|
||||
abstract class FormatAbstract implements FormatInterface
|
||||
abstract class FormatAbstract
|
||||
{
|
||||
/** The default charset (UTF-8) */
|
||||
const DEFAULT_CHARSET = 'UTF-8';
|
||||
|
||||
/** MIME type of format output */
|
||||
const MIME_TYPE = 'text/plain';
|
||||
|
||||
/** @var string $charset The charset */
|
||||
protected $charset;
|
||||
protected string $charset = 'UTF-8';
|
||||
protected array $items = [];
|
||||
protected int $lastModified;
|
||||
protected array $extraInfos = [];
|
||||
|
||||
/** @var array $items The items */
|
||||
protected $items;
|
||||
abstract public function stringify();
|
||||
|
||||
/**
|
||||
* @var int $lastModified A timestamp to indicate the last modified time of
|
||||
* the output data.
|
||||
*/
|
||||
protected $lastModified;
|
||||
|
||||
/** @var array $extraInfos The extra infos */
|
||||
protected $extraInfos;
|
||||
|
||||
/** {@inheritdoc} */
|
||||
public function getMimeType()
|
||||
public function getMimeType(): string
|
||||
{
|
||||
return static::MIME_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param string $charset {@inheritdoc}
|
||||
*/
|
||||
public function setCharset($charset)
|
||||
public function setCharset(string $charset)
|
||||
{
|
||||
$this->charset = $charset;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
public function getCharset()
|
||||
public function getCharset(): string
|
||||
{
|
||||
$charset = $this->charset;
|
||||
|
||||
if (is_null($charset)) {
|
||||
return static::DEFAULT_CHARSET;
|
||||
}
|
||||
return $charset;
|
||||
return $this->charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the last modified time
|
||||
*
|
||||
* @param int $lastModified The last modified time
|
||||
* @return void
|
||||
*/
|
||||
public function setLastModified($lastModified)
|
||||
public function setLastModified(int $lastModified)
|
||||
{
|
||||
$this->lastModified = $lastModified;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param array $items {@inheritdoc}
|
||||
*/
|
||||
public function setItems(array $items)
|
||||
{
|
||||
$this->items = $items;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
public function getItems()
|
||||
{
|
||||
if (!is_array($this->items)) {
|
||||
throw new \LogicException(sprintf('Feed the %s with "setItems" method before !', get_class($this)));
|
||||
}
|
||||
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param array $extraInfos {@inheritdoc}
|
||||
* @return FeedItem[] The items
|
||||
*/
|
||||
public function setExtraInfos(array $extraInfos = [])
|
||||
public function getItems(): array
|
||||
{
|
||||
foreach (['name', 'uri', 'icon', 'donationUri'] as $infoName) {
|
||||
if (!isset($extraInfos[$infoName])) {
|
||||
$extraInfos[$infoName] = '';
|
||||
}
|
||||
}
|
||||
|
||||
$this->extraInfos = $extraInfos;
|
||||
|
||||
return $this;
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
public function getExtraInfos()
|
||||
public function setExtraInfos(array $infos = [])
|
||||
{
|
||||
if (is_null($this->extraInfos)) { // No extra info ?
|
||||
$this->setExtraInfos(); // Define with default value
|
||||
$extras = [
|
||||
'name',
|
||||
'uri',
|
||||
'icon',
|
||||
'donationUri',
|
||||
];
|
||||
foreach ($extras as $extra) {
|
||||
if (!isset($infos[$extra])) {
|
||||
$infos[$extra] = '';
|
||||
}
|
||||
}
|
||||
$this->extraInfos = $infos;
|
||||
}
|
||||
|
||||
public function getExtraInfos(): array
|
||||
{
|
||||
if (!$this->extraInfos) {
|
||||
$this->setExtraInfos();
|
||||
}
|
||||
return $this->extraInfos;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ class FormatFactory
|
|||
* @throws \InvalidArgumentException
|
||||
* @param string $name The name of the format e.g. "Atom", "Mrss" or "Json"
|
||||
*/
|
||||
public function create(string $name): FormatInterface
|
||||
public function create(string $name): FormatAbstract
|
||||
{
|
||||
if (! preg_match('/^[a-zA-Z0-9-]*$/', $name)) {
|
||||
throw new \InvalidArgumentException('Format name invalid!');
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of RSS-Bridge, a PHP project capable of generating RSS and
|
||||
* Atom feeds for websites that don't have one.
|
||||
*
|
||||
* For the full license information, please view the UNLICENSE file distributed
|
||||
* with this source code.
|
||||
*
|
||||
* @package Core
|
||||
* @license http://unlicense.org/ UNLICENSE
|
||||
* @link https://github.com/rss-bridge/rss-bridge
|
||||
*/
|
||||
|
||||
/**
|
||||
* The format interface
|
||||
*
|
||||
* @todo Add missing function to the interface
|
||||
* @todo Explain parameters and return values in more detail
|
||||
* @todo Return self more often (to allow call chaining)
|
||||
*/
|
||||
interface FormatInterface
|
||||
{
|
||||
/**
|
||||
* Generate a string representation of the current data
|
||||
*
|
||||
* @return string The string representation
|
||||
*/
|
||||
public function stringify();
|
||||
|
||||
public function setItems(array $items);
|
||||
|
||||
/**
|
||||
* Return items
|
||||
*
|
||||
* @throws \LogicException if the items are not set
|
||||
* @return FeedItem[] The items
|
||||
*/
|
||||
public function getItems();
|
||||
|
||||
/**
|
||||
* Set extra information
|
||||
*
|
||||
* @param array $infos Extra information
|
||||
* @return self The format object
|
||||
*/
|
||||
public function setExtraInfos(array $infos);
|
||||
|
||||
/**
|
||||
* Return extra information
|
||||
*
|
||||
* @return array Extra information
|
||||
*/
|
||||
public function getExtraInfos();
|
||||
|
||||
/**
|
||||
* Return MIME type
|
||||
*
|
||||
* @return string The MIME type
|
||||
*/
|
||||
public function getMimeType();
|
||||
|
||||
/**
|
||||
* Set charset
|
||||
*
|
||||
* @param string $charset The charset
|
||||
* @return self The format object
|
||||
*/
|
||||
public function setCharset($charset);
|
||||
|
||||
/**
|
||||
* Return current charset
|
||||
*
|
||||
* @return string The charset
|
||||
*/
|
||||
public function getCharset();
|
||||
}
|
|
@ -12,20 +12,12 @@
|
|||
* @link https://github.com/rss-bridge/rss-bridge
|
||||
*/
|
||||
|
||||
/** Path to the root folder of RSS-Bridge (where index.php is located) */
|
||||
const PATH_ROOT = __DIR__ . '/../';
|
||||
|
||||
/** Path to the bridges library */
|
||||
|
||||
/** Path to the formats library */
|
||||
const PATH_LIB_FORMATS = __DIR__ . '/../formats/';
|
||||
|
||||
/** Path to the caches library */
|
||||
const PATH_LIB_CACHES = __DIR__ . '/../caches/';
|
||||
|
||||
/** Path to the actions library */
|
||||
const PATH_LIB_ACTIONS = __DIR__ . '/../actions/';
|
||||
|
||||
/** Path to the cache folder */
|
||||
const PATH_CACHE = __DIR__ . '/../cache/';
|
||||
|
||||
|
|
|
@ -148,6 +148,9 @@ final class StreamHandler
|
|||
$context
|
||||
);
|
||||
error_log($text);
|
||||
if (Debug::isEnabled()) {
|
||||
print sprintf("<pre>%s</pre>\n", e($text));
|
||||
}
|
||||
//$bytes = file_put_contents('/tmp/rss-bridge.log', $text, FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ abstract class BaseFormatTest extends TestCase
|
|||
|
||||
$items = [];
|
||||
foreach ($data['items'] as $item) {
|
||||
$items[] = new \FeedItem($item);
|
||||
$items[] = \FeedItem::fromArray($item);
|
||||
}
|
||||
|
||||
return (object)[
|
||||
|
|
|
@ -24,7 +24,7 @@ class FormatImplementationTest extends TestCase
|
|||
public function testClassType($path)
|
||||
{
|
||||
$this->setFormat($path);
|
||||
$this->assertInstanceOf(FormatInterface::class, $this->obj);
|
||||
$this->assertInstanceOf(FormatAbstract::class, $this->obj);
|
||||
}
|
||||
|
||||
public function dataFormatsProvider()
|
||||
|
|
Loading…
Add table
Reference in a new issue