<?php /** * Class: AtomFeed * Generates an Atom feed piece by piece. * * See Also: * https://tools.ietf.org/html/rfc4287 */ class AtomFeed implements FeedGenerator { # Boolean: $open # Has the feed been opened? protected $open = false; # Variable: $count # The number of entries generated. protected $count = 0; # Array: $xml # Holds the feed as an array. protected $xml = array(); /** * Function: type * Returns the content type of the feed. */ public static function type(): string { return "application/atom+xml"; } /** * Function: open * Adds the opening feed element and top-level elements. * * Parameters: * $title - Title for this feed. * $subtitle - Subtitle (optional). * $id - Feed ID (optional). * $updated - Time of update (optional). */ public function open( $title, $subtitle = "", $id = "", $updated = null ): bool { if ($this->open) return false; $this->open = true; $language = lang_base(Config::current()->locale); $feed = '<?xml version="1.0" encoding="UTF-8"?>'."\n"; $feed.= '<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="'. fix($language, true).'">'. "\n"; $feed.= '<title>'.fix($title).'</title>'."\n"; if (!empty($subtitle)) $feed.= '<subtitle>'.fix($subtitle).'</subtitle>'."\n"; $feed.= '<id>'.fix(oneof($id, self_url())).'</id>'."\n"; $feed.= '<updated>'. when(DATE_ATOM, oneof($updated, time())). '</updated>'. "\n"; $feed.= '<link href="'. self_url(). '" rel="self" type="application/atom+xml" />'. "\n"; $feed.= '<generator uri="http://chyrplite.net/" version="'. CHYRP_VERSION. '">'. CHYRP_IDENTITY. '</generator>'. "\n"; $this->xml = array( "feed" => $feed, "items" => array() ); return $this->open = true; } /** * Function: entry * Adds an individual feed entry. * * Parameters: * $title - Title for this entry. * $id - The unique ID. * $content - Content for this entry. * $link - The URL to the resource. * $published - Time of creation. * $updated - Time of update (optional). * $name - Name of the author (optional). * $uri - URI of the author (optional). * $email - Email address of the author (optional). * * Notes: * The entry remains open to allow triggered insertions. */ public function entry( $title, $id, $content, $link, $published, $updated = null, $name = "", $uri = "", $email = "" ): bool { if (!$this->open) return false; $this->count++; $entry = '<title type="html">'. fix($title, false, true). '</title>'. "\n"; $entry.= '<id>'.fix($id).'</id>'."\n"; $entry.= '<updated>'. when(DATE_ATOM, oneof($updated, $published)). '</updated>'. "\n"; $entry.= '<published>'. when(DATE_ATOM, $published). '</published>'. "\n"; $entry.= '<link rel="alternate" type="text/html" href="'. fix($link, true). '" />'. "\n"; $entry.= '<author>'."\n"; $entry.= '<name>'. fix(oneof($name, __("Guest"))). '</name>'. "\n"; if (!empty($uri) and is_url($uri)) $entry.= '<uri>'.fix($uri).'</uri>'."\n"; if (!empty($email) and is_email($email)) $entry.= '<email>'.fix($email).'</email>'."\n"; $entry.= '</author>'."\n"; $entry.= '<content type="html">'. fix($content, false, true). '</content>'. "\n"; $item = $this->count - 1; $this->xml["items"][$item] = $entry; return true; } /** * Function: category * Adds a category element for an entry or feed. * * Parameters: * $term - String that identifies the category. * $scheme - URI for the categorization scheme (optional). * $label - Human-readable label for the category (optional). */ public function category( $term, $scheme = "", $label = "" ): bool { if (!$this->open) return false; $category = '<category term="'. fix($term, true). '"'; if (!empty($scheme)) $category.= ' scheme="'.fix($scheme, true).'"'; if (!empty($label)) $category.= ' label="'.fix($label, true).'"'; $category.= ' />'."\n"; if (!$this->count) { $this->xml["feed"].= $category; } else { $item = $this->count - 1; $this->xml["items"][$item].= $category; } return true; } /** * Function: rights * Adds a rights element for an entry or feed. * * Parameters: * $text - Human-readable licensing information. */ public function rights( $text ): bool { if (!$this->open) return false; $rights = '<rights>'. fix($text, false, true). '</rights>'. "\n"; if (!$this->count) { $this->xml["feed"].= $rights; } else { $item = $this->count - 1; $this->xml["items"][$item].= $rights; } return true; } /** * Function: enclosure * Adds a link element for a resource that is potentially large in size. * * Parameters: * $link - The URL to the resource. * $length - Size in bytes of the resource (optional). * $type - The media type of the resource (optional). * $title - Title for the resource (optional). */ public function enclosure( $link, $length = null, $type = "", $title = "" ): bool { if (!$this->open) return false; $enclosure = '<link rel="enclosure" href="'. fix($link, true). '"'; if (!empty($length)) $enclosure.= ' length="'.fix($length, true).'"'; if (!empty($type)) $enclosure.= ' type="'.fix($type, true).'"'; if (!empty($title)) $enclosure.= ' title="'.fix($title, true).'"'; $enclosure.= ' />'."\n"; if (!$this->count) { $this->xml["feed"].= $enclosure; } else { $item = $this->count - 1; $this->xml["items"][$item].= $enclosure; } return true; } /** * Function: related * Adds a link element for a resource related to an entry or feed. * * Parameters: * $link - The URL to the resource. */ public function related( $link ): bool { if (!$this->open) return false; if (empty($link) or !is_url($link)) return false; $related = '<link rel="related" href="'. fix($link, true). '" />'. "\n"; if (!$this->count) { $this->xml["feed"].= $related; } else { $item = $this->count - 1; $this->xml["items"][$item].= $related; } return true; } /** * Function: feed * Returns the generated feed. */ public function feed(): string { $feed = $this->xml["feed"]; $items = $this->xml["items"]; foreach ($items as $item) { $feed.= '<entry>'."\n". $item. '</entry>'."\n"; } $feed.= '</feed>'."\n"; return $feed; } /** * Function: output * Displays the generated feed. */ public function display(): bool { if (headers_sent()) return false; header("Content-Type: ".self::type()."; charset=UTF-8"); echo $this->feed(); return true; } }