PHP Universal Feed Generator
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

374 lines
11 KiB

  1. <?php
  2. namespace FeedWriter;
  3. use \DateTime;
  4. /*
  5. * Copyright (C) 2008 Anis uddin Ahmad <anisniit@gmail.com>
  6. * Copyright (C) 2010-2013 Michael Bemmerl <mail@mx-server.de>
  7. *
  8. * This file is part of the "Universal Feed Writer" project.
  9. *
  10. * This program is free software: you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation, either version 3 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. */
  23. /**
  24. * Universal Feed Writer
  25. *
  26. * Item class - Used as feed element in Feed class
  27. *
  28. * @package UniversalFeedWriter
  29. * @author Anis uddin Ahmad <anisniit@gmail.com>
  30. * @link http://www.ajaxray.com/projects/rss
  31. */
  32. class Item
  33. {
  34. /**
  35. * Collection of feed item elements
  36. */
  37. private $elements = array();
  38. /**
  39. * Contains the format of this feed.
  40. */
  41. private $version;
  42. /**
  43. * Is used as a suffix when multiple elements have the same name.
  44. **/
  45. private $_cpt = 0;
  46. /**
  47. * Constructor
  48. *
  49. * @param constant (RSS1/RSS2/ATOM) RSS2 is default.
  50. */
  51. public function __construct($version = Feed::RSS2)
  52. {
  53. $this->version = $version;
  54. }
  55. /**
  56. * Return an unique number
  57. *
  58. * @access private
  59. * @return int
  60. **/
  61. private function cpt()
  62. {
  63. return $this->_cpt++;
  64. }
  65. /**
  66. * Add an element to elements array
  67. *
  68. * @access public
  69. * @param string The tag name of an element
  70. * @param string The content of tag
  71. * @param array Attributes (if any) in 'attrName' => 'attrValue' format
  72. * @param boolean Specifies if an already existing element is overwritten.
  73. * @param boolean Specifies if multiple elements of the same name are allowed.
  74. * @return self
  75. */
  76. public function addElement($elementName, $content, $attributes = null, $overwrite = FALSE, $allowMultiple = FALSE)
  77. {
  78. $key = $elementName;
  79. // return if element already exists & if overwriting is disabled
  80. // & if multiple elements are not allowed.
  81. if (isset($this->elements[$elementName]) && !$overwrite) {
  82. if (!$allowMultiple)
  83. return;
  84. $key .= '-' . $this->cpt();
  85. }
  86. $this->elements[$key]['name'] = $elementName;
  87. $this->elements[$key]['content'] = $content;
  88. $this->elements[$key]['attributes'] = $attributes;
  89. return $this;
  90. }
  91. /**
  92. * Set multiple feed elements from an array.
  93. * Elements which have attributes cannot be added by this method
  94. *
  95. * @access public
  96. * @param array array of elements in 'tagName' => 'tagContent' format.
  97. * @return self
  98. */
  99. public function addElementArray($elementArray)
  100. {
  101. if (!is_array($elementArray))
  102. return;
  103. foreach ($elementArray as $elementName => $content) {
  104. $this->addElement($elementName, $content);
  105. }
  106. return $this;
  107. }
  108. /**
  109. * Return the collection of elements in this feed item
  110. *
  111. * @access public
  112. * @return array All elements of this item.
  113. */
  114. public function getElements()
  115. {
  116. return $this->elements;
  117. }
  118. /**
  119. * Return the type of this feed item
  120. *
  121. * @access public
  122. * @return string The feed type, as defined in Feed.php
  123. */
  124. public function getVersion()
  125. {
  126. return $this->version;
  127. }
  128. // Wrapper functions ------------------------------------------------------
  129. /**
  130. * Set the 'description' element of feed item
  131. *
  132. * @access public
  133. * @param string The content of 'description' or 'summary' element
  134. * @return self
  135. */
  136. public function setDescription($description)
  137. {
  138. $tag = ($this->version == Feed::ATOM) ? 'summary' : 'description';
  139. return $this->addElement($tag, $description);
  140. }
  141. /**
  142. * Set the 'content' element of the feed item
  143. * For ATOM feeds only
  144. *
  145. * @access public
  146. * @param string Content for the item (i.e., the body of a blog post).
  147. * @return self
  148. */
  149. public function setContent($content)
  150. {
  151. if ($this->version != Feed::ATOM)
  152. die('The content element is supported in ATOM feeds only.');
  153. return $this->addElement('content', $content, array('type' => 'html'));
  154. }
  155. /**
  156. * Set the 'title' element of feed item
  157. *
  158. * @access public
  159. * @param string The content of 'title' element
  160. * @return self
  161. */
  162. public function setTitle($title)
  163. {
  164. return $this->addElement('title', $title);
  165. }
  166. /**
  167. * Set the 'date' element of the feed item.
  168. *
  169. * The value of the date parameter can be either an instance of the
  170. * DateTime class, an integer containing a UNIX timestamp or a string
  171. * which is parseable by PHP's 'strtotime' function.
  172. *
  173. * @access public
  174. * @param DateTime|int|string Date which should be used.
  175. * @return self
  176. */
  177. public function setDate($date)
  178. {
  179. if (!is_numeric($date)) {
  180. if ($date instanceof DateTime)
  181. $date = $date->getTimestamp();
  182. else {
  183. $date = strtotime($date);
  184. if ($date === FALSE)
  185. die('The given date string was not parseable.');
  186. }
  187. } elseif ($date < 0)
  188. die('The given date is not an UNIX timestamp.');
  189. if ($this->version == Feed::ATOM) {
  190. $tag = 'updated';
  191. $value = date(\DATE_ATOM, $date);
  192. } elseif ($this->version == Feed::RSS2) {
  193. $tag = 'pubDate';
  194. $value = date(\DATE_RSS, $date);
  195. } else {
  196. $tag = 'dc:date';
  197. $value = date("Y-m-d", $date);
  198. }
  199. return $this->addElement($tag, $value);
  200. }
  201. /**
  202. * Set the 'link' element of feed item
  203. *
  204. * @access public
  205. * @param string The content of 'link' element
  206. * @return void
  207. */
  208. public function setLink($link)
  209. {
  210. if ($this->version == Feed::RSS2 || $this->version == Feed::RSS1) {
  211. $this->addElement('link', $link);
  212. } else {
  213. $this->addElement('link','',array('href'=>$link));
  214. $this->addElement('id', Feed::uuid($link,'urn:uuid:'));
  215. }
  216. return $this;
  217. }
  218. /**
  219. * Attach a external media to the feed item.
  220. * Not supported in RSS 1.0 feeds.
  221. *
  222. * See RFC 4288 for syntactical correct MIME types.
  223. *
  224. * Note that you should avoid the use of more than one enclosure in one item,
  225. * since some RSS aggregators don't support it.
  226. *
  227. * @access public
  228. * @param string The URL of the media.
  229. * @param integer The length of the media.
  230. * @param string The MIME type attribute of the media.
  231. * @param boolean Specifies, if multiple enclosures are allowed
  232. * @return self
  233. * @link https://tools.ietf.org/html/rfc4288
  234. */
  235. public function addEnclosure($url, $length, $type, $multiple = TRUE)
  236. {
  237. if ($this->version == Feed::RSS1)
  238. die('Media attachment is not supported in RSS1 feeds.');
  239. // the length parameter should be set to 0 if it can't be determined
  240. // see http://www.rssboard.org/rss-profile#element-channel-item-enclosure
  241. if (!is_numeric($length) || $length < 0)
  242. die('The length parameter must be an integer and greater or equals to zero.');
  243. // Regex used from RFC 4287, page 41
  244. if (!is_string($type) || preg_match('/.+\/.+/', $type) != 1)
  245. die('type parameter must be a string and a MIME type.');
  246. $attributes = array('length' => $length, 'type' => $type);
  247. if ($this->version == Feed::RSS2) {
  248. $attributes['url'] = $url;
  249. $this->addElement('enclosure', '', $attributes, FALSE, $multiple);
  250. } else {
  251. $attributes['href'] = $url;
  252. $attributes['rel'] = 'enclosure';
  253. $this->addElement('atom:link', '', $attributes, FALSE, $multiple);
  254. }
  255. return $this;
  256. }
  257. /**
  258. * Alias of addEnclosure, for backward compatibility. Using only this
  259. * method ensures that the 'enclosure' element will be present only once.
  260. *
  261. * @access public
  262. * @param string The URL of the media.
  263. * @param integer The length of the media.
  264. * @param string The MIME type attribute of the media.
  265. * @return self
  266. * @link https://tools.ietf.org/html/rfc4288
  267. * @deprecated Use the addEnclosure method instead.
  268. *
  269. **/
  270. public function setEnclosure($url, $length, $type)
  271. {
  272. return $this->addEnclosure($url, $length, $type, false);
  273. }
  274. /**
  275. * Set the 'author' element of feed item.
  276. * Not supported in RSS 1.0 feeds.
  277. *
  278. * @access public
  279. * @param string The author of this item
  280. * @param string Optional email address of the author
  281. * @param string Optional URI related to the author
  282. * @return self
  283. */
  284. public function setAuthor($author, $email = null, $uri = null)
  285. {
  286. switch ($this->version) {
  287. case Feed::RSS1: die('The author element is not supported in RSS1 feeds.');
  288. break;
  289. case Feed::RSS2:
  290. if ($email != null)
  291. $author = $email . ' (' . $author . ')';
  292. $this->addElement('author', $author);
  293. break;
  294. case Feed::ATOM:
  295. $elements = array('name' => $author);
  296. // Regex from RFC 4287 page 41
  297. if ($email != null && preg_match('/.+@.+/', $email) == 1)
  298. $elements['email'] = $email;
  299. if ($uri != null)
  300. $elements['uri'] = $uri;
  301. $this->addElement('author', $elements);
  302. break;
  303. }
  304. return $this;
  305. }
  306. /**
  307. * Set the unique identifier of the feed item
  308. *
  309. * @access public
  310. * @param string The unique identifier of this item
  311. * @param boolean The value of the 'isPermaLink' attribute in RSS 2 feeds.
  312. * @return self
  313. */
  314. public function setId($id, $permaLink = false)
  315. {
  316. if ($this->version == Feed::RSS2) {
  317. if (!is_bool($permaLink))
  318. die('The permaLink parameter must be boolean.');
  319. $permaLink = $permaLink ? 'true' : 'false';
  320. $this->addElement('guid', $id, array('isPermaLink' => $permaLink));
  321. } elseif ($this->version == Feed::ATOM) {
  322. $this->addElement('id', Feed::uuid($id,'urn:uuid:'), NULL, TRUE);
  323. } else
  324. die('A unique ID is not supported in RSS1 feeds.');
  325. return $this;
  326. }
  327. } // end of class Item