PluginProbe
FeedWordPress / 2021.0713
FeedWordPress v2021.0713
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 2021.0713, at feedfinder.class.php

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