1
0
mirror of https://github.com/Chouchen/ShikiryuRSS.git synced 2024-11-22 07:48:51 +01:00

🚧 Continues tests, parser and starts builder

For #4
This commit is contained in:
Clement Desmidt 2023-04-07 17:57:35 +02:00
parent fec8c122e3
commit 63d0b03b50
18 changed files with 494 additions and 234 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
/vendor/ /vendor/
/.idea /.idea
/tests/error.log /tests/error.log
/.phpunit.result.cache

30
phpunit.xml Normal file
View 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>

View File

@ -1,16 +1,27 @@
<?php <?php
namespace Shikiryu\SRSS; namespace Shikiryu\SRSS\Builder;
use DOMDocument; 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 class SRSSBuilder extends DomDocument
{ {
public function build() public function build(SRSS $srss, string $filepath)
{ {
$root = $this->createElement('rss'); $root = $this->createElement('rss');
$root->setAttribute('version', '2.0'); $root->setAttribute('version', '2.0');
$channel = $this->createElement('channel'); $channel = $this->createElement('channel');
$this->appendChannelToDom($srss->channel, $channel);
$this->appendItemsToDom($srss->items, $channel);
$root->appendChild($channel); $root->appendChild($channel);
$this->appendChild($root); $this->appendChild($root);
$this->encoding = 'UTF-8'; $this->encoding = 'UTF-8';
@ -18,13 +29,16 @@ class SRSSBuilder extends DomDocument
$this->formatOutput = true; $this->formatOutput = true;
$this->preserveWhiteSpace = false; $this->preserveWhiteSpace = false;
// $docs = 'http://www.scriptol.fr/rss/RSS-2.0.html'; // $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 * 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); $node = $this->importNode($item->getItem(), true);
$items = $this->getElementsByTagName('item'); $items = $this->getElementsByTagName('item');
@ -41,9 +55,10 @@ class SRSSBuilder extends DomDocument
/** /**
* add a SRSS Item as an item into current RSS * 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); $node = $this->importNode($item->getItem(), true);
$channel = $this->_getChannel(); $channel = $this->_getChannel();
@ -54,7 +69,7 @@ class SRSSBuilder extends DomDocument
* display XML * display XML
* see DomDocument's docs * see DomDocument's docs
*/ */
public function show(): bool|string public function show()
{ {
// TODO build // TODO build
return $this->saveXml(); return $this->saveXml();
@ -159,4 +174,29 @@ class SRSSBuilder extends DomDocument
} }
$this->attr['cloud'] = $array; $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);
}
} }

View File

@ -16,18 +16,18 @@ class Channel extends HasValidator implements SRSSElement
* @required * @required
* @nohtml * @nohtml
*/ */
public string $title; public string $title = '';
/** /**
* @required * @required
* @url * @url
*/ */
public string $link; public string $link = '';
/** /**
* @required * @required
*/ */
public string $description; public string $description = '';
/** /**
* @lang * @lang
@ -96,9 +96,7 @@ class Channel extends HasValidator implements SRSSElement
*/ */
public function isValid(): bool public function isValid(): bool
{ {
$annotation_validation = new Validator(); return (new Validator())->isObjectValid($this);
return $annotation_validation->isObjectValid($this);
} }
/** /**
@ -106,6 +104,8 @@ class Channel extends HasValidator implements SRSSElement
*/ */
public function toArray(): array public function toArray(): array
{ {
return get_object_vars($this); $vars = get_object_vars($this);
unset($vars['validated']);
return $vars;
} }
} }

View File

@ -2,14 +2,47 @@
namespace Shikiryu\SRSS\Entity\Channel; 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; public string $url;
/**
* @required
* @nohtml
*/
public string $title; public string $title;
/**
* @required
* @url
*/
public string $link; 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 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);
}
} }

View File

@ -2,33 +2,68 @@
namespace Shikiryu\SRSS\Entity; 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; * @requiredOr description
public ?string $description; * @nohtml
public ?string $author; */
public ?string $category; public ?string $title = null;
public ?string $comments; /**
public ?string $enclosure; * @url
public ?string $guid; */
public ?string $pubDate; public ?string $link = null;
public ?string $source; /**
* @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[] * @var \Shikiryu\SRSS\Entity\Media\Content[]
* @contentMedia
*/ */
public array $medias = []; public array $medias = [];
public array $required = ['description'];
public function isValid(): bool 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 public function toArray(): array
{ {
return get_object_vars($this); $vars = get_object_vars($this);
unset($vars['validated']);
return $vars;
} }
} }

