diff --git a/Tests/rssTest.php b/Tests/rssTest.php
new file mode 100644
index 0000000..f9c1839
--- /dev/null
+++ b/Tests/rssTest.php
@@ -0,0 +1,148 @@
+expectException ("Exception", "No title is provided for this RSS");
+ $rss = new rss ();
+ $res = $rss->asXML ();
+ }
+
+ public function test_functional1 ()
+ {
+ // Functionnal 1
+ $rss = new rss ();
+ $rss->title ("Global Title")
+ ->link ("http://localhost")
+ ->language ("fr-fr")
+ ->description ("The Super Global Description");
+ $rss->addItem ()->title ("Item1 Title")
+ ->link ("http://Item1Link")
+ ->description ("Item1 Description");
+ $rss->addItem ()->title ("Item2 Title")
+ ->pubDate ("2017-04-13 12:25:30")
+ ->link ("http://Item2Link")
+ ->description ("Item2 Description");
+ $res = $rss->asXML ();
+ $this->assertSame ($res, "
+
+
+ Global Title
+ http://localhost
+ The Super Global Description
+ fr-fr
+ -
+ Item1 Title
+ http://Item1Link
+ Item1 Description
+
+ -
+ Item2 Title
+ http://Item2Link
+ Item2 Description
+ Thu, 13 Apr 2017 12:25:30 +0200
+
+
+
+");
+ }
+
+ public function test_error1 ()
+ {
+ // Missing global description
+ $this->expectException ("Exception",
+ "No description is provided for this RSS");
+ $rss = new rss ();
+ $rss->title ("Global Title")
+ ->link ("http://localhost")
+ ->language ("fr-fr");
+ $res = $rss->asXML ();
+ }
+
+ public function test_error2 ()
+ {
+ // Missing global link
+ $this->expectException ("Exception", "No link is provided for this RSS");
+ $rss = new rss ();
+ $rss->title ("Global Title")
+ ->language ("fr-fr")
+ ->description ("The Super Global Description");
+ $res = $rss->asXML ();
+ }
+
+ public function test_error3 ()
+ {
+ // Missing global title
+ $this->expectException ("Exception", "No title is provided for this RSS");
+ $rss = new rss ();
+ $rss->link ("http://localhost")
+ ->language ("fr-fr")
+ ->description ("The Super Global Description");
+ $res = $rss->asXML ();
+ }
+
+ public function test_error4 ()
+ {
+ // Invalid date provided
+ $this->expectException ("Exception",
+ "lastBuildDate provided to RSS is not a valid date");
+ $rss = new rss ();
+ $rss->title ("Global Title")
+ ->link ("http://localhost")
+ ->lastBuildDate ("2017-04-33")
+ ->language ("fr-fr")
+ ->description ("The Super Global Description");
+ $res = $rss->asXML ();
+ }
+
+ public function test_itemError1 ()
+ {
+ // Empty Item provided
+ $this->expectException ("Exception",
+ "No title nor description defined in the RSS item");
+ $rss = new rss ();
+ $rss->title ("Global Title")
+ ->link ("http://localhost")
+ ->lastBuildDate ("2017-04-30 12:35:32")
+ ->language ("fr-fr")
+ ->description ("The Super Global Description");
+ $rss->addItem ();
+ $res = $rss->asXML ();
+ }
+
+ public function test_itemError2 ()
+ {
+ // Item without Title and Description
+ $this->expectException ("Exception",
+ "No title nor description defined in the RSS item");
+ $rss = new rss ();
+ $rss->title ("Global Title")
+ ->link ("http://localhost")
+ ->lastBuildDate ("2017-04-30 12:35:32")
+ ->language ("fr-fr")
+ ->description ("The Super Global Description");
+ $rss->addItem ()->link ("http://localhost/link");
+ $res = $rss->asXML ();
+ }
+
+ public function test_itemError3 ()
+ {
+ // Item with invalid date
+ $this->expectException ("Exception",
+ "pubDate provided to RSS Item is not a valid date");
+ $rss = new rss ();
+ $rss->title ("Global Title")
+ ->link ("http://localhost")
+ ->lastBuildDate ("2017-10-30 12:35:32")
+ ->language ("fr-fr")
+ ->description ("The Super Global Description");
+ $rss->addItem ()->title ("Item title")
+ ->pubDate ("2017-14-33 12:32:32");
+ $res = $rss->asXML ();
+ }
+}
diff --git a/rss.php b/rss.php
new file mode 100644
index 0000000..3b71e4c
--- /dev/null
+++ b/rss.php
@@ -0,0 +1,353 @@
+ */
+
+error_reporting (E_ALL);
+require_once ("domframework/verify.php");
+require_once ("domframework/convert.php");
+
+/** Create the RSS pages for the websites
+ * Specification http://www.rssboard.org/rss-specification
+ */
+class rss
+{
+ /** The title of the site (MANDATORY)
+ */
+ private $title;
+
+ /** The link (URL) to the site (MANDATORY)
+ */
+ private $link;
+
+ /** The Description of the site (MANDATORY)
+ */
+ private $description;
+
+ /** The language of the feed
+ */
+ private $language;
+
+ /** The lastBuildDate
+ */
+ private $lastBuildDate;
+
+ /** The items linked to this RSS
+ */
+ private $items = array ();
+
+ /** Get/Set the value
+ * @param string|null $title The title to get/set
+ */
+ public function title ($title = null)
+ {
+ if ($title === null)
+ return $this->title;
+ if (! is_string ($title))
+ throw new \Exception ("Title provided to RSS is not a string", 500);
+ if ($title === "")
+ $title = null;
+ $this->title = $title;
+ return $this;
+ }
+
+ /** Get/Set the value
+ * @param string|null $link The link to get/set
+ */
+ public function link ($link = null)
+ {
+ if ($link === null)
+ return $this->link;
+ if (! is_string ($link))
+ throw new \Exception ("Link provided to RSS is not a string", 500);
+ $verify = new \verify ();
+ if (! $verify->is_URL ($link))
+ throw new \Exception ("Link provided to RSS is not an URL", 500);
+ if ($link === "")
+ $link = null;
+ $this->link = $link;
+ return $this;
+ }
+
+ /** Get/Set the value
+ * @param string|null $description The description to get/set
+ */
+ public function description ($description = null)
+ {
+ if ($description === null)
+ return $this->description;
+ if (! is_string ($description))
+ throw new \Exception ("Description provided to RSS is not a string", 500);
+ if ($description === "")
+ $description = null;
+ $this->description = $description;
+ return $this;
+ }
+
+ /** Get/Set the value
+ * @param string|null $language The language to get/set
+ */
+ public function language ($language = null)
+ {
+ if ($language === null)
+ return $this->language;
+ if (! is_string ($language))
+ throw new \Exception ("Language provided to RSS is not a string", 500);
+ if (! in_array ($language,
+ array ("af", "sq", "eu", "be", "bg", "ca", "zh-cn", "zh-tw", "hr", "cs",
+ "da", "nl", "nl-be", "nl-nl", "en", "en-au", "en-bz", "en-ca",
+ "en-ie", "en-jm", "en-nz", "en-ph", "en-za", "en-tt", "en-gb",
+ "en-us", "en-zw", "et", "fo", "fi", "fr", "fr-be", "fr-ca",
+ "fr-fr", "fr-lu", "fr-mc", "fr-ch", "gl", "gd", "de", "de-at",
+ "de-de", "de-li", "de-lu", "de-ch", "el", "haw", "hu", "is",
+ "in", "ga", "it", "it-it", "it-ch", "ja", "ko", "mk", "no", "pl",
+ "pt", "pt-br", "pt-pt", "ro", "ro-mo", "ro-ro", "ru", "ru-mo",
+ "ru-ru", "sr", "sk", "sl", "es", "es-ar", "es-bo", "es-cl",
+ "es-co", "es-cr", "es-do", "es-ec", "es-sv", "es-gt", "es-hn",
+ "es-mx", "es-ni", "es-pa", "es-py", "es-pe", "es-pr", "es-es",
+ "es-uy", "es-ve", "sv", "sv-fi", "sv-se", "tr", "uk")))
+ throw new \Exception ("Provided language for RSS is not allowed", 500);
+ if ($language === "")
+ $language = null;
+ $this->language = $language;
+ return $this;
+ }
+
+ /** Get/Set the value
+ * The date must be in the SQL format Y-m-d H:i:s
+ * @param string|null $lastBuildDate The lastBuildDate to get/set
+ */
+ public function lastBuildDate ($lastBuildDate = null)
+ {
+ if ($lastBuildDate === null)
+ return $this->lastBuildDate;
+ if (! is_string ($lastBuildDate))
+ throw new \Exception ("lastBuildDate provided to RSS is not a string",
+ 500);
+ $verify = new \verify ();
+ if (! $verify->is_datetimeSQL ($lastBuildDate))
+ throw new \Exception ("lastBuildDate provided to RSS is not a valid date",
+ 500);
+ if ($lastBuildDate === "")
+ $lastBuildDate = null;
+ else
+ $lastBuildDate = \convert::convertDate ($lastBuildDate, "Y-m-d H:i:s",
+ \DateTime::RFC2822);
+ $this->lastBuildDate = $lastBuildDate;
+ return $this;
+ }
+
+ /** Add a new item to the RSS
+ * Return the new rss object created
+ */
+ public function addItem ()
+ {
+ $rss_item = new \rss_item ();
+ $this->items[] = $rss_item;
+ return $rss_item;
+ }
+
+ /** Return the complete RSS in XML format
+ * Throw an exception if there is some mandatory fields missing
+ */
+ public function asXML ()
+ {
+ if ($this->title === null)
+ throw new \Exception ("No title is provided for this RSS", 500);
+ if ($this->link === null)
+ throw new \Exception ("No link is provided for this RSS", 500);
+ if ($this->description === null)
+ throw new \Exception ("No description is provided for this RSS", 500);
+ $xmlstr =
+ ''."\n".
+ ''."\n".
+ ' '."\n".
+ ' '."\n".
+ '';
+ $xml = new \SimpleXMLElement ($xmlstr);
+ $xml->channel->title = $this->title;
+ $xml->channel->link = $this->link;
+ $xml->channel->description = $this->description;
+ if ($this->language !== null)
+ $xml->channel->language = $this->language;
+ if ($this->lastBuildDate !== null)
+ $xml->channel->lastBuildDate = $this->lastBuildDate;
+ foreach ($this->items as $item)
+ {
+ if ($item->title() === null && $item->description() === null)
+ throw new \Exception (
+ "No title nor description defined in the RSS item", 500);
+ $tmpItem = $xml->channel->addChild ("item");
+ if ($item->title () !== null)
+ $tmpItem->title = $item->title ();
+ if ($item->link () !== null)
+ $tmpItem->link = $item->link ();
+ if ($item->description () !== null)
+ $tmpItem->description = $item->description ();
+ if ($item->author () !== null)
+ $tmpItem->author = $item->author ();
+ if ($item->comments () !== null)
+ $tmpItem->comments = $item->comments ();
+ if ($item->guid () !== null)
+ $tmpItem->guid = $item->guid ();
+ if ($item->pubDate () !== null)
+ $tmpItem->pubDate = $item->pubDate ();
+ }
+ // Ident correctely the xml created in SimpleXML
+ $dom = new DOMDocument('1.0');
+ $dom->preserveWhiteSpace = false;
+ $dom->formatOutput = true;
+ $dom->loadXML($xml->asXML());
+ return $dom->saveXML();
+ }
+}
+
+/** Define one item in the RSS
+ */
+class rss_item
+{
+ /** The title of the item
+ */
+ private $title;
+
+ /** The URL of the item
+ */
+ private $link;
+
+ /** The item synopsis
+ */
+ private $description;
+
+ /** Email address of the author of the item
+ */
+ private $author;
+
+ /** URL of a page for comments relating to the item
+ */
+ private $comments;
+
+ /** A string that uniquely identifies the item
+ */
+ private $guid;
+
+ /** Indicates when the item was published
+ */
+ private $pubDate;
+
+ /** Get/Set the value
+ * @param string|null $title The title to get/set
+ */
+ public function title ($title = null)
+ {
+ if ($title === null)
+ return $this->title;
+ if (! is_string ($title))
+ throw new \Exception ("Title provided to RSS Item is not a string", 500);
+ if ($title === "")
+ $title = null;
+ $this->title = $title;
+ return $this;
+ }
+
+ /** Get/Set the value
+ * @param string|null $link The link to get/set
+ */
+ public function link ($link = null)
+ {
+ if ($link === null)
+ return $this->link;
+ if (! is_string ($link))
+ throw new \Exception ("Link provided to RSS Item is not a string", 500);
+ $verify = new \verify ();
+ if (! $verify->is_URL ($link))
+ throw new \Exception ("Link provided to RSS Item is not an URL", 500);
+ if ($link === "")
+ $link = null;
+ $this->link = $link;
+ return $this;
+ }
+
+ /** Get/Set the value
+ * @param string|null $description The description to get/set
+ */
+ public function description ($description = null)
+ {
+ if ($description === null)
+ return $this->description;
+ if (! is_string ($description))
+ throw new \Exception ("Description provided to RSS Item is not a string",
+ 500);
+ if ($description === "")
+ $description = null;
+ $this->description = $description;
+ return $this;
+ }
+
+ /** Get/Set the value
+ * @param string|null $author The author to get/set
+ */
+ public function author ($author = null)
+ {
+ if ($author === null)
+ return $this->author;
+ if (! is_string ($author))
+ throw new \Exception ("Author provided to RSS Item is not a string", 500);
+ if ($author === "")
+ $author = null;
+ $this->author = $author;
+ return $this;
+ }
+
+ /** Get/Set the value
+ * @param string|null $comments The comments to get/set
+ */
+ public function comments ($comments = null)
+ {
+ if ($comments === null)
+ return $this->comments;
+ if (! is_string ($comments))
+ throw new \Exception ("Comments provided to RSS Item is not a string",
+ 500);
+ if ($comments === "")
+ $comments = null;
+ $this->comments = $comments;
+ return $this;
+ }
+
+ /** Get/Set the value
+ * @param string|null $guid The guid to get/set
+ */
+ public function guid ($guid = null)
+ {
+ if ($guid === null)
+ return $this->guid;
+ if (! is_string ($guid))
+ throw new \Exception ("GUID provided to RSS Item is not a string", 500);
+ if ($guid === "")
+ $guid = null;
+ $this->guid = $guid;
+ return $this;
+ }
+
+ /** Get/Set the value
+ * @param string|null $pubDate The pubDate to get/set
+ */
+ public function pubDate ($pubDate = null)
+ {
+ if ($pubDate === null)
+ return $this->pubDate;
+ if (! is_string ($pubDate))
+ throw new \Exception ("pubDate provided to RSS Item is not a string",
+ 500);
+ if (! \verify::staticIs_datetimeSQL ($pubDate))
+ throw new \Exception ("pubDate provided to RSS Item is not a valid date",
+ 500);
+ if ($pubDate === "")
+ $pubDate = null;
+ else
+ $pubDate = \convert::convertDate ($pubDate, "Y-m-d H:i:s",
+ DateTime::RFC2822);
+ $this->pubDate = $pubDate;
+ return $this;
+ }
+}