PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

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

http://selfpublish.googlecode.com/
PHP | 555 lines | 410 code | 60 blank | 85 comment | 46 complexity | 572fe0ec662a7545ca1a8a2be4256594 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1, AGPL-3.0, BSD-3-Clause
  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()) || ctype_digit($item->getPubDate()) ? $item->getPubDate() : 0;
  132. while(isset($feed_items[$index]))
  133. {
  134. $index++;
  135. }
  136. $feed_items[$index] = $item;
  137. }
  138. }
  139. // when specified, sort items chronologically instead of reverse
  140. if (isset($parameters['sort']) && 'chronological' == $parameters['sort'])
  141. {
  142. ksort($feed_items);
  143. }
  144. else
  145. {
  146. // default behaviour: sort in reverse chronological order
  147. krsort($feed_items);
  148. }
  149. // limit the number of feed items to be added
  150. if(isset($parameters['limit']))
  151. {
  152. $feed_items = array_slice($feed_items, 0, $parameters['limit']);
  153. }
  154. // create a feed with these items
  155. $feed = self::newInstance(isset($parameters['format']) ? $parameters['format'] : '');
  156. $feed->initialize($parameters);
  157. foreach($feed_items as $item)
  158. {
  159. $origin_feed = clone $item->getFeed();
  160. $origin_feed->setItems();
  161. $feed->addItem($item);
  162. $item->setFeed($origin_feed);
  163. }
  164. return $feed;
  165. }
  166. /**
  167. * Populates a feed with items based on objects.
  168. * Inspects the available methods of the objects to populate items properties.
  169. *
  170. * @param array an array of objects
  171. * @param string A route name for building the URIs to the items
  172. * @param array An associative array of options
  173. *
  174. * @return sfFeed the current sfFeed object
  175. */
  176. public static function convertObjectsToItems($objects, $options = array())
  177. {
  178. $items = array();
  179. foreach($objects as $object)
  180. {
  181. $item = new sfFeedItem();
  182. // For each item property, check if an object method is provided,
  183. // and if not, guess it. Here is what it does for the link property
  184. if(isset($options['methods']['link']))
  185. {
  186. if($options['methods']['link'])
  187. {
  188. $item->setLink(call_user_func(array($object, $options['methods']['link'])));
  189. }
  190. else
  191. {
  192. $item->setLink('');
  193. }
  194. }
  195. else
  196. {
  197. $routeName = (isset($options['routeName'])) ? $options['routeName'] : '';
  198. $fallbackUrl = (isset($options['fallbackUrl'])) ? $options['fallbackUrl'] : '';
  199. $item->setLink(self::getItemFeedLink($object, $routeName, $fallbackUrl));
  200. }
  201. // For the other properties, it can be automated
  202. // Not as readable but definitely more concise
  203. $details = array('title', 'description', 'content', 'authorEmail', 'authorName', 'authorLink', 'pubdate', 'comments', 'uniqueId', 'enclosure', 'categories');
  204. foreach ($details as $detail)
  205. {
  206. $itemMethod = 'set'.ucfirst($detail);
  207. if (isset($options['methods'][$detail]))
  208. {
  209. if ($options['methods'][$detail])
  210. {
  211. if (is_array($options['methods'][$detail]))
  212. {
  213. call_user_func(array($item, $itemMethod), call_user_func_array(array($object, $options['methods'][$detail][0]), $options['methods'][$detail][1]));
  214. }
  215. else
  216. {
  217. call_user_func(array($item, $itemMethod), call_user_func(array($object, $options['methods'][$detail])));
  218. }
  219. }
  220. else
  221. {
  222. call_user_func(array($item, $itemMethod), '');
  223. }
  224. }
  225. else
  226. {
  227. call_user_func(array($item, $itemMethod), call_user_func(array('sfFeedPeer', 'getItemFeed'.ucfirst($detail)), $object));
  228. }
  229. }
  230. $items[] = $item;
  231. }
  232. return $items;
  233. }
  234. /**
  235. * Creates and populates a feed with items based on objects
  236. * This is a proxy method that combines calls to newInstance() and convertObjectsToItems()
  237. *
  238. * @param array an array of objects
  239. * @param array an associative array of feed parameters
  240. *
  241. * @return sfFeed A sfFeed implementation instance, containing the parameters and populated with the objects
  242. */
  243. public static function createFromObjects($objects, $options = array())
  244. {
  245. $feed = self::newInstance(isset($options['format']) ? $options['format'] : '');
  246. $feed->initialize($options);
  247. $options['fallbackUrl'] = $feed->getLink();
  248. $feed->addItems(self::convertObjectsToItems($objects, $options));
  249. return $feed;
  250. }
  251. private static function getItemFeedTitle($item)
  252. {
  253. foreach (array('getFeedTitle', 'getTitle', 'getName', '__toString') as $methodName)
  254. {
  255. if (method_exists($item, $methodName))
  256. {
  257. return $item->$methodName();
  258. }
  259. }
  260. return '';
  261. }
  262. private static function getItemFeedLink($item, $routeName = '', $fallback_url = '')
  263. {
  264. if ($routeName)
  265. {
  266. if (method_exists('sfRouting', 'getInstance'))
  267. {
  268. $route = sfRouting::getInstance()->getRouteByName($routeName);
  269. $url = $route[0];
  270. $paramNames = $route[2];
  271. $defaults = $route[4];
  272. }
  273. else
  274. {
  275. $routes = sfContext::getInstance()->getRouting()->getRoutes();
  276. $route = $routes[substr($routeName, 1)];
  277. if($route instanceof sfRoute)
  278. {
  279. $url = $route->getPattern();
  280. $paramNames = array_keys($route->getVariables());
  281. $defaults = $route->getDefaults();
  282. }
  283. else
  284. {
  285. $url = $route[0];
  286. $paramNames = array_keys($route[2]);
  287. $defaults = $route[3];
  288. }
  289. }
  290. // we get all parameters
  291. $params = array();
  292. foreach ($paramNames as $paramName)
  293. {
  294. $value = null;
  295. $name = ucfirst(sfInflector::camelize($paramName));
  296. $found = false;
  297. foreach (array('getFeed'.$name, 'get'.$name) as $methodName)
  298. {
  299. if (method_exists($item, $methodName))
  300. {
  301. $value = $item->$methodName();
  302. $found = true;
  303. break;
  304. }
  305. }
  306. if (!$found)
  307. {
  308. if (array_key_exists($paramName, $defaults))
  309. {
  310. $value = $defaults[$paramName];
  311. }
  312. else
  313. {
  314. $error = 'Cannot find a "getFeed%s()" or "get%s()" method for object "%s" to generate URL with the "%s" route';
  315. $error = sprintf($error, $name, $name, get_class($item), $routeName);
  316. throw new sfException($error);
  317. }
  318. }
  319. $params[] = $paramName.'='.$value;
  320. }
  321. return sfContext::getInstance()->getController()->genUrl($routeName.($params ? '?'.implode('&', $params) : ''), true);
  322. }
  323. foreach (array('getFeedLink', 'getLink', 'getUrl') as $methodName)
  324. {
  325. if (method_exists($item, $methodName))
  326. {
  327. return sfContext::getInstance()->getController()->genUrl($item->$methodName(), true);
  328. }
  329. }
  330. if ($fallback_url)
  331. {
  332. return sfContext::getInstance()->getController()->genUrl($fallback_url, true);
  333. }
  334. else
  335. {
  336. return sfContext::getInstance()->getController()->genUrl('/', true);
  337. }
  338. }
  339. private static function getItemFeedDescription($item)
  340. {
  341. foreach (array('getFeedDescription', 'getDescription', 'getBody') as $methodName)
  342. {
  343. if (method_exists($item, $methodName))
  344. {
  345. return $item->$methodName();
  346. }
  347. }
  348. return '';
  349. }
  350. private static function getItemFeedContent($item)
  351. {
  352. foreach (array('getFeedContent', 'getContent', 'getHtmlBody', 'getBody') as $methodName)
  353. {
  354. if (method_exists($item, $methodName))
  355. {
  356. return $item->$methodName();
  357. }
  358. }
  359. return '';
  360. }
  361. private static function getItemFeedUniqueId($item)
  362. {
  363. foreach (array('getFeedUniqueId', 'getUniqueId', 'getId') as $methodName)
  364. {
  365. if (method_exists($item, $methodName))
  366. {
  367. return $item->$methodName();
  368. }
  369. }
  370. return '';
  371. }
  372. private static function getItemFeedAuthorEmail($item)
  373. {
  374. foreach (array('getFeedAuthorEmail', 'getAuthorEmail') as $methodName)
  375. {
  376. if (method_exists($item, $methodName))
  377. {
  378. return $item->$methodName();
  379. }
  380. }
  381. // author as an object link
  382. if ($author = self::getItemFeedAuthor($item))
  383. {
  384. foreach (array('getEmail', 'getMail') as $methodName)
  385. {
  386. if (method_exists($author, $methodName))
  387. {
  388. return $author->$methodName();
  389. }
  390. }
  391. }
  392. return '';
  393. }
  394. private static function getItemFeedAuthorName($item)
  395. {
  396. foreach (array('getFeedAuthorName', 'getAuthorName') as $methodName)
  397. {
  398. if (method_exists($item, $methodName))
  399. {
  400. return $item->$methodName();
  401. }
  402. }
  403. // author as an object link
  404. if ($author = self::getItemFeedAuthor($item))
  405. {
  406. foreach (array('getName', '__toString') as $methodName)
  407. {
  408. if (method_exists($author, $methodName))
  409. {
  410. return $author->$methodName();
  411. }
  412. }
  413. }
  414. return '';
  415. }
  416. private static function getItemFeedAuthorLink($item)
  417. {
  418. foreach (array('getFeedAuthorLink', 'getAuthorLink') as $methodName)
  419. {
  420. if (method_exists($item, $methodName))
  421. {
  422. return $item->$methodName();
  423. }
  424. }
  425. // author as an object link
  426. if ($author = self::getItemFeedAuthor($item))
  427. {
  428. foreach (array('getLink') as $methodName)
  429. {
  430. if (method_exists($author, $methodName))
  431. {
  432. return $author->$methodName();
  433. }
  434. }
  435. }
  436. return '';
  437. }
  438. private static function getItemFeedAuthor($item)
  439. {
  440. foreach (array('getAuthor', 'getUser', 'getPerson') as $methodName)
  441. {
  442. if (method_exists($item, $methodName) && is_object($item->$methodName()))
  443. {
  444. return $item->$methodName();
  445. }
  446. }
  447. return null;
  448. }
  449. private static function getItemFeedPubdate($item)
  450. {
  451. foreach (array('getFeedPubdate', 'getPubdate', 'getCreatedAt', 'getDate', 'getPublishedAt') as $methodName)
  452. {
  453. if (method_exists($item, $methodName))
  454. {
  455. return $item->$methodName('U');
  456. }
  457. }
  458. return '';
  459. }
  460. private static function getItemFeedComments($item)
  461. {
  462. foreach (array('getFeedComments', 'getComments') as $methodName)
  463. {
  464. if (method_exists($item, $methodName))
  465. {
  466. return $item->$methodName();
  467. }
  468. }
  469. return '';
  470. }
  471. private static function getItemFeedCategories($item)
  472. {
  473. foreach (array('getFeedCategories', 'getCategories') as $methodName)
  474. {
  475. if (method_exists($item, $methodName) && is_array($item->$methodName()))
  476. {
  477. $cats = array();
  478. foreach ($item->$methodName() as $category)
  479. {
  480. $cats[] = (string) $category;
  481. }
  482. return $cats;
  483. }
  484. }
  485. return array();
  486. }
  487. private static function getItemFeedEnclosure($item)
  488. {
  489. if (method_exists($item, 'getFeedEnclosure'))
  490. {
  491. return $item->getFeedEnclosure();
  492. }
  493. return '';
  494. }
  495. }