View File

@ -2,123 +2,76 @@
namespace Shikiryu\SRSS\Entity\Media; namespace Shikiryu\SRSS\Entity\Media;
use DOMDocument; use Shikiryu\SRSS\Entity\SRSSElement;
use DOMElement; use Shikiryu\SRSS\Validator\HasValidator;
use DOMNode; use Shikiryu\SRSS\Validator\Validator;
use Shikiryu\SRSS\SRSSException;
use Shikiryu\SRSS\SRSSTools;
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 * @url
*
* @param DomNode $node
*/ */
public function __construct(?\DOMNode $node = null) public ?string $url = null;
{
parent::__construct();
if ($node instanceof DOMElement) {
$this->node = $this->importNode($node, true);
} else {
$this->node = $this->importNode(new DomElement('item'));
}
$this->_loadAttributes();
}
/** /**
* @return void * @int
*/ */
private function _loadAttributes(): void public ?int $filesize = null;
{
foreach ($this->node->attributes as $attributes) {
if (array_key_exists($attributes->name, $this->possibilities)) {
$this->{$attributes->name} = $attributes->value;
}
}
}
/** /**
* main getter for properties * @mediaType
*
* @param $name
*
* @return null|string
* @throws SRSSException
*/ */
public function __get($name) public ?string $type = null;
{
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 * @mediaMedium
*
* @return bool
*/ */
public function __isset($name) public ?string $medium = null;
{
return isset($this->attr[$name]);
}
/** /**
* main setter for properties * @bool
*
* @param $name
* @param $val
*
* @throws SRSSException
*/ */
public function __set($name, $val) 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
{ {
if (!array_key_exists($name, $this->possibilities)) { return (new Validator())->isObjectValid($this);
throw new SRSSException(sprintf('%s is not a possible item (%s)', $name, implode(', ', array_keys($this->possibilities))));
} }
$flag = $this->possibilities[$name]; public function toArray(): array
{
if ($flag !== '') { return get_object_vars($this);
$val = SRSSTools::check($val, $flag);
}
if (!empty($val)) {
if ($this->$name === null) {
$this->node->appendChild(new DomElement($name, $val));
}
$this->attr[$name] = $val;
}
} }
} }

View File

@ -1,8 +0,0 @@
<?php
namespace Shikiryu\SRSS\Entity\Media;
class Group
{
}

View File

@ -1,6 +1,6 @@
<?php <?php
namespace Shikiryu\SRSS; namespace Shikiryu\SRSS\Exception;
use Exception; use Exception;

View File

@ -1,18 +1,19 @@
<?php <?php
namespace Shikiryu\SRSS; namespace Shikiryu\SRSS\Parser;
use DOMDocument; use DOMDocument;
use DOMElement; use DOMElement;
use DOMException; use DOMException;
use DOMNode; use DOMNode;
use Shikiryu\SRSS\Entity\Item; use Shikiryu\SRSS\Entity\Item;
use Shikiryu\SRSS\Entity\Media\Content; use Shikiryu\SRSS\Exception\SRSSException;
use Shikiryu\SRSS\SRSSTools;
/** /**
* @property string|null $description * @property string|null $description
*/ */
class SRSSItem extends DomDocument class ItemParser extends DomDocument
{ {
protected DOMNode $node; // item node protected DOMNode $node; // item node
@ -42,9 +43,6 @@ class SRSSItem extends DomDocument
public function __construct($node = null) public function __construct($node = null)
{ {
parent::__construct(); 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') { if (array_key_exists($child->nodeName, self::$possibilities) && self::$possibilities[$child->nodeName] === 'folder') {
self::_loadChildAttributes($item, $child); self::_loadChildAttributes($item, $child);
} elseif ($child->nodeName === 'media:content') { } elseif ($child->nodeName === 'media:content') {
$item->medias[] = new Content($child); $item->medias[] = MediaContentParser::read($child);
} else { } 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)))); 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 * transform current item's object into an array
* @return array * @return array

View 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;
}
}

View File

