mirror of
https://github.com/Chouchen/ShikiryuRSS.git
synced 2024-11-21 19:38:52 +01:00
parent
4783124a1d
commit
bc0e818bbc
@ -18,6 +18,7 @@
|
||||
"phpunit/phpunit": "^9"
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*"
|
||||
"ext-dom": "*",
|
||||
"ext-libxml": "*"
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ namespace Shikiryu\SRSS\Entity;
|
||||
|
||||
use Shikiryu\SRSS\Entity\Channel\Image;
|
||||
|
||||
class Channel
|
||||
class Channel implements SRSSElement
|
||||
{
|
||||
public string $title;
|
||||
public string $link;
|
||||
@ -28,4 +28,20 @@ class Channel
|
||||
public ?string $skipDays;
|
||||
|
||||
public array $required = ['title', 'link', 'description'];
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid(): bool
|
||||
{
|
||||
return count(array_filter($this->required, fn($field) => !empty($this->{$field}))) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return get_object_vars($this);
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Shikiryu\SRSS\Entity;
|
||||
|
||||
class Item
|
||||
class Item implements SRSSElement
|
||||
{
|
||||
public ?string $title;
|
||||
public ?string $link;
|
||||
@ -15,5 +15,20 @@ class Item
|
||||
public ?string $pubDate;
|
||||
public ?string $source;
|
||||
|
||||
/**
|
||||
* @var \Shikiryu\SRSS\Entity\Media\Content[]
|
||||
*/
|
||||
public array $medias = [];
|
||||
|
||||
public array $required = ['description'];
|
||||
|
||||
public function isValid(): bool
|
||||
{
|
||||
return count(array_filter($this->required, fn($field) => !empty($this->{$field}))) === 0;
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return get_object_vars($this);
|
||||
}
|
||||
}
|
@ -58,7 +58,6 @@ class Content extends DomDocument
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* main getter for properties
|
||||
*
|
||||
|
16
src/Entity/SRSSElement.php
Normal file
16
src/Entity/SRSSElement.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Shikiryu\SRSS\Entity;
|
||||
|
||||
interface SRSSElement
|
||||
{
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid(): bool;
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array;
|
||||
}
|
380
src/SRSS.php
380
src/SRSS.php
@ -2,46 +2,40 @@
|
||||
|
||||
namespace Shikiryu\SRSS;
|
||||
|
||||
use DOMDocument;
|
||||
use DOMNodeList;
|
||||
use DOMXPath;
|
||||
use Iterator;
|
||||
use ReturnTypeWillChange;
|
||||
use Shikiryu\SRSS\Entity\Channel;
|
||||
use Shikiryu\SRSS\Entity\Channel\Image;
|
||||
use Shikiryu\SRSS\Entity\Item;
|
||||
|
||||
class SRSS extends DomDocument implements Iterator
|
||||
class SRSS implements Iterator
|
||||
{
|
||||
protected DOMXPath $xpath; // xpath engine
|
||||
protected array $items; // array of SRSSItems
|
||||
public array $items; // array of SRSSItems
|
||||
protected $attr; // array of RSS attributes
|
||||
private $position; // Iterator position
|
||||
|
||||
private Channel $channel;
|
||||
public Channel $channel;
|
||||
|
||||
// lists of possible attributes for RSS
|
||||
protected $possibleAttr = [
|
||||
'title' => 'nohtml',
|
||||
'link' => 'link',
|
||||
'description' => 'html',
|
||||
'language' => '',
|
||||
'title' => 'nohtml',
|
||||
'link' => 'link',
|
||||
'description' => 'html',
|
||||
'language' => '',
|
||||
//'language' => 'lang',
|
||||
'copyright' => 'nohtml',
|
||||
'pubDate' => 'date',
|
||||
'lastBuildDate' => 'date',
|
||||
'category' => 'nohtml',
|
||||
'docs' => 'link',
|
||||
'cloud' => '',
|
||||
'generator' => 'nohtml',
|
||||
'managingEditor' => 'email',
|
||||
'webMaster' => 'email',
|
||||
'ttl' => 'int',
|
||||
'image' => '',
|
||||
'rating' => 'nohtml',
|
||||
'copyright' => 'nohtml',
|
||||
'pubDate' => 'date',
|
||||
'lastBuildDate' => 'date',
|
||||
'category' => 'nohtml',
|
||||
'docs' => 'link',
|
||||
'cloud' => '',
|
||||
'generator' => 'nohtml',
|
||||
'managingEditor' => 'email',
|
||||
'webMaster' => 'email',
|
||||
'ttl' => 'int',
|
||||
'image' => '',
|
||||
'rating' => 'nohtml',
|
||||
//'textInput' => '',
|
||||
'skipHours' => 'hour',
|
||||
'skipDays' => 'day',
|
||||
'skipHours' => 'hour',
|
||||
'skipDays' => 'day',
|
||||
];
|
||||
|
||||
/**
|
||||
@ -49,49 +43,20 @@ class SRSS extends DomDocument implements Iterator
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
libxml_use_internal_errors(true);
|
||||
parent::__construct();
|
||||
$this->xpath = new DOMXpath($this);
|
||||
$this->attr = [];
|
||||
$this->items = [];
|
||||
$this->position = 0;
|
||||
$this->formatOutput = true;
|
||||
$this->preserveWhiteSpace = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
* manage of libxml errors
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
foreach (libxml_get_errors() as $error) {
|
||||
error_log($error->message, 3, 'error.log');
|
||||
}
|
||||
libxml_clear_errors();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $link url of the rss
|
||||
* @throws SRSSException
|
||||
*
|
||||
* @return SRSS
|
||||
* @throws SRSSException
|
||||
*/
|
||||
public static function read(string $link): SRSS
|
||||
{
|
||||
$doc = new SRSS;
|
||||
if(@$doc->load($link)) { // We don't want the warning in case of bad XML. Let's manage it with an exception.
|
||||
$channel = $doc->getElementsByTagName('channel');
|
||||
if($channel->length == 1){ // Good URL and good RSS
|
||||
$doc->_loadAttributes(); // loading channel properties
|
||||
$doc->getItems(); // loading all items
|
||||
|
||||
return $doc;
|
||||
}
|
||||
|
||||
throw new SRSSException('invalid file '.$link);
|
||||
}
|
||||
|
||||
throw new SRSSException('Can not open file '.$link);
|
||||
return (new SRSSParser())->parse($link);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,145 +66,13 @@ class SRSS extends DomDocument implements Iterator
|
||||
public static function create()
|
||||
{
|
||||
$doc = new SRSS;
|
||||
$root = $doc->createElement('rss');
|
||||
$root->setAttribute('version', '2.0');
|
||||
$channel = $doc->createElement('channel');
|
||||
$root->appendChild($channel);
|
||||
$doc->appendChild($root);
|
||||
$doc->encoding = "UTF-8";
|
||||
$doc->generator = 'Shikiryu RSS';
|
||||
// $docs = 'http://www.scriptol.fr/rss/RSS-2.0.html';
|
||||
|
||||
$doc->channel = new Channel();
|
||||
$doc->items = [];
|
||||
|
||||
return $doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* getter of "image"'s channel attributes
|
||||
* @return string|array
|
||||
* TODO
|
||||
*/
|
||||
public function image()
|
||||
{
|
||||
$args = func_get_args();
|
||||
if (func_num_args() == 0) {
|
||||
$args[0] = 'url';
|
||||
}
|
||||
$img = $this->xpath->query('//channel/image');
|
||||
if($img->length != 1) { // <image> is not in channel
|
||||
return null;
|
||||
}
|
||||
$img = $img->item(0);
|
||||
$r = [];
|
||||
foreach($img->childNodes as $child) {
|
||||
if($child->nodeType == XML_ELEMENT_NODE && in_array($child->nodeName, $args)) {
|
||||
$r[$child->nodeName] = $child->nodeValue;
|
||||
}
|
||||
}
|
||||
return (func_num_args() > 1) ? $r : $r[$args[0]];
|
||||
}
|
||||
|
||||
/**
|
||||
* setter of "image"'s channel attributes
|
||||
* @param $url string picture's url
|
||||
* @param $title string picture's title
|
||||
* @param $link string link on the picture
|
||||
* @param $width int width
|
||||
* @param $height int height
|
||||
* @param $description string description
|
||||
* TODO
|
||||
*/
|
||||
public function setImage($url, $title, $link, $width = 0, $height = 0, $description = '')
|
||||
{
|
||||
$channel = $this->_getChannel();
|
||||
$array = [];
|
||||
$url = SRSSTools::checkLink($url);
|
||||
$array['url'] = $url;
|
||||
$title = SRSSTools::noHTML($title);
|
||||
$array['title'] = $title;
|
||||
$link = SRSSTools::checkLink($link);
|
||||
$array['link'] = $link;
|
||||
if($width != 0)
|
||||
{
|
||||
$width = SRSSTools::checkInt($width);
|
||||
$array['width'] = $width;
|
||||
}
|
||||
if($height != 0)
|
||||
{
|
||||
$height = SRSSTools::checkInt($height);
|
||||
$array['height'] = $height;
|
||||
}
|
||||
if($description != 0)
|
||||
{
|
||||
$description = SRSSTools::noHTML($description);
|
||||
$array['description'] = $description;
|
||||
}
|
||||
if($this->image == null)
|
||||
{
|
||||
$node = $this->createElement('image');
|
||||
$urlNode = $this->createElement('url', $url);
|
||||
$titleNode = $this->createElement('title', $title);
|
||||
$linkNode = $this->createElement('link', $link);
|
||||
$node->appendChild($urlNode);
|
||||
$node->appendChild($titleNode);
|
||||
$node->appendChild($linkNode);
|
||||
if($width != 0)
|
||||
{
|
||||
$widthNode = $this->createElement('width', $width);
|
||||
$node->appendChild($widthNode);
|
||||
}
|
||||
if($height != 0)
|
||||
{
|
||||
$heightNode = $this->createElement('height', $height);
|
||||
$node->appendChild($heightNode);
|
||||
}
|
||||
if($description != '')
|
||||
{
|
||||
$descNode = $this->createElement('description', $description);
|
||||
$node->appendChild($descNode);
|
||||
}
|
||||
$channel->appendChild($node);
|
||||
}
|
||||
$this->attr['image'] = $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter of "cloud"'s channel attributes
|
||||
* @param $domain string domain
|
||||
* @param $port int port
|
||||
* @param $path string path
|
||||
* @param $registerProcedure string register procedure
|
||||
* @param $protocol string protocol
|
||||
* TODO
|
||||
*/
|
||||
public function setCloud($domain, $port, $path, $registerProcedure, $protocol)
|
||||
{
|
||||
$channel = $this->_getChannel();
|
||||
$array = array();
|
||||
$domain = SRSSTools::noHTML($domain);
|
||||
$array['domain'] = $domain;
|
||||
$port = SRSSTools::checkInt($port);
|
||||
$array['port'] = $port;
|
||||
$path = SRSSTools::noHTML($path);
|
||||
$array['path'] = $path;
|
||||
$registerProcedure = SRSSTools::noHTML($registerProcedure);
|
||||
$array['registerProcedure'] = $registerProcedure;
|
||||
$protocol = SRSSTools::noHTML($protocol);
|
||||
$array['protocol'] = $protocol;
|
||||
if($this->cloud == null)
|
||||
{
|
||||
$node = $this->createElement('cloud');
|
||||
$node->setAttribute('domain', $domain);
|
||||
$node->setAttribute('port', $port);
|
||||
$node->setAttribute('path', $path);
|
||||
$node->setAttribute('registerProcedure', $registerProcedure);
|
||||
$node->setAttribute('protocol', $protocol);
|
||||
$channel->appendChild($node);
|
||||
}
|
||||
$this->attr['cloud'] = $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if current RSS is a valid one (based on specifications)
|
||||
* @return bool
|
||||
@ -251,29 +84,15 @@ class SRSS extends DomDocument implements Iterator
|
||||
$items = $this->getItems();
|
||||
$invalidItems = [];
|
||||
$i = 1;
|
||||
foreach($items as $item){
|
||||
if($item->isValid() === false){
|
||||
foreach ($items as $item) {
|
||||
if ($item->isValid() === false) {
|
||||
$invalidItems[] = $i;
|
||||
$valid = false;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
return ($valid && $this->title != null && $this->link != null && $this->description != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* getter of current RSS channel
|
||||
* @return \DOMNode
|
||||
* @throws SRSSException
|
||||
*/
|
||||
private function _getChannel(): \DOMNode
|
||||
{
|
||||
$channel = $this->getElementsByTagName('channel');
|
||||
if($channel->length != 1) {
|
||||
throw new SRSSException('channel node not created, or too many channel nodes');
|
||||
}
|
||||
|
||||
return $channel->item(0);
|
||||
return ($valid && $this->channel->isValid());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -283,104 +102,61 @@ class SRSS extends DomDocument implements Iterator
|
||||
*/
|
||||
public function __isset($name)
|
||||
{
|
||||
return isset($this->attr[$name]);
|
||||
return isset($this->channel->{$name});
|
||||
}
|
||||
|
||||
/**
|
||||
* setter of others attributes
|
||||
*
|
||||
* @param $name
|
||||
* @param $val
|
||||
*
|
||||
* @throws SRSSException
|
||||
*/
|
||||
public function __set($name, $val)
|
||||
{
|
||||
$channel = $this->_getChannel();
|
||||
if(array_key_exists($name, $this->possibleAttr)){
|
||||
$flag = $this->possibleAttr[$name];
|
||||
$val = SRSSTools::check($val, $flag);
|
||||
if(!empty($val)){
|
||||
if($this->$name == null){
|
||||
$node = $this->createElement($name, $val);
|
||||
$channel->appendChild($node);
|
||||
}
|
||||
$this->attr[$name] = $val;
|
||||
}
|
||||
}else{
|
||||
throw new SRSSException($name.' is not a possible item');
|
||||
if (!array_key_exists($name, $this->possibleAttr)) {
|
||||
throw new SRSSException($name . ' is not a possible item');
|
||||
}
|
||||
$flag = $this->possibleAttr[$name];
|
||||
$val = SRSSTools::check($val, $flag);
|
||||
if (!empty($val)) {
|
||||
$this->channel->{$name} = $val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* getter of others attributes
|
||||
*
|
||||
* @param $name
|
||||
*
|
||||
* @return null|string
|
||||
* @throws SRSSException
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
if (isset($this->channel->{$name})) {
|
||||
return $this->channel->{$name};
|
||||
}
|
||||
// $channel = $this->_getChannel();
|
||||
if(array_key_exists($name, $this->possibleAttr)){
|
||||
$tmp = $this->xpath->query('//channel/'.$name);
|
||||
if($tmp->length != 1) {
|
||||
return null;
|
||||
}
|
||||
return $tmp->item(0)->nodeValue;
|
||||
}
|
||||
|
||||
throw new SRSSException($name.' is not a possible value.');
|
||||
}
|
||||
|
||||
/**
|
||||
* add a SRSS Item as an item into current RSS as first item
|
||||
* @param SRSSItem $item
|
||||
*/
|
||||
public function addItemBefore(SRSSItem $item)
|
||||
{
|
||||
$node = $this->importNode($item->getItem(), true);
|
||||
$items = $this->getElementsByTagName('item');
|
||||
if($items->length != 0){
|
||||
$firstNode = $items->item(0);
|
||||
if($firstNode != null)
|
||||
$firstNode->parentNode->insertBefore($node, $firstNode);
|
||||
else
|
||||
$this->addItem($item);
|
||||
}
|
||||
else
|
||||
$this->addItem($item);
|
||||
}
|
||||
|
||||
/**
|
||||
* add a SRSS Item as an item into current RSS
|
||||
* @param SRSSItem $item
|
||||
*/
|
||||
public function addItem(SRSSItem $item)
|
||||
{
|
||||
$node = $this->importNode($item->getItem(), true);
|
||||
$channel = $this->_getChannel();
|
||||
$channel->appendChild($node);
|
||||
return $this->channel->{$name} ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* rewind from Iterator
|
||||
*/
|
||||
public function rewind() {
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->position = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* current from Iterator
|
||||
*/
|
||||
public function current() {
|
||||
public function current()
|
||||
{
|
||||
return $this->items[$this->position];
|
||||
}
|
||||
|
||||
/**
|
||||
* key from Iterator
|
||||
*/
|
||||
#[ReturnTypeWillChange] public function key(): int
|
||||
public function key(): int
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
@ -388,7 +164,7 @@ class SRSS extends DomDocument implements Iterator
|
||||
/**
|
||||
* next from Iterator
|
||||
*/
|
||||
#[ReturnTypeWillChange] public function next(): void
|
||||
public function next(): void
|
||||
{
|
||||
++$this->position;
|
||||
}
|
||||
@ -396,7 +172,7 @@ class SRSS extends DomDocument implements Iterator
|
||||
/**
|
||||
* valid from Iterator
|
||||
*/
|
||||
#[ReturnTypeWillChange] public function valid(): bool
|
||||
public function valid(): bool
|
||||
{
|
||||
return isset($this->items[$this->position]);
|
||||
}
|
||||
@ -417,11 +193,13 @@ class SRSS extends DomDocument implements Iterator
|
||||
public function getLast(): Item
|
||||
{
|
||||
$items = $this->getItems();
|
||||
|
||||
return $items[array_key_last($items)];
|
||||
}
|
||||
|
||||
/**
|
||||
* getter of an item
|
||||
*
|
||||
* @param $i int
|
||||
*
|
||||
* @return Item|null
|
||||
@ -429,6 +207,7 @@ class SRSS extends DomDocument implements Iterator
|
||||
public function getItem(int $i): ?Item
|
||||
{
|
||||
$i--;
|
||||
|
||||
return $this->items[$i] ?? null;
|
||||
}
|
||||
|
||||
@ -439,62 +218,9 @@ class SRSS extends DomDocument implements Iterator
|
||||
*/
|
||||
public function getItems(): array
|
||||
{
|
||||
if (!empty($this->items)) {
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
$channel = $this->_getChannel();
|
||||
/** @var DOMNodeList $items */
|
||||
$items = $channel->getElementsByTagName('item');
|
||||
$length = $items->length;
|
||||
$this->items = [];
|
||||
if ($length > 0) {
|
||||
for($i = 0; $i < $length; $i++) {
|
||||
$this->items[$i] = SRSSItem::read($items->item($i));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
/**
|
||||
* display XML
|
||||
* see DomDocument's docs
|
||||
*/
|
||||
public function show(): bool|string
|
||||
{
|
||||
// TODO build
|
||||
return $this->saveXml();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* putting all RSS attributes into the object
|
||||
* @throws SRSSException
|
||||
*/
|
||||
private function _loadAttributes(): void
|
||||
{
|
||||
$node_channel = $this->_getChannel();
|
||||
$this->channel = new Channel();
|
||||
|
||||
foreach($node_channel->childNodes as $child) {
|
||||
if($child->nodeType == XML_ELEMENT_NODE && $child->nodeName !== 'item') {
|
||||
if($child->nodeName == 'image') {
|
||||
$image = new Image();
|
||||
foreach($child->childNodes as $children) {
|
||||
if($children->nodeType == XML_ELEMENT_NODE) {
|
||||
$image->{$child->nodeName} = $children->nodeValue;
|
||||
}
|
||||
}
|
||||
$this->channel->image = $image;
|
||||
|
||||
} else {
|
||||
$this->channel->{$child->nodeName} = $child->nodeValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* transform current object into an array
|
||||
* @return array
|
||||
@ -504,10 +230,10 @@ class SRSS extends DomDocument implements Iterator
|
||||
{
|
||||
$doc = $this->channel->toArray();
|
||||
|
||||
foreach($this->getItems() as $item) {
|
||||
foreach ($this->getItems() as $item) {
|
||||
$doc['items'][] = $item->toArray();
|
||||
}
|
||||
|
||||
return $doc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,161 @@
|
||||
|
||||
namespace Shikiryu\SRSS;
|
||||
|
||||
class SRSSBuilder
|
||||
{
|
||||
use DOMDocument;
|
||||
|
||||
class SRSSBuilder extends DomDocument
|
||||
{
|
||||
public function build()
|
||||
{
|
||||
$root = $this->createElement('rss');
|
||||
$root->setAttribute('version', '2.0');
|
||||
$channel = $this->createElement('channel');
|
||||
$root->appendChild($channel);
|
||||
$this->appendChild($root);
|
||||
$this->encoding = 'UTF-8';
|
||||
$this->generator = 'Shikiryu RSS';
|
||||
$this->formatOutput = true;
|
||||
$this->preserveWhiteSpace = false;
|
||||
// $docs = 'http://www.scriptol.fr/rss/RSS-2.0.html';
|
||||
}
|
||||
|
||||
/**
|
||||
* add a SRSS Item as an item into current RSS as first item
|
||||
* @param SRSSItem $item
|
||||
*/
|
||||
public function addItemBefore(SRSSItem $item)
|
||||
{
|
||||
$node = $this->importNode($item->getItem(), true);
|
||||
$items = $this->getElementsByTagName('item');
|
||||
if($items->length != 0){
|
||||
$firstNode = $items->item(0);
|
||||
if($firstNode != null)
|
||||
$firstNode->parentNode->insertBefore($node, $firstNode);
|
||||
else
|
||||
$this->addItem($item);
|
||||
}
|
||||
else
|
||||
$this->addItem($item);
|
||||
}
|
||||
|
||||
/**
|
||||
* add a SRSS Item as an item into current RSS
|
||||
* @param SRSSItem $item
|
||||
*/
|
||||
public function addItem(SRSSItem $item)
|
||||
{
|
||||
$node = $this->importNode($item->getItem(), true);
|
||||
$channel = $this->_getChannel();
|
||||
$channel->appendChild($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* display XML
|
||||
* see DomDocument's docs
|
||||
*/
|
||||
public function show(): bool|string
|
||||
{
|
||||
// TODO build
|
||||
return $this->saveXml();
|
||||
}
|
||||
|
||||
/**
|
||||
* setter of "image"'s channel attributes
|
||||
* @param $url string picture's url
|
||||
* @param $title string picture's title
|
||||
* @param $link string link on the picture
|
||||
* @param $width int width
|
||||
* @param $height int height
|
||||
* @param $description string description
|
||||
* TODO
|
||||
*/
|
||||
public function setImage($url, $title, $link, $width = 0, $height = 0, $description = '')
|
||||
{
|
||||
$channel = $this->_getChannel();
|
||||
$array = [];
|
||||
$url = SRSSTools::checkLink($url);
|
||||
$array['url'] = $url;
|
||||
$title = SRSSTools::noHTML($title);
|
||||
$array['title'] = $title;
|
||||
$link = SRSSTools::checkLink($link);
|
||||
$array['link'] = $link;
|
||||
if($width != 0)
|
||||
{
|
||||
$width = SRSSTools::checkInt($width);
|
||||
$array['width'] = $width;
|
||||
}
|
||||
if($height != 0)
|
||||
{
|
||||
$height = SRSSTools::checkInt($height);
|
||||
$array['height'] = $height;
|
||||
}
|
||||
if($description != 0)
|
||||
{
|
||||
$description = SRSSTools::noHTML($description);
|
||||
$array['description'] = $description;
|
||||
}
|
||||
if($this->image == null)
|
||||
{
|
||||
$node = $this->createElement('image');
|
||||
$urlNode = $this->createElement('url', $url);
|
||||
$titleNode = $this->createElement('title', $title);
|
||||
$linkNode = $this->createElement('link', $link);
|
||||
$node->appendChild($urlNode);
|
||||
$node->appendChild($titleNode);
|
||||
$node->appendChild($linkNode);
|
||||
if($width != 0)
|
||||
{
|
||||
$widthNode = $this->createElement('width', $width);
|
||||
$node->appendChild($widthNode);
|
||||
}
|
||||
if($height != 0)
|
||||
{
|
||||
$heightNode = $this->createElement('height', $height);
|
||||
$node->appendChild($heightNode);
|
||||
}
|
||||
if($description != '')
|
||||
{
|
||||
$descNode = $this->createElement('description', $description);
|
||||
$node->appendChild($descNode);
|
||||
}
|
||||
$channel->appendChild($node);
|
||||
}
|
||||
$this->attr['image'] = $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter of "cloud"'s channel attributes
|
||||
* @param $domain string domain
|
||||
* @param $port int port
|
||||
* @param $path string path
|
||||
* @param $registerProcedure string register procedure
|
||||
* @param $protocol string protocol
|
||||
* TODO
|
||||
*/
|
||||
public function setCloud($domain, $port, $path, $registerProcedure, $protocol)
|
||||
{
|
||||
$channel = $this->_getChannel();
|
||||
$array = array();
|
||||
$domain = SRSSTools::noHTML($domain);
|
||||
$array['domain'] = $domain;
|
||||
$port = SRSSTools::checkInt($port);
|
||||
$array['port'] = $port;
|
||||
$path = SRSSTools::noHTML($path);
|
||||
$array['path'] = $path;
|
||||
$registerProcedure = SRSSTools::noHTML($registerProcedure);
|
||||
$array['registerProcedure'] = $registerProcedure;
|
||||
$protocol = SRSSTools::noHTML($protocol);
|
||||
$array['protocol'] = $protocol;
|
||||
if($this->cloud == null)
|
||||
{
|
||||
$node = $this->createElement('cloud');
|
||||
$node->setAttribute('domain', $domain);
|
||||
$node->setAttribute('port', $port);
|
||||
$node->setAttribute('path', $path);
|
||||
$node->setAttribute('registerProcedure', $registerProcedure);
|
||||
$node->setAttribute('protocol', $protocol);
|
||||
$channel->appendChild($node);
|
||||
}
|
||||
$this->attr['cloud'] = $array;
|
||||
}
|
||||
}
|
@ -59,10 +59,8 @@ class SRSSItem extends DomDocument
|
||||
if ($child->nodeType === XML_ELEMENT_NODE && $child->nodeName !== 'item') {
|
||||
if (array_key_exists($child->nodeName, self::$possibilities) && self::$possibilities[$child->nodeName] === 'folder') {
|
||||
self::_loadChildAttributes($item, $child);
|
||||
} elseif ($child->nodeName === 'media:group') {
|
||||
// TODO
|
||||
} elseif ($child->nodeName === 'media:content') {
|
||||
$item->{$child->nodeName} = new Content($child);
|
||||
$item->medias[] = new Content($child);
|
||||
} else {
|
||||
$item->{$child->nodeName} = $child->nodeValue;
|
||||
}
|
||||
|
146
src/SRSSParser.php
Normal file
146
src/SRSSParser.php
Normal file
@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
namespace Shikiryu\SRSS;
|
||||
|
||||
use DOMDocument;
|
||||
use DOMNodeList;
|
||||
use DOMXPath;
|
||||
use Shikiryu\SRSS\Entity\Channel;
|
||||
use Shikiryu\SRSS\Entity\Channel\Image;
|
||||
|
||||
class SRSSParser extends DomDocument
|
||||
{
|
||||
private SRSS $doc;
|
||||
private DOMXPath $xpath;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
libxml_use_internal_errors(true);
|
||||
parent::__construct();
|
||||
$this->xpath = new DOMXpath($this);
|
||||
$this->doc = new SRSS();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
* manage of libxml errors
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
foreach (libxml_get_errors() as $error) {
|
||||
error_log($error->message, 3, 'error.log');
|
||||
}
|
||||
libxml_clear_errors();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $link
|
||||
*
|
||||
* @return \Shikiryu\SRSS\SRSS
|
||||
* @throws \Shikiryu\SRSS\SRSSException
|
||||
*/
|
||||
public function parse(string $link)
|
||||
{
|
||||
if(@$this->load($link)) { // We don't want the warning in case of bad XML. Let's manage it with an exception.
|
||||
$channel = $this->getElementsByTagName('channel');
|
||||
if($channel->length === 1){ // Good URL and good RSS
|
||||
$this->_loadAttributes(); // loading channel properties
|
||||
$this->getItems(); // loading all items
|
||||
|
||||
return $this->doc;
|
||||
}
|
||||
|
||||
throw new SRSSException('invalid file '.$link);
|
||||
}
|
||||
|
||||
throw new SRSSException('Can not open file '.$link);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|mixed
|
||||
* @throws \Shikiryu\SRSS\SRSSException
|
||||
*/
|
||||
private function getItems()
|
||||
{
|
||||
$channel = $this->_getChannel();
|
||||
/** @var DOMNodeList $items */
|
||||
$items = $channel->getElementsByTagName('item');
|
||||
$length = $items->length;
|
||||
$this->doc->items = [];
|
||||
if ($length > 0) {
|
||||
for($i = 0; $i < $length; $i++) {
|
||||
$this->doc->items[$i] = SRSSItem::read($items->item($i));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->doc->items;
|
||||
}
|
||||
/**
|
||||
* putting all RSS attributes into the object
|
||||
* @throws SRSSException
|
||||
*/
|
||||
private function _loadAttributes(): void
|
||||
{
|
||||
$node_channel = $this->_getChannel();
|
||||
$this->doc->channel = new Channel();
|
||||
|
||||
foreach($node_channel->childNodes as $child) {
|
||||
if ($child->nodeType === XML_ELEMENT_NODE && $child->nodeName !== 'item') {
|
||||
if($child->nodeName === 'image') {
|
||||
$image = new Image();
|
||||
foreach($child->childNodes as $children) {
|
||||
if($children->nodeType == XML_ELEMENT_NODE) {
|
||||
$image->{$child->nodeName} = $children->nodeValue;
|
||||
}
|
||||
}
|
||||
$this->doc->channel->image = $image;
|
||||
|
||||
} else {
|
||||
$this->doc->channel->{$child->nodeName} = $child->nodeValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* getter of current RSS channel
|
||||
* @return \DOMNode
|
||||
* @throws SRSSException
|
||||
*/
|
||||
private function _getChannel(): \DOMNode
|
||||
{
|
||||
$channel = $this->getElementsByTagName('channel');
|
||||
if($channel->length != 1) {
|
||||
throw new SRSSException('channel node not created, or too many channel nodes');
|
||||
}
|
||||
|
||||
return $channel->item(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* getter of "image"'s channel attributes
|
||||
* @return string|array
|
||||
* TODO
|
||||
*/
|
||||
public function image()
|
||||
{
|
||||
$args = func_get_args();
|
||||
if (func_num_args() == 0) {
|
||||
$args[0] = 'url';
|
||||
}
|
||||
$img = $this->xpath->query('//channel/image');
|
||||
if ($img->length != 1) { // <image> is not in channel
|
||||
return null;
|
||||
}
|
||||
$img = $img->item(0);
|
||||
$r = [];
|
||||
foreach ($img->childNodes as $child) {
|
||||
if ($child->nodeType == XML_ELEMENT_NODE && in_array($child->nodeName, $args)) {
|
||||
$r[$child->nodeName] = $child->nodeValue;
|
||||
}
|
||||
}
|
||||
|
||||
return (func_num_args() > 1) ? $r : $r[$args[0]];
|
||||
}
|
||||
}
|
@ -20,4 +20,23 @@ class BasicReader extends TestCase
|
||||
$this->expectException(SRSSException::class);
|
||||
$rss = SRSS::read('not_found.xml');
|
||||
}
|
||||
|
||||
public function testSpecificationExampleRSS()
|
||||
{
|
||||
$rss = SRSS::read(__DIR__.'/resources/harvard.xml');
|
||||
self::assertEquals('Liftoff News', $rss->title);
|
||||
self::assertEquals('http://liftoff.msfc.nasa.gov/', $rss->link);
|
||||
self::assertEquals('Liftoff to Space Exploration.', $rss->description);
|
||||
self::assertEquals('en-us', $rss->language);
|
||||
self::assertEquals('Tue, 10 Jun 2003 04:00:00 GMT', $rss->pubDate);
|
||||
self::assertEquals('Tue, 10 Jun 2003 09:41:01 GMT', $rss->lastBuildDate);
|
||||
self::assertEquals('http://blogs.law.harvard.edu/tech/rss', $rss->docs);
|
||||
self::assertEquals('Weblog Editor 2.0', $rss->generator);
|
||||
self::assertEquals('editor@example.com', $rss->managingEditor);
|
||||
self::assertEquals('webmaster@example.com', $rss->webMaster);
|
||||
self::assertCount(4, $rss->items);
|
||||
self::assertEquals('Star City', $rss->getFirst()->title);
|
||||
self::assertEquals('http://liftoff.msfc.nasa.gov/news/2003/news-laundry.asp', $rss->getLast()->link);
|
||||
self::assertEquals('Fri, 30 May 2003 11:06:42 GMT', $rss->getItem(2)->pubDate);
|
||||
}
|
||||
}
|
@ -13,6 +13,17 @@ class MediaTest extends TestCase
|
||||
$first_item = $rss->getFirst();
|
||||
self::assertEquals('Kirstie Alley, \'Cheers\' and \'Veronica\'s Closet\' star, dead at 71', $first_item->title);
|
||||
|
||||
self::assertEquals('https://cdn.cnn.com/cnnnext/dam/assets/221205172141-kirstie-alley-2005-super-169.jpg', $first_item->{'media:content'}->url);
|
||||
self::assertEquals('https://cdn.cnn.com/cnnnext/dam/assets/221205172141-kirstie-alley-2005-super-169.jpg', $first_item->medias[0]->url);
|
||||
}
|
||||
|
||||
public function testMusicVideo()
|
||||
{
|
||||
$rss = SRSS::read('resources/media/music-video.xml');
|
||||
self::assertEquals('Music Videos 101', $rss->title);
|
||||
|
||||
self::assertCount(1, $rss->items);
|
||||
|
||||
$first_item = $rss->getFirst();
|
||||
self::assertEquals('http://www.foo.com/movie.mov', $first_item->medias[0]->url);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user