source: cpc/trunk/project/plugins/sfFeed2Plugin/lib/sfFeedPeer.class.php @ 367

Last change on this file since 367 was 367, checked in by teymour, 12 years ago

Du rss pour suivre l'activité d'un député

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