version = $version; // Setting default values for essential channel elements $this->channels['title'] = $version . ' Feed'; $this->channels['link'] = 'http://www.ajaxray.com/blog'; $this->channels["feedUrl"] = "http://example.com/feed"; // Tag names to encode in CDATA $this->CDATAEncoding = ['description', 'content:encoded', 'summary']; } // Public functions /** * Set a channel element * * @param string $elementName Name of the channel tag * @param string $content Content of the channel tag */ public function setChannelElement(string $elementName, string|array $content): void { $this->channels[$elementName] = $content; } /** * Generate the actual RSS/Atom file */ public function generateFeed(): void { $this->printHead(); $this->printChannels(); $this->printItems(); $this->printTail(); } /** * Create a new FeedItem. * * @return FeedItem An instance of FeedItem class */ public function createNewItem(): FeedItem { $item = new FeedItem($this->version); return $item; } /** * Add a FeedItem to the main class * * @param FeedItem $feedItem An instance of FeedItem class */ public function addItem(FeedItem $feedItem): void { $this->items[] = $feedItem; } // Wrapper functions /** * Set the 'title' channel element * * @param string $title Value of 'title' channel tag */ public function setTitle(string $title): void { $this->setChannelElement('title', $title); } /** * Set the 'description' channel element * * @param string $description Value of 'description' channel tag */ public function setDescription(string $description): void { $this->setChannelElement('description', $description); } /** * Set the 'link' channel element * * @param string $link Value of 'link' channel tag */ public function setLink(string $link): void { $this->setChannelElement('link', $link); } /** * Set the 'image' channel element * * @param string $title Title of the image * @param string $link Link URL of the image * @param string $url Path URL of the image */ public function setImage(string $title, string $link, string $url): void { $this->setChannelElement('image', ['title' => $title, 'link' => $link, 'url' => $url]); } /** * Set the 'about' channel element. Only for RSS 1.0 * * @param string $url Value of 'about' channel tag */ public function setChannelAbout(string $url): void { $this->data['ChannelAbout'] = $url; } // Other functions /** * Generates a UUID * * @param string $key An optional prefix * @param string $prefix A prefix * @return string The formatted UUID */ public static function uuid(?string $key = null, string $prefix = ''): string { $key = $key ?? uniqid((string)rand()); $chars = md5($key); $uuid = substr($chars, 0, 8) . '-'; $uuid .= substr($chars, 8, 4) . '-'; $uuid .= substr($chars, 12, 4) . '-'; $uuid .= substr($chars, 16, 4) . '-'; $uuid .= substr($chars, 20, 12); return $prefix . $uuid; } // Private functions /** * Prints the XML and RSS namespace */ private function printHead(): void { $out = '' . "\n"; if ($this->version == RSS2) { $out .= '' . PHP_EOL; } elseif ($this->version == RSS1) { $out .= '' . PHP_EOL; } elseif ($this->version == ATOM) { $out .= '' . PHP_EOL; } echo $out; } /** * Closes the open tags at the end of the file */ private function printTail(): void { if ($this->version == RSS2) { echo '' . PHP_EOL . ''; } elseif ($this->version == RSS1) { echo ''; } elseif ($this->version == ATOM) { echo ''; } } /** * Creates a single node in XML format * * @param string $tagName Name of the tag * @param mixed $tagContent Tag value as a string or an array of nested tags in 'tagName' => 'tagValue' format * @param array|null $attributes Attributes (if any) in 'attrName' => 'attrValue' format * @return string Formatted XML tag */ private function makeNode(string $tagName, $tagContent, ?array $attributes = null): string { $nodeText = ''; $attrText = ''; if (is_array($attributes)) { foreach ($attributes as $key => $value) { $attrText .= " $key=\"$value\""; } } if (is_array($tagContent) && $this->version == RSS1) { $attrText = ' rdf:parseType="Resource"'; } $attrText .= (in_array($tagName, $this->CDATAEncoding) && $this->version == ATOM) ? ' type="html" ' : ''; $nodeText .= (in_array($tagName, $this->CDATAEncoding)) ? "<{$tagName}{$attrText}>"; if (is_array($tagContent)) { foreach ($tagContent as $key => $value) { $nodeText .= $this->makeNode($key, $value); } } else { $nodeText .= (in_array($tagName, $this->CDATAEncoding)) ? $tagContent : htmlentities($tagContent); } $nodeText .= (in_array($tagName, $this->CDATAEncoding)) ? "]]>" : ""; return $nodeText . PHP_EOL; } /** * Prints channel elements */ private function printChannels(): void { // Start channel tag switch ($this->version) { case RSS2: echo '' . PHP_EOL; break; case RSS1: echo (isset($this->data['ChannelAbout'])) ? "data['ChannelAbout']}\">" : "channels['link']}\">"; break; } // Print items of channel foreach ($this->channels as $key => $value) { if ($this->version == ATOM && $key == 'link') { // ATOM prints the link element as an href attribute echo $this->makeNode($key, '', ['href' => $value]); // Add the id for ATOM echo $this->makeNode('id', $this->uuid($value, 'urn:uuid:')); } else if ($this->version == ATOM && $key == 'feedUrl') { echo $this->makeNode('link', '', ['rel' => 'self', 'href' => $value]); } else { echo $this->makeNode($key, $value); } } // RSS 1.0 has a special tag with channel if ($this->version == RSS1) { echo "" . PHP_EOL . "" . PHP_EOL; foreach ($this->items as $item) { $thisItems = $item->getElements(); echo "" . PHP_EOL; } echo "" . PHP_EOL . "" . PHP_EOL . "" . PHP_EOL; } } /** * Prints formatted feed items */ private function printItems(): void { foreach ($this->items as $item) { $thisItems = $item->getElements(); // The argument is printed as rdf:about attribute of item in RSS 1.0 echo $this->startItem($thisItems['link']['content']); foreach ($thisItems as $feedItem) { echo $this->makeNode($feedItem['name'], $feedItem['content'], $feedItem['attributes']); } echo $this->endItem(); } } /** * Makes the starting tag of items * * @param string|false $about The value of about tag, which is used only for RSS 1.0 */ private function startItem($about = false): void { if ($this->version == RSS2) { echo '' . PHP_EOL; } elseif ($this->version == RSS1) { if ($about) { echo "" . PHP_EOL; } else { die("link element is not set.\n It's required for RSS 1.0 to be used as the about attribute of the item"); } } elseif ($this->version == ATOM) { echo "" . PHP_EOL; } } /** * Closes the feed item tag */ private function endItem(): void { if ($this->version == RSS2 || $this->version == RSS1) { echo '' . PHP_EOL; } elseif ($this->version == ATOM) { echo "" . PHP_EOL; } } /** * Set the Feed URL * @param string $string - The URL of the feed * @return void */ public function setFeedURL(string $string): void { $this->setChannelElement("feedUrl", $string); } } // Define constants for RSS 1.0, RSS 2.0, and Atom const RSS1 = 'RSS 1.0'; const RSS2 = 'RSS 2.0'; const ATOM = 'ATOM';