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