mirror of
https://github.com/Chouchen/ShikiryuRSS.git
synced 2024-11-13 06:08:50 +01:00
parent
09d16fb74b
commit
58e0866b46
@ -15,81 +15,93 @@ use Shikiryu\SRSS\Validator\Validator;
|
||||
class Channel extends HasValidator implements SRSSElement
|
||||
{
|
||||
/**
|
||||
* @required
|
||||
* @nohtml
|
||||
* @validate required
|
||||
* @format html
|
||||
*/
|
||||
public string $title = '';
|
||||
protected string $title = '';
|
||||
|
||||
/**
|
||||
* @required
|
||||
* @url
|
||||
* @validate required
|
||||
* @validate url
|
||||
* @format url
|
||||
*/
|
||||
public string $link = '';
|
||||
protected string $link = '';
|
||||
|
||||
/**
|
||||
* @required
|
||||
* @validate required
|
||||
* @format html
|
||||
*/
|
||||
public string $description = '';
|
||||
protected string $description = '';
|
||||
|
||||
/**
|
||||
* @lang
|
||||
* @validate lang
|
||||
*/
|
||||
public ?string $language = null;
|
||||
protected ?string $language = null;
|
||||
/**
|
||||
* @nohtml
|
||||
* @validate nohtml
|
||||
* @format nohtml
|
||||
*/
|
||||
public ?string $copyright = null;
|
||||
protected ?string $copyright = null;
|
||||
/**
|
||||
* @nohtml
|
||||
* @validate nohtml
|
||||
* @format nohtml
|
||||
*/
|
||||
public ?string $managingEditor = null;
|
||||
protected ?string $managingEditor = null;
|
||||
/**
|
||||
* @nohtml
|
||||
* @validate nohtml
|
||||
* @format nohtml
|
||||
*/
|
||||
public ?string $webMaster = null;
|
||||
protected ?string $webMaster = null;
|
||||
/**
|
||||
* @date
|
||||
* @validate date
|
||||
* @format date
|
||||
*/
|
||||
public ?string $pubDate = null;
|
||||
protected ?string $pubDate = null;
|
||||
/**
|
||||
* @date
|
||||
* @validate date
|
||||
* @format date
|
||||
*/
|
||||
public ?string $lastBuildDate = null;
|
||||
protected ?string $lastBuildDate = null;
|
||||
/**
|
||||
* @var Category[]
|
||||
*/
|
||||
public ?array $category = null;
|
||||
protected ?array $category = null;
|
||||
/**
|
||||
* @nohtml
|
||||
* @validate nohtml
|
||||
* @format nohtml
|
||||
*/
|
||||
public ?string $generator = null;
|
||||
protected ?string $generator = null;
|
||||
/**
|
||||
* @url
|
||||
* @validate url
|
||||
* @format url
|
||||
*/
|
||||
public ?string $docs = null;
|
||||
protected ?string $docs = null;
|
||||
/**
|
||||
* @var Cloud|null
|
||||
*/
|
||||
public ?Cloud $cloud = null;
|
||||
protected ?Cloud $cloud = null;
|
||||
/**
|
||||
* @int
|
||||
* @validate int
|
||||
* @format int
|
||||
*/
|
||||
public ?string $ttl = null;
|
||||
public ?Image $image = null;
|
||||
public ?string $rating = null;
|
||||
protected ?string $ttl = null;
|
||||
protected ?Image $image = null;
|
||||
protected ?string $rating = null;
|
||||
/**
|
||||
* @var string|null
|
||||
* The purpose of the <textInput> element is something of a mystery. You can use it to specify a search engine box. Or to allow a reader to provide feedback. Most aggregators ignore it.
|
||||
*/
|
||||
public ?string $textInput = null;
|
||||
protected ?string $textInput = null;
|
||||
/**
|
||||
* @hour
|
||||
* @validate hour
|
||||
* @format hour
|
||||
*/
|
||||
public ?string $skipHours = null;
|
||||
protected ?string $skipHours = null;
|
||||
/**
|
||||
* @day
|
||||
* @validate day
|
||||
* @format day
|
||||
*/
|
||||
public ?string $skipDays = null;
|
||||
protected ?string $skipDays = null;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
|
@ -10,13 +10,14 @@ use Shikiryu\SRSS\Validator\Validator;
|
||||
class Category extends HasValidator implements SRSSElement
|
||||
{
|
||||
/**
|
||||
* @string
|
||||
* @validate url
|
||||
* @format url
|
||||
*/
|
||||
public ?string $domain = null;
|
||||
protected ?string $domain = null;
|
||||
/**
|
||||
* @string
|
||||
* @validate string
|
||||
*/
|
||||
public ?string $value = null;
|
||||
protected ?string $value = null;
|
||||
|
||||
public function isValid(): bool
|
||||
{
|
||||
|
@ -10,25 +10,26 @@ use Shikiryu\SRSS\Validator\Validator;
|
||||
class Cloud extends HasValidator implements SRSSElement
|
||||
{
|
||||
/**
|
||||
* @string
|
||||
* @validate string
|
||||
*/
|
||||
public ?string $domain = null;
|
||||
protected ?string $domain = null;
|
||||
/**
|
||||
* @int
|
||||
* @validate int
|
||||
* @format int
|
||||
*/
|
||||
public ?int $port = null;
|
||||
protected ?int $port = null;
|
||||
/**
|
||||
* @string
|
||||
* @validate string
|
||||
*/
|
||||
public ?string $path = null;
|
||||
protected ?string $path = null;
|
||||
/**
|
||||
* @string
|
||||
* @validate string
|
||||
*/
|
||||
public ?string $registerProcedure = null;
|
||||
protected ?string $registerProcedure = null;
|
||||
/**
|
||||
* @string
|
||||
* @validate string
|
||||
*/
|
||||
public ?string $protocol = null;
|
||||
protected ?string $protocol = null;
|
||||
|
||||
public function isValid(): bool
|
||||
{
|
||||
|
@ -10,32 +10,41 @@ use Shikiryu\SRSS\Validator\Validator;
|
||||
class Image extends HasValidator implements SRSSElement
|
||||
{
|
||||
/**
|
||||
* @required
|
||||
* @url
|
||||
* @validate required
|
||||
* @validate url
|
||||
* @format url
|
||||
*/
|
||||
public ?string $url = null;
|
||||
protected ?string $url = null;
|
||||
/**
|
||||
* @required
|
||||
* @nohtml
|
||||
* @validate required
|
||||
* @validate nohtml
|
||||
* @format nohtml
|
||||
*/
|
||||
public ?string $title = null;
|
||||
protected ?string $title = null;
|
||||
/**
|
||||
* @required
|
||||
* @url
|
||||
* @validate required
|
||||
* @validate url
|
||||
* @format url
|
||||
*/
|
||||
public ?string $link = null;
|
||||
protected ?string $link = null;
|
||||
/**
|
||||
* @int
|
||||
* @max 144
|
||||
* @validate int
|
||||
* @format int
|
||||
* @validate max 144
|
||||
*/
|
||||
public int $width = 88; // Maximum value for width is 144, default value is 88.
|
||||
protected int $width = 88; // Maximum value for width is 144, default value is 88.
|
||||
/**
|
||||
* @int
|
||||
* @max 400
|
||||
* @format int
|
||||
* @validate int
|
||||
* @validate max 400
|
||||
*/
|
||||
public int $height = 31; //Maximum value for height is 400, default value is 31.
|
||||
protected int $height = 31; //Maximum value for height is 400, default value is 31.
|
||||
|
||||
public string $description;
|
||||
/**
|
||||
* @var string
|
||||
* @format html
|
||||
*/
|
||||
protected string $description;
|
||||
|
||||
public function isValid(): bool
|
||||
{
|
||||
|
@ -16,50 +16,55 @@ use Shikiryu\SRSS\Validator\Validator;
|
||||
class Item extends HasValidator implements SRSSElement
|
||||
{
|
||||
/**
|
||||
* @requiredOr description
|
||||
* @nohtml
|
||||
* @validate requiredOr description
|
||||
* @format html
|
||||
*/
|
||||
public ?string $title = null;
|
||||
protected ?string $title = null;
|
||||
/**
|
||||
* @url
|
||||
* @validate url
|
||||
* @format url
|
||||
*/
|
||||
public ?string $link = null;
|
||||
protected ?string $link = null;
|
||||
/**
|
||||
* @requiredOr title
|
||||
* @validate requiredOr title
|
||||
* @format html
|
||||
*/
|
||||
public ?string $description = null;
|
||||
protected ?string $description = null;
|
||||
/**
|
||||
* @email
|
||||
* @validate email
|
||||
* @format email
|
||||
*/
|
||||
public ?string $author = null;
|
||||
protected ?string $author = null;
|
||||
/**
|
||||
* @var Category[]
|
||||
*/
|
||||
public ?array $category = null;
|
||||
protected ?array $category = null;
|
||||
/**
|
||||
* @url
|
||||
* @validate url
|
||||
* @format url
|
||||
*/
|
||||
public ?string $comments = null;
|
||||
protected ?string $comments = null;
|
||||
/**
|
||||
* @var Enclosure|null
|
||||
*/
|
||||
public ?Enclosure $enclosure = null;
|
||||
public ?string $guid = null;
|
||||
protected ?Enclosure $enclosure = null;
|
||||
protected ?string $guid = null;
|
||||
/**
|
||||
* @date
|
||||
* @validate date
|
||||
* @format date
|
||||
*/
|
||||
public ?string $pubDate = null;
|
||||
protected ?string $pubDate = null;
|
||||
|
||||
/**
|
||||
* @var Source|null
|
||||
*/
|
||||
public ?Source $source = null;
|
||||
protected ?Source $source = null;
|
||||
|
||||
/**
|
||||
* @var Content[]
|
||||
* @contentMedia
|
||||
*/
|
||||
public array $medias = [];
|
||||
protected array $medias = [];
|
||||
|
||||
public function isValid(): bool
|
||||
{
|
||||
|
@ -10,13 +10,14 @@ use Shikiryu\SRSS\Validator\Validator;
|
||||
class Category extends HasValidator implements SRSSElement
|
||||
{
|
||||
/**
|
||||
* @string
|
||||
* @validate string
|
||||
*/
|
||||
public string $domain;
|
||||
protected string $domain;
|
||||
/**
|
||||
* @string
|
||||
* @validate string
|
||||
*/
|
||||
public string $value;
|
||||
protected string $value;
|
||||
|
||||
|
||||
public function isValid(): bool
|
||||
{
|
||||
|
@ -10,19 +10,22 @@ use Shikiryu\SRSS\Validator\Validator;
|
||||
class Enclosure extends HasValidator implements SRSSElement
|
||||
{
|
||||
/**
|
||||
* @url
|
||||
* @validate url
|
||||
* @format url
|
||||
*/
|
||||
public ?string $url = null;
|
||||
protected ?string $url = null;
|
||||
|
||||
/**
|
||||
* @int
|
||||
* @validate int
|
||||
* @format int
|
||||
*/
|
||||
public ?int $length = null;
|
||||
protected ?int $length = null;
|
||||
|
||||
/**
|
||||
* @mediaType
|
||||
* @validate mediaType
|
||||
* @format mediaType
|
||||
*/
|
||||
public ?string $type = null;
|
||||
protected ?string $type = null;
|
||||
|
||||
public function isValid(): bool
|
||||
{
|
||||
|
@ -10,14 +10,16 @@ use Shikiryu\SRSS\Validator\Validator;
|
||||
class Source extends HasValidator implements SRSSElement
|
||||
{
|
||||
/**
|
||||
* @url
|
||||
* @validate url
|
||||
* @format url
|
||||
*/
|
||||
public ?string $url = null;
|
||||
protected ?string $url = null;
|
||||
|
||||
/**
|
||||
* @nohtml
|
||||
* @validate nohtml
|
||||
* @format nohtml
|
||||
*/
|
||||
public ?string $value = null;
|
||||
protected ?string $value = null;
|
||||
|
||||
public function isValid(): bool
|
||||
{
|
||||
|
@ -10,61 +10,73 @@ use Shikiryu\SRSS\Validator\Validator;
|
||||
class Content extends HasValidator implements SRSSElement
|
||||
{
|
||||
/**
|
||||
* @url
|
||||
* @validate url
|
||||
* @format url
|
||||
*/
|
||||
public ?string $url = null;
|
||||
protected ?string $url = null;
|
||||
/**
|
||||
* @int
|
||||
* @validate int
|
||||
* @format int
|
||||
*/
|
||||
public ?int $filesize = null;
|
||||
protected ?int $filesize = null;
|
||||
/**
|
||||
* @mediaType
|
||||
* @validate mediaType
|
||||
*/
|
||||
public ?string $type = null;
|
||||
protected ?string $type = null;
|
||||
/**
|
||||
* @mediaMedium
|
||||
* @validate mediaMedium
|
||||
* @format mediaMedium
|
||||
*/
|
||||
public ?string $medium = null;
|
||||
protected ?string $medium = null;
|
||||
/**
|
||||
* @bool
|
||||
* @validate bool
|
||||
* @format bool
|
||||
*/
|
||||
public ?bool $isDefault = null;
|
||||
protected ?bool $isDefault = null;
|
||||
/**
|
||||
* @mediaExpression
|
||||
* @validate mediaExpression
|
||||
* @format mediaExpression
|
||||
*/
|
||||
public ?string $expression = null;
|
||||
protected ?string $expression = null;
|
||||
/**
|
||||
* @int
|
||||
* @validate int
|
||||
* @format int
|
||||
*/
|
||||
public ?int $bitrate = null;
|
||||
protected ?int $bitrate = null;
|
||||
/**
|
||||
* @int
|
||||
* @validate int
|
||||
* @format int
|
||||
*/
|
||||
public ?int $framerate = null;
|
||||
protected ?int $framerate = null;
|
||||
/**
|
||||
* @float
|
||||
* @validate float
|
||||
* @format float
|
||||
*/
|
||||
public ?float $samplerate = null;
|
||||
protected ?float $samplerate = null;
|
||||
/**
|
||||
* @int
|
||||
* @validate int
|
||||
* @format int
|
||||
*/
|
||||
public ?int $channels = null;
|
||||
protected ?int $channels = null;
|
||||
/**
|
||||
* @int
|
||||
* @validate int
|
||||
* @format int
|
||||
*/
|
||||
public ?int $duration = null;
|
||||
protected ?int $duration = null;
|
||||
/**
|
||||
* @int
|
||||
* @validate int
|
||||
* @format int
|
||||
*/
|
||||
public ?int $height = null;
|
||||
protected ?int $height = null;
|
||||
/**
|
||||
* @int
|
||||
* @validate int
|
||||
* @format int
|
||||
*/
|
||||
public ?int $width = null;
|
||||
protected ?int $width = null;
|
||||
/**
|
||||
* @lang
|
||||
* @validate lang
|
||||
*/
|
||||
public ?string $lang = null;
|
||||
protected ?string $lang = null;
|
||||
|
||||
public function isValid(): bool
|
||||
{
|
||||
|
@ -27,7 +27,7 @@ class ItemParser extends DomDocument
|
||||
if ($child->nodeName === 'media:group') {
|
||||
self::_loadChildAttributes($item, $child);
|
||||
} elseif ($child->nodeName === 'media:content') {
|
||||
$item->medias[] = MediaContentParser::read($child);
|
||||
$item->medias = MediaContentParser::read($child);
|
||||
} elseif ($child->nodeName === 'category') {
|
||||
$category = new Item\Category();
|
||||
foreach($child->attributes as $attribute) {
|
||||
|
@ -109,7 +109,7 @@ class SRSSParser extends DomDocument
|
||||
$category->{$attribute->name} = $attribute->value;
|
||||
}
|
||||
$category->value = $child->nodeValue;
|
||||
$this->doc->channel->category[] = $category;
|
||||
$this->doc->channel->category = $category;
|
||||
} else {
|
||||
$this->doc->channel->{$child->nodeName} = $child->nodeValue;
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ use Shikiryu\SRSS\Exception\PropertyNotFoundException;
|
||||
use Shikiryu\SRSS\Exception\SRSSException;
|
||||
use Shikiryu\SRSS\Exception\UnreadableRSSException;
|
||||
use Shikiryu\SRSS\Parser\SRSSParser;
|
||||
use Shikiryu\SRSS\Validator\Formator;
|
||||
use Shikiryu\SRSS\Validator\Validator;
|
||||
|
||||
/**
|
||||
@ -126,11 +127,14 @@ class SRSS implements Iterator
|
||||
throw new PropertyNotFoundException(Channel::class, $name);
|
||||
}
|
||||
if ((new Validator())->isValidValueForObjectProperty($this->channel, $name, $val)) {
|
||||
|
||||
if (SRSSTools::getPropertyType(Channel::class, $name) === 'array') {
|
||||
$this->channel->{$name}[] = $val;
|
||||
$this->channel->{$name} = $val;
|
||||
} else {
|
||||
$val = is_string($val) ? (new Formator())->formatValue($this->channel, $name, $val) : $val;
|
||||
$this->channel->{$name} = $val;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,210 +2,27 @@
|
||||
|
||||
namespace Shikiryu\SRSS;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeInterface;
|
||||
use ReflectionProperty;
|
||||
use Shikiryu\SRSS\Validator\Formator;
|
||||
|
||||
class SRSSTools
|
||||
{
|
||||
/**
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public static function getPropertyType($object, $property): ?string
|
||||
{
|
||||
$rp = new ReflectionProperty($object, $property);
|
||||
return $rp->getType()?->getName();
|
||||
}
|
||||
public static function check($check, $flag)
|
||||
{
|
||||
return match ($flag) {
|
||||
'nohtml' => self::noHTML($check),
|
||||
'link' => self::checkLink($check),
|
||||
'html' => self::HTML4XML($check),
|
||||
'date' => self::getRSSDate($check),
|
||||
'email' => self::checkEmail($check),
|
||||
'int' => self::checkInt($check),
|
||||
'hour' => self::checkHour($check),
|
||||
'day' => self::checkDay($check),
|
||||
'media_type' => self::checkMediaType($check),
|
||||
'media_medium' => self::checkMediaMedium($check),
|
||||
'bool' => self::checkBool($check),
|
||||
'medium_expression' => self::checkMediumExpression($check)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* format the RSS to the wanted format
|
||||
*
|
||||
* @param $format string wanted format
|
||||
* @param $date string RSS date
|
||||
*
|
||||
* @return string date
|
||||
*/
|
||||
public static function formatDate(string $format, string $date): string
|
||||
{
|
||||
return date($format, strtotime($date));
|
||||
}
|
||||
|
||||
/**
|
||||
* format a date for RSS format
|
||||
*
|
||||
* @param string $date date to format
|
||||
* @param string $format
|
||||
* @param string $string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getRSSDate(string $date, string $format = ''): string
|
||||
public static function getRSSDate(string $string)
|
||||
{
|
||||
$date_position = 'dDjlNSwzWFmMntLoYyaABgGhHisueIOPTZcrU';
|
||||
if($format !== '' && preg_match('~^(['.$date_position.']{1})([-/])(['.$date_position.']{1})([-/])(['.$date_position.']{1})$~', $format)){
|
||||
$datetime = DateTime::createFromFormat($format, $date);
|
||||
if ($datetime === false) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $datetime->format(DATE_RSS);
|
||||
}
|
||||
|
||||
if (strtotime($date) !==false ) {
|
||||
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));
|
||||
return Formator::checkDate($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* check if it's an url
|
||||
*
|
||||
* @param $check string to check
|
||||
*
|
||||
* @return string|boolean the filtered data, or FALSE if the filter fails.
|
||||
*/
|
||||
public static function checkLink(string $check): bool|string
|
||||
{
|
||||
return filter_var($check, FILTER_VALIDATE_URL);
|
||||
}
|
||||
|
||||
/**
|
||||
* make a string XML-compatible
|
||||
*
|
||||
* @param $check string to format
|
||||
*
|
||||
* @return string formatted string
|
||||
*/
|
||||
public static function HTML4XML(string $check): string
|
||||
{
|
||||
return sprintf('<![CDATA[ %s ]]>', htmlspecialchars($check));
|
||||
}
|
||||
|
||||
/**
|
||||
* delete html tags
|
||||
*
|
||||
* @param $check string to format
|
||||
*
|
||||
* @return string formatted string
|
||||
*/
|
||||
public static function noHTML(string $check): string
|
||||
{
|
||||
return strip_tags($check);
|
||||
}
|
||||
|
||||
/**
|
||||
* check if it's a day (in RSS terms)
|
||||
*
|
||||
* @param $check string to check
|
||||
*
|
||||
* @return string the day, or empty string
|
||||
*/
|
||||
public static function checkDay(string $check): string
|
||||
{
|
||||
$possibleDay = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
|
||||
return in_array(strtolower($check), $possibleDay) ? $check : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* check if it's an email
|
||||
*
|
||||
* @param $check string to check
|
||||
*
|
||||
* @return string|boolean the filtered data, or FALSE if the filter fails.
|
||||
*/
|
||||
public static function checkEmail(string $check): bool|string
|
||||
{
|
||||
return filter_var($check, FILTER_VALIDATE_EMAIL);
|
||||
}
|
||||
|
||||
/**
|
||||
* check if it's an hour (in RSS terms)
|
||||
*
|
||||
* @param $check string to check
|
||||
*
|
||||
* @return string|boolean the filtered data, or FALSE if the filter fails.
|
||||
*/
|
||||
public static function checkHour(string $check): bool|string
|
||||
{
|
||||
$options = [
|
||||
'options' => [
|
||||
'default' => 0,
|
||||
'min_range' => 0,
|
||||
'max_range' => 23
|
||||
]
|
||||
];
|
||||
return filter_var($check, FILTER_VALIDATE_INT, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* check if it's an int
|
||||
*
|
||||
* @param $check int to check
|
||||
*
|
||||
* @return int|boolean the filtered data, or FALSE if the filter fails.
|
||||
*/
|
||||
public static function checkInt(int $check): bool|int
|
||||
{
|
||||
return filter_var($check, FILTER_VALIDATE_INT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $check
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function checkMediaType($check): mixed
|
||||
{
|
||||
return $check;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $check
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public static function checkMediaMedium($check): ?string
|
||||
{
|
||||
return in_array($check, ['image', 'audio', 'video', 'document', 'executable']) ? $check : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $check
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public static function checkBool($check): ?string
|
||||
{
|
||||
return in_array($check, ['true', 'false']) ? $check : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $check
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public static function checkMediumExpression($check): ?string
|
||||
{
|
||||
return in_array($check, ['sample', 'full', 'nonstop']) ? $check : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
268
src/Validator/Formator.php
Normal file
268
src/Validator/Formator.php
Normal file
@ -0,0 +1,268 @@
|
||||
<?php
|
||||
|
||||
namespace Shikiryu\SRSS\Validator;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeInterface;
|
||||
use ReflectionException;
|
||||
use ReflectionProperty;
|
||||
|
||||
class Formator
|
||||
{
|
||||
use ReadProperties;
|
||||
|
||||
/**
|
||||
* @param $object
|
||||
* @param $property
|
||||
* @param $value
|
||||
*
|
||||
* @return false|string
|
||||
*/
|
||||
public function formatValue($object, $property, $value): bool|string
|
||||
{
|
||||
try {
|
||||
$property = $this->getReflectedProperty($object, $property);
|
||||
} catch (ReflectionException) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$propertyAnnotations = $this->_getPropertyAnnotations($property);
|
||||
|
||||
foreach ($propertyAnnotations as $propertyAnnotation) {
|
||||
$value = $this->_formatValue($propertyAnnotation, $value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ReflectionProperty $property
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function _getPropertyAnnotations(ReflectionProperty $property): array
|
||||
{
|
||||
preg_match_all('#@format (.*?)\n#s', $property->getDocComment(), $annotations);
|
||||
|
||||
return array_map(static fn($annotation) => trim($annotation), $annotations[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $annotation
|
||||
* @param string $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function _formatValue(string $annotation, string $value)
|
||||
{
|
||||
return match ($annotation) {
|
||||
'nohtml' => self::noHTML($value),
|
||||
'url' => self::checkLink($value),
|
||||
'html' => self::HTML4XML($value),
|
||||
'date' => self::checkDate($value),
|
||||
'email' => self::checkEmail($value),
|
||||
'int' => self::checkInt($value),
|
||||
'float' => self::checkFloat($value),
|
||||
'hour' => self::checkHour($value),
|
||||
'day' => self::checkDay($value),
|
||||
'mediaType' => self::checkMediaType($value),
|
||||
'mediaMedium' => self::checkMediaMedium($value),
|
||||
'bool' => self::checkBool($value),
|
||||
'mediaExpression' => self::checkMediumExpression($value),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* format a date for RSS format
|
||||
*
|
||||
* @param string $date date to format
|
||||
* @param string $format
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function checkDate(string $date, string $format = ''): string
|
||||
{
|
||||
$date_position = 'dDjlNSwzWFmMntLoYyaABgGhHisueIOPTZcrU';
|
||||
if($format !== '' && preg_match('~^(['.$date_position.']{1})([-/])(['.$date_position.']{1})([-/])(['.$date_position.']{1})$~', $format)){
|
||||
$datetime = DateTime::createFromFormat($format, $date);
|
||||
if ($datetime === false) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $datetime->format(DATE_RSS);
|
||||
}
|
||||
|
||||
if (strtotime($date) !==false ) {
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* format the RSS to the wanted format
|
||||
*
|
||||
* @param $format string wanted format
|
||||
* @param $date string RSS date
|
||||
*
|
||||
* @return string date
|
||||
*/
|
||||
public static function formatDate(string $format, string $date): string
|
||||
{
|
||||
return date($format, strtotime($date));
|
||||
}
|
||||
|
||||
/**
|
||||
* check if it's an url
|
||||
*
|
||||
* @param $check string to check
|
||||
*
|
||||
* @return string|boolean the filtered data, or FALSE if the filter fails.
|
||||
*/
|
||||
public static function checkLink(string $check): bool|string
|
||||
{
|
||||
return filter_var($check, FILTER_VALIDATE_URL);
|
||||
}
|
||||
|
||||
/**
|
||||
* make a string XML-compatible
|
||||
*
|
||||
* @param $check string to format
|
||||
*
|
||||
* @return string formatted string
|
||||
*/
|
||||
public static function HTML4XML(string $check): string
|
||||
{
|
||||
if (str_starts_with($check, '<![CDATA[ ') && str_ends_with($check, ' ]]>')) {
|
||||
return $check;
|
||||
}
|
||||
|
||||
return sprintf('<![CDATA[ %s ]]>', htmlspecialchars($check));
|
||||
}
|
||||
|
||||
/**
|
||||
* delete html tags
|
||||
*
|
||||
* @param $check string to format
|
||||
*
|
||||
* @return string formatted string
|
||||
*/
|
||||
public static function noHTML(string $check): string
|
||||
{
|
||||
return strip_tags($check);
|
||||
}
|
||||
|
||||
/**
|
||||
* check if it's a day (in RSS terms)
|
||||
*
|
||||
* @param $check string to check
|
||||
*
|
||||
* @return string the day, or empty string
|
||||
*/
|
||||
public static function checkDay(string $check): string
|
||||
{
|
||||
$possibleDay = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
|
||||
return in_array(strtolower($check), $possibleDay) ? $check : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* check if it's an email
|
||||
*
|
||||
* @param $check string to check
|
||||
*
|
||||
* @return string|boolean the filtered data, or FALSE if the filter fails.
|
||||
*/
|
||||
public static function checkEmail(string $check): bool|string
|
||||
{
|
||||
return filter_var($check, FILTER_VALIDATE_EMAIL);
|
||||
}
|
||||
|
||||
/**
|
||||
* check if it's an hour (in RSS terms)
|
||||
*
|
||||
* @param $check string to check
|
||||
*
|
||||
* @return string|boolean the filtered data, or FALSE if the filter fails.
|
||||
*/
|
||||
public static function checkHour(string $check): bool|string
|
||||
{
|
||||
$options = [
|
||||
'options' => [
|
||||
'default' => 0,
|
||||
'min_range' => 0,
|
||||
'max_range' => 23
|
||||
]
|
||||
];
|
||||
return filter_var($check, FILTER_VALIDATE_INT, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* check if it's an int
|
||||
*
|
||||
* @param $check int to check
|
||||
*
|
||||
* @return int|boolean the filtered data, or FALSE if the filter fails.
|
||||
*/
|
||||
public static function checkInt(int $check): bool|int
|
||||
{
|
||||
return filter_var($check, FILTER_VALIDATE_INT);
|
||||
}
|
||||
|
||||
/**
|
||||
* check if it's a float
|
||||
*
|
||||
* @param float $check to check
|
||||
*
|
||||
* @return float|boolean the filtered data, or FALSE if the filter fails.
|
||||
*/
|
||||
public static function checkFloat(float $check): bool|float
|
||||
{
|
||||
return filter_var($check, FILTER_VALIDATE_FLOAT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $check
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function checkMediaType($check): mixed
|
||||
{
|
||||
return $check;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $check
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public static function checkMediaMedium($check): ?string
|
||||
{
|
||||
return in_array($check, ['image', 'audio', 'video', 'document', 'executable']) ? $check : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $check
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public static function checkBool($check): ?string
|
||||
{
|
||||
return in_array($check, ['true', 'false']) ? $check : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $check
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public static function checkMediumExpression($check): ?string
|
||||
{
|
||||
return in_array($check, ['sample', 'full', 'nonstop']) ? $check : null;
|
||||
}
|
||||
}
|
@ -2,10 +2,63 @@
|
||||
|
||||
namespace Shikiryu\SRSS\Validator;
|
||||
|
||||
use Shikiryu\SRSS\Exception\PropertyNotFoundException;
|
||||
use Shikiryu\SRSS\Exception\SRSSException;
|
||||
use Shikiryu\SRSS\SRSSTools;
|
||||
|
||||
abstract class HasValidator
|
||||
{
|
||||
/**
|
||||
* @var bool[]
|
||||
*/
|
||||
public array $validated = [];
|
||||
|
||||
/**
|
||||
* setter of others attributes
|
||||
*
|
||||
* @param $name
|
||||
* @param $val
|
||||
*
|
||||
* @throws SRSSException
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function __set($name, $val)
|
||||
{
|
||||
if (!property_exists(static::class, $name)) {
|
||||
throw new PropertyNotFoundException(static::class, $name);
|
||||
}
|
||||
if ((new Validator())->isValidValueForObjectProperty($this, $name, $val)) {
|
||||
|
||||
if (SRSSTools::getPropertyType(static::class, $name) === 'array') {
|
||||
/** @var array $this->{$name} */
|
||||
$this->{$name}[] = $val;
|
||||
} else {
|
||||
$val = is_string($val) ? (new Formator())->formatValue($this, $name, $val) : $val;
|
||||
$this->{$name} = $val;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($name)
|
||||
{
|
||||
return isset($this->{$name});
|
||||
}
|
||||
|
||||
/**
|
||||
* getter of others attributes
|
||||
*
|
||||
* @param $name
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
return $this->{$name} ?? null;
|
||||
}
|
||||
}
|
39
src/Validator/ReadProperties.php
Normal file
39
src/Validator/ReadProperties.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Shikiryu\SRSS\Validator;
|
||||
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
use ReflectionProperty;
|
||||
|
||||
trait ReadProperties
|
||||
{
|
||||
/**
|
||||
* @param $object
|
||||
* @param $property
|
||||
* @return ReflectionProperty|null
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
private function getReflectedProperty($object, $property): ?ReflectionProperty
|
||||
{
|
||||
$properties = array_filter(
|
||||
$this->_getClassProperties(get_class($object)),
|
||||
static fn($p) => $p->getName() === $property
|
||||
);
|
||||
|
||||
if (count($properties) !== 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return current($properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ReflectionProperty[]
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
private function _getClassProperties($class): array
|
||||
{
|
||||
return (new ReflectionClass($class))->getProperties();
|
||||
}
|
||||
}
|
@ -11,29 +11,11 @@ use Shikiryu\SRSS\Entity\Media\Content;
|
||||
|
||||
class Validator
|
||||
{
|
||||
use ReadProperties;
|
||||
|
||||
protected ?object $object = null;
|
||||
|
||||
|
||||
/**
|
||||
* @param $object
|
||||
* @param $property
|
||||
* @return ReflectionProperty|null
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
private function getReflectedProperty($object, $property): ?ReflectionProperty
|
||||
{
|
||||
$properties = array_filter(
|
||||
$this->_getClassProperties(get_class($object)),
|
||||
static fn($p) => $p->getName() === $property
|
||||
);
|
||||
|
||||
if (count($properties) !== 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return current($properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $object
|
||||
* @param $property
|
||||
@ -138,18 +120,10 @@ class Validator
|
||||
return $this->{sprintf('_validate%s', ucfirst($annotation[0]))}($property, ...$args_annotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ReflectionProperty[]
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
private function _getClassProperties($class): array
|
||||
{
|
||||
return (new ReflectionClass($class))->getProperties();
|
||||
}
|
||||
|
||||
private function _getPropertyAnnotations(ReflectionProperty $property): array
|
||||
{
|
||||
preg_match_all('#@(.*?)\n#s', $property->getDocComment(), $annotations);
|
||||
preg_match_all('#@validate (.*?)\n#s', $property->getDocComment(), $annotations);
|
||||
|
||||
return array_map(static fn($annotation) => trim($annotation), $annotations[1]);
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ use Shikiryu\SRSS\Builder\SRSSBuilder;
|
||||
use Shikiryu\SRSS\Entity\Item;
|
||||
use Shikiryu\SRSS\SRSS;
|
||||
use Shikiryu\SRSS\SRSSTools;
|
||||
use Shikiryu\SRSS\Validator\Formator;
|
||||
|
||||
class BasicBuilderTest extends TestCase
|
||||
{
|
||||
@ -42,8 +43,8 @@ class BasicBuilderTest extends TestCase
|
||||
|
||||
self::assertTrue($srss->isValid());
|
||||
|
||||
self::assertEquals($title, $srss->title);
|
||||
self::assertEquals($description, $srss->description);
|
||||
self::assertEquals('<![CDATA[ '.$title.' ]]>', $srss->title);
|
||||
self::assertEquals('<![CDATA[ '.$description.' ]]>', $srss->description);
|
||||
self::assertEquals($link, $srss->link);
|
||||
|
||||
$builder = new SRSSBuilder();
|
||||
|
@ -12,10 +12,10 @@ class BasicReaderTest extends TestCase
|
||||
public function testReadBasicRSS(): void
|
||||
{
|
||||
$rss = SRSS::read(__DIR__.'/resources/basic.xml');
|
||||
self::assertEquals('test Home Page', $rss->title);
|
||||
self::assertEquals('<![CDATA[ test Home Page ]]>', $rss->title);
|
||||
$first_item = $rss->getFirst();
|
||||
self::assertNotNull($first_item);
|
||||
self::assertEquals('RSS Tutorial', $first_item->title);
|
||||
self::assertEquals('<![CDATA[ RSS Tutorial ]]>', $first_item->title);
|
||||
|
||||
self::assertTrue($rss->isValid());
|
||||
}
|
||||
@ -23,20 +23,20 @@ class BasicReaderTest extends TestCase
|
||||
public function testSpecificationExampleRSS(): void
|
||||
{
|
||||
$rss = SRSS::read(__DIR__.'/resources/harvard.xml');
|
||||
self::assertEquals('Liftoff News', $rss->title);
|
||||
self::assertEquals('<![CDATA[ Liftoff News ]]>', $rss->title);
|
||||
self::assertEquals('http://liftoff.msfc.nasa.gov/', $rss->link);
|
||||
self::assertEquals('Liftoff to Space Exploration.', $rss->description);
|
||||
self::assertEquals('<![CDATA[ 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('Tue, 10 Jun 2003 04:00:00 UTC', $rss->pubDate);
|
||||
self::assertEquals('Tue, 10 Jun 2003 09:41:01 UTC', $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('<![CDATA[ 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);
|
||||
self::assertEquals('Fri, 30 May 2003 11:06:42 UTC', $rss->getItem(2)->pubDate);
|
||||
|
||||
self::assertTrue($rss->isValid());
|
||||
}
|
||||
|
@ -86,13 +86,13 @@ class CompleteBuilderTest extends TestCase
|
||||
$item->link = $item_link;
|
||||
$item->description = $item_description;
|
||||
$item->author = $item_author;
|
||||
$item->category[] = $item_category;
|
||||
$item->category = $item_category;
|
||||
$item->comments = $item_comments;
|
||||
$item->enclosure = $item_enclosure;
|
||||
$item->guid = $item_guid;
|
||||
$item->pubDate = $item_pubdate;
|
||||
$item->source = $item_source;
|
||||
$item->medias[] = $item_media;
|
||||
$item->medias = $item_media;
|
||||
|
||||
$srss->addItem($item);
|
||||
|
||||
|
@ -8,10 +8,10 @@ class MediaTest extends TestCase
|
||||
public function testImages(): void
|
||||
{
|
||||
$rss = SRSS::read(__DIR__.'/resources/media/cnn.xml');
|
||||
self::assertEquals('CNN.com - RSS Channel - Entertainment', $rss->title);
|
||||
self::assertEquals('<![CDATA[ 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('<![CDATA[ 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->isValid(), var_export($rss->channel->validated, true));
|
||||
@ -20,7 +20,7 @@ class MediaTest extends TestCase
|
||||
public function testMusicVideo(): void
|
||||
{
|
||||
$rss = SRSS::read(__DIR__.'/resources/media/music-video.xml');
|
||||
self::assertEquals('Music Videos 101', $rss->title);
|
||||
self::assertEquals('<![CDATA[ Music Videos 101 ]]>', $rss->title);
|
||||
|
||||
self::assertCount(1, $rss->items);
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Shikiryu\SRSS\SRSS;
|
||||
use Shikiryu\SRSS\SRSSTools;
|
||||
use Shikiryu\SRSS\Validator\Formator;
|
||||
|
||||
class OriginalReaderSRSSTest extends TestCase
|
||||
{
|
||||
@ -19,7 +20,7 @@ class OriginalReaderSRSSTest extends TestCase
|
||||
public function testOriginalReader(): void
|
||||
{
|
||||
$rss = SRSS::read($this->original);
|
||||
self::assertEquals('Liftoff News', $rss->title);
|
||||
self::assertEquals('<![CDATA[ Liftoff News ]]>', $rss->title);
|
||||
|
||||
$article1 = $rss->getItem(1);
|
||||
$articleFirst = $rss->getFirst();
|
||||
@ -27,7 +28,7 @@ class OriginalReaderSRSSTest extends TestCase
|
||||
|
||||
$links = [];
|
||||
foreach($rss as $article) {
|
||||
$links[] = sprintf('<a href="%s">%s %s</a>', $article->link, SRSSTools::formatDate('d/m/y', $article->pubDate), $article->title);
|
||||
$links[] = sprintf('<a href="%s">%s %s</a>', $article->link, SRSSTools::getRSSDate('d/m/y', $article->pubDate), $article->title);
|
||||
}
|
||||
self::assertCount(4, $links, var_export($links, true));
|
||||
|
||||
|
@ -4,6 +4,7 @@ use PHPUnit\Framework\TestCase;
|
||||
use Shikiryu\SRSS\Entity\Item;
|
||||
use Shikiryu\SRSS\SRSS;
|
||||
use Shikiryu\SRSS\SRSSTools;
|
||||
use Shikiryu\SRSS\Validator\Formator;
|
||||
|
||||
class OriginalWriterSRSSTest extends TestCase
|
||||
{
|
||||
@ -38,8 +39,8 @@ class OriginalWriterSRSSTest extends TestCase
|
||||
$rss->addItemBefore($firstItem);
|
||||
|
||||
self::assertCount(5, $rss->items, var_export($rss->items, true));
|
||||
self::assertEquals('title 0', $rss->getFirst()->title, var_export($rss->items, true));
|
||||
self::assertEquals('title 1', $rss->getItem(2)->title, var_export($rss->items, true));
|
||||
self::assertEquals('<![CDATA[ title 0 ]]>', $rss->getFirst()->title, var_export($rss->items, true));
|
||||
self::assertEquals('<![CDATA[ title 1 ]]>', $rss->getItem(2)->title, var_export($rss->items, true));
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user