diff --git a/src/Entity/Item.php b/src/Entity/Item.php index 8e43f6a..a50fe93 100644 --- a/src/Entity/Item.php +++ b/src/Entity/Item.php @@ -12,6 +12,17 @@ use Shikiryu\SRSS\Validator\Validator; /** * https://cyber.harvard.edu/rss/rss.html#hrelementsOfLtitemgt + * @property null|string title + * @property null|string link + * @property null|string description + * @property null|string author + * @property null|array category + * @property null|string comments + * @property null|Enclosure enclosure + * @property null|string guid + * @property null|string pubDate + * @property null|Source source + * @property array medias */ class Item extends HasValidator implements SRSSElement { diff --git a/src/Exception/InvalidPropertyException.php b/src/Exception/InvalidPropertyException.php new file mode 100644 index 0000000..818f855 --- /dev/null +++ b/src/Exception/InvalidPropertyException.php @@ -0,0 +1,12 @@ +isValidValueForObjectProperty($this->channel, $name, $val)) { - if (SRSSTools::getPropertyType(Channel::class, $name) === 'array') { - $this->channel->{$name} = $val; - } else { - $val = is_string($val) ? (new Formator())->formatValue($this->channel, $name, $val) : $val; - $this->channel->{$name} = $val; - } + if (!(new Validator())->isValidValueForObjectProperty($this->channel, $name, $val)) { + throw new InvalidPropertyException(get_class($this), $name, $val); + } + if (SRSSTools::getPropertyType(Channel::class, $name) === 'array') { + $this->channel->{$name} = $val; + } else { + $val = is_string($val) ? (new Formator())->formatValue($this->channel, $name, $val) : $val; + $this->channel->{$name} = $val; } } diff --git a/src/Validator/HasValidator.php b/src/Validator/HasValidator.php index 5017501..8896080 100644 --- a/src/Validator/HasValidator.php +++ b/src/Validator/HasValidator.php @@ -2,6 +2,7 @@ namespace Shikiryu\SRSS\Validator; +use Shikiryu\SRSS\Exception\InvalidPropertyException; use Shikiryu\SRSS\Exception\PropertyNotFoundException; use Shikiryu\SRSS\Exception\SRSSException; use Shikiryu\SRSS\SRSSTools; @@ -27,17 +28,19 @@ abstract class HasValidator 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; - } + if (!(new Validator())->isValidValueForObjectProperty($this, $name, $val)) { + throw new InvalidPropertyException(get_class($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; + } + } /** diff --git a/src/Validator/Validator.php b/src/Validator/Validator.php index 0d6bdbb..6ff38f1 100644 --- a/src/Validator/Validator.php +++ b/src/Validator/Validator.php @@ -24,6 +24,7 @@ class Validator */ public function isValidValueForObjectProperty($object, $property, $value): bool { + $this->object = $object; try { $property = $this->getReflectedProperty($object, $property); } catch (ReflectionException) { @@ -31,17 +32,19 @@ class Validator } $propertyAnnotations = $this->_getPropertyAnnotations($property); - if (empty($value) && count(array_filter($property['rules'], static fn($rule) => str_starts_with($rule, 'required'))) === 0) { + if (empty($value) && count(array_filter($propertyAnnotations, static fn($rule) => str_starts_with($rule, 'required'))) === 0) { return true; } foreach ($propertyAnnotations as $propertyAnnotation) { $annotation = explode(' ', $propertyAnnotation); - $object->validated[$property->name] = $this->_validateProperty($annotation, $value); + if ($this->_validateProperty($annotation, $value) === false) { + return false; + } } - return count(array_filter($object->validated, static fn($v) => ($v !== null && $v === false))) === 0; + return true; } /** @@ -69,6 +72,7 @@ class Validator */ public function isObjectValid($object): bool { + $object->validated = []; // if (!$object->validated) { $object = $this->validateObject($object); // } @@ -250,4 +254,13 @@ class Validator { return filter_var($value, FILTER_VALIDATE_EMAIL); } + + private function _validateMax($value, array $max): bool + { + return $value <= current($max); + } + private function _validateMin($value, array $max): bool + { + return $value >= current($max); + } } \ No newline at end of file diff --git a/tests/FailingTest.php b/tests/FailingTest.php index 17291e1..7dacf65 100644 --- a/tests/FailingTest.php +++ b/tests/FailingTest.php @@ -1,114 +1,230 @@ srss = SRSS::create(); - self::assertFalse($this->srss->isValid(), var_export($this->srss->validated, true)); - $this->srss->title = 'title'; // mandatory - self::assertFalse($this->srss->isValid(), var_export($this->srss->validated, true)); - $this->srss->description = 'desc'; // mandatory - self::assertFalse($this->srss->isValid(), var_export($this->srss->validated, true)); - - $this->srss->link = 'desc'; // mandatory but should be a url - self::assertFalse($this->srss->isValid(), var_export($this->srss->validated, true)); - $this->srss->link = 'https://example.org'; - self::assertTrue($this->srss->isValid(), var_export($this->srss->validated, true)); - - $this->srss->language = 'en-en'; // should be a valid language - self::assertFalse($this->srss->isValid(), var_export($this->srss->validated, true)); - $this->srss->language = 'en-us'; // should be a valid - self::assertTrue($this->srss->isValid(), var_export($this->srss->validated, true)); - - $this->srss->copyright = 'test'; // should not have html element - self::assertFalse($this->srss->isValid(), var_export($this->srss->validated, true)); - $this->srss->copyright = 'shikiryu'; - self::assertTrue($this->srss->isValid(), var_export($this->srss->validated, true)); - - $this->srss->managingEditor = 'test'; // should not have html element - self::assertFalse($this->srss->isValid(), var_export($this->srss->validated, true)); - $this->srss->managingEditor = 'shikiryu'; - self::assertTrue($this->srss->isValid(), var_export($this->srss->validated, true)); - - $this->srss->webMaster = 'test'; // should not have html element - self::assertFalse($this->srss->isValid(), var_export($this->srss->validated, true)); - $this->srss->webMaster = 'shikiryu'; - self::assertTrue($this->srss->isValid(), var_export($this->srss->validated, true)); - - $this->srss->pubDate = 'test'; // should be a valid date - self::assertFalse($this->srss->isValid(), var_export($this->srss->validated, true)); - $this->srss->pubDate = (new DateTime())->format(DATE_RSS); - self::assertTrue($this->srss->isValid(), var_export($this->srss->validated, true)); - - $this->srss->lastBuildDate = 'test'; // should be a valid date - self::assertFalse($this->srss->isValid(), var_export($this->srss->validated, true)); - $this->srss->lastBuildDate = (new DateTime())->format(DATE_RSS); - self::assertTrue($this->srss->isValid(), var_export($this->srss->validated, true)); - - $this->srss->generator = 'test'; // should not have html element - self::assertFalse($this->srss->isValid(), var_export($this->srss->validated, true)); - $this->srss->generator = 'shikiryuRSS'; - self::assertTrue($this->srss->isValid(), var_export($this->srss->validated, true)); - - $this->srss->docs = 'desc'; //should be a url - self::assertFalse($this->srss->isValid(), var_export($this->srss->validated, true)); - $this->srss->docs = 'https://example.org'; - self::assertTrue($this->srss->isValid(), var_export($this->srss->validated, true)); - - $this->srss->ttl = 'desc'; // should be an int - self::assertFalse($this->srss->isValid(), var_export($this->srss->validated, true)); - $this->srss->ttl = '85'; - self::assertTrue($this->srss->isValid(), var_export($this->srss->validated, true)); - - // rating and textInput not tested because there's no validation - - $this->srss->skipHours = 'desc'; // should be an hour - self::assertFalse($this->srss->isValid(), var_export($this->srss->validated, true)); - $this->srss->skipHours = '12'; - self::assertTrue($this->srss->isValid(), var_export($this->srss->validated, true)); - - $this->srss->skipDays = 'desc'; // should be a day - self::assertFalse($this->srss->isValid(), var_export($this->srss->validated, true)); - $this->srss->skipDays = 'monday'; - self::assertTrue($this->srss->isValid(), var_export($this->srss->validated, true)); + $rss = SRSS::create(); + self::assertFalse($rss->isValid(), var_export($rss->validated, true)); + $rss->title = 'title'; // mandatory + self::assertFalse($rss->isValid(), var_export($rss->validated, true)); + $rss->description = 'desc'; // mandatory + self::assertFalse($rss->isValid(), var_export($rss->validated, true)); + $rss->link = 'https://example.org'; + self::assertTrue($rss->isValid(), var_export($rss->validated, true)); } - public function testItem() + public function testInvalidChannelLink(): void + { + $this->expectException(InvalidPropertyException::class); + $rss = SRSS::create(); + $rss->title = 'title'; // mandatory + $rss->description = 'desc'; // mandatory + $rss->link = 'desc'; // mandatory but should be an url + } + + public function testInvalidChannelLanguage(): void + { + $rss = SRSS::create(); + $this->expectException(InvalidPropertyException::class); + $rss->language = 'en-en'; // should be a valid language + } + + public function testInvalidChannelCopyright(): void + { + $rss = SRSS::create(); + $this->expectException(InvalidPropertyException::class); + $rss->copyright = 'test'; // should not have html element + } + + public function testInvalidChannelManagingEditor(): void + { + $rss = SRSS::create(); + $this->expectException(InvalidPropertyException::class); + $rss->managingEditor = 'test'; // should not have html element + } + + public function testInvalidChannelWebmaster(): void + { + $rss = SRSS::create(); + $this->expectException(InvalidPropertyException::class); + $rss->webMaster = 'test'; // should not have html element + } + + public function testInvalidChannelPubDate(): void + { + $rss = SRSS::create(); + $this->expectException(InvalidPropertyException::class); + $rss->pubDate = 'test'; // should be a valid date + } + + public function testInvalidChannelLastBuildDate(): void + { + $rss = SRSS::create(); + $this->expectException(InvalidPropertyException::class); + $rss->lastBuildDate = 'test'; // should be a valid date + } + + public function testInvalidChannelGenerator(): void + { + $rss = SRSS::create(); + $this->expectException(InvalidPropertyException::class); + $rss->generator = 'test'; // should not have html element + } + + public function testInvalidChannelDocs(): void + { + $rss = SRSS::create(); + $this->expectException(InvalidPropertyException::class); + $rss->docs = 'desc'; //should be a url + } + + public function testInvalidChannelTTL(): void + { + $rss = SRSS::create(); + $this->expectException(InvalidPropertyException::class); + $rss->ttl = 'desc'; // should be an int + } + + public function testInvalidChannelSkipHours(): void + { + $rss = SRSS::create(); + $this->expectException(InvalidPropertyException::class); + $rss->skipHours = 'desc'; // should be an hour + } + + public function testInvalidChannelSkipDays(): void + { + $rss = SRSS::create(); + $this->expectException(InvalidPropertyException::class); + $rss->skipDays = 'desc'; // should be a day + } + + public function testInvalidItemAuthor(): void { $item = new Shikiryu\SRSS\Entity\Item(); - - self::assertFalse($item->isValid(), var_export($item->validated, true)); - $item->title = 'title'; - self::assertTrue($item->isValid(), var_export($item->validated, true)); - - $item->link = 'test'; - self::assertFalse($item->isValid(), var_export($item->validated, true)); - $item->link = 'https://example.org/link1'; - self::assertTrue($item->isValid(), var_export($item->validated, true)); - - $item->title = null; - self::assertFalse($item->isValid(), var_export($item->validated, true)); - $item->description = 'desc'; - self::assertTrue($item->isValid(), var_export($item->validated, true)); - $item->title = 'title'; - self::assertTrue($item->isValid(), var_export($item->validated, true)); - - $item->author = 'test'; - self::assertFalse($item->isValid(), var_export($item->validated, true)); - $item->author = 'email@example.org'; - self::assertTrue($item->isValid(), var_export($item->validated, true)); - - $item->comments = 'test'; - self::assertFalse($item->isValid(), var_export($item->validated, true)); - $item->comments = 'https://example.org/link1'; - self::assertTrue($item->isValid(), var_export($item->validated, true)); - - // guid is not validated and, so, not tested + $this->expectException(InvalidPropertyException::class); + $item->author = 'test'; // should be an email } -} \ No newline at end of file + + public function testInvalidItemComments(): void + { + $item = new Shikiryu\SRSS\Entity\Item(); + $this->expectException(InvalidPropertyException::class); + $item->comments = 'test'; // should be an url + } + + public function testInvalidItemMandatory(): void + { + $item = new Shikiryu\SRSS\Entity\Item(); + $item->title = 'title'; + self::assertTrue($item->isValid(), var_export($item->validated, true)); + $item->description = 'desc'; + $item->title = null; + self::assertTrue($item->isValid(), var_export($item->validated, true)); + + $this->expectException(InvalidPropertyException::class); + $item->title = null; + $item->description = null; + } + + public function testChannelCategoryDomain(): void + { + $category = new Category(); + $this->expectException(InvalidPropertyException::class); + $category->domain = 'test'; + } + + public function testChannelCloudPort(): void + { + $cloud = new Cloud(); + $this->expectException(InvalidPropertyException::class); + $cloud->port = 'test'; + } + + public function testChannelImageUrl(): void + { + $image = new Image(); + $this->expectException(InvalidPropertyException::class); + $image->url = 'test'; + } + + public function testChannelImageTitle(): void + { + $image = new Image(); + $this->expectException(InvalidPropertyException::class); + $image->title = 'test'; + } + + public function testChannelImageLink(): void + { + $image = new Image(); + $this->expectException(InvalidPropertyException::class); + $image->link = 'test'; + } + + public function testChannelImageWidthType(): void + { + $image = new Image(); + $this->expectException(InvalidPropertyException::class); + $image->width = 'test'; + } + + public function testChannelImageWidthMax(): void + { + $image = new Image(); + $this->expectException(InvalidPropertyException::class); + $image->width = '150'; + } + + public function testChannelImageHeightType(): void + { + $image = new Image(); + $this->expectException(InvalidPropertyException::class); + $image->height = 'test'; + } + + public function testChannelImageHeightMax(): void + { + $image = new Image(); + $this->expectException(InvalidPropertyException::class); + $image->height = '500'; + } + + public function testItemEnclosureUrl(): void + { + $enclosure = new Enclosure(); + $this->expectException(InvalidPropertyException::class); + $enclosure->url = 'test'; + } + + public function testItemEnclosureLength(): void + { + $enclosure = new Enclosure(); + $this->expectException(InvalidPropertyException::class); + $enclosure->length = 'test'; + } + + public function testItemSourceUrl() + { + $source = new Source(); + $this->expectException(InvalidPropertyException::class); + $source->url = 'test'; + } + + public function testItemSourceValue() + { + $source = new Source(); + $this->expectException(InvalidPropertyException::class); + $source->value = 'test'; + } +}