diff --git a/src/Entity/Channel.php b/src/Entity/Channel.php new file mode 100644 index 0000000..176237e --- /dev/null +++ b/src/Entity/Channel.php @@ -0,0 +1,31 @@ + 'link', @@ -27,6 +28,8 @@ class Content extends DOMElement ]; private array $attr = []; + private DOMNode $node; + /** * Constructor * @@ -34,16 +37,21 @@ class Content extends DOMElement */ public function __construct(?\DOMNode $node = null) { - parent::__construct('media:content'); + parent::__construct(); + if ($node instanceof DOMElement) { + $this->node = $this->importNode($node, true); + } else { + $this->node = $this->importNode(new DomElement('item')); + } $this->_loadAttributes(); } /** * @return void */ - private function _loadAttributes() + private function _loadAttributes(): void { - foreach ($this->attributes as $attributes) { + foreach ($this->node->attributes as $attributes) { if (array_key_exists($attributes->name, $this->possibilities)) { $this->{$attributes->name} = $attributes->value; } @@ -102,8 +110,11 @@ class Content extends DOMElement } $flag = $this->possibilities[$name]; - if ($flag !== '') + + if ($flag !== '') { $val = SRSSTools::check($val, $flag); + } + if (!empty($val)) { if ($this->$name === null) { $this->node->appendChild(new DomElement($name, $val)); diff --git a/src/Entity/Media/Group.php b/src/Entity/Media/Group.php new file mode 100644 index 0000000..64ca71c --- /dev/null +++ b/src/Entity/Media/Group.php @@ -0,0 +1,8 @@ + 'nohtml', @@ -45,8 +52,8 @@ class SRSS extends DomDocument implements Iterator libxml_use_internal_errors(true); parent::__construct(); $this->xpath = new DOMXpath($this); - $this->attr = array(); - $this->items = array(); + $this->attr = []; + $this->items = []; $this->position = 0; $this->formatOutput = true; $this->preserveWhiteSpace = false; @@ -65,19 +72,19 @@ class SRSS extends DomDocument implements Iterator } /** - * @param $link string url of the rss + * @param string $link url of the rss * @throws SRSSException * @return SRSS */ - public static function read($link): SRSS + public static function read(string $link): SRSS { $doc = new SRSS; - if(@$doc->load($link)) // We don't want the warning in case of bad XML. Let's manage it with an exception. - { + if(@$doc->load($link)) { // We don't want the warning in case of bad XML. Let's manage it with an exception. $channel = $doc->getElementsByTagName('channel'); if($channel->length == 1){ // Good URL and good RSS $doc->_loadAttributes(); // loading channel properties $doc->getItems(); // loading all items + return $doc; } @@ -89,6 +96,7 @@ class SRSS extends DomDocument implements Iterator /** * @return SRSS + * @throws \DOMException */ public static function create() { @@ -101,25 +109,31 @@ class SRSS extends DomDocument implements Iterator $doc->encoding = "UTF-8"; $doc->generator = 'Shikiryu RSS'; // $docs = 'http://www.scriptol.fr/rss/RSS-2.0.html'; + $doc->channel = new Channel(); + $doc->items = []; + return $doc; } /** * getter of "image"'s channel attributes * @return string|array + * TODO */ public function image() { $args = func_get_args(); - if(func_num_args() == 0) $args[0] = 'url'; + if (func_num_args() == 0) { + $args[0] = 'url'; + } $img = $this->xpath->query('//channel/image'); - if($img->length != 1) return null; // is not in channel + if($img->length != 1) { // is not in channel + return null; + } $img = $img->item(0); - $r = array(); - foreach($img->childNodes as $child) - { - if($child->nodeType == XML_ELEMENT_NODE && in_array($child->nodeName, $args)) - { + $r = []; + foreach($img->childNodes as $child) { + if($child->nodeType == XML_ELEMENT_NODE && in_array($child->nodeName, $args)) { $r[$child->nodeName] = $child->nodeValue; } } @@ -134,6 +148,7 @@ class SRSS extends DomDocument implements Iterator * @param $width int width * @param $height int height * @param $description string description + * TODO */ public function setImage($url, $title, $link, $width = 0, $height = 0, $description = '') { @@ -196,6 +211,7 @@ class SRSS extends DomDocument implements Iterator * @param $path string path * @param $registerProcedure string register procedure * @param $protocol string protocol + * TODO */ public function setCloud($domain, $port, $path, $registerProcedure, $protocol) { @@ -227,12 +243,13 @@ class SRSS extends DomDocument implements Iterator /** * check if current RSS is a valid one (based on specifications) * @return bool + * TODO use required */ public function isValid() { $valid = true; $items = $this->getItems(); - $invalidItems = array(); + $invalidItems = []; $i = 1; foreach($items as $item){ if($item->isValid() === false){ @@ -246,13 +263,16 @@ class SRSS extends DomDocument implements Iterator /** * getter of current RSS channel - * @return DOMElement + * @return \DOMNode * @throws SRSSException */ - private function _getChannel() + private function _getChannel(): \DOMNode { $channel = $this->getElementsByTagName('channel'); - if($channel->length != 1) throw new SRSSException('channel node not created, or too many channel nodes'); + if($channel->length != 1) { + throw new SRSSException('channel node not created, or too many channel nodes'); + } + return $channel->item(0); } @@ -298,16 +318,19 @@ class SRSS extends DomDocument implements Iterator */ public function __get($name) { - if(isset($this->attr[$name])) - return $this->attr[$name]; + if (isset($this->channel->{$name})) { + return $this->channel->{$name}; + } // $channel = $this->_getChannel(); if(array_key_exists($name, $this->possibleAttr)){ $tmp = $this->xpath->query('//channel/'.$name); - if($tmp->length != 1) return null; + if($tmp->length != 1) { + return null; + } return $tmp->item(0)->nodeValue; - }else{ - throw new SRSSException($name.' is not a possible value.'); } + + throw new SRSSException($name.' is not a possible value.'); } /** @@ -357,71 +380,80 @@ class SRSS extends DomDocument implements Iterator /** * key from Iterator */ - public function key() { + #[ReturnTypeWillChange] public function key(): int + { return $this->position; } /** * next from Iterator */ - public function next() { + #[ReturnTypeWillChange] public function next(): void + { ++$this->position; } /** * valid from Iterator */ - public function valid() { + #[ReturnTypeWillChange] public function valid(): bool + { return isset($this->items[$this->position]); } /** * getter of 1st item - * @return SRSSItem + * @return Item */ - public function getFirst() + public function getFirst(): ?Item { return $this->getItem(1); } /** * getter of last item - * @return SRSSItem + * @return Item */ - public function getLast() + public function getLast(): Item { $items = $this->getItems(); - return $items[count($items)-1]; + return $items[array_key_last($items)]; } /** * getter of an item * @param $i int - * @return SRSSItem + * + * @return Item|null */ - public function getItem($i) + public function getItem(int $i): ?Item { $i--; - return isset($this->items[$i]) ? $this->items[$i] : null; + return $this->items[$i] ?? null; } /** * getter of all items - * @return SRSSItem[] + * @return Item[] * @throws SRSSException */ - public function getItems() + public function getItems(): array { + if (!empty($this->items)) { + return $this->items; + } + $channel = $this->_getChannel(); - $item = $channel->getElementsByTagName('item'); - $length = $item->length; + /** @var DOMNodeList $items */ + $items = $channel->getElementsByTagName('item'); + $length = $items->length; $this->items = []; - if($length > 0){ - for($i = 0; $i < $length; $i++) - { - $this->items[$i] = new SRSSItem($item->item($i)); + if ($length > 0) { + for($i = 0; $i < $length; $i++) { + $this->items[$i] = SRSSItem::read($items->item($i)); } } + return $this->items; } @@ -429,31 +461,36 @@ class SRSS extends DomDocument implements Iterator * display XML * see DomDocument's docs */ - public function show() + public function show(): bool|string { + // TODO build return $this->saveXml(); } /** * putting all RSS attributes into the object + * @throws SRSSException */ - private function _loadAttributes() + private function _loadAttributes(): void { - $channel = $this->_getChannel(); - foreach($channel->childNodes as $child) - { - if($child->nodeType == XML_ELEMENT_NODE && $child->nodeName != 'item') - { - if($child->nodeName == 'image'){ - foreach($child->childNodes as $children) - { - if($children->nodeType == XML_ELEMENT_NODE) - $this->attr['image'][$children->nodeName] = $children->nodeValue; + $node_channel = $this->_getChannel(); + $this->channel = new Channel(); + + foreach($node_channel->childNodes as $child) { + if($child->nodeType == XML_ELEMENT_NODE && $child->nodeName !== 'item') { + if($child->nodeName == 'image') { + $image = new Image(); + foreach($child->childNodes as $children) { + if($children->nodeType == XML_ELEMENT_NODE) { + $image->{$child->nodeName} = $children->nodeValue; + } } + $this->channel->image = $image; + + } else { + $this->channel->{$child->nodeName} = $child->nodeValue; } - else - $this->attr[$child->nodeName] = $child->nodeValue; } } } @@ -461,18 +498,16 @@ class SRSS extends DomDocument implements Iterator /** * transform current object into an array * @return array + * @throws SRSSException */ - public function toArray() + public function toArray(): array { - $doc = array(); - foreach($this->attr as $attrName => $attrVal) - { - $doc[$attrName] = $attrVal; - } - foreach($this->getItems() as $item) - { + $doc = $this->channel->toArray(); + + foreach($this->getItems() as $item) { $doc['items'][] = $item->toArray(); } + return $doc; } } \ No newline at end of file diff --git a/src/SRSSBuilder.php b/src/SRSSBuilder.php new file mode 100644 index 0000000..6612563 --- /dev/null +++ b/src/SRSSBuilder.php @@ -0,0 +1,8 @@ + 'nohtml', 'link' => 'link', 'description' => 'html', @@ -38,44 +42,53 @@ 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(); - } - - /** - * putting all item attributes into the object - */ - private function _loadAttributes(): void - { - $this->_loadChildAttributes($this->node->childNodes); +// if ($node instanceof DOMElement) $this->node = $this->importNode($node, true); +// else $this->node = $this->importNode(new DomElement('item')); +// $this->_loadAttributes(); } /** + * @param Item $item * @param $nodes * * @return void */ - private function _loadChildAttributes($nodes): void + private static function _loadChildAttributes(Item $item, $nodes): void { - foreach ($nodes as $child) { + foreach ($nodes->childNodes as $child) { if ($child->nodeType === XML_ELEMENT_NODE && $child->nodeName !== 'item') { - if (array_key_exists($child->nodeName, $this->possibilities) && $this->possibilities[$child->nodeName] === 'folder') { - $this->_loadChildAttributes($child->childNodes); - } - if ($child->nodeName === 'media:content') { - $this->{$child->nodeName} = new Content($child); + if (array_key_exists($child->nodeName, self::$possibilities) && self::$possibilities[$child->nodeName] === 'folder') { + self::_loadChildAttributes($item, $child); + } elseif ($child->nodeName === 'media:group') { + // TODO + } elseif ($child->nodeName === 'media:content') { + $item->{$child->nodeName} = new Content($child); } else { - $this->{$child->nodeName} = $child->nodeValue; + $item->{$child->nodeName} = $child->nodeValue; } } } } + /** + * @param DOMNode|null $node + * + * @return Item + */ + public static function read(?DOMNode $node = null): Item + { + $item = new Item(); + if ($node instanceof DOMNode) { + self::_loadChildAttributes($item, $node); + } + + return $item; + } + /** * getter of item DomElement */ - public function getItem() + public function getItem(): ?\DOMNode { $this->appendChild($this->node); @@ -88,8 +101,9 @@ class SRSSItem extends DomDocument * @param $url string url * @param $length int length * @param $type string type + * @throws DOMException */ - public function setEnclosure($url, $length, $type) + public function setEnclosure(string $url, int $length, string $type): void { $array = []; $url = SRSSTools::checkLink($url); @@ -112,9 +126,9 @@ class SRSSItem extends DomDocument * check if current item is valid (following specifications) * @return bool */ - public function isValid() + public function isValid(): bool { - return $this->description != null ? true : false; + return $this->description != null; } /** @@ -133,7 +147,7 @@ class SRSSItem extends DomDocument * @param $name * @param $val * - * @throws SRSSException + * @throws SRSSException|DOMException */ public function __set($name, $val) { @@ -145,7 +159,9 @@ class SRSSItem extends DomDocument if ($flag !== '') $val = SRSSTools::check($val, $flag); if (!empty($val)) { - if ($this->$name == null) { + if ($val instanceof DOMElement) { + $this->node->appendChild($val); + } elseif ($this->$name == null) { $this->node->appendChild(new DomElement($name, $val)); } $this->attr[$name] = $val; diff --git a/tests/BasicReader.php b/tests/BasicReader.php index 36d9468..400eabb 100644 --- a/tests/BasicReader.php +++ b/tests/BasicReader.php @@ -11,6 +11,7 @@ class BasicReader extends TestCase $rss = SRSS::read(__DIR__.'/resources/basic.xml'); self::assertEquals('test Home Page', $rss->title); $first_item = $rss->getFirst(); + self::assertNotNull($first_item); self::assertEquals('RSS Tutorial', $first_item->title); } diff --git a/tests/MediaTest.php b/tests/MediaTest.php index fec2729..655f04f 100644 --- a/tests/MediaTest.php +++ b/tests/MediaTest.php @@ -13,6 +13,6 @@ class MediaTest extends TestCase $first_item = $rss->getFirst(); self::assertEquals('Kirstie Alley, \'Cheers\' and \'Veronica\'s Closet\' star, dead at 71', $first_item->title); - self::assertEquals('https://cdn.cnn.com/cnnnext/dam/assets/221205172141-kirstie-alley-2005-super-169.jpg', $first_item->{'media:group'}->{'media:content'}->url); + self::assertEquals('https://cdn.cnn.com/cnnnext/dam/assets/221205172141-kirstie-alley-2005-super-169.jpg', $first_item->{'media:content'}->url); } } \ No newline at end of file diff --git a/tests/resources/harvard.xml b/tests/resources/harvard.xml new file mode 100644 index 0000000..48dcb54 --- /dev/null +++ b/tests/resources/harvard.xml @@ -0,0 +1,41 @@ + + + + Liftoff News + http://liftoff.msfc.nasa.gov/ + Liftoff to Space Exploration. + en-us + Tue, 10 Jun 2003 04:00:00 GMT + Tue, 10 Jun 2003 09:41:01 GMT + http://blogs.law.harvard.edu/tech/rss + Weblog Editor 2.0 + editor@example.com + webmaster@example.com + + Star City + http://liftoff.msfc.nasa.gov/news/2003/news-starcity.asp + How do Americans get ready to work with Russians aboard the International Space Station? They take a crash course in culture, language and protocol at Russia's <a href="http://howe.iki.rssi.ru/GCTC/gctc_e.htm">Star City</a>. + Tue, 03 Jun 2003 09:39:21 GMT + http://liftoff.msfc.nasa.gov/2003/06/03.html#item573 + + + Sky watchers in Europe, Asia, and parts of Alaska and Canada will experience a <a href="http://science.nasa.gov/headlines/y2003/30may_solareclipse.htm">partial eclipse of the Sun</a> on Saturday, May 31st. + Fri, 30 May 2003 11:06:42 GMT + http://liftoff.msfc.nasa.gov/2003/05/30.html#item572 + + + The Engine That Does More + http://liftoff.msfc.nasa.gov/news/2003/news-VASIMR.asp + Before man travels to Mars, NASA hopes to design new engines that will let us fly through the Solar System more quickly. The proposed VASIMR engine would do that. + Tue, 27 May 2003 08:37:32 GMT + http://liftoff.msfc.nasa.gov/2003/05/27.html#item571 + + + Astronauts' Dirty Laundry + http://liftoff.msfc.nasa.gov/news/2003/news-laundry.asp + Compared to earlier spacecraft, the International Space Station has many luxuries, but laundry facilities are not one of them. Instead, astronauts have other options. + Tue, 20 May 2003 08:56:02 GMT + http://liftoff.msfc.nasa.gov/2003/05/20.html#item570 + + + \ No newline at end of file