PluginProbe
FeedWordPress / 2025.1211
FeedWordPress v2025.1211
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 +246 -56 2009.06132025.1211 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,65 +33,139 @@
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 - $feed = NULL;
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 (isset($this->_response->status)) :
79 - $ret = $this->_response->status;
165 +
166 + if ( !is_wp_error($this->_response) and isset($this->_response['response']['code'])) :
167 + $ret = $this->_response['response']['code'];
80 168 else :
81 169 $ret = NULL;
82 170 endif;
83 171 return $ret;
@@ -82,12 +170,24 @@
82 170 endif;
83 171 return $ret;
84 172 }
85 173
86 - function error () {
87 - return $this->_error;
174 + function error ($index = NULL) {
175 + $message = NULL;
176 + if (count($this->_error) > 0) :
177 + if (is_scalar($index) and !is_null($index)) :
178 + $message = $this->_error[$index];
179 + else :
180 + $message = implode(" / ", $this->_error)."\n";
181 + endif;
182 + endif;
183 + return $message;
88 184 }
89 -
185 +
186 + function is_401 ($uri = NULL) {
187 + return (intval($this->status($uri))==401);
188 + } /* FeedFinder::is_401 () */
189 +
90 190 function is_feed ($uri = NULL) {
91 191 $data = $this->data($uri);
92 192
93 193 return (
@@ -100,27 +200,45 @@
100 200 )
101 201 );
102 202 } /* FeedFinder::is_feed () */
103 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 +
104 214 # --- Private methods ---
105 215 function _get ($uri = NULL) {
106 216 if ($uri) $this->uri = $uri;
107 217
108 218 // Is the result not yet cached?
109 - if ($this->_cache_uri !== $this->uri) :
219 + if ($this->uri != 'tag:localhost' and $this->_cache_uri !== $this->uri) :
110 220 $headers['Connection'] = 'close';
111 221 $headers['Accept'] = 'application/atom+xml application/rdf+xml application/rss+xml application/xml text/html */*';
112 222 $headers['User-Agent'] = 'feedfinder/1.2 (compatible; PHP FeedFinder) +http://projects.radgeek.com/feedwordpress';
113 223
114 - // Use function provided by MagpieRSS package
115 - $client = _fetch_remote_file($this->uri, $headers);
116 - if (isset($client->error)) :
117 - $this->_error = $client->error;
224 + // Use WordPress API function
225 + $client = wp_remote_request($this->uri, array_merge(
226 + $this->credentials,
227 + array(
228 + 'headers' => $headers,
229 + 'timeout' => FeedWordPress::fetch_timeout(),
230 + )
231 + ));
232 +
233 + $this->_response = $client;
234 + if (is_wp_error($client)) :
235 + $this->_data = NULL;
236 + $this->_error = $client->get_error_messages();
118 237 else :
238 + $this->_data = $client['body'];
119 239 $this->_error = NULL;
120 240 endif;
121 - $this->_response = $client;
122 - $this->_data = $client->results;
123 241
124 242 // Kilroy was here
125 243 $this->_cache_uri = $this->uri;
126 244 endif;
@@ -125,8 +243,25 @@
125 243 $this->_cache_uri = $this->uri;
126 244 endif;
127 245 } /* FeedFinder::_get () */
128 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 +
129 264 function _link_rel_feeds () {
130 265 $links = $this->_tags('link');
131 266 $link_count = count($links);
132 267
@@ -139,9 +274,9 @@
139 274 } /* if */
140 275 } /* if */
141 276 } /* for */
142 277 return $href;
143 - }
278 + } /* FeedFinder::_link_rel_feeds () */
144 279
145 280 function _a_href_feeds ($obvious = TRUE) {
146 281 $pattern = ($obvious ? $this->_obvious_feed_url : $this->_maybe_feed_url);
147 282
@@ -155,13 +290,68 @@
155 290 $href[] = $links[$n]['href'];
156 291 } /* if */
157 292 } /* for */
158 293 return $href;
159 - }
294 + } /* FeedFinder::_link_a_href_feeds () */
160 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 +
161 351 function _tags ($tag) {
162 352 $html = $this->data();
163 -
353 +
164 354 // search through the HTML, save all <link> tags
165 355 // and store each link's attributes in an associative array
166 356 preg_match_all('/<'.$tag.'\s+(.*?)\s*\/?>/si', $html, $matches);
167 357 $links = $matches[1];
@@ -178,7 +368,7 @@
178 368 } /* foreach */
179 369 $ret[$n] = $final_link;
180 370 } /* for */
181 371 return $ret;
182 - }
372 + } /* FeedFinder::_tags () */
183 373 } /* class FeedFinder */
184 374