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
feedwordpress / feedfinder.class.php

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

375 lines 10.6 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 /**
14 * FeedFinder attempts to get all possible RSS/Atom feeds from a site.
15 *
16 * @see SimplePie_Misc
17 */
18 class FeedFinder {
19 var $uri = NULL;
20 var $credentials = NULL;
21 var $_cache_uri = NULL;
22
23 var $verify = FALSE;
24 var $fallbacks = 3;
25
26 var $_response = NULL;
27 var $_data = NULL;
28 var $_error = NULL;
29 var $_head = NULL;
30
31 # -- Recognition patterns
32 var $_feed_types = array(
33 'application/rss+xml',
34 'text/xml',
35 'application/atom+xml',
36 'application/x.atom+xml',
37 'application/x-atom+xml',
38 );
39 var $_feed_markers = array('\\<feed', '\\<rss', 'xmlns="http://purl.org/rss/1.0');
40 var $_html_markers = array('\\<html');
41 var $_opml_markers = array('\\<opml', '\\<outline');
42 var $_obvious_feed_url = array('[./]rss', '[./]rdf', '[./]atom', '[./]feed', '\.xml');
43 var $_maybe_feed_url = array ('rss', 'rdf', 'atom', 'feed', 'xml');
44
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
63 $this->uri = $uri; $this->verify = $verify;
64 $this->fallbacks = $fallbacks;
65 } /* FeedFinder::FeedFinder () */
66
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
94 $ret = array ();
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();
105
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;
110
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);
151 } /* FeedFinder::find () */
152
153 function data ($uri = NULL) {
154 $this->_get($uri);
155 return $this->_data;
156 } /* FeedFinder::data () */
157
158 function upload_data ($data) {
159 $this->uri = 'tag:localhost';
160 $this->_data = $data;
161 } /* FeedFinder::upload_data () */
162
163 function status ($uri = NULL) {
164 $this->_get($uri);
165
166 if ( !is_wp_error($this->_response) and isset($this->_response['response']['code'])) :
167 $ret = $this->_response['response']['code'];
168 else :
169 $ret = NULL;
170 endif;
171 return $ret;
172 }
173
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;
184 }
185
186 function is_401 ($uri = NULL) {
187 return (intval($this->status($uri))==401);
188 } /* FeedFinder::is_401 () */
189
190 function is_feed ($uri = NULL) {
191 $data = $this->data($uri);
192
193 return (
194 preg_match (
195 "\007(".implode('|',$this->_feed_markers).")\007i",
196 $data
197 ) and !preg_match (
198 "\007(".implode('|',$this->_html_markers).")\007i",
199 $data
200 )
201 );
202 } /* FeedFinder::is_feed () */
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
214 # --- Private methods ---
215 function _get ($uri = NULL) {
216 if ($uri) $this->uri = $uri;
217
218 // Is the result not yet cached?
219 if ($this->uri != 'tag:localhost' and $this->_cache_uri !== $this->uri) :
220 $headers['Connection'] = 'close';
221 $headers['Accept'] = 'application/atom+xml application/rdf+xml application/rss+xml application/xml text/html */*';
222 $headers['User-Agent'] = 'feedfinder/1.2 (compatible; PHP FeedFinder) +http://projects.radgeek.com/feedwordpress';
223
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();
237 else :
238 $this->_data = $client['body'];
239 $this->_error = NULL;
240 endif;
241
242 // Kilroy was here
243 $this->_cache_uri = $this->uri;
244 endif;
245 } /* FeedFinder::_get () */
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
264 function _link_rel_feeds () {
265 $links = $this->_tags('link');
266 $link_count = count($links);
267
268 // now figure out which one points to the RSS file
269 $href = array ();
270 for ($n=0; $n<$link_count; $n++) {
271 if (strtolower($links[$n]['rel']) == 'alternate') {
272 if (in_array(strtolower($links[$n]['type']), $this->_feed_types)) {
273 $href[] = $links[$n]['href'];
274 } /* if */
275 } /* if */
276 } /* for */
277 return $href;
278 } /* FeedFinder::_link_rel_feeds () */
279
280 function _a_href_feeds ($obvious = TRUE) {
281 $pattern = ($obvious ? $this->_obvious_feed_url : $this->_maybe_feed_url);
282
283 $links = $this->_tags('a');
284 $link_count = count($links);
285
286 // now figure out which one points to the RSS file
287 $href = array ();
288 for ($n=0; $n<$link_count; $n++) {
289 if (preg_match("\007(".implode('|',$pattern).")\007i", $links[$n]['href'])) {
290 $href[] = $links[$n]['href'];
291 } /* if */
292 } /* for */
293 return $href;
294 } /* FeedFinder::_link_a_href_feeds () */
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
351 function _tags ($tag) {
352 $html = $this->data();
353
354 // search through the HTML, save all <link> tags
355 // and store each link's attributes in an associative array
356 preg_match_all('/<'.$tag.'\s+(.*?)\s*\/?>/si', $html, $matches);
357 $links = $matches[1];
358 $ret = array();
359 $link_count = count($links);
360 for ($n=0; $n<$link_count; $n++) {
361 $attributes = preg_split('/\s+/s', $links[$n]);
362 foreach($attributes as $attribute) {
363 $att = preg_split('/\s*=\s*/s', $attribute, 2);
364 if (isset($att[1])) {
365 $att[1] = preg_replace('/([\'"]?)(.*)\1/', '$2', $att[1]);
366 $final_link[strtolower($att[0])] = $att[1];
367 } /* if */
368 } /* foreach */
369 $ret[$n] = $final_link;
370 } /* for */
371 return $ret;
372 } /* FeedFinder::_tags () */
373 } /* class FeedFinder */
374
375