All checks were successful
		
		
	
	🚀 Deploy website on push / 🎉 Deploy (push) Successful in 19s
				
			Signed-off-by: rodude123 <rodude123@gmail.com>
		
			
				
	
	
		
			387 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			387 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
 | 
						|
namespace api\utils\feedGenerator;
 | 
						|
 | 
						|
require_once "FeedItem.php";
 | 
						|
 | 
						|
/**
 | 
						|
 * Universal Feed Writer class
 | 
						|
 *
 | 
						|
 * Generate RSS 1.0, RSS 2.0, and Atom Feed
 | 
						|
 *
 | 
						|
 * @package     UniversalFeedWriter
 | 
						|
 * @link        http://www.ajaxray.com/projects/rss
 | 
						|
 */
 | 
						|
class FeedWriter
 | 
						|
{
 | 
						|
    private array $channels = []; // Collection of channel elements
 | 
						|
    private array $items = []; // Collection of items as objects of FeedItem class
 | 
						|
    private array $data = []; // Store some other version-wise data
 | 
						|
    private array $CDATAEncoding = []; // The tag names that need to be encoded as CDATA
 | 
						|
 | 
						|
    private string $version;
 | 
						|
 | 
						|
    /**
 | 
						|
     * Constructor
 | 
						|
     *
 | 
						|
     * @param string $version The version (RSS1, RSS2, ATOM).
 | 
						|
     */
 | 
						|
    public function __construct(string $version = RSS2)
 | 
						|
    {
 | 
						|
        $this->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 = '<?xml version="1.0" encoding="utf-8"?>' . "\n";
 | 
						|
 | 
						|
        if ($this->version == RSS2)
 | 
						|
        {
 | 
						|
            $out .= '<rss version="2.0"
 | 
						|
                    xmlns:content="http://purl.org/rss/1.0/modules/content/"
 | 
						|
                    xmlns:wfw="http://wellformedweb.org/CommentAPI/"
 | 
						|
                  >' . PHP_EOL;
 | 
						|
        }
 | 
						|
        elseif ($this->version == RSS1)
 | 
						|
        {
 | 
						|
            $out .= '<rdf:RDF
 | 
						|
                     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
						|
                     xmlns="http://purl.org/rss/1.0/"
 | 
						|
                     xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
						|
                    >' . PHP_EOL;
 | 
						|
        }
 | 
						|
        elseif ($this->version == ATOM)
 | 
						|
        {
 | 
						|
            $out .= '<feed xmlns="http://www.w3.org/2005/Atom">' . PHP_EOL;
 | 
						|
        }
 | 
						|
        echo $out;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Closes the open tags at the end of the file
 | 
						|
     */
 | 
						|
    private function printTail(): void
 | 
						|
    {
 | 
						|
        if ($this->version == RSS2)
 | 
						|
        {
 | 
						|
            echo '</channel>' . PHP_EOL . '</rss>';
 | 
						|
        }
 | 
						|
        elseif ($this->version == RSS1)
 | 
						|
        {
 | 
						|
            echo '</rdf:RDF>';
 | 
						|
        }
 | 
						|
        elseif ($this->version == ATOM)
 | 
						|
        {
 | 
						|
            echo '</feed>';
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * 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}><![CDATA[" : "<{$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)) ? "]]></$tagName>" : "</$tagName>";
 | 
						|
 | 
						|
        return $nodeText . PHP_EOL;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Prints channel elements
 | 
						|
     */
 | 
						|
    private function printChannels(): void
 | 
						|
    {
 | 
						|
        // Start channel tag
 | 
						|
        switch ($this->version)
 | 
						|
        {
 | 
						|
            case RSS2:
 | 
						|
                echo '<channel>' . PHP_EOL;
 | 
						|
                break;
 | 
						|
            case RSS1:
 | 
						|
                echo (isset($this->data['ChannelAbout'])) ? "<channel rdf:about=\"{$this->data['ChannelAbout']}\">" : "<channel rdf:about=\"{$this->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 <rdf:Seq> with channel
 | 
						|
        if ($this->version == RSS1)
 | 
						|
        {
 | 
						|
            echo "<items>" . PHP_EOL . "<rdf:Seq>" . PHP_EOL;
 | 
						|
            foreach ($this->items as $item)
 | 
						|
            {
 | 
						|
                $thisItems = $item->getElements();
 | 
						|
                echo "<rdf:li resource=\"{$thisItems['link']['content']}\"/>" . PHP_EOL;
 | 
						|
            }
 | 
						|
            echo "</rdf:Seq>" . PHP_EOL . "</items>" . PHP_EOL . "</channel>" . 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 '<item>' . PHP_EOL;
 | 
						|
        }
 | 
						|
        elseif ($this->version == RSS1)
 | 
						|
        {
 | 
						|
            if ($about)
 | 
						|
            {
 | 
						|
                echo "<item rdf:about=\"$about\">" . 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 "<entry>" . PHP_EOL;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Closes the feed item tag
 | 
						|
     */
 | 
						|
    private function endItem(): void
 | 
						|
    {
 | 
						|
        if ($this->version == RSS2 || $this->version == RSS1)
 | 
						|
        {
 | 
						|
            echo '</item>' . PHP_EOL;
 | 
						|
        }
 | 
						|
        elseif ($this->version == ATOM)
 | 
						|
        {
 | 
						|
            echo "</entry>" . 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';
 |