Feed.php 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. <?php
  2. /**
  3. * RSS and Atom feed helper.
  4. *
  5. * @package KO7
  6. * @category Helpers
  7. *
  8. * @copyright (c) 2007-2016 Kohana Team
  9. * @copyright (c) since 2016 Koseven Team
  10. * @license https://koseven.dev/LICENSE
  11. */
  12. class KO7_Feed {
  13. /**
  14. * Parses a remote feed into an array.
  15. *
  16. * @param string $feed remote feed URL
  17. * @param integer $limit item limit to fetch
  18. * @return array
  19. */
  20. public static function parse($feed, $limit = 0)
  21. {
  22. // Check if SimpleXML is installed
  23. if ( ! function_exists('simplexml_load_file'))
  24. throw new KO7_Exception('SimpleXML must be installed!');
  25. // Make limit an integer
  26. $limit = (int) $limit;
  27. // Disable error reporting while opening the feed
  28. $error_level = error_reporting(0);
  29. // Allow loading by filename or raw XML string
  30. if (Valid::url($feed))
  31. {
  32. // Use native Request client to get remote contents
  33. $response = Request::factory($feed)->execute();
  34. $feed = $response->body();
  35. }
  36. elseif (is_file($feed))
  37. {
  38. // Get file contents
  39. $feed = file_get_contents($feed);
  40. }
  41. // Load the feed
  42. $feed = simplexml_load_string($feed, 'SimpleXMLElement', LIBXML_NOCDATA);
  43. // Restore error reporting
  44. error_reporting($error_level);
  45. // Feed could not be loaded
  46. if ($feed === FALSE)
  47. return [];
  48. $namespaces = $feed->getNamespaces(TRUE);
  49. // Detect the feed type. RSS 1.0/2.0 and Atom 1.0 are supported.
  50. $feed = isset($feed->channel) ? $feed->xpath('//item') : $feed->entry;
  51. $i = 0;
  52. $items = [];
  53. foreach ($feed as $item)
  54. {
  55. if ($limit > 0 AND $i++ === $limit)
  56. break;
  57. $item_fields = (array) $item;
  58. // get namespaced tags
  59. foreach ($namespaces as $ns)
  60. {
  61. $item_fields += (array) $item->children($ns);
  62. }
  63. $items[] = $item_fields;
  64. }
  65. return $items;
  66. }
  67. /**
  68. * Creates a feed from the given parameters.
  69. *
  70. * @param array $info feed information
  71. * @param array $items items to add to the feed
  72. * @param string $encoding define which encoding to use
  73. * @return string
  74. */
  75. public static function create($info, $items, $encoding = 'UTF-8')
  76. {
  77. $info += ['title' => 'Generated Feed', 'link' => '', 'generator' => 'KO7PHP'];
  78. $feed = '<?xml version="1.0" encoding="'.$encoding.'"?><rss version="2.0"><channel></channel></rss>';
  79. $feed = simplexml_load_string($feed);
  80. foreach ($info as $name => $value)
  81. {
  82. if ($name === 'image')
  83. {
  84. // Create an image element
  85. $image = $feed->channel->addChild('image');
  86. if ( ! isset($value['link'], $value['url'], $value['title']))
  87. {
  88. throw new KO7_Exception('Feed images require a link, url, and title');
  89. }
  90. if (strpos($value['link'], '://') === FALSE)
  91. {
  92. // Convert URIs to URLs
  93. $value['link'] = URL::site($value['link'], 'http');
  94. }
  95. if (strpos($value['url'], '://') === FALSE)
  96. {
  97. // Convert URIs to URLs
  98. $value['url'] = URL::site($value['url'], 'http');
  99. }
  100. // Create the image elements
  101. $image->addChild('link', $value['link']);
  102. $image->addChild('url', $value['url']);
  103. $image->addChild('title', $value['title']);
  104. }
  105. else
  106. {
  107. if (($name === 'pubDate' OR $name === 'lastBuildDate') AND (is_int($value) OR ctype_digit($value)))
  108. {
  109. // Convert timestamps to RFC 822 formatted dates
  110. $value = date('r', $value);
  111. }
  112. elseif (($name === 'link' OR $name === 'docs') AND strpos($value, '://') === FALSE)
  113. {
  114. // Convert URIs to URLs
  115. $value = URL::site($value, 'http');
  116. }
  117. // Add the info to the channel
  118. $feed->channel->addChild($name, $value);
  119. }
  120. }
  121. foreach ($items as $item)
  122. {
  123. // Add the item to the channel
  124. $row = $feed->channel->addChild('item');
  125. foreach ($item as $name => $value)
  126. {
  127. if ($name === 'pubDate' AND (is_int($value) OR ctype_digit($value)))
  128. {
  129. // Convert timestamps to RFC 822 formatted dates
  130. $value = date('r', $value);
  131. }
  132. elseif (($name === 'link' OR $name === 'guid') AND strpos($value, '://') === FALSE)
  133. {
  134. // Convert URIs to URLs
  135. $value = URL::site($value, 'http');
  136. }
  137. // Add the info to the row
  138. $row->addChild($name, $value);
  139. }
  140. }
  141. if (function_exists('dom_import_simplexml'))
  142. {
  143. // Convert the feed object to a DOM object
  144. $feed = dom_import_simplexml($feed)->ownerDocument;
  145. // DOM generates more readable XML
  146. $feed->formatOutput = TRUE;
  147. // Export the document as XML
  148. $feed = $feed->saveXML();
  149. }
  150. else
  151. {
  152. // Export the document as XML
  153. $feed = $feed->asXML();
  154. }
  155. return $feed;
  156. }
  157. }