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