|
1 <?php |
|
2 |
|
3 /** |
|
4 * SimplePie |
|
5 * |
|
6 * A PHP-Based RSS and Atom Feed Framework. |
|
7 * Takes the hard work out of managing a complete RSS/Atom solution. |
|
8 * |
|
9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors |
|
10 * All rights reserved. |
|
11 * |
|
12 * Redistribution and use in source and binary forms, with or without modification, are |
|
13 * permitted provided that the following conditions are met: |
|
14 * |
|
15 * * Redistributions of source code must retain the above copyright notice, this list of |
|
16 * conditions and the following disclaimer. |
|
17 * |
|
18 * * Redistributions in binary form must reproduce the above copyright notice, this list |
|
19 * of conditions and the following disclaimer in the documentation and/or other materials |
|
20 * provided with the distribution. |
|
21 * |
|
22 * * Neither the name of the SimplePie Team nor the names of its contributors may be used |
|
23 * to endorse or promote products derived from this software without specific prior |
|
24 * written permission. |
|
25 * |
|
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS |
|
27 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY |
|
28 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS |
|
29 * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
|
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
|
33 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
34 * POSSIBILITY OF SUCH DAMAGE. |
|
35 * |
|
36 * @package SimplePie |
|
37 * @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue |
|
38 * @author Ryan Parman |
|
39 * @author Sam Sneddon |
|
40 * @author Ryan McCue |
|
41 * @link http://simplepie.org/ SimplePie |
|
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License |
|
43 */ |
|
44 |
|
45 namespace SimplePie; |
|
46 |
|
47 use InvalidArgumentException; |
|
48 use Psr\SimpleCache\CacheInterface; |
|
49 use SimplePie\Cache\Base; |
|
50 use SimplePie\Cache\BaseDataCache; |
|
51 use SimplePie\Cache\CallableNameFilter; |
|
52 use SimplePie\Cache\DataCache; |
|
53 use SimplePie\Cache\NameFilter; |
|
54 use SimplePie\Cache\Psr16; |
|
55 use SimplePie\Content\Type\Sniffer; |
|
56 |
|
57 /** |
|
58 * SimplePie |
|
59 * |
|
60 * @package SimplePie |
|
61 * @subpackage API |
|
62 */ |
|
63 class SimplePie |
|
64 { |
|
65 /** |
|
66 * SimplePie Name |
|
67 */ |
|
68 public const NAME = 'SimplePie'; |
|
69 |
|
70 /** |
|
71 * SimplePie Version |
|
72 */ |
|
73 public const VERSION = '1.8.0'; |
|
74 |
|
75 /** |
|
76 * SimplePie Website URL |
|
77 */ |
|
78 public const URL = 'http://simplepie.org'; |
|
79 |
|
80 /** |
|
81 * SimplePie Linkback |
|
82 */ |
|
83 public const LINKBACK = '<a href="' . self::URL . '" title="' . self::NAME . ' ' . self::VERSION . '">' . self::NAME . '</a>'; |
|
84 |
|
85 /** |
|
86 * No Autodiscovery |
|
87 * @see SimplePie::set_autodiscovery_level() |
|
88 */ |
|
89 public const LOCATOR_NONE = 0; |
|
90 |
|
91 /** |
|
92 * Feed Link Element Autodiscovery |
|
93 * @see SimplePie::set_autodiscovery_level() |
|
94 */ |
|
95 public const LOCATOR_AUTODISCOVERY = 1; |
|
96 |
|
97 /** |
|
98 * Local Feed Extension Autodiscovery |
|
99 * @see SimplePie::set_autodiscovery_level() |
|
100 */ |
|
101 public const LOCATOR_LOCAL_EXTENSION = 2; |
|
102 |
|
103 /** |
|
104 * Local Feed Body Autodiscovery |
|
105 * @see SimplePie::set_autodiscovery_level() |
|
106 */ |
|
107 public const LOCATOR_LOCAL_BODY = 4; |
|
108 |
|
109 /** |
|
110 * Remote Feed Extension Autodiscovery |
|
111 * @see SimplePie::set_autodiscovery_level() |
|
112 */ |
|
113 public const LOCATOR_REMOTE_EXTENSION = 8; |
|
114 |
|
115 /** |
|
116 * Remote Feed Body Autodiscovery |
|
117 * @see SimplePie::set_autodiscovery_level() |
|
118 */ |
|
119 public const LOCATOR_REMOTE_BODY = 16; |
|
120 |
|
121 /** |
|
122 * All Feed Autodiscovery |
|
123 * @see SimplePie::set_autodiscovery_level() |
|
124 */ |
|
125 public const LOCATOR_ALL = 31; |
|
126 |
|
127 /** |
|
128 * No known feed type |
|
129 */ |
|
130 public const TYPE_NONE = 0; |
|
131 |
|
132 /** |
|
133 * RSS 0.90 |
|
134 */ |
|
135 public const TYPE_RSS_090 = 1; |
|
136 |
|
137 /** |
|
138 * RSS 0.91 (Netscape) |
|
139 */ |
|
140 public const TYPE_RSS_091_NETSCAPE = 2; |
|
141 |
|
142 /** |
|
143 * RSS 0.91 (Userland) |
|
144 */ |
|
145 public const TYPE_RSS_091_USERLAND = 4; |
|
146 |
|
147 /** |
|
148 * RSS 0.91 (both Netscape and Userland) |
|
149 */ |
|
150 public const TYPE_RSS_091 = 6; |
|
151 |
|
152 /** |
|
153 * RSS 0.92 |
|
154 */ |
|
155 public const TYPE_RSS_092 = 8; |
|
156 |
|
157 /** |
|
158 * RSS 0.93 |
|
159 */ |
|
160 public const TYPE_RSS_093 = 16; |
|
161 |
|
162 /** |
|
163 * RSS 0.94 |
|
164 */ |
|
165 public const TYPE_RSS_094 = 32; |
|
166 |
|
167 /** |
|
168 * RSS 1.0 |
|
169 */ |
|
170 public const TYPE_RSS_10 = 64; |
|
171 |
|
172 /** |
|
173 * RSS 2.0 |
|
174 */ |
|
175 public const TYPE_RSS_20 = 128; |
|
176 |
|
177 /** |
|
178 * RDF-based RSS |
|
179 */ |
|
180 public const TYPE_RSS_RDF = 65; |
|
181 |
|
182 /** |
|
183 * Non-RDF-based RSS (truly intended as syndication format) |
|
184 */ |
|
185 public const TYPE_RSS_SYNDICATION = 190; |
|
186 |
|
187 /** |
|
188 * All RSS |
|
189 */ |
|
190 public const TYPE_RSS_ALL = 255; |
|
191 |
|
192 /** |
|
193 * Atom 0.3 |
|
194 */ |
|
195 public const TYPE_ATOM_03 = 256; |
|
196 |
|
197 /** |
|
198 * Atom 1.0 |
|
199 */ |
|
200 public const TYPE_ATOM_10 = 512; |
|
201 |
|
202 /** |
|
203 * All Atom |
|
204 */ |
|
205 public const TYPE_ATOM_ALL = 768; |
|
206 |
|
207 /** |
|
208 * All feed types |
|
209 */ |
|
210 public const TYPE_ALL = 1023; |
|
211 |
|
212 /** |
|
213 * No construct |
|
214 */ |
|
215 public const CONSTRUCT_NONE = 0; |
|
216 |
|
217 /** |
|
218 * Text construct |
|
219 */ |
|
220 public const CONSTRUCT_TEXT = 1; |
|
221 |
|
222 /** |
|
223 * HTML construct |
|
224 */ |
|
225 public const CONSTRUCT_HTML = 2; |
|
226 |
|
227 /** |
|
228 * XHTML construct |
|
229 */ |
|
230 public const CONSTRUCT_XHTML = 4; |
|
231 |
|
232 /** |
|
233 * base64-encoded construct |
|
234 */ |
|
235 public const CONSTRUCT_BASE64 = 8; |
|
236 |
|
237 /** |
|
238 * IRI construct |
|
239 */ |
|
240 public const CONSTRUCT_IRI = 16; |
|
241 |
|
242 /** |
|
243 * A construct that might be HTML |
|
244 */ |
|
245 public const CONSTRUCT_MAYBE_HTML = 32; |
|
246 |
|
247 /** |
|
248 * All constructs |
|
249 */ |
|
250 public const CONSTRUCT_ALL = 63; |
|
251 |
|
252 /** |
|
253 * Don't change case |
|
254 */ |
|
255 public const SAME_CASE = 1; |
|
256 |
|
257 /** |
|
258 * Change to lowercase |
|
259 */ |
|
260 public const LOWERCASE = 2; |
|
261 |
|
262 /** |
|
263 * Change to uppercase |
|
264 */ |
|
265 public const UPPERCASE = 4; |
|
266 |
|
267 /** |
|
268 * PCRE for HTML attributes |
|
269 */ |
|
270 public const PCRE_HTML_ATTRIBUTE = '((?:[\x09\x0A\x0B\x0C\x0D\x20]+[^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?)*)[\x09\x0A\x0B\x0C\x0D\x20]*'; |
|
271 |
|
272 /** |
|
273 * PCRE for XML attributes |
|
274 */ |
|
275 public const PCRE_XML_ATTRIBUTE = '((?:\s+(?:(?:[^\s:]+:)?[^\s:]+)\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'))*)\s*'; |
|
276 |
|
277 /** |
|
278 * XML Namespace |
|
279 */ |
|
280 public const NAMESPACE_XML = 'http://www.w3.org/XML/1998/namespace'; |
|
281 |
|
282 /** |
|
283 * Atom 1.0 Namespace |
|
284 */ |
|
285 public const NAMESPACE_ATOM_10 = 'http://www.w3.org/2005/Atom'; |
|
286 |
|
287 /** |
|
288 * Atom 0.3 Namespace |
|
289 */ |
|
290 public const NAMESPACE_ATOM_03 = 'http://purl.org/atom/ns#'; |
|
291 |
|
292 /** |
|
293 * RDF Namespace |
|
294 */ |
|
295 public const NAMESPACE_RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; |
|
296 |
|
297 /** |
|
298 * RSS 0.90 Namespace |
|
299 */ |
|
300 public const NAMESPACE_RSS_090 = 'http://my.netscape.com/rdf/simple/0.9/'; |
|
301 |
|
302 /** |
|
303 * RSS 1.0 Namespace |
|
304 */ |
|
305 public const NAMESPACE_RSS_10 = 'http://purl.org/rss/1.0/'; |
|
306 |
|
307 /** |
|
308 * RSS 1.0 Content Module Namespace |
|
309 */ |
|
310 public const NAMESPACE_RSS_10_MODULES_CONTENT = 'http://purl.org/rss/1.0/modules/content/'; |
|
311 |
|
312 /** |
|
313 * RSS 2.0 Namespace |
|
314 * (Stupid, I know, but I'm certain it will confuse people less with support.) |
|
315 */ |
|
316 public const NAMESPACE_RSS_20 = ''; |
|
317 |
|
318 /** |
|
319 * DC 1.0 Namespace |
|
320 */ |
|
321 public const NAMESPACE_DC_10 = 'http://purl.org/dc/elements/1.0/'; |
|
322 |
|
323 /** |
|
324 * DC 1.1 Namespace |
|
325 */ |
|
326 public const NAMESPACE_DC_11 = 'http://purl.org/dc/elements/1.1/'; |
|
327 |
|
328 /** |
|
329 * W3C Basic Geo (WGS84 lat/long) Vocabulary Namespace |
|
330 */ |
|
331 public const NAMESPACE_W3C_BASIC_GEO = 'http://www.w3.org/2003/01/geo/wgs84_pos#'; |
|
332 |
|
333 /** |
|
334 * GeoRSS Namespace |
|
335 */ |
|
336 public const NAMESPACE_GEORSS = 'http://www.georss.org/georss'; |
|
337 |
|
338 /** |
|
339 * Media RSS Namespace |
|
340 */ |
|
341 public const NAMESPACE_MEDIARSS = 'http://search.yahoo.com/mrss/'; |
|
342 |
|
343 /** |
|
344 * Wrong Media RSS Namespace. Caused by a long-standing typo in the spec. |
|
345 */ |
|
346 public const NAMESPACE_MEDIARSS_WRONG = 'http://search.yahoo.com/mrss'; |
|
347 |
|
348 /** |
|
349 * Wrong Media RSS Namespace #2. New namespace introduced in Media RSS 1.5. |
|
350 */ |
|
351 public const NAMESPACE_MEDIARSS_WRONG2 = 'http://video.search.yahoo.com/mrss'; |
|
352 |
|
353 /** |
|
354 * Wrong Media RSS Namespace #3. A possible typo of the Media RSS 1.5 namespace. |
|
355 */ |
|
356 public const NAMESPACE_MEDIARSS_WRONG3 = 'http://video.search.yahoo.com/mrss/'; |
|
357 |
|
358 /** |
|
359 * Wrong Media RSS Namespace #4. New spec location after the RSS Advisory Board takes it over, but not a valid namespace. |
|
360 */ |
|
361 public const NAMESPACE_MEDIARSS_WRONG4 = 'http://www.rssboard.org/media-rss'; |
|
362 |
|
363 /** |
|
364 * Wrong Media RSS Namespace #5. A possible typo of the RSS Advisory Board URL. |
|
365 */ |
|
366 public const NAMESPACE_MEDIARSS_WRONG5 = 'http://www.rssboard.org/media-rss/'; |
|
367 |
|
368 /** |
|
369 * iTunes RSS Namespace |
|
370 */ |
|
371 public const NAMESPACE_ITUNES = 'http://www.itunes.com/dtds/podcast-1.0.dtd'; |
|
372 |
|
373 /** |
|
374 * XHTML Namespace |
|
375 */ |
|
376 public const NAMESPACE_XHTML = 'http://www.w3.org/1999/xhtml'; |
|
377 |
|
378 /** |
|
379 * IANA Link Relations Registry |
|
380 */ |
|
381 public const IANA_LINK_RELATIONS_REGISTRY = 'http://www.iana.org/assignments/relation/'; |
|
382 |
|
383 /** |
|
384 * No file source |
|
385 */ |
|
386 public const FILE_SOURCE_NONE = 0; |
|
387 |
|
388 /** |
|
389 * Remote file source |
|
390 */ |
|
391 public const FILE_SOURCE_REMOTE = 1; |
|
392 |
|
393 /** |
|
394 * Local file source |
|
395 */ |
|
396 public const FILE_SOURCE_LOCAL = 2; |
|
397 |
|
398 /** |
|
399 * fsockopen() file source |
|
400 */ |
|
401 public const FILE_SOURCE_FSOCKOPEN = 4; |
|
402 |
|
403 /** |
|
404 * cURL file source |
|
405 */ |
|
406 public const FILE_SOURCE_CURL = 8; |
|
407 |
|
408 /** |
|
409 * file_get_contents() file source |
|
410 */ |
|
411 public const FILE_SOURCE_FILE_GET_CONTENTS = 16; |
|
412 |
|
413 /** |
|
414 * @var array Raw data |
|
415 * @access private |
|
416 */ |
|
417 public $data = []; |
|
418 |
|
419 /** |
|
420 * @var mixed Error string |
|
421 * @access private |
|
422 */ |
|
423 public $error; |
|
424 |
|
425 /** |
|
426 * @var int HTTP status code |
|
427 * @see SimplePie::status_code() |
|
428 * @access private |
|
429 */ |
|
430 public $status_code = 0; |
|
431 |
|
432 /** |
|
433 * @var object Instance of \SimplePie\Sanitize (or other class) |
|
434 * @see SimplePie::set_sanitize_class() |
|
435 * @access private |
|
436 */ |
|
437 public $sanitize; |
|
438 |
|
439 /** |
|
440 * @var string SimplePie Useragent |
|
441 * @see SimplePie::set_useragent() |
|
442 * @access private |
|
443 */ |
|
444 public $useragent = ''; |
|
445 |
|
446 /** |
|
447 * @var string Feed URL |
|
448 * @see SimplePie::set_feed_url() |
|
449 * @access private |
|
450 */ |
|
451 public $feed_url; |
|
452 |
|
453 /** |
|
454 * @var string Original feed URL, or new feed URL iff HTTP 301 Moved Permanently |
|
455 * @see SimplePie::subscribe_url() |
|
456 * @access private |
|
457 */ |
|
458 public $permanent_url = null; |
|
459 |
|
460 /** |
|
461 * @var object Instance of \SimplePie\File to use as a feed |
|
462 * @see SimplePie::set_file() |
|
463 * @access private |
|
464 */ |
|
465 public $file; |
|
466 |
|
467 /** |
|
468 * @var string Raw feed data |
|
469 * @see SimplePie::set_raw_data() |
|
470 * @access private |
|
471 */ |
|
472 public $raw_data; |
|
473 |
|
474 /** |
|
475 * @var int Timeout for fetching remote files |
|
476 * @see SimplePie::set_timeout() |
|
477 * @access private |
|
478 */ |
|
479 public $timeout = 10; |
|
480 |
|
481 /** |
|
482 * @var array Custom curl options |
|
483 * @see SimplePie::set_curl_options() |
|
484 * @access private |
|
485 */ |
|
486 public $curl_options = []; |
|
487 |
|
488 /** |
|
489 * @var bool Forces fsockopen() to be used for remote files instead |
|
490 * of cURL, even if a new enough version is installed |
|
491 * @see SimplePie::force_fsockopen() |
|
492 * @access private |
|
493 */ |
|
494 public $force_fsockopen = false; |
|
495 |
|
496 /** |
|
497 * @var bool Force the given data/URL to be treated as a feed no matter what |
|
498 * it appears like |
|
499 * @see SimplePie::force_feed() |
|
500 * @access private |
|
501 */ |
|
502 public $force_feed = false; |
|
503 |
|
504 /** |
|
505 * @var bool Enable/Disable Caching |
|
506 * @see SimplePie::enable_cache() |
|
507 * @access private |
|
508 */ |
|
509 private $enable_cache = true; |
|
510 |
|
511 /** |
|
512 * @var DataCache|null |
|
513 * @see SimplePie::set_cache() |
|
514 */ |
|
515 private $cache = null; |
|
516 |
|
517 /** |
|
518 * @var NameFilter |
|
519 * @see SimplePie::set_cache_namefilter() |
|
520 */ |
|
521 private $cache_namefilter; |
|
522 |
|
523 /** |
|
524 * @var bool Force SimplePie to fallback to expired cache, if enabled, |
|
525 * when feed is unavailable. |
|
526 * @see SimplePie::force_cache_fallback() |
|
527 * @access private |
|
528 */ |
|
529 public $force_cache_fallback = false; |
|
530 |
|
531 /** |
|
532 * @var int Cache duration (in seconds) |
|
533 * @see SimplePie::set_cache_duration() |
|
534 * @access private |
|
535 */ |
|
536 public $cache_duration = 3600; |
|
537 |
|
538 /** |
|
539 * @var int Auto-discovery cache duration (in seconds) |
|
540 * @see SimplePie::set_autodiscovery_cache_duration() |
|
541 * @access private |
|
542 */ |
|
543 public $autodiscovery_cache_duration = 604800; // 7 Days. |
|
544 |
|
545 /** |
|
546 * @var string Cache location (relative to executing script) |
|
547 * @see SimplePie::set_cache_location() |
|
548 * @access private |
|
549 */ |
|
550 public $cache_location = './cache'; |
|
551 |
|
552 /** |
|
553 * @var string Function that creates the cache filename |
|
554 * @see SimplePie::set_cache_name_function() |
|
555 * @access private |
|
556 */ |
|
557 public $cache_name_function = 'md5'; |
|
558 |
|
559 /** |
|
560 * @var bool Reorder feed by date descending |
|
561 * @see SimplePie::enable_order_by_date() |
|
562 * @access private |
|
563 */ |
|
564 public $order_by_date = true; |
|
565 |
|
566 /** |
|
567 * @var mixed Force input encoding to be set to the follow value |
|
568 * (false, or anything type-cast to false, disables this feature) |
|
569 * @see SimplePie::set_input_encoding() |
|
570 * @access private |
|
571 */ |
|
572 public $input_encoding = false; |
|
573 |
|
574 /** |
|
575 * @var int Feed Autodiscovery Level |
|
576 * @see SimplePie::set_autodiscovery_level() |
|
577 * @access private |
|
578 */ |
|
579 public $autodiscovery = self::LOCATOR_ALL; |
|
580 |
|
581 /** |
|
582 * Class registry object |
|
583 * |
|
584 * @var \SimplePie\Registry |
|
585 */ |
|
586 public $registry; |
|
587 |
|
588 /** |
|
589 * @var int Maximum number of feeds to check with autodiscovery |
|
590 * @see SimplePie::set_max_checked_feeds() |
|
591 * @access private |
|
592 */ |
|
593 public $max_checked_feeds = 10; |
|
594 |
|
595 /** |
|
596 * @var array All the feeds found during the autodiscovery process |
|
597 * @see SimplePie::get_all_discovered_feeds() |
|
598 * @access private |
|
599 */ |
|
600 public $all_discovered_feeds = []; |
|
601 |
|
602 /** |
|
603 * @var string Web-accessible path to the handler_image.php file. |
|
604 * @see SimplePie::set_image_handler() |
|
605 * @access private |
|
606 */ |
|
607 public $image_handler = ''; |
|
608 |
|
609 /** |
|
610 * @var array Stores the URLs when multiple feeds are being initialized. |
|
611 * @see SimplePie::set_feed_url() |
|
612 * @access private |
|
613 */ |
|
614 public $multifeed_url = []; |
|
615 |
|
616 /** |
|
617 * @var array Stores SimplePie objects when multiple feeds initialized. |
|
618 * @access private |
|
619 */ |
|
620 public $multifeed_objects = []; |
|
621 |
|
622 /** |
|
623 * @var array Stores the get_object_vars() array for use with multifeeds. |
|
624 * @see SimplePie::set_feed_url() |
|
625 * @access private |
|
626 */ |
|
627 public $config_settings = null; |
|
628 |
|
629 /** |
|
630 * @var integer Stores the number of items to return per-feed with multifeeds. |
|
631 * @see SimplePie::set_item_limit() |
|
632 * @access private |
|
633 */ |
|
634 public $item_limit = 0; |
|
635 |
|
636 /** |
|
637 * @var bool Stores if last-modified and/or etag headers were sent with the |
|
638 * request when checking a feed. |
|
639 */ |
|
640 public $check_modified = false; |
|
641 |
|
642 /** |
|
643 * @var array Stores the default attributes to be stripped by strip_attributes(). |
|
644 * @see SimplePie::strip_attributes() |
|
645 * @access private |
|
646 */ |
|
647 public $strip_attributes = ['bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc']; |
|
648 |
|
649 /** |
|
650 * @var array Stores the default attributes to add to different tags by add_attributes(). |
|
651 * @see SimplePie::add_attributes() |
|
652 * @access private |
|
653 */ |
|
654 public $add_attributes = ['audio' => ['preload' => 'none'], 'iframe' => ['sandbox' => 'allow-scripts allow-same-origin'], 'video' => ['preload' => 'none']]; |
|
655 |
|
656 /** |
|
657 * @var array Stores the default tags to be stripped by strip_htmltags(). |
|
658 * @see SimplePie::strip_htmltags() |
|
659 * @access private |
|
660 */ |
|
661 public $strip_htmltags = ['base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style']; |
|
662 |
|
663 /** |
|
664 * @var array Stores the default attributes to be renamed by rename_attributes(). |
|
665 * @see SimplePie::rename_attributes() |
|
666 * @access private |
|
667 */ |
|
668 public $rename_attributes = []; |
|
669 |
|
670 /** |
|
671 * @var bool Should we throw exceptions, or use the old-style error property? |
|
672 * @access private |
|
673 */ |
|
674 public $enable_exceptions = false; |
|
675 |
|
676 /** |
|
677 * The SimplePie class contains feed level data and options |
|
678 * |
|
679 * To use SimplePie, create the SimplePie object with no parameters. You can |
|
680 * then set configuration options using the provided methods. After setting |
|
681 * them, you must initialise the feed using $feed->init(). At that point the |
|
682 * object's methods and properties will be available to you. |
|
683 * |
|
684 * Previously, it was possible to pass in the feed URL along with cache |
|
685 * options directly into the constructor. This has been removed as of 1.3 as |
|
686 * it caused a lot of confusion. |
|
687 * |
|
688 * @since 1.0 Preview Release |
|
689 */ |
|
690 public function __construct() |
|
691 { |
|
692 if (version_compare(PHP_VERSION, '7.2', '<')) { |
|
693 exit('Please upgrade to PHP 7.2 or newer.'); |
|
694 } |
|
695 |
|
696 $this->set_useragent(); |
|
697 |
|
698 $this->set_cache_namefilter(new CallableNameFilter($this->cache_name_function)); |
|
699 |
|
700 // Other objects, instances created here so we can set options on them |
|
701 $this->sanitize = new \SimplePie\Sanitize(); |
|
702 $this->registry = new \SimplePie\Registry(); |
|
703 |
|
704 if (func_num_args() > 0) { |
|
705 trigger_error('Passing parameters to the constructor is no longer supported. Please use set_feed_url(), set_cache_location(), and set_cache_duration() directly.', \E_USER_DEPRECATED); |
|
706 |
|
707 $args = func_get_args(); |
|
708 switch (count($args)) { |
|
709 case 3: |
|
710 $this->set_cache_duration($args[2]); |
|
711 // no break |
|
712 case 2: |
|
713 $this->set_cache_location($args[1]); |
|
714 // no break |
|
715 case 1: |
|
716 $this->set_feed_url($args[0]); |
|
717 $this->init(); |
|
718 } |
|
719 } |
|
720 } |
|
721 |
|
722 /** |
|
723 * Used for converting object to a string |
|
724 */ |
|
725 public function __toString() |
|
726 { |
|
727 return md5(serialize($this->data)); |
|
728 } |
|
729 |
|
730 /** |
|
731 * Remove items that link back to this before destroying this object |
|
732 */ |
|
733 public function __destruct() |
|
734 { |
|
735 if (!gc_enabled()) { |
|
736 if (!empty($this->data['items'])) { |
|
737 foreach ($this->data['items'] as $item) { |
|
738 $item->__destruct(); |
|
739 } |
|
740 unset($item, $this->data['items']); |
|
741 } |
|
742 if (!empty($this->data['ordered_items'])) { |
|
743 foreach ($this->data['ordered_items'] as $item) { |
|
744 $item->__destruct(); |
|
745 } |
|
746 unset($item, $this->data['ordered_items']); |
|
747 } |
|
748 } |
|
749 } |
|
750 |
|
751 /** |
|
752 * Force the given data/URL to be treated as a feed |
|
753 * |
|
754 * This tells SimplePie to ignore the content-type provided by the server. |
|
755 * Be careful when using this option, as it will also disable autodiscovery. |
|
756 * |
|
757 * @since 1.1 |
|
758 * @param bool $enable Force the given data/URL to be treated as a feed |
|
759 */ |
|
760 public function force_feed($enable = false) |
|
761 { |
|
762 $this->force_feed = (bool) $enable; |
|
763 } |
|
764 |
|
765 /** |
|
766 * Set the URL of the feed you want to parse |
|
767 * |
|
768 * This allows you to enter the URL of the feed you want to parse, or the |
|
769 * website you want to try to use auto-discovery on. This takes priority |
|
770 * over any set raw data. |
|
771 * |
|
772 * You can set multiple feeds to mash together by passing an array instead |
|
773 * of a string for the $url. Remember that with each additional feed comes |
|
774 * additional processing and resources. |
|
775 * |
|
776 * @since 1.0 Preview Release |
|
777 * @see set_raw_data() |
|
778 * @param string|array $url This is the URL (or array of URLs) that you want to parse. |
|
779 */ |
|
780 public function set_feed_url($url) |
|
781 { |
|
782 $this->multifeed_url = []; |
|
783 if (is_array($url)) { |
|
784 foreach ($url as $value) { |
|
785 $this->multifeed_url[] = $this->registry->call(Misc::class, 'fix_protocol', [$value, 1]); |
|
786 } |
|
787 } else { |
|
788 $this->feed_url = $this->registry->call(Misc::class, 'fix_protocol', [$url, 1]); |
|
789 $this->permanent_url = $this->feed_url; |
|
790 } |
|
791 } |
|
792 |
|
793 /** |
|
794 * Set an instance of {@see \SimplePie\File} to use as a feed |
|
795 * |
|
796 * @param \SimplePie\File &$file |
|
797 * @return bool True on success, false on failure |
|
798 */ |
|
799 public function set_file(&$file) |
|
800 { |
|
801 if ($file instanceof \SimplePie\File) { |
|
802 $this->feed_url = $file->url; |
|
803 $this->permanent_url = $this->feed_url; |
|
804 $this->file = &$file; |
|
805 return true; |
|
806 } |
|
807 return false; |
|
808 } |
|
809 |
|
810 /** |
|
811 * Set the raw XML data to parse |
|
812 * |
|
813 * Allows you to use a string of RSS/Atom data instead of a remote feed. |
|
814 * |
|
815 * If you have a feed available as a string in PHP, you can tell SimplePie |
|
816 * to parse that data string instead of a remote feed. Any set feed URL |
|
817 * takes precedence. |
|
818 * |
|
819 * @since 1.0 Beta 3 |
|
820 * @param string $data RSS or Atom data as a string. |
|
821 * @see set_feed_url() |
|
822 */ |
|
823 public function set_raw_data($data) |
|
824 { |
|
825 $this->raw_data = $data; |
|
826 } |
|
827 |
|
828 /** |
|
829 * Set the default timeout for fetching remote feeds |
|
830 * |
|
831 * This allows you to change the maximum time the feed's server to respond |
|
832 * and send the feed back. |
|
833 * |
|
834 * @since 1.0 Beta 3 |
|
835 * @param int $timeout The maximum number of seconds to spend waiting to retrieve a feed. |
|
836 */ |
|
837 public function set_timeout($timeout = 10) |
|
838 { |
|
839 $this->timeout = (int) $timeout; |
|
840 } |
|
841 |
|
842 /** |
|
843 * Set custom curl options |
|
844 * |
|
845 * This allows you to change default curl options |
|
846 * |
|
847 * @since 1.0 Beta 3 |
|
848 * @param array $curl_options Curl options to add to default settings |
|
849 */ |
|
850 public function set_curl_options(array $curl_options = []) |
|
851 { |
|
852 $this->curl_options = $curl_options; |
|
853 } |
|
854 |
|
855 /** |
|
856 * Force SimplePie to use fsockopen() instead of cURL |
|
857 * |
|
858 * @since 1.0 Beta 3 |
|
859 * @param bool $enable Force fsockopen() to be used |
|
860 */ |
|
861 public function force_fsockopen($enable = false) |
|
862 { |
|
863 $this->force_fsockopen = (bool) $enable; |
|
864 } |
|
865 |
|
866 /** |
|
867 * Enable/disable caching in SimplePie. |
|
868 * |
|
869 * This option allows you to disable caching all-together in SimplePie. |
|
870 * However, disabling the cache can lead to longer load times. |
|
871 * |
|
872 * @since 1.0 Preview Release |
|
873 * @param bool $enable Enable caching |
|
874 */ |
|
875 public function enable_cache($enable = true) |
|
876 { |
|
877 $this->enable_cache = (bool) $enable; |
|
878 } |
|
879 |
|
880 /** |
|
881 * Set a PSR-16 implementation as cache |
|
882 * |
|
883 * @param CacheInterface $psr16cache The PSR-16 cache implementation |
|
884 * |
|
885 * @return void |
|
886 */ |
|
887 public function set_cache(CacheInterface $cache) |
|
888 { |
|
889 $this->cache = new Psr16($cache); |
|
890 } |
|
891 |
|
892 /** |
|
893 * SimplePie to continue to fall back to expired cache, if enabled, when |
|
894 * feed is unavailable. |
|
895 * |
|
896 * This tells SimplePie to ignore any file errors and fall back to cache |
|
897 * instead. This only works if caching is enabled and cached content |
|
898 * still exists. |
|
899 * |
|
900 * @deprecated since SimplePie 1.8.0, expired cache will not be used anymore. |
|
901 * |
|
902 * @param bool $enable Force use of cache on fail. |
|
903 */ |
|
904 public function force_cache_fallback($enable = false) |
|
905 { |
|
906 // @trigger_error(sprintf('SimplePie\SimplePie::force_cache_fallback() is deprecated since SimplePie 1.8.0, expired cache will not be used anymore.'), \E_USER_DEPRECATED); |
|
907 $this->force_cache_fallback = (bool) $enable; |
|
908 } |
|
909 |
|
910 /** |
|
911 * Set the length of time (in seconds) that the contents of a feed will be |
|
912 * cached |
|
913 * |
|
914 * @param int $seconds The feed content cache duration |
|
915 */ |
|
916 public function set_cache_duration($seconds = 3600) |
|
917 { |
|
918 $this->cache_duration = (int) $seconds; |
|
919 } |
|
920 |
|
921 /** |
|
922 * Set the length of time (in seconds) that the autodiscovered feed URL will |
|
923 * be cached |
|
924 * |
|
925 * @param int $seconds The autodiscovered feed URL cache duration. |
|
926 */ |
|
927 public function set_autodiscovery_cache_duration($seconds = 604800) |
|
928 { |
|
929 $this->autodiscovery_cache_duration = (int) $seconds; |
|
930 } |
|
931 |
|
932 /** |
|
933 * Set the file system location where the cached files should be stored |
|
934 * |
|
935 * @deprecated since SimplePie 1.8.0, use \SimplePie\SimplePie::set_cache() instead. |
|
936 * |
|
937 * @param string $location The file system location. |
|
938 */ |
|
939 public function set_cache_location($location = './cache') |
|
940 { |
|
941 // @trigger_error(sprintf('SimplePie\SimplePie::set_cache_location() is deprecated since SimplePie 1.8.0, please use "SimplePie\SimplePie::set_cache()" instead.'), \E_USER_DEPRECATED); |
|
942 $this->cache_location = (string) $location; |
|
943 } |
|
944 |
|
945 /** |
|
946 * Return the filename (i.e. hash, without path and without extension) of the file to cache a given URL. |
|
947 * |
|
948 * @param string $url The URL of the feed to be cached. |
|
949 * @return string A filename (i.e. hash, without path and without extension). |
|
950 */ |
|
951 public function get_cache_filename($url) |
|
952 { |
|
953 // Append custom parameters to the URL to avoid cache pollution in case of multiple calls with different parameters. |
|
954 $url .= $this->force_feed ? '#force_feed' : ''; |
|
955 $options = []; |
|
956 if ($this->timeout != 10) { |
|
957 $options[CURLOPT_TIMEOUT] = $this->timeout; |
|
958 } |
|
959 if ($this->useragent !== \SimplePie\Misc::get_default_useragent()) { |
|
960 $options[CURLOPT_USERAGENT] = $this->useragent; |
|
961 } |
|
962 if (!empty($this->curl_options)) { |
|
963 foreach ($this->curl_options as $k => $v) { |
|
964 $options[$k] = $v; |
|
965 } |
|
966 } |
|
967 if (!empty($options)) { |
|
968 ksort($options); |
|
969 $url .= '#' . urlencode(var_export($options, true)); |
|
970 } |
|
971 |
|
972 return $this->cache_namefilter->filter($url); |
|
973 } |
|
974 |
|
975 /** |
|
976 * Set whether feed items should be sorted into reverse chronological order |
|
977 * |
|
978 * @param bool $enable Sort as reverse chronological order. |
|
979 */ |
|
980 public function enable_order_by_date($enable = true) |
|
981 { |
|
982 $this->order_by_date = (bool) $enable; |
|
983 } |
|
984 |
|
985 /** |
|
986 * Set the character encoding used to parse the feed |
|
987 * |
|
988 * This overrides the encoding reported by the feed, however it will fall |
|
989 * back to the normal encoding detection if the override fails |
|
990 * |
|
991 * @param string $encoding Character encoding |
|
992 */ |
|
993 public function set_input_encoding($encoding = false) |
|
994 { |
|
995 if ($encoding) { |
|
996 $this->input_encoding = (string) $encoding; |
|
997 } else { |
|
998 $this->input_encoding = false; |
|
999 } |
|
1000 } |
|
1001 |
|
1002 /** |
|
1003 * Set how much feed autodiscovery to do |
|
1004 * |
|
1005 * @see \SimplePie\SimplePie::LOCATOR_NONE |
|
1006 * @see \SimplePie\SimplePie::LOCATOR_AUTODISCOVERY |
|
1007 * @see \SimplePie\SimplePie::LOCATOR_LOCAL_EXTENSION |
|
1008 * @see \SimplePie\SimplePie::LOCATOR_LOCAL_BODY |
|
1009 * @see \SimplePie\SimplePie::LOCATOR_REMOTE_EXTENSION |
|
1010 * @see \SimplePie\SimplePie::LOCATOR_REMOTE_BODY |
|
1011 * @see \SimplePie\SimplePie::LOCATOR_ALL |
|
1012 * @param int $level Feed Autodiscovery Level (level can be a combination of the above constants, see bitwise OR operator) |
|
1013 */ |
|
1014 public function set_autodiscovery_level($level = self::LOCATOR_ALL) |
|
1015 { |
|
1016 $this->autodiscovery = (int) $level; |
|
1017 } |
|
1018 |
|
1019 /** |
|
1020 * Get the class registry |
|
1021 * |
|
1022 * Use this to override SimplePie's default classes |
|
1023 * @see \SimplePie\Registry |
|
1024 * |
|
1025 * @return Registry |
|
1026 */ |
|
1027 public function &get_registry() |
|
1028 { |
|
1029 return $this->registry; |
|
1030 } |
|
1031 |
|
1032 /** |
|
1033 * Set which class SimplePie uses for caching |
|
1034 * |
|
1035 * @deprecated since SimplePie 1.3, use {@see set_cache()} instead |
|
1036 * |
|
1037 * @param string $class Name of custom class |
|
1038 * |
|
1039 * @return boolean True on success, false otherwise |
|
1040 */ |
|
1041 public function set_cache_class($class = Cache::class) |
|
1042 { |
|
1043 // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::set_cache()" instead.', __METHOD__), \E_USER_DEPRECATED); |
|
1044 |
|
1045 return $this->registry->register(Cache::class, $class, true); |
|
1046 } |
|
1047 |
|
1048 /** |
|
1049 * Set which class SimplePie uses for auto-discovery |
|
1050 * |
|
1051 * @deprecated since SimplePie 1.3, use {@see get_registry()} instead |
|
1052 * |
|
1053 * @param string $class Name of custom class |
|
1054 * |
|
1055 * @return boolean True on success, false otherwise |
|
1056 */ |
|
1057 public function set_locator_class($class = Locator::class) |
|
1058 { |
|
1059 // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); |
|
1060 |
|
1061 return $this->registry->register(Locator::class, $class, true); |
|
1062 } |
|
1063 |
|
1064 /** |
|
1065 * Set which class SimplePie uses for XML parsing |
|
1066 * |
|
1067 * @deprecated since SimplePie 1.3, use {@see get_registry()} instead |
|
1068 * |
|
1069 * @param string $class Name of custom class |
|
1070 * |
|
1071 * @return boolean True on success, false otherwise |
|
1072 */ |
|
1073 public function set_parser_class($class = Parser::class) |
|
1074 { |
|
1075 // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); |
|
1076 |
|
1077 return $this->registry->register(Parser::class, $class, true); |
|
1078 } |
|
1079 |
|
1080 /** |
|
1081 * Set which class SimplePie uses for remote file fetching |
|
1082 * |
|
1083 * @deprecated since SimplePie 1.3, use {@see get_registry()} instead |
|
1084 * |
|
1085 * @param string $class Name of custom class |
|
1086 * |
|
1087 * @return boolean True on success, false otherwise |
|
1088 */ |
|
1089 public function set_file_class($class = File::class) |
|
1090 { |
|
1091 // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); |
|
1092 |
|
1093 return $this->registry->register(File::class, $class, true); |
|
1094 } |
|
1095 |
|
1096 /** |
|
1097 * Set which class SimplePie uses for data sanitization |
|
1098 * |
|
1099 * @deprecated since SimplePie 1.3, use {@see get_registry()} instead |
|
1100 * |
|
1101 * @param string $class Name of custom class |
|
1102 * |
|
1103 * @return boolean True on success, false otherwise |
|
1104 */ |
|
1105 public function set_sanitize_class($class = Sanitize::class) |
|
1106 { |
|
1107 // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); |
|
1108 |
|
1109 return $this->registry->register(Sanitize::class, $class, true); |
|
1110 } |
|
1111 |
|
1112 /** |
|
1113 * Set which class SimplePie uses for handling feed items |
|
1114 * |
|
1115 * @deprecated since SimplePie 1.3, use {@see get_registry()} instead |
|
1116 * |
|
1117 * @param string $class Name of custom class |
|
1118 * |
|
1119 * @return boolean True on success, false otherwise |
|
1120 */ |
|
1121 public function set_item_class($class = Item::class) |
|
1122 { |
|
1123 // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); |
|
1124 |
|
1125 return $this->registry->register(Item::class, $class, true); |
|
1126 } |
|
1127 |
|
1128 /** |
|
1129 * Set which class SimplePie uses for handling author data |
|
1130 * |
|
1131 * @deprecated since SimplePie 1.3, use {@see get_registry()} instead |
|
1132 * |
|
1133 * @param string $class Name of custom class |
|
1134 * |
|
1135 * @return boolean True on success, false otherwise |
|
1136 */ |
|
1137 public function set_author_class($class = Author::class) |
|
1138 { |
|
1139 // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); |
|
1140 |
|
1141 return $this->registry->register(Author::class, $class, true); |
|
1142 } |
|
1143 |
|
1144 /** |
|
1145 * Set which class SimplePie uses for handling category data |
|
1146 * |
|
1147 * @deprecated since SimplePie 1.3, use {@see get_registry()} instead |
|
1148 * |
|
1149 * @param string $class Name of custom class |
|
1150 * |
|
1151 * @return boolean True on success, false otherwise |
|
1152 */ |
|
1153 public function set_category_class($class = Category::class) |
|
1154 { |
|
1155 // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); |
|
1156 |
|
1157 return $this->registry->register(Category::class, $class, true); |
|
1158 } |
|
1159 |
|
1160 /** |
|
1161 * Set which class SimplePie uses for feed enclosures |
|
1162 * |
|
1163 * @deprecated since SimplePie 1.3, use {@see get_registry()} instead |
|
1164 * |
|
1165 * @param string $class Name of custom class |
|
1166 * |
|
1167 * @return boolean True on success, false otherwise |
|
1168 */ |
|
1169 public function set_enclosure_class($class = Enclosure::class) |
|
1170 { |
|
1171 // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); |
|
1172 |
|
1173 return $this->registry->register(Enclosure::class, $class, true); |
|
1174 } |
|
1175 |
|
1176 /** |
|
1177 * Set which class SimplePie uses for `<media:text>` captions |
|
1178 * |
|
1179 * @deprecated since SimplePie 1.3, use {@see get_registry()} instead |
|
1180 * |
|
1181 * @param string $class Name of custom class |
|
1182 * |
|
1183 * @return boolean True on success, false otherwise |
|
1184 */ |
|
1185 public function set_caption_class($class = Caption::class) |
|
1186 { |
|
1187 // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); |
|
1188 |
|
1189 return $this->registry->register(Caption::class, $class, true); |
|
1190 } |
|
1191 |
|
1192 /** |
|
1193 * Set which class SimplePie uses for `<media:copyright>` |
|
1194 * |
|
1195 * @deprecated since SimplePie 1.3, use {@see get_registry()} instead |
|
1196 * |
|
1197 * @param string $class Name of custom class |
|
1198 * |
|
1199 * @return boolean True on success, false otherwise |
|
1200 */ |
|
1201 public function set_copyright_class($class = Copyright::class) |
|
1202 { |
|
1203 // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); |
|
1204 |
|
1205 return $this->registry->register(Copyright::class, $class, true); |
|
1206 } |
|
1207 |
|
1208 /** |
|
1209 * Set which class SimplePie uses for `<media:credit>` |
|
1210 * |
|
1211 * @deprecated since SimplePie 1.3, use {@see get_registry()} instead |
|
1212 * |
|
1213 * @param string $class Name of custom class |
|
1214 * |
|
1215 * @return boolean True on success, false otherwise |
|
1216 */ |
|
1217 public function set_credit_class($class = Credit::class) |
|
1218 { |
|
1219 // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); |
|
1220 |
|
1221 return $this->registry->register(Credit::class, $class, true); |
|
1222 } |
|
1223 |
|
1224 /** |
|
1225 * Set which class SimplePie uses for `<media:rating>` |
|
1226 * |
|
1227 * @deprecated since SimplePie 1.3, use {@see get_registry()} instead |
|
1228 * |
|
1229 * @param string $class Name of custom class |
|
1230 * |
|
1231 * @return boolean True on success, false otherwise |
|
1232 */ |
|
1233 public function set_rating_class($class = Rating::class) |
|
1234 { |
|
1235 // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); |
|
1236 |
|
1237 return $this->registry->register(Rating::class, $class, true); |
|
1238 } |
|
1239 |
|
1240 /** |
|
1241 * Set which class SimplePie uses for `<media:restriction>` |
|
1242 * |
|
1243 * @deprecated since SimplePie 1.3, use {@see get_registry()} instead |
|
1244 * |
|
1245 * @param string $class Name of custom class |
|
1246 * |
|
1247 * @return boolean True on success, false otherwise |
|
1248 */ |
|
1249 public function set_restriction_class($class = Restriction::class) |
|
1250 { |
|
1251 // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); |
|
1252 |
|
1253 return $this->registry->register(Restriction::class, $class, true); |
|
1254 } |
|
1255 |
|
1256 /** |
|
1257 * Set which class SimplePie uses for content-type sniffing |
|
1258 * |
|
1259 * @deprecated since SimplePie 1.3, use {@see get_registry()} instead |
|
1260 * |
|
1261 * @param string $class Name of custom class |
|
1262 * |
|
1263 * @return boolean True on success, false otherwise |
|
1264 */ |
|
1265 public function set_content_type_sniffer_class($class = Sniffer::class) |
|
1266 { |
|
1267 // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); |
|
1268 |
|
1269 return $this->registry->register(Sniffer::class, $class, true); |
|
1270 } |
|
1271 |
|
1272 /** |
|
1273 * Set which class SimplePie uses item sources |
|
1274 * |
|
1275 * @deprecated since SimplePie 1.3, use {@see get_registry()} instead |
|
1276 * |
|
1277 * @param string $class Name of custom class |
|
1278 * |
|
1279 * @return boolean True on success, false otherwise |
|
1280 */ |
|
1281 public function set_source_class($class = Source::class) |
|
1282 { |
|
1283 // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED); |
|
1284 |
|
1285 return $this->registry->register(Source::class, $class, true); |
|
1286 } |
|
1287 |
|
1288 /** |
|
1289 * Set the user agent string |
|
1290 * |
|
1291 * @param string $ua New user agent string. |
|
1292 */ |
|
1293 public function set_useragent($ua = null) |
|
1294 { |
|
1295 if ($ua === null) { |
|
1296 $ua = \SimplePie\Misc::get_default_useragent(); |
|
1297 } |
|
1298 |
|
1299 $this->useragent = (string) $ua; |
|
1300 } |
|
1301 |
|
1302 /** |
|
1303 * Set a namefilter to modify the cache filename with |
|
1304 * |
|
1305 * @param NameFilter $filter |
|
1306 * |
|
1307 * @return void |
|
1308 */ |
|
1309 public function set_cache_namefilter(NameFilter $filter): void |
|
1310 { |
|
1311 $this->cache_namefilter = $filter; |
|
1312 } |
|
1313 |
|
1314 /** |
|
1315 * Set callback function to create cache filename with |
|
1316 * |
|
1317 * @deprecated since SimplePie 1.8.0, use {@see set_cache_namefilter()} instead |
|
1318 * |
|
1319 * @param mixed $function Callback function |
|
1320 */ |
|
1321 public function set_cache_name_function($function = 'md5') |
|
1322 { |
|
1323 // trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.8.0, please use "SimplePie\SimplePie::set_cache_namefilter()" instead.', __METHOD__), \E_USER_DEPRECATED); |
|
1324 |
|
1325 if (is_callable($function)) { |
|
1326 $this->cache_name_function = $function; |
|
1327 |
|
1328 $this->set_cache_namefilter(new CallableNameFilter($this->cache_name_function)); |
|
1329 } |
|
1330 } |
|
1331 |
|
1332 /** |
|
1333 * Set options to make SP as fast as possible |
|
1334 * |
|
1335 * Forgoes a substantial amount of data sanitization in favor of speed. This |
|
1336 * turns SimplePie into a dumb parser of feeds. |
|
1337 * |
|
1338 * @param bool $set Whether to set them or not |
|
1339 */ |
|
1340 public function set_stupidly_fast($set = false) |
|
1341 { |
|
1342 if ($set) { |
|
1343 $this->enable_order_by_date(false); |
|
1344 $this->remove_div(false); |
|
1345 $this->strip_comments(false); |
|
1346 $this->strip_htmltags(false); |
|
1347 $this->strip_attributes(false); |
|
1348 $this->add_attributes(false); |
|
1349 $this->set_image_handler(false); |
|
1350 $this->set_https_domains([]); |
|
1351 } |
|
1352 } |
|
1353 |
|
1354 /** |
|
1355 * Set maximum number of feeds to check with autodiscovery |
|
1356 * |
|
1357 * @param int $max Maximum number of feeds to check |
|
1358 */ |
|
1359 public function set_max_checked_feeds($max = 10) |
|
1360 { |
|
1361 $this->max_checked_feeds = (int) $max; |
|
1362 } |
|
1363 |
|
1364 public function remove_div($enable = true) |
|
1365 { |
|
1366 $this->sanitize->remove_div($enable); |
|
1367 } |
|
1368 |
|
1369 public function strip_htmltags($tags = '', $encode = null) |
|
1370 { |
|
1371 if ($tags === '') { |
|
1372 $tags = $this->strip_htmltags; |
|
1373 } |
|
1374 $this->sanitize->strip_htmltags($tags); |
|
1375 if ($encode !== null) { |
|
1376 $this->sanitize->encode_instead_of_strip($tags); |
|
1377 } |
|
1378 } |
|
1379 |
|
1380 public function encode_instead_of_strip($enable = true) |
|
1381 { |
|
1382 $this->sanitize->encode_instead_of_strip($enable); |
|
1383 } |
|
1384 |
|
1385 public function rename_attributes($attribs = '') |
|
1386 { |
|
1387 if ($attribs === '') { |
|
1388 $attribs = $this->rename_attributes; |
|
1389 } |
|
1390 $this->sanitize->rename_attributes($attribs); |
|
1391 } |
|
1392 |
|
1393 public function strip_attributes($attribs = '') |
|
1394 { |
|
1395 if ($attribs === '') { |
|
1396 $attribs = $this->strip_attributes; |
|
1397 } |
|
1398 $this->sanitize->strip_attributes($attribs); |
|
1399 } |
|
1400 |
|
1401 public function add_attributes($attribs = '') |
|
1402 { |
|
1403 if ($attribs === '') { |
|
1404 $attribs = $this->add_attributes; |
|
1405 } |
|
1406 $this->sanitize->add_attributes($attribs); |
|
1407 } |
|
1408 |
|
1409 /** |
|
1410 * Set the output encoding |
|
1411 * |
|
1412 * Allows you to override SimplePie's output to match that of your webpage. |
|
1413 * This is useful for times when your webpages are not being served as |
|
1414 * UTF-8. This setting will be obeyed by {@see handle_content_type()}, and |
|
1415 * is similar to {@see set_input_encoding()}. |
|
1416 * |
|
1417 * It should be noted, however, that not all character encodings can support |
|
1418 * all characters. If your page is being served as ISO-8859-1 and you try |
|
1419 * to display a Japanese feed, you'll likely see garbled characters. |
|
1420 * Because of this, it is highly recommended to ensure that your webpages |
|
1421 * are served as UTF-8. |
|
1422 * |
|
1423 * The number of supported character encodings depends on whether your web |
|
1424 * host supports {@link http://php.net/mbstring mbstring}, |
|
1425 * {@link http://php.net/iconv iconv}, or both. See |
|
1426 * {@link http://simplepie.org/wiki/faq/Supported_Character_Encodings} for |
|
1427 * more information. |
|
1428 * |
|
1429 * @param string $encoding |
|
1430 */ |
|
1431 public function set_output_encoding($encoding = 'UTF-8') |
|
1432 { |
|
1433 $this->sanitize->set_output_encoding($encoding); |
|
1434 } |
|
1435 |
|
1436 public function strip_comments($strip = false) |
|
1437 { |
|
1438 $this->sanitize->strip_comments($strip); |
|
1439 } |
|
1440 |
|
1441 /** |
|
1442 * Set element/attribute key/value pairs of HTML attributes |
|
1443 * containing URLs that need to be resolved relative to the feed |
|
1444 * |
|
1445 * Defaults to |a|@href, |area|@href, |blockquote|@cite, |del|@cite, |
|
1446 * |form|@action, |img|@longdesc, |img|@src, |input|@src, |ins|@cite, |
|
1447 * |q|@cite |
|
1448 * |
|
1449 * @since 1.0 |
|
1450 * @param array|null $element_attribute Element/attribute key/value pairs, null for default |
|
1451 */ |
|
1452 public function set_url_replacements($element_attribute = null) |
|
1453 { |
|
1454 $this->sanitize->set_url_replacements($element_attribute); |
|
1455 } |
|
1456 |
|
1457 /** |
|
1458 * Set the list of domains for which to force HTTPS. |
|
1459 * @see \SimplePie\Sanitize::set_https_domains() |
|
1460 * @param array List of HTTPS domains. Example array('biz', 'example.com', 'example.org', 'www.example.net'). |
|
1461 */ |
|
1462 public function set_https_domains($domains = []) |
|
1463 { |
|
1464 if (is_array($domains)) { |
|
1465 $this->sanitize->set_https_domains($domains); |
|
1466 } |
|
1467 } |
|
1468 |
|
1469 /** |
|
1470 * Set the handler to enable the display of cached images. |
|
1471 * |
|
1472 * @param string $page Web-accessible path to the handler_image.php file. |
|
1473 * @param string $qs The query string that the value should be passed to. |
|
1474 */ |
|
1475 public function set_image_handler($page = false, $qs = 'i') |
|
1476 { |
|
1477 if ($page !== false) { |
|
1478 $this->sanitize->set_image_handler($page . '?' . $qs . '='); |
|
1479 } else { |
|
1480 $this->image_handler = ''; |
|
1481 } |
|
1482 } |
|
1483 |
|
1484 /** |
|
1485 * Set the limit for items returned per-feed with multifeeds |
|
1486 * |
|
1487 * @param integer $limit The maximum number of items to return. |
|
1488 */ |
|
1489 public function set_item_limit($limit = 0) |
|
1490 { |
|
1491 $this->item_limit = (int) $limit; |
|
1492 } |
|
1493 |
|
1494 /** |
|
1495 * Enable throwing exceptions |
|
1496 * |
|
1497 * @param boolean $enable Should we throw exceptions, or use the old-style error property? |
|
1498 */ |
|
1499 public function enable_exceptions($enable = true) |
|
1500 { |
|
1501 $this->enable_exceptions = $enable; |
|
1502 } |
|
1503 |
|
1504 /** |
|
1505 * Initialize the feed object |
|
1506 * |
|
1507 * This is what makes everything happen. Period. This is where all of the |
|
1508 * configuration options get processed, feeds are fetched, cached, and |
|
1509 * parsed, and all of that other good stuff. |
|
1510 * |
|
1511 * @return boolean True if successful, false otherwise |
|
1512 */ |
|
1513 public function init() |
|
1514 { |
|
1515 // Check absolute bare minimum requirements. |
|
1516 if (!extension_loaded('xml') || !extension_loaded('pcre')) { |
|
1517 $this->error = 'XML or PCRE extensions not loaded!'; |
|
1518 return false; |
|
1519 } |
|
1520 // Then check the xml extension is sane (i.e., libxml 2.7.x issue on PHP < 5.2.9 and libxml 2.7.0 to 2.7.2 on any version) if we don't have xmlreader. |
|
1521 elseif (!extension_loaded('xmlreader')) { |
|
1522 static $xml_is_sane = null; |
|
1523 if ($xml_is_sane === null) { |
|
1524 $parser_check = xml_parser_create(); |
|
1525 xml_parse_into_struct($parser_check, '<foo>&</foo>', $values); |
|
1526 xml_parser_free($parser_check); |
|
1527 $xml_is_sane = isset($values[0]['value']); |
|
1528 } |
|
1529 if (!$xml_is_sane) { |
|
1530 return false; |
|
1531 } |
|
1532 } |
|
1533 |
|
1534 // The default sanitize class gets set in the constructor, check if it has |
|
1535 // changed. |
|
1536 if ($this->registry->get_class(Sanitize::class) !== 'SimplePie\Sanitize') { |
|
1537 $this->sanitize = $this->registry->create(Sanitize::class); |
|
1538 } |
|
1539 if (method_exists($this->sanitize, 'set_registry')) { |
|
1540 $this->sanitize->set_registry($this->registry); |
|
1541 } |
|
1542 |
|
1543 // Pass whatever was set with config options over to the sanitizer. |
|
1544 // Pass the classes in for legacy support; new classes should use the registry instead |
|
1545 $this->sanitize->pass_cache_data( |
|
1546 $this->enable_cache, |
|
1547 $this->cache_location, |
|
1548 $this->cache_namefilter, |
|
1549 $this->registry->get_class(Cache::class), |
|
1550 $this->cache |
|
1551 ); |
|
1552 $this->sanitize->pass_file_data($this->registry->get_class(File::class), $this->timeout, $this->useragent, $this->force_fsockopen, $this->curl_options); |
|
1553 |
|
1554 if (!empty($this->multifeed_url)) { |
|
1555 $i = 0; |
|
1556 $success = 0; |
|
1557 $this->multifeed_objects = []; |
|
1558 $this->error = []; |
|
1559 foreach ($this->multifeed_url as $url) { |
|
1560 $this->multifeed_objects[$i] = clone $this; |
|
1561 $this->multifeed_objects[$i]->set_feed_url($url); |
|
1562 $single_success = $this->multifeed_objects[$i]->init(); |
|
1563 $success |= $single_success; |
|
1564 if (!$single_success) { |
|
1565 $this->error[$i] = $this->multifeed_objects[$i]->error(); |
|
1566 } |
|
1567 $i++; |
|
1568 } |
|
1569 return (bool) $success; |
|
1570 } elseif ($this->feed_url === null && $this->raw_data === null) { |
|
1571 return false; |
|
1572 } |
|
1573 |
|
1574 $this->error = null; |
|
1575 $this->data = []; |
|
1576 $this->check_modified = false; |
|
1577 $this->multifeed_objects = []; |
|
1578 $cache = false; |
|
1579 |
|
1580 if ($this->feed_url !== null) { |
|
1581 $parsed_feed_url = $this->registry->call(Misc::class, 'parse_url', [$this->feed_url]); |
|
1582 |
|
1583 // Decide whether to enable caching |
|
1584 if ($this->enable_cache && $parsed_feed_url['scheme'] !== '') { |
|
1585 $cache = $this->get_cache($this->feed_url); |
|
1586 } |
|
1587 |
|
1588 // Fetch the data via \SimplePie\File into $this->raw_data |
|
1589 if (($fetched = $this->fetch_data($cache)) === true) { |
|
1590 return true; |
|
1591 } elseif ($fetched === false) { |
|
1592 return false; |
|
1593 } |
|
1594 |
|
1595 [$headers, $sniffed] = $fetched; |
|
1596 } |
|
1597 |
|
1598 // Empty response check |
|
1599 if (empty($this->raw_data)) { |
|
1600 $this->error = "A feed could not be found at `$this->feed_url`. Empty body."; |
|
1601 $this->registry->call(Misc::class, 'error', [$this->error, E_USER_NOTICE, __FILE__, __LINE__]); |
|
1602 return false; |
|
1603 } |
|
1604 |
|
1605 // Set up array of possible encodings |
|
1606 $encodings = []; |
|
1607 |
|
1608 // First check to see if input has been overridden. |
|
1609 if ($this->input_encoding !== false) { |
|
1610 $encodings[] = strtoupper($this->input_encoding); |
|
1611 } |
|
1612 |
|
1613 $application_types = ['application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity']; |
|
1614 $text_types = ['text/xml', 'text/xml-external-parsed-entity']; |
|
1615 |
|
1616 // RFC 3023 (only applies to sniffed content) |
|
1617 if (isset($sniffed)) { |
|
1618 if (in_array($sniffed, $application_types) || substr($sniffed, 0, 12) === 'application/' && substr($sniffed, -4) === '+xml') { |
|
1619 if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) { |
|
1620 $encodings[] = strtoupper($charset[1]); |
|
1621 } |
|
1622 $encodings = array_merge($encodings, $this->registry->call(Misc::class, 'xml_encoding', [$this->raw_data, &$this->registry])); |
|
1623 $encodings[] = 'UTF-8'; |
|
1624 } elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml') { |
|
1625 if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) { |
|
1626 $encodings[] = strtoupper($charset[1]); |
|
1627 } |
|
1628 $encodings[] = 'US-ASCII'; |
|
1629 } |
|
1630 // Text MIME-type default |
|
1631 elseif (substr($sniffed, 0, 5) === 'text/') { |
|
1632 $encodings[] = 'UTF-8'; |
|
1633 } |
|
1634 } |
|
1635 |
|
1636 // Fallback to XML 1.0 Appendix F.1/UTF-8/ISO-8859-1 |
|
1637 $encodings = array_merge($encodings, $this->registry->call(Misc::class, 'xml_encoding', [$this->raw_data, &$this->registry])); |
|
1638 $encodings[] = 'UTF-8'; |
|
1639 $encodings[] = 'ISO-8859-1'; |
|
1640 |
|
1641 // There's no point in trying an encoding twice |
|
1642 $encodings = array_unique($encodings); |
|
1643 |
|
1644 // Loop through each possible encoding, till we return something, or run out of possibilities |
|
1645 foreach ($encodings as $encoding) { |
|
1646 // Change the encoding to UTF-8 (as we always use UTF-8 internally) |
|
1647 if ($utf8_data = $this->registry->call(Misc::class, 'change_encoding', [$this->raw_data, $encoding, 'UTF-8'])) { |
|
1648 // Create new parser |
|
1649 $parser = $this->registry->create(Parser::class); |
|
1650 |
|
1651 // If it's parsed fine |
|
1652 if ($parser->parse($utf8_data, 'UTF-8', $this->permanent_url)) { |
|
1653 $this->data = $parser->get_data(); |
|
1654 if (!($this->get_type() & ~self::TYPE_NONE)) { |
|
1655 $this->error = "A feed could not be found at `$this->feed_url`. This does not appear to be a valid RSS or Atom feed."; |
|
1656 $this->registry->call(Misc::class, 'error', [$this->error, E_USER_NOTICE, __FILE__, __LINE__]); |
|
1657 return false; |
|
1658 } |
|
1659 |
|
1660 if (isset($headers)) { |
|
1661 $this->data['headers'] = $headers; |
|
1662 } |
|
1663 $this->data['build'] = \SimplePie\Misc::get_build(); |
|
1664 |
|
1665 // Cache the file if caching is enabled |
|
1666 $this->data['cache_expiration_time'] = $this->cache_duration + time(); |
|
1667 if ($cache && !$cache->set_data($this->get_cache_filename($this->feed_url), $this->data, $this->cache_duration)) { |
|
1668 trigger_error("$this->cache_location is not writable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); |
|
1669 } |
|
1670 return true; |
|
1671 } |
|
1672 } |
|
1673 } |
|
1674 |
|
1675 if (isset($parser)) { |
|
1676 // We have an error, just set \SimplePie\Misc::error to it and quit |
|
1677 $this->error = $this->feed_url; |
|
1678 $this->error .= sprintf(' is invalid XML, likely due to invalid characters. XML error: %s at line %d, column %d', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column()); |
|
1679 } else { |
|
1680 $this->error = 'The data could not be converted to UTF-8.'; |
|
1681 if (!extension_loaded('mbstring') && !extension_loaded('iconv') && !class_exists('\UConverter')) { |
|
1682 $this->error .= ' You MUST have either the iconv, mbstring or intl (PHP 5.5+) extension installed and enabled.'; |
|
1683 } else { |
|
1684 $missingExtensions = []; |
|
1685 if (!extension_loaded('iconv')) { |
|
1686 $missingExtensions[] = 'iconv'; |
|
1687 } |
|
1688 if (!extension_loaded('mbstring')) { |
|
1689 $missingExtensions[] = 'mbstring'; |
|
1690 } |
|
1691 if (!class_exists('\UConverter')) { |
|
1692 $missingExtensions[] = 'intl (PHP 5.5+)'; |
|
1693 } |
|
1694 $this->error .= ' Try installing/enabling the ' . implode(' or ', $missingExtensions) . ' extension.'; |
|
1695 } |
|
1696 } |
|
1697 |
|
1698 $this->registry->call(Misc::class, 'error', [$this->error, E_USER_NOTICE, __FILE__, __LINE__]); |
|
1699 |
|
1700 return false; |
|
1701 } |
|
1702 |
|
1703 /** |
|
1704 * Fetch the data via \SimplePie\File |
|
1705 * |
|
1706 * If the data is already cached, attempt to fetch it from there instead |
|
1707 * @param Base|DataCache|false $cache Cache handler, or false to not load from the cache |
|
1708 * @return array|true Returns true if the data was loaded from the cache, or an array of HTTP headers and sniffed type |
|
1709 */ |
|
1710 protected function fetch_data(&$cache) |
|
1711 { |
|
1712 if (is_object($cache) && $cache instanceof Base) { |
|
1713 // @trigger_error(sprintf('Providing $cache as "\SimplePie\Cache\Base" in %s() is deprecated since SimplePie 1.8.0, please provide "\SimplePie\Cache\DataCache" implementation instead.', __METHOD__), \E_USER_DEPRECATED); |
|
1714 $cache = new BaseDataCache($cache); |
|
1715 } |
|
1716 |
|
1717 if ($cache !== false && !$cache instanceof DataCache) { |
|
1718 throw new InvalidArgumentException(sprintf( |
|
1719 '%s(): Argument #1 ($cache) must be of type %s|false', |
|
1720 __METHOD__, |
|
1721 DataCache::class |
|
1722 ), 1); |
|
1723 } |
|
1724 |
|
1725 $cacheKey = $this->get_cache_filename($this->feed_url); |
|
1726 |
|
1727 // If it's enabled, use the cache |
|
1728 if ($cache) { |
|
1729 // Load the Cache |
|
1730 $this->data = $cache->get_data($cacheKey, []); |
|
1731 |
|
1732 if (!empty($this->data)) { |
|
1733 // If the cache is for an outdated build of SimplePie |
|
1734 if (!isset($this->data['build']) || $this->data['build'] !== \SimplePie\Misc::get_build()) { |
|
1735 $cache->delete_data($cacheKey); |
|
1736 $this->data = []; |
|
1737 } |
|
1738 // If we've hit a collision just rerun it with caching disabled |
|
1739 elseif (isset($this->data['url']) && $this->data['url'] !== $this->feed_url) { |
|
1740 $cache = false; |
|
1741 $this->data = []; |
|
1742 } |
|
1743 // If we've got a non feed_url stored (if the page isn't actually a feed, or is a redirect) use that URL. |
|
1744 elseif (isset($this->data['feed_url'])) { |
|
1745 // Do not need to do feed autodiscovery yet. |
|
1746 if ($this->data['feed_url'] !== $this->data['url']) { |
|
1747 $this->set_feed_url($this->data['feed_url']); |
|
1748 $this->data['url'] = $this->data['feed_url']; |
|
1749 |
|
1750 $cache->set_data($this->get_cache_filename($this->feed_url), $this->data, $this->autodiscovery_cache_duration); |
|
1751 |
|
1752 return $this->init(); |
|
1753 } |
|
1754 |
|
1755 $cache->delete_data($this->get_cache_filename($this->feed_url)); |
|
1756 $this->data = []; |
|
1757 } |
|
1758 // Check if the cache has been updated |
|
1759 elseif (isset($this->data['cache_expiration_time']) && $this->data['cache_expiration_time'] > time()) { |
|
1760 // Want to know if we tried to send last-modified and/or etag headers |
|
1761 // when requesting this file. (Note that it's up to the file to |
|
1762 // support this, but we don't always send the headers either.) |
|
1763 $this->check_modified = true; |
|
1764 if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag'])) { |
|
1765 $headers = [ |
|
1766 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1', |
|
1767 ]; |
|
1768 if (isset($this->data['headers']['last-modified'])) { |
|
1769 $headers['if-modified-since'] = $this->data['headers']['last-modified']; |
|
1770 } |
|
1771 if (isset($this->data['headers']['etag'])) { |
|
1772 $headers['if-none-match'] = $this->data['headers']['etag']; |
|
1773 } |
|
1774 |
|
1775 $file = $this->registry->create(File::class, [$this->feed_url, $this->timeout / 10, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options]); |
|
1776 $this->status_code = $file->status_code; |
|
1777 |
|
1778 if ($file->success) { |
|
1779 if ($file->status_code === 304) { |
|
1780 // Set raw_data to false here too, to signify that the cache |
|
1781 // is still valid. |
|
1782 $this->raw_data = false; |
|
1783 $cache->set_data($cacheKey, $this->data, $this->cache_duration); |
|
1784 return true; |
|
1785 } |
|
1786 } else { |
|
1787 $this->check_modified = false; |
|
1788 if ($this->force_cache_fallback) { |
|
1789 $cache->set_data($cacheKey, $this->data, $this->cache_duration); |
|
1790 return true; |
|
1791 } |
|
1792 |
|
1793 unset($file); |
|
1794 } |
|
1795 } |
|
1796 } |
|
1797 // If the cache is still valid, just return true |
|
1798 else { |
|
1799 $this->raw_data = false; |
|
1800 return true; |
|
1801 } |
|
1802 } |
|
1803 // If the cache is empty |
|
1804 else { |
|
1805 $this->data = []; |
|
1806 } |
|
1807 } |
|
1808 |
|
1809 // If we don't already have the file (it'll only exist if we've opened it to check if the cache has been modified), open it. |
|
1810 if (!isset($file)) { |
|
1811 if ($this->file instanceof \SimplePie\File && $this->file->url === $this->feed_url) { |
|
1812 $file = &$this->file; |
|
1813 } else { |
|
1814 $headers = [ |
|
1815 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1', |
|
1816 ]; |
|
1817 $file = $this->registry->create(File::class, [$this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options]); |
|
1818 } |
|
1819 } |
|
1820 $this->status_code = $file->status_code; |
|
1821 |
|
1822 // If the file connection has an error, set SimplePie::error to that and quit |
|
1823 if (!$file->success && !($file->method & self::FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300))) { |
|
1824 $this->error = $file->error; |
|
1825 return !empty($this->data); |
|
1826 } |
|
1827 |
|
1828 if (!$this->force_feed) { |
|
1829 // Check if the supplied URL is a feed, if it isn't, look for it. |
|
1830 $locate = $this->registry->create(Locator::class, [&$file, $this->timeout, $this->useragent, $this->max_checked_feeds, $this->force_fsockopen, $this->curl_options]); |
|
1831 |
|
1832 if (!$locate->is_feed($file)) { |
|
1833 $copyStatusCode = $file->status_code; |
|
1834 $copyContentType = $file->headers['content-type']; |
|
1835 try { |
|
1836 $microformats = false; |
|
1837 if (class_exists('DOMXpath') && function_exists('Mf2\parse')) { |
|
1838 $doc = new \DOMDocument(); |
|
1839 @$doc->loadHTML($file->body); |
|
1840 $xpath = new \DOMXpath($doc); |
|
1841 // Check for both h-feed and h-entry, as both a feed with no entries |
|
1842 // and a list of entries without an h-feed wrapper are both valid. |
|
1843 $query = '//*[contains(concat(" ", @class, " "), " h-feed ") or '. |
|
1844 'contains(concat(" ", @class, " "), " h-entry ")]'; |
|
1845 $result = $xpath->query($query); |
|
1846 $microformats = $result->length !== 0; |
|
1847 } |
|
1848 // Now also do feed discovery, but if microformats were found don't |
|
1849 // overwrite the current value of file. |
|
1850 $discovered = $locate->find( |
|
1851 $this->autodiscovery, |
|
1852 $this->all_discovered_feeds |
|
1853 ); |
|
1854 if ($microformats) { |
|
1855 if ($hub = $locate->get_rel_link('hub')) { |
|
1856 $self = $locate->get_rel_link('self'); |
|
1857 $this->store_links($file, $hub, $self); |
|
1858 } |
|
1859 // Push the current file onto all_discovered feeds so the user can |
|
1860 // be shown this as one of the options. |
|
1861 if (isset($this->all_discovered_feeds)) { |
|
1862 $this->all_discovered_feeds[] = $file; |
|
1863 } |
|
1864 } else { |
|
1865 if ($discovered) { |
|
1866 $file = $discovered; |
|
1867 } else { |
|
1868 // We need to unset this so that if SimplePie::set_file() has |
|
1869 // been called that object is untouched |
|
1870 unset($file); |
|
1871 $this->error = "A feed could not be found at `$this->feed_url`; the status code is `$copyStatusCode` and content-type is `$copyContentType`"; |
|
1872 $this->registry->call(Misc::class, 'error', [$this->error, E_USER_NOTICE, __FILE__, __LINE__]); |
|
1873 return false; |
|
1874 } |
|
1875 } |
|
1876 } catch (\SimplePie\Exception $e) { |
|
1877 // We need to unset this so that if SimplePie::set_file() has been called that object is untouched |
|
1878 unset($file); |
|
1879 // This is usually because DOMDocument doesn't exist |
|
1880 $this->error = $e->getMessage(); |
|
1881 $this->registry->call(Misc::class, 'error', [$this->error, E_USER_NOTICE, $e->getFile(), $e->getLine()]); |
|
1882 return false; |
|
1883 } |
|
1884 |
|
1885 if ($cache) { |
|
1886 $this->data = [ |
|
1887 'url' => $this->feed_url, |
|
1888 'feed_url' => $file->url, |
|
1889 'build' => \SimplePie\Misc::get_build(), |
|
1890 'cache_expiration_time' => $this->cache_duration + time(), |
|
1891 ]; |
|
1892 |
|
1893 if (!$cache->set_data($cacheKey, $this->data, $this->cache_duration)) { |
|
1894 trigger_error("$this->cache_location is not writable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); |
|
1895 } |
|
1896 } |
|
1897 } |
|
1898 $this->feed_url = $file->url; |
|
1899 $locate = null; |
|
1900 } |
|
1901 |
|
1902 $this->raw_data = $file->body; |
|
1903 $this->permanent_url = $file->permanent_url; |
|
1904 $headers = $file->headers; |
|
1905 $sniffer = $this->registry->create(Sniffer::class, [&$file]); |
|
1906 $sniffed = $sniffer->get_type(); |
|
1907 |
|
1908 return [$headers, $sniffed]; |
|
1909 } |
|
1910 |
|
1911 /** |
|
1912 * Get the error message for the occurred error |
|
1913 * |
|
1914 * @return string|array Error message, or array of messages for multifeeds |
|
1915 */ |
|
1916 public function error() |
|
1917 { |
|
1918 return $this->error; |
|
1919 } |
|
1920 |
|
1921 /** |
|
1922 * Get the last HTTP status code |
|
1923 * |
|
1924 * @return int Status code |
|
1925 */ |
|
1926 public function status_code() |
|
1927 { |
|
1928 return $this->status_code; |
|
1929 } |
|
1930 |
|
1931 /** |
|
1932 * Get the raw XML |
|
1933 * |
|
1934 * This is the same as the old `$feed->enable_xml_dump(true)`, but returns |
|
1935 * the data instead of printing it. |
|
1936 * |
|
1937 * @return string|boolean Raw XML data, false if the cache is used |
|
1938 */ |
|
1939 public function get_raw_data() |
|
1940 { |
|
1941 return $this->raw_data; |
|
1942 } |
|
1943 |
|
1944 /** |
|
1945 * Get the character encoding used for output |
|
1946 * |
|
1947 * @since Preview Release |
|
1948 * @return string |
|
1949 */ |
|
1950 public function get_encoding() |
|
1951 { |
|
1952 return $this->sanitize->output_encoding; |
|
1953 } |
|
1954 |
|
1955 /** |
|
1956 * Send the content-type header with correct encoding |
|
1957 * |
|
1958 * This method ensures that the SimplePie-enabled page is being served with |
|
1959 * the correct {@link http://www.iana.org/assignments/media-types/ mime-type} |
|
1960 * and character encoding HTTP headers (character encoding determined by the |
|
1961 * {@see set_output_encoding} config option). |
|
1962 * |
|
1963 * This won't work properly if any content or whitespace has already been |
|
1964 * sent to the browser, because it relies on PHP's |
|
1965 * {@link http://php.net/header header()} function, and these are the |
|
1966 * circumstances under which the function works. |
|
1967 * |
|
1968 * Because it's setting these settings for the entire page (as is the nature |
|
1969 * of HTTP headers), this should only be used once per page (again, at the |
|
1970 * top). |
|
1971 * |
|
1972 * @param string $mime MIME type to serve the page as |
|
1973 */ |
|
1974 public function handle_content_type($mime = 'text/html') |
|
1975 { |
|
1976 if (!headers_sent()) { |
|
1977 $header = "Content-type: $mime;"; |
|
1978 if ($this->get_encoding()) { |
|
1979 $header .= ' charset=' . $this->get_encoding(); |
|
1980 } else { |
|
1981 $header .= ' charset=UTF-8'; |
|
1982 } |
|
1983 header($header); |
|
1984 } |
|
1985 } |
|
1986 |
|
1987 /** |
|
1988 * Get the type of the feed |
|
1989 * |
|
1990 * This returns a \SimplePie\SimplePie::TYPE_* constant, which can be tested against |
|
1991 * using {@link http://php.net/language.operators.bitwise bitwise operators} |
|
1992 * |
|
1993 * @since 0.8 (usage changed to using constants in 1.0) |
|
1994 * @see \SimplePie\SimplePie::TYPE_NONE Unknown. |
|
1995 * @see \SimplePie\SimplePie::TYPE_RSS_090 RSS 0.90. |
|
1996 * @see \SimplePie\SimplePie::TYPE_RSS_091_NETSCAPE RSS 0.91 (Netscape). |
|
1997 * @see \SimplePie\SimplePie::TYPE_RSS_091_USERLAND RSS 0.91 (Userland). |
|
1998 * @see \SimplePie\SimplePie::TYPE_RSS_091 RSS 0.91. |
|
1999 * @see \SimplePie\SimplePie::TYPE_RSS_092 RSS 0.92. |
|
2000 * @see \SimplePie\SimplePie::TYPE_RSS_093 RSS 0.93. |
|
2001 * @see \SimplePie\SimplePie::TYPE_RSS_094 RSS 0.94. |
|
2002 * @see \SimplePie\SimplePie::TYPE_RSS_10 RSS 1.0. |
|
2003 * @see \SimplePie\SimplePie::TYPE_RSS_20 RSS 2.0.x. |
|
2004 * @see \SimplePie\SimplePie::TYPE_RSS_RDF RDF-based RSS. |
|
2005 * @see \SimplePie\SimplePie::TYPE_RSS_SYNDICATION Non-RDF-based RSS (truly intended as syndication format). |
|
2006 * @see \SimplePie\SimplePie::TYPE_RSS_ALL Any version of RSS. |
|
2007 * @see \SimplePie\SimplePie::TYPE_ATOM_03 Atom 0.3. |
|
2008 * @see \SimplePie\SimplePie::TYPE_ATOM_10 Atom 1.0. |
|
2009 * @see \SimplePie\SimplePie::TYPE_ATOM_ALL Any version of Atom. |
|
2010 * @see \SimplePie\SimplePie::TYPE_ALL Any known/supported feed type. |
|
2011 * @return int \SimplePie\SimplePie::TYPE_* constant |
|
2012 */ |
|
2013 public function get_type() |
|
2014 { |
|
2015 if (!isset($this->data['type'])) { |
|
2016 $this->data['type'] = self::TYPE_ALL; |
|
2017 if (isset($this->data['child'][self::NAMESPACE_ATOM_10]['feed'])) { |
|
2018 $this->data['type'] &= self::TYPE_ATOM_10; |
|
2019 } elseif (isset($this->data['child'][self::NAMESPACE_ATOM_03]['feed'])) { |
|
2020 $this->data['type'] &= self::TYPE_ATOM_03; |
|
2021 } elseif (isset($this->data['child'][self::NAMESPACE_RDF]['RDF'])) { |
|
2022 if (isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_10]['channel']) |
|
2023 || isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_10]['image']) |
|
2024 || isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_10]['item']) |
|
2025 || isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_10]['textinput'])) { |
|
2026 $this->data['type'] &= self::TYPE_RSS_10; |
|
2027 } |
|
2028 if (isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_090]['channel']) |
|
2029 || isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_090]['image']) |
|
2030 || isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_090]['item']) |
|
2031 || isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_090]['textinput'])) { |
|
2032 $this->data['type'] &= self::TYPE_RSS_090; |
|
2033 } |
|
2034 } elseif (isset($this->data['child'][self::NAMESPACE_RSS_20]['rss'])) { |
|
2035 $this->data['type'] &= self::TYPE_RSS_ALL; |
|
2036 if (isset($this->data['child'][self::NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version'])) { |
|
2037 switch (trim($this->data['child'][self::NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version'])) { |
|
2038 case '0.91': |
|
2039 $this->data['type'] &= self::TYPE_RSS_091; |
|
2040 if (isset($this->data['child'][self::NAMESPACE_RSS_20]['rss'][0]['child'][self::NAMESPACE_RSS_20]['skiphours']['hour'][0]['data'])) { |
|
2041 switch (trim($this->data['child'][self::NAMESPACE_RSS_20]['rss'][0]['child'][self::NAMESPACE_RSS_20]['skiphours']['hour'][0]['data'])) { |
|
2042 case '0': |
|
2043 $this->data['type'] &= self::TYPE_RSS_091_NETSCAPE; |
|
2044 break; |
|
2045 |
|
2046 case '24': |
|
2047 $this->data['type'] &= self::TYPE_RSS_091_USERLAND; |
|
2048 break; |
|
2049 } |
|
2050 } |
|
2051 break; |
|
2052 |
|
2053 case '0.92': |
|
2054 $this->data['type'] &= self::TYPE_RSS_092; |
|
2055 break; |
|
2056 |
|
2057 case '0.93': |
|
2058 $this->data['type'] &= self::TYPE_RSS_093; |
|
2059 break; |
|
2060 |
|
2061 case '0.94': |
|
2062 $this->data['type'] &= self::TYPE_RSS_094; |
|
2063 break; |
|
2064 |
|
2065 case '2.0': |
|
2066 $this->data['type'] &= self::TYPE_RSS_20; |
|
2067 break; |
|
2068 } |
|
2069 } |
|
2070 } else { |
|
2071 $this->data['type'] = self::TYPE_NONE; |
|
2072 } |
|
2073 } |
|
2074 return $this->data['type']; |
|
2075 } |
|
2076 |
|
2077 /** |
|
2078 * Get the URL for the feed |
|
2079 * |
|
2080 * When the 'permanent' mode is enabled, returns the original feed URL, |
|
2081 * except in the case of an `HTTP 301 Moved Permanently` status response, |
|
2082 * in which case the location of the first redirection is returned. |
|
2083 * |
|
2084 * When the 'permanent' mode is disabled (default), |
|
2085 * may or may not be different from the URL passed to {@see set_feed_url()}, |
|
2086 * depending on whether auto-discovery was used, and whether there were |
|
2087 * any redirects along the way. |
|
2088 * |
|
2089 * @since Preview Release (previously called `get_feed_url()` since SimplePie 0.8.) |
|
2090 * @todo Support <itunes:new-feed-url> |
|
2091 * @todo Also, |atom:link|@rel=self |
|
2092 * @param bool $permanent Permanent mode to return only the original URL or the first redirection |
|
2093 * iff it is a 301 redirection |
|
2094 * @return string|null |
|
2095 */ |
|
2096 public function subscribe_url($permanent = false) |
|
2097 { |
|
2098 if ($permanent) { |
|
2099 if ($this->permanent_url !== null) { |
|
2100 // sanitize encodes ampersands which are required when used in a url. |
|
2101 return str_replace( |
|
2102 '&', |
|
2103 '&', |
|
2104 $this->sanitize( |
|
2105 $this->permanent_url, |
|
2106 self::CONSTRUCT_IRI |
|
2107 ) |
|
2108 ); |
|
2109 } |
|
2110 } else { |
|
2111 if ($this->feed_url !== null) { |
|
2112 return str_replace( |
|
2113 '&', |
|
2114 '&', |
|
2115 $this->sanitize( |
|
2116 $this->feed_url, |
|
2117 self::CONSTRUCT_IRI |
|
2118 ) |
|
2119 ); |
|
2120 } |
|
2121 } |
|
2122 return null; |
|
2123 } |
|
2124 |
|
2125 /** |
|
2126 * Get data for an feed-level element |
|
2127 * |
|
2128 * This method allows you to get access to ANY element/attribute that is a |
|
2129 * sub-element of the opening feed tag. |
|
2130 * |
|
2131 * The return value is an indexed array of elements matching the given |
|
2132 * namespace and tag name. Each element has `attribs`, `data` and `child` |
|
2133 * subkeys. For `attribs` and `child`, these contain namespace subkeys. |
|
2134 * `attribs` then has one level of associative name => value data (where |
|
2135 * `value` is a string) after the namespace. `child` has tag-indexed keys |
|
2136 * after the namespace, each member of which is an indexed array matching |
|
2137 * this same format. |
|
2138 * |
|
2139 * For example: |
|
2140 * <pre> |
|
2141 * // This is probably a bad example because we already support |
|
2142 * // <media:content> natively, but it shows you how to parse through |
|
2143 * // the nodes. |
|
2144 * $group = $item->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'group'); |
|
2145 * $content = $group[0]['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['content']; |
|
2146 * $file = $content[0]['attribs']['']['url']; |
|
2147 * echo $file; |
|
2148 * </pre> |
|
2149 * |
|
2150 * @since 1.0 |
|
2151 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces |
|
2152 * @param string $namespace The URL of the XML namespace of the elements you're trying to access |
|
2153 * @param string $tag Tag name |
|
2154 * @return array |
|
2155 */ |
|
2156 public function get_feed_tags($namespace, $tag) |
|
2157 { |
|
2158 $type = $this->get_type(); |
|
2159 if ($type & self::TYPE_ATOM_10) { |
|
2160 if (isset($this->data['child'][self::NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag])) { |
|
2161 return $this->data['child'][self::NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag]; |
|
2162 } |
|
2163 } |
|
2164 if ($type & self::TYPE_ATOM_03) { |
|
2165 if (isset($this->data['child'][self::NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag])) { |
|
2166 return $this->data['child'][self::NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag]; |
|
2167 } |
|
2168 } |
|
2169 if ($type & self::TYPE_RSS_RDF) { |
|
2170 if (isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag])) { |
|
2171 return $this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag]; |
|
2172 } |
|
2173 } |
|
2174 if ($type & self::TYPE_RSS_SYNDICATION) { |
|
2175 if (isset($this->data['child'][self::NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag])) { |
|
2176 return $this->data['child'][self::NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag]; |
|
2177 } |
|
2178 } |
|
2179 return null; |
|
2180 } |
|
2181 |
|
2182 /** |
|
2183 * Get data for an channel-level element |
|
2184 * |
|
2185 * This method allows you to get access to ANY element/attribute in the |
|
2186 * channel/header section of the feed. |
|
2187 * |
|
2188 * See {@see SimplePie::get_feed_tags()} for a description of the return value |
|
2189 * |
|
2190 * @since 1.0 |
|
2191 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces |
|
2192 * @param string $namespace The URL of the XML namespace of the elements you're trying to access |
|
2193 * @param string $tag Tag name |
|
2194 * @return array |
|
2195 */ |
|
2196 public function get_channel_tags($namespace, $tag) |
|
2197 { |
|
2198 $type = $this->get_type(); |
|
2199 if ($type & self::TYPE_ATOM_ALL) { |
|
2200 if ($return = $this->get_feed_tags($namespace, $tag)) { |
|
2201 return $return; |
|
2202 } |
|
2203 } |
|
2204 if ($type & self::TYPE_RSS_10) { |
|
2205 if ($channel = $this->get_feed_tags(self::NAMESPACE_RSS_10, 'channel')) { |
|
2206 if (isset($channel[0]['child'][$namespace][$tag])) { |
|
2207 return $channel[0]['child'][$namespace][$tag]; |
|
2208 } |
|
2209 } |
|
2210 } |
|
2211 if ($type & self::TYPE_RSS_090) { |
|
2212 if ($channel = $this->get_feed_tags(self::NAMESPACE_RSS_090, 'channel')) { |
|
2213 if (isset($channel[0]['child'][$namespace][$tag])) { |
|
2214 return $channel[0]['child'][$namespace][$tag]; |
|
2215 } |
|
2216 } |
|
2217 } |
|
2218 if ($type & self::TYPE_RSS_SYNDICATION) { |
|
2219 if ($channel = $this->get_feed_tags(self::NAMESPACE_RSS_20, 'channel')) { |
|
2220 if (isset($channel[0]['child'][$namespace][$tag])) { |
|
2221 return $channel[0]['child'][$namespace][$tag]; |
|
2222 } |
|
2223 } |
|
2224 } |
|
2225 return null; |
|
2226 } |
|
2227 |
|
2228 /** |
|
2229 * Get data for an channel-level element |
|
2230 * |
|
2231 * This method allows you to get access to ANY element/attribute in the |
|
2232 * image/logo section of the feed. |
|
2233 * |
|
2234 * See {@see SimplePie::get_feed_tags()} for a description of the return value |
|
2235 * |
|
2236 * @since 1.0 |
|
2237 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces |
|
2238 * @param string $namespace The URL of the XML namespace of the elements you're trying to access |
|
2239 * @param string $tag Tag name |
|
2240 * @return array |
|
2241 */ |
|
2242 public function get_image_tags($namespace, $tag) |
|
2243 { |
|
2244 $type = $this->get_type(); |
|
2245 if ($type & self::TYPE_RSS_10) { |
|
2246 if ($image = $this->get_feed_tags(self::NAMESPACE_RSS_10, 'image')) { |
|
2247 if (isset($image[0]['child'][$namespace][$tag])) { |
|
2248 return $image[0]['child'][$namespace][$tag]; |
|
2249 } |
|
2250 } |
|
2251 } |
|
2252 if ($type & self::TYPE_RSS_090) { |
|
2253 if ($image = $this->get_feed_tags(self::NAMESPACE_RSS_090, 'image')) { |
|
2254 if (isset($image[0]['child'][$namespace][$tag])) { |
|
2255 return $image[0]['child'][$namespace][$tag]; |
|
2256 } |
|
2257 } |
|
2258 } |
|
2259 if ($type & self::TYPE_RSS_SYNDICATION) { |
|
2260 if ($image = $this->get_channel_tags(self::NAMESPACE_RSS_20, 'image')) { |
|
2261 if (isset($image[0]['child'][$namespace][$tag])) { |
|
2262 return $image[0]['child'][$namespace][$tag]; |
|
2263 } |
|
2264 } |
|
2265 } |
|
2266 return null; |
|
2267 } |
|
2268 |
|
2269 /** |
|
2270 * Get the base URL value from the feed |
|
2271 * |
|
2272 * Uses `<xml:base>` if available, otherwise uses the first link in the |
|
2273 * feed, or failing that, the URL of the feed itself. |
|
2274 * |
|
2275 * @see get_link |
|
2276 * @see subscribe_url |
|
2277 * |
|
2278 * @param array $element |
|
2279 * @return string |
|
2280 */ |
|
2281 public function get_base($element = []) |
|
2282 { |
|
2283 if (!empty($element['xml_base_explicit']) && isset($element['xml_base'])) { |
|
2284 return $element['xml_base']; |
|
2285 } elseif ($this->get_link() !== null) { |
|
2286 return $this->get_link(); |
|
2287 } |
|
2288 |
|
2289 return $this->subscribe_url(); |
|
2290 } |
|
2291 |
|
2292 /** |
|
2293 * Sanitize feed data |
|
2294 * |
|
2295 * @access private |
|
2296 * @see \SimplePie\Sanitize::sanitize() |
|
2297 * @param string $data Data to sanitize |
|
2298 * @param int $type One of the \SimplePie\SimplePie::CONSTRUCT_* constants |
|
2299 * @param string $base Base URL to resolve URLs against |
|
2300 * @return string Sanitized data |
|
2301 */ |
|
2302 public function sanitize($data, $type, $base = '') |
|
2303 { |
|
2304 try { |
|
2305 return $this->sanitize->sanitize($data, $type, $base); |
|
2306 } catch (\SimplePie\Exception $e) { |
|
2307 if (!$this->enable_exceptions) { |
|
2308 $this->error = $e->getMessage(); |
|
2309 $this->registry->call(Misc::class, 'error', [$this->error, E_USER_WARNING, $e->getFile(), $e->getLine()]); |
|
2310 return ''; |
|
2311 } |
|
2312 |
|
2313 throw $e; |
|
2314 } |
|
2315 } |
|
2316 |
|
2317 /** |
|
2318 * Get the title of the feed |
|
2319 * |
|
2320 * Uses `<atom:title>`, `<title>` or `<dc:title>` |
|
2321 * |
|
2322 * @since 1.0 (previously called `get_feed_title` since 0.8) |
|
2323 * @return string|null |
|
2324 */ |
|
2325 public function get_title() |
|
2326 { |
|
2327 if ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'title')) { |
|
2328 return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_10_construct_type', [$return[0]['attribs']]), $this->get_base($return[0])); |
|
2329 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_03, 'title')) { |
|
2330 return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_03_construct_type', [$return[0]['attribs']]), $this->get_base($return[0])); |
|
2331 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_RSS_10, 'title')) { |
|
2332 return $this->sanitize($return[0]['data'], self::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); |
|
2333 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_RSS_090, 'title')) { |
|
2334 return $this->sanitize($return[0]['data'], self::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); |
|
2335 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_RSS_20, 'title')) { |
|
2336 return $this->sanitize($return[0]['data'], self::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); |
|
2337 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_11, 'title')) { |
|
2338 return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); |
|
2339 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_10, 'title')) { |
|
2340 return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); |
|
2341 } |
|
2342 |
|
2343 return null; |
|
2344 } |
|
2345 |
|
2346 /** |
|
2347 * Get a category for the feed |
|
2348 * |
|
2349 * @since Unknown |
|
2350 * @param int $key The category that you want to return. Remember that arrays begin with 0, not 1 |
|
2351 * @return \SimplePie\Category|null |
|
2352 */ |
|
2353 public function get_category($key = 0) |
|
2354 { |
|
2355 $categories = $this->get_categories(); |
|
2356 if (isset($categories[$key])) { |
|
2357 return $categories[$key]; |
|
2358 } |
|
2359 |
|
2360 return null; |
|
2361 } |
|
2362 |
|
2363 /** |
|
2364 * Get all categories for the feed |
|
2365 * |
|
2366 * Uses `<atom:category>`, `<category>` or `<dc:subject>` |
|
2367 * |
|
2368 * @since Unknown |
|
2369 * @return array|null List of {@see \SimplePie\Category} objects |
|
2370 */ |
|
2371 public function get_categories() |
|
2372 { |
|
2373 $categories = []; |
|
2374 |
|
2375 foreach ((array) $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'category') as $category) { |
|
2376 $term = null; |
|
2377 $scheme = null; |
|
2378 $label = null; |
|
2379 if (isset($category['attribs']['']['term'])) { |
|
2380 $term = $this->sanitize($category['attribs']['']['term'], self::CONSTRUCT_TEXT); |
|
2381 } |
|
2382 if (isset($category['attribs']['']['scheme'])) { |
|
2383 $scheme = $this->sanitize($category['attribs']['']['scheme'], self::CONSTRUCT_TEXT); |
|
2384 } |
|
2385 if (isset($category['attribs']['']['label'])) { |
|
2386 $label = $this->sanitize($category['attribs']['']['label'], self::CONSTRUCT_TEXT); |
|
2387 } |
|
2388 $categories[] = $this->registry->create(Category::class, [$term, $scheme, $label]); |
|
2389 } |
|
2390 foreach ((array) $this->get_channel_tags(self::NAMESPACE_RSS_20, 'category') as $category) { |
|
2391 // This is really the label, but keep this as the term also for BC. |
|
2392 // Label will also work on retrieving because that falls back to term. |
|
2393 $term = $this->sanitize($category['data'], self::CONSTRUCT_TEXT); |
|
2394 if (isset($category['attribs']['']['domain'])) { |
|
2395 $scheme = $this->sanitize($category['attribs']['']['domain'], self::CONSTRUCT_TEXT); |
|
2396 } else { |
|
2397 $scheme = null; |
|
2398 } |
|
2399 $categories[] = $this->registry->create(Category::class, [$term, $scheme, null]); |
|
2400 } |
|
2401 foreach ((array) $this->get_channel_tags(self::NAMESPACE_DC_11, 'subject') as $category) { |
|
2402 $categories[] = $this->registry->create(Category::class, [$this->sanitize($category['data'], self::CONSTRUCT_TEXT), null, null]); |
|
2403 } |
|
2404 foreach ((array) $this->get_channel_tags(self::NAMESPACE_DC_10, 'subject') as $category) { |
|
2405 $categories[] = $this->registry->create(Category::class, [$this->sanitize($category['data'], self::CONSTRUCT_TEXT), null, null]); |
|
2406 } |
|
2407 |
|
2408 if (!empty($categories)) { |
|
2409 return array_unique($categories); |
|
2410 } |
|
2411 |
|
2412 return null; |
|
2413 } |
|
2414 |
|
2415 /** |
|
2416 * Get an author for the feed |
|
2417 * |
|
2418 * @since 1.1 |
|
2419 * @param int $key The author that you want to return. Remember that arrays begin with 0, not 1 |
|
2420 * @return \SimplePie\Author|null |
|
2421 */ |
|
2422 public function get_author($key = 0) |
|
2423 { |
|
2424 $authors = $this->get_authors(); |
|
2425 if (isset($authors[$key])) { |
|
2426 return $authors[$key]; |
|
2427 } |
|
2428 |
|
2429 return null; |
|
2430 } |
|
2431 |
|
2432 /** |
|
2433 * Get all authors for the feed |
|
2434 * |
|
2435 * Uses `<atom:author>`, `<author>`, `<dc:creator>` or `<itunes:author>` |
|
2436 * |
|
2437 * @since 1.1 |
|
2438 * @return array|null List of {@see \SimplePie\Author} objects |
|
2439 */ |
|
2440 public function get_authors() |
|
2441 { |
|
2442 $authors = []; |
|
2443 foreach ((array) $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'author') as $author) { |
|
2444 $name = null; |
|
2445 $uri = null; |
|
2446 $email = null; |
|
2447 if (isset($author['child'][self::NAMESPACE_ATOM_10]['name'][0]['data'])) { |
|
2448 $name = $this->sanitize($author['child'][self::NAMESPACE_ATOM_10]['name'][0]['data'], self::CONSTRUCT_TEXT); |
|
2449 } |
|
2450 if (isset($author['child'][self::NAMESPACE_ATOM_10]['uri'][0]['data'])) { |
|
2451 $uri = $this->sanitize($author['child'][self::NAMESPACE_ATOM_10]['uri'][0]['data'], self::CONSTRUCT_IRI, $this->get_base($author['child'][self::NAMESPACE_ATOM_10]['uri'][0])); |
|
2452 } |
|
2453 if (isset($author['child'][self::NAMESPACE_ATOM_10]['email'][0]['data'])) { |
|
2454 $email = $this->sanitize($author['child'][self::NAMESPACE_ATOM_10]['email'][0]['data'], self::CONSTRUCT_TEXT); |
|
2455 } |
|
2456 if ($name !== null || $email !== null || $uri !== null) { |
|
2457 $authors[] = $this->registry->create(Author::class, [$name, $uri, $email]); |
|
2458 } |
|
2459 } |
|
2460 if ($author = $this->get_channel_tags(self::NAMESPACE_ATOM_03, 'author')) { |
|
2461 $name = null; |
|
2462 $url = null; |
|
2463 $email = null; |
|
2464 if (isset($author[0]['child'][self::NAMESPACE_ATOM_03]['name'][0]['data'])) { |
|
2465 $name = $this->sanitize($author[0]['child'][self::NAMESPACE_ATOM_03]['name'][0]['data'], self::CONSTRUCT_TEXT); |
|
2466 } |
|
2467 if (isset($author[0]['child'][self::NAMESPACE_ATOM_03]['url'][0]['data'])) { |
|
2468 $url = $this->sanitize($author[0]['child'][self::NAMESPACE_ATOM_03]['url'][0]['data'], self::CONSTRUCT_IRI, $this->get_base($author[0]['child'][self::NAMESPACE_ATOM_03]['url'][0])); |
|
2469 } |
|
2470 if (isset($author[0]['child'][self::NAMESPACE_ATOM_03]['email'][0]['data'])) { |
|
2471 $email = $this->sanitize($author[0]['child'][self::NAMESPACE_ATOM_03]['email'][0]['data'], self::CONSTRUCT_TEXT); |
|
2472 } |
|
2473 if ($name !== null || $email !== null || $url !== null) { |
|
2474 $authors[] = $this->registry->create(Author::class, [$name, $url, $email]); |
|
2475 } |
|
2476 } |
|
2477 foreach ((array) $this->get_channel_tags(self::NAMESPACE_DC_11, 'creator') as $author) { |
|
2478 $authors[] = $this->registry->create(Author::class, [$this->sanitize($author['data'], self::CONSTRUCT_TEXT), null, null]); |
|
2479 } |
|
2480 foreach ((array) $this->get_channel_tags(self::NAMESPACE_DC_10, 'creator') as $author) { |
|
2481 $authors[] = $this->registry->create(Author::class, [$this->sanitize($author['data'], self::CONSTRUCT_TEXT), null, null]); |
|
2482 } |
|
2483 foreach ((array) $this->get_channel_tags(self::NAMESPACE_ITUNES, 'author') as $author) { |
|
2484 $authors[] = $this->registry->create(Author::class, [$this->sanitize($author['data'], self::CONSTRUCT_TEXT), null, null]); |
|
2485 } |
|
2486 |
|
2487 if (!empty($authors)) { |
|
2488 return array_unique($authors); |
|
2489 } |
|
2490 |
|
2491 return null; |
|
2492 } |
|
2493 |
|
2494 /** |
|
2495 * Get a contributor for the feed |
|
2496 * |
|
2497 * @since 1.1 |
|
2498 * @param int $key The contrbutor that you want to return. Remember that arrays begin with 0, not 1 |
|
2499 * @return \SimplePie\Author|null |
|
2500 */ |
|
2501 public function get_contributor($key = 0) |
|
2502 { |
|
2503 $contributors = $this->get_contributors(); |
|
2504 if (isset($contributors[$key])) { |
|
2505 return $contributors[$key]; |
|
2506 } |
|
2507 |
|
2508 return null; |
|
2509 } |
|
2510 |
|
2511 /** |
|
2512 * Get all contributors for the feed |
|
2513 * |
|
2514 * Uses `<atom:contributor>` |
|
2515 * |
|
2516 * @since 1.1 |
|
2517 * @return array|null List of {@see \SimplePie\Author} objects |
|
2518 */ |
|
2519 public function get_contributors() |
|
2520 { |
|
2521 $contributors = []; |
|
2522 foreach ((array) $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'contributor') as $contributor) { |
|
2523 $name = null; |
|
2524 $uri = null; |
|
2525 $email = null; |
|
2526 if (isset($contributor['child'][self::NAMESPACE_ATOM_10]['name'][0]['data'])) { |
|
2527 $name = $this->sanitize($contributor['child'][self::NAMESPACE_ATOM_10]['name'][0]['data'], self::CONSTRUCT_TEXT); |
|
2528 } |
|
2529 if (isset($contributor['child'][self::NAMESPACE_ATOM_10]['uri'][0]['data'])) { |
|
2530 $uri = $this->sanitize($contributor['child'][self::NAMESPACE_ATOM_10]['uri'][0]['data'], self::CONSTRUCT_IRI, $this->get_base($contributor['child'][self::NAMESPACE_ATOM_10]['uri'][0])); |
|
2531 } |
|
2532 if (isset($contributor['child'][self::NAMESPACE_ATOM_10]['email'][0]['data'])) { |
|
2533 $email = $this->sanitize($contributor['child'][self::NAMESPACE_ATOM_10]['email'][0]['data'], self::CONSTRUCT_TEXT); |
|
2534 } |
|
2535 if ($name !== null || $email !== null || $uri !== null) { |
|
2536 $contributors[] = $this->registry->create(Author::class, [$name, $uri, $email]); |
|
2537 } |
|
2538 } |
|
2539 foreach ((array) $this->get_channel_tags(self::NAMESPACE_ATOM_03, 'contributor') as $contributor) { |
|
2540 $name = null; |
|
2541 $url = null; |
|
2542 $email = null; |
|
2543 if (isset($contributor['child'][self::NAMESPACE_ATOM_03]['name'][0]['data'])) { |
|
2544 $name = $this->sanitize($contributor['child'][self::NAMESPACE_ATOM_03]['name'][0]['data'], self::CONSTRUCT_TEXT); |
|
2545 } |
|
2546 if (isset($contributor['child'][self::NAMESPACE_ATOM_03]['url'][0]['data'])) { |
|
2547 $url = $this->sanitize($contributor['child'][self::NAMESPACE_ATOM_03]['url'][0]['data'], self::CONSTRUCT_IRI, $this->get_base($contributor['child'][self::NAMESPACE_ATOM_03]['url'][0])); |
|
2548 } |
|
2549 if (isset($contributor['child'][self::NAMESPACE_ATOM_03]['email'][0]['data'])) { |
|
2550 $email = $this->sanitize($contributor['child'][self::NAMESPACE_ATOM_03]['email'][0]['data'], self::CONSTRUCT_TEXT); |
|
2551 } |
|
2552 if ($name !== null || $email !== null || $url !== null) { |
|
2553 $contributors[] = $this->registry->create(Author::class, [$name, $url, $email]); |
|
2554 } |
|
2555 } |
|
2556 |
|
2557 if (!empty($contributors)) { |
|
2558 return array_unique($contributors); |
|
2559 } |
|
2560 |
|
2561 return null; |
|
2562 } |
|
2563 |
|
2564 /** |
|
2565 * Get a single link for the feed |
|
2566 * |
|
2567 * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8) |
|
2568 * @param int $key The link that you want to return. Remember that arrays begin with 0, not 1 |
|
2569 * @param string $rel The relationship of the link to return |
|
2570 * @return string|null Link URL |
|
2571 */ |
|
2572 public function get_link($key = 0, $rel = 'alternate') |
|
2573 { |
|
2574 $links = $this->get_links($rel); |
|
2575 if (isset($links[$key])) { |
|
2576 return $links[$key]; |
|
2577 } |
|
2578 |
|
2579 return null; |
|
2580 } |
|
2581 |
|
2582 /** |
|
2583 * Get the permalink for the item |
|
2584 * |
|
2585 * Returns the first link available with a relationship of "alternate". |
|
2586 * Identical to {@see get_link()} with key 0 |
|
2587 * |
|
2588 * @see get_link |
|
2589 * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8) |
|
2590 * @internal Added for parity between the parent-level and the item/entry-level. |
|
2591 * @return string|null Link URL |
|
2592 */ |
|
2593 public function get_permalink() |
|
2594 { |
|
2595 return $this->get_link(0); |
|
2596 } |
|
2597 |
|
2598 /** |
|
2599 * Get all links for the feed |
|
2600 * |
|
2601 * Uses `<atom:link>` or `<link>` |
|
2602 * |
|
2603 * @since Beta 2 |
|
2604 * @param string $rel The relationship of links to return |
|
2605 * @return array|null Links found for the feed (strings) |
|
2606 */ |
|
2607 public function get_links($rel = 'alternate') |
|
2608 { |
|
2609 if (!isset($this->data['links'])) { |
|
2610 $this->data['links'] = []; |
|
2611 if ($links = $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'link')) { |
|
2612 foreach ($links as $link) { |
|
2613 if (isset($link['attribs']['']['href'])) { |
|
2614 $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; |
|
2615 $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], self::CONSTRUCT_IRI, $this->get_base($link)); |
|
2616 } |
|
2617 } |
|
2618 } |
|
2619 if ($links = $this->get_channel_tags(self::NAMESPACE_ATOM_03, 'link')) { |
|
2620 foreach ($links as $link) { |
|
2621 if (isset($link['attribs']['']['href'])) { |
|
2622 $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; |
|
2623 $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], self::CONSTRUCT_IRI, $this->get_base($link)); |
|
2624 } |
|
2625 } |
|
2626 } |
|
2627 if ($links = $this->get_channel_tags(self::NAMESPACE_RSS_10, 'link')) { |
|
2628 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], self::CONSTRUCT_IRI, $this->get_base($links[0])); |
|
2629 } |
|
2630 if ($links = $this->get_channel_tags(self::NAMESPACE_RSS_090, 'link')) { |
|
2631 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], self::CONSTRUCT_IRI, $this->get_base($links[0])); |
|
2632 } |
|
2633 if ($links = $this->get_channel_tags(self::NAMESPACE_RSS_20, 'link')) { |
|
2634 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], self::CONSTRUCT_IRI, $this->get_base($links[0])); |
|
2635 } |
|
2636 |
|
2637 $keys = array_keys($this->data['links']); |
|
2638 foreach ($keys as $key) { |
|
2639 if ($this->registry->call(Misc::class, 'is_isegment_nz_nc', [$key])) { |
|
2640 if (isset($this->data['links'][self::IANA_LINK_RELATIONS_REGISTRY . $key])) { |
|
2641 $this->data['links'][self::IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][self::IANA_LINK_RELATIONS_REGISTRY . $key]); |
|
2642 $this->data['links'][$key] = &$this->data['links'][self::IANA_LINK_RELATIONS_REGISTRY . $key]; |
|
2643 } else { |
|
2644 $this->data['links'][self::IANA_LINK_RELATIONS_REGISTRY . $key] = &$this->data['links'][$key]; |
|
2645 } |
|
2646 } elseif (substr($key, 0, 41) === self::IANA_LINK_RELATIONS_REGISTRY) { |
|
2647 $this->data['links'][substr($key, 41)] = &$this->data['links'][$key]; |
|
2648 } |
|
2649 $this->data['links'][$key] = array_unique($this->data['links'][$key]); |
|
2650 } |
|
2651 } |
|
2652 |
|
2653 if (isset($this->data['headers']['link'])) { |
|
2654 $link_headers = $this->data['headers']['link']; |
|
2655 if (is_array($link_headers)) { |
|
2656 $link_headers = implode(',', $link_headers); |
|
2657 } |
|
2658 // https://datatracker.ietf.org/doc/html/rfc8288 |
|
2659 if (is_string($link_headers) && |
|
2660 preg_match_all('/<(?P<uri>[^>]+)>\s*;\s*rel\s*=\s*(?P<quote>"?)' . preg_quote($rel) . '(?P=quote)\s*(?=,|$)/i', $link_headers, $matches)) { |
|
2661 return $matches['uri']; |
|
2662 } |
|
2663 } |
|
2664 |
|
2665 if (isset($this->data['links'][$rel])) { |
|
2666 return $this->data['links'][$rel]; |
|
2667 } |
|
2668 |
|
2669 return null; |
|
2670 } |
|
2671 |
|
2672 public function get_all_discovered_feeds() |
|
2673 { |
|
2674 return $this->all_discovered_feeds; |
|
2675 } |
|
2676 |
|
2677 /** |
|
2678 * Get the content for the item |
|
2679 * |
|
2680 * Uses `<atom:subtitle>`, `<atom:tagline>`, `<description>`, |
|
2681 * `<dc:description>`, `<itunes:summary>` or `<itunes:subtitle>` |
|
2682 * |
|
2683 * @since 1.0 (previously called `get_feed_description()` since 0.8) |
|
2684 * @return string|null |
|
2685 */ |
|
2686 public function get_description() |
|
2687 { |
|
2688 if ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'subtitle')) { |
|
2689 return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_10_construct_type', [$return[0]['attribs']]), $this->get_base($return[0])); |
|
2690 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_03, 'tagline')) { |
|
2691 return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_03_construct_type', [$return[0]['attribs']]), $this->get_base($return[0])); |
|
2692 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_RSS_10, 'description')) { |
|
2693 return $this->sanitize($return[0]['data'], self::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); |
|
2694 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_RSS_090, 'description')) { |
|
2695 return $this->sanitize($return[0]['data'], self::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); |
|
2696 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_RSS_20, 'description')) { |
|
2697 return $this->sanitize($return[0]['data'], self::CONSTRUCT_HTML, $this->get_base($return[0])); |
|
2698 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_11, 'description')) { |
|
2699 return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); |
|
2700 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_10, 'description')) { |
|
2701 return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); |
|
2702 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_ITUNES, 'summary')) { |
|
2703 return $this->sanitize($return[0]['data'], self::CONSTRUCT_HTML, $this->get_base($return[0])); |
|
2704 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_ITUNES, 'subtitle')) { |
|
2705 return $this->sanitize($return[0]['data'], self::CONSTRUCT_HTML, $this->get_base($return[0])); |
|
2706 } |
|
2707 |
|
2708 return null; |
|
2709 } |
|
2710 |
|
2711 /** |
|
2712 * Get the copyright info for the feed |
|
2713 * |
|
2714 * Uses `<atom:rights>`, `<atom:copyright>` or `<dc:rights>` |
|
2715 * |
|
2716 * @since 1.0 (previously called `get_feed_copyright()` since 0.8) |
|
2717 * @return string|null |
|
2718 */ |
|
2719 public function get_copyright() |
|
2720 { |
|
2721 if ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'rights')) { |
|
2722 return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_10_construct_type', [$return[0]['attribs']]), $this->get_base($return[0])); |
|
2723 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_03, 'copyright')) { |
|
2724 return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_03_construct_type', [$return[0]['attribs']]), $this->get_base($return[0])); |
|
2725 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_RSS_20, 'copyright')) { |
|
2726 return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); |
|
2727 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_11, 'rights')) { |
|
2728 return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); |
|
2729 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_10, 'rights')) { |
|
2730 return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); |
|
2731 } |
|
2732 |
|
2733 return null; |
|
2734 } |
|
2735 |
|
2736 /** |
|
2737 * Get the language for the feed |
|
2738 * |
|
2739 * Uses `<language>`, `<dc:language>`, or @xml_lang |
|
2740 * |
|
2741 * @since 1.0 (previously called `get_feed_language()` since 0.8) |
|
2742 * @return string|null |
|
2743 */ |
|
2744 public function get_language() |
|
2745 { |
|
2746 if ($return = $this->get_channel_tags(self::NAMESPACE_RSS_20, 'language')) { |
|
2747 return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); |
|
2748 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_11, 'language')) { |
|
2749 return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); |
|
2750 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_10, 'language')) { |
|
2751 return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); |
|
2752 } elseif (isset($this->data['child'][self::NAMESPACE_ATOM_10]['feed'][0]['xml_lang'])) { |
|
2753 return $this->sanitize($this->data['child'][self::NAMESPACE_ATOM_10]['feed'][0]['xml_lang'], self::CONSTRUCT_TEXT); |
|
2754 } elseif (isset($this->data['child'][self::NAMESPACE_ATOM_03]['feed'][0]['xml_lang'])) { |
|
2755 return $this->sanitize($this->data['child'][self::NAMESPACE_ATOM_03]['feed'][0]['xml_lang'], self::CONSTRUCT_TEXT); |
|
2756 } elseif (isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['xml_lang'])) { |
|
2757 return $this->sanitize($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['xml_lang'], self::CONSTRUCT_TEXT); |
|
2758 } elseif (isset($this->data['headers']['content-language'])) { |
|
2759 return $this->sanitize($this->data['headers']['content-language'], self::CONSTRUCT_TEXT); |
|
2760 } |
|
2761 |
|
2762 return null; |
|
2763 } |
|
2764 |
|
2765 /** |
|
2766 * Get the latitude coordinates for the item |
|
2767 * |
|
2768 * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications |
|
2769 * |
|
2770 * Uses `<geo:lat>` or `<georss:point>` |
|
2771 * |
|
2772 * @since 1.0 |
|
2773 * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo |
|
2774 * @link http://www.georss.org/ GeoRSS |
|
2775 * @return string|null |
|
2776 */ |
|
2777 public function get_latitude() |
|
2778 { |
|
2779 if ($return = $this->get_channel_tags(self::NAMESPACE_W3C_BASIC_GEO, 'lat')) { |
|
2780 return (float) $return[0]['data']; |
|
2781 } elseif (($return = $this->get_channel_tags(self::NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) { |
|
2782 return (float) $match[1]; |
|
2783 } |
|
2784 |
|
2785 return null; |
|
2786 } |
|
2787 |
|
2788 /** |
|
2789 * Get the longitude coordinates for the feed |
|
2790 * |
|
2791 * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications |
|
2792 * |
|
2793 * Uses `<geo:long>`, `<geo:lon>` or `<georss:point>` |
|
2794 * |
|
2795 * @since 1.0 |
|
2796 * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo |
|
2797 * @link http://www.georss.org/ GeoRSS |
|
2798 * @return string|null |
|
2799 */ |
|
2800 public function get_longitude() |
|
2801 { |
|
2802 if ($return = $this->get_channel_tags(self::NAMESPACE_W3C_BASIC_GEO, 'long')) { |
|
2803 return (float) $return[0]['data']; |
|
2804 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_W3C_BASIC_GEO, 'lon')) { |
|
2805 return (float) $return[0]['data']; |
|
2806 } elseif (($return = $this->get_channel_tags(self::NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) { |
|
2807 return (float) $match[2]; |
|
2808 } |
|
2809 |
|
2810 return null; |
|
2811 } |
|
2812 |
|
2813 /** |
|
2814 * Get the feed logo's title |
|
2815 * |
|
2816 * RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" title. |
|
2817 * |
|
2818 * Uses `<image><title>` or `<image><dc:title>` |
|
2819 * |
|
2820 * @return string|null |
|
2821 */ |
|
2822 public function get_image_title() |
|
2823 { |
|
2824 if ($return = $this->get_image_tags(self::NAMESPACE_RSS_10, 'title')) { |
|
2825 return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); |
|
2826 } elseif ($return = $this->get_image_tags(self::NAMESPACE_RSS_090, 'title')) { |
|
2827 return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); |
|
2828 } elseif ($return = $this->get_image_tags(self::NAMESPACE_RSS_20, 'title')) { |
|
2829 return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); |
|
2830 } elseif ($return = $this->get_image_tags(self::NAMESPACE_DC_11, 'title')) { |
|
2831 return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); |
|
2832 } elseif ($return = $this->get_image_tags(self::NAMESPACE_DC_10, 'title')) { |
|
2833 return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT); |
|
2834 } |
|
2835 |
|
2836 return null; |
|
2837 } |
|
2838 |
|
2839 /** |
|
2840 * Get the feed logo's URL |
|
2841 * |
|
2842 * RSS 0.9.0, 2.0, Atom 1.0, and feeds with iTunes RSS tags are allowed to |
|
2843 * have a "feed logo" URL. This points directly to the image itself. |
|
2844 * |
|
2845 * Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`, |
|
2846 * `<image><title>` or `<image><dc:title>` |
|
2847 * |
|
2848 * @return string|null |
|
2849 */ |
|
2850 public function get_image_url() |
|
2851 { |
|
2852 if ($return = $this->get_channel_tags(self::NAMESPACE_ITUNES, 'image')) { |
|
2853 return $this->sanitize($return[0]['attribs']['']['href'], self::CONSTRUCT_IRI); |
|
2854 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'logo')) { |
|
2855 return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0])); |
|
2856 } elseif ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'icon')) { |
|
2857 return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0])); |
|
2858 } elseif ($return = $this->get_image_tags(self::NAMESPACE_RSS_10, 'url')) { |
|
2859 return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0])); |
|
2860 } elseif ($return = $this->get_image_tags(self::NAMESPACE_RSS_090, 'url')) { |
|
2861 return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0])); |
|
2862 } elseif ($return = $this->get_image_tags(self::NAMESPACE_RSS_20, 'url')) { |
|
2863 return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0])); |
|
2864 } |
|
2865 |
|
2866 return null; |
|
2867 } |
|
2868 |
|
2869 |
|
2870 /** |
|
2871 * Get the feed logo's link |
|
2872 * |
|
2873 * RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" link. This |
|
2874 * points to a human-readable page that the image should link to. |
|
2875 * |
|
2876 * Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`, |
|
2877 * `<image><title>` or `<image><dc:title>` |
|
2878 * |
|
2879 * @return string|null |
|
2880 */ |
|
2881 public function get_image_link() |
|
2882 { |
|
2883 if ($return = $this->get_image_tags(self::NAMESPACE_RSS_10, 'link')) { |
|
2884 return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0])); |
|
2885 } elseif ($return = $this->get_image_tags(self::NAMESPACE_RSS_090, 'link')) { |
|
2886 return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0])); |
|
2887 } elseif ($return = $this->get_image_tags(self::NAMESPACE_RSS_20, 'link')) { |
|
2888 return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0])); |
|
2889 } |
|
2890 |
|
2891 return null; |
|
2892 } |
|
2893 |
|
2894 /** |
|
2895 * Get the feed logo's link |
|
2896 * |
|
2897 * RSS 2.0 feeds are allowed to have a "feed logo" width. |
|
2898 * |
|
2899 * Uses `<image><width>` or defaults to 88 if no width is specified and |
|
2900 * the feed is an RSS 2.0 feed. |
|
2901 * |
|
2902 * @return int|null |
|
2903 */ |
|
2904 public function get_image_width() |
|
2905 { |
|
2906 if ($return = $this->get_image_tags(self::NAMESPACE_RSS_20, 'width')) { |
|
2907 return intval($return[0]['data']); |
|
2908 } elseif ($this->get_type() & self::TYPE_RSS_SYNDICATION && $this->get_image_tags(self::NAMESPACE_RSS_20, 'url')) { |
|
2909 return 88; |
|
2910 } |
|
2911 |
|
2912 return null; |
|
2913 } |
|
2914 |
|
2915 /** |
|
2916 * Get the feed logo's height |
|
2917 * |
|
2918 * RSS 2.0 feeds are allowed to have a "feed logo" height. |
|
2919 * |
|
2920 * Uses `<image><height>` or defaults to 31 if no height is specified and |
|
2921 * the feed is an RSS 2.0 feed. |
|
2922 * |
|
2923 * @return int|null |
|
2924 */ |
|
2925 public function get_image_height() |
|
2926 { |
|
2927 if ($return = $this->get_image_tags(self::NAMESPACE_RSS_20, 'height')) { |
|
2928 return intval($return[0]['data']); |
|
2929 } elseif ($this->get_type() & self::TYPE_RSS_SYNDICATION && $this->get_image_tags(self::NAMESPACE_RSS_20, 'url')) { |
|
2930 return 31; |
|
2931 } |
|
2932 |
|
2933 return null; |
|
2934 } |
|
2935 |
|
2936 /** |
|
2937 * Get the number of items in the feed |
|
2938 * |
|
2939 * This is well-suited for {@link http://php.net/for for()} loops with |
|
2940 * {@see get_item()} |
|
2941 * |
|
2942 * @param int $max Maximum value to return. 0 for no limit |
|
2943 * @return int Number of items in the feed |
|
2944 */ |
|
2945 public function get_item_quantity($max = 0) |
|
2946 { |
|
2947 $max = (int) $max; |
|
2948 $qty = count($this->get_items()); |
|
2949 if ($max === 0) { |
|
2950 return $qty; |
|
2951 } |
|
2952 |
|
2953 return ($qty > $max) ? $max : $qty; |
|
2954 } |
|
2955 |
|
2956 /** |
|
2957 * Get a single item from the feed |
|
2958 * |
|
2959 * This is better suited for {@link http://php.net/for for()} loops, whereas |
|
2960 * {@see get_items()} is better suited for |
|
2961 * {@link http://php.net/foreach foreach()} loops. |
|
2962 * |
|
2963 * @see get_item_quantity() |
|
2964 * @since Beta 2 |
|
2965 * @param int $key The item that you want to return. Remember that arrays begin with 0, not 1 |
|
2966 * @return \SimplePie\Item|null |
|
2967 */ |
|
2968 public function get_item($key = 0) |
|
2969 { |
|
2970 $items = $this->get_items(); |
|
2971 if (isset($items[$key])) { |
|
2972 return $items[$key]; |
|
2973 } |
|
2974 |
|
2975 return null; |
|
2976 } |
|
2977 |
|
2978 /** |
|
2979 * Get all items from the feed |
|
2980 * |
|
2981 * This is better suited for {@link http://php.net/for for()} loops, whereas |
|
2982 * {@see get_items()} is better suited for |
|
2983 * {@link http://php.net/foreach foreach()} loops. |
|
2984 * |
|
2985 * @see get_item_quantity |
|
2986 * @since Beta 2 |
|
2987 * @param int $start Index to start at |
|
2988 * @param int $end Number of items to return. 0 for all items after `$start` |
|
2989 * @return \SimplePie\Item[]|null List of {@see \SimplePie\Item} objects |
|
2990 */ |
|
2991 public function get_items($start = 0, $end = 0) |
|
2992 { |
|
2993 if (!isset($this->data['items'])) { |
|
2994 if (!empty($this->multifeed_objects)) { |
|
2995 $this->data['items'] = SimplePie::merge_items($this->multifeed_objects, $start, $end, $this->item_limit); |
|
2996 if (empty($this->data['items'])) { |
|
2997 return []; |
|
2998 } |
|
2999 return $this->data['items']; |
|
3000 } |
|
3001 $this->data['items'] = []; |
|
3002 if ($items = $this->get_feed_tags(self::NAMESPACE_ATOM_10, 'entry')) { |
|
3003 $keys = array_keys($items); |
|
3004 foreach ($keys as $key) { |
|
3005 $this->data['items'][] = $this->registry->create(Item::class, [$this, $items[$key]]); |
|
3006 } |
|
3007 } |
|
3008 if ($items = $this->get_feed_tags(self::NAMESPACE_ATOM_03, 'entry')) { |
|
3009 $keys = array_keys($items); |
|
3010 foreach ($keys as $key) { |
|
3011 $this->data['items'][] = $this->registry->create(Item::class, [$this, $items[$key]]); |
|
3012 } |
|
3013 } |
|
3014 if ($items = $this->get_feed_tags(self::NAMESPACE_RSS_10, 'item')) { |
|
3015 $keys = array_keys($items); |
|
3016 foreach ($keys as $key) { |
|
3017 $this->data['items'][] = $this->registry->create(Item::class, [$this, $items[$key]]); |
|
3018 } |
|
3019 } |
|
3020 if ($items = $this->get_feed_tags(self::NAMESPACE_RSS_090, 'item')) { |
|
3021 $keys = array_keys($items); |
|
3022 foreach ($keys as $key) { |
|
3023 $this->data['items'][] = $this->registry->create(Item::class, [$this, $items[$key]]); |
|
3024 } |
|
3025 } |
|
3026 if ($items = $this->get_channel_tags(self::NAMESPACE_RSS_20, 'item')) { |
|
3027 $keys = array_keys($items); |
|
3028 foreach ($keys as $key) { |
|
3029 $this->data['items'][] = $this->registry->create(Item::class, [$this, $items[$key]]); |
|
3030 } |
|
3031 } |
|
3032 } |
|
3033 |
|
3034 if (empty($this->data['items'])) { |
|
3035 return []; |
|
3036 } |
|
3037 |
|
3038 if ($this->order_by_date) { |
|
3039 if (!isset($this->data['ordered_items'])) { |
|
3040 $this->data['ordered_items'] = $this->data['items']; |
|
3041 usort($this->data['ordered_items'], [get_class($this), 'sort_items']); |
|
3042 } |
|
3043 $items = $this->data['ordered_items']; |
|
3044 } else { |
|
3045 $items = $this->data['items']; |
|
3046 } |
|
3047 // Slice the data as desired |
|
3048 if ($end === 0) { |
|
3049 return array_slice($items, $start); |
|
3050 } |
|
3051 |
|
3052 return array_slice($items, $start, $end); |
|
3053 } |
|
3054 |
|
3055 /** |
|
3056 * Set the favicon handler |
|
3057 * |
|
3058 * @deprecated Use your own favicon handling instead |
|
3059 */ |
|
3060 public function set_favicon_handler($page = false, $qs = 'i') |
|
3061 { |
|
3062 trigger_error('Favicon handling has been removed, please use your own handling', \E_USER_DEPRECATED); |
|
3063 return false; |
|
3064 } |
|
3065 |
|
3066 /** |
|
3067 * Get the favicon for the current feed |
|
3068 * |
|
3069 * @deprecated Use your own favicon handling instead |
|
3070 */ |
|
3071 public function get_favicon() |
|
3072 { |
|
3073 trigger_error('Favicon handling has been removed, please use your own handling', \E_USER_DEPRECATED); |
|
3074 |
|
3075 if (($url = $this->get_link()) !== null) { |
|
3076 return 'https://www.google.com/s2/favicons?domain=' . urlencode($url); |
|
3077 } |
|
3078 |
|
3079 return false; |
|
3080 } |
|
3081 |
|
3082 /** |
|
3083 * Magic method handler |
|
3084 * |
|
3085 * @param string $method Method name |
|
3086 * @param array $args Arguments to the method |
|
3087 * @return mixed |
|
3088 */ |
|
3089 public function __call($method, $args) |
|
3090 { |
|
3091 if (strpos($method, 'subscribe_') === 0) { |
|
3092 trigger_error('subscribe_*() has been deprecated, implement the callback yourself', \E_USER_DEPRECATED); |
|
3093 return ''; |
|
3094 } |
|
3095 if ($method === 'enable_xml_dump') { |
|
3096 trigger_error('enable_xml_dump() has been deprecated, use get_raw_data() instead', \E_USER_DEPRECATED); |
|
3097 return false; |
|
3098 } |
|
3099 |
|
3100 $class = get_class($this); |
|
3101 $trace = debug_backtrace(); // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection |
|
3102 $file = $trace[0]['file']; |
|
3103 $line = $trace[0]['line']; |
|
3104 throw new SimplePieException("Call to undefined method $class::$method() in $file on line $line"); |
|
3105 } |
|
3106 |
|
3107 /** |
|
3108 * Sorting callback for items |
|
3109 * |
|
3110 * @access private |
|
3111 * @param SimplePie $a |
|
3112 * @param SimplePie $b |
|
3113 * @return boolean |
|
3114 */ |
|
3115 public static function sort_items($a, $b) |
|
3116 { |
|
3117 $a_date = $a->get_date('U'); |
|
3118 $b_date = $b->get_date('U'); |
|
3119 if ($a_date && $b_date) { |
|
3120 return $a_date > $b_date ? -1 : 1; |
|
3121 } |
|
3122 // Sort items without dates to the top. |
|
3123 if ($a_date) { |
|
3124 return 1; |
|
3125 } |
|
3126 if ($b_date) { |
|
3127 return -1; |
|
3128 } |
|
3129 return 0; |
|
3130 } |
|
3131 |
|
3132 /** |
|
3133 * Merge items from several feeds into one |
|
3134 * |
|
3135 * If you're merging multiple feeds together, they need to all have dates |
|
3136 * for the items or else SimplePie will refuse to sort them. |
|
3137 * |
|
3138 * @link http://simplepie.org/wiki/tutorial/sort_multiple_feeds_by_time_and_date#if_feeds_require_separate_per-feed_settings |
|
3139 * @param array $urls List of SimplePie feed objects to merge |
|
3140 * @param int $start Starting item |
|
3141 * @param int $end Number of items to return |
|
3142 * @param int $limit Maximum number of items per feed |
|
3143 * @return array |
|
3144 */ |
|
3145 public static function merge_items($urls, $start = 0, $end = 0, $limit = 0) |
|
3146 { |
|
3147 if (is_array($urls) && sizeof($urls) > 0) { |
|
3148 $items = []; |
|
3149 foreach ($urls as $arg) { |
|
3150 if ($arg instanceof SimplePie) { |
|
3151 $items = array_merge($items, $arg->get_items(0, $limit)); |
|
3152 } else { |
|
3153 trigger_error('Arguments must be SimplePie objects', E_USER_WARNING); |
|
3154 } |
|
3155 } |
|
3156 |
|
3157 usort($items, [get_class($urls[0]), 'sort_items']); |
|
3158 |
|
3159 if ($end === 0) { |
|
3160 return array_slice($items, $start); |
|
3161 } |
|
3162 |
|
3163 return array_slice($items, $start, $end); |
|
3164 } |
|
3165 |
|
3166 trigger_error('Cannot merge zero SimplePie objects', E_USER_WARNING); |
|
3167 return []; |
|
3168 } |
|
3169 |
|
3170 /** |
|
3171 * Store PubSubHubbub links as headers |
|
3172 * |
|
3173 * There is no way to find PuSH links in the body of a microformats feed, |
|
3174 * so they are added to the headers when found, to be used later by get_links. |
|
3175 * @param \SimplePie\File $file |
|
3176 * @param string $hub |
|
3177 * @param string $self |
|
3178 */ |
|
3179 private function store_links(&$file, $hub, $self) |
|
3180 { |
|
3181 if (isset($file->headers['link']['hub']) || |
|
3182 (isset($file->headers['link']) && |
|
3183 preg_match('/rel=hub/', $file->headers['link']))) { |
|
3184 return; |
|
3185 } |
|
3186 |
|
3187 if ($hub) { |
|
3188 if (isset($file->headers['link'])) { |
|
3189 if ($file->headers['link'] !== '') { |
|
3190 $file->headers['link'] = ', '; |
|
3191 } |
|
3192 } else { |
|
3193 $file->headers['link'] = ''; |
|
3194 } |
|
3195 $file->headers['link'] .= '<'.$hub.'>; rel=hub'; |
|
3196 if ($self) { |
|
3197 $file->headers['link'] .= ', <'.$self.'>; rel=self'; |
|
3198 } |
|
3199 } |
|
3200 } |
|
3201 |
|
3202 /** |
|
3203 * Get a DataCache |
|
3204 * |
|
3205 * @param string $feed_url Only needed for BC, can be removed in SimplePie 2.0.0 |
|
3206 * |
|
3207 * @return DataCache |
|
3208 */ |
|
3209 private function get_cache($feed_url = '') |
|
3210 { |
|
3211 if ($this->cache === null) { |
|
3212 // @trigger_error(sprintf('Not providing as PSR-16 cache implementation is deprecated since SimplePie 1.8.0, please use "SimplePie\SimplePie::set_cache()".'), \E_USER_DEPRECATED); |
|
3213 $cache = $this->registry->call(Cache::class, 'get_handler', [ |
|
3214 $this->cache_location, |
|
3215 $this->get_cache_filename($feed_url), |
|
3216 Base::TYPE_FEED |
|
3217 ]); |
|
3218 |
|
3219 return new BaseDataCache($cache); |
|
3220 } |
|
3221 |
|
3222 return $this->cache; |
|
3223 } |
|
3224 } |
|
3225 |
|
3226 class_alias('SimplePie\SimplePie', 'SimplePie'); |