* Fixed bug in HttpRequestPool where a negative timeout was passed to select()
[m6w6/ext-http] / lib / FeedAggregator.php
1 <?php
2
3 /**
4 * Simple Feed Aggregator
5 * $Id$
6 *
7 * @copyright Michael Wallner, <mike@iworks.at>
8 * @license BSD, revised
9 * @package pecl/http
10 * @version $Revision$
11 */
12 class FeedAggregator
13 {
14 /**
15 * Cache directory
16 *
17 * @var string
18 */
19 public $directory;
20
21 /**
22 * Feeds
23 *
24 * @var array
25 */
26 protected $feeds = array();
27
28 /**
29 * Constructor
30 *
31 * @param string $directory
32 */
33 public function __construct($directory = 'feeds')
34 {
35 $this->setDirectory($directory);
36 }
37
38 /**
39 * Set cache directory
40 *
41 * @param string $directory
42 */
43 public function setDirectory($directory)
44 {
45 $this->directory = $directory;
46 foreach (glob($this->directory .'/*.xml') as $feed) {
47 $this->feeds[basename($feed, '.xml')] = filemtime($feed);
48 }
49 }
50
51 /**
52 * Strips all special chars
53 *
54 * @param string $url
55 * @return string
56 */
57 public function url2name($url)
58 {
59 return preg_replace('/[^\w\.-]+/', '_', $url);
60 }
61
62 /**
63 * Checks if $url is a known feed
64 *
65 * @param string $url
66 * @return bool
67 */
68 public function hasFeed($url)
69 {
70 return isset($this->feeds[$this->url2name($url)]);
71 }
72
73 /**
74 * Add an URL as feed
75 *
76 * @param string $url
77 * @return void
78 * @throws Exception
79 */
80 public function addFeed($url)
81 {
82 $r = $this->setupRequest($url);
83 $r->send();
84 $this->handleResponse($r);
85 }
86
87 /**
88 * Add several URLs as feeds
89 *
90 * @param array $urls
91 * @return void
92 * @throws Exception
93 */
94 public function addFeeds(array $urls)
95 {
96 $pool = new HttpRequestPool;
97 foreach ($urls as $url) {
98 $pool->attach($r = $this->setupRequest($url));
99 }
100 $pool->send();
101
102 foreach ($pool as $request) {
103 $this->handleResponse($request);
104 }
105 }
106
107 /**
108 * Load a feed (from cache)
109 *
110 * @param string $url
111 * @return string
112 * @throws Exception
113 */
114 public function getFeed($url)
115 {
116 $this->addFeed($url);
117 return $this->loadFeed($this->url2name($url));
118 }
119
120 /**
121 * Load several feeds (from cache)
122 *
123 * @param array $urls
124 * @return array
125 * @throws Exception
126 */
127 public function getFeeds(array $urls)
128 {
129 $feeds = array();
130 $this->addFeeds($urls);
131 foreach ($urls as $url) {
132 $feeds[] = $this->loadFeed($this->url2name($url));
133 }
134 return $feeds;
135 }
136
137 protected function saveFeed($file, $contents)
138 {
139 if (file_put_contents($this->directory .'/'. $file .'.xml', $contents)) {
140 $this->feeds[$file] = time();
141 } else {
142 throw new Exception("Could not save feed contents to $file.xml");
143 }
144 }
145
146 protected function loadFeed($file)
147 {
148 if (isset($this->feeds[$file])) {
149 if ($data = file_get_contents($this->directory .'/'. $file .'.xml')) {
150 return $data;
151 } else {
152 throw new Exception("Could not load feed contents from $file.xml");
153 }
154 } else {
155 throw new Exception("Unknown feed/file $file.xml");
156 }
157 }
158
159 protected function setupRequest($url, $escape = true)
160 {
161 $r = new HttpRequest($url);
162 $r->setOptions(array('redirect' => true));
163
164 $file = $escape ? $this->url2name($url) : $url;
165
166 if (isset($this->feeds[$file])) {
167 $r->setOptions(array('lastmodified' => $this->feeds[$file]));
168 }
169
170 return $r;
171 }
172
173 protected function handleResponse(HttpRequest $r)
174 {
175 if ($r->getResponseCode() != 304) {
176 if ($r->getResponseCode() != 200) {
177 throw new Exception("Unexpected response code ". $r->getResponseCode());
178 }
179 if (!strlen($body = $r->getResponseBody())) {
180 throw new Exception("Received empty feed from ". $r->getUrl());
181 }
182 $this->saveFeed($this->url2name($r->getUrl()), $body);
183 }
184 }
185 }
186
187 ?>