PluginProbe
FeedWordPress / 2012.1212
FeedWordPress v2012.1212
trunk 0.8 0.9 0.91 0.95 0.96 0.97 0.98 0.981 0.99 0.991 0.992 0.993 2008.1030 2008.1101 2008.1105 2008.1214 2009.0612 2009.0613 2009.0618 2009.0707 2009.1111 2009.1112 2010.0127 2010.0528 All 65 releases
feedwordpress / feedfinder.class.php

feedfinder.class.php in FeedWordPress 2012.1212, at feedfinder.class.php

366 lines 10.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * class FeedFinder: find likely feeds using autodetection and/or guesswork
4 * @version 2010.0622
5 * @uses SimplePie_Misc
6 */
7
8 if (!class_exists('SimplePie')) :
9 require_once(ABSPATH . WPINC . '/class-simplepie.php');
10 endif;
11 require_once(dirname(__FILE__).'/feedwordpresshtml.class.php');
12
13 class FeedFinder {
14 var $uri = NULL;
15 var $credentials = NULL;
16 var $_cache_uri = NULL;
17
18 var $verify = FALSE;
19 var $fallbacks = 3;
20
21 var $_response = NULL;
22 var $_data = NULL;
23 var $_error = NULL;
24 var $_head = NULL;
25
26 # -- Recognition patterns
27 var $_feed_types = array(
28 'application/rss+xml',
29 'text/xml',
30 'application/atom+xml',
31 'application/x.atom+xml',
32 'application/x-atom+xml'
33 );
34 var $_feed_markers = array('\\<feed', '\\<rss', 'xmlns="http://purl.org/rss/1.0');
35 var $_html_markers = array('\\<html');
36 var $_opml_markers = array('\\<opml', '\\<outline');
37 var $_obvious_feed_url = array('[./]rss', '[./]rdf', '[./]atom', '[./]feed', '\.xml');
38 var $_maybe_feed_url = array ('rss', 'rdf', 'atom', 'feed', 'xml');
39
40 function FeedFinder ($uri = NULL, $params = array(), $fallbacks = 3) {
41 if (is_bool($params)) :
42 $params = array("verify" => $params);
43 endif;
44
45 $params = wp_parse_args($params, array(
46 "verify" => true,
47 "authentication" => NULL,
48 "username" => NULL,
49 "password" => NULL,
50 ));
51 $verify = $params['verify'];
52 $this->credentials = array(
53 "authentication" => $params['authentication'],
54 "username" => $params['username'],
55 "password" => $params['password'],
56 );
57
58 $this->uri = $uri; $this->verify = $verify;
59 $this->fallbacks = $fallbacks;
60 } /* FeedFinder::FeedFinder () */
61
62 function find ($uri = NULL, $params = array()) {
63 $params = wp_parse_args($params, array( // Defaults
64 "authentication" => -1,
65 "username" => NULL,
66 "password" => NULL,
67 ));
68
69 // Equivalents
70 if ($params['authentication']=='-') :
71 $params['authentication'] = NULL;
72 $params['username'] = NULL;
73 $params['password'] = NULL;
74 endif;
75
76 // Set/reset
77 if ($params['authentication'] != -1) :
78 $this->credentials = array(
79 "authentication" => $params['authentication'],
80 "username" => $params['username'],
81 "password" => $params['password'],
82 );
83 endif;
84
85 $ret = array ();
86 if (!is_null($this->data($uri))) :
87 if ($this->is_opml($uri)) :
88 $href = $this->_opml_rss_uris();
89 else :
90 if ($this->is_feed($uri)) :
91 $href = array($this->uri);
92 else :
93 // Assume that we have HTML or XHTML (even if we don't, who's
94 // it gonna hurt?) Autodiscovery is the preferred method.
95 $href = $this->_link_rel_feeds();
96
97 // ... but we'll also take the little orange buttons
98 if ($this->fallbacks > 0) :
99 $href = array_merge($href, $this->_a_href_feeds(TRUE));
100 endif;
101
102 // If all that failed, look harder
103 if ($this->fallbacks > 1) :
104 if (count($href) == 0) :
105 $href = $this->_a_href_feeds(FALSE);
106 endif;
107 endif;
108
109 // Our search may turn up duplicate URIs. We only need to do
110 // any given URI once. Props to Camilo <http://projects.radgeek.com/2008/12/14/feedwordpress-20081214/#comment-20090122160414>
111 $href = array_unique($href);
112 endif;
113
114 // Try some clever URL little tricks before we go
115 if ($this->fallbacks > 2) :
116 $href = array_merge($href, $this->_url_manipulation_feeds());
117 endif;
118 endif;
119
120 $href = array_unique($href);
121
122 // Verify feeds and resolve relative URIs
123 foreach ($href as $u) :
124 $the_uri = SimplePie_Misc::absolutize_url($u, $this->uri);
125 if ($this->verify and ($u != $this->uri and $the_uri != $this->uri)) :
126 $feed = new FeedFinder($the_uri, $this->credentials);
127 if ($feed->is_feed()) : $ret[] = $the_uri; endif;
128 unset($feed);
129 else :
130 $ret[] = $the_uri;
131 endif;
132 endforeach;
133 endif;
134
135 if ($this->is_401($uri)) :
136 $ret = array_merge(array(
137 new WP_Error('http_request_failed', '401 Not authorized', array("uri" => $this->uri, "status" => 401)),
138 ), $ret);
139 endif;
140
141 return array_values($ret);
142 } /* FeedFinder::find () */
143
144 function data ($uri = NULL) {
145 $this->_get($uri);
146 return $this->_data;
147 } /* FeedFinder::data () */
148
149 function upload_data ($data) {
150 $this->uri = 'tag:localhost';
151 $this->_data = $data;
152 } /* FeedFinder::upload_data () */
153
154 function status ($uri = NULL) {
155 $this->_get($uri);
156
157 if (!is_wp_error($this->_response) and isset($this->_response['response']['code'])) :
158 $ret = $this->_response['response']['code'];
159 else :
160 $ret = NULL;
161 endif;
162 return $ret;
163 }
164
165 function error ($index = NULL) {
166 $message = NULL;
167 if (count($this->_error) > 0) :
168 if (is_scalar($index) and !is_null($index)) :
169 $message = $this->_error[$index];
170 else :
171 $message = implode(" / ", $this->_error)."\n";
172 endif;
173 endif;
174 return $message;
175 }
176
177 function is_401 ($uri = NULL) {
178 return (intval($this->status($uri))==401);
179 } /* FeedFinder::is_401 () */
180
181 function is_feed ($uri = NULL) {
182 $data = $this->data($uri);
183
184 return (
185 preg_match (
186 "\007(".implode('|',$this->_feed_markers).")\007i",
187 $data
188 ) and !preg_match (
189 "\007(".implode('|',$this->_html_markers).")\007i",
190 $data
191 )
192 );
193 } /* FeedFinder::is_feed () */
194
195 function is_opml ($uri = NULL) {
196 $data = $this->data($uri);
197 return (
198 preg_match (
199 "\007(".implode('|',$this->_opml_markers).")\007i",
200 $data
201 )
202 );
203 } /* FeedFinder::is_opml () */
204
205 # --- Private methods ---
206 function _get ($uri = NULL) {
207 if ($uri) $this->uri = $uri;
208
209 // Is the result not yet cached?
210 if ($this->uri != 'tag:localhost' and $this->_cache_uri !== $this->uri) :
211 $headers['Connection'] = 'close';
212 $headers['Accept'] = 'application/atom+xml application/rdf+xml application/rss+xml application/xml text/html */*';
213 $headers['User-Agent'] = 'feedfinder/1.2 (compatible; PHP FeedFinder) +http://projects.radgeek.com/feedwordpress';
214
215 // Use WordPress API function
216 $client = wp_remote_request($this->uri, array_merge(
217 $this->credentials,
218 array(
219 'headers' => $headers,
220 'timeout' => FeedWordPress::fetch_timeout(),
221 )
222 ));
223
224 $this->_response = $client;
225 if (is_wp_error($client)) :
226 $this->_data = NULL;
227 $this->_error = $client->get_error_messages();
228 else :
229 $this->_data = $client['body'];
230 $this->_error = NULL;
231 endif;
232
233 // Kilroy was here
234 $this->_cache_uri = $this->uri;
235 endif;
236 } /* FeedFinder::_get () */
237
238 function _opml_rss_uris () {
239 // Really we should parse the XML and use the structure to
240 // return something intelligent to programs that want to use it
241 // Oh babe! maybe some day...
242
243 $opml = $this->data();
244
245 $rx = FeedWordPressHTML::attributeRegex('outline', 'xmlUrl');
246 if (preg_match_all($rx, $opml, $matches, PREG_SET_ORDER)) :
247 foreach ($matches as $m) :
248 $match = FeedWordPressHTML::attributeMatch($m);
249 $r[] = $match['value'];
250 endforeach;
251 endif;
252 return $r;
253 } /* FeedFinder::_opml_rss_uris () */
254
255 function _link_rel_feeds () {
256 $links = $this->_tags('link');
257 $link_count = count($links);
258
259 // now figure out which one points to the RSS file
260 $href = array ();
261 for ($n=0; $n<$link_count; $n++) {
262 if (strtolower($links[$n]['rel']) == 'alternate') {
263 if (in_array(strtolower($links[$n]['type']), $this->_feed_types)) {
264 $href[] = $links[$n]['href'];
265 } /* if */
266 } /* if */
267 } /* for */
268 return $href;
269 } /* FeedFinder::_link_rel_feeds () */
270
271 function _a_href_feeds ($obvious = TRUE) {
272 $pattern = ($obvious ? $this->_obvious_feed_url : $this->_maybe_feed_url);
273
274 $links = $this->_tags('a');
275 $link_count = count($links);
276
277 // now figure out which one points to the RSS file
278 $href = array ();
279 for ($n=0; $n<$link_count; $n++) {
280 if (preg_match("\007(".implode('|',$pattern).")\007i", $links[$n]['href'])) {
281 $href[] = $links[$n]['href'];
282 } /* if */
283 } /* for */
284 return $href;
285 } /* FeedFinder::_link_a_href_feeds () */
286
287 function _url_manipulation_feeds () {
288 $href = array();
289
290 // check for HTTP GET parameters that look feed-like.
291 $bits = parse_url($this->uri);
292 foreach (array('rss', 'rss2', 'atom', 'rdf') as $format) :
293 if (isset($bits['query']) and (strlen($bits['query']) > 0)) :
294 $newQuery = preg_replace('/([?&=])(rss2?|atom|rdf)/i', '$1'.$format, $bits['query']);
295 else :
296 $newQuery = NULL;
297 endif;
298
299 if (isset($bits['path']) and (strlen($bits['path']) > 0)) :
300 $newPath = preg_replace('!([/.])(rss2?|atom|rdf)!i', '$1'.$format, $bits['path']);
301 else :
302 $newPath = NULL;
303 endif;
304
305 // Reassemble, check and return
306 $credentials = '';
307 if (isset($bits['user'])) :
308 $credentials = $bits['user'];
309 if (isset($bits['pass'])) :
310 $credentials .= ':'.$bits['pass'];
311 endif;
312 $credentials .= '@';
313 endif;
314
315 // Variations on a theme
316 $newUrl[0] = (''
317 .(isset($bits['scheme']) ? $bits['scheme'].':' : '')
318 .(isset($bits['host']) ? '//'.$credentials.$bits['host'] : '')
319 .(!is_null($newPath) ? $newPath : '')
320 .(!is_null($newQuery) ? '?'.$newQuery : '')
321 .(isset($bits['fragment']) ? '#'.$bits['fragment'] : '')
322 );
323 $newUrl[1] = (''
324 .(isset($bits['scheme']) ? $bits['scheme'].':' : '')
325 .(isset($bits['host']) ? '//'.$credentials.$bits['host'] : '')
326 .(!is_null($newPath) ? $newPath : '')
327 .(isset($bits['query']) ? '?'.$bits['query'] : '')
328 .(isset($bits['fragment']) ? '#'.$bits['fragment'] : '')
329 );
330 $newUrl[2] = (''
331 .(isset($bits['scheme']) ? $bits['scheme'].':' : '')
332 .(isset($bits['host']) ? '//'.$credentials.$bits['host'] : '')
333 .(isset($bits['path']) ? $bits['path'] : '')
334 .(!is_null($newQuery) ? '?'.$newQuery : '')
335 .(isset($bits['fragment']) ? '#'.$bits['fragment'] : '')
336 );
337 $href = array_merge($href, $newUrl);
338 endforeach;
339 return array_unique($href);
340 } /* FeedFinder::_url_manipulation_feeds () */
341
342 function _tags ($tag) {
343 $html = $this->data();
344
345 // search through the HTML, save all <link> tags
346 // and store each link's attributes in an associative array
347 preg_match_all('/<'.$tag.'\s+(.*?)\s*\/?>/si', $html, $matches);
348 $links = $matches[1];
349 $ret = array();
350 $link_count = count($links);
351 for ($n=0; $n<$link_count; $n++) {
352 $attributes = preg_split('/\s+/s', $links[$n]);
353 foreach($attributes as $attribute) {
354 $att = preg_split('/\s*=\s*/s', $attribute, 2);
355 if (isset($att[1])) {
356 $att[1] = preg_replace('/([\'"]?)(.*)\1/', '$2', $att[1]);
357 $final_link[strtolower($att[0])] = $att[1];
358 } /* if */
359 } /* foreach */
360 $ret[$n] = $final_link;
361 } /* for */
362 return $ret;
363 } /* FeedFinder::_tags () */
364 } /* class FeedFinder */
365
366