Feed.php 4.4 KB

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