* @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Feed; use Joomla\CMS\Date\Date; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Class to encapsulate a feed for the Joomla Platform. * * @property FeedPerson $author Person responsible for feed content. * @property array $categories Categories to which the feed belongs. * @property array $contributors People who contributed to the feed content. * @property string $copyright Information about rights, e.g. copyrights, held in and over the feed. * @property string $description A phrase or sentence describing the feed. * @property string $generator A string indicating the program used to generate the feed. * @property FeedLink|null $image FeedLink object containing feed image properties. * @property Date $publishedDate The publication date for the feed content. * @property string $title A human readable title for the feed. * @property Date $updatedDate The last time the content of the feed changed. * @property string $uri Universal, permanent identifier for the feed. * * @since 3.1.4 */ class Feed implements \ArrayAccess, \Countable { /** * @var array The entry properties. * @since 3.1.4 */ protected $properties = array( 'uri' => '', 'title' => '', 'updatedDate' => '', 'description' => '', 'categories' => array(), 'contributors' => array(), ); /** * @var array The list of feed entry objects. * @since 3.1.4 */ protected $entries = array(); /** * Magic method to return values for feed properties. * * @param string $name The name of the property. * * @return mixed * * @since 3.1.4 */ public function __get($name) { return $this->properties[$name] ?? null; } /** * Magic method to set values for feed properties. * * @param string $name The name of the property. * @param mixed $value The value to set for the property. * * @return void * * @since 3.1.4 */ public function __set($name, $value) { // Ensure that setting a date always sets a Date instance. if ((($name === 'updatedDate') || ($name === 'publishedDate')) && !($value instanceof Date)) { $value = new Date($value); } // Validate that any authors that are set are instances of JFeedPerson or null. if (($name === 'author') && (!($value instanceof FeedPerson) || ($value === null))) { throw new \InvalidArgumentException( sprintf( '%1$s "author" must be an instance of Joomla\\CMS\\Feed\\FeedPerson. %2$s given.', \get_class($this), \gettype($value) === 'object' ? \get_class($value) : \gettype($value) ) ); } // Disallow setting categories or contributors directly. if (\in_array($name, array('categories', 'contributors'))) { throw new \InvalidArgumentException( sprintf( 'Cannot directly set %1$s property "%2$s".', \get_class($this), $name ) ); } $this->properties[$name] = $value; } /** * Method to add a category to the feed object. * * @param string $name The name of the category to add. * @param string $uri The optional URI for the category to add. * * @return Feed * * @since 3.1.4 */ public function addCategory($name, $uri = '') { $this->properties['categories'][$name] = $uri; return $this; } /** * Method to add a contributor to the feed object. * * @param string $name The full name of the person to add. * @param string $email The email address of the person to add. * @param string $uri The optional URI for the person to add. * @param string $type The optional type of person to add. * * @return Feed * * @since 3.1.4 */ public function addContributor($name, $email, $uri = null, $type = null) { $contributor = new FeedPerson($name, $email, $uri, $type); // If the new contributor already exists then there is nothing to do, so just return. foreach ($this->properties['contributors'] as $c) { if ($c == $contributor) { return $this; } } // Add the new contributor. $this->properties['contributors'][] = $contributor; return $this; } /** * Method to add an entry to the feed object. * * @param FeedEntry $entry The entry object to add. * * @return Feed * * @since 3.1.4 */ public function addEntry(FeedEntry $entry) { // If the new entry already exists then there is nothing to do, so just return. foreach ($this->entries as $e) { if ($e == $entry) { return $this; } } // Add the new entry. $this->entries[] = $entry; return $this; } /** * Returns a count of the number of entries in the feed. * * This method is here to implement the Countable interface. * You can call it by doing count($feed) rather than $feed->count(); * * @return integer number of entries in the feed. */ #[\ReturnTypeWillChange] public function count() { return \count($this->entries); } /** * Whether or not an offset exists. This method is executed when using isset() or empty() on * objects implementing ArrayAccess. * * @param mixed $offset An offset to check for. * * @return boolean * * @see ArrayAccess::offsetExists() * @since 3.1.4 */ #[\ReturnTypeWillChange] public function offsetExists($offset) { return isset($this->entries[$offset]); } /** * Returns the value at specified offset. * * @param mixed $offset The offset to retrieve. * * @return mixed The value at the offset. * * @see ArrayAccess::offsetGet() * @since 3.1.4 */ #[\ReturnTypeWillChange] public function offsetGet($offset) { return $this->entries[$offset]; } /** * Assigns a value to the specified offset. * * @param mixed $offset The offset to assign the value to. * @param FeedEntry $value The JFeedEntry to set. * * @return boolean * * @see ArrayAccess::offsetSet() * @since 3.1.4 * @throws \InvalidArgumentException */ #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { if (!($value instanceof FeedEntry)) { throw new \InvalidArgumentException( sprintf( '%1$s entries must be an instance of Joomla\\CMS\\Feed\\FeedPerson. %2$s given.', \get_class($this), \gettype($value) === 'object' ? \get_class($value) : \gettype($value) ) ); } $this->entries[$offset] = $value; return true; } /** * Unsets an offset. * * @param mixed $offset The offset to unset. * * @return void * * @see ArrayAccess::offsetUnset() * @since 3.1.4 */ #[\ReturnTypeWillChange] public function offsetUnset($offset) { unset($this->entries[$offset]); } /** * Method to remove a category from the feed object. * * @param string $name The name of the category to remove. * * @return Feed * * @since 3.1.4 */ public function removeCategory($name) { unset($this->properties['categories'][$name]); return $this; } /** * Method to remove a contributor from the feed object. * * @param FeedPerson $contributor The person object to remove. * * @return Feed * * @since 3.1.4 */ public function removeContributor(FeedPerson $contributor) { // If the contributor exists remove it. foreach ($this->properties['contributors'] as $k => $c) { if ($c == $contributor) { unset($this->properties['contributors'][$k]); $this->properties['contributors'] = array_values($this->properties['contributors']); return $this; } } return $this; } /** * Method to remove an entry from the feed object. * * @param FeedEntry $entry The entry object to remove. * * @return Feed * * @since 3.1.4 */ public function removeEntry(FeedEntry $entry) { // If the entry exists remove it. foreach ($this->entries as $k => $e) { if ($e == $entry) { unset($this->entries[$k]); $this->entries = array_values($this->entries); return $this; } } return $this; } /** * Shortcut method to set the author for the feed object. * * @param string $name The full name of the person to set. * @param string $email The email address of the person to set. * @param string $uri The optional URI for the person to set. * @param string $type The optional type of person to set. * * @return Feed * * @since 3.1.4 */ public function setAuthor($name, $email, $uri = null, $type = null) { $author = new FeedPerson($name, $email, $uri, $type); $this->properties['author'] = $author; return $this; } /** * Method to reverse the items if display is set to 'oldest first' * * @return Feed * * @since 3.1.4 */ public function reverseItems() { if (\is_array($this->entries) && !empty($this->entries)) { $this->entries = array_reverse($this->entries); } return $this; } }