@ -1,6 +1,6 @@
<?php <?php
namespace Shikiryu\SRSS; namespace Shikiryu\SRSS\Parser;
use DOMDocument; use DOMDocument;
use DOMNodeList; use DOMNodeList;
@ -8,6 +8,8 @@ use DOMXPath;
use Shikiryu\SRSS\Entity\Channel; use Shikiryu\SRSS\Entity\Channel;
use Shikiryu\SRSS\Entity\Channel\Image; use Shikiryu\SRSS\Entity\Channel\Image;
use Shikiryu\SRSS\Entity\Item; use Shikiryu\SRSS\Entity\Item;
use Shikiryu\SRSS\Exception\SRSSException;
use Shikiryu\SRSS\SRSS;
class SRSSParser extends DomDocument class SRSSParser extends DomDocument
{ {
@ -38,7 +40,7 @@ class SRSSParser extends DomDocument
* @param string $link * @param string $link
* *
* @return \Shikiryu\SRSS\SRSS * @return \Shikiryu\SRSS\SRSS
* @throws \Shikiryu\SRSS\SRSSException * @throws \Shikiryu\SRSS\Exception\SRSSException
*/ */
public function parse(string $link) public function parse(string $link)
{ {
@ -59,9 +61,9 @@ class SRSSParser extends DomDocument
/** /**
* @return Item[] * @return Item[]
* @throws \Shikiryu\SRSS\SRSSException * @throws \Shikiryu\SRSS\Exception\SRSSException
*/ */
private function getItems(): mixed private function getItems(): array
{ {
$channel = $this->_getChannel(); $channel = $this->_getChannel();
/** @var DOMNodeList $items */ /** @var DOMNodeList $items */
@ -70,7 +72,7 @@ class SRSSParser extends DomDocument
$this->doc->items = []; $this->doc->items = [];
if ($length > 0) { if ($length > 0) {
for($i = 0; $i < $length; $i++) { for($i = 0; $i < $length; $i++) {
$this->doc->items[$i] = SRSSItem::read($items->item($i)); $this->doc->items[$i] = ItemParser::read($items->item($i));
} }
} }

View File

@ -5,38 +5,17 @@ namespace Shikiryu\SRSS;
use Iterator; use Iterator;
use Shikiryu\SRSS\Entity\Channel; use Shikiryu\SRSS\Entity\Channel;
use Shikiryu\SRSS\Entity\Item; use Shikiryu\SRSS\Entity\Item;
use Shikiryu\SRSS\Exception\SRSSException;
use Shikiryu\SRSS\Parser\SRSSParser;
class SRSS implements Iterator class SRSS implements Iterator
{ {
public Channel $channel; public Channel $channel;
/** @var Item[] */
public array $items; // array of SRSSItems public array $items; // array of SRSSItems
private int $position; // Iterator position 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 * Constructor
*/ */
@ -78,15 +57,10 @@ class SRSS implements Iterator
public function isValid(): bool public function isValid(): bool
{ {
$valid = true; $valid = true;
$items = $this->getItems(); foreach ($this->getItems() as $item) {
$invalidItems = [];
$i = 1;
foreach ($items as $item) {
if ($item->isValid() === false) { if ($item->isValid() === false) {
$invalidItems[] = $i;
$valid = false; $valid = false;
} }
$i++;
} }
return ($valid && $this->channel->isValid()); return ($valid && $this->channel->isValid());
@ -115,11 +89,10 @@ class SRSS implements Iterator
if (!property_exists(Channel::class, $name)) { if (!property_exists(Channel::class, $name)) {
throw new SRSSException($name . ' is not a possible item'); throw new SRSSException($name . ' is not a possible item');
} }
$flag = $this->possibleAttr[$name]; // TODO add validator ?
$val = SRSSTools::check($val, $flag); // if ((new Validator())->isPropertyValid($this->channel, $name)) {
if (!empty($val)) {
$this->channel->{$name} = $val; $this->channel->{$name} = $val;
} // }
} }
/** /**
@ -231,4 +204,9 @@ class SRSS implements Iterator
return $doc; return $doc;
} }
public function addItem(Item $rssItem)
{
$this->items[] = $rssItem;
}
} }

View File

@ -2,9 +2,11 @@
namespace Shikiryu\SRSS; namespace Shikiryu\SRSS;
use DateTimeInterface;
class SRSSTools class SRSSTools
{ {
public static function check($check, $flag) /*public static function check($check, $flag)
{ {
return match ($flag) { return match ($flag) {
'nohtml' => self::noHTML($check), 'nohtml' => self::noHTML($check),
@ -19,11 +21,9 @@ class SRSSTools
'media_type' => self::checkMediaType($check), 'media_type' => self::checkMediaType($check),
'media_medium' => self::checkMediaMedium($check), 'media_medium' => self::checkMediaMedium($check),
'bool' => self::checkBool($check), 'bool' => self::checkBool($check),
'medium_expression' => self::checkMediumExpression($check), 'medium_expression' => self::checkMediumExpression($check)
'' => $check,
default => throw new SRSSException('flag ' . $flag . ' does not exist.'),
}; };
} }*/
/** /**
* format the RSS to the wanted format * 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)); 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); [$j, $m, $a] = explode('/', $date);
return date("D, d M Y H:i:s T", strtotime($a.'-'.$m.'-'.$j)); return date("D, d M Y H:i:s T", strtotime($a.'-'.$m.'-'.$j));

View File

@ -5,9 +5,36 @@ namespace Shikiryu\SRSS\Validator;
use DateTimeInterface; use DateTimeInterface;
use ReflectionClass; use ReflectionClass;
use ReflectionException; use ReflectionException;
use ReflectionProperty;
use Shikiryu\SRSS\Entity\Media\Content;
class Validator 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 * @throws ReflectionException
@ -18,7 +45,7 @@ class Validator
$object = $this->validateObject($object); $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) public function validateObject($object)
{ {
$this->object = $object;
$properties = $this->_getClassProperties(get_class($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) { foreach ($properties as $property) {
$propertyValue = $object->{$property->name}; $propertyValue = $object->{$property['name']};
$propertyAnnotations = $this->_getPropertyAnnotations($property, get_class($object)); // $propertyAnnotations = $this->_getPropertyAnnotations($property, get_class($object));
if (!in_array('required', $propertyAnnotations) && empty($propertyValue)) { if (!in_array('required', $property['rules'], true) && empty($propertyValue)) {
continue; continue;
} }
foreach ($propertyAnnotations as $propertyAnnotation) { foreach ($property['rules'] as $propertyAnnotation) {
$annotation = explode(' ', $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 private function _validateProperty(array $annotation, $property): bool
{ {
if (count($annotation) === 1) { if ($annotation[0] === 'var') {
return call_user_func([$this, sprintf('_validate%s', ucfirst($annotation[0]))], $property); 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 * @throws ReflectionException
*/ */
private function _getClassProperties($class): array private function _getClassProperties($class): array
{ {
$ReflectionClass = new ReflectionClass($class); return (new ReflectionClass($class))->getProperties();
return $ReflectionClass->getProperties();
} }
private function _getPropertyAnnotations($property, $className): array private function _getPropertyAnnotations(ReflectionProperty $property): array
{ {
preg_match_all('#@(.*?)\n#s', $property->getDocComment(), $annotations); preg_match_all('#@(.*?)\n#s', $property->getDocComment(), $annotations);
@ -87,6 +124,21 @@ class Validator
return !empty(trim($value)); 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 * @param $value
* @return bool * @return bool
@ -143,4 +195,36 @@ class Validator
['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'] ['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']);
}
} }

View 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);
}
}

View File

@ -1,10 +1,10 @@
<?php <?php
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Shikiryu\SRSS\Exception\SRSSException;
use Shikiryu\SRSS\SRSS; use Shikiryu\SRSS\SRSS;
use Shikiryu\SRSS\SRSSException;
class BasicReader extends TestCase class BasicReaderTest extends TestCase
{ {
public function testReadBasicRSS() public function testReadBasicRSS()
{ {
@ -14,7 +14,7 @@ class BasicReader extends TestCase
self::assertNotNull($first_item); self::assertNotNull($first_item);
self::assertEquals('RSS Tutorial', $first_item->title); self::assertEquals('RSS Tutorial', $first_item->title);
self::assertTrue($rss->channel->isValid()); self::assertTrue($rss->isValid());
} }
public function testRssNotFound() 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('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::assertEquals('Fri, 30 May 2003 11:06:42 GMT', $rss->getItem(2)->pubDate);
self::assertTrue($rss->channel->isValid()); self::assertTrue($rss->isValid());
} }
} }

View File

@ -7,25 +7,25 @@ class MediaTest extends TestCase
{ {
public function testImages() 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); self::assertEquals('CNN.com - RSS Channel - Entertainment', $rss->title);
$first_item = $rss->getFirst(); $first_item = $rss->getFirst();
self::assertEquals('Kirstie Alley, \'Cheers\' and \'Veronica\'s Closet\' star, dead at 71', $first_item->title); 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::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() 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::assertEquals('Music Videos 101', $rss->title);
self::assertCount(1, $rss->items); self::assertCount(1, $rss->items);
$first_item = $rss->getFirst(); $first_item = $rss->getFirst();
self::assertEquals('http://www.foo.com/movie.mov', $first_item->medias[0]->url); self::assertEquals('http://www.foo.com/movie.mov', $first_item->medias[0]->url);
self::assertTrue($rss->channel->isValid()); self::assertTrue($rss->isValid());
} }
} }