PageRenderTime 56ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/plugins/sfFeed2Plugin/lib/sfFeedPeer.class.php

https://github.com/Zadkiel/zadkiel_blog
PHP | 538 lines | 394 code | 60 blank | 84 comment | 40 complexity | c27a86aa193c31cc9d1a0bd82ee5cdb8 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the sfFeed2 package.
  4. * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com>
  5. * (c) 2004-2007 Francois Zaninotto <francois.zaninotto@symfony-project.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. /**
  11. * sfFeedPeer.
  12. *
  13. * @package sfFeed2
  14. * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  15. * @author Francois Zaninotto <francois.zaninotto@symfony-project.com>
  16. */
  17. class sfFeedPeer
  18. {
  19. /**
  20. * Retrieve a new sfFeed implementation instance.
  21. *
  22. * @param string A sfFeed implementation name
  23. *
  24. * @return sfFeed A sfFeed implementation instance
  25. *
  26. * @throws sfFactoryException If a new syndication feed implementation instance cannot be created
  27. */
  28. public static function newInstance($format = '')
  29. {
  30. try
  31. {
  32. $class = 'sf'.ucfirst($format).'Feed';
  33. // the class exists
  34. $object = new $class();
  35. if (!($object instanceof sfFeed))
  36. {
  37. // the class name is of the wrong type
  38. $error = 'Class "%s" is not of the type sfFeed';
  39. $error = sprintf($error, $class);
  40. throw new sfFactoryException($error);
  41. }
  42. return $object;
  43. }
  44. catch (sfException $e)
  45. {
  46. $e->printStackTrace();
  47. }
  48. }
  49. /**
  50. * Retrieve a new sfFeed implementation instance, populated from a web feed.
  51. * The class of the returned instance depends on the nature of the web feed.
  52. * This method uses the sfWebBrowser plugin.
  53. *
  54. * @param string A web feed URI
  55. *
  56. * @return sfFeed A sfFeed implementation instance
  57. */
  58. public static function createFromWeb($uri, $options = array())
  59. {
  60. if(isset($options['adapter']))
  61. {
  62. $browser = new sfWebBrowser(array(), $options['adapter'], isset($options['adapter_options']) ? $options['adapter_options'] : array());
  63. }
  64. else
  65. {
  66. $browser = new sfWebBrowser();
  67. }
  68. $browser->setUserAgent(isset($options['userAgent']) ? $options['userAgent'] : 'sfFeedReader/0.9');
  69. if($browser->get($uri)->responseIsError())
  70. {
  71. $error = 'The given URL (%s) returns an error (%s: %s)';
  72. $error = sprintf($error, $uri, $browser->getResponseCode(), $browser->getResponseMessage());
  73. throw new Exception($error);
  74. }
  75. $feedString = $browser->getResponseText();
  76. return self::createFromXml($feedString, $uri);
  77. }
  78. /**
  79. * Retrieve a new sfFeed implementation instance, populated from a xml feed.
  80. * The class of the returned instance depends on the nature of the xml feed.
  81. *
  82. * @param string $feedString a feed as xml string
  83. * @param string A web feed URI
  84. *
  85. * @return sfFeed A sfFeed implementation instance
  86. */
  87. public static function createFromXml($feedString, $uri)
  88. {
  89. $feedClass = '';
  90. if(preg_match('/xmlns=[\"\'](http:\/\/www\.w3\.org\/2005\/Atom|http:\/\/purl\.org\/atom)/', $feedString))
  91. {
  92. $feedClass = 'sfAtom1Feed';
  93. }
  94. if(strpos($feedString, '<rss') !== false)
  95. {
  96. $feedClass = 'sfRssFeed';
  97. }
  98. if(strpos($feedString, '<rdf:RDF') !== false)
  99. {
  100. $feedClass = 'sfRss10Feed';
  101. }
  102. if($feedClass)
  103. {
  104. $object = new $feedClass();
  105. $object->setFeedUrl($uri);
  106. $object->fromXml($feedString);
  107. return $object;
  108. }
  109. else
  110. {
  111. throw new Exception('Impossible to decode feed format');
  112. }
  113. }
  114. /**
  115. * Merge the items from several feeds and retrieve a sfFeed instance.
  116. * Populated with all the items, and sorted.
  117. *
  118. * @param array an array of sfFeed objects
  119. * @param array an associative array of feed parameters
  120. *
  121. * @return sfFeed A sfFeed implementation instance
  122. */
  123. public static function aggregate($feeds, $parameters = array())
  124. {
  125. // merge all items
  126. $feed_items = array();
  127. foreach($feeds as $feed)
  128. {
  129. foreach($feed->getItems() as $item)
  130. {
  131. $index = is_integer($item->getPubDate()) ? $item->getPubDate() : 0;
  132. while(isset($feed_items[$index]))
  133. {
  134. $index++;
  135. }
  136. $feed_items[$index] = $item;
  137. }
  138. }
  139. // sort in reverse chronological order
  140. krsort($feed_items);
  141. // limit the number of feed items to be added
  142. if(isset($parameters['limit']))
  143. {
  144. $feed_items = array_slice($feed_items, 0, $parameters['limit']);
  145. }
  146. // create a feed with these items
  147. $feed = self::newInstance(isset($parameters['format']) ? $parameters['format'] : '');
  148. $feed->initialize($parameters);
  149. foreach($feed_items as $item)
  150. {
  151. $origin_feed = clone $item->getFeed();
  152. $origin_feed->setItems();
  153. $feed->addItem($item);
  154. $item->setFeed($origin_feed);
  155. }
  156. return $feed;
  157. }
  158. /**
  159. * Populates a feed with items based on objects.
  160. * Inspects the available methods of the objects to populate items properties.
  161. *
  162. * @param array an array of objects
  163. * @param string A route name for building the URIs to the items
  164. * @param array An associative array of options
  165. *
  166. * @return sfFeed the current sfFeed object
  167. */
  168. public static function convertObjectsToItems($objects, $options = array())
  169. {
  170. $items = array();
  171. foreach($objects as $object)
  172. {
  173. $item = new sfFeedItem();
  174. // For each item property, check if an object method is provided,
  175. // and if not, guess it. Here is what it does for the link property
  176. if(isset($options['methods']['link']))
  177. {
  178. if($options['methods']['link'])
  179. {
  180. $item->setLink(call_user_func(array($object, $options['methods']['link'])));
  181. }
  182. else
  183. {
  184. $item->setLink('');
  185. }
  186. }
  187. else
  188. {
  189. $routeName = (isset($options['routeName'])) ? $options['routeName'] : '';
  190. $fallbackUrl = (isset($options['fallbackUrl'])) ? $options['fallbackUrl'] : '';
  191. $item->setLink(self::getItemFeedLink($object, $routeName, $fallbackUrl));
  192. }
  193. // For the other properties, it can be automated
  194. // Not as readable but definitely more concise
  195. $details = array('title', 'description', 'content', 'authorEmail', 'authorName', 'authorLink', 'pubdate', 'comments', 'uniqueId', 'enclosure', 'categories');
  196. foreach ($details as $detail)
  197. {
  198. $itemMethod = 'set'.ucfirst($detail);
  199. if (isset($options['methods'][$detail]))
  200. {
  201. if ($options['methods'][$detail])
  202. {
  203. if (is_array($options['methods'][$detail]))
  204. {
  205. call_user_func(array($item, $itemMethod), call_user_func_array(array($object, $options['methods'][$detail][0]), $options['methods'][$detail][1]));
  206. }
  207. else
  208. {
  209. call_user_func(array($item, $itemMethod), call_user_func(array($object, $options['methods'][$detail])));
  210. }
  211. }
  212. else
  213. {
  214. call_user_func(array($item, $itemMethod), '');
  215. }
  216. }
  217. else
  218. {
  219. call_user_func(array($item, $itemMethod), call_user_func(array('sfFeedPeer', 'getItemFeed'.ucfirst($detail)), $object));
  220. }
  221. }
  222. $items[] = $item;
  223. }
  224. return $items;
  225. }
  226. /**
  227. * Creates and populates a feed with items based on objects
  228. * This is a proxy method that combines calls to newInstance() and convertObjectsToItems()
  229. *
  230. * @param array an array of objects
  231. * @param array an associative array of feed parameters
  232. *
  233. * @return sfFeed A sfFeed implementation instance, containing the parameters and populated with the objects
  234. */
  235. public static function createFromObjects($objects, $options = array())
  236. {
  237. $feed = self::newInstance(isset($options['format']) ? $options['format'] : '');
  238. $feed->initialize($options);
  239. $options['fallbackUrl'] = $feed->getLink();
  240. $feed->addItems(self::convertObjectsToItems($objects, $options));
  241. return $feed;
  242. }
  243. private static function getItemFeedTitle($item)
  244. {
  245. foreach (array('getFeedTitle', 'getTitle', 'getName', '__toString') as $methodName)
  246. {
  247. if (method_exists($item, $methodName))
  248. {
  249. return $item->$methodName();
  250. }
  251. }
  252. return '';
  253. }
  254. private static function getItemFeedLink($item, $routeName = '', $fallback_url = '')
  255. {
  256. if ($routeName)
  257. {
  258. if (method_exists('sfRouting', 'getInstance'))
  259. {
  260. $route = sfRouting::getInstance()->getRouteByName($routeName);
  261. $url = $route[0];
  262. $paramNames = $route[2];
  263. $defaults = $route[4];
  264. }
  265. else
  266. {
  267. $routes = sfContext::getInstance()->getRouting()->getRoutes();
  268. $route = $routes[substr($routeName, 1)];
  269. $url = $route[0];
  270. $paramNames = array_keys($route[2]);
  271. $defaults = $route[3];
  272. }
  273. // we get all parameters
  274. $params = array();
  275. foreach ($paramNames as $paramName)
  276. {
  277. $value = null;
  278. $name = ucfirst(sfInflector::camelize($paramName));
  279. $found = false;
  280. foreach (array('getFeed'.$name, 'get'.$name) as $methodName)
  281. {
  282. if (method_exists($item, $methodName))
  283. {
  284. $value = $item->$methodName();
  285. $found = true;
  286. break;
  287. }
  288. }
  289. if (!$found)
  290. {
  291. if (array_key_exists($paramName, $defaults))
  292. {
  293. $value = $defaults[$paramName];
  294. }
  295. else
  296. {
  297. $error = 'Cannot find a "getFeed%s()" or "get%s()" method for object "%s" to generate URL with the "%s" route';
  298. $error = sprintf($error, $name, $name, get_class($item), $routeName);
  299. throw new sfException($error);
  300. }
  301. }
  302. $params[] = $paramName.'='.$value;
  303. }
  304. return sfContext::getInstance()->getController()->genUrl($routeName.($params ? '?'.implode('&', $params) : ''), true);
  305. }
  306. foreach (array('getFeedLink', 'getLink', 'getUrl') as $methodName)
  307. {
  308. if (method_exists($item, $methodName))
  309. {
  310. return sfContext::getInstance()->getController()->genUrl($item->$methodName(), true);
  311. }
  312. }
  313. if ($fallback_url)
  314. {
  315. return sfContext::getInstance()->getController()->genUrl($fallback_url, true);
  316. }
  317. else
  318. {
  319. return sfContext::getInstance()->getController()->genUrl('/', true);
  320. }
  321. }
  322. private static function getItemFeedDescription($item)
  323. {
  324. foreach (array('getFeedDescription', 'getDescription', 'getBody') as $methodName)
  325. {
  326. if (method_exists($item, $methodName))
  327. {
  328. return $item->$methodName();
  329. }
  330. }
  331. return '';
  332. }
  333. private static function getItemFeedContent($item)
  334. {
  335. foreach (array('getFeedContent', 'getContent', 'getHtmlBody', 'getBody') as $methodName)
  336. {
  337. if (method_exists($item, $methodName))
  338. {
  339. return $item->$methodName();
  340. }
  341. }
  342. return '';
  343. }
  344. private static function getItemFeedUniqueId($item)
  345. {
  346. foreach (array('getFeedUniqueId', 'getUniqueId', 'getId') as $methodName)
  347. {
  348. if (method_exists($item, $methodName))
  349. {
  350. return $item->$methodName();
  351. }
  352. }
  353. return '';
  354. }
  355. private static function getItemFeedAuthorEmail($item)
  356. {
  357. foreach (array('getFeedAuthorEmail', 'getAuthorEmail') as $methodName)
  358. {
  359. if (method_exists($item, $methodName))
  360. {
  361. return $item->$methodName();
  362. }
  363. }
  364. // author as an object link
  365. if ($author = self::getItemFeedAuthor($item))
  366. {
  367. foreach (array('getEmail', 'getMail') as $methodName)
  368. {
  369. if (method_exists($author, $methodName))
  370. {
  371. return $author->$methodName();
  372. }
  373. }
  374. }
  375. return '';
  376. }
  377. private static function getItemFeedAuthorName($item)
  378. {
  379. foreach (array('getFeedAuthorName', 'getAuthorName') as $methodName)
  380. {
  381. if (method_exists($item, $methodName))
  382. {
  383. return $item->$methodName();
  384. }
  385. }
  386. // author as an object link
  387. if ($author = self::getItemFeedAuthor($item))
  388. {
  389. foreach (array('getName', '__toString') as $methodName)
  390. {
  391. if (method_exists($author, $methodName))
  392. {
  393. return $author->$methodName();
  394. }
  395. }
  396. }
  397. return '';
  398. }
  399. private static function getItemFeedAuthorLink($item)
  400. {
  401. foreach (array('getFeedAuthorLink', 'getAuthorLink') as $methodName)
  402. {
  403. if (method_exists($item, $methodName))
  404. {
  405. return $item->$methodName();
  406. }
  407. }
  408. // author as an object link
  409. if ($author = self::getItemFeedAuthor($item))
  410. {
  411. foreach (array('getLink') as $methodName)
  412. {
  413. if (method_exists($author, $methodName))
  414. {
  415. return $author->$methodName();
  416. }
  417. }
  418. }
  419. return '';
  420. }
  421. private static function getItemFeedAuthor($item)
  422. {
  423. foreach (array('getAuthor', 'getUser', 'getPerson') as $methodName)
  424. {
  425. if (method_exists($item, $methodName) && is_object($item->$methodName()))
  426. {
  427. return $item->$methodName();
  428. }
  429. }
  430. return null;
  431. }
  432. private static function getItemFeedPubdate($item)
  433. {
  434. foreach (array('getFeedPubdate', 'getPubdate', 'getCreatedAt', 'getDate', 'getPublishedAt') as $methodName)
  435. {
  436. if (method_exists($item, $methodName))
  437. {
  438. return $item->$methodName('U');
  439. }
  440. }
  441. return '';
  442. }
  443. private static function getItemFeedComments($item)
  444. {
  445. foreach (array('getFeedComments', 'getComments') as $methodName)
  446. {
  447. if (method_exists($item, $methodName))
  448. {
  449. return $item->$methodName();
  450. }
  451. }
  452. return '';
  453. }
  454. private static function getItemFeedCategories($item)
  455. {
  456. foreach (array('getFeedCategories', 'getCategories') as $methodName)
  457. {
  458. if (method_exists($item, $methodName) && is_array($item->$methodName()))
  459. {
  460. $cats = array();
  461. foreach ($item->$methodName() as $category)
  462. {
  463. $cats[] = (string) $category;
  464. }
  465. return $cats;
  466. }
  467. }
  468. return array();
  469. }
  470. private static function getItemFeedEnclosure($item)
  471. {
  472. if (method_exists($item, 'getFeedEnclosure'))
  473. {
  474. return $item->getFeedEnclosure();
  475. }
  476. return '';
  477. }
  478. }