mirror of
https://github.com/Chouchen/ShikiryuRSS.git
synced 2024-11-21 19:38:52 +01:00
parent
fec8c122e3
commit
63d0b03b50
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
/vendor/
|
||||
/.idea
|
||||
/tests/error.log
|
||||
/.phpunit.result.cache
|
||||
|
30
phpunit.xml
Normal file
30
phpunit.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit bootstrap = "vendor/autoload.php"
|
||||
backupGlobals = "false"
|
||||
backupStaticAttributes = "false"
|
||||
colors = "true"
|
||||
convertErrorsToExceptions = "true"
|
||||
convertNoticesToExceptions = "true"
|
||||
convertWarningsToExceptions = "true"
|
||||
processIsolation = "false"
|
||||
stopOnFailure = "false"
|
||||
syntaxCheck = "false">
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Project Test Suite">
|
||||
<directory>tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">src/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
<php>
|
||||
<env name="APP_ENV" value="testing"/>
|
||||
</php>
|
||||
|
||||
</phpunit>
|
@ -1,16 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Shikiryu\SRSS;
|
||||
namespace Shikiryu\SRSS\Builder;
|
||||
|
||||
use DOMDocument;
|
||||
use DOMElement;
|
||||
use Shikiryu\SRSS\Entity\Channel;
|
||||
use Shikiryu\SRSS\Entity\Item;
|
||||
use Shikiryu\SRSS\Parser\ItemParser;
|
||||
use Shikiryu\SRSS\SRSS;
|
||||
use Shikiryu\SRSS\SRSSTools;
|
||||
|
||||
class SRSSBuilder extends DomDocument
|
||||
{
|
||||
public function build()
|
||||
public function build(SRSS $srss, string $filepath)
|
||||
{
|
||||
$root = $this->createElement('rss');
|
||||
$root->setAttribute('version', '2.0');
|
||||
$channel = $this->createElement('channel');
|
||||
|
||||
$this->appendChannelToDom($srss->channel, $channel);
|
||||
|
||||
$this->appendItemsToDom($srss->items, $channel);
|
||||
|
||||
$root->appendChild($channel);
|
||||
$this->appendChild($root);
|
||||
$this->encoding = 'UTF-8';
|
||||
@ -18,13 +29,16 @@ class SRSSBuilder extends DomDocument
|
||||
$this->formatOutput = true;
|
||||
$this->preserveWhiteSpace = false;
|
||||
// $docs = 'http://www.scriptol.fr/rss/RSS-2.0.html';
|
||||
|
||||
$this->save($filepath);
|
||||
}
|
||||
|
||||
/**
|
||||
* add a SRSS Item as an item into current RSS as first item
|
||||
* @param SRSSItem $item
|
||||
*
|
||||
* @param ItemParser $item
|
||||
*/
|
||||
public function addItemBefore(SRSSItem $item)
|
||||
public function addItemBefore(ItemParser $item)
|
||||
{
|
||||
$node = $this->importNode($item->getItem(), true);
|
||||
$items = $this->getElementsByTagName('item');
|
||||
@ -41,9 +55,10 @@ class SRSSBuilder extends DomDocument
|
||||
|
||||
/**
|
||||
* add a SRSS Item as an item into current RSS
|
||||
* @param SRSSItem $item
|
||||
*
|
||||
* @param ItemParser $item
|
||||
*/
|
||||
public function addItem(SRSSItem $item)
|
||||
public function addItem(ItemParser $item)
|
||||
{
|
||||
$node = $this->importNode($item->getItem(), true);
|
||||
$channel = $this->_getChannel();
|
||||
@ -54,7 +69,7 @@ class SRSSBuilder extends DomDocument
|
||||
* display XML
|
||||
* see DomDocument's docs
|
||||
*/
|
||||
public function show(): bool|string
|
||||
public function show()
|
||||
{
|
||||
// TODO build
|
||||
return $this->saveXml();
|
||||
@ -159,4 +174,29 @@ class SRSSBuilder extends DomDocument
|
||||
}
|
||||
$this->attr['cloud'] = $array;
|
||||
}
|
||||
|
||||
private function appendChannelToDom(Channel $channel, DOMElement $node)
|
||||
{
|
||||
foreach (array_filter($channel->toArray(), fn($el) => !empty($el)) as $name => $value) {
|
||||
$new_node = $this->createElement($name, $value);
|
||||
$node->appendChild($new_node);
|
||||
}
|
||||
}
|
||||
|
||||
private function appendItemsToDom(array $items, DOMElement $channel)
|
||||
{
|
||||
foreach ($items as $item) {
|
||||
$this->appendItemToDom($item, $channel);
|
||||
}
|
||||
}
|
||||
|
||||
private function appendItemToDom(Item $item, DOMElement $channel)
|
||||
{
|
||||
$itemNode = $this->createElement('item');
|
||||
foreach (array_filter($item->toArray()) as $name => $value) {
|
||||
$new_node = $this->createElement($name, $value);
|
||||
$itemNode->appendChild($new_node);
|
||||
}
|
||||
$channel->appendChild($itemNode);
|
||||
}
|
||||
}
|
@ -16,18 +16,18 @@ class Channel extends HasValidator implements SRSSElement
|
||||
* @required
|
||||
* @nohtml
|
||||
*/
|
||||
public string $title;
|
||||
public string $title = '';
|
||||
|
||||
/**
|
||||
* @required
|
||||
* @url
|
||||
*/
|
||||
public string $link;
|
||||
public string $link = '';
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public string $description;
|
||||
public string $description = '';
|
||||
|
||||
/**
|
||||
* @lang
|
||||
@ -96,9 +96,7 @@ class Channel extends HasValidator implements SRSSElement
|
||||
*/
|
||||
public function isValid(): bool
|
||||
{
|
||||
$annotation_validation = new Validator();
|
||||
|
||||
return $annotation_validation->isObjectValid($this);
|
||||
return (new Validator())->isObjectValid($this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,6 +104,8 @@ class Channel extends HasValidator implements SRSSElement
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return get_object_vars($this);
|
||||
$vars = get_object_vars($this);
|
||||
unset($vars['validated']);
|
||||
return $vars;
|
||||
}
|
||||
}
|
@ -2,14 +2,47 @@
|
||||
|
||||
namespace Shikiryu\SRSS\Entity\Channel;
|
||||
|
||||
class Image
|
||||
use Shikiryu\SRSS\Entity\SRSSElement;
|
||||
use Shikiryu\SRSS\Validator\HasValidator;
|
||||
use Shikiryu\SRSS\Validator\Validator;
|
||||
|
||||
class Image extends HasValidator implements SRSSElement
|
||||
{
|
||||
/**
|
||||
* @required
|
||||
* @url
|
||||
*/
|
||||
public string $url;
|
||||
/**
|
||||
* @required
|
||||
* @nohtml
|
||||
*/
|
||||
public string $title;
|
||||
/**
|
||||
* @required
|
||||
* @url
|
||||
*/
|
||||
public string $link;
|
||||
public int $width; // Maximum value for width is 144, default value is 88.
|
||||
public int $height; //Maximum value for height is 400, default value is 31.
|
||||
/**
|
||||
* @int
|
||||
* @max 144
|
||||
*/
|
||||
public int $width = 88; // Maximum value for width is 144, default value is 88.
|
||||
/**
|
||||
* @int
|
||||
* @max 400
|
||||
*/
|
||||
public int $height = 31; //Maximum value for height is 400, default value is 31.
|
||||
|
||||
public string $description;
|
||||
|
||||
public array $required = ['url', 'title', 'link'];
|
||||
public function isValid(): bool
|
||||
{
|
||||
return (new Validator())->isObjectValid($this);
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return get_object_vars($this);
|
||||
}
|
||||
}
|
@ -2,33 +2,68 @@
|
||||
|
||||
namespace Shikiryu\SRSS\Entity;
|
||||
|
||||
class Item implements SRSSElement
|
||||
use Shikiryu\SRSS\Validator\HasValidator;
|
||||
use Shikiryu\SRSS\Validator\Validator;
|
||||
|
||||
/**
|
||||
* https://cyber.harvard.edu/rss/rss.html#hrelementsOfLtitemgt
|
||||
*/
|
||||
class Item extends HasValidator implements SRSSElement
|
||||
{
|
||||
public ?string $title;
|
||||
public ?string $link;
|
||||
public ?string $description;
|
||||
public ?string $author;
|
||||
public ?string $category;
|
||||
public ?string $comments;
|
||||
public ?string $enclosure;
|
||||
public ?string $guid;
|
||||
public ?string $pubDate;
|
||||
public ?string $source;
|
||||
/**
|
||||
* @requiredOr description
|
||||
* @nohtml
|
||||
*/
|
||||
public ?string $title = null;
|
||||
/**
|
||||
* @url
|
||||
*/
|
||||
public ?string $link = null;
|
||||
/**
|
||||
* @requiredOr title
|
||||
*/
|
||||
public ?string $description = null;
|
||||
/**
|
||||
* @email
|
||||
*/
|
||||
public ?string $author = null;
|
||||
/*
|
||||
* TODO can be multiple with attributes and all
|
||||
*/
|
||||
public ?string $category = null;
|
||||
/**
|
||||
* @url
|
||||
*/
|
||||
public ?string $comments = null;
|
||||
/*
|
||||
* TODO 1 attributes and 1 value
|
||||
*/
|
||||
public ?string $enclosure = null;
|
||||
public ?string $guid = null;
|
||||
/**
|
||||
* @date
|
||||
*/
|
||||
public ?string $pubDate = null;
|
||||
/*
|
||||
* TODO 1 attributes and 1 value
|
||||
*/
|
||||
public ?string $source = null;
|
||||
|
||||
/**
|
||||
* @var \Shikiryu\SRSS\Entity\Media\Content[]
|
||||
* @contentMedia
|
||||
*/
|
||||
public array $medias = [];
|
||||
|
||||
public array $required = ['description'];
|
||||
|
||||
public function isValid(): bool
|
||||
{
|
||||
return count(array_filter($this->required, fn($field) => !empty($this->{$field}))) === 0;
|
||||
return (new Validator())->isObjectValid($this);
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return get_object_vars($this);
|
||||
$vars = get_object_vars($this);
|
||||
unset($vars['validated']);
|
||||
return $vars;
|
||||
}
|
||||
}
|
@ -2,123 +2,76 @@
|
||||
|
||||
namespace Shikiryu\SRSS\Entity\Media;
|
||||
|
||||
use DOMDocument;
|
||||
use DOMElement;
|
||||
use DOMNode;
|
||||
use Shikiryu\SRSS\SRSSException;
|
||||
use Shikiryu\SRSS\SRSSTools;
|
||||
use Shikiryu\SRSS\Entity\SRSSElement;
|
||||
use Shikiryu\SRSS\Validator\HasValidator;
|
||||
use Shikiryu\SRSS\Validator\Validator;
|
||||
|
||||
class Content extends DomDocument
|
||||
class Content extends HasValidator implements SRSSElement
|
||||
{
|
||||
protected array $possibilities = [
|
||||
'url' => 'link',
|
||||
'fileSize' => 'int', // TODO
|
||||
'type' => 'media_type', // TODO
|
||||
'medium' => 'media_medium', // TODO
|
||||
'isDefault' => 'bool', // TODO
|
||||
'expression' => 'medium_expression', // TODO
|
||||
'bitrate' => 'int',
|
||||
'framerate' => 'int',
|
||||
'samplingrate' => 'float',
|
||||
'channels' => 'int',
|
||||
'duration' => 'int',
|
||||
'height' => 'int',
|
||||
'width' => 'int',
|
||||
'lang' => '',
|
||||
];
|
||||
private array $attr = [];
|
||||
|
||||
private DOMNode $node;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param DomNode $node
|
||||
* @url
|
||||
*/
|
||||
public function __construct(?\DOMNode $node = null)
|
||||
public ?string $url = null;
|
||||
/**
|
||||
* @int
|
||||
*/
|
||||
public ?int $filesize = null;
|
||||
/**
|
||||
* @mediaType
|
||||
*/
|
||||
public ?string $type = null;
|
||||
/**
|
||||
* @mediaMedium
|
||||
*/
|
||||
public ?string $medium = null;
|
||||
/**
|
||||
* @bool
|
||||
*/
|
||||
public ?bool $isDefault = null;
|
||||
/**
|
||||
* @mediaExpression
|
||||
*/
|
||||
public ?string $expression = null;
|
||||
/**
|
||||
* @int
|
||||
*/
|
||||
public ?int $bitrate = null;
|
||||
/**
|
||||
* @int
|
||||
*/
|
||||
public ?int $framerate = null;
|
||||
/**
|
||||
* @float
|
||||
*/
|
||||
public ?float $samplerate = null;
|
||||
/**
|
||||
* @int
|
||||
*/
|
||||
public ?int $channels = null;
|
||||
/**
|
||||
* @int
|
||||
*/
|
||||
public ?int $duration = null;
|
||||
/**
|
||||
* @int
|
||||
*/
|
||||
public ?int $height = null;
|
||||
/**
|
||||
* @int
|
||||
*/
|
||||
public ?int $width = null;
|
||||
/**
|
||||
* @lang
|
||||
*/
|
||||
public ?string $lang = null;
|
||||
|
||||
public function isValid(): bool
|
||||
{
|
||||
parent::__construct();
|
||||
if ($node instanceof DOMElement) {
|
||||
$this->node = $this->importNode($node, true);
|
||||
} else {
|
||||
$this->node = $this->importNode(new DomElement('item'));
|
||||
}
|
||||
$this->_loadAttributes();
|
||||
return (new Validator())->isObjectValid($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private function _loadAttributes(): void
|
||||
public function toArray(): array
|
||||
{
|
||||
foreach ($this->node->attributes as $attributes) {
|
||||
if (array_key_exists($attributes->name, $this->possibilities)) {
|
||||
$this->{$attributes->name} = $attributes->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* main getter for properties
|
||||
*
|
||||
* @param $name
|
||||
*
|
||||
* @return null|string
|
||||
* @throws SRSSException
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
if (array_key_exists($name, $this->attr)) {
|
||||
return $this->attr[$name];
|
||||
}
|
||||
|
||||
if (array_key_exists($name, $this->possibilities)) {
|
||||
$tmp = $this->node->getElementsByTagName($name);
|
||||
if ($tmp->length !== 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $tmp->item(0)->nodeValue;
|
||||
}
|
||||
|
||||
throw new SRSSException(sprintf('%s is not a possible item (%s)', $name, implode(', ', array_keys($this->possibilities))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($name)
|
||||
{
|
||||
return isset($this->attr[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* main setter for properties
|
||||
*
|
||||
* @param $name
|
||||
* @param $val
|
||||
*
|
||||
* @throws SRSSException
|
||||
*/
|
||||
public function __set($name, $val)
|
||||
{
|
||||
if (!array_key_exists($name, $this->possibilities)) {
|
||||
throw new SRSSException(sprintf('%s is not a possible item (%s)', $name, implode(', ', array_keys($this->possibilities))));
|
||||
}
|
||||
|
||||
$flag = $this->possibilities[$name];
|
||||
|
||||
if ($flag !== '') {
|
||||
$val = SRSSTools::check($val, $flag);
|
||||
}
|
||||
|
||||
if (!empty($val)) {
|
||||
if ($this->$name === null) {
|
||||
$this->node->appendChild(new DomElement($name, $val));
|
||||
}
|
||||
$this->attr[$name] = $val;
|
||||
}
|
||||
return get_object_vars($this);
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Shikiryu\SRSS\Entity\Media;
|
||||
|
||||
class Group
|
||||
{
|
||||
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Shikiryu\SRSS;
|
||||
namespace Shikiryu\SRSS\Exception;
|
||||
|
||||
use Exception;
|
||||
|
@ -1,18 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Shikiryu\SRSS;
|
||||
namespace Shikiryu\SRSS\Parser;
|
||||
|
||||
use DOMDocument;
|
||||
use DOMElement;
|
||||
use DOMException;
|
||||
use DOMNode;
|
||||
use Shikiryu\SRSS\Entity\Item;
|
||||
use Shikiryu\SRSS\Entity\Media\Content;
|
||||
use Shikiryu\SRSS\Exception\SRSSException;
|
||||
use Shikiryu\SRSS\SRSSTools;
|
||||
|
||||
/**
|
||||
* @property string|null $description
|
||||
*/
|
||||
class SRSSItem extends DomDocument
|
||||
class ItemParser extends DomDocument
|
||||
{
|
||||
|
||||
protected DOMNode $node; // item node
|
||||
@ -42,9 +43,6 @@ class SRSSItem extends DomDocument
|
||||
public function __construct($node = null)
|
||||
{
|
||||
parent::__construct();
|
||||
// if ($node instanceof DOMElement) $this->node = $this->importNode($node, true);
|
||||
// else $this->node = $this->importNode(new DomElement('item'));
|
||||
// $this->_loadAttributes();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -60,9 +58,9 @@ class SRSSItem extends DomDocument
|
||||
if (array_key_exists($child->nodeName, self::$possibilities) && self::$possibilities[$child->nodeName] === 'folder') {
|
||||
self::_loadChildAttributes($item, $child);
|
||||
} elseif ($child->nodeName === 'media:content') {
|
||||
$item->medias[] = new Content($child);
|
||||
$item->medias[] = MediaContentParser::read($child);
|
||||
} else {
|
||||
$item->{$child->nodeName} = $child->nodeValue;
|
||||
$item->{$child->nodeName} = trim($child->nodeValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -188,15 +186,6 @@ class SRSSItem extends DomDocument
|
||||
throw new SRSSException(sprintf('%s is not a possible item (%s)', $name, implode(', ', array_keys($this->possibilities))));
|
||||
}
|
||||
|
||||
/**
|
||||
* display item's XML
|
||||
* see DomDocument's docs
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
return $this->saveXml();
|
||||
}
|
||||
|
||||
/**
|
||||
* transform current item's object into an array
|
||||
* @return array
|
79
src/Parser/MediaContentParser.php
Normal file
79
src/Parser/MediaContentParser.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Shikiryu\SRSS\Parser;
|
||||
|
||||
use DOMDocument;
|
||||
use DOMElement;
|
||||
use DOMException;
|
||||
use DOMNode;
|
||||
use Shikiryu\SRSS\Entity\Item;
|
||||
use Shikiryu\SRSS\Entity\Media\Content;
|
||||
use Shikiryu\SRSS\Exception\SRSSException;
|
||||
use Shikiryu\SRSS\SRSSTools;
|
||||
|
||||
/**
|
||||
* @property string|null $description
|
||||
*/
|
||||
class MediaContentParser extends DomDocument
|
||||
{
|
||||
protected DOMNode $node; // item node
|
||||
protected $attr; // item's properties
|
||||
|
||||
// possible properties' names
|
||||
protected static $possibilities = [
|
||||
'title' => 'nohtml',
|
||||
'link' => 'link',
|
||||
'description' => 'html',
|
||||
'author' => 'email',
|
||||
'category' => 'nohtml',
|
||||
'comments' => 'link',
|
||||
'enclosure' => '',
|
||||
'guid' => 'nohtml',
|
||||
'pubDate' => 'date',
|
||||
'source' => 'link',
|
||||
'media:group' => 'folder',
|
||||
'media:content' => '',
|
||||
];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param DomNode $node
|
||||
*/
|
||||
public function __construct($node = null)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Content $item
|
||||
* @param $nodes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function _loadChildAttributes(Content $item, $nodes): void
|
||||
{
|
||||
foreach ($nodes->attributes as $attribute) {
|
||||
|
||||
if (property_exists(Content::class, $attribute->name)) {
|
||||
$item->{$attribute->name} = $attribute->value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DOMNode|null $node
|
||||
*
|
||||
* @return Content
|
||||
*/
|
||||
public static function read(?DOMNode $node = null): Content
|
||||
{
|
||||
$content = new Content();
|
||||
if ($node instanceof DOMNode) {
|
||||
self::_loadChildAttributes($content, $node);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Shikiryu\SRSS;
|
||||
namespace Shikiryu\SRSS\Parser;
|
||||
|
||||
use DOMDocument;
|
||||
use DOMNodeList;
|
||||
@ -8,6 +8,8 @@ use DOMXPath;
|
||||
use Shikiryu\SRSS\Entity\Channel;
|
||||
use Shikiryu\SRSS\Entity\Channel\Image;
|
||||
use Shikiryu\SRSS\Entity\Item;
|
||||
use Shikiryu\SRSS\Exception\SRSSException;
|
||||
use Shikiryu\SRSS\SRSS;
|
||||
|
||||
class SRSSParser extends DomDocument
|
||||
{
|
||||
@ -38,7 +40,7 @@ class SRSSParser extends DomDocument
|
||||
* @param string $link
|
||||
*
|
||||
* @return \Shikiryu\SRSS\SRSS
|
||||
* @throws \Shikiryu\SRSS\SRSSException
|
||||
* @throws \Shikiryu\SRSS\Exception\SRSSException
|
||||
*/
|
||||
public function parse(string $link)
|
||||
{
|
||||
@ -59,9 +61,9 @@ class SRSSParser extends DomDocument
|
||||
|
||||
/**
|
||||
* @return Item[]
|
||||
* @throws \Shikiryu\SRSS\SRSSException
|
||||
* @throws \Shikiryu\SRSS\Exception\SRSSException
|
||||
*/
|
||||
private function getItems(): mixed
|
||||
private function getItems(): array
|
||||
{
|
||||
$channel = $this->_getChannel();
|
||||
/** @var DOMNodeList $items */
|
||||
@ -70,7 +72,7 @@ class SRSSParser extends DomDocument
|
||||
$this->doc->items = [];
|
||||
if ($length > 0) {
|
||||
for($i = 0; $i < $length; $i++) {
|
||||
$this->doc->items[$i] = SRSSItem::read($items->item($i));
|
||||
$this->doc->items[$i] = ItemParser::read($items->item($i));
|
||||
}
|
||||
}
|
||||
|
46
src/SRSS.php
46
src/SRSS.php
@ -5,38 +5,17 @@ namespace Shikiryu\SRSS;
|
||||
use Iterator;
|
||||
use Shikiryu\SRSS\Entity\Channel;
|
||||
use Shikiryu\SRSS\Entity\Item;
|
||||
use Shikiryu\SRSS\Exception\SRSSException;
|
||||
use Shikiryu\SRSS\Parser\SRSSParser;
|
||||
|
||||
class SRSS implements Iterator
|
||||
{
|
||||
public Channel $channel;
|
||||
/** @var Item[] */
|
||||
public array $items; // array of SRSSItems
|
||||
|
||||
private int $position; // Iterator position
|
||||
|
||||
// lists of possible attributes for RSS
|
||||
protected $possibleAttr = [
|
||||
'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',
|
||||
//'textInput' => '',
|
||||
'skipHours' => 'hour',
|
||||
'skipDays' => 'day',
|
||||
];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
@ -78,15 +57,10 @@ class SRSS implements Iterator
|
||||
public function isValid(): bool
|
||||
{
|
||||
$valid = true;
|
||||
$items = $this->getItems();
|
||||
$invalidItems = [];
|
||||
$i = 1;
|
||||
foreach ($items as $item) {
|
||||
foreach ($this->getItems() as $item) {
|
||||
if ($item->isValid() === false) {
|
||||
$invalidItems[] = $i;
|
||||
$valid = false;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
|
||||
return ($valid && $this->channel->isValid());
|
||||
@ -115,11 +89,10 @@ class SRSS implements Iterator
|
||||
if (!property_exists(Channel::class, $name)) {
|
||||
throw new SRSSException($name . ' is not a possible item');
|
||||
}
|
||||
$flag = $this->possibleAttr[$name];
|
||||
$val = SRSSTools::check($val, $flag);
|
||||
if (!empty($val)) {
|
||||
// TODO add validator ?
|
||||
// if ((new Validator())->isPropertyValid($this->channel, $name)) {
|
||||
$this->channel->{$name} = $val;
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -231,4 +204,9 @@ class SRSS implements Iterator
|
||||
|
||||
return $doc;
|
||||
}
|
||||
|
||||
public function addItem(Item $rssItem)
|
||||
{
|
||||
$this->items[] = $rssItem;
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,11 @@
|
||||
|
||||
namespace Shikiryu\SRSS;
|
||||
|
||||
use DateTimeInterface;
|
||||
|
||||
class SRSSTools
|
||||
{
|
||||
public static function check($check, $flag)
|
||||
/*public static function check($check, $flag)
|
||||
{
|
||||
return match ($flag) {
|
||||
'nohtml' => self::noHTML($check),
|
||||
@ -19,11 +21,9 @@ class SRSSTools
|
||||
'media_type' => self::checkMediaType($check),
|
||||
'media_medium' => self::checkMediaMedium($check),
|
||||
'bool' => self::checkBool($check),
|
||||
'medium_expression' => self::checkMediumExpression($check),
|
||||
'' => $check,
|
||||
default => throw new SRSSException('flag ' . $flag . ' does not exist.'),
|
||||
'medium_expression' => self::checkMediumExpression($check)
|
||||
};
|
||||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
* format the RSS to the wanted format
|
||||
@ -61,6 +61,10 @@ class SRSSTools
|
||||
return date("D, d M Y H:i:s T", strtotime($date));
|
||||
}
|
||||
|
||||
if (count(explode(' ', $date)) === 2) {
|
||||
return \DateTime::createFromFormat('Y-m-d H:i:s', $date)->format(DateTimeInterface::RSS);
|
||||
}
|
||||
|
||||
[$j, $m, $a] = explode('/', $date);
|
||||
|
||||
return date("D, d M Y H:i:s T", strtotime($a.'-'.$m.'-'.$j));
|
||||
|
@ -5,9 +5,36 @@ namespace Shikiryu\SRSS\Validator;
|
||||
use DateTimeInterface;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
use ReflectionProperty;
|
||||
use Shikiryu\SRSS\Entity\Media\Content;
|
||||
|
||||
class Validator
|
||||
{
|
||||
protected ?object $object = null;
|
||||
/**
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function isPropertyValid($object, $property)
|
||||
{
|
||||
$properties = array_filter($this->_getClassProperties(get_class($object)), fn($p) => $p->getName() === $property);
|
||||
if (count($properties) !== 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$properties = current($properties);
|
||||
$propertyValue = $object->{$properties->name};
|
||||
$propertyAnnotations = $this->_getPropertyAnnotations($properties);
|
||||
|
||||
if (!in_array('required', $propertyAnnotations, true) && empty($propertyValue)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($propertyAnnotations as $propertyAnnotation) {
|
||||
$annotation = explode(' ', $propertyAnnotation);
|
||||
|
||||
$object->validated[$properties->name] = $this->_validateProperty($annotation, $propertyValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ReflectionException
|
||||
@ -18,7 +45,7 @@ class Validator
|
||||
$object = $this->validateObject($object);
|
||||
}
|
||||
|
||||
return !in_array(false, $object->validated);
|
||||
return !in_array(false, $object->validated, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -26,20 +53,25 @@ class Validator
|
||||
*/
|
||||
public function validateObject($object)
|
||||
{
|
||||
$this->object = $object;
|
||||
$properties = $this->_getClassProperties(get_class($object));
|
||||
$properties = array_map(fn($property) => array_merge(
|
||||
['name' => $property->name],
|
||||
['rules' => $this->_getPropertyAnnotations($property)]
|
||||
), $properties);
|
||||
|
||||
foreach ($properties as $property) {
|
||||
$propertyValue = $object->{$property->name};
|
||||
$propertyAnnotations = $this->_getPropertyAnnotations($property, get_class($object));
|
||||
$propertyValue = $object->{$property['name']};
|
||||
// $propertyAnnotations = $this->_getPropertyAnnotations($property, get_class($object));
|
||||
|
||||
if (!in_array('required', $propertyAnnotations) && empty($propertyValue)) {
|
||||
if (!in_array('required', $property['rules'], true) && empty($propertyValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($propertyAnnotations as $propertyAnnotation) {
|
||||
foreach ($property['rules'] as $propertyAnnotation) {
|
||||
$annotation = explode(' ', $propertyAnnotation);
|
||||
|
||||
$object->validated[$property->name] = $this->_validateProperty($annotation, $propertyValue);
|
||||
$object->validated[$property['name']] = $this->_validateProperty($annotation, $propertyValue);
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,24 +80,29 @@ class Validator
|
||||
|
||||
private function _validateProperty(array $annotation, $property): bool
|
||||
{
|
||||
if (count($annotation) === 1) {
|
||||
return call_user_func([$this, sprintf('_validate%s', ucfirst($annotation[0]))], $property);
|
||||
if ($annotation[0] === 'var') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return true; // TODO check
|
||||
if (count($annotation) === 1) {
|
||||
return $this->{sprintf('_validate%s', ucfirst($annotation[0]))}($property);
|
||||
}
|
||||
|
||||
$args_annotation = array_splice($annotation, 1);
|
||||
|
||||
return $this->{sprintf('_validate%s', ucfirst($annotation[0]))}($property, ...$args_annotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ReflectionProperty[]
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
private function _getClassProperties($class): array
|
||||
{
|
||||
$ReflectionClass = new ReflectionClass($class);
|
||||
|
||||
return $ReflectionClass->getProperties();
|
||||
return (new ReflectionClass($class))->getProperties();
|
||||
}
|
||||
|
||||
private function _getPropertyAnnotations($property, $className): array
|
||||
private function _getPropertyAnnotations(ReflectionProperty $property): array
|
||||
{
|
||||
preg_match_all('#@(.*?)\n#s', $property->getDocComment(), $annotations);
|
||||
|
||||
@ -87,6 +124,21 @@ class Validator
|
||||
return !empty(trim($value));
|
||||
}
|
||||
|
||||
private function _validateRequiredOr($value, $other_values): bool
|
||||
{
|
||||
if (!empty($value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($other_values as $other_value) {
|
||||
if (!empty($this->object->$other_value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
* @return bool
|
||||
@ -143,4 +195,36 @@ class Validator
|
||||
['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']
|
||||
);
|
||||
}
|
||||
|
||||
private function _validateContentMedia($value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $content) {
|
||||
if (!$content->isValid()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($value instanceof Content) {
|
||||
return $value->isValid();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function _validateMediaType($value): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
private function _validateMediaMedium($value): bool
|
||||
{
|
||||
return in_array($value, ['image', 'audio', 'video', 'document', 'executable']);
|
||||
}
|
||||
|
||||
private function _validateMediaExpression($value): bool
|
||||
{
|
||||
return in_array($value, ['sample', 'full', 'nonstop']);
|
||||
}
|
||||
}
|
40
tests/BasicBuilderTest.php
Normal file
40
tests/BasicBuilderTest.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Shikiryu\SRSS\Builder\SRSSBuilder;
|
||||
use Shikiryu\SRSS\Entity\Item;
|
||||
use Shikiryu\SRSS\SRSS;
|
||||
use Shikiryu\SRSS\SRSSTools;
|
||||
|
||||
class BasicBuilderTest extends TestCase
|
||||
{
|
||||
public function testCreateBasicRSS()
|
||||
{
|
||||
$srss = SRSS::create();
|
||||
$srss->title = 'My Blog';
|
||||
$srss->description = 'is the best';
|
||||
$srss->link = 'http://shikiryu.com/devblog/';
|
||||
$items = [
|
||||
['title' => 'title 1', 'link' => 'http://shikiryu.com/devblog/article-1', 'pubDate' => SRSSTools::getRSSDate('2012-03-05 12:02:01'), 'description' => 'description 1'],
|
||||
['title' => 'title 2', 'link' => 'http://shikiryu.com/devblog/article-2', 'pubDate' => SRSSTools::getRSSDate('2022-03-05 22:02:02'), 'description' => 'description 2'],
|
||||
['title' => 'title 3', 'link' => 'http://shikiryu.com/devblog/article-3', 'pubDate' => SRSSTools::getRSSDate('2032-03-05 32:02:03'), 'description' => 'description 3'],
|
||||
['title' => 'title 4', 'link' => 'http://shikiryu.com/devblog/article-4', 'pubDate' => SRSSTools::getRSSDate('2042-03-05 42:02:04'), 'description' => 'description 4'],
|
||||
];
|
||||
foreach ($items as $item) {
|
||||
$rssItem = new Item();
|
||||
$rssItem->title = $item['title'];
|
||||
$rssItem->link = $item['link'];
|
||||
$rssItem->pubDate = $item['pubDate'];
|
||||
$rssItem->description = $item['description'];
|
||||
$srss->addItem($rssItem);
|
||||
}
|
||||
|
||||
self::assertTrue($srss->isValid());
|
||||
|
||||
$filepath = __DIR__.'/resources/tmp/build/testCreateBasicRSS.rss';
|
||||
$builder = new SRSSBuilder();
|
||||
$builder->build($srss, $filepath);
|
||||
|
||||
self::assertFileExists($filepath);
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Shikiryu\SRSS\Exception\SRSSException;
|
||||
use Shikiryu\SRSS\SRSS;
|
||||
use Shikiryu\SRSS\SRSSException;
|
||||
|
||||
class BasicReader extends TestCase
|
||||
class BasicReaderTest extends TestCase
|
||||
{
|
||||
public function testReadBasicRSS()
|
||||
{
|
||||
@ -14,7 +14,7 @@ class BasicReader extends TestCase
|
||||
self::assertNotNull($first_item);
|
||||
self::assertEquals('RSS Tutorial', $first_item->title);
|
||||
|
||||
self::assertTrue($rss->channel->isValid());
|
||||
self::assertTrue($rss->isValid());
|
||||
}
|
||||
|
||||
public function testRssNotFound()
|
||||
@ -41,6 +41,6 @@ class BasicReader extends TestCase
|
||||
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);
|
||||
|
||||
self::assertTrue($rss->channel->isValid());
|
||||
self::assertTrue($rss->isValid());
|
||||
}
|
||||
}
|
@ -7,25 +7,25 @@ class MediaTest extends TestCase
|
||||
{
|
||||
public function testImages()
|
||||
{
|
||||
$rss = SRSS::read('resources/media/cnn.xml');
|
||||
$rss = SRSS::read(__DIR__.'/resources/media/cnn.xml');
|
||||
self::assertEquals('CNN.com - RSS Channel - Entertainment', $rss->title);
|
||||
|
||||
$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->medias[0]->url);
|
||||
self::assertTrue($rss->channel->isValid(), var_export($rss->channel->validated, true));
|
||||
self::assertTrue($rss->isValid(), var_export($rss->channel->validated, true));
|
||||
}
|
||||
|
||||
public function testMusicVideo()
|
||||
{
|
||||
$rss = SRSS::read('resources/media/music-video.xml');
|
||||
$rss = SRSS::read(__DIR__.'/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);
|
||||
self::assertTrue($rss->channel->isValid());
|
||||
self::assertTrue($rss->isValid());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user