2018-12-27 00:41:32 +03:00
|
|
|
<?php
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2018-12-27 00:41:32 +03:00
|
|
|
class FeedItem
|
|
|
|
{
|
2023-09-25 22:18:48 +03:00
|
|
|
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 = [];
|
|
|
|
|
|
|
|
public function __construct()
|
|
|
|
{
|
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2023-09-25 22:18:48 +03:00
|
|
|
public static function fromArray(array $itemArray): self
|
|
|
|
{
|
|
|
|
$item = new self();
|
|
|
|
foreach ($itemArray as $key => $value) {
|
|
|
|
$item->__set($key, $value);
|
|
|
|
}
|
|
|
|
return $item;
|
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2023-09-25 22:18:48 +03:00
|
|
|
public function __set($name, $value)
|
2018-12-27 00:41:32 +03:00
|
|
|
{
|
2023-09-25 22:18:48 +03:00
|
|
|
switch ($name) {
|
|
|
|
case 'uri':
|
|
|
|
$this->setURI($value);
|
|
|
|
break;
|
|
|
|
case 'title':
|
|
|
|
$this->setTitle($value);
|
|
|
|
break;
|
|
|
|
case 'timestamp':
|
|
|
|
$this->setTimestamp($value);
|
|
|
|
break;
|
|
|
|
case 'author':
|
|
|
|
$this->setAuthor($value);
|
|
|
|
break;
|
|
|
|
case 'content':
|
|
|
|
$this->setContent($value);
|
|
|
|
break;
|
|
|
|
case 'enclosures':
|
|
|
|
$this->setEnclosures($value);
|
|
|
|
break;
|
|
|
|
case 'categories':
|
|
|
|
$this->setCategories($value);
|
|
|
|
break;
|
|
|
|
case 'uid':
|
|
|
|
$this->setUid($value);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
$this->addMisc($name, $value);
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
2023-09-25 22:18:48 +03:00
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2023-09-25 22:18:48 +03:00
|
|
|
public function __get($name)
|
|
|
|
{
|
|
|
|
switch ($name) {
|
|
|
|
case 'uri':
|
|
|
|
return $this->getURI();
|
|
|
|
case 'title':
|
|
|
|
return $this->getTitle();
|
|
|
|
case 'timestamp':
|
|
|
|
return $this->getTimestamp();
|
|
|
|
case 'author':
|
|
|
|
return $this->getAuthor();
|
|
|
|
case 'content':
|
|
|
|
return $this->getContent();
|
|
|
|
case 'enclosures':
|
|
|
|
return $this->getEnclosures();
|
|
|
|
case 'categories':
|
|
|
|
return $this->getCategories();
|
|
|
|
case 'uid':
|
|
|
|
return $this->getUid();
|
|
|
|
default:
|
|
|
|
if (array_key_exists($name, $this->misc)) {
|
|
|
|
return $this->misc[$name];
|
|
|
|
}
|
|
|
|
return null;
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
2018-12-27 00:41:32 +03:00
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2023-09-25 22:18:48 +03:00
|
|
|
public function getURI(): ?string
|
2018-12-27 00:41:32 +03:00
|
|
|
{
|
|
|
|
return $this->uri;
|
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2018-12-27 00:41:32 +03:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*
|
2023-09-25 22:18:48 +03:00
|
|
|
* @param simple_html_dom_node|object|string $uri URI to the full article.
|
2018-12-27 00:41:32 +03:00
|
|
|
*/
|
|
|
|
public function setURI($uri)
|
|
|
|
{
|
|
|
|
$this->uri = null; // Clear previous data
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2018-12-27 00:41:32 +03:00
|
|
|
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!');
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
2018-12-27 00:41:32 +03:00
|
|
|
}
|
|
|
|
if (!is_string($uri)) {
|
2022-09-22 00:07:56 +03:00
|
|
|
Debug::log(sprintf('Expected $uri to be string but got %s', gettype($uri)));
|
2023-09-25 22:18:48 +03:00
|
|
|
return;
|
2022-09-22 00:07:56 +03:00
|
|
|
}
|
|
|
|
$uri = trim($uri);
|
|
|
|
// Intentionally doing a weak url validation here because FILTER_VALIDATE_URL is too strict
|
|
|
|
if (!preg_match('#^https?://#i', $uri)) {
|
2022-09-08 20:07:57 +03:00
|
|
|
Debug::log(sprintf('Not a valid url: "%s"', $uri));
|
2023-09-25 22:18:48 +03:00
|
|
|
return;
|
2018-12-27 00:41:32 +03:00
|
|
|
}
|
2022-09-22 00:07:56 +03:00
|
|
|
$this->uri = $uri;
|
2018-12-27 00:41:32 +03:00
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2023-09-25 22:18:48 +03:00
|
|
|
public function getTitle(): ?string
|
2018-12-27 00:41:32 +03:00
|
|
|
{
|
|
|
|
return $this->title;
|
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2018-12-27 00:41:32 +03:00
|
|
|
public function setTitle($title)
|
|
|
|
{
|
2023-09-25 22:18:48 +03:00
|
|
|
$this->title = null;
|
2018-12-27 00:41:32 +03:00
|
|
|
if (!is_string($title)) {
|
2024-08-07 19:56:27 +03:00
|
|
|
trigger_error('Title must be a string: ' . print_r($title, true));
|
2018-12-27 00:41:32 +03:00
|
|
|
} else {
|
2022-07-31 04:52:27 +03:00
|
|
|
$this->title = truncate(trim($title));
|
2018-12-27 00:41:32 +03:00
|
|
|
}
|
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2023-09-25 22:18:48 +03:00
|
|
|
public function getTimestamp(): ?int
|
2018-12-27 00:41:32 +03:00
|
|
|
{
|
|
|
|
return $this->timestamp;
|
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2023-09-26 01:27:45 +03:00
|
|
|
public function setTimestamp($datetime)
|
2018-12-27 00:41:32 +03:00
|
|
|
{
|
2023-09-25 22:18:48 +03:00
|
|
|
$this->timestamp = null;
|
2023-09-26 01:27:45 +03:00
|
|
|
if (is_numeric($datetime)) {
|
|
|
|
$timestamp = $datetime;
|
|
|
|
} else {
|
|
|
|
$timestamp = strtotime($datetime);
|
|
|
|
if ($timestamp === false) {
|
|
|
|
Debug::log('Unable to parse timestamp!');
|
|
|
|
}
|
2018-12-27 00:41:32 +03:00
|
|
|
}
|
|
|
|
if ($timestamp <= 0) {
|
|
|
|
Debug::log('Timestamp must be greater than zero!');
|
|
|
|
} else {
|
|
|
|
$this->timestamp = $timestamp;
|
|
|
|
}
|
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2023-09-25 22:18:48 +03:00
|
|
|
public function getAuthor(): ?string
|
2022-07-01 16:10:30 +03:00
|
|
|
{
|
2018-12-27 00:41:32 +03:00
|
|
|
return $this->author;
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
|
|
|
|
2018-12-27 00:41:32 +03:00
|
|
|
public function setAuthor($author)
|
|
|
|
{
|
2023-09-25 22:18:48 +03:00
|
|
|
$this->author = null;
|
2018-12-27 00:41:32 +03:00
|
|
|
if (!is_string($author)) {
|
|
|
|
Debug::log('Author must be a string!');
|
2022-07-01 16:10:30 +03:00
|
|
|
} else {
|
2018-12-27 00:41:32 +03:00
|
|
|
$this->author = $author;
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
2018-12-27 00:41:32 +03:00
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2023-09-25 22:18:48 +03:00
|
|
|
public function getContent(): ?string
|
2018-12-27 00:41:32 +03:00
|
|
|
{
|
|
|
|
return $this->content;
|
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2018-12-27 00:41:32 +03:00
|
|
|
/**
|
2024-07-31 20:04:07 +03:00
|
|
|
* @param string|array|\simple_html_dom|\simple_html_dom_node $content The item content
|
2018-12-27 00:41:32 +03:00
|
|
|
*/
|
|
|
|
public function setContent($content)
|
|
|
|
{
|
2023-09-25 22:18:48 +03:00
|
|
|
$this->content = null;
|
2024-07-31 20:04:07 +03:00
|
|
|
|
2018-12-27 00:41:32 +03:00
|
|
|
if (
|
|
|
|
$content instanceof simple_html_dom
|
|
|
|
|| $content instanceof simple_html_dom_node
|
2022-07-01 16:10:30 +03:00
|
|
|
) {
|
2023-09-25 22:18:48 +03:00
|
|
|
$content = (string) $content;
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
2024-07-31 20:04:07 +03:00
|
|
|
|
2022-08-06 23:46:28 +03:00
|
|
|
if (is_string($content)) {
|
2018-12-27 00:41:32 +03:00
|
|
|
$this->content = $content;
|
2022-08-06 23:46:28 +03:00
|
|
|
} else {
|
2024-07-31 20:04:07 +03:00
|
|
|
Debug::log(sprintf('Unable to convert feed content to string: %s', gettype($content)));
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-25 22:18:48 +03:00
|
|
|
public function getEnclosures(): array
|
2018-12-27 00:41:32 +03:00
|
|
|
{
|
|
|
|
return $this->enclosures;
|
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2018-12-27 00:41:32 +03:00
|
|
|
public function setEnclosures($enclosures)
|
|
|
|
{
|
2022-08-06 23:46:28 +03:00
|
|
|
$this->enclosures = [];
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2023-09-25 22:18:48 +03:00
|
|
|
if (!is_array($enclosures)) {
|
2022-08-06 23:46:28 +03:00
|
|
|
Debug::log('Enclosures must be an array!');
|
2023-09-25 22:18:48 +03:00
|
|
|
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;
|
|
|
|
}
|
2019-03-21 21:42:44 +03:00
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
|
|
|
|
2023-09-25 22:18:48 +03:00
|
|
|
public function getCategories(): array
|
2022-07-01 16:10:30 +03:00
|
|
|
{
|
2018-12-27 00:41:32 +03:00
|
|
|
return $this->categories;
|
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2018-12-27 00:41:32 +03:00
|
|
|
public function setCategories($categories)
|
|
|
|
{
|
2022-08-06 23:46:28 +03:00
|
|
|
$this->categories = [];
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2023-09-25 22:18:48 +03:00
|
|
|
if (!is_array($categories)) {
|
2022-08-06 23:46:28 +03:00
|
|
|
Debug::log('Categories must be an array!');
|
2023-09-25 22:18:48 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
foreach ($categories as $category) {
|
|
|
|
if (is_string($category)) {
|
|
|
|
$this->categories[] = $category;
|
|
|
|
} else {
|
|
|
|
Debug::log('Category must be a string!');
|
|
|
|
}
|
2018-12-27 00:41:32 +03:00
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
|
|
|
|
2023-09-25 22:18:48 +03:00
|
|
|
public function getUid(): ?string
|
2022-07-01 16:10:30 +03:00
|
|
|
{
|
2018-12-27 00:41:32 +03:00
|
|
|
return $this->uid;
|
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2023-10-13 21:48:08 +03:00
|
|
|
public function setUid($uid): void
|
2019-02-03 22:56:41 +03:00
|
|
|
{
|
2023-09-25 22:18:48 +03:00
|
|
|
$this->uid = null;
|
2019-02-03 22:56:41 +03:00
|
|
|
if (!is_string($uid)) {
|
2023-10-13 21:48:08 +03:00
|
|
|
Debug::log(sprintf('uid must be string: %s (%s)', (string) $uid, var_export($uid, true)));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (preg_match('/^[a-f0-9]{40}$/', $uid)) {
|
|
|
|
// Preserve sha1 hash
|
2019-06-01 20:36:46 +03:00
|
|
|
$this->uid = $uid;
|
2022-07-01 16:10:30 +03:00
|
|
|
} else {
|
2019-06-01 20:36:46 +03:00
|
|
|
$this->uid = sha1($uid);
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-25 22:18:48 +03:00
|
|
|
public function addMisc($name, $value)
|
2019-02-03 22:56:41 +03:00
|
|
|
{
|
2023-09-25 22:18:48 +03:00
|
|
|
if (!is_string($name)) {
|
2019-02-03 22:56:41 +03:00
|
|
|
Debug::log('Key must be a string!');
|
2023-09-25 22:18:48 +03:00
|
|
|
} elseif (in_array($name, get_object_vars($this))) {
|
2019-06-01 20:36:46 +03:00
|
|
|
Debug::log('Key must be unique!');
|
2019-02-03 22:56:41 +03:00
|
|
|
} else {
|
2023-09-25 22:18:48 +03:00
|
|
|
$this->misc[$name] = $value;
|
2019-02-03 22:56:41 +03:00
|
|
|
}
|
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2023-09-25 22:18:48 +03:00
|
|
|
public function toArray(): array
|
2018-12-27 00:41:32 +03:00
|
|
|
{
|
|
|
|
return array_merge(
|
2022-07-01 16:10:30 +03:00
|
|
|
[
|
2019-02-03 22:56:41 +03:00
|
|
|
'uri' => $this->uri,
|
2018-12-27 00:41:32 +03:00
|
|
|
'title' => $this->title,
|
|
|
|
'timestamp' => $this->timestamp,
|
|
|
|
'author' => $this->author,
|
|
|
|
'content' => $this->content,
|
|
|
|
'enclosures' => $this->enclosures,
|
|
|
|
'categories' => $this->categories,
|
2019-02-03 22:56:41 +03:00
|
|
|
'uid' => $this->uid,
|
2022-07-01 16:10:30 +03:00
|
|
|
],
|
2018-12-27 00:41:32 +03:00
|
|
|
$this->misc
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|