]> git.wh0rd.org - tt-rss.git/blob - lib/simplepie/simplepie.inc
update simplepie to 1.3.1 (closes #471)
[tt-rss.git] / lib / simplepie / simplepie.inc
1 <?php
2 /**
3 * SimplePie
4 *
5 * A PHP-Based RSS and Atom Feed Framework.
6 * Takes the hard work out of managing a complete RSS/Atom solution.
7 *
8 * Please note: This file is automatically generated by a build script. The
9 * full original source is always available from http://simplepie.org/
10 *
11 * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
12 * All rights reserved.
13 *
14 * Redistribution and use in source and binary forms, with or without modification, are
15 * permitted provided that the following conditions are met:
16 *
17 * * Redistributions of source code must retain the above copyright notice, this list of
18 * conditions and the following disclaimer.
19 *
20 * * Redistributions in binary form must reproduce the above copyright notice, this list
21 * of conditions and the following disclaimer in the documentation and/or other materials
22 * provided with the distribution.
23 *
24 * * Neither the name of the SimplePie Team nor the names of its contributors may be used
25 * to endorse or promote products derived from this software without specific prior
26 * written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
29 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
30 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
31 * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
35 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 *
38 * @package SimplePie
39 * @version 1.3.1
40 * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
41 * @author Ryan Parman
42 * @author Geoffrey Sneddon
43 * @author Ryan McCue
44 * @link http://simplepie.org/ SimplePie
45 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
46 */
47
48 /**
49 * SimplePie Name
50 */
51 define('SIMPLEPIE_NAME', 'SimplePie');
52
53 /**
54 * SimplePie Version
55 */
56 define('SIMPLEPIE_VERSION', '1.3.1');
57
58 /**
59 * SimplePie Build
60 * @todo Hardcode for release (there's no need to have to call SimplePie_Misc::get_build() only every load of simplepie.inc)
61 */
62 define('SIMPLEPIE_BUILD', '20121030175911');
63
64 /**
65 * SimplePie Website URL
66 */
67 define('SIMPLEPIE_URL', 'http://simplepie.org');
68
69 /**
70 * SimplePie Useragent
71 * @see SimplePie::set_useragent()
72 */
73 define('SIMPLEPIE_USERAGENT', SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION . ' (Feed Parser; ' . SIMPLEPIE_URL . '; Allow like Gecko) Build/' . SIMPLEPIE_BUILD);
74
75 /**
76 * SimplePie Linkback
77 */
78 define('SIMPLEPIE_LINKBACK', '<a href="' . SIMPLEPIE_URL . '" title="' . SIMPLEPIE_NAME . ' ' . SIMPLEPIE_VERSION . '">' . SIMPLEPIE_NAME . '</a>');
79
80 /**
81 * No Autodiscovery
82 * @see SimplePie::set_autodiscovery_level()
83 */
84 define('SIMPLEPIE_LOCATOR_NONE', 0);
85
86 /**
87 * Feed Link Element Autodiscovery
88 * @see SimplePie::set_autodiscovery_level()
89 */
90 define('SIMPLEPIE_LOCATOR_AUTODISCOVERY', 1);
91
92 /**
93 * Local Feed Extension Autodiscovery
94 * @see SimplePie::set_autodiscovery_level()
95 */
96 define('SIMPLEPIE_LOCATOR_LOCAL_EXTENSION', 2);
97
98 /**
99 * Local Feed Body Autodiscovery
100 * @see SimplePie::set_autodiscovery_level()
101 */
102 define('SIMPLEPIE_LOCATOR_LOCAL_BODY', 4);
103
104 /**
105 * Remote Feed Extension Autodiscovery
106 * @see SimplePie::set_autodiscovery_level()
107 */
108 define('SIMPLEPIE_LOCATOR_REMOTE_EXTENSION', 8);
109
110 /**
111 * Remote Feed Body Autodiscovery
112 * @see SimplePie::set_autodiscovery_level()
113 */
114 define('SIMPLEPIE_LOCATOR_REMOTE_BODY', 16);
115
116 /**
117 * All Feed Autodiscovery
118 * @see SimplePie::set_autodiscovery_level()
119 */
120 define('SIMPLEPIE_LOCATOR_ALL', 31);
121
122 /**
123 * No known feed type
124 */
125 define('SIMPLEPIE_TYPE_NONE', 0);
126
127 /**
128 * RSS 0.90
129 */
130 define('SIMPLEPIE_TYPE_RSS_090', 1);
131
132 /**
133 * RSS 0.91 (Netscape)
134 */
135 define('SIMPLEPIE_TYPE_RSS_091_NETSCAPE', 2);
136
137 /**
138 * RSS 0.91 (Userland)
139 */
140 define('SIMPLEPIE_TYPE_RSS_091_USERLAND', 4);
141
142 /**
143 * RSS 0.91 (both Netscape and Userland)
144 */
145 define('SIMPLEPIE_TYPE_RSS_091', 6);
146
147 /**
148 * RSS 0.92
149 */
150 define('SIMPLEPIE_TYPE_RSS_092', 8);
151
152 /**
153 * RSS 0.93
154 */
155 define('SIMPLEPIE_TYPE_RSS_093', 16);
156
157 /**
158 * RSS 0.94
159 */
160 define('SIMPLEPIE_TYPE_RSS_094', 32);
161
162 /**
163 * RSS 1.0
164 */
165 define('SIMPLEPIE_TYPE_RSS_10', 64);
166
167 /**
168 * RSS 2.0
169 */
170 define('SIMPLEPIE_TYPE_RSS_20', 128);
171
172 /**
173 * RDF-based RSS
174 */
175 define('SIMPLEPIE_TYPE_RSS_RDF', 65);
176
177 /**
178 * Non-RDF-based RSS (truly intended as syndication format)
179 */
180 define('SIMPLEPIE_TYPE_RSS_SYNDICATION', 190);
181
182 /**
183 * All RSS
184 */
185 define('SIMPLEPIE_TYPE_RSS_ALL', 255);
186
187 /**
188 * Atom 0.3
189 */
190 define('SIMPLEPIE_TYPE_ATOM_03', 256);
191
192 /**
193 * Atom 1.0
194 */
195 define('SIMPLEPIE_TYPE_ATOM_10', 512);
196
197 /**
198 * All Atom
199 */
200 define('SIMPLEPIE_TYPE_ATOM_ALL', 768);
201
202 /**
203 * All feed types
204 */
205 define('SIMPLEPIE_TYPE_ALL', 1023);
206
207 /**
208 * No construct
209 */
210 define('SIMPLEPIE_CONSTRUCT_NONE', 0);
211
212 /**
213 * Text construct
214 */
215 define('SIMPLEPIE_CONSTRUCT_TEXT', 1);
216
217 /**
218 * HTML construct
219 */
220 define('SIMPLEPIE_CONSTRUCT_HTML', 2);
221
222 /**
223 * XHTML construct
224 */
225 define('SIMPLEPIE_CONSTRUCT_XHTML', 4);
226
227 /**
228 * base64-encoded construct
229 */
230 define('SIMPLEPIE_CONSTRUCT_BASE64', 8);
231
232 /**
233 * IRI construct
234 */
235 define('SIMPLEPIE_CONSTRUCT_IRI', 16);
236
237 /**
238 * A construct that might be HTML
239 */
240 define('SIMPLEPIE_CONSTRUCT_MAYBE_HTML', 32);
241
242 /**
243 * All constructs
244 */
245 define('SIMPLEPIE_CONSTRUCT_ALL', 63);
246
247 /**
248 * Don't change case
249 */
250 define('SIMPLEPIE_SAME_CASE', 1);
251
252 /**
253 * Change to lowercase
254 */
255 define('SIMPLEPIE_LOWERCASE', 2);
256
257 /**
258 * Change to uppercase
259 */
260 define('SIMPLEPIE_UPPERCASE', 4);
261
262 /**
263 * PCRE for HTML attributes
264 */
265 define('SIMPLEPIE_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]*');
266
267 /**
268 * PCRE for XML attributes
269 */
270 define('SIMPLEPIE_PCRE_XML_ATTRIBUTE', '((?:\s+(?:(?:[^\s:]+:)?[^\s:]+)\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'))*)\s*');
271
272 /**
273 * XML Namespace
274 */
275 define('SIMPLEPIE_NAMESPACE_XML', 'http://www.w3.org/XML/1998/namespace');
276
277 /**
278 * Atom 1.0 Namespace
279 */
280 define('SIMPLEPIE_NAMESPACE_ATOM_10', 'http://www.w3.org/2005/Atom');
281
282 /**
283 * Atom 0.3 Namespace
284 */
285 define('SIMPLEPIE_NAMESPACE_ATOM_03', 'http://purl.org/atom/ns#');
286
287 /**
288 * RDF Namespace
289 */
290 define('SIMPLEPIE_NAMESPACE_RDF', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
291
292 /**
293 * RSS 0.90 Namespace
294 */
295 define('SIMPLEPIE_NAMESPACE_RSS_090', 'http://my.netscape.com/rdf/simple/0.9/');
296
297 /**
298 * RSS 1.0 Namespace
299 */
300 define('SIMPLEPIE_NAMESPACE_RSS_10', 'http://purl.org/rss/1.0/');
301
302 /**
303 * RSS 1.0 Content Module Namespace
304 */
305 define('SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT', 'http://purl.org/rss/1.0/modules/content/');
306
307 /**
308 * RSS 2.0 Namespace
309 * (Stupid, I know, but I'm certain it will confuse people less with support.)
310 */
311 define('SIMPLEPIE_NAMESPACE_RSS_20', '');
312
313 /**
314 * DC 1.0 Namespace
315 */
316 define('SIMPLEPIE_NAMESPACE_DC_10', 'http://purl.org/dc/elements/1.0/');
317
318 /**
319 * DC 1.1 Namespace
320 */
321 define('SIMPLEPIE_NAMESPACE_DC_11', 'http://purl.org/dc/elements/1.1/');
322
323 /**
324 * W3C Basic Geo (WGS84 lat/long) Vocabulary Namespace
325 */
326 define('SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO', 'http://www.w3.org/2003/01/geo/wgs84_pos#');
327
328 /**
329 * GeoRSS Namespace
330 */
331 define('SIMPLEPIE_NAMESPACE_GEORSS', 'http://www.georss.org/georss');
332
333 /**
334 * Media RSS Namespace
335 */
336 define('SIMPLEPIE_NAMESPACE_MEDIARSS', 'http://search.yahoo.com/mrss/');
337
338 /**
339 * Wrong Media RSS Namespace. Caused by a long-standing typo in the spec.
340 */
341 define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG', 'http://search.yahoo.com/mrss');
342
343 /**
344 * Wrong Media RSS Namespace #2. New namespace introduced in Media RSS 1.5.
345 */
346 define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG2', 'http://video.search.yahoo.com/mrss');
347
348 /**
349 * Wrong Media RSS Namespace #3. A possible typo of the Media RSS 1.5 namespace.
350 */
351 define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG3', 'http://video.search.yahoo.com/mrss/');
352
353 /**
354 * Wrong Media RSS Namespace #4. New spec location after the RSS Advisory Board takes it over, but not a valid namespace.
355 */
356 define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG4', 'http://www.rssboard.org/media-rss');
357
358 /**
359 * Wrong Media RSS Namespace #5. A possible typo of the RSS Advisory Board URL.
360 */
361 define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG5', 'http://www.rssboard.org/media-rss/');
362
363 /**
364 * iTunes RSS Namespace
365 */
366 define('SIMPLEPIE_NAMESPACE_ITUNES', 'http://www.itunes.com/dtds/podcast-1.0.dtd');
367
368 /**
369 * XHTML Namespace
370 */
371 define('SIMPLEPIE_NAMESPACE_XHTML', 'http://www.w3.org/1999/xhtml');
372
373 /**
374 * IANA Link Relations Registry
375 */
376 define('SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY', 'http://www.iana.org/assignments/relation/');
377
378 /**
379 * No file source
380 */
381 define('SIMPLEPIE_FILE_SOURCE_NONE', 0);
382
383 /**
384 * Remote file source
385 */
386 define('SIMPLEPIE_FILE_SOURCE_REMOTE', 1);
387
388 /**
389 * Local file source
390 */
391 define('SIMPLEPIE_FILE_SOURCE_LOCAL', 2);
392
393 /**
394 * fsockopen() file source
395 */
396 define('SIMPLEPIE_FILE_SOURCE_FSOCKOPEN', 4);
397
398 /**
399 * cURL file source
400 */
401 define('SIMPLEPIE_FILE_SOURCE_CURL', 8);
402
403 /**
404 * file_get_contents() file source
405 */
406 define('SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS', 16);
407
408 /**
409 * SimplePie
410 *
411 * @package SimplePie
412 * @subpackage API
413 */
414 class SimplePie
415 {
416 /**
417 * @var array Raw data
418 * @access private
419 */
420 public $data = array();
421
422 /**
423 * @var mixed Error string
424 * @access private
425 */
426 public $error;
427
428 /**
429 * @var object Instance of SimplePie_Sanitize (or other class)
430 * @see SimplePie::set_sanitize_class()
431 * @access private
432 */
433 public $sanitize;
434
435 /**
436 * @var string SimplePie Useragent
437 * @see SimplePie::set_useragent()
438 * @access private
439 */
440 public $useragent = SIMPLEPIE_USERAGENT;
441
442 /**
443 * @var string Feed URL
444 * @see SimplePie::set_feed_url()
445 * @access private
446 */
447 public $feed_url;
448
449 /**
450 * @var object Instance of SimplePie_File to use as a feed
451 * @see SimplePie::set_file()
452 * @access private
453 */
454 public $file;
455
456 /**
457 * @var string Raw feed data
458 * @see SimplePie::set_raw_data()
459 * @access private
460 */
461 public $raw_data;
462
463 /**
464 * @var int Timeout for fetching remote files
465 * @see SimplePie::set_timeout()
466 * @access private
467 */
468 public $timeout = 10;
469
470 /**
471 * @var bool Forces fsockopen() to be used for remote files instead
472 * of cURL, even if a new enough version is installed
473 * @see SimplePie::force_fsockopen()
474 * @access private
475 */
476 public $force_fsockopen = false;
477
478 /**
479 * @var bool Force the given data/URL to be treated as a feed no matter what
480 * it appears like
481 * @see SimplePie::force_feed()
482 * @access private
483 */
484 public $force_feed = false;
485
486 /**
487 * @var bool Enable/Disable Caching
488 * @see SimplePie::enable_cache()
489 * @access private
490 */
491 public $cache = true;
492
493 /**
494 * @var int Cache duration (in seconds)
495 * @see SimplePie::set_cache_duration()
496 * @access private
497 */
498 public $cache_duration = 3600;
499
500 /**
501 * @var int Auto-discovery cache duration (in seconds)
502 * @see SimplePie::set_autodiscovery_cache_duration()
503 * @access private
504 */
505 public $autodiscovery_cache_duration = 604800; // 7 Days.
506
507 /**
508 * @var string Cache location (relative to executing script)
509 * @see SimplePie::set_cache_location()
510 * @access private
511 */
512 public $cache_location = './cache';
513
514 /**
515 * @var string Function that creates the cache filename
516 * @see SimplePie::set_cache_name_function()
517 * @access private
518 */
519 public $cache_name_function = 'md5';
520
521 /**
522 * @var bool Reorder feed by date descending
523 * @see SimplePie::enable_order_by_date()
524 * @access private
525 */
526 public $order_by_date = true;
527
528 /**
529 * @var mixed Force input encoding to be set to the follow value
530 * (false, or anything type-cast to false, disables this feature)
531 * @see SimplePie::set_input_encoding()
532 * @access private
533 */
534 public $input_encoding = false;
535
536 /**
537 * @var int Feed Autodiscovery Level
538 * @see SimplePie::set_autodiscovery_level()
539 * @access private
540 */
541 public $autodiscovery = SIMPLEPIE_LOCATOR_ALL;
542
543 /**
544 * Class registry object
545 *
546 * @var SimplePie_Registry
547 */
548 public $registry;
549
550 /**
551 * @var int Maximum number of feeds to check with autodiscovery
552 * @see SimplePie::set_max_checked_feeds()
553 * @access private
554 */
555 public $max_checked_feeds = 10;
556
557 /**
558 * @var array All the feeds found during the autodiscovery process
559 * @see SimplePie::get_all_discovered_feeds()
560 * @access private
561 */
562 public $all_discovered_feeds = array();
563
564 /**
565 * @var string Web-accessible path to the handler_image.php file.
566 * @see SimplePie::set_image_handler()
567 * @access private
568 */
569 public $image_handler = '';
570
571 /**
572 * @var array Stores the URLs when multiple feeds are being initialized.
573 * @see SimplePie::set_feed_url()
574 * @access private
575 */
576 public $multifeed_url = array();
577
578 /**
579 * @var array Stores SimplePie objects when multiple feeds initialized.
580 * @access private
581 */
582 public $multifeed_objects = array();
583
584 /**
585 * @var array Stores the get_object_vars() array for use with multifeeds.
586 * @see SimplePie::set_feed_url()
587 * @access private
588 */
589 public $config_settings = null;
590
591 /**
592 * @var integer Stores the number of items to return per-feed with multifeeds.
593 * @see SimplePie::set_item_limit()
594 * @access private
595 */
596 public $item_limit = 0;
597
598 /**
599 * @var array Stores the default attributes to be stripped by strip_attributes().
600 * @see SimplePie::strip_attributes()
601 * @access private
602 */
603 public $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc');
604
605 /**
606 * @var array Stores the default tags to be stripped by strip_htmltags().
607 * @see SimplePie::strip_htmltags()
608 * @access private
609 */
610 public $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style');
611
612 /**
613 * The SimplePie class contains feed level data and options
614 *
615 * To use SimplePie, create the SimplePie object with no parameters. You can
616 * then set configuration options using the provided methods. After setting
617 * them, you must initialise the feed using $feed->init(). At that point the
618 * object's methods and properties will be available to you.
619 *
620 * Previously, it was possible to pass in the feed URL along with cache
621 * options directly into the constructor. This has been removed as of 1.3 as
622 * it caused a lot of confusion.
623 *
624 * @since 1.0 Preview Release
625 */
626 public function __construct()
627 {
628 if (version_compare(PHP_VERSION, '5.2', '<'))
629 {
630 trigger_error('PHP 4.x, 5.0 and 5.1 are no longer supported. Please upgrade to PHP 5.2 or newer.');
631 die();
632 }
633
634 // Other objects, instances created here so we can set options on them
635 $this->sanitize = new SimplePie_Sanitize();
636 $this->registry = new SimplePie_Registry();
637
638 if (func_num_args() > 0)
639 {
640 $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING;
641 trigger_error('Passing parameters to the constructor is no longer supported. Please use set_feed_url(), set_cache_location(), and set_cache_location() directly.', $level);
642
643 $args = func_get_args();
644 switch (count($args)) {
645 case 3:
646 $this->set_cache_duration($args[2]);
647 case 2:
648 $this->set_cache_location($args[1]);
649 case 1:
650 $this->set_feed_url($args[0]);
651 $this->init();
652 }
653 }
654 }
655
656 /**
657 * Used for converting object to a string
658 */
659 public function __toString()
660 {
661 return md5(serialize($this->data));
662 }
663
664 /**
665 * Remove items that link back to this before destroying this object
666 */
667 public function __destruct()
668 {
669 if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode'))
670 {
671 if (!empty($this->data['items']))
672 {
673 foreach ($this->data['items'] as $item)
674 {
675 $item->__destruct();
676 }
677 unset($item, $this->data['items']);
678 }
679 if (!empty($this->data['ordered_items']))
680 {
681 foreach ($this->data['ordered_items'] as $item)
682 {
683 $item->__destruct();
684 }
685 unset($item, $this->data['ordered_items']);
686 }
687 }
688 }
689
690 /**
691 * Force the given data/URL to be treated as a feed
692 *
693 * This tells SimplePie to ignore the content-type provided by the server.
694 * Be careful when using this option, as it will also disable autodiscovery.
695 *
696 * @since 1.1
697 * @param bool $enable Force the given data/URL to be treated as a feed
698 */
699 public function force_feed($enable = false)
700 {
701 $this->force_feed = (bool) $enable;
702 }
703
704 /**
705 * Set the URL of the feed you want to parse
706 *
707 * This allows you to enter the URL of the feed you want to parse, or the
708 * website you want to try to use auto-discovery on. This takes priority
709 * over any set raw data.
710 *
711 * You can set multiple feeds to mash together by passing an array instead
712 * of a string for the $url. Remember that with each additional feed comes
713 * additional processing and resources.
714 *
715 * @since 1.0 Preview Release
716 * @see set_raw_data()
717 * @param string|array $url This is the URL (or array of URLs) that you want to parse.
718 */
719 public function set_feed_url($url)
720 {
721 $this->multifeed_url = array();
722 if (is_array($url))
723 {
724 foreach ($url as $value)
725 {
726 $this->multifeed_url[] = $this->registry->call('Misc', 'fix_protocol', array($value, 1));
727 }
728 }
729 else
730 {
731 $this->feed_url = $this->registry->call('Misc', 'fix_protocol', array($url, 1));
732 }
733 }
734
735 /**
736 * Set an instance of {@see SimplePie_File} to use as a feed
737 *
738 * @param SimplePie_File &$file
739 * @return bool True on success, false on failure
740 */
741 public function set_file(&$file)
742 {
743 if ($file instanceof SimplePie_File)
744 {
745 $this->feed_url = $file->url;
746 $this->file =& $file;
747 return true;
748 }
749 return false;
750 }
751
752 /**
753 * Set the raw XML data to parse
754 *
755 * Allows you to use a string of RSS/Atom data instead of a remote feed.
756 *
757 * If you have a feed available as a string in PHP, you can tell SimplePie
758 * to parse that data string instead of a remote feed. Any set feed URL
759 * takes precedence.
760 *
761 * @since 1.0 Beta 3
762 * @param string $data RSS or Atom data as a string.
763 * @see set_feed_url()
764 */
765 public function set_raw_data($data)
766 {
767 $this->raw_data = $data;
768 }
769
770 /**
771 * Set the the default timeout for fetching remote feeds
772 *
773 * This allows you to change the maximum time the feed's server to respond
774 * and send the feed back.
775 *
776 * @since 1.0 Beta 3
777 * @param int $timeout The maximum number of seconds to spend waiting to retrieve a feed.
778 */
779 public function set_timeout($timeout = 10)
780 {
781 $this->timeout = (int) $timeout;
782 }
783
784 /**
785 * Force SimplePie to use fsockopen() instead of cURL
786 *
787 * @since 1.0 Beta 3
788 * @param bool $enable Force fsockopen() to be used
789 */
790 public function force_fsockopen($enable = false)
791 {
792 $this->force_fsockopen = (bool) $enable;
793 }
794
795 /**
796 * Enable/disable caching in SimplePie.
797 *
798 * This option allows you to disable caching all-together in SimplePie.
799 * However, disabling the cache can lead to longer load times.
800 *
801 * @since 1.0 Preview Release
802 * @param bool $enable Enable caching
803 */
804 public function enable_cache($enable = true)
805 {
806 $this->cache = (bool) $enable;
807 }
808
809 /**
810 * Set the length of time (in seconds) that the contents of a feed will be
811 * cached
812 *
813 * @param int $seconds The feed content cache duration
814 */
815 public function set_cache_duration($seconds = 3600)
816 {
817 $this->cache_duration = (int) $seconds;
818 }
819
820 /**
821 * Set the length of time (in seconds) that the autodiscovered feed URL will
822 * be cached
823 *
824 * @param int $seconds The autodiscovered feed URL cache duration.
825 */
826 public function set_autodiscovery_cache_duration($seconds = 604800)
827 {
828 $this->autodiscovery_cache_duration = (int) $seconds;
829 }
830
831 /**
832 * Set the file system location where the cached files should be stored
833 *
834 * @param string $location The file system location.
835 */
836 public function set_cache_location($location = './cache')
837 {
838 $this->cache_location = (string) $location;
839 }
840
841 /**
842 * Set whether feed items should be sorted into reverse chronological order
843 *
844 * @param bool $enable Sort as reverse chronological order.
845 */
846 public function enable_order_by_date($enable = true)
847 {
848 $this->order_by_date = (bool) $enable;
849 }
850
851 /**
852 * Set the character encoding used to parse the feed
853 *
854 * This overrides the encoding reported by the feed, however it will fall
855 * back to the normal encoding detection if the override fails
856 *
857 * @param string $encoding Character encoding
858 */
859 public function set_input_encoding($encoding = false)
860 {
861 if ($encoding)
862 {
863 $this->input_encoding = (string) $encoding;
864 }
865 else
866 {
867 $this->input_encoding = false;
868 }
869 }
870
871 /**
872 * Set how much feed autodiscovery to do
873 *
874 * @see SIMPLEPIE_LOCATOR_NONE
875 * @see SIMPLEPIE_LOCATOR_AUTODISCOVERY
876 * @see SIMPLEPIE_LOCATOR_LOCAL_EXTENSION
877 * @see SIMPLEPIE_LOCATOR_LOCAL_BODY
878 * @see SIMPLEPIE_LOCATOR_REMOTE_EXTENSION
879 * @see SIMPLEPIE_LOCATOR_REMOTE_BODY
880 * @see SIMPLEPIE_LOCATOR_ALL
881 * @param int $level Feed Autodiscovery Level (level can be a combination of the above constants, see bitwise OR operator)
882 */
883 public function set_autodiscovery_level($level = SIMPLEPIE_LOCATOR_ALL)
884 {
885 $this->autodiscovery = (int) $level;
886 }
887
888 /**
889 * Get the class registry
890 *
891 * Use this to override SimplePie's default classes
892 * @see SimplePie_Registry
893 * @return SimplePie_Registry
894 */
895 public function &get_registry()
896 {
897 return $this->registry;
898 }
899
900 /**#@+
901 * Useful when you are overloading or extending SimplePie's default classes.
902 *
903 * @deprecated Use {@see get_registry()} instead
904 * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
905 * @param string $class Name of custom class
906 * @return boolean True on success, false otherwise
907 */
908 /**
909 * Set which class SimplePie uses for caching
910 */
911 public function set_cache_class($class = 'SimplePie_Cache')
912 {
913 return $this->registry->register('Cache', $class, true);
914 }
915
916 /**
917 * Set which class SimplePie uses for auto-discovery
918 */
919 public function set_locator_class($class = 'SimplePie_Locator')
920 {
921 return $this->registry->register('Locator', $class, true);
922 }
923
924 /**
925 * Set which class SimplePie uses for XML parsing
926 */
927 public function set_parser_class($class = 'SimplePie_Parser')
928 {
929 return $this->registry->register('Parser', $class, true);
930 }
931
932 /**
933 * Set which class SimplePie uses for remote file fetching
934 */
935 public function set_file_class($class = 'SimplePie_File')
936 {
937 return $this->registry->register('File', $class, true);
938 }
939
940 /**
941 * Set which class SimplePie uses for data sanitization
942 */
943 public function set_sanitize_class($class = 'SimplePie_Sanitize')
944 {
945 return $this->registry->register('Sanitize', $class, true);
946 }
947
948 /**
949 * Set which class SimplePie uses for handling feed items
950 */
951 public function set_item_class($class = 'SimplePie_Item')
952 {
953 return $this->registry->register('Item', $class, true);
954 }
955
956 /**
957 * Set which class SimplePie uses for handling author data
958 */
959 public function set_author_class($class = 'SimplePie_Author')
960 {
961 return $this->registry->register('Author', $class, true);
962 }
963
964 /**
965 * Set which class SimplePie uses for handling category data
966 */
967 public function set_category_class($class = 'SimplePie_Category')
968 {
969 return $this->registry->register('Category', $class, true);
970 }
971
972 /**
973 * Set which class SimplePie uses for feed enclosures
974 */
975 public function set_enclosure_class($class = 'SimplePie_Enclosure')
976 {
977 return $this->registry->register('Enclosure', $class, true);
978 }
979
980 /**
981 * Set which class SimplePie uses for `<media:text>` captions
982 */
983 public function set_caption_class($class = 'SimplePie_Caption')
984 {
985 return $this->registry->register('Caption', $class, true);
986 }
987
988 /**
989 * Set which class SimplePie uses for `<media:copyright>`
990 */
991 public function set_copyright_class($class = 'SimplePie_Copyright')
992 {
993 return $this->registry->register('Copyright', $class, true);
994 }
995
996 /**
997 * Set which class SimplePie uses for `<media:credit>`
998 */
999 public function set_credit_class($class = 'SimplePie_Credit')
1000 {
1001 return $this->registry->register('Credit', $class, true);
1002 }
1003
1004 /**
1005 * Set which class SimplePie uses for `<media:rating>`
1006 */
1007 public function set_rating_class($class = 'SimplePie_Rating')
1008 {
1009 return $this->registry->register('Rating', $class, true);
1010 }
1011
1012 /**
1013 * Set which class SimplePie uses for `<media:restriction>`
1014 */
1015 public function set_restriction_class($class = 'SimplePie_Restriction')
1016 {
1017 return $this->registry->register('Restriction', $class, true);
1018 }
1019
1020 /**
1021 * Set which class SimplePie uses for content-type sniffing
1022 */
1023 public function set_content_type_sniffer_class($class = 'SimplePie_Content_Type_Sniffer')
1024 {
1025 return $this->registry->register('Content_Type_Sniffer', $class, true);
1026 }
1027
1028 /**
1029 * Set which class SimplePie uses item sources
1030 */
1031 public function set_source_class($class = 'SimplePie_Source')
1032 {
1033 return $this->registry->register('Source', $class, true);
1034 }
1035 /**#@-*/
1036
1037 /**
1038 * Set the user agent string
1039 *
1040 * @param string $ua New user agent string.
1041 */
1042 public function set_useragent($ua = SIMPLEPIE_USERAGENT)
1043 {
1044 $this->useragent = (string) $ua;
1045 }
1046
1047 /**
1048 * Set callback function to create cache filename with
1049 *
1050 * @param mixed $function Callback function
1051 */
1052 public function set_cache_name_function($function = 'md5')
1053 {
1054 if (is_callable($function))
1055 {
1056 $this->cache_name_function = $function;
1057 }
1058 }
1059
1060 /**
1061 * Set options to make SP as fast as possible
1062 *
1063 * Forgoes a substantial amount of data sanitization in favor of speed. This
1064 * turns SimplePie into a dumb parser of feeds.
1065 *
1066 * @param bool $set Whether to set them or not
1067 */
1068 public function set_stupidly_fast($set = false)
1069 {
1070 if ($set)
1071 {
1072 $this->enable_order_by_date(false);
1073 $this->remove_div(false);
1074 $this->strip_comments(false);
1075 $this->strip_htmltags(false);
1076 $this->strip_attributes(false);
1077 $this->set_image_handler(false);
1078 }
1079 }
1080
1081 /**
1082 * Set maximum number of feeds to check with autodiscovery
1083 *
1084 * @param int $max Maximum number of feeds to check
1085 */
1086 public function set_max_checked_feeds($max = 10)
1087 {
1088 $this->max_checked_feeds = (int) $max;
1089 }
1090
1091 public function remove_div($enable = true)
1092 {
1093 $this->sanitize->remove_div($enable);
1094 }
1095
1096 public function strip_htmltags($tags = '', $encode = null)
1097 {
1098 if ($tags === '')
1099 {
1100 $tags = $this->strip_htmltags;
1101 }
1102 $this->sanitize->strip_htmltags($tags);
1103 if ($encode !== null)
1104 {
1105 $this->sanitize->encode_instead_of_strip($tags);
1106 }
1107 }
1108
1109 public function encode_instead_of_strip($enable = true)
1110 {
1111 $this->sanitize->encode_instead_of_strip($enable);
1112 }
1113
1114 public function strip_attributes($attribs = '')
1115 {
1116 if ($attribs === '')
1117 {
1118 $attribs = $this->strip_attributes;
1119 }
1120 $this->sanitize->strip_attributes($attribs);
1121 }
1122
1123 /**
1124 * Set the output encoding
1125 *
1126 * Allows you to override SimplePie's output to match that of your webpage.
1127 * This is useful for times when your webpages are not being served as
1128 * UTF-8. This setting will be obeyed by {@see handle_content_type()}, and
1129 * is similar to {@see set_input_encoding()}.
1130 *
1131 * It should be noted, however, that not all character encodings can support
1132 * all characters. If your page is being served as ISO-8859-1 and you try
1133 * to display a Japanese feed, you'll likely see garbled characters.
1134 * Because of this, it is highly recommended to ensure that your webpages
1135 * are served as UTF-8.
1136 *
1137 * The number of supported character encodings depends on whether your web
1138 * host supports {@link http://php.net/mbstring mbstring},
1139 * {@link http://php.net/iconv iconv}, or both. See
1140 * {@link http://simplepie.org/wiki/faq/Supported_Character_Encodings} for
1141 * more information.
1142 *
1143 * @param string $encoding
1144 */
1145 public function set_output_encoding($encoding = 'UTF-8')
1146 {
1147 $this->sanitize->set_output_encoding($encoding);
1148 }
1149
1150 public function strip_comments($strip = false)
1151 {
1152 $this->sanitize->strip_comments($strip);
1153 }
1154
1155 /**
1156 * Set element/attribute key/value pairs of HTML attributes
1157 * containing URLs that need to be resolved relative to the feed
1158 *
1159 * Defaults to |a|@href, |area|@href, |blockquote|@cite, |del|@cite,
1160 * |form|@action, |img|@longdesc, |img|@src, |input|@src, |ins|@cite,
1161 * |q|@cite
1162 *
1163 * @since 1.0
1164 * @param array|null $element_attribute Element/attribute key/value pairs, null for default
1165 */
1166 public function set_url_replacements($element_attribute = null)
1167 {
1168 $this->sanitize->set_url_replacements($element_attribute);
1169 }
1170
1171 /**
1172 * Set the handler to enable the display of cached images.
1173 *
1174 * @param str $page Web-accessible path to the handler_image.php file.
1175 * @param str $qs The query string that the value should be passed to.
1176 */
1177 public function set_image_handler($page = false, $qs = 'i')
1178 {
1179 if ($page !== false)
1180 {
1181 $this->sanitize->set_image_handler($page . '?' . $qs . '=');
1182 }
1183 else
1184 {
1185 $this->image_handler = '';
1186 }
1187 }
1188
1189 /**
1190 * Set the limit for items returned per-feed with multifeeds
1191 *
1192 * @param integer $limit The maximum number of items to return.
1193 */
1194 public function set_item_limit($limit = 0)
1195 {
1196 $this->item_limit = (int) $limit;
1197 }
1198
1199 /**
1200 * Initialize the feed object
1201 *
1202 * This is what makes everything happen. Period. This is where all of the
1203 * configuration options get processed, feeds are fetched, cached, and
1204 * parsed, and all of that other good stuff.
1205 *
1206 * @return boolean True if successful, false otherwise
1207 */
1208 public function init()
1209 {
1210 // Check absolute bare minimum requirements.
1211 if (!extension_loaded('xml') || !extension_loaded('pcre'))
1212 {
1213 return false;
1214 }
1215 // 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.
1216 elseif (!extension_loaded('xmlreader'))
1217 {
1218 static $xml_is_sane = null;
1219 if ($xml_is_sane === null)
1220 {
1221 $parser_check = xml_parser_create();
1222 xml_parse_into_struct($parser_check, '<foo>&amp;</foo>', $values);
1223 xml_parser_free($parser_check);
1224 $xml_is_sane = isset($values[0]['value']);
1225 }
1226 if (!$xml_is_sane)
1227 {
1228 return false;
1229 }
1230 }
1231
1232 if (method_exists($this->sanitize, 'set_registry'))
1233 {
1234 $this->sanitize->set_registry($this->registry);
1235 }
1236
1237 // Pass whatever was set with config options over to the sanitizer.
1238 // Pass the classes in for legacy support; new classes should use the registry instead
1239 $this->sanitize->pass_cache_data($this->cache, $this->cache_location, $this->cache_name_function, $this->registry->get_class('Cache'));
1240 $this->sanitize->pass_file_data($this->registry->get_class('File'), $this->timeout, $this->useragent, $this->force_fsockopen);
1241
1242 if (!empty($this->multifeed_url))
1243 {
1244 $i = 0;
1245 $success = 0;
1246 $this->multifeed_objects = array();
1247 $this->error = array();
1248 foreach ($this->multifeed_url as $url)
1249 {
1250 $this->multifeed_objects[$i] = clone $this;
1251 $this->multifeed_objects[$i]->set_feed_url($url);
1252 $single_success = $this->multifeed_objects[$i]->init();
1253 $success |= $single_success;
1254 if (!$single_success)
1255 {
1256 $this->error[$i] = $this->multifeed_objects[$i]->error();
1257 }
1258 $i++;
1259 }
1260 return (bool) $success;
1261 }
1262 elseif ($this->feed_url === null && $this->raw_data === null)
1263 {
1264 return false;
1265 }
1266
1267 $this->error = null;
1268 $this->data = array();
1269 $this->multifeed_objects = array();
1270 $cache = false;
1271
1272 if ($this->feed_url !== null)
1273 {
1274 $parsed_feed_url = $this->registry->call('Misc', 'parse_url', array($this->feed_url));
1275
1276 // Decide whether to enable caching
1277 if ($this->cache && $parsed_feed_url['scheme'] !== '')
1278 {
1279 $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $this->feed_url), 'spc'));
1280 }
1281
1282 // Fetch the data via SimplePie_File into $this->raw_data
1283 if (($fetched = $this->fetch_data($cache)) === true)
1284 {
1285 return true;
1286 }
1287 elseif ($fetched === false) {
1288 return false;
1289 }
1290
1291 list($headers, $sniffed) = $fetched;
1292 }
1293
1294 // Set up array of possible encodings
1295 $encodings = array();
1296
1297 // First check to see if input has been overridden.
1298 if ($this->input_encoding !== false)
1299 {
1300 $encodings[] = $this->input_encoding;
1301 }
1302
1303 $application_types = array('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity');
1304 $text_types = array('text/xml', 'text/xml-external-parsed-entity');
1305
1306 // RFC 3023 (only applies to sniffed content)
1307 if (isset($sniffed))
1308 {
1309 if (in_array($sniffed, $application_types) || substr($sniffed, 0, 12) === 'application/' && substr($sniffed, -4) === '+xml')
1310 {
1311 if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset))
1312 {
1313 $encodings[] = strtoupper($charset[1]);
1314 }
1315 $encodings = array_merge($encodings, $this->registry->call('Misc', 'xml_encoding', array($this->raw_data, &$this->registry)));
1316 $encodings[] = 'UTF-8';
1317 }
1318 elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml')
1319 {
1320 if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset))
1321 {
1322 $encodings[] = $charset[1];
1323 }
1324 $encodings[] = 'US-ASCII';
1325 }
1326 // Text MIME-type default
1327 elseif (substr($sniffed, 0, 5) === 'text/')
1328 {
1329 $encodings[] = 'US-ASCII';
1330 }
1331 }
1332
1333 // Fallback to XML 1.0 Appendix F.1/UTF-8/ISO-8859-1
1334 $encodings = array_merge($encodings, $this->registry->call('Misc', 'xml_encoding', array($this->raw_data, &$this->registry)));
1335 $encodings[] = 'UTF-8';
1336 $encodings[] = 'ISO-8859-1';
1337
1338 // There's no point in trying an encoding twice
1339 $encodings = array_unique($encodings);
1340
1341 // Loop through each possible encoding, till we return something, or run out of possibilities
1342 foreach ($encodings as $encoding)
1343 {
1344 // Change the encoding to UTF-8 (as we always use UTF-8 internally)
1345 if ($utf8_data = $this->registry->call('Misc', 'change_encoding', array($this->raw_data, $encoding, 'UTF-8')))
1346 {
1347 // Create new parser
1348 $parser = $this->registry->create('Parser');
1349
1350 // If it's parsed fine
1351 if ($parser->parse($utf8_data, 'UTF-8'))
1352 {
1353 $this->data = $parser->get_data();
1354 if (!($this->get_type() & ~SIMPLEPIE_TYPE_NONE))
1355 {
1356 $this->error = "A feed could not be found at $this->feed_url. This does not appear to be a valid RSS or Atom feed.";
1357 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__));
1358 return false;
1359 }
1360
1361 if (isset($headers))
1362 {
1363 $this->data['headers'] = $headers;
1364 }
1365 $this->data['build'] = SIMPLEPIE_BUILD;
1366
1367 // Cache the file if caching is enabled
1368 if ($cache && !$cache->save($this))
1369 {
1370 trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING);
1371 }
1372 return true;
1373 }
1374 }
1375 }
1376
1377 if (isset($parser))
1378 {
1379 // We have an error, just set SimplePie_Misc::error to it and quit
1380 $this->error = sprintf('This XML document is invalid, 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());
1381 }
1382 else
1383 {
1384 $this->error = 'The data could not be converted to UTF-8. You MUST have either the iconv or mbstring extension installed. Upgrading to PHP 5.x (which includes iconv) is highly recommended.';
1385 }
1386
1387 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__));
1388
1389 return false;
1390 }
1391
1392 /**
1393 * Fetch the data via SimplePie_File
1394 *
1395 * If the data is already cached, attempt to fetch it from there instead
1396 * @param SimplePie_Cache|false $cache Cache handler, or false to not load from the cache
1397 * @return array|true Returns true if the data was loaded from the cache, or an array of HTTP headers and sniffed type
1398 */
1399 protected function fetch_data(&$cache)
1400 {
1401 // If it's enabled, use the cache
1402 if ($cache)
1403 {
1404 // Load the Cache
1405 $this->data = $cache->load();
1406 if (!empty($this->data))
1407 {
1408 // If the cache is for an outdated build of SimplePie
1409 if (!isset($this->data['build']) || $this->data['build'] !== SIMPLEPIE_BUILD)
1410 {
1411 $cache->unlink();
1412 $this->data = array();
1413 }
1414 // If we've hit a collision just rerun it with caching disabled
1415 elseif (isset($this->data['url']) && $this->data['url'] !== $this->feed_url)
1416 {
1417 $cache = false;
1418 $this->data = array();
1419 }
1420 // If we've got a non feed_url stored (if the page isn't actually a feed, or is a redirect) use that URL.
1421 elseif (isset($this->data['feed_url']))
1422 {
1423 // If the autodiscovery cache is still valid use it.
1424 if ($cache->mtime() + $this->autodiscovery_cache_duration > time())
1425 {
1426 // Do not need to do feed autodiscovery yet.
1427 if ($this->data['feed_url'] !== $this->data['url'])
1428 {
1429 $this->set_feed_url($this->data['feed_url']);
1430 return $this->init();
1431 }
1432
1433 $cache->unlink();
1434 $this->data = array();
1435 }
1436 }
1437 // Check if the cache has been updated
1438 elseif ($cache->mtime() + $this->cache_duration < time())
1439 {
1440 // If we have last-modified and/or etag set
1441 if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag']))
1442 {
1443 $headers = array(
1444 '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',
1445 );
1446 if (isset($this->data['headers']['last-modified']))
1447 {
1448 $headers['if-modified-since'] = $this->data['headers']['last-modified'];
1449 }
1450 if (isset($this->data['headers']['etag']))
1451 {
1452 $headers['if-none-match'] = $this->data['headers']['etag'];
1453 }
1454
1455 $file = $this->registry->create('File', array($this->feed_url, $this->timeout/10, 5, $headers, $this->useragent, $this->force_fsockopen));
1456
1457 if ($file->success)
1458 {
1459 if ($file->status_code === 304)
1460 {
1461 $cache->touch();
1462 return true;
1463 }
1464 }
1465 else
1466 {
1467 unset($file);
1468 }
1469 }
1470 }
1471 // If the cache is still valid, just return true
1472 else
1473 {
1474 $this->raw_data = false;
1475 return true;
1476 }
1477 }
1478 // If the cache is empty, delete it
1479 else
1480 {
1481 $cache->unlink();
1482 $this->data = array();
1483 }
1484 }
1485 // 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.
1486 if (!isset($file))
1487 {
1488 if ($this->file instanceof SimplePie_File && $this->file->url === $this->feed_url)
1489 {
1490 $file =& $this->file;
1491 }
1492 else
1493 {
1494 $headers = array(
1495 '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',
1496 );
1497 $file = $this->registry->create('File', array($this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen));
1498 }
1499 }
1500 // If the file connection has an error, set SimplePie::error to that and quit
1501 if (!$file->success && !($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300)))
1502 {
1503 $this->error = $file->error;
1504 return !empty($this->data);
1505 }
1506
1507 if (!$this->force_feed)
1508 {
1509 // Check if the supplied URL is a feed, if it isn't, look for it.
1510 $locate = $this->registry->create('Locator', array(&$file, $this->timeout, $this->useragent, $this->max_checked_feeds));
1511
1512 if (!$locate->is_feed($file))
1513 {
1514 // We need to unset this so that if SimplePie::set_file() has been called that object is untouched
1515 unset($file);
1516 try
1517 {
1518 if (!($file = $locate->find($this->autodiscovery, $this->all_discovered_feeds)))
1519 {
1520 $this->error = "A feed could not be found at $this->feed_url. A feed with an invalid mime type may fall victim to this error, or " . SIMPLEPIE_NAME . " was unable to auto-discover it.. Use force_feed() if you are certain this URL is a real feed.";
1521 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__));
1522 return false;
1523 }
1524 }
1525 catch (SimplePie_Exception $e)
1526 {
1527 // This is usually because DOMDocument doesn't exist
1528 $this->error = $e->getMessage();
1529 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, $e->getFile(), $e->getLine()));
1530 return false;
1531 }
1532 if ($cache)
1533 {
1534 $this->data = array('url' => $this->feed_url, 'feed_url' => $file->url, 'build' => SIMPLEPIE_BUILD);
1535 if (!$cache->save($this))
1536 {
1537 trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING);
1538 }
1539 $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $file->url), 'spc'));
1540 }
1541 $this->feed_url = $file->url;
1542 }
1543 $locate = null;
1544 }
1545
1546 $this->raw_data = $file->body;
1547
1548 $headers = $file->headers;
1549 $sniffer = $this->registry->create('Content_Type_Sniffer', array(&$file));
1550 $sniffed = $sniffer->get_type();
1551
1552 return array($headers, $sniffed);
1553 }
1554
1555 /**
1556 * Get the error message for the occured error
1557 *
1558 * @return string|array Error message, or array of messages for multifeeds
1559 */
1560 public function error()
1561 {
1562 return $this->error;
1563 }
1564
1565 /**
1566 * Get the raw XML
1567 *
1568 * This is the same as the old `$feed->enable_xml_dump(true)`, but returns
1569 * the data instead of printing it.
1570 *
1571 * @return string|boolean Raw XML data, false if the cache is used
1572 */
1573 public function get_raw_data()
1574 {
1575 return $this->raw_data;
1576 }
1577
1578 /**
1579 * Get the character encoding used for output
1580 *
1581 * @since Preview Release
1582 * @return string
1583 */
1584 public function get_encoding()
1585 {
1586 return $this->sanitize->output_encoding;
1587 }
1588
1589 /**
1590 * Send the content-type header with correct encoding
1591 *
1592 * This method ensures that the SimplePie-enabled page is being served with
1593 * the correct {@link http://www.iana.org/assignments/media-types/ mime-type}
1594 * and character encoding HTTP headers (character encoding determined by the
1595 * {@see set_output_encoding} config option).
1596 *
1597 * This won't work properly if any content or whitespace has already been
1598 * sent to the browser, because it relies on PHP's
1599 * {@link http://php.net/header header()} function, and these are the
1600 * circumstances under which the function works.
1601 *
1602 * Because it's setting these settings for the entire page (as is the nature
1603 * of HTTP headers), this should only be used once per page (again, at the
1604 * top).
1605 *
1606 * @param string $mime MIME type to serve the page as
1607 */
1608 public function handle_content_type($mime = 'text/html')
1609 {
1610 if (!headers_sent())
1611 {
1612 $header = "Content-type: $mime;";
1613 if ($this->get_encoding())
1614 {
1615 $header .= ' charset=' . $this->get_encoding();
1616 }
1617 else
1618 {
1619 $header .= ' charset=UTF-8';
1620 }
1621 header($header);
1622 }
1623 }
1624
1625 /**
1626 * Get the type of the feed
1627 *
1628 * This returns a SIMPLEPIE_TYPE_* constant, which can be tested against
1629 * using {@link http://php.net/language.operators.bitwise bitwise operators}
1630 *
1631 * @since 0.8 (usage changed to using constants in 1.0)
1632 * @see SIMPLEPIE_TYPE_NONE Unknown.
1633 * @see SIMPLEPIE_TYPE_RSS_090 RSS 0.90.
1634 * @see SIMPLEPIE_TYPE_RSS_091_NETSCAPE RSS 0.91 (Netscape).
1635 * @see SIMPLEPIE_TYPE_RSS_091_USERLAND RSS 0.91 (Userland).
1636 * @see SIMPLEPIE_TYPE_RSS_091 RSS 0.91.
1637 * @see SIMPLEPIE_TYPE_RSS_092 RSS 0.92.
1638 * @see SIMPLEPIE_TYPE_RSS_093 RSS 0.93.
1639 * @see SIMPLEPIE_TYPE_RSS_094 RSS 0.94.
1640 * @see SIMPLEPIE_TYPE_RSS_10 RSS 1.0.
1641 * @see SIMPLEPIE_TYPE_RSS_20 RSS 2.0.x.
1642 * @see SIMPLEPIE_TYPE_RSS_RDF RDF-based RSS.
1643 * @see SIMPLEPIE_TYPE_RSS_SYNDICATION Non-RDF-based RSS (truly intended as syndication format).
1644 * @see SIMPLEPIE_TYPE_RSS_ALL Any version of RSS.
1645 * @see SIMPLEPIE_TYPE_ATOM_03 Atom 0.3.
1646 * @see SIMPLEPIE_TYPE_ATOM_10 Atom 1.0.
1647 * @see SIMPLEPIE_TYPE_ATOM_ALL Any version of Atom.
1648 * @see SIMPLEPIE_TYPE_ALL Any known/supported feed type.
1649 * @return int SIMPLEPIE_TYPE_* constant
1650 */
1651 public function get_type()
1652 {
1653 if (!isset($this->data['type']))
1654 {
1655 $this->data['type'] = SIMPLEPIE_TYPE_ALL;
1656 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed']))
1657 {
1658 $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_10;
1659 }
1660 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed']))
1661 {
1662 $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_03;
1663 }
1664 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF']))
1665 {
1666 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['channel'])
1667 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['image'])
1668 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item'])
1669 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['textinput']))
1670 {
1671 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_10;
1672 }
1673 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['channel'])
1674 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['image'])
1675 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item'])
1676 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['textinput']))
1677 {
1678 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_090;
1679 }
1680 }
1681 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss']))
1682 {
1683 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_ALL;
1684 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version']))
1685 {
1686 switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version']))
1687 {
1688 case '0.91':
1689 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091;
1690 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data']))
1691 {
1692 switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data']))
1693 {
1694 case '0':
1695 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_NETSCAPE;
1696 break;
1697
1698 case '24':
1699 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_USERLAND;
1700 break;
1701 }
1702 }
1703 break;
1704
1705 case '0.92':
1706 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_092;
1707 break;
1708
1709 case '0.93':
1710 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_093;
1711 break;
1712
1713 case '0.94':
1714 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_094;
1715 break;
1716
1717 case '2.0':
1718 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_20;
1719 break;
1720 }
1721 }
1722 }
1723 else
1724 {
1725 $this->data['type'] = SIMPLEPIE_TYPE_NONE;
1726 }
1727 }
1728 return $this->data['type'];
1729 }
1730
1731 /**
1732 * Get the URL for the feed
1733 *
1734 * May or may not be different from the URL passed to {@see set_feed_url()},
1735 * depending on whether auto-discovery was used.
1736 *
1737 * @since Preview Release (previously called `get_feed_url()` since SimplePie 0.8.)
1738 * @todo If we have a perm redirect we should return the new URL
1739 * @todo When we make the above change, let's support <itunes:new-feed-url> as well
1740 * @todo Also, |atom:link|@rel=self
1741 * @return string|null
1742 */
1743 public function subscribe_url()
1744 {
1745 if ($this->feed_url !== null)
1746 {
1747 return $this->sanitize($this->feed_url, SIMPLEPIE_CONSTRUCT_IRI);
1748 }
1749 else
1750 {
1751 return null;
1752 }
1753 }
1754
1755 /**
1756 * Get data for an feed-level element
1757 *
1758 * This method allows you to get access to ANY element/attribute that is a
1759 * sub-element of the opening feed tag.
1760 *
1761 * The return value is an indexed array of elements matching the given
1762 * namespace and tag name. Each element has `attribs`, `data` and `child`
1763 * subkeys. For `attribs` and `child`, these contain namespace subkeys.
1764 * `attribs` then has one level of associative name => value data (where
1765 * `value` is a string) after the namespace. `child` has tag-indexed keys
1766 * after the namespace, each member of which is an indexed array matching
1767 * this same format.
1768 *
1769 * For example:
1770 * <pre>
1771 * // This is probably a bad example because we already support
1772 * // <media:content> natively, but it shows you how to parse through
1773 * // the nodes.
1774 * $group = $item->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group');
1775 * $content = $group[0]['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'];
1776 * $file = $content[0]['attribs']['']['url'];
1777 * echo $file;
1778 * </pre>
1779 *
1780 * @since 1.0
1781 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces
1782 * @param string $namespace The URL of the XML namespace of the elements you're trying to access
1783 * @param string $tag Tag name
1784 * @return array
1785 */
1786 public function get_feed_tags($namespace, $tag)
1787 {
1788 $type = $this->get_type();
1789 if ($type & SIMPLEPIE_TYPE_ATOM_10)
1790 {
1791 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag]))
1792 {
1793 return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag];
1794 }
1795 }
1796 if ($type & SIMPLEPIE_TYPE_ATOM_03)
1797 {
1798 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag]))
1799 {
1800 return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag];
1801 }
1802 }
1803 if ($type & SIMPLEPIE_TYPE_RSS_RDF)
1804 {
1805 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag]))
1806 {
1807 return $this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag];
1808 }
1809 }
1810 if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION)
1811 {
1812 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag]))
1813 {
1814 return $this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag];
1815 }
1816 }
1817 return null;
1818 }
1819
1820 /**
1821 * Get data for an channel-level element
1822 *
1823 * This method allows you to get access to ANY element/attribute in the
1824 * channel/header section of the feed.
1825 *
1826 * See {@see SimplePie::get_feed_tags()} for a description of the return value
1827 *
1828 * @since 1.0
1829 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces
1830 * @param string $namespace The URL of the XML namespace of the elements you're trying to access
1831 * @param string $tag Tag name
1832 * @return array
1833 */
1834 public function get_channel_tags($namespace, $tag)
1835 {
1836 $type = $this->get_type();
1837 if ($type & SIMPLEPIE_TYPE_ATOM_ALL)
1838 {
1839 if ($return = $this->get_feed_tags($namespace, $tag))
1840 {
1841 return $return;
1842 }
1843 }
1844 if ($type & SIMPLEPIE_TYPE_RSS_10)
1845 {
1846 if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'channel'))
1847 {
1848 if (isset($channel[0]['child'][$namespace][$tag]))
1849 {
1850 return $channel[0]['child'][$namespace][$tag];
1851 }
1852 }
1853 }
1854 if ($type & SIMPLEPIE_TYPE_RSS_090)
1855 {
1856 if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'channel'))
1857 {
1858 if (isset($channel[0]['child'][$namespace][$tag]))
1859 {
1860 return $channel[0]['child'][$namespace][$tag];
1861 }
1862 }
1863 }
1864 if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION)
1865 {
1866 if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'channel'))
1867 {
1868 if (isset($channel[0]['child'][$namespace][$tag]))
1869 {
1870 return $channel[0]['child'][$namespace][$tag];
1871 }
1872 }
1873 }
1874 return null;
1875 }
1876
1877 /**
1878 * Get data for an channel-level element
1879 *
1880 * This method allows you to get access to ANY element/attribute in the
1881 * image/logo section of the feed.
1882 *
1883 * See {@see SimplePie::get_feed_tags()} for a description of the return value
1884 *
1885 * @since 1.0
1886 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces
1887 * @param string $namespace The URL of the XML namespace of the elements you're trying to access
1888 * @param string $tag Tag name
1889 * @return array
1890 */
1891 public function get_image_tags($namespace, $tag)
1892 {
1893 $type = $this->get_type();
1894 if ($type & SIMPLEPIE_TYPE_RSS_10)
1895 {
1896 if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'image'))
1897 {
1898 if (isset($image[0]['child'][$namespace][$tag]))
1899 {
1900 return $image[0]['child'][$namespace][$tag];
1901 }
1902 }
1903 }
1904 if ($type & SIMPLEPIE_TYPE_RSS_090)
1905 {
1906 if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'image'))
1907 {
1908 if (isset($image[0]['child'][$namespace][$tag]))
1909 {
1910 return $image[0]['child'][$namespace][$tag];
1911 }
1912 }
1913 }
1914 if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION)
1915 {
1916 if ($image = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'image'))
1917 {
1918 if (isset($image[0]['child'][$namespace][$tag]))
1919 {
1920 return $image[0]['child'][$namespace][$tag];
1921 }
1922 }
1923 }
1924 return null;
1925 }
1926
1927 /**
1928 * Get the base URL value from the feed
1929 *
1930 * Uses `<xml:base>` if available, otherwise uses the first link in the
1931 * feed, or failing that, the URL of the feed itself.
1932 *
1933 * @see get_link
1934 * @see subscribe_url
1935 *
1936 * @param array $element
1937 * @return string
1938 */
1939 public function get_base($element = array())
1940 {
1941 if (!($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION) && !empty($element['xml_base_explicit']) && isset($element['xml_base']))
1942 {
1943 return $element['xml_base'];
1944 }
1945 elseif ($this->get_link() !== null)
1946 {
1947 return $this->get_link();
1948 }
1949 else
1950 {
1951 return $this->subscribe_url();
1952 }
1953 }
1954
1955 /**
1956 * Sanitize feed data
1957 *
1958 * @access private
1959 * @see SimplePie_Sanitize::sanitize()
1960 * @param string $data Data to sanitize
1961 * @param int $type One of the SIMPLEPIE_CONSTRUCT_* constants
1962 * @param string $base Base URL to resolve URLs against
1963 * @return string Sanitized data
1964 */
1965 public function sanitize($data, $type, $base = '')
1966 {
1967 return $this->sanitize->sanitize($data, $type, $base);
1968 }
1969
1970 /**
1971 * Get the title of the feed
1972 *
1973 * Uses `<atom:title>`, `<title>` or `<dc:title>`
1974 *
1975 * @since 1.0 (previously called `get_feed_title` since 0.8)
1976 * @return string|null
1977 */
1978 public function get_title()
1979 {
1980 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title'))
1981 {
1982 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
1983 }
1984 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title'))
1985 {
1986 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
1987 }
1988 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
1989 {
1990 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
1991 }
1992 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
1993 {
1994 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
1995 }
1996 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
1997 {
1998 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
1999 }
2000 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
2001 {
2002 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2003 }
2004 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
2005 {
2006 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2007 }
2008 else
2009 {
2010 return null;
2011 }
2012 }
2013
2014 /**
2015 * Get a category for the feed
2016 *
2017 * @since Unknown
2018 * @param int $key The category that you want to return. Remember that arrays begin with 0, not 1
2019 * @return SimplePie_Category|null
2020 */
2021 public function get_category($key = 0)
2022 {
2023 $categories = $this->get_categories();
2024 if (isset($categories[$key]))
2025 {
2026 return $categories[$key];
2027 }
2028 else
2029 {
2030 return null;
2031 }
2032 }
2033
2034 /**
2035 * Get all categories for the feed
2036 *
2037 * Uses `<atom:category>`, `<category>` or `<dc:subject>`
2038 *
2039 * @since Unknown
2040 * @return array|null List of {@see SimplePie_Category} objects
2041 */
2042 public function get_categories()
2043 {
2044 $categories = array();
2045
2046 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category)
2047 {
2048 $term = null;
2049 $scheme = null;
2050 $label = null;
2051 if (isset($category['attribs']['']['term']))
2052 {
2053 $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT);
2054 }
2055 if (isset($category['attribs']['']['scheme']))
2056 {
2057 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
2058 }
2059 if (isset($category['attribs']['']['label']))
2060 {
2061 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
2062 }
2063 $categories[] = $this->registry->create('Category', array($term, $scheme, $label));
2064 }
2065 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category)
2066 {
2067 // This is really the label, but keep this as the term also for BC.
2068 // Label will also work on retrieving because that falls back to term.
2069 $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2070 if (isset($category['attribs']['']['domain']))
2071 {
2072 $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT);
2073 }
2074 else
2075 {
2076 $scheme = null;
2077 }
2078 $categories[] = $this->registry->create('Category', array($term, $scheme, null));
2079 }
2080 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category)
2081 {
2082 $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2083 }
2084 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category)
2085 {
2086 $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2087 }
2088
2089 if (!empty($categories))
2090 {
2091 return array_unique($categories);
2092 }
2093 else
2094 {
2095 return null;
2096 }
2097 }
2098
2099 /**
2100 * Get an author for the feed
2101 *
2102 * @since 1.1
2103 * @param int $key The author that you want to return. Remember that arrays begin with 0, not 1
2104 * @return SimplePie_Author|null
2105 */
2106 public function get_author($key = 0)
2107 {
2108 $authors = $this->get_authors();
2109 if (isset($authors[$key]))
2110 {
2111 return $authors[$key];
2112 }
2113 else
2114 {
2115 return null;
2116 }
2117 }
2118
2119 /**
2120 * Get all authors for the feed
2121 *
2122 * Uses `<atom:author>`, `<author>`, `<dc:creator>` or `<itunes:author>`
2123 *
2124 * @since 1.1
2125 * @return array|null List of {@see SimplePie_Author} objects
2126 */
2127 public function get_authors()
2128 {
2129 $authors = array();
2130 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author)
2131 {
2132 $name = null;
2133 $uri = null;
2134 $email = null;
2135 if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
2136 {
2137 $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2138 }
2139 if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
2140 {
2141 $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
2142 }
2143 if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
2144 {
2145 $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2146 }
2147 if ($name !== null || $email !== null || $uri !== null)
2148 {
2149 $authors[] = $this->registry->create('Author', array($name, $uri, $email));
2150 }
2151 }
2152 if ($author = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author'))
2153 {
2154 $name = null;
2155 $url = null;
2156 $email = null;
2157 if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
2158 {
2159 $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2160 }
2161 if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
2162 {
2163 $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
2164 }
2165 if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
2166 {
2167 $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2168 }
2169 if ($name !== null || $email !== null || $url !== null)
2170 {
2171 $authors[] = $this->registry->create('Author', array($name, $url, $email));
2172 }
2173 }
2174 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author)
2175 {
2176 $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2177 }
2178 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author)
2179 {
2180 $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2181 }
2182 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author)
2183 {
2184 $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2185 }
2186
2187 if (!empty($authors))
2188 {
2189 return array_unique($authors);
2190 }
2191 else
2192 {
2193 return null;
2194 }
2195 }
2196
2197 /**
2198 * Get a contributor for the feed
2199 *
2200 * @since 1.1
2201 * @param int $key The contrbutor that you want to return. Remember that arrays begin with 0, not 1
2202 * @return SimplePie_Author|null
2203 */
2204 public function get_contributor($key = 0)
2205 {
2206 $contributors = $this->get_contributors();
2207 if (isset($contributors[$key]))
2208 {
2209 return $contributors[$key];
2210 }
2211 else
2212 {
2213 return null;
2214 }
2215 }
2216
2217 /**
2218 * Get all contributors for the feed
2219 *
2220 * Uses `<atom:contributor>`
2221 *
2222 * @since 1.1
2223 * @return array|null List of {@see SimplePie_Author} objects
2224 */
2225 public function get_contributors()
2226 {
2227 $contributors = array();
2228 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor)
2229 {
2230 $name = null;
2231 $uri = null;
2232 $email = null;
2233 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
2234 {
2235 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2236 }
2237 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
2238 {
2239 $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
2240 }
2241 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
2242 {
2243 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2244 }
2245 if ($name !== null || $email !== null || $uri !== null)
2246 {
2247 $contributors[] = $this->registry->create('Author', array($name, $uri, $email));
2248 }
2249 }
2250 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor)
2251 {
2252 $name = null;
2253 $url = null;
2254 $email = null;
2255 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
2256 {
2257 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2258 }
2259 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
2260 {
2261 $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
2262 }
2263 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
2264 {
2265 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2266 }
2267 if ($name !== null || $email !== null || $url !== null)
2268 {
2269 $contributors[] = $this->registry->create('Author', array($name, $url, $email));
2270 }
2271 }
2272
2273 if (!empty($contributors))
2274 {
2275 return array_unique($contributors);
2276 }
2277 else
2278 {
2279 return null;
2280 }
2281 }
2282
2283 /**
2284 * Get a single link for the feed
2285 *
2286 * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8)
2287 * @param int $key The link that you want to return. Remember that arrays begin with 0, not 1
2288 * @param string $rel The relationship of the link to return
2289 * @return string|null Link URL
2290 */
2291 public function get_link($key = 0, $rel = 'alternate')
2292 {
2293 $links = $this->get_links($rel);
2294 if (isset($links[$key]))
2295 {
2296 return $links[$key];
2297 }
2298 else
2299 {
2300 return null;
2301 }
2302 }
2303
2304 /**
2305 * Get the permalink for the item
2306 *
2307 * Returns the first link available with a relationship of "alternate".
2308 * Identical to {@see get_link()} with key 0
2309 *
2310 * @see get_link
2311 * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8)
2312 * @internal Added for parity between the parent-level and the item/entry-level.
2313 * @return string|null Link URL
2314 */
2315 public function get_permalink()
2316 {
2317 return $this->get_link(0);
2318 }
2319
2320 /**
2321 * Get all links for the feed
2322 *
2323 * Uses `<atom:link>` or `<link>`
2324 *
2325 * @since Beta 2
2326 * @param string $rel The relationship of links to return
2327 * @return array|null Links found for the feed (strings)
2328 */
2329 public function get_links($rel = 'alternate')
2330 {
2331 if (!isset($this->data['links']))
2332 {
2333 $this->data['links'] = array();
2334 if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link'))
2335 {
2336 foreach ($links as $link)
2337 {
2338 if (isset($link['attribs']['']['href']))
2339 {
2340 $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
2341 $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
2342 }
2343 }
2344 }
2345 if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link'))
2346 {
2347 foreach ($links as $link)
2348 {
2349 if (isset($link['attribs']['']['href']))
2350 {
2351 $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
2352 $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
2353
2354 }
2355 }
2356 }
2357 if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
2358 {
2359 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
2360 }
2361 if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
2362 {
2363 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
2364 }
2365 if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
2366 {
2367 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
2368 }
2369
2370 $keys = array_keys($this->data['links']);
2371 foreach ($keys as $key)
2372 {
2373 if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key)))
2374 {
2375 if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]))
2376 {
2377 $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]);
2378 $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key];
2379 }
2380 else
2381 {
2382 $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key];
2383 }
2384 }
2385 elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY)
2386 {
2387 $this->data['links'][substr($key, 41)] =& $this->data['links'][$key];
2388 }
2389 $this->data['links'][$key] = array_unique($this->data['links'][$key]);
2390 }
2391 }
2392
2393 if (isset($this->data['links'][$rel]))
2394 {
2395 return $this->data['links'][$rel];
2396 }
2397 else
2398 {
2399 return null;
2400 }
2401 }
2402
2403 public function get_all_discovered_feeds()
2404 {
2405 return $this->all_discovered_feeds;
2406 }
2407
2408 /**
2409 * Get the content for the item
2410 *
2411 * Uses `<atom:subtitle>`, `<atom:tagline>`, `<description>`,
2412 * `<dc:description>`, `<itunes:summary>` or `<itunes:subtitle>`
2413 *
2414 * @since 1.0 (previously called `get_feed_description()` since 0.8)
2415 * @return string|null
2416 */
2417 public function get_description()
2418 {
2419 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle'))
2420 {
2421 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2422 }
2423 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline'))
2424 {
2425 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2426 }
2427 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description'))
2428 {
2429 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
2430 }
2431 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description'))
2432 {
2433 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
2434 }
2435 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description'))
2436 {
2437 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
2438 }
2439 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description'))
2440 {
2441 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2442 }
2443 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description'))
2444 {
2445 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2446 }
2447 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary'))
2448 {
2449 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
2450 }
2451 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle'))
2452 {
2453 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
2454 }
2455 else
2456 {
2457 return null;
2458 }
2459 }
2460
2461 /**
2462 * Get the copyright info for the feed
2463 *
2464 * Uses `<atom:rights>`, `<atom:copyright>` or `<dc:rights>`
2465 *
2466 * @since 1.0 (previously called `get_feed_copyright()` since 0.8)
2467 * @return string|null
2468 */
2469 public function get_copyright()
2470 {
2471 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights'))
2472 {
2473 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2474 }
2475 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright'))
2476 {
2477 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2478 }
2479 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright'))
2480 {
2481 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2482 }
2483 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights'))
2484 {
2485 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2486 }
2487 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights'))
2488 {
2489 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2490 }
2491 else
2492 {
2493 return null;
2494 }
2495 }
2496
2497 /**
2498 * Get the language for the feed
2499 *
2500 * Uses `<language>`, `<dc:language>`, or @xml_lang
2501 *
2502 * @since 1.0 (previously called `get_feed_language()` since 0.8)
2503 * @return string|null
2504 */
2505 public function get_language()
2506 {
2507 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language'))
2508 {
2509 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2510 }
2511 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language'))
2512 {
2513 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2514 }
2515 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language'))
2516 {
2517 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2518 }
2519 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang']))
2520 {
2521 return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
2522 }
2523 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang']))
2524 {
2525 return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
2526 }
2527 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang']))
2528 {
2529 return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
2530 }
2531 elseif (isset($this->data['headers']['content-language']))
2532 {
2533 return $this->sanitize($this->data['headers']['content-language'], SIMPLEPIE_CONSTRUCT_TEXT);
2534 }
2535 else
2536 {
2537 return null;
2538 }
2539 }
2540
2541 /**
2542 * Get the latitude coordinates for the item
2543 *
2544 * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
2545 *
2546 * Uses `<geo:lat>` or `<georss:point>`
2547 *
2548 * @since 1.0
2549 * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
2550 * @link http://www.georss.org/ GeoRSS
2551 * @return string|null
2552 */
2553 public function get_latitude()
2554 {
2555
2556 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat'))
2557 {
2558 return (float) $return[0]['data'];
2559 }
2560 elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
2561 {
2562 return (float) $match[1];
2563 }
2564 else
2565 {
2566 return null;
2567 }
2568 }
2569
2570 /**
2571 * Get the longitude coordinates for the feed
2572 *
2573 * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
2574 *
2575 * Uses `<geo:long>`, `<geo:lon>` or `<georss:point>`
2576 *
2577 * @since 1.0
2578 * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
2579 * @link http://www.georss.org/ GeoRSS
2580 * @return string|null
2581 */
2582 public function get_longitude()
2583 {
2584 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long'))
2585 {
2586 return (float) $return[0]['data'];
2587 }
2588 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon'))
2589 {
2590 return (float) $return[0]['data'];
2591 }
2592 elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
2593 {
2594 return (float) $match[2];
2595 }
2596 else
2597 {
2598 return null;
2599 }
2600 }
2601
2602 /**
2603 * Get the feed logo's title
2604 *
2605 * RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" title.
2606 *
2607 * Uses `<image><title>` or `<image><dc:title>`
2608 *
2609 * @return string|null
2610 */
2611 public function get_image_title()
2612 {
2613 if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
2614 {
2615 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2616 }
2617 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
2618 {
2619 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2620 }
2621 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
2622 {
2623 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2624 }
2625 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
2626 {
2627 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2628 }
2629 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
2630 {
2631 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2632 }
2633 else
2634 {
2635 return null;
2636 }
2637 }
2638
2639 /**
2640 * Get the feed logo's URL
2641 *
2642 * RSS 0.9.0, 2.0, Atom 1.0, and feeds with iTunes RSS tags are allowed to
2643 * have a "feed logo" URL. This points directly to the image itself.
2644 *
2645 * Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`,
2646 * `<image><title>` or `<image><dc:title>`
2647 *
2648 * @return string|null
2649 */
2650 public function get_image_url()
2651 {
2652 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image'))
2653 {
2654 return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI);
2655 }
2656 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo'))
2657 {
2658 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2659 }
2660 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon'))
2661 {
2662 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2663 }
2664 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'url'))
2665 {
2666 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2667 }
2668 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'url'))
2669 {
2670 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2671 }
2672 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url'))
2673 {
2674 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2675 }
2676 else
2677 {
2678 return null;
2679 }
2680 }
2681
2682 /**
2683 * Get the feed logo's link
2684 *
2685 * RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" link. This
2686 * points to a human-readable page that the image should link to.
2687 *
2688 * Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`,
2689 * `<image><title>` or `<image><dc:title>`
2690 *
2691 * @return string|null
2692 */
2693 public function get_image_link()
2694 {
2695 if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
2696 {
2697 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2698 }
2699 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
2700 {
2701 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2702 }
2703 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
2704 {
2705 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2706 }
2707 else
2708 {
2709 return null;
2710 }
2711 }
2712
2713 /**
2714 * Get the feed logo's link
2715 *
2716 * RSS 2.0 feeds are allowed to have a "feed logo" width.
2717 *
2718 * Uses `<image><width>` or defaults to 88.0 if no width is specified and
2719 * the feed is an RSS 2.0 feed.
2720 *
2721 * @return int|float|null
2722 */
2723 public function get_image_width()
2724 {
2725 if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'width'))
2726 {
2727 return round($return[0]['data']);
2728 }
2729 elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url'))
2730 {
2731 return 88.0;
2732 }
2733 else
2734 {
2735 return null;
2736 }
2737 }
2738
2739 /**
2740 * Get the feed logo's height
2741 *
2742 * RSS 2.0 feeds are allowed to have a "feed logo" height.
2743 *
2744 * Uses `<image><height>` or defaults to 31.0 if no height is specified and
2745 * the feed is an RSS 2.0 feed.
2746 *
2747 * @return int|float|null
2748 */
2749 public function get_image_height()
2750 {
2751 if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'height'))
2752 {
2753 return round($return[0]['data']);
2754 }
2755 elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url'))
2756 {
2757 return 31.0;
2758 }
2759 else
2760 {
2761 return null;
2762 }
2763 }
2764
2765 /**
2766 * Get the number of items in the feed
2767 *
2768 * This is well-suited for {@link http://php.net/for for()} loops with
2769 * {@see get_item()}
2770 *
2771 * @param int $max Maximum value to return. 0 for no limit
2772 * @return int Number of items in the feed
2773 */
2774 public function get_item_quantity($max = 0)
2775 {
2776 $max = (int) $max;
2777 $qty = count($this->get_items());
2778 if ($max === 0)
2779 {
2780 return $qty;
2781 }
2782 else
2783 {
2784 return ($qty > $max) ? $max : $qty;
2785 }
2786 }
2787
2788 /**
2789 * Get a single item from the feed
2790 *
2791 * This is better suited for {@link http://php.net/for for()} loops, whereas
2792 * {@see get_items()} is better suited for
2793 * {@link http://php.net/foreach foreach()} loops.
2794 *
2795 * @see get_item_quantity()
2796 * @since Beta 2
2797 * @param int $key The item that you want to return. Remember that arrays begin with 0, not 1
2798 * @return SimplePie_Item|null
2799 */
2800 public function get_item($key = 0)
2801 {
2802 $items = $this->get_items();
2803 if (isset($items[$key]))
2804 {
2805 return $items[$key];
2806 }
2807 else
2808 {
2809 return null;
2810 }
2811 }
2812
2813 /**
2814 * Get all items from the feed
2815 *
2816 * This is better suited for {@link http://php.net/for for()} loops, whereas
2817 * {@see get_items()} is better suited for
2818 * {@link http://php.net/foreach foreach()} loops.
2819 *
2820 * @see get_item_quantity
2821 * @since Beta 2
2822 * @param int $start Index to start at
2823 * @param int $end Number of items to return. 0 for all items after `$start`
2824 * @return array|null List of {@see SimplePie_Item} objects
2825 */
2826 public function get_items($start = 0, $end = 0)
2827 {
2828 if (!isset($this->data['items']))
2829 {
2830 if (!empty($this->multifeed_objects))
2831 {
2832 $this->data['items'] = SimplePie::merge_items($this->multifeed_objects, $start, $end, $this->item_limit);
2833 }
2834 else
2835 {
2836 $this->data['items'] = array();
2837 if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'entry'))
2838 {
2839 $keys = array_keys($items);
2840 foreach ($keys as $key)
2841 {
2842 $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key]));
2843 }
2844 }
2845 if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'entry'))
2846 {
2847 $keys = array_keys($items);
2848 foreach ($keys as $key)
2849 {
2850 $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key]));
2851 }
2852 }
2853 if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'item'))
2854 {
2855 $keys = array_keys($items);
2856 foreach ($keys as $key)
2857 {
2858 $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key]));
2859 }
2860 }
2861 if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'item'))
2862 {
2863 $keys = array_keys($items);
2864 foreach ($keys as $key)
2865 {
2866 $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key]));
2867 }
2868 }
2869 if ($items = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'item'))
2870 {
2871 $keys = array_keys($items);
2872 foreach ($keys as $key)
2873 {
2874 $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key]));
2875 }
2876 }
2877 }
2878 }
2879
2880 if (!empty($this->data['items']))
2881 {
2882 // If we want to order it by date, check if all items have a date, and then sort it
2883 if ($this->order_by_date && empty($this->multifeed_objects))
2884 {
2885 if (!isset($this->data['ordered_items']))
2886 {
2887 $do_sort = true;
2888 foreach ($this->data['items'] as $item)
2889 {
2890 if (!$item->get_date('U'))
2891 {
2892 $do_sort = false;
2893 break;
2894 }
2895 }
2896 $item = null;
2897 $this->data['ordered_items'] = $this->data['items'];
2898 if ($do_sort)
2899 {
2900 usort($this->data['ordered_items'], array(get_class($this), 'sort_items'));
2901 }
2902 }
2903 $items = $this->data['ordered_items'];
2904 }
2905 else
2906 {
2907 $items = $this->data['items'];
2908 }
2909
2910 // Slice the data as desired
2911 if ($end === 0)
2912 {
2913 return array_slice($items, $start);
2914 }
2915 else
2916 {
2917 return array_slice($items, $start, $end);
2918 }
2919 }
2920 else
2921 {
2922 return array();
2923 }
2924 }
2925
2926 /**
2927 * Set the favicon handler
2928 *
2929 * @deprecated Use your own favicon handling instead
2930 */
2931 public function set_favicon_handler($page = false, $qs = 'i')
2932 {
2933 $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING;
2934 trigger_error('Favicon handling has been removed, please use your own handling', $level);
2935 return false;
2936 }
2937
2938 /**
2939 * Get the favicon for the current feed
2940 *
2941 * @deprecated Use your own favicon handling instead
2942 */
2943 public function get_favicon()
2944 {
2945 $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING;
2946 trigger_error('Favicon handling has been removed, please use your own handling', $level);
2947
2948 if (($url = $this->get_link()) !== null)
2949 {
2950 return 'http://g.etfv.co/' . urlencode($url);
2951 }
2952
2953 return false;
2954 }
2955
2956 /**
2957 * Magic method handler
2958 *
2959 * @param string $method Method name
2960 * @param array $args Arguments to the method
2961 * @return mixed
2962 */
2963 public function __call($method, $args)
2964 {
2965 if (strpos($method, 'subscribe_') === 0)
2966 {
2967 $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING;
2968 trigger_error('subscribe_*() has been deprecated, implement the callback yourself', $level);
2969 return '';
2970 }
2971 if ($method === 'enable_xml_dump')
2972 {
2973 $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING;
2974 trigger_error('enable_xml_dump() has been deprecated, use get_raw_data() instead', $level);
2975 return false;
2976 }
2977
2978 $class = get_class($this);
2979 $trace = debug_backtrace();
2980 $file = $trace[0]['file'];
2981 $line = $trace[0]['line'];
2982 trigger_error("Call to undefined method $class::$method() in $file on line $line", E_USER_ERROR);
2983 }
2984
2985 /**
2986 * Sorting callback for items
2987 *
2988 * @access private
2989 * @param SimplePie $a
2990 * @param SimplePie $b
2991 * @return boolean
2992 */
2993 public static function sort_items($a, $b)
2994 {
2995 return $a->get_date('U') <= $b->get_date('U');
2996 }
2997
2998 /**
2999 * Merge items from several feeds into one
3000 *
3001 * If you're merging multiple feeds together, they need to all have dates
3002 * for the items or else SimplePie will refuse to sort them.
3003 *
3004 * @link http://simplepie.org/wiki/tutorial/sort_multiple_feeds_by_time_and_date#if_feeds_require_separate_per-feed_settings
3005 * @param array $urls List of SimplePie feed objects to merge
3006 * @param int $start Starting item
3007 * @param int $end Number of items to return
3008 * @param int $limit Maximum number of items per feed
3009 * @return array
3010 */
3011 public static function merge_items($urls, $start = 0, $end = 0, $limit = 0)
3012 {
3013 if (is_array($urls) && sizeof($urls) > 0)
3014 {
3015 $items = array();
3016 foreach ($urls as $arg)
3017 {
3018 if ($arg instanceof SimplePie)
3019 {
3020 $items = array_merge($items, $arg->get_items(0, $limit));
3021 }
3022 else
3023 {
3024 trigger_error('Arguments must be SimplePie objects', E_USER_WARNING);
3025 }
3026 }
3027
3028 $do_sort = true;
3029 foreach ($items as $item)
3030 {
3031 if (!$item->get_date('U'))
3032 {
3033 $do_sort = false;
3034 break;
3035 }
3036 }
3037 $item = null;
3038 if ($do_sort)
3039 {
3040 usort($items, array(get_class($urls[0]), 'sort_items'));
3041 }
3042
3043 if ($end === 0)
3044 {
3045 return array_slice($items, $start);
3046 }
3047 else
3048 {
3049 return array_slice($items, $start, $end);
3050 }
3051 }
3052 else
3053 {
3054 trigger_error('Cannot merge zero SimplePie objects', E_USER_WARNING);
3055 return array();
3056 }
3057 }
3058 }
3059
3060 /**
3061 * Manages all author-related data
3062 *
3063 * Used by {@see SimplePie_Item::get_author()} and {@see SimplePie::get_authors()}
3064 *
3065 * This class can be overloaded with {@see SimplePie::set_author_class()}
3066 *
3067 * @package SimplePie
3068 * @subpackage API
3069 */
3070 class SimplePie_Author
3071 {
3072 /**
3073 * Author's name
3074 *
3075 * @var string
3076 * @see get_name()
3077 */
3078 var $name;
3079
3080 /**
3081 * Author's link
3082 *
3083 * @var string
3084 * @see get_link()
3085 */
3086 var $link;
3087
3088 /**
3089 * Author's email address
3090 *
3091 * @var string
3092 * @see get_email()
3093 */
3094 var $email;
3095
3096 /**
3097 * Constructor, used to input the data
3098 *
3099 * @param string $name
3100 * @param string $link
3101 * @param string $email
3102 */
3103 public function __construct($name = null, $link = null, $email = null)
3104 {
3105 $this->name = $name;
3106 $this->link = $link;
3107 $this->email = $email;
3108 }
3109
3110 /**
3111 * String-ified version
3112 *
3113 * @return string
3114 */
3115 public function __toString()
3116 {
3117 // There is no $this->data here
3118 return md5(serialize($this));
3119 }
3120
3121 /**
3122 * Author's name
3123 *
3124 * @return string|null
3125 */
3126 public function get_name()
3127 {
3128 if ($this->name !== null)
3129 {
3130 return $this->name;
3131 }
3132 else
3133 {
3134 return null;
3135 }
3136 }
3137
3138 /**
3139 * Author's link
3140 *
3141 * @return string|null
3142 */
3143 public function get_link()
3144 {
3145 if ($this->link !== null)
3146 {
3147 return $this->link;
3148 }
3149 else
3150 {
3151 return null;
3152 }
3153 }
3154
3155 /**
3156 * Author's email address
3157 *
3158 * @return string|null
3159 */
3160 public function get_email()
3161 {
3162 if ($this->email !== null)
3163 {
3164 return $this->email;
3165 }
3166 else
3167 {
3168 return null;
3169 }
3170 }
3171 }
3172
3173 /**
3174 * Base for cache objects
3175 *
3176 * Classes to be used with {@see SimplePie_Cache::register()} are expected
3177 * to implement this interface.
3178 *
3179 * @package SimplePie
3180 * @subpackage Caching
3181 */
3182 interface SimplePie_Cache_Base
3183 {
3184 /**
3185 * Feed cache type
3186 *
3187 * @var string
3188 */
3189 const TYPE_FEED = 'spc';
3190
3191 /**
3192 * Image cache type
3193 *
3194 * @var string
3195 */
3196 const TYPE_IMAGE = 'spi';
3197
3198 /**
3199 * Create a new cache object
3200 *
3201 * @param string $location Location string (from SimplePie::$cache_location)
3202 * @param string $name Unique ID for the cache
3203 * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
3204 */
3205 public function __construct($location, $name, $type);
3206
3207 /**
3208 * Save data to the cache
3209 *
3210 * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
3211 * @return bool Successfulness
3212 */
3213 public function save($data);
3214
3215 /**
3216 * Retrieve the data saved to the cache
3217 *
3218 * @return array Data for SimplePie::$data
3219 */
3220 public function load();
3221
3222 /**
3223 * Retrieve the last modified time for the cache
3224 *
3225 * @return int Timestamp
3226 */
3227 public function mtime();
3228
3229 /**
3230 * Set the last modified time to the current time
3231 *
3232 * @return bool Success status
3233 */
3234 public function touch();
3235
3236 /**
3237 * Remove the cache
3238 *
3239 * @return bool Success status
3240 */
3241 public function unlink();
3242 }
3243
3244 /**
3245 * Base class for database-based caches
3246 *
3247 * @package SimplePie
3248 * @subpackage Caching
3249 */
3250 abstract class SimplePie_Cache_DB implements SimplePie_Cache_Base
3251 {
3252 /**
3253 * Helper for database conversion
3254 *
3255 * Converts a given {@see SimplePie} object into data to be stored
3256 *
3257 * @param SimplePie $data
3258 * @return array First item is the serialized data for storage, second item is the unique ID for this item
3259 */
3260 protected static function prepare_simplepie_object_for_cache($data)
3261 {
3262 $items = $data->get_items();
3263 $items_by_id = array();
3264
3265 if (!empty($items))
3266 {
3267 foreach ($items as $item)
3268 {
3269 $items_by_id[$item->get_id()] = $item;
3270 }
3271
3272 if (count($items_by_id) !== count($items))
3273 {
3274 $items_by_id = array();
3275 foreach ($items as $item)
3276 {
3277 $items_by_id[$item->get_id(true)] = $item;
3278 }
3279 }
3280
3281 if (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]))
3282 {
3283 $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0];
3284 }
3285 elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]))
3286 {
3287 $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0];
3288 }
3289 elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]))
3290 {
3291 $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0];
3292 }
3293 elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0]))
3294 {
3295 $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0];
3296 }
3297 else
3298 {
3299 $channel = null;
3300 }
3301
3302 if ($channel !== null)
3303 {
3304 if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry']))
3305 {
3306 unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry']);
3307 }
3308 if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry']))
3309 {
3310 unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry']);
3311 }
3312 if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']))
3313 {
3314 unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']);
3315 }
3316 if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']))
3317 {
3318 unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']);
3319 }
3320 if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item']))
3321 {
3322 unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item']);
3323 }
3324 }
3325 if (isset($data->data['items']))
3326 {
3327 unset($data->data['items']);
3328 }
3329 if (isset($data->data['ordered_items']))
3330 {
3331 unset($data->data['ordered_items']);
3332 }
3333 }
3334 return array(serialize($data->data), $items_by_id);
3335 }
3336 }
3337
3338 /**
3339 * Caches data to the filesystem
3340 *
3341 * @package SimplePie
3342 * @subpackage Caching
3343 */
3344 class SimplePie_Cache_File implements SimplePie_Cache_Base
3345 {
3346 /**
3347 * Location string
3348 *
3349 * @see SimplePie::$cache_location
3350 * @var string
3351 */
3352 protected $location;
3353
3354 /**
3355 * Filename
3356 *
3357 * @var string
3358 */
3359 protected $filename;
3360
3361 /**
3362 * File extension
3363 *
3364 * @var string
3365 */
3366 protected $extension;
3367
3368 /**
3369 * File path
3370 *
3371 * @var string
3372 */
3373 protected $name;
3374
3375 /**
3376 * Create a new cache object
3377 *
3378 * @param string $location Location string (from SimplePie::$cache_location)
3379 * @param string $name Unique ID for the cache
3380 * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
3381 */
3382 public function __construct($location, $name, $type)
3383 {
3384 $this->location = $location;
3385 $this->filename = $name;
3386 $this->extension = $type;
3387 $this->name = "$this->location/$this->filename.$this->extension";
3388 }
3389
3390 /**
3391 * Save data to the cache
3392 *
3393 * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
3394 * @return bool Successfulness
3395 */
3396 public function save($data)
3397 {
3398 if (file_exists($this->name) && is_writeable($this->name) || file_exists($this->location) && is_writeable($this->location))
3399 {
3400 if ($data instanceof SimplePie)
3401 {
3402 $data = $data->data;
3403 }
3404
3405 $data = serialize($data);
3406 return (bool) file_put_contents($this->name, $data);
3407 }
3408 return false;
3409 }
3410
3411 /**
3412 * Retrieve the data saved to the cache
3413 *
3414 * @return array Data for SimplePie::$data
3415 */
3416 public function load()
3417 {
3418 if (file_exists($this->name) && is_readable($this->name))
3419 {
3420 return unserialize(file_get_contents($this->name));
3421 }
3422 return false;
3423 }
3424
3425 /**
3426 * Retrieve the last modified time for the cache
3427 *
3428 * @return int Timestamp
3429 */
3430 public function mtime()
3431 {
3432 if (file_exists($this->name))
3433 {
3434 return filemtime($this->name);
3435 }
3436 return false;
3437 }
3438
3439 /**
3440 * Set the last modified time to the current time
3441 *
3442 * @return bool Success status
3443 */
3444 public function touch()
3445 {
3446 if (file_exists($this->name))
3447 {
3448 return touch($this->name);
3449 }
3450 return false;
3451 }
3452
3453 /**
3454 * Remove the cache
3455 *
3456 * @return bool Success status
3457 */
3458 public function unlink()
3459 {
3460 if (file_exists($this->name))
3461 {
3462 return unlink($this->name);
3463 }
3464 return false;
3465 }
3466 }
3467
3468 /**
3469 * Caches data to memcache
3470 *
3471 * Registered for URLs with the "memcache" protocol
3472 *
3473 * For example, `memcache://localhost:11211/?timeout=3600&prefix=sp_` will
3474 * connect to memcache on `localhost` on port 11211. All tables will be
3475 * prefixed with `sp_` and data will expire after 3600 seconds
3476 *
3477 * @package SimplePie
3478 * @subpackage Caching
3479 * @uses Memcache
3480 */
3481 class SimplePie_Cache_Memcache implements SimplePie_Cache_Base
3482 {
3483 /**
3484 * Memcache instance
3485 *
3486 * @var Memcache
3487 */
3488 protected $cache;
3489
3490 /**
3491 * Options
3492 *
3493 * @var array
3494 */
3495 protected $options;
3496
3497 /**
3498 * Cache name
3499 *
3500 * @var string
3501 */
3502 protected $name;
3503
3504 /**
3505 * Create a new cache object
3506 *
3507 * @param string $location Location string (from SimplePie::$cache_location)
3508 * @param string $name Unique ID for the cache
3509 * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
3510 */
3511 public function __construct($location, $name, $type)
3512 {
3513 $this->options = array(
3514 'host' => '127.0.0.1',
3515 'port' => 11211,
3516 'extras' => array(
3517 'timeout' => 3600, // one hour
3518 'prefix' => 'simplepie_',
3519 ),
3520 );
3521 $parsed = SimplePie_Cache::parse_URL($location);
3522 $this->options['host'] = empty($parsed['host']) ? $this->options['host'] : $parsed['host'];
3523 $this->options['port'] = empty($parsed['port']) ? $this->options['port'] : $parsed['port'];
3524 $this->options['extras'] = array_merge($this->options['extras'], $parsed['extras']);
3525 $this->name = $this->options['extras']['prefix'] . md5("$name:$type");
3526
3527 $this->cache = new Memcache();
3528 $this->cache->addServer($this->options['host'], (int) $this->options['port']);
3529 }
3530
3531 /**
3532 * Save data to the cache
3533 *
3534 * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
3535 * @return bool Successfulness
3536 */
3537 public function save($data)
3538 {
3539 if ($data instanceof SimplePie)
3540 {
3541 $data = $data->data;
3542 }
3543 return $this->cache->set($this->name, serialize($data), MEMCACHE_COMPRESSED, (int) $this->options['extras']['timeout']);
3544 }
3545
3546 /**
3547 * Retrieve the data saved to the cache
3548 *
3549 * @return array Data for SimplePie::$data
3550 */
3551 public function load()
3552 {
3553 $data = $this->cache->get($this->name);
3554
3555 if ($data !== false)
3556 {
3557 return unserialize($data);
3558 }
3559 return false;
3560 }
3561
3562 /**
3563 * Retrieve the last modified time for the cache
3564 *
3565 * @return int Timestamp
3566 */
3567 public function mtime()
3568 {
3569 $data = $this->cache->get($this->name);
3570
3571 if ($data !== false)
3572 {
3573 // essentially ignore the mtime because Memcache expires on it's own
3574 return time();
3575 }
3576
3577 return false;
3578 }
3579
3580 /**
3581 * Set the last modified time to the current time
3582 *
3583 * @return bool Success status
3584 */
3585 public function touch()
3586 {
3587 $data = $this->cache->get($this->name);
3588
3589 if ($data !== false)
3590 {
3591 return $this->cache->set($this->name, $data, MEMCACHE_COMPRESSED, (int) $this->duration);
3592 }
3593
3594 return false;
3595 }
3596
3597 /**
3598 * Remove the cache
3599 *
3600 * @return bool Success status
3601 */
3602 public function unlink()
3603 {
3604 return $this->cache->delete($this->name, 0);
3605 }
3606 }
3607
3608 /**
3609 * Caches data to a MySQL database
3610 *
3611 * Registered for URLs with the "mysql" protocol
3612 *
3613 * For example, `mysql://root:password@localhost:3306/mydb?prefix=sp_` will
3614 * connect to the `mydb` database on `localhost` on port 3306, with the user
3615 * `root` and the password `password`. All tables will be prefixed with `sp_`
3616 *
3617 * @package SimplePie
3618 * @subpackage Caching
3619 */
3620 class SimplePie_Cache_MySQL extends SimplePie_Cache_DB
3621 {
3622 /**
3623 * PDO instance
3624 *
3625 * @var PDO
3626 */
3627 protected $mysql;
3628
3629 /**
3630 * Options
3631 *
3632 * @var array
3633 */
3634 protected $options;
3635
3636 /**
3637 * Cache ID
3638 *
3639 * @var string
3640 */
3641 protected $id;
3642
3643 /**
3644 * Create a new cache object
3645 *
3646 * @param string $location Location string (from SimplePie::$cache_location)
3647 * @param string $name Unique ID for the cache
3648 * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
3649 */
3650 public function __construct($location, $name, $type)
3651 {
3652 $this->options = array(
3653 'user' => null,
3654 'pass' => null,
3655 'host' => '127.0.0.1',
3656 'port' => '3306',
3657 'path' => '',
3658 'extras' => array(
3659 'prefix' => '',
3660 ),
3661 );
3662 $this->options = array_merge_recursive($this->options, SimplePie_Cache::parse_URL($location));
3663
3664 // Path is prefixed with a "/"
3665 $this->options['dbname'] = substr($this->options['path'], 1);
3666
3667 try
3668 {
3669 $this->mysql = new PDO("mysql:dbname={$this->options['dbname']};host={$this->options['host']};port={$this->options['port']}", $this->options['user'], $this->options['pass'], array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));
3670 }
3671 catch (PDOException $e)
3672 {
3673 $this->mysql = null;
3674 return;
3675 }
3676
3677 $this->id = $name . $type;
3678
3679 if (!$query = $this->mysql->query('SHOW TABLES'))
3680 {
3681 $this->mysql = null;
3682 return;
3683 }
3684
3685 $db = array();
3686 while ($row = $query->fetchColumn())
3687 {
3688 $db[] = $row;
3689 }
3690
3691 if (!in_array($this->options['extras']['prefix'] . 'cache_data', $db))
3692 {
3693 $query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'cache_data` (`id` TEXT CHARACTER SET utf8 NOT NULL, `items` SMALLINT NOT NULL DEFAULT 0, `data` BLOB NOT NULL, `mtime` INT UNSIGNED NOT NULL, UNIQUE (`id`(125)))');
3694 if ($query === false)
3695 {
3696 $this->mysql = null;
3697 }
3698 }
3699
3700 if (!in_array($this->options['extras']['prefix'] . 'items', $db))
3701 {
3702 $query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'items` (`feed_id` TEXT CHARACTER SET utf8 NOT NULL, `id` TEXT CHARACTER SET utf8 NOT NULL, `data` TEXT CHARACTER SET utf8 NOT NULL, `posted` INT UNSIGNED NOT NULL, INDEX `feed_id` (`feed_id`(125)))');
3703 if ($query === false)
3704 {
3705 $this->mysql = null;
3706 }
3707 }
3708 }
3709
3710 /**
3711 * Save data to the cache
3712 *
3713 * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
3714 * @return bool Successfulness
3715 */
3716 public function save($data)
3717 {
3718 if ($this->mysql === null)
3719 {
3720 return false;
3721 }
3722
3723 if ($data instanceof SimplePie)
3724 {
3725 $data = clone $data;
3726
3727 $prepared = self::prepare_simplepie_object_for_cache($data);
3728
3729 $query = $this->mysql->prepare('SELECT COUNT(*) FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed');
3730 $query->bindValue(':feed', $this->id);
3731 if ($query->execute())
3732 {
3733 if ($query->fetchColumn() > 0)
3734 {
3735 $items = count($prepared[1]);
3736 if ($items)
3737 {
3738 $sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = :items, `data` = :data, `mtime` = :time WHERE `id` = :feed';
3739 $query = $this->mysql->prepare($sql);
3740 $query->bindValue(':items', $items);
3741 }
3742 else
3743 {
3744 $sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `data` = :data, `mtime` = :time WHERE `id` = :feed';
3745 $query = $this->mysql->prepare($sql);
3746 }
3747
3748 $query->bindValue(':data', $prepared[0]);
3749 $query->bindValue(':time', time());
3750 $query->bindValue(':feed', $this->id);
3751 if (!$query->execute())
3752 {
3753 return false;
3754 }
3755 }
3756 else
3757 {
3758 $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:feed, :count, :data, :time)');
3759 $query->bindValue(':feed', $this->id);
3760 $query->bindValue(':count', count($prepared[1]));
3761 $query->bindValue(':data', $prepared[0]);
3762 $query->bindValue(':time', time());
3763 if (!$query->execute())
3764 {
3765 return false;
3766 }
3767 }
3768
3769 $ids = array_keys($prepared[1]);
3770 if (!empty($ids))
3771 {
3772 foreach ($ids as $id)
3773 {
3774 $database_ids[] = $this->mysql->quote($id);
3775 }
3776
3777 $query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `id` = ' . implode(' OR `id` = ', $database_ids) . ' AND `feed_id` = :feed');
3778 $query->bindValue(':feed', $this->id);
3779
3780 if ($query->execute())
3781 {
3782 $existing_ids = array();
3783 while ($row = $query->fetchColumn())
3784 {
3785 $existing_ids[] = $row;
3786 }
3787
3788 $new_ids = array_diff($ids, $existing_ids);
3789
3790 foreach ($new_ids as $new_id)
3791 {
3792 if (!($date = $prepared[1][$new_id]->get_date('U')))
3793 {
3794 $date = time();
3795 }
3796
3797 $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'items` (`feed_id`, `id`, `data`, `posted`) VALUES(:feed, :id, :data, :date)');
3798 $query->bindValue(':feed', $this->id);
3799 $query->bindValue(':id', $new_id);
3800 $query->bindValue(':data', serialize($prepared[1][$new_id]->data));
3801 $query->bindValue(':date', $date);
3802 if (!$query->execute())
3803 {
3804 return false;
3805 }
3806 }
3807 return true;
3808 }
3809 }
3810 else
3811 {
3812 return true;
3813 }
3814 }
3815 }
3816 else
3817 {
3818 $query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed');
3819 $query->bindValue(':feed', $this->id);
3820 if ($query->execute())
3821 {
3822 if ($query->rowCount() > 0)
3823 {
3824 $query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = 0, `data` = :data, `mtime` = :time WHERE `id` = :feed');
3825 $query->bindValue(':data', serialize($data));
3826 $query->bindValue(':time', time());
3827 $query->bindValue(':feed', $this->id);
3828 if ($this->execute())
3829 {
3830 return true;
3831 }
3832 }
3833 else
3834 {
3835 $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:id, 0, :data, :time)');
3836 $query->bindValue(':id', $this->id);
3837 $query->bindValue(':data', serialize($data));
3838 $query->bindValue(':time', time());
3839 if ($query->execute())
3840 {
3841 return true;
3842 }
3843 }
3844 }
3845 }
3846 return false;
3847 }
3848
3849 /**
3850 * Retrieve the data saved to the cache
3851 *
3852 * @return array Data for SimplePie::$data
3853 */
3854 public function load()
3855 {
3856 if ($this->mysql === null)
3857 {
3858 return false;
3859 }
3860
3861 $query = $this->mysql->prepare('SELECT `items`, `data` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
3862 $query->bindValue(':id', $this->id);
3863 if ($query->execute() && ($row = $query->fetch()))
3864 {
3865 $data = unserialize($row[1]);
3866
3867 if (isset($this->options['items'][0]))
3868 {
3869 $items = (int) $this->options['items'][0];
3870 }
3871 else
3872 {
3873 $items = (int) $row[0];
3874 }
3875
3876 if ($items !== 0)
3877 {
3878 if (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]))
3879 {
3880 $feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0];
3881 }
3882 elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]))
3883 {
3884 $feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0];
3885 }
3886 elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]))
3887 {
3888 $feed =& $data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0];
3889 }
3890 elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]))
3891 {
3892 $feed =& $data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0];
3893 }
3894 else
3895 {
3896 $feed = null;
3897 }
3898
3899 if ($feed !== null)
3900 {
3901 $sql = 'SELECT `data` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :feed ORDER BY `posted` DESC';
3902 if ($items > 0)
3903 {
3904 $sql .= ' LIMIT ' . $items;
3905 }
3906
3907 $query = $this->mysql->prepare($sql);
3908 $query->bindValue(':feed', $this->id);
3909 if ($query->execute())
3910 {
3911 while ($row = $query->fetchColumn())
3912 {
3913 $feed['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry'][] = unserialize($row);
3914 }
3915 }
3916 else
3917 {
3918 return false;
3919 }
3920 }
3921 }
3922 return $data;
3923 }
3924 return false;
3925 }
3926
3927 /**
3928 * Retrieve the last modified time for the cache
3929 *
3930 * @return int Timestamp
3931 */
3932 public function mtime()
3933 {
3934 if ($this->mysql === null)
3935 {
3936 return false;
3937 }
3938
3939 $query = $this->mysql->prepare('SELECT `mtime` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
3940 $query->bindValue(':id', $this->id);
3941 if ($query->execute() && ($time = $query->fetchColumn()))
3942 {
3943 return $time;
3944 }
3945 else
3946 {
3947 return false;
3948 }
3949 }
3950
3951 /**
3952 * Set the last modified time to the current time
3953 *
3954 * @return bool Success status
3955 */
3956 public function touch()
3957 {
3958 if ($this->mysql === null)
3959 {
3960 return false;
3961 }
3962
3963 $query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `mtime` = :time WHERE `id` = :id');
3964 $query->bindValue(':time', time());
3965 $query->bindValue(':id', $this->id);
3966 if ($query->execute() && $query->rowCount() > 0)
3967 {
3968 return true;
3969 }
3970 else
3971 {
3972 return false;
3973 }
3974 }
3975
3976 /**
3977 * Remove the cache
3978 *
3979 * @return bool Success status
3980 */
3981 public function unlink()
3982 {
3983 if ($this->mysql === null)
3984 {
3985 return false;
3986 }
3987
3988 $query = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
3989 $query->bindValue(':id', $this->id);
3990 $query2 = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :id');
3991 $query2->bindValue(':id', $this->id);
3992 if ($query->execute() && $query2->execute())
3993 {
3994 return true;
3995 }
3996 else
3997 {
3998 return false;
3999 }
4000 }
4001 }
4002
4003 /**
4004 * Used to create cache objects
4005 *
4006 * This class can be overloaded with {@see SimplePie::set_cache_class()},
4007 * although the preferred way is to create your own handler
4008 * via {@see register()}
4009 *
4010 * @package SimplePie
4011 * @subpackage Caching
4012 */
4013 class SimplePie_Cache
4014 {
4015 /**
4016 * Cache handler classes
4017 *
4018 * These receive 3 parameters to their constructor, as documented in
4019 * {@see register()}
4020 * @var array
4021 */
4022 protected static $handlers = array(
4023 'mysql' => 'SimplePie_Cache_MySQL',
4024 'memcache' => 'SimplePie_Cache_Memcache',
4025 );
4026
4027 /**
4028 * Don't call the constructor. Please.
4029 */
4030 private function __construct() { }
4031
4032 /**
4033 * Create a new SimplePie_Cache object
4034 *
4035 * @param string $location URL location (scheme is used to determine handler)
4036 * @param string $filename Unique identifier for cache object
4037 * @param string $extension 'spi' or 'spc'
4038 * @return SimplePie_Cache_Base Type of object depends on scheme of `$location`
4039 */
4040 public static function get_handler($location, $filename, $extension)
4041 {
4042 $type = explode(':', $location, 2);
4043 $type = $type[0];
4044 if (!empty(self::$handlers[$type]))
4045 {
4046 $class = self::$handlers[$type];
4047 return new $class($location, $filename, $extension);
4048 }
4049
4050 return new SimplePie_Cache_File($location, $filename, $extension);
4051 }
4052
4053 /**
4054 * Create a new SimplePie_Cache object
4055 *
4056 * @deprecated Use {@see get_handler} instead
4057 */
4058 public function create($location, $filename, $extension)
4059 {
4060 trigger_error('Cache::create() has been replaced with Cache::get_handler(). Switch to the registry system to use this.', E_USER_DEPRECATED);
4061 return self::get_handler($location, $filename, $extension);
4062 }
4063
4064 /**
4065 * Register a handler
4066 *
4067 * @param string $type DSN type to register for
4068 * @param string $class Name of handler class. Must implement SimplePie_Cache_Base
4069 */
4070 public static function register($type, $class)
4071 {
4072 self::$handlers[$type] = $class;
4073 }
4074
4075 /**
4076 * Parse a URL into an array
4077 *
4078 * @param string $url
4079 * @return array
4080 */
4081 public static function parse_URL($url)
4082 {
4083 $params = parse_url($url);
4084 $params['extras'] = array();
4085 if (isset($params['query']))
4086 {
4087 parse_str($params['query'], $params['extras']);
4088 }
4089 return $params;
4090 }
4091 }
4092
4093 /**
4094 * Handles `<media:text>` captions as defined in Media RSS.
4095 *
4096 * Used by {@see SimplePie_Enclosure::get_caption()} and {@see SimplePie_Enclosure::get_captions()}
4097 *
4098 * This class can be overloaded with {@see SimplePie::set_caption_class()}
4099 *
4100 * @package SimplePie
4101 * @subpackage API
4102 */
4103 class SimplePie_Caption
4104 {
4105 /**
4106 * Content type
4107 *
4108 * @var string
4109 * @see get_type()
4110 */
4111 var $type;
4112
4113 /**
4114 * Language
4115 *
4116 * @var string
4117 * @see get_language()
4118 */
4119 var $lang;
4120
4121 /**
4122 * Start time
4123 *
4124 * @var string
4125 * @see get_starttime()
4126 */
4127 var $startTime;
4128
4129 /**
4130 * End time
4131 *
4132 * @var string
4133 * @see get_endtime()
4134 */
4135 var $endTime;
4136
4137 /**
4138 * Caption text
4139 *
4140 * @var string
4141 * @see get_text()
4142 */
4143 var $text;
4144
4145 /**
4146 * Constructor, used to input the data
4147 *
4148 * For documentation on all the parameters, see the corresponding
4149 * properties and their accessors
4150 */
4151 public function __construct($type = null, $lang = null, $startTime = null, $endTime = null, $text = null)
4152 {
4153 $this->type = $type;
4154 $this->lang = $lang;
4155 $this->startTime = $startTime;
4156 $this->endTime = $endTime;
4157 $this->text = $text;
4158 }
4159
4160 /**
4161 * String-ified version
4162 *
4163 * @return string
4164 */
4165 public function __toString()
4166 {
4167 // There is no $this->data here
4168 return md5(serialize($this));
4169 }
4170
4171 /**
4172 * Get the end time
4173 *
4174 * @return string|null Time in the format 'hh:mm:ss.SSS'
4175 */
4176 public function get_endtime()
4177 {
4178 if ($this->endTime !== null)
4179 {
4180 return $this->endTime;
4181 }
4182 else
4183 {
4184 return null;
4185 }
4186 }
4187
4188 /**
4189 * Get the language
4190 *
4191 * @link http://tools.ietf.org/html/rfc3066
4192 * @return string|null Language code as per RFC 3066
4193 */
4194 public function get_language()
4195 {
4196 if ($this->lang !== null)
4197 {
4198 return $this->lang;
4199 }
4200 else
4201 {
4202 return null;
4203 }
4204 }
4205
4206 /**
4207 * Get the start time
4208 *
4209 * @return string|null Time in the format 'hh:mm:ss.SSS'
4210 */
4211 public function get_starttime()
4212 {
4213 if ($this->startTime !== null)
4214 {
4215 return $this->startTime;
4216 }
4217 else
4218 {
4219 return null;
4220 }
4221 }
4222
4223 /**
4224 * Get the text of the caption
4225 *
4226 * @return string|null
4227 */
4228 public function get_text()
4229 {
4230 if ($this->text !== null)
4231 {
4232 return $this->text;
4233 }
4234 else
4235 {
4236 return null;
4237 }
4238 }
4239
4240 /**
4241 * Get the content type (not MIME type)
4242 *
4243 * @return string|null Either 'text' or 'html'
4244 */
4245 public function get_type()
4246 {
4247 if ($this->type !== null)
4248 {
4249 return $this->type;
4250 }
4251 else
4252 {
4253 return null;
4254 }
4255 }
4256 }
4257
4258 /**
4259 * Manages all category-related data
4260 *
4261 * Used by {@see SimplePie_Item::get_category()} and {@see SimplePie_Item::get_categories()}
4262 *
4263 * This class can be overloaded with {@see SimplePie::set_category_class()}
4264 *
4265 * @package SimplePie
4266 * @subpackage API
4267 */
4268 class SimplePie_Category
4269 {
4270 /**
4271 * Category identifier
4272 *
4273 * @var string
4274 * @see get_term
4275 */
4276 var $term;
4277
4278 /**
4279 * Categorization scheme identifier
4280 *
4281 * @var string
4282 * @see get_scheme()
4283 */
4284 var $scheme;
4285
4286 /**
4287 * Human readable label
4288 *
4289 * @var string
4290 * @see get_label()
4291 */
4292 var $label;
4293
4294 /**
4295 * Constructor, used to input the data
4296 *
4297 * @param string $term
4298 * @param string $scheme
4299 * @param string $label
4300 */
4301 public function __construct($term = null, $scheme = null, $label = null)
4302 {
4303 $this->term = $term;
4304 $this->scheme = $scheme;
4305 $this->label = $label;
4306 }
4307
4308 /**
4309 * String-ified version
4310 *
4311 * @return string
4312 */
4313 public function __toString()
4314 {
4315 // There is no $this->data here
4316 return md5(serialize($this));
4317 }
4318
4319 /**
4320 * Get the category identifier
4321 *
4322 * @return string|null
4323 */
4324 public function get_term()
4325 {
4326 if ($this->term !== null)
4327 {
4328 return $this->term;
4329 }
4330 else
4331 {
4332 return null;
4333 }
4334 }
4335
4336 /**
4337 * Get the categorization scheme identifier
4338 *
4339 * @return string|null
4340 */
4341 public function get_scheme()
4342 {
4343 if ($this->scheme !== null)
4344 {
4345 return $this->scheme;
4346 }
4347 else
4348 {
4349 return null;
4350 }
4351 }
4352
4353 /**
4354 * Get the human readable label
4355 *
4356 * @return string|null
4357 */
4358 public function get_label()
4359 {
4360 if ($this->label !== null)
4361 {
4362 return $this->label;
4363 }
4364 else
4365 {
4366 return $this->get_term();
4367 }
4368 }
4369 }
4370
4371 /**
4372 * Content-type sniffing
4373 *
4374 * Based on the rules in http://tools.ietf.org/html/draft-abarth-mime-sniff-06
4375 *
4376 * This is used since we can't always trust Content-Type headers, and is based
4377 * upon the HTML5 parsing rules.
4378 *
4379 *
4380 * This class can be overloaded with {@see SimplePie::set_content_type_sniffer_class()}
4381 *
4382 * @package SimplePie
4383 * @subpackage HTTP
4384 */
4385 class SimplePie_Content_Type_Sniffer
4386 {
4387 /**
4388 * File object
4389 *
4390 * @var SimplePie_File
4391 */
4392 var $file;
4393
4394 /**
4395 * Create an instance of the class with the input file
4396 *
4397 * @param SimplePie_Content_Type_Sniffer $file Input file
4398 */
4399 public function __construct($file)
4400 {
4401 $this->file = $file;
4402 }
4403
4404 /**
4405 * Get the Content-Type of the specified file
4406 *
4407 * @return string Actual Content-Type
4408 */
4409 public function get_type()
4410 {
4411 if (isset($this->file->headers['content-type']))
4412 {
4413 if (!isset($this->file->headers['content-encoding'])
4414 && ($this->file->headers['content-type'] === 'text/plain'
4415 || $this->file->headers['content-type'] === 'text/plain; charset=ISO-8859-1'
4416 || $this->file->headers['content-type'] === 'text/plain; charset=iso-8859-1'
4417 || $this->file->headers['content-type'] === 'text/plain; charset=UTF-8'))
4418 {
4419 return $this->text_or_binary();
4420 }
4421
4422 if (($pos = strpos($this->file->headers['content-type'], ';')) !== false)
4423 {
4424 $official = substr($this->file->headers['content-type'], 0, $pos);
4425 }
4426 else
4427 {
4428 $official = $this->file->headers['content-type'];
4429 }
4430 $official = trim(strtolower($official));
4431
4432 if ($official === 'unknown/unknown'
4433 || $official === 'application/unknown')
4434 {
4435 return $this->unknown();
4436 }
4437 elseif (substr($official, -4) === '+xml'
4438 || $official === 'text/xml'
4439 || $official === 'application/xml')
4440 {
4441 return $official;
4442 }
4443 elseif (substr($official, 0, 6) === 'image/')
4444 {
4445 if ($return = $this->image())
4446 {
4447 return $return;
4448 }
4449 else
4450 {
4451 return $official;
4452 }
4453 }
4454 elseif ($official === 'text/html')
4455 {
4456 return $this->feed_or_html();
4457 }
4458 else
4459 {
4460 return $official;
4461 }
4462 }
4463 else
4464 {
4465 return $this->unknown();
4466 }
4467 }
4468
4469 /**
4470 * Sniff text or binary
4471 *
4472 * @return string Actual Content-Type
4473 */
4474 public function text_or_binary()
4475 {
4476 if (substr($this->file->body, 0, 2) === "\xFE\xFF"
4477 || substr($this->file->body, 0, 2) === "\xFF\xFE"
4478 || substr($this->file->body, 0, 4) === "\x00\x00\xFE\xFF"
4479 || substr($this->file->body, 0, 3) === "\xEF\xBB\xBF")
4480 {
4481 return 'text/plain';
4482 }
4483 elseif (preg_match('/[\x00-\x08\x0E-\x1A\x1C-\x1F]/', $this->file->body))
4484 {
4485 return 'application/octect-stream';
4486 }
4487 else
4488 {
4489 return 'text/plain';
4490 }
4491 }
4492
4493 /**
4494 * Sniff unknown
4495 *
4496 * @return string Actual Content-Type
4497 */
4498 public function unknown()
4499 {
4500 $ws = strspn($this->file->body, "\x09\x0A\x0B\x0C\x0D\x20");
4501 if (strtolower(substr($this->file->body, $ws, 14)) === '<!doctype html'
4502 || strtolower(substr($this->file->body, $ws, 5)) === '<html'
4503 || strtolower(substr($this->file->body, $ws, 7)) === '<script')
4504 {
4505 return 'text/html';
4506 }
4507 elseif (substr($this->file->body, 0, 5) === '%PDF-')
4508 {
4509 return 'application/pdf';
4510 }
4511 elseif (substr($this->file->body, 0, 11) === '%!PS-Adobe-')
4512 {
4513 return 'application/postscript';
4514 }
4515 elseif (substr($this->file->body, 0, 6) === 'GIF87a'
4516 || substr($this->file->body, 0, 6) === 'GIF89a')
4517 {
4518 return 'image/gif';
4519 }
4520 elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A")
4521 {
4522 return 'image/png';
4523 }
4524 elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF")
4525 {
4526 return 'image/jpeg';
4527 }
4528 elseif (substr($this->file->body, 0, 2) === "\x42\x4D")
4529 {
4530 return 'image/bmp';
4531 }
4532 elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00")
4533 {
4534 return 'image/vnd.microsoft.icon';
4535 }
4536 else
4537 {
4538 return $this->text_or_binary();
4539 }
4540 }
4541
4542 /**
4543 * Sniff images
4544 *
4545 * @return string Actual Content-Type
4546 */
4547 public function image()
4548 {
4549 if (substr($this->file->body, 0, 6) === 'GIF87a'
4550 || substr($this->file->body, 0, 6) === 'GIF89a')
4551 {
4552 return 'image/gif';
4553 }
4554 elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A")
4555 {
4556 return 'image/png';
4557 }
4558 elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF")
4559 {
4560 return 'image/jpeg';
4561 }
4562 elseif (substr($this->file->body, 0, 2) === "\x42\x4D")
4563 {
4564 return 'image/bmp';
4565 }
4566 elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00")
4567 {
4568 return 'image/vnd.microsoft.icon';
4569 }
4570 else
4571 {
4572 return false;
4573 }
4574 }
4575
4576 /**
4577 * Sniff HTML
4578 *
4579 * @return string Actual Content-Type
4580 */
4581 public function feed_or_html()
4582 {
4583 $len = strlen($this->file->body);
4584 $pos = strspn($this->file->body, "\x09\x0A\x0D\x20");
4585
4586 while ($pos < $len)
4587 {
4588 switch ($this->file->body[$pos])
4589 {
4590 case "\x09":
4591 case "\x0A":
4592 case "\x0D":
4593 case "\x20":
4594 $pos += strspn($this->file->body, "\x09\x0A\x0D\x20", $pos);
4595 continue 2;
4596
4597 case '<':
4598 $pos++;
4599 break;
4600
4601 default:
4602 return 'text/html';
4603 }
4604
4605 if (substr($this->file->body, $pos, 3) === '!--')
4606 {
4607 $pos += 3;
4608 if ($pos < $len && ($pos = strpos($this->file->body, '-->', $pos)) !== false)
4609 {
4610 $pos += 3;
4611 }
4612 else
4613 {
4614 return 'text/html';
4615 }
4616 }
4617 elseif (substr($this->file->body, $pos, 1) === '!')
4618 {
4619 if ($pos < $len && ($pos = strpos($this->file->body, '>', $pos)) !== false)
4620 {
4621 $pos++;
4622 }
4623 else
4624 {
4625 return 'text/html';
4626 }
4627 }
4628 elseif (substr($this->file->body, $pos, 1) === '?')
4629 {
4630 if ($pos < $len && ($pos = strpos($this->file->body, '?>', $pos)) !== false)
4631 {
4632 $pos += 2;
4633 }
4634 else
4635 {
4636 return 'text/html';
4637 }
4638 }
4639 elseif (substr($this->file->body, $pos, 3) === 'rss'
4640 || substr($this->file->body, $pos, 7) === 'rdf:RDF')
4641 {
4642 return 'application/rss+xml';
4643 }
4644 elseif (substr($this->file->body, $pos, 4) === 'feed')
4645 {
4646 return 'application/atom+xml';
4647 }
4648 else
4649 {
4650 return 'text/html';
4651 }
4652 }
4653
4654 return 'text/html';
4655 }
4656 }
4657
4658 /**
4659 * Manages `<media:copyright>` copyright tags as defined in Media RSS
4660 *
4661 * Used by {@see SimplePie_Enclosure::get_copyright()}
4662 *
4663 * This class can be overloaded with {@see SimplePie::set_copyright_class()}
4664 *
4665 * @package SimplePie
4666 * @subpackage API
4667 */
4668 class SimplePie_Copyright
4669 {
4670 /**
4671 * Copyright URL
4672 *
4673 * @var string
4674 * @see get_url()
4675 */
4676 var $url;
4677
4678 /**
4679 * Attribution
4680 *
4681 * @var string
4682 * @see get_attribution()
4683 */
4684 var $label;
4685
4686 /**
4687 * Constructor, used to input the data
4688 *
4689 * For documentation on all the parameters, see the corresponding
4690 * properties and their accessors
4691 */
4692 public function __construct($url = null, $label = null)
4693 {
4694 $this->url = $url;
4695 $this->label = $label;
4696 }
4697
4698 /**
4699 * String-ified version
4700 *
4701 * @return string
4702 */
4703 public function __toString()
4704 {
4705 // There is no $this->data here
4706 return md5(serialize($this));
4707 }
4708
4709 /**
4710 * Get the copyright URL
4711 *
4712 * @return string|null URL to copyright information
4713 */
4714 public function get_url()
4715 {
4716 if ($this->url !== null)
4717 {
4718 return $this->url;
4719 }
4720 else
4721 {
4722 return null;
4723 }
4724 }
4725
4726 /**
4727 * Get the attribution text
4728 *
4729 * @return string|null
4730 */
4731 public function get_attribution()
4732 {
4733 if ($this->label !== null)
4734 {
4735 return $this->label;
4736 }
4737 else
4738 {
4739 return null;
4740 }
4741 }
4742 }
4743
4744 /**
4745 * SimplePie class.
4746 *
4747 * Class for backward compatibility.
4748 *
4749 * @deprecated Use {@see SimplePie} directly
4750 * @package SimplePie
4751 * @subpackage API
4752 */
4753 class SimplePie_Core extends SimplePie
4754 {
4755
4756 }
4757
4758 /**
4759 * Handles `<media:credit>` as defined in Media RSS
4760 *
4761 * Used by {@see SimplePie_Enclosure::get_credit()} and {@see SimplePie_Enclosure::get_credits()}
4762 *
4763 * This class can be overloaded with {@see SimplePie::set_credit_class()}
4764 *
4765 * @package SimplePie
4766 * @subpackage API
4767 */
4768 class SimplePie_Credit
4769 {
4770 /**
4771 * Credited role
4772 *
4773 * @var string
4774 * @see get_role()
4775 */
4776 var $role;
4777
4778 /**
4779 * Organizational scheme
4780 *
4781 * @var string
4782 * @see get_scheme()
4783 */
4784 var $scheme;
4785
4786 /**
4787 * Credited name
4788 *
4789 * @var string
4790 * @see get_name()
4791 */
4792 var $name;
4793
4794 /**
4795 * Constructor, used to input the data
4796 *
4797 * For documentation on all the parameters, see the corresponding
4798 * properties and their accessors
4799 */
4800 public function __construct($role = null, $scheme = null, $name = null)
4801 {
4802 $this->role = $role;
4803 $this->scheme = $scheme;
4804 $this->name = $name;
4805 }
4806
4807 /**
4808 * String-ified version
4809 *
4810 * @return string
4811 */
4812 public function __toString()
4813 {
4814 // There is no $this->data here
4815 return md5(serialize($this));
4816 }
4817
4818 /**
4819 * Get the role of the person receiving credit
4820 *
4821 * @return string|null
4822 */
4823 public function get_role()
4824 {
4825 if ($this->role !== null)
4826 {
4827 return $this->role;
4828 }
4829 else
4830 {
4831 return null;
4832 }
4833 }
4834
4835 /**
4836 * Get the organizational scheme
4837 *
4838 * @return string|null
4839 */
4840 public function get_scheme()
4841 {
4842 if ($this->scheme !== null)
4843 {
4844 return $this->scheme;
4845 }
4846 else
4847 {
4848 return null;
4849 }
4850 }
4851
4852 /**
4853 * Get the credited person/entity's name
4854 *
4855 * @return string|null
4856 */
4857 public function get_name()
4858 {
4859 if ($this->name !== null)
4860 {
4861 return $this->name;
4862 }
4863 else
4864 {
4865 return null;
4866 }
4867 }
4868 }
4869
4870 /**
4871 * Decode HTML Entities
4872 *
4873 * This implements HTML5 as of revision 967 (2007-06-28)
4874 *
4875 * @deprecated Use DOMDocument instead!
4876 * @package SimplePie
4877 */
4878 class SimplePie_Decode_HTML_Entities
4879 {
4880 /**
4881 * Data to be parsed
4882 *
4883 * @access private
4884 * @var string
4885 */
4886 var $data = '';
4887
4888 /**
4889 * Currently consumed bytes
4890 *
4891 * @access private
4892 * @var string
4893 */
4894 var $consumed = '';
4895
4896 /**
4897 * Position of the current byte being parsed
4898 *
4899 * @access private
4900 * @var int
4901 */
4902 var $position = 0;
4903
4904 /**
4905 * Create an instance of the class with the input data
4906 *
4907 * @access public
4908 * @param string $data Input data
4909 */
4910 public function __construct($data)
4911 {
4912 $this->data = $data;
4913 }
4914
4915 /**
4916 * Parse the input data
4917 *
4918 * @access public
4919 * @return string Output data
4920 */
4921 public function parse()
4922 {
4923 while (($this->position = strpos($this->data, '&', $this->position)) !== false)
4924 {
4925 $this->consume();
4926 $this->entity();
4927 $this->consumed = '';
4928 }
4929 return $this->data;
4930 }
4931
4932 /**
4933 * Consume the next byte
4934 *
4935 * @access private
4936 * @return mixed The next byte, or false, if there is no more data
4937 */
4938 public function consume()
4939 {
4940 if (isset($this->data[$this->position]))
4941 {
4942 $this->consumed .= $this->data[$this->position];
4943 return $this->data[$this->position++];
4944 }
4945 else
4946 {
4947 return false;
4948 }
4949 }
4950
4951 /**
4952 * Consume a range of characters
4953 *
4954 * @access private
4955 * @param string $chars Characters to consume
4956 * @return mixed A series of characters that match the range, or false
4957 */
4958 public function consume_range($chars)
4959 {
4960 if ($len = strspn($this->data, $chars, $this->position))
4961 {
4962 $data = substr($this->data, $this->position, $len);
4963 $this->consumed .= $data;
4964 $this->position += $len;
4965 return $data;
4966 }
4967 else
4968 {
4969 return false;
4970 }
4971 }
4972
4973 /**
4974 * Unconsume one byte
4975 *
4976 * @access private
4977 */
4978 public function unconsume()
4979 {
4980 $this->consumed = substr($this->consumed, 0, -1);
4981 $this->position--;
4982 }
4983
4984 /**
4985 * Decode an entity
4986 *
4987 * @access private
4988 */
4989 public function entity()
4990 {
4991 switch ($this->consume())
4992 {
4993 case "\x09":
4994 case "\x0A":
4995 case "\x0B":
4996 case "\x0B":
4997 case "\x0C":
4998 case "\x20":
4999 case "\x3C":
5000 case "\x26":
5001 case false:
5002 break;
5003
5004 case "\x23":
5005 switch ($this->consume())
5006 {
5007 case "\x78":
5008 case "\x58":
5009 $range = '0123456789ABCDEFabcdef';
5010 $hex = true;
5011 break;
5012
5013 default:
5014 $range = '0123456789';
5015 $hex = false;
5016 $this->unconsume();
5017 break;
5018 }
5019
5020 if ($codepoint = $this->consume_range($range))
5021 {
5022 static $windows_1252_specials = array(0x0D => "\x0A", 0x80 => "\xE2\x82\xAC", 0x81 => "\xEF\xBF\xBD", 0x82 => "\xE2\x80\x9A", 0x83 => "\xC6\x92", 0x84 => "\xE2\x80\x9E", 0x85 => "\xE2\x80\xA6", 0x86 => "\xE2\x80\xA0", 0x87 => "\xE2\x80\xA1", 0x88 => "\xCB\x86", 0x89 => "\xE2\x80\xB0", 0x8A => "\xC5\xA0", 0x8B => "\xE2\x80\xB9", 0x8C => "\xC5\x92", 0x8D => "\xEF\xBF\xBD", 0x8E => "\xC5\xBD", 0x8F => "\xEF\xBF\xBD", 0x90 => "\xEF\xBF\xBD", 0x91 => "\xE2\x80\x98", 0x92 => "\xE2\x80\x99", 0x93 => "\xE2\x80\x9C", 0x94 => "\xE2\x80\x9D", 0x95 => "\xE2\x80\xA2", 0x96 => "\xE2\x80\x93", 0x97 => "\xE2\x80\x94", 0x98 => "\xCB\x9C", 0x99 => "\xE2\x84\xA2", 0x9A => "\xC5\xA1", 0x9B => "\xE2\x80\xBA", 0x9C => "\xC5\x93", 0x9D => "\xEF\xBF\xBD", 0x9E => "\xC5\xBE", 0x9F => "\xC5\xB8");
5023
5024 if ($hex)
5025 {
5026 $codepoint = hexdec($codepoint);
5027 }
5028 else
5029 {
5030 $codepoint = intval($codepoint);
5031 }
5032
5033 if (isset($windows_1252_specials[$codepoint]))
5034 {
5035 $replacement = $windows_1252_specials[$codepoint];
5036 }
5037 else
5038 {
5039 $replacement = SimplePie_Misc::codepoint_to_utf8($codepoint);
5040 }
5041
5042 if (!in_array($this->consume(), array(';', false), true))
5043 {
5044 $this->unconsume();
5045 }
5046
5047 $consumed_length = strlen($this->consumed);
5048 $this->data = substr_replace($this->data, $replacement, $this->position - $consumed_length, $consumed_length);
5049 $this->position += strlen($replacement) - $consumed_length;
5050 }
5051 break;
5052
5053 default:
5054 static $entities = array(
5055 'Aacute' => "\xC3\x81",
5056 'aacute' => "\xC3\xA1",
5057 'Aacute;' => "\xC3\x81",
5058 'aacute;' => "\xC3\xA1",
5059 'Acirc' => "\xC3\x82",
5060 'acirc' => "\xC3\xA2",
5061 'Acirc;' => "\xC3\x82",
5062 'acirc;' => "\xC3\xA2",
5063 'acute' => "\xC2\xB4",
5064 'acute;' => "\xC2\xB4",
5065 'AElig' => "\xC3\x86",
5066 'aelig' => "\xC3\xA6",
5067 'AElig;' => "\xC3\x86",
5068 'aelig;' => "\xC3\xA6",
5069 'Agrave' => "\xC3\x80",
5070 'agrave' => "\xC3\xA0",
5071 'Agrave;' => "\xC3\x80",
5072 'agrave;' => "\xC3\xA0",
5073 'alefsym;' => "\xE2\x84\xB5",
5074 'Alpha;' => "\xCE\x91",
5075 'alpha;' => "\xCE\xB1",
5076 'AMP' => "\x26",
5077 'amp' => "\x26",
5078 'AMP;' => "\x26",
5079 'amp;' => "\x26",
5080 'and;' => "\xE2\x88\xA7",
5081 'ang;' => "\xE2\x88\xA0",
5082 'apos;' => "\x27",
5083 'Aring' => "\xC3\x85",
5084 'aring' => "\xC3\xA5",
5085 'Aring;' => "\xC3\x85",
5086 'aring;' => "\xC3\xA5",
5087 'asymp;' => "\xE2\x89\x88",
5088 'Atilde' => "\xC3\x83",
5089 'atilde' => "\xC3\xA3",
5090 'Atilde;' => "\xC3\x83",
5091 'atilde;' => "\xC3\xA3",
5092 'Auml' => "\xC3\x84",
5093 'auml' => "\xC3\xA4",
5094 'Auml;' => "\xC3\x84",
5095 'auml;' => "\xC3\xA4",
5096 'bdquo;' => "\xE2\x80\x9E",
5097 'Beta;' => "\xCE\x92",
5098 'beta;' => "\xCE\xB2",
5099 'brvbar' => "\xC2\xA6",
5100 'brvbar;' => "\xC2\xA6",
5101 'bull;' => "\xE2\x80\xA2",
5102 'cap;' => "\xE2\x88\xA9",
5103 'Ccedil' => "\xC3\x87",
5104 'ccedil' => "\xC3\xA7",
5105 'Ccedil;' => "\xC3\x87",
5106 'ccedil;' => "\xC3\xA7",
5107 'cedil' => "\xC2\xB8",
5108 'cedil;' => "\xC2\xB8",
5109 'cent' => "\xC2\xA2",
5110 'cent;' => "\xC2\xA2",
5111 'Chi;' => "\xCE\xA7",
5112 'chi;' => "\xCF\x87",
5113 'circ;' => "\xCB\x86",
5114 'clubs;' => "\xE2\x99\xA3",
5115 'cong;' => "\xE2\x89\x85",
5116 'COPY' => "\xC2\xA9",
5117 'copy' => "\xC2\xA9",
5118 'COPY;' => "\xC2\xA9",
5119 'copy;' => "\xC2\xA9",
5120 'crarr;' => "\xE2\x86\xB5",
5121 'cup;' => "\xE2\x88\xAA",
5122 'curren' => "\xC2\xA4",
5123 'curren;' => "\xC2\xA4",
5124 'Dagger;' => "\xE2\x80\xA1",
5125 'dagger;' => "\xE2\x80\xA0",
5126 'dArr;' => "\xE2\x87\x93",
5127 'darr;' => "\xE2\x86\x93",
5128 'deg' => "\xC2\xB0",
5129 'deg;' => "\xC2\xB0",
5130 'Delta;' => "\xCE\x94",
5131 'delta;' => "\xCE\xB4",
5132 'diams;' => "\xE2\x99\xA6",
5133 'divide' => "\xC3\xB7",
5134 'divide;' => "\xC3\xB7",
5135 'Eacute' => "\xC3\x89",
5136 'eacute' => "\xC3\xA9",
5137 'Eacute;' => "\xC3\x89",
5138 'eacute;' => "\xC3\xA9",
5139 'Ecirc' => "\xC3\x8A",
5140 'ecirc' => "\xC3\xAA",
5141 'Ecirc;' => "\xC3\x8A",
5142 'ecirc;' => "\xC3\xAA",
5143 'Egrave' => "\xC3\x88",
5144 'egrave' => "\xC3\xA8",
5145 'Egrave;' => "\xC3\x88",
5146 'egrave;' => "\xC3\xA8",
5147 'empty;' => "\xE2\x88\x85",
5148 'emsp;' => "\xE2\x80\x83",
5149 'ensp;' => "\xE2\x80\x82",
5150 'Epsilon;' => "\xCE\x95",
5151 'epsilon;' => "\xCE\xB5",
5152 'equiv;' => "\xE2\x89\xA1",
5153 'Eta;' => "\xCE\x97",
5154 'eta;' => "\xCE\xB7",
5155 'ETH' => "\xC3\x90",
5156 'eth' => "\xC3\xB0",
5157 'ETH;' => "\xC3\x90",
5158 'eth;' => "\xC3\xB0",
5159 'Euml' => "\xC3\x8B",
5160 'euml' => "\xC3\xAB",
5161 'Euml;' => "\xC3\x8B",
5162 'euml;' => "\xC3\xAB",
5163 'euro;' => "\xE2\x82\xAC",
5164 'exist;' => "\xE2\x88\x83",
5165 'fnof;' => "\xC6\x92",
5166 'forall;' => "\xE2\x88\x80",
5167 'frac12' => "\xC2\xBD",
5168 'frac12;' => "\xC2\xBD",
5169 'frac14' => "\xC2\xBC",
5170 'frac14;' => "\xC2\xBC",
5171 'frac34' => "\xC2\xBE",
5172 'frac34;' => "\xC2\xBE",
5173 'frasl;' => "\xE2\x81\x84",
5174 'Gamma;' => "\xCE\x93",
5175 'gamma;' => "\xCE\xB3",
5176 'ge;' => "\xE2\x89\xA5",
5177 'GT' => "\x3E",
5178 'gt' => "\x3E",
5179 'GT;' => "\x3E",
5180 'gt;' => "\x3E",
5181 'hArr;' => "\xE2\x87\x94",
5182 'harr;' => "\xE2\x86\x94",
5183 'hearts;' => "\xE2\x99\xA5",
5184 'hellip;' => "\xE2\x80\xA6",
5185 'Iacute' => "\xC3\x8D",
5186 'iacute' => "\xC3\xAD",
5187 'Iacute;' => "\xC3\x8D",
5188 'iacute;' => "\xC3\xAD",
5189 'Icirc' => "\xC3\x8E",
5190 'icirc' => "\xC3\xAE",
5191 'Icirc;' => "\xC3\x8E",
5192 'icirc;' => "\xC3\xAE",
5193 'iexcl' => "\xC2\xA1",
5194 'iexcl;' => "\xC2\xA1",
5195 'Igrave' => "\xC3\x8C",
5196 'igrave' => "\xC3\xAC",
5197 'Igrave;' => "\xC3\x8C",
5198 'igrave;' => "\xC3\xAC",
5199 'image;' => "\xE2\x84\x91",
5200 'infin;' => "\xE2\x88\x9E",
5201 'int;' => "\xE2\x88\xAB",
5202 'Iota;' => "\xCE\x99",
5203 'iota;' => "\xCE\xB9",
5204 'iquest' => "\xC2\xBF",
5205 'iquest;' => "\xC2\xBF",
5206 'isin;' => "\xE2\x88\x88",
5207 'Iuml' => "\xC3\x8F",
5208 'iuml' => "\xC3\xAF",
5209 'Iuml;' => "\xC3\x8F",
5210 'iuml;' => "\xC3\xAF",
5211 'Kappa;' => "\xCE\x9A",
5212 'kappa;' => "\xCE\xBA",
5213 'Lambda;' => "\xCE\x9B",
5214 'lambda;' => "\xCE\xBB",
5215 'lang;' => "\xE3\x80\x88",
5216 'laquo' => "\xC2\xAB",
5217 'laquo;' => "\xC2\xAB",
5218 'lArr;' => "\xE2\x87\x90",
5219 'larr;' => "\xE2\x86\x90",
5220 'lceil;' => "\xE2\x8C\x88",
5221 'ldquo;' => "\xE2\x80\x9C",
5222 'le;' => "\xE2\x89\xA4",
5223 'lfloor;' => "\xE2\x8C\x8A",
5224 'lowast;' => "\xE2\x88\x97",
5225 'loz;' => "\xE2\x97\x8A",
5226 'lrm;' => "\xE2\x80\x8E",
5227 'lsaquo;' => "\xE2\x80\xB9",
5228 'lsquo;' => "\xE2\x80\x98",
5229 'LT' => "\x3C",
5230 'lt' => "\x3C",
5231 'LT;' => "\x3C",
5232 'lt;' => "\x3C",
5233 'macr' => "\xC2\xAF",
5234 'macr;' => "\xC2\xAF",
5235 'mdash;' => "\xE2\x80\x94",
5236 'micro' => "\xC2\xB5",
5237 'micro;' => "\xC2\xB5",
5238 'middot' => "\xC2\xB7",
5239 'middot;' => "\xC2\xB7",
5240 'minus;' => "\xE2\x88\x92",
5241 'Mu;' => "\xCE\x9C",
5242 'mu;' => "\xCE\xBC",
5243 'nabla;' => "\xE2\x88\x87",
5244 'nbsp' => "\xC2\xA0",
5245 'nbsp;' => "\xC2\xA0",
5246 'ndash;' => "\xE2\x80\x93",
5247 'ne;' => "\xE2\x89\xA0",
5248 'ni;' => "\xE2\x88\x8B",
5249 'not' => "\xC2\xAC",
5250 'not;' => "\xC2\xAC",
5251 'notin;' => "\xE2\x88\x89",
5252 'nsub;' => "\xE2\x8A\x84",
5253 'Ntilde' => "\xC3\x91",
5254 'ntilde' => "\xC3\xB1",
5255 'Ntilde;' => "\xC3\x91",
5256 'ntilde;' => "\xC3\xB1",
5257 'Nu;' => "\xCE\x9D",
5258 'nu;' => "\xCE\xBD",
5259 'Oacute' => "\xC3\x93",
5260 'oacute' => "\xC3\xB3",
5261 'Oacute;' => "\xC3\x93",
5262 'oacute;' => "\xC3\xB3",
5263 'Ocirc' => "\xC3\x94",
5264 'ocirc' => "\xC3\xB4",
5265 'Ocirc;' => "\xC3\x94",
5266 'ocirc;' => "\xC3\xB4",
5267 'OElig;' => "\xC5\x92",
5268 'oelig;' => "\xC5\x93",
5269 'Ograve' => "\xC3\x92",
5270 'ograve' => "\xC3\xB2",
5271 'Ograve;' => "\xC3\x92",
5272 'ograve;' => "\xC3\xB2",
5273 'oline;' => "\xE2\x80\xBE",
5274 'Omega;' => "\xCE\xA9",
5275 'omega;' => "\xCF\x89",
5276 'Omicron;' => "\xCE\x9F",
5277 'omicron;' => "\xCE\xBF",
5278 'oplus;' => "\xE2\x8A\x95",
5279 'or;' => "\xE2\x88\xA8",
5280 'ordf' => "\xC2\xAA",
5281 'ordf;' => "\xC2\xAA",
5282 'ordm' => "\xC2\xBA",
5283 'ordm;' => "\xC2\xBA",
5284 'Oslash' => "\xC3\x98",
5285 'oslash' => "\xC3\xB8",
5286 'Oslash;' => "\xC3\x98",
5287 'oslash;' => "\xC3\xB8",
5288 'Otilde' => "\xC3\x95",
5289 'otilde' => "\xC3\xB5",
5290 'Otilde;' => "\xC3\x95",
5291 'otilde;' => "\xC3\xB5",
5292 'otimes;' => "\xE2\x8A\x97",
5293 'Ouml' => "\xC3\x96",
5294 'ouml' => "\xC3\xB6",
5295 'Ouml;' => "\xC3\x96",
5296 'ouml;' => "\xC3\xB6",
5297 'para' => "\xC2\xB6",
5298 'para;' => "\xC2\xB6",
5299 'part;' => "\xE2\x88\x82",
5300 'permil;' => "\xE2\x80\xB0",
5301 'perp;' => "\xE2\x8A\xA5",
5302 'Phi;' => "\xCE\xA6",
5303 'phi;' => "\xCF\x86",
5304 'Pi;' => "\xCE\xA0",
5305 'pi;' => "\xCF\x80",
5306 'piv;' => "\xCF\x96",
5307 'plusmn' => "\xC2\xB1",
5308 'plusmn;' => "\xC2\xB1",
5309 'pound' => "\xC2\xA3",
5310 'pound;' => "\xC2\xA3",
5311 'Prime;' => "\xE2\x80\xB3",
5312 'prime;' => "\xE2\x80\xB2",
5313 'prod;' => "\xE2\x88\x8F",
5314 'prop;' => "\xE2\x88\x9D",
5315 'Psi;' => "\xCE\xA8",
5316 'psi;' => "\xCF\x88",
5317 'QUOT' => "\x22",
5318 'quot' => "\x22",
5319 'QUOT;' => "\x22",
5320 'quot;' => "\x22",
5321 'radic;' => "\xE2\x88\x9A",
5322 'rang;' => "\xE3\x80\x89",
5323 'raquo' => "\xC2\xBB",
5324 'raquo;' => "\xC2\xBB",
5325 'rArr;' => "\xE2\x87\x92",
5326 'rarr;' => "\xE2\x86\x92",
5327 'rceil;' => "\xE2\x8C\x89",
5328 'rdquo;' => "\xE2\x80\x9D",
5329 'real;' => "\xE2\x84\x9C",
5330 'REG' => "\xC2\xAE",
5331 'reg' => "\xC2\xAE",
5332 'REG;' => "\xC2\xAE",
5333 'reg;' => "\xC2\xAE",
5334 'rfloor;' => "\xE2\x8C\x8B",
5335 'Rho;' => "\xCE\xA1",
5336 'rho;' => "\xCF\x81",
5337 'rlm;' => "\xE2\x80\x8F",
5338 'rsaquo;' => "\xE2\x80\xBA",
5339 'rsquo;' => "\xE2\x80\x99",
5340 'sbquo;' => "\xE2\x80\x9A",
5341 'Scaron;' => "\xC5\xA0",
5342 'scaron;' => "\xC5\xA1",
5343 'sdot;' => "\xE2\x8B\x85",
5344 'sect' => "\xC2\xA7",
5345 'sect;' => "\xC2\xA7",
5346 'shy' => "\xC2\xAD",
5347 'shy;' => "\xC2\xAD",
5348 'Sigma;' => "\xCE\xA3",
5349 'sigma;' => "\xCF\x83",
5350 'sigmaf;' => "\xCF\x82",
5351 'sim;' => "\xE2\x88\xBC",
5352 'spades;' => "\xE2\x99\xA0",
5353 'sub;' => "\xE2\x8A\x82",
5354 'sube;' => "\xE2\x8A\x86",
5355 'sum;' => "\xE2\x88\x91",
5356 'sup;' => "\xE2\x8A\x83",
5357 'sup1' => "\xC2\xB9",
5358 'sup1;' => "\xC2\xB9",
5359 'sup2' => "\xC2\xB2",
5360 'sup2;' => "\xC2\xB2",
5361 'sup3' => "\xC2\xB3",
5362 'sup3;' => "\xC2\xB3",
5363 'supe;' => "\xE2\x8A\x87",
5364 'szlig' => "\xC3\x9F",
5365 'szlig;' => "\xC3\x9F",
5366 'Tau;' => "\xCE\xA4",
5367 'tau;' => "\xCF\x84",
5368 'there4;' => "\xE2\x88\xB4",
5369 'Theta;' => "\xCE\x98",
5370 'theta;' => "\xCE\xB8",
5371 'thetasym;' => "\xCF\x91",
5372 'thinsp;' => "\xE2\x80\x89",
5373 'THORN' => "\xC3\x9E",
5374 'thorn' => "\xC3\xBE",
5375 'THORN;' => "\xC3\x9E",
5376 'thorn;' => "\xC3\xBE",
5377 'tilde;' => "\xCB\x9C",
5378 'times' => "\xC3\x97",
5379 'times;' => "\xC3\x97",
5380 'TRADE;' => "\xE2\x84\xA2",
5381 'trade;' => "\xE2\x84\xA2",
5382 'Uacute' => "\xC3\x9A",
5383 'uacute' => "\xC3\xBA",
5384 'Uacute;' => "\xC3\x9A",
5385 'uacute;' => "\xC3\xBA",
5386 'uArr;' => "\xE2\x87\x91",
5387 'uarr;' => "\xE2\x86\x91",
5388 'Ucirc' => "\xC3\x9B",
5389 'ucirc' => "\xC3\xBB",
5390 'Ucirc;' => "\xC3\x9B",
5391 'ucirc;' => "\xC3\xBB",
5392 'Ugrave' => "\xC3\x99",
5393 'ugrave' => "\xC3\xB9",
5394 'Ugrave;' => "\xC3\x99",
5395 'ugrave;' => "\xC3\xB9",
5396 'uml' => "\xC2\xA8",
5397 'uml;' => "\xC2\xA8",
5398 'upsih;' => "\xCF\x92",
5399 'Upsilon;' => "\xCE\xA5",
5400 'upsilon;' => "\xCF\x85",
5401 'Uuml' => "\xC3\x9C",
5402 'uuml' => "\xC3\xBC",
5403 'Uuml;' => "\xC3\x9C",
5404 'uuml;' => "\xC3\xBC",
5405 'weierp;' => "\xE2\x84\x98",
5406 'Xi;' => "\xCE\x9E",
5407 'xi;' => "\xCE\xBE",
5408 'Yacute' => "\xC3\x9D",
5409 'yacute' => "\xC3\xBD",
5410 'Yacute;' => "\xC3\x9D",
5411 'yacute;' => "\xC3\xBD",
5412 'yen' => "\xC2\xA5",
5413 'yen;' => "\xC2\xA5",
5414 'yuml' => "\xC3\xBF",
5415 'Yuml;' => "\xC5\xB8",
5416 'yuml;' => "\xC3\xBF",
5417 'Zeta;' => "\xCE\x96",
5418 'zeta;' => "\xCE\xB6",
5419 'zwj;' => "\xE2\x80\x8D",
5420 'zwnj;' => "\xE2\x80\x8C"
5421 );
5422
5423 for ($i = 0, $match = null; $i < 9 && $this->consume() !== false; $i++)
5424 {
5425 $consumed = substr($this->consumed, 1);
5426 if (isset($entities[$consumed]))
5427 {
5428 $match = $consumed;
5429 }
5430 }
5431
5432 if ($match !== null)
5433 {
5434 $this->data = substr_replace($this->data, $entities[$match], $this->position - strlen($consumed) - 1, strlen($match) + 1);
5435 $this->position += strlen($entities[$match]) - strlen($consumed) - 1;
5436 }
5437 break;
5438 }
5439 }
5440 }
5441
5442 /**
5443 * Handles everything related to enclosures (including Media RSS and iTunes RSS)
5444 *
5445 * Used by {@see SimplePie_Item::get_enclosure()} and {@see SimplePie_Item::get_enclosures()}
5446 *
5447 * This class can be overloaded with {@see SimplePie::set_enclosure_class()}
5448 *
5449 * @package SimplePie
5450 * @subpackage API
5451 */
5452 class SimplePie_Enclosure
5453 {
5454 /**
5455 * @var string
5456 * @see get_bitrate()
5457 */
5458 var $bitrate;
5459
5460 /**
5461 * @var array
5462 * @see get_captions()
5463 */
5464 var $captions;
5465
5466 /**
5467 * @var array
5468 * @see get_categories()
5469 */
5470 var $categories;
5471
5472 /**
5473 * @var int
5474 * @see get_channels()
5475 */
5476 var $channels;
5477
5478 /**
5479 * @var SimplePie_Copyright
5480 * @see get_copyright()
5481 */
5482 var $copyright;
5483
5484 /**
5485 * @var array
5486 * @see get_credits()
5487 */
5488 var $credits;
5489
5490 /**
5491 * @var string
5492 * @see get_description()
5493 */
5494 var $description;
5495
5496 /**
5497 * @var int
5498 * @see get_duration()
5499 */
5500 var $duration;
5501
5502 /**
5503 * @var string
5504 * @see get_expression()
5505 */
5506 var $expression;
5507
5508 /**
5509 * @var string
5510 * @see get_framerate()
5511 */
5512 var $framerate;
5513
5514 /**
5515 * @var string
5516 * @see get_handler()
5517 */
5518 var $handler;
5519
5520 /**
5521 * @var array
5522 * @see get_hashes()
5523 */
5524 var $hashes;
5525
5526 /**
5527 * @var string
5528 * @see get_height()
5529 */
5530 var $height;
5531
5532 /**
5533 * @deprecated
5534 * @var null
5535 */
5536 var $javascript;
5537
5538 /**
5539 * @var array
5540 * @see get_keywords()
5541 */
5542 var $keywords;
5543
5544 /**
5545 * @var string
5546 * @see get_language()
5547 */
5548 var $lang;
5549
5550 /**
5551 * @var string
5552 * @see get_length()
5553 */
5554 var $length;
5555
5556 /**
5557 * @var string
5558 * @see get_link()
5559 */
5560 var $link;
5561
5562 /**
5563 * @var string
5564 * @see get_medium()
5565 */
5566 var $medium;
5567
5568 /**
5569 * @var string
5570 * @see get_player()
5571 */
5572 var $player;
5573
5574 /**
5575 * @var array
5576 * @see get_ratings()
5577 */
5578 var $ratings;
5579
5580 /**
5581 * @var array
5582 * @see get_restrictions()
5583 */
5584 var $restrictions;
5585
5586 /**
5587 * @var string
5588 * @see get_sampling_rate()
5589 */
5590 var $samplingrate;
5591
5592 /**
5593 * @var array
5594 * @see get_thumbnails()
5595 */
5596 var $thumbnails;
5597
5598 /**
5599 * @var string
5600 * @see get_title()
5601 */
5602 var $title;
5603
5604 /**
5605 * @var string
5606 * @see get_type()
5607 */
5608 var $type;
5609
5610 /**
5611 * @var string
5612 * @see get_width()
5613 */
5614 var $width;
5615
5616 /**
5617 * Constructor, used to input the data
5618 *
5619 * For documentation on all the parameters, see the corresponding
5620 * properties and their accessors
5621 *
5622 * @uses idna_convert If available, this will convert an IDN
5623 */
5624 public function __construct($link = null, $type = null, $length = null, $javascript = null, $bitrate = null, $captions = null, $categories = null, $channels = null, $copyright = null, $credits = null, $description = null, $duration = null, $expression = null, $framerate = null, $hashes = null, $height = null, $keywords = null, $lang = null, $medium = null, $player = null, $ratings = null, $restrictions = null, $samplingrate = null, $thumbnails = null, $title = null, $width = null)
5625 {
5626 $this->bitrate = $bitrate;
5627 $this->captions = $captions;
5628 $this->categories = $categories;
5629 $this->channels = $channels;
5630 $this->copyright = $copyright;
5631 $this->credits = $credits;
5632 $this->description = $description;
5633 $this->duration = $duration;
5634 $this->expression = $expression;
5635 $this->framerate = $framerate;
5636 $this->hashes = $hashes;
5637 $this->height = $height;
5638 $this->keywords = $keywords;
5639 $this->lang = $lang;
5640 $this->length = $length;
5641 $this->link = $link;
5642 $this->medium = $medium;
5643 $this->player = $player;
5644 $this->ratings = $ratings;
5645 $this->restrictions = $restrictions;
5646 $this->samplingrate = $samplingrate;
5647 $this->thumbnails = $thumbnails;
5648 $this->title = $title;
5649 $this->type = $type;
5650 $this->width = $width;
5651
5652 if (class_exists('idna_convert'))
5653 {
5654 $idn = new idna_convert();
5655 $parsed = SimplePie_Misc::parse_url($link);
5656 $this->link = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']);
5657 }
5658 $this->handler = $this->get_handler(); // Needs to load last
5659 }
5660
5661 /**
5662 * String-ified version
5663 *
5664 * @return string
5665 */
5666 public function __toString()
5667 {
5668 // There is no $this->data here
5669 return md5(serialize($this));
5670 }
5671
5672 /**
5673 * Get the bitrate
5674 *
5675 * @return string|null
5676 */
5677 public function get_bitrate()
5678 {
5679 if ($this->bitrate !== null)
5680 {
5681 return $this->bitrate;
5682 }
5683 else
5684 {
5685 return null;
5686 }
5687 }
5688
5689 /**
5690 * Get a single caption
5691 *
5692 * @param int $key
5693 * @return SimplePie_Caption|null
5694 */
5695 public function get_caption($key = 0)
5696 {
5697 $captions = $this->get_captions();
5698 if (isset($captions[$key]))
5699 {
5700 return $captions[$key];
5701 }
5702 else
5703 {
5704 return null;
5705 }
5706 }
5707
5708 /**
5709 * Get all captions
5710 *
5711 * @return array|null Array of {@see SimplePie_Caption} objects
5712 */
5713 public function get_captions()
5714 {
5715 if ($this->captions !== null)
5716 {
5717 return $this->captions;
5718 }
5719 else
5720 {
5721 return null;
5722 }
5723 }
5724
5725 /**
5726 * Get a single category
5727 *
5728 * @param int $key
5729 * @return SimplePie_Category|null
5730 */
5731 public function get_category($key = 0)
5732 {
5733 $categories = $this->get_categories();
5734 if (isset($categories[$key]))
5735 {
5736 return $categories[$key];
5737 }
5738 else
5739 {
5740 return null;
5741 }
5742 }
5743
5744 /**
5745 * Get all categories
5746 *
5747 * @return array|null Array of {@see SimplePie_Category} objects
5748 */
5749 public function get_categories()
5750 {
5751 if ($this->categories !== null)
5752 {
5753 return $this->categories;
5754 }
5755 else
5756 {
5757 return null;
5758 }
5759 }
5760
5761 /**
5762 * Get the number of audio channels
5763 *
5764 * @return int|null
5765 */
5766 public function get_channels()
5767 {
5768 if ($this->channels !== null)
5769 {
5770 return $this->channels;
5771 }
5772 else
5773 {
5774 return null;
5775 }
5776 }
5777
5778 /**
5779 * Get the copyright information
5780 *
5781 * @return SimplePie_Copyright|null
5782 */
5783 public function get_copyright()
5784 {
5785 if ($this->copyright !== null)
5786 {
5787 return $this->copyright;
5788 }
5789 else
5790 {
5791 return null;
5792 }
5793 }
5794
5795 /**
5796 * Get a single credit
5797 *
5798 * @param int $key
5799 * @return SimplePie_Credit|null
5800 */
5801 public function get_credit($key = 0)
5802 {
5803 $credits = $this->get_credits();
5804 if (isset($credits[$key]))
5805 {
5806 return $credits[$key];
5807 }
5808 else
5809 {
5810 return null;
5811 }
5812 }
5813
5814 /**
5815 * Get all credits
5816 *
5817 * @return array|null Array of {@see SimplePie_Credit} objects
5818 */
5819 public function get_credits()
5820 {
5821 if ($this->credits !== null)
5822 {
5823 return $this->credits;
5824 }
5825 else
5826 {
5827 return null;
5828 }
5829 }
5830
5831 /**
5832 * Get the description of the enclosure
5833 *
5834 * @return string|null
5835 */
5836 public function get_description()
5837 {
5838 if ($this->description !== null)
5839 {
5840 return $this->description;
5841 }
5842 else
5843 {
5844 return null;
5845 }
5846 }
5847
5848 /**
5849 * Get the duration of the enclosure
5850 *
5851 * @param string $convert Convert seconds into hh:mm:ss
5852 * @return string|int|null 'hh:mm:ss' string if `$convert` was specified, otherwise integer (or null if none found)
5853 */
5854 public function get_duration($convert = false)
5855 {
5856 if ($this->duration !== null)
5857 {
5858 if ($convert)
5859 {
5860 $time = SimplePie_Misc::time_hms($this->duration);
5861 return $time;
5862 }
5863 else
5864 {
5865 return $this->duration;
5866 }
5867 }
5868 else
5869 {
5870 return null;
5871 }
5872 }
5873
5874 /**
5875 * Get the expression
5876 *
5877 * @return string Probably one of 'sample', 'full', 'nonstop', 'clip'. Defaults to 'full'
5878 */
5879 public function get_expression()
5880 {
5881 if ($this->expression !== null)
5882 {
5883 return $this->expression;
5884 }
5885 else
5886 {
5887 return 'full';
5888 }
5889 }
5890
5891 /**
5892 * Get the file extension
5893 *
5894 * @return string|null
5895 */
5896 public function get_extension()
5897 {
5898 if ($this->link !== null)
5899 {
5900 $url = SimplePie_Misc::parse_url($this->link);
5901 if ($url['path'] !== '')
5902 {
5903 return pathinfo($url['path'], PATHINFO_EXTENSION);
5904 }
5905 }
5906 return null;
5907 }
5908
5909 /**
5910 * Get the framerate (in frames-per-second)
5911 *
5912 * @return string|null
5913 */
5914 public function get_framerate()
5915 {
5916 if ($this->framerate !== null)
5917 {
5918 return $this->framerate;
5919 }
5920 else
5921 {
5922 return null;
5923 }
5924 }
5925
5926 /**
5927 * Get the preferred handler
5928 *
5929 * @return string|null One of 'flash', 'fmedia', 'quicktime', 'wmedia', 'mp3'
5930 */
5931 public function get_handler()
5932 {
5933 return $this->get_real_type(true);
5934 }
5935
5936 /**
5937 * Get a single hash
5938 *
5939 * @link http://www.rssboard.org/media-rss#media-hash
5940 * @param int $key
5941 * @return string|null Hash as per `media:hash`, prefixed with "$algo:"
5942 */
5943 public function get_hash($key = 0)
5944 {
5945 $hashes = $this->get_hashes();
5946 if (isset($hashes[$key]))
5947 {
5948 return $hashes[$key];
5949 }
5950 else
5951 {
5952 return null;
5953 }
5954 }
5955
5956 /**
5957 * Get all credits
5958 *
5959 * @return array|null Array of strings, see {@see get_hash()}
5960 */
5961 public function get_hashes()
5962 {
5963 if ($this->hashes !== null)
5964 {
5965 return $this->hashes;
5966 }
5967 else
5968 {
5969 return null;
5970 }
5971 }
5972
5973 /**
5974 * Get the height
5975 *
5976 * @return string|null
5977 */
5978 public function get_height()
5979 {
5980 if ($this->height !== null)
5981 {
5982 return $this->height;
5983 }
5984 else
5985 {
5986 return null;
5987 }
5988 }
5989
5990 /**
5991 * Get the language
5992 *
5993 * @link http://tools.ietf.org/html/rfc3066
5994 * @return string|null Language code as per RFC 3066
5995 */
5996 public function get_language()
5997 {
5998 if ($this->lang !== null)
5999 {
6000 return $this->lang;
6001 }
6002 else
6003 {
6004 return null;
6005 }
6006 }
6007
6008 /**
6009 * Get a single keyword
6010 *
6011 * @param int $key
6012 * @return string|null
6013 */
6014 public function get_keyword($key = 0)
6015 {
6016 $keywords = $this->get_keywords();
6017 if (isset($keywords[$key]))
6018 {
6019 return $keywords[$key];
6020 }
6021 else
6022 {
6023 return null;
6024 }
6025 }
6026
6027 /**
6028 * Get all keywords
6029 *
6030 * @return array|null Array of strings
6031 */
6032 public function get_keywords()
6033 {
6034 if ($this->keywords !== null)
6035 {
6036 return $this->keywords;
6037 }
6038 else
6039 {
6040 return null;
6041 }
6042 }
6043
6044 /**
6045 * Get length
6046 *
6047 * @return float Length in bytes
6048 */
6049 public function get_length()
6050 {
6051 if ($this->length !== null)
6052 {
6053 return $this->length;
6054 }
6055 else
6056 {
6057 return null;
6058 }
6059 }
6060
6061 /**
6062 * Get the URL
6063 *
6064 * @return string|null
6065 */
6066 public function get_link()
6067 {
6068 if ($this->link !== null)
6069 {
6070 return urldecode($this->link);
6071 }
6072 else
6073 {
6074 return null;
6075 }
6076 }
6077
6078 /**
6079 * Get the medium
6080 *
6081 * @link http://www.rssboard.org/media-rss#media-content
6082 * @return string|null Should be one of 'image', 'audio', 'video', 'document', 'executable'
6083 */
6084 public function get_medium()
6085 {
6086 if ($this->medium !== null)
6087 {
6088 return $this->medium;
6089 }
6090 else
6091 {
6092 return null;
6093 }
6094 }
6095
6096 /**
6097 * Get the player URL
6098 *
6099 * Typically the same as {@see get_permalink()}
6100 * @return string|null Player URL
6101 */
6102 public function get_player()
6103 {
6104 if ($this->player !== null)
6105 {
6106 return $this->player;
6107 }
6108 else
6109 {
6110 return null;
6111 }
6112 }
6113
6114 /**
6115 * Get a single rating
6116 *
6117 * @param int $key
6118 * @return SimplePie_Rating|null
6119 */
6120 public function get_rating($key = 0)
6121 {
6122 $ratings = $this->get_ratings();
6123 if (isset($ratings[$key]))
6124 {
6125 return $ratings[$key];
6126 }
6127 else
6128 {
6129 return null;
6130 }
6131 }
6132
6133 /**
6134 * Get all ratings
6135 *
6136 * @return array|null Array of {@see SimplePie_Rating} objects
6137 */
6138 public function get_ratings()
6139 {
6140 if ($this->ratings !== null)
6141 {
6142 return $this->ratings;
6143 }
6144 else
6145 {
6146 return null;
6147 }
6148 }
6149
6150 /**
6151 * Get a single restriction
6152 *
6153 * @param int $key
6154 * @return SimplePie_Restriction|null
6155 */
6156 public function get_restriction($key = 0)
6157 {
6158 $restrictions = $this->get_restrictions();
6159 if (isset($restrictions[$key]))
6160 {
6161 return $restrictions[$key];
6162 }
6163 else
6164 {
6165 return null;
6166 }
6167 }
6168
6169 /**
6170 * Get all restrictions
6171 *
6172 * @return array|null Array of {@see SimplePie_Restriction} objects
6173 */
6174 public function get_restrictions()
6175 {
6176 if ($this->restrictions !== null)
6177 {
6178 return $this->restrictions;
6179 }
6180 else
6181 {
6182 return null;
6183 }
6184 }
6185
6186 /**
6187 * Get the sampling rate (in kHz)
6188 *
6189 * @return string|null
6190 */
6191 public function get_sampling_rate()
6192 {
6193 if ($this->samplingrate !== null)
6194 {
6195 return $this->samplingrate;
6196 }
6197 else
6198 {
6199 return null;
6200 }
6201 }
6202
6203 /**
6204 * Get the file size (in MiB)
6205 *
6206 * @return float|null File size in mebibytes (1048 bytes)
6207 */
6208 public function get_size()
6209 {
6210 $length = $this->get_length();
6211 if ($length !== null)
6212 {
6213 return round($length/1048576, 2);
6214 }
6215 else
6216 {
6217 return null;
6218 }
6219 }
6220
6221 /**
6222 * Get a single thumbnail
6223 *
6224 * @param int $key
6225 * @return string|null Thumbnail URL
6226 */
6227 public function get_thumbnail($key = 0)
6228 {
6229 $thumbnails = $this->get_thumbnails();
6230 if (isset($thumbnails[$key]))
6231 {
6232 return $thumbnails[$key];
6233 }
6234 else
6235 {
6236 return null;
6237 }
6238 }
6239
6240 /**
6241 * Get all thumbnails
6242 *
6243 * @return array|null Array of thumbnail URLs
6244 */
6245 public function get_thumbnails()
6246 {
6247 if ($this->thumbnails !== null)
6248 {
6249 return $this->thumbnails;
6250 }
6251 else
6252 {
6253 return null;
6254 }
6255 }
6256
6257 /**
6258 * Get the title
6259 *
6260 * @return string|null
6261 */
6262 public function get_title()
6263 {
6264 if ($this->title !== null)
6265 {
6266 return $this->title;
6267 }
6268 else
6269 {
6270 return null;
6271 }
6272 }
6273
6274 /**
6275 * Get mimetype of the enclosure
6276 *
6277 * @see get_real_type()
6278 * @return string|null MIME type
6279 */
6280 public function get_type()
6281 {
6282 if ($this->type !== null)
6283 {
6284 return $this->type;
6285 }
6286 else
6287 {
6288 return null;
6289 }
6290 }
6291
6292 /**
6293 * Get the width
6294 *
6295 * @return string|null
6296 */
6297 public function get_width()
6298 {
6299 if ($this->width !== null)
6300 {
6301 return $this->width;
6302 }
6303 else
6304 {
6305 return null;
6306 }
6307 }
6308
6309 /**
6310 * Embed the enclosure using `<embed>`
6311 *
6312 * @deprecated Use the second parameter to {@see embed} instead
6313 *
6314 * @param array|string $options See first paramter to {@see embed}
6315 * @return string HTML string to output
6316 */
6317 public function native_embed($options='')
6318 {
6319 return $this->embed($options, true);
6320 }
6321
6322 /**
6323 * Embed the enclosure using Javascript
6324 *
6325 * `$options` is an array or comma-separated key:value string, with the
6326 * following properties:
6327 *
6328 * - `alt` (string): Alternate content for when an end-user does not have
6329 * the appropriate handler installed or when a file type is
6330 * unsupported. Can be any text or HTML. Defaults to blank.
6331 * - `altclass` (string): If a file type is unsupported, the end-user will
6332 * see the alt text (above) linked directly to the content. That link
6333 * will have this value as its class name. Defaults to blank.
6334 * - `audio` (string): This is an image that should be used as a
6335 * placeholder for audio files before they're loaded (QuickTime-only).
6336 * Can be any relative or absolute URL. Defaults to blank.
6337 * - `bgcolor` (string): The background color for the media, if not
6338 * already transparent. Defaults to `#ffffff`.
6339 * - `height` (integer): The height of the embedded media. Accepts any
6340 * numeric pixel value (such as `360`) or `auto`. Defaults to `auto`,
6341 * and it is recommended that you use this default.
6342 * - `loop` (boolean): Do you want the media to loop when its done?
6343 * Defaults to `false`.
6344 * - `mediaplayer` (string): The location of the included
6345 * `mediaplayer.swf` file. This allows for the playback of Flash Video
6346 * (`.flv`) files, and is the default handler for non-Odeo MP3's.
6347 * Defaults to blank.
6348 * - `video` (string): This is an image that should be used as a
6349 * placeholder for video files before they're loaded (QuickTime-only).
6350 * Can be any relative or absolute URL. Defaults to blank.
6351 * - `width` (integer): The width of the embedded media. Accepts any
6352 * numeric pixel value (such as `480`) or `auto`. Defaults to `auto`,
6353 * and it is recommended that you use this default.
6354 * - `widescreen` (boolean): Is the enclosure widescreen or standard?
6355 * This applies only to video enclosures, and will automatically resize
6356 * the content appropriately. Defaults to `false`, implying 4:3 mode.
6357 *
6358 * Note: Non-widescreen (4:3) mode with `width` and `height` set to `auto`
6359 * will default to 480x360 video resolution. Widescreen (16:9) mode with
6360 * `width` and `height` set to `auto` will default to 480x270 video resolution.
6361 *
6362 * @todo If the dimensions for media:content are defined, use them when width/height are set to 'auto'.
6363 * @param array|string $options Comma-separated key:value list, or array
6364 * @param bool $native Use `<embed>`
6365 * @return string HTML string to output
6366 */
6367 public function embed($options = '', $native = false)
6368 {
6369 // Set up defaults
6370 $audio = '';
6371 $video = '';
6372 $alt = '';
6373 $altclass = '';
6374 $loop = 'false';
6375 $width = 'auto';
6376 $height = 'auto';
6377 $bgcolor = '#ffffff';
6378 $mediaplayer = '';
6379 $widescreen = false;
6380 $handler = $this->get_handler();
6381 $type = $this->get_real_type();
6382
6383 // Process options and reassign values as necessary
6384 if (is_array($options))
6385 {
6386 extract($options);
6387 }
6388 else
6389 {
6390 $options = explode(',', $options);
6391 foreach($options as $option)
6392 {
6393 $opt = explode(':', $option, 2);
6394 if (isset($opt[0], $opt[1]))
6395 {
6396 $opt[0] = trim($opt[0]);
6397 $opt[1] = trim($opt[1]);
6398 switch ($opt[0])
6399 {
6400 case 'audio':
6401 $audio = $opt[1];
6402 break;
6403
6404 case 'video':
6405 $video = $opt[1];
6406 break;
6407
6408 case 'alt':
6409 $alt = $opt[1];
6410 break;
6411
6412 case 'altclass':
6413 $altclass = $opt[1];
6414 break;
6415
6416 case 'loop':
6417 $loop = $opt[1];
6418 break;
6419
6420 case 'width':
6421 $width = $opt[1];
6422 break;
6423
6424 case 'height':
6425 $height = $opt[1];
6426 break;
6427
6428 case 'bgcolor':
6429 $bgcolor = $opt[1];
6430 break;
6431
6432 case 'mediaplayer':
6433 $mediaplayer = $opt[1];
6434 break;
6435
6436 case 'widescreen':
6437 $widescreen = $opt[1];
6438 break;
6439 }
6440 }
6441 }
6442 }
6443
6444 $mime = explode('/', $type, 2);
6445 $mime = $mime[0];
6446
6447 // Process values for 'auto'
6448 if ($width === 'auto')
6449 {
6450 if ($mime === 'video')
6451 {
6452 if ($height === 'auto')
6453 {
6454 $width = 480;
6455 }
6456 elseif ($widescreen)
6457 {
6458 $width = round((intval($height)/9)*16);
6459 }
6460 else
6461 {
6462 $width = round((intval($height)/3)*4);
6463 }
6464 }
6465 else
6466 {
6467 $width = '100%';
6468 }
6469 }
6470
6471 if ($height === 'auto')
6472 {
6473 if ($mime === 'audio')
6474 {
6475 $height = 0;
6476 }
6477 elseif ($mime === 'video')
6478 {
6479 if ($width === 'auto')
6480 {
6481 if ($widescreen)
6482 {
6483 $height = 270;
6484 }
6485 else
6486 {
6487 $height = 360;
6488 }
6489 }
6490 elseif ($widescreen)
6491 {
6492 $height = round((intval($width)/16)*9);
6493 }
6494 else
6495 {
6496 $height = round((intval($width)/4)*3);
6497 }
6498 }
6499 else
6500 {
6501 $height = 376;
6502 }
6503 }
6504 elseif ($mime === 'audio')
6505 {
6506 $height = 0;
6507 }
6508
6509 // Set proper placeholder value
6510 if ($mime === 'audio')
6511 {
6512 $placeholder = $audio;
6513 }
6514 elseif ($mime === 'video')
6515 {
6516 $placeholder = $video;
6517 }
6518
6519 $embed = '';
6520
6521 // Flash
6522 if ($handler === 'flash')
6523 {
6524 if ($native)
6525 {
6526 $embed .= "<embed src=\"" . $this->get_link() . "\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"$type\" quality=\"high\" width=\"$width\" height=\"$height\" bgcolor=\"$bgcolor\" loop=\"$loop\"></embed>";
6527 }
6528 else
6529 {
6530 $embed .= "<script type='text/javascript'>embed_flash('$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$loop', '$type');</script>";
6531 }
6532 }
6533
6534 // Flash Media Player file types.
6535 // Preferred handler for MP3 file types.
6536 elseif ($handler === 'fmedia' || ($handler === 'mp3' && $mediaplayer !== ''))
6537 {
6538 $height += 20;
6539 if ($native)
6540 {
6541 $embed .= "<embed src=\"$mediaplayer\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"application/x-shockwave-flash\" quality=\"high\" width=\"$width\" height=\"$height\" wmode=\"transparent\" flashvars=\"file=" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "&autostart=false&repeat=$loop&showdigits=true&showfsbutton=false\"></embed>";
6542 }
6543 else
6544 {
6545 $embed .= "<script type='text/javascript'>embed_flv('$width', '$height', '" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "', '$placeholder', '$loop', '$mediaplayer');</script>";
6546 }
6547 }
6548
6549 // QuickTime 7 file types. Need to test with QuickTime 6.
6550 // Only handle MP3's if the Flash Media Player is not present.
6551 elseif ($handler === 'quicktime' || ($handler === 'mp3' && $mediaplayer === ''))
6552 {
6553 $height += 16;
6554 if ($native)
6555 {
6556 if ($placeholder !== '')
6557 {
6558 $embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" href=\"" . $this->get_link() . "\" src=\"$placeholder\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"false\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\"></embed>";
6559 }
6560 else
6561 {
6562 $embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" src=\"" . $this->get_link() . "\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"true\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\"></embed>";
6563 }
6564 }
6565 else
6566 {
6567 $embed .= "<script type='text/javascript'>embed_quicktime('$type', '$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$placeholder', '$loop');</script>";
6568 }
6569 }
6570
6571 // Windows Media
6572 elseif ($handler === 'wmedia')
6573 {
6574 $height += 45;
6575 if ($native)
6576 {
6577 $embed .= "<embed type=\"application/x-mplayer2\" src=\"" . $this->get_link() . "\" autosize=\"1\" width=\"$width\" height=\"$height\" showcontrols=\"1\" showstatusbar=\"0\" showdisplay=\"0\" autostart=\"0\"></embed>";
6578 }
6579 else
6580 {
6581 $embed .= "<script type='text/javascript'>embed_wmedia('$width', '$height', '" . $this->get_link() . "');</script>";
6582 }
6583 }
6584
6585 // Everything else
6586 else $embed .= '<a href="' . $this->get_link() . '" class="' . $altclass . '">' . $alt . '</a>';
6587
6588 return $embed;
6589 }
6590
6591 /**
6592 * Get the real media type
6593 *
6594 * Often, feeds lie to us, necessitating a bit of deeper inspection. This
6595 * converts types to their canonical representations based on the file
6596 * extension
6597 *
6598 * @see get_type()
6599 * @param bool $find_handler Internal use only, use {@see get_handler()} instead
6600 * @return string MIME type
6601 */
6602 public function get_real_type($find_handler = false)
6603 {
6604 // Mime-types by handler.
6605 $types_flash = array('application/x-shockwave-flash', 'application/futuresplash'); // Flash
6606 $types_fmedia = array('video/flv', 'video/x-flv','flv-application/octet-stream'); // Flash Media Player
6607 $types_quicktime = array('audio/3gpp', 'audio/3gpp2', 'audio/aac', 'audio/x-aac', 'audio/aiff', 'audio/x-aiff', 'audio/mid', 'audio/midi', 'audio/x-midi', 'audio/mp4', 'audio/m4a', 'audio/x-m4a', 'audio/wav', 'audio/x-wav', 'video/3gpp', 'video/3gpp2', 'video/m4v', 'video/x-m4v', 'video/mp4', 'video/mpeg', 'video/x-mpeg', 'video/quicktime', 'video/sd-video'); // QuickTime
6608 $types_wmedia = array('application/asx', 'application/x-mplayer2', 'audio/x-ms-wma', 'audio/x-ms-wax', 'video/x-ms-asf-plugin', 'video/x-ms-asf', 'video/x-ms-wm', 'video/x-ms-wmv', 'video/x-ms-wvx'); // Windows Media
6609 $types_mp3 = array('audio/mp3', 'audio/x-mp3', 'audio/mpeg', 'audio/x-mpeg'); // MP3
6610
6611 if ($this->get_type() !== null)
6612 {
6613 $type = strtolower($this->type);
6614 }
6615 else
6616 {
6617 $type = null;
6618 }
6619
6620 // If we encounter an unsupported mime-type, check the file extension and guess intelligently.
6621 if (!in_array($type, array_merge($types_flash, $types_fmedia, $types_quicktime, $types_wmedia, $types_mp3)))
6622 {
6623 switch (strtolower($this->get_extension()))
6624 {
6625 // Audio mime-types
6626 case 'aac':
6627 case 'adts':
6628 $type = 'audio/acc';
6629 break;
6630
6631 case 'aif':
6632 case 'aifc':
6633 case 'aiff':
6634 case 'cdda':
6635 $type = 'audio/aiff';
6636 break;
6637
6638 case 'bwf':
6639 $type = 'audio/wav';
6640 break;
6641
6642 case 'kar':
6643 case 'mid':
6644 case 'midi':
6645 case 'smf':
6646 $type = 'audio/midi';
6647 break;
6648
6649 case 'm4a':
6650 $type = 'audio/x-m4a';
6651 break;
6652
6653 case 'mp3':
6654 case 'swa':
6655 $type = 'audio/mp3';
6656 break;
6657
6658 case 'wav':
6659 $type = 'audio/wav';
6660 break;
6661
6662 case 'wax':
6663 $type = 'audio/x-ms-wax';
6664 break;
6665
6666 case 'wma':
6667 $type = 'audio/x-ms-wma';
6668 break;
6669
6670 // Video mime-types
6671 case '3gp':
6672 case '3gpp':
6673 $type = 'video/3gpp';
6674 break;
6675
6676 case '3g2':
6677 case '3gp2':
6678 $type = 'video/3gpp2';
6679 break;
6680
6681 case 'asf':
6682 $type = 'video/x-ms-asf';
6683 break;
6684
6685 case 'flv':
6686 $type = 'video/x-flv';
6687 break;
6688
6689 case 'm1a':
6690 case 'm1s':
6691 case 'm1v':
6692 case 'm15':
6693 case 'm75':
6694 case 'mp2':
6695 case 'mpa':
6696 case 'mpeg':
6697 case 'mpg':
6698 case 'mpm':
6699 case 'mpv':
6700 $type = 'video/mpeg';
6701 break;
6702
6703 case 'm4v':
6704 $type = 'video/x-m4v';
6705 break;
6706
6707 case 'mov':
6708 case 'qt':
6709 $type = 'video/quicktime';
6710 break;
6711
6712 case 'mp4':
6713 case 'mpg4':
6714 $type = 'video/mp4';
6715 break;
6716
6717 case 'sdv':
6718 $type = 'video/sd-video';
6719 break;
6720
6721 case 'wm':
6722 $type = 'video/x-ms-wm';
6723 break;
6724
6725 case 'wmv':
6726 $type = 'video/x-ms-wmv';
6727 break;
6728
6729 case 'wvx':
6730 $type = 'video/x-ms-wvx';
6731 break;
6732
6733 // Flash mime-types
6734 case 'spl':
6735 $type = 'application/futuresplash';
6736 break;
6737
6738 case 'swf':
6739 $type = 'application/x-shockwave-flash';
6740 break;
6741 }
6742 }
6743
6744 if ($find_handler)
6745 {
6746 if (in_array($type, $types_flash))
6747 {
6748 return 'flash';
6749 }
6750 elseif (in_array($type, $types_fmedia))
6751 {
6752 return 'fmedia';
6753 }
6754 elseif (in_array($type, $types_quicktime))
6755 {
6756 return 'quicktime';
6757 }
6758 elseif (in_array($type, $types_wmedia))
6759 {
6760 return 'wmedia';
6761 }
6762 elseif (in_array($type, $types_mp3))
6763 {
6764 return 'mp3';
6765 }
6766 else
6767 {
6768 return null;
6769 }
6770 }
6771 else
6772 {
6773 return $type;
6774 }
6775 }
6776 }
6777
6778 /**
6779 * General SimplePie exception class
6780 *
6781 * @package SimplePie
6782 */
6783 class SimplePie_Exception extends Exception
6784 {
6785 }
6786
6787 /**
6788 * Used for fetching remote files and reading local files
6789 *
6790 * Supports HTTP 1.0 via cURL or fsockopen, with spotty HTTP 1.1 support
6791 *
6792 * This class can be overloaded with {@see SimplePie::set_file_class()}
6793 *
6794 * @package SimplePie
6795 * @subpackage HTTP
6796 * @todo Move to properly supporting RFC2616 (HTTP/1.1)
6797 */
6798 class SimplePie_File
6799 {
6800 var $url;
6801 var $useragent;
6802 var $success = true;
6803 var $headers = array();
6804 var $body;
6805 var $status_code;
6806 var $redirects = 0;
6807 var $error;
6808 var $method = SIMPLEPIE_FILE_SOURCE_NONE;
6809
6810 public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false)
6811 {
6812 if (class_exists('idna_convert'))
6813 {
6814 $idn = new idna_convert();
6815 $parsed = SimplePie_Misc::parse_url($url);
6816 $url = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']);
6817 }
6818 $this->url = $url;
6819 $this->useragent = $useragent;
6820 if (preg_match('/^http(s)?:\/\//i', $url))
6821 {
6822 if ($useragent === null)
6823 {
6824 $useragent = ini_get('user_agent');
6825 $this->useragent = $useragent;
6826 }
6827 if (!is_array($headers))
6828 {
6829 $headers = array();
6830 }
6831 if (!$force_fsockopen && function_exists('curl_exec'))
6832 {
6833 $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_CURL;
6834 $fp = curl_init();
6835 $headers2 = array();
6836 foreach ($headers as $key => $value)
6837 {
6838 $headers2[] = "$key: $value";
6839 }
6840 if (version_compare(SimplePie_Misc::get_curl_version(), '7.10.5', '>='))
6841 {
6842 curl_setopt($fp, CURLOPT_ENCODING, '');
6843 }
6844 curl_setopt($fp, CURLOPT_URL, $url);
6845 curl_setopt($fp, CURLOPT_HEADER, 1);
6846 curl_setopt($fp, CURLOPT_RETURNTRANSFER, 1);
6847 curl_setopt($fp, CURLOPT_TIMEOUT, $timeout);
6848 curl_setopt($fp, CURLOPT_CONNECTTIMEOUT, $timeout);
6849 curl_setopt($fp, CURLOPT_REFERER, $url);
6850 curl_setopt($fp, CURLOPT_USERAGENT, $useragent);
6851 curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2);
6852 if (!ini_get('open_basedir') && !ini_get('safe_mode') && version_compare(SimplePie_Misc::get_curl_version(), '7.15.2', '>='))
6853 {
6854 curl_setopt($fp, CURLOPT_FOLLOWLOCATION, 1);
6855 curl_setopt($fp, CURLOPT_MAXREDIRS, $redirects);
6856 }
6857
6858 /* Enable Digest authentication and SSL -fox */
6859 curl_setopt($fp, CURLOPT_SSL_VERIFYPEER, false);
6860 curl_setopt($fp, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
6861
6862 $this->headers = curl_exec($fp);
6863 if (curl_errno($fp) === 23 || curl_errno($fp) === 61)
6864 {
6865 curl_setopt($fp, CURLOPT_ENCODING, 'none');
6866 $this->headers = curl_exec($fp);
6867 }
6868 if (curl_errno($fp))
6869 {
6870 $this->error = 'cURL error ' . curl_errno($fp) . ': ' . curl_error($fp);
6871 $this->success = false;
6872 }
6873 else
6874 {
6875 $info = curl_getinfo($fp);
6876 curl_close($fp);
6877 $this->headers = explode("\r\n\r\n", $this->headers, $info['redirect_count'] + 1);
6878 $this->headers = array_pop($this->headers);
6879 $parser = new SimplePie_HTTP_Parser($this->headers);
6880 if ($parser->parse())
6881 {
6882 $this->headers = $parser->headers;
6883 $this->body = $parser->body;
6884 $this->status_code = $parser->status_code;
6885 if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects)
6886 {
6887 $this->redirects++;
6888 $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url);
6889 return $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen);
6890 }
6891 }
6892 }
6893 }
6894 else
6895 {
6896 $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_FSOCKOPEN;
6897 $url_parts = parse_url($url);
6898 $socket_host = $url_parts['host'];
6899 if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https')
6900 {
6901 $socket_host = "ssl://$url_parts[host]";
6902 $url_parts['port'] = 443;
6903 }
6904 if (!isset($url_parts['port']))
6905 {
6906 $url_parts['port'] = 80;
6907 }
6908 $fp = @fsockopen($socket_host, $url_parts['port'], $errno, $errstr, $timeout);
6909 if (!$fp)
6910 {
6911 $this->error = 'fsockopen error: ' . $errstr;
6912 $this->success = false;
6913 }
6914 else
6915 {
6916 stream_set_timeout($fp, $timeout);
6917 if (isset($url_parts['path']))
6918 {
6919 if (isset($url_parts['query']))
6920 {
6921 $get = "$url_parts[path]?$url_parts[query]";
6922 }
6923 else
6924 {
6925 $get = $url_parts['path'];
6926 }
6927 }
6928 else
6929 {
6930 $get = '/';
6931 }
6932 $out = "GET $get HTTP/1.1\r\n";
6933 $out .= "Host: $url_parts[host]\r\n";
6934 $out .= "User-Agent: $useragent\r\n";
6935 if (extension_loaded('zlib'))
6936 {
6937 $out .= "Accept-Encoding: x-gzip,gzip,deflate\r\n";
6938 }
6939
6940 if (isset($url_parts['user']) && isset($url_parts['pass']))
6941 {
6942 $out .= "Authorization: Basic " . base64_encode("$url_parts[user]:$url_parts[pass]") . "\r\n";
6943 }
6944 foreach ($headers as $key => $value)
6945 {
6946 $out .= "$key: $value\r\n";
6947 }
6948 $out .= "Connection: Close\r\n\r\n";
6949 fwrite($fp, $out);
6950
6951 $info = stream_get_meta_data($fp);
6952
6953 $this->headers = '';
6954 while (!$info['eof'] && !$info['timed_out'])
6955 {
6956 $this->headers .= fread($fp, 1160);
6957 $info = stream_get_meta_data($fp);
6958 }
6959 if (!$info['timed_out'])
6960 {
6961 $parser = new SimplePie_HTTP_Parser($this->headers);
6962 if ($parser->parse())
6963 {
6964 $this->headers = $parser->headers;
6965 $this->body = $parser->body;
6966 $this->status_code = $parser->status_code;
6967 if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects)
6968 {
6969 $this->redirects++;
6970 $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url);
6971 return $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen);
6972 }
6973 if (isset($this->headers['content-encoding']))
6974 {
6975 // Hey, we act dumb elsewhere, so let's do that here too
6976 switch (strtolower(trim($this->headers['content-encoding'], "\x09\x0A\x0D\x20")))
6977 {
6978 case 'gzip':
6979 case 'x-gzip':
6980 $decoder = new SimplePie_gzdecode($this->body);
6981 if (!$decoder->parse())
6982 {
6983 $this->error = 'Unable to decode HTTP "gzip" stream';
6984 $this->success = false;
6985 }
6986 else
6987 {
6988 $this->body = $decoder->data;
6989 }
6990 break;
6991
6992 case 'deflate':
6993 if (($decompressed = gzinflate($this->body)) !== false)
6994 {
6995 $this->body = $decompressed;
6996 }
6997 else if (($decompressed = gzuncompress($this->body)) !== false)
6998 {
6999 $this->body = $decompressed;
7000 }
7001 else if (function_exists('gzdecode') && ($decompressed = gzdecode($this->body)) !== false)
7002 {
7003 $this->body = $decompressed;
7004 }
7005 else
7006 {
7007 $this->error = 'Unable to decode HTTP "deflate" stream';
7008 $this->success = false;
7009 }
7010 break;
7011
7012 default:
7013 $this->error = 'Unknown content coding';
7014 $this->success = false;
7015 }
7016 }
7017 }
7018 }
7019 else
7020 {
7021 $this->error = 'fsocket timed out';
7022 $this->success = false;
7023 }
7024 fclose($fp);
7025 }
7026 }
7027 }
7028 else
7029 {
7030 $this->method = SIMPLEPIE_FILE_SOURCE_LOCAL | SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS;
7031 if (!$this->body = file_get_contents($url))
7032 {
7033 $this->error = 'file_get_contents could not read the file';
7034 $this->success = false;
7035 }
7036 }
7037 }
7038 }
7039
7040 /**
7041 * Decode 'gzip' encoded HTTP data
7042 *
7043 * @package SimplePie
7044 * @subpackage HTTP
7045 * @link http://www.gzip.org/format.txt
7046 */
7047 class SimplePie_gzdecode
7048 {
7049 /**
7050 * Compressed data
7051 *
7052 * @access private
7053 * @var string
7054 * @see gzdecode::$data
7055 */
7056 var $compressed_data;
7057
7058 /**
7059 * Size of compressed data
7060 *
7061 * @access private
7062 * @var int
7063 */
7064 var $compressed_size;
7065
7066 /**
7067 * Minimum size of a valid gzip string
7068 *
7069 * @access private
7070 * @var int
7071 */
7072 var $min_compressed_size = 18;
7073
7074 /**
7075 * Current position of pointer
7076 *
7077 * @access private
7078 * @var int
7079 */
7080 var $position = 0;
7081
7082 /**
7083 * Flags (FLG)
7084 *
7085 * @access private
7086 * @var int
7087 */
7088 var $flags;
7089
7090 /**
7091 * Uncompressed data
7092 *
7093 * @access public
7094 * @see gzdecode::$compressed_data
7095 * @var string
7096 */
7097 var $data;
7098
7099 /**
7100 * Modified time
7101 *
7102 * @access public
7103 * @var int
7104 */
7105 var $MTIME;
7106
7107 /**
7108 * Extra Flags
7109 *
7110 * @access public
7111 * @var int
7112 */
7113 var $XFL;
7114
7115 /**
7116 * Operating System
7117 *
7118 * @access public
7119 * @var int
7120 */
7121 var $OS;
7122
7123 /**
7124 * Subfield ID 1
7125 *
7126 * @access public
7127 * @see gzdecode::$extra_field
7128 * @see gzdecode::$SI2
7129 * @var string
7130 */
7131 var $SI1;
7132
7133 /**
7134 * Subfield ID 2
7135 *
7136 * @access public
7137 * @see gzdecode::$extra_field
7138 * @see gzdecode::$SI1
7139 * @var string
7140 */
7141 var $SI2;
7142
7143 /**
7144 * Extra field content
7145 *
7146 * @access public
7147 * @see gzdecode::$SI1
7148 * @see gzdecode::$SI2
7149 * @var string
7150 */
7151 var $extra_field;
7152
7153 /**
7154 * Original filename
7155 *
7156 * @access public
7157 * @var string
7158 */
7159 var $filename;
7160
7161 /**
7162 * Human readable comment
7163 *
7164 * @access public
7165 * @var string
7166 */
7167 var $comment;
7168
7169 /**
7170 * Don't allow anything to be set
7171 *
7172 * @param string $name
7173 * @param mixed $value
7174 */
7175 public function __set($name, $value)
7176 {
7177 trigger_error("Cannot write property $name", E_USER_ERROR);
7178 }
7179
7180 /**
7181 * Set the compressed string and related properties
7182 *
7183 * @param string $data
7184 */
7185 public function __construct($data)
7186 {
7187 $this->compressed_data = $data;
7188 $this->compressed_size = strlen($data);
7189 }
7190
7191 /**
7192 * Decode the GZIP stream
7193 *
7194 * @return bool Successfulness
7195 */
7196 public function parse()
7197 {
7198 if ($this->compressed_size >= $this->min_compressed_size)
7199 {
7200 // Check ID1, ID2, and CM
7201 if (substr($this->compressed_data, 0, 3) !== "\x1F\x8B\x08")
7202 {
7203 return false;
7204 }
7205
7206 // Get the FLG (FLaGs)
7207 $this->flags = ord($this->compressed_data[3]);
7208
7209 // FLG bits above (1 << 4) are reserved
7210 if ($this->flags > 0x1F)
7211 {
7212 return false;
7213 }
7214
7215 // Advance the pointer after the above
7216 $this->position += 4;
7217
7218 // MTIME
7219 $mtime = substr($this->compressed_data, $this->position, 4);
7220 // Reverse the string if we're on a big-endian arch because l is the only signed long and is machine endianness
7221 if (current(unpack('S', "\x00\x01")) === 1)
7222 {
7223 $mtime = strrev($mtime);
7224 }
7225 $this->MTIME = current(unpack('l', $mtime));
7226 $this->position += 4;
7227
7228 // Get the XFL (eXtra FLags)
7229 $this->XFL = ord($this->compressed_data[$this->position++]);
7230
7231 // Get the OS (Operating System)
7232 $this->OS = ord($this->compressed_data[$this->position++]);
7233
7234 // Parse the FEXTRA
7235 if ($this->flags & 4)
7236 {
7237 // Read subfield IDs
7238 $this->SI1 = $this->compressed_data[$this->position++];
7239 $this->SI2 = $this->compressed_data[$this->position++];
7240
7241 // SI2 set to zero is reserved for future use
7242 if ($this->SI2 === "\x00")
7243 {
7244 return false;
7245 }
7246
7247 // Get the length of the extra field
7248 $len = current(unpack('v', substr($this->compressed_data, $this->position, 2)));
7249 $this->position += 2;
7250
7251 // Check the length of the string is still valid
7252 $this->min_compressed_size += $len + 4;
7253 if ($this->compressed_size >= $this->min_compressed_size)
7254 {
7255 // Set the extra field to the given data
7256 $this->extra_field = substr($this->compressed_data, $this->position, $len);
7257 $this->position += $len;
7258 }
7259 else
7260 {
7261 return false;
7262 }
7263 }
7264
7265 // Parse the FNAME
7266 if ($this->flags & 8)
7267 {
7268 // Get the length of the filename
7269 $len = strcspn($this->compressed_data, "\x00", $this->position);
7270
7271 // Check the length of the string is still valid
7272 $this->min_compressed_size += $len + 1;
7273 if ($this->compressed_size >= $this->min_compressed_size)
7274 {
7275 // Set the original filename to the given string
7276 $this->filename = substr($this->compressed_data, $this->position, $len);
7277 $this->position += $len + 1;
7278 }
7279 else
7280 {
7281 return false;
7282 }
7283 }
7284
7285 // Parse the FCOMMENT
7286 if ($this->flags & 16)
7287 {
7288 // Get the length of the comment
7289 $len = strcspn($this->compressed_data, "\x00", $this->position);
7290
7291 // Check the length of the string is still valid
7292 $this->min_compressed_size += $len + 1;
7293 if ($this->compressed_size >= $this->min_compressed_size)
7294 {
7295 // Set the original comment to the given string
7296 $this->comment = substr($this->compressed_data, $this->position, $len);
7297 $this->position += $len + 1;
7298 }
7299 else
7300 {
7301 return false;
7302 }
7303 }
7304
7305 // Parse the FHCRC
7306 if ($this->flags & 2)
7307 {
7308 // Check the length of the string is still valid
7309 $this->min_compressed_size += $len + 2;
7310 if ($this->compressed_size >= $this->min_compressed_size)
7311 {
7312 // Read the CRC
7313 $crc = current(unpack('v', substr($this->compressed_data, $this->position, 2)));
7314
7315 // Check the CRC matches
7316 if ((crc32(substr($this->compressed_data, 0, $this->position)) & 0xFFFF) === $crc)
7317 {
7318 $this->position += 2;
7319 }
7320 else
7321 {
7322 return false;
7323 }
7324 }
7325 else
7326 {
7327 return false;
7328 }
7329 }
7330
7331 // Decompress the actual data
7332 if (($this->data = gzinflate(substr($this->compressed_data, $this->position, -8))) === false)
7333 {
7334 return false;
7335 }
7336 else
7337 {
7338 $this->position = $this->compressed_size - 8;
7339 }
7340
7341 // Check CRC of data
7342 $crc = current(unpack('V', substr($this->compressed_data, $this->position, 4)));
7343 $this->position += 4;
7344 /*if (extension_loaded('hash') && sprintf('%u', current(unpack('V', hash('crc32b', $this->data)))) !== sprintf('%u', $crc))
7345 {
7346 return false;
7347 }*/
7348
7349 // Check ISIZE of data
7350 $isize = current(unpack('V', substr($this->compressed_data, $this->position, 4)));
7351 $this->position += 4;
7352 if (sprintf('%u', strlen($this->data) & 0xFFFFFFFF) !== sprintf('%u', $isize))
7353 {
7354 return false;
7355 }
7356
7357 // Wow, against all odds, we've actually got a valid gzip string
7358 return true;
7359 }
7360 else
7361 {
7362 return false;
7363 }
7364 }
7365 }
7366
7367 /**
7368 * HTTP Response Parser
7369 *
7370 * @package SimplePie
7371 * @subpackage HTTP
7372 */
7373 class SimplePie_HTTP_Parser
7374 {
7375 /**
7376 * HTTP Version
7377 *
7378 * @var float
7379 */
7380 public $http_version = 0.0;
7381
7382 /**
7383 * Status code
7384 *
7385 * @var int
7386 */
7387 public $status_code = 0;
7388
7389 /**
7390 * Reason phrase
7391 *
7392 * @var string
7393 */
7394 public $reason = '';
7395
7396 /**
7397 * Key/value pairs of the headers
7398 *
7399 * @var array
7400 */
7401 public $headers = array();
7402
7403 /**
7404 * Body of the response
7405 *
7406 * @var string
7407 */
7408 public $body = '';
7409
7410 /**
7411 * Current state of the state machine
7412 *
7413 * @var string
7414 */
7415 protected $state = 'http_version';
7416
7417 /**
7418 * Input data
7419 *
7420 * @var string
7421 */
7422 protected $data = '';
7423
7424 /**
7425 * Input data length (to avoid calling strlen() everytime this is needed)
7426 *
7427 * @var int
7428 */
7429 protected $data_length = 0;
7430
7431 /**
7432 * Current position of the pointer
7433 *
7434 * @var int
7435 */
7436 protected $position = 0;
7437
7438 /**
7439 * Name of the hedaer currently being parsed
7440 *
7441 * @var string
7442 */
7443 protected $name = '';
7444
7445 /**
7446 * Value of the hedaer currently being parsed
7447 *
7448 * @var string
7449 */
7450 protected $value = '';
7451
7452 /**
7453 * Create an instance of the class with the input data
7454 *
7455 * @param string $data Input data
7456 */
7457 public function __construct($data)
7458 {
7459 $this->data = $data;
7460 $this->data_length = strlen($this->data);
7461 }
7462
7463 /**
7464 * Parse the input data
7465 *
7466 * @return bool true on success, false on failure
7467 */
7468 public function parse()
7469 {
7470 while ($this->state && $this->state !== 'emit' && $this->has_data())
7471 {
7472 $state = $this->state;
7473 $this->$state();
7474 }
7475 $this->data = '';
7476 if ($this->state === 'emit' || $this->state === 'body')
7477 {
7478 return true;
7479 }
7480 else
7481 {
7482 $this->http_version = '';
7483 $this->status_code = '';
7484 $this->reason = '';
7485 $this->headers = array();
7486 $this->body = '';
7487 return false;
7488 }
7489 }
7490
7491 /**
7492 * Check whether there is data beyond the pointer
7493 *
7494 * @return bool true if there is further data, false if not
7495 */
7496 protected function has_data()
7497 {
7498 return (bool) ($this->position < $this->data_length);
7499 }
7500
7501 /**
7502 * See if the next character is LWS
7503 *
7504 * @return bool true if the next character is LWS, false if not
7505 */
7506 protected function is_linear_whitespace()
7507 {
7508 return (bool) ($this->data[$this->position] === "\x09"
7509 || $this->data[$this->position] === "\x20"
7510 || ($this->data[$this->position] === "\x0A"
7511 && isset($this->data[$this->position + 1])
7512 && ($this->data[$this->position + 1] === "\x09" || $this->data[$this->position + 1] === "\x20")));
7513 }
7514
7515 /**
7516 * Parse the HTTP version
7517 */
7518 protected function http_version()
7519 {
7520 if (strpos($this->data, "\x0A") !== false && strtoupper(substr($this->data, 0, 5)) === 'HTTP/')
7521 {
7522 $len = strspn($this->data, '0123456789.', 5);
7523 $this->http_version = substr($this->data, 5, $len);
7524 $this->position += 5 + $len;
7525 if (substr_count($this->http_version, '.') <= 1)
7526 {
7527 $this->http_version = (float) $this->http_version;
7528 $this->position += strspn($this->data, "\x09\x20", $this->position);
7529 $this->state = 'status';
7530 }
7531 else
7532 {
7533 $this->state = false;
7534 }
7535 }
7536 else
7537 {
7538 $this->state = false;
7539 }
7540 }
7541
7542 /**
7543 * Parse the status code
7544 */
7545 protected function status()
7546 {
7547 if ($len = strspn($this->data, '0123456789', $this->position))
7548 {
7549 $this->status_code = (int) substr($this->data, $this->position, $len);
7550 $this->position += $len;
7551 $this->state = 'reason';
7552 }
7553 else
7554 {
7555 $this->state = false;
7556 }
7557 }
7558
7559 /**
7560 * Parse the reason phrase
7561 */
7562 protected function reason()
7563 {
7564 $len = strcspn($this->data, "\x0A", $this->position);
7565 $this->reason = trim(substr($this->data, $this->position, $len), "\x09\x0D\x20");
7566 $this->position += $len + 1;
7567 $this->state = 'new_line';
7568 }
7569
7570 /**
7571 * Deal with a new line, shifting data around as needed
7572 */
7573 protected function new_line()
7574 {
7575 $this->value = trim($this->value, "\x0D\x20");
7576 if ($this->name !== '' && $this->value !== '')
7577 {
7578 $this->name = strtolower($this->name);
7579 // We should only use the last Content-Type header. c.f. issue #1
7580 if (isset($this->headers[$this->name]) && $this->name !== 'content-type')
7581 {
7582 $this->headers[$this->name] .= ', ' . $this->value;
7583 }
7584 else
7585 {
7586 $this->headers[$this->name] = $this->value;
7587 }
7588 }
7589 $this->name = '';
7590 $this->value = '';
7591 if (substr($this->data[$this->position], 0, 2) === "\x0D\x0A")
7592 {
7593 $this->position += 2;
7594 $this->state = 'body';
7595 }
7596 elseif ($this->data[$this->position] === "\x0A")
7597 {
7598 $this->position++;
7599 $this->state = 'body';
7600 }
7601 else
7602 {
7603 $this->state = 'name';
7604 }
7605 }
7606
7607 /**
7608 * Parse a header name
7609 */
7610 protected function name()
7611 {
7612 $len = strcspn($this->data, "\x0A:", $this->position);
7613 if (isset($this->data[$this->position + $len]))
7614 {
7615 if ($this->data[$this->position + $len] === "\x0A")
7616 {
7617 $this->position += $len;
7618 $this->state = 'new_line';
7619 }
7620 else
7621 {
7622 $this->name = substr($this->data, $this->position, $len);
7623 $this->position += $len + 1;
7624 $this->state = 'value';
7625 }
7626 }
7627 else
7628 {
7629 $this->state = false;
7630 }
7631 }
7632
7633 /**
7634 * Parse LWS, replacing consecutive LWS characters with a single space
7635 */
7636 protected function linear_whitespace()
7637 {
7638 do
7639 {
7640 if (substr($this->data, $this->position, 2) === "\x0D\x0A")
7641 {
7642 $this->position += 2;
7643 }
7644 elseif ($this->data[$this->position] === "\x0A")
7645 {
7646 $this->position++;
7647 }
7648 $this->position += strspn($this->data, "\x09\x20", $this->position);
7649 } while ($this->has_data() && $this->is_linear_whitespace());
7650 $this->value .= "\x20";
7651 }
7652
7653 /**
7654 * See what state to move to while within non-quoted header values
7655 */
7656 protected function value()
7657 {
7658 if ($this->is_linear_whitespace())
7659 {
7660 $this->linear_whitespace();
7661 }
7662 else
7663 {
7664 switch ($this->data[$this->position])
7665 {
7666 case '"':
7667 // Workaround for ETags: we have to include the quotes as
7668 // part of the tag.
7669 if (strtolower($this->name) === 'etag')
7670 {
7671 $this->value .= '"';
7672 $this->position++;
7673 $this->state = 'value_char';
7674 break;
7675 }
7676 $this->position++;
7677 $this->state = 'quote';
7678 break;
7679
7680 case "\x0A":
7681 $this->position++;
7682 $this->state = 'new_line';
7683 break;
7684
7685 default:
7686 $this->state = 'value_char';
7687 break;
7688 }
7689 }
7690 }
7691
7692 /**
7693 * Parse a header value while outside quotes
7694 */
7695 protected function value_char()
7696 {
7697 $len = strcspn($this->data, "\x09\x20\x0A\"", $this->position);
7698 $this->value .= substr($this->data, $this->position, $len);
7699 $this->position += $len;
7700 $this->state = 'value';
7701 }
7702
7703 /**
7704 * See what state to move to while within quoted header values
7705 */
7706 protected function quote()
7707 {
7708 if ($this->is_linear_whitespace())
7709 {
7710 $this->linear_whitespace();
7711 }
7712 else
7713 {
7714 switch ($this->data[$this->position])
7715 {
7716 case '"':
7717 $this->position++;
7718 $this->state = 'value';
7719 break;
7720
7721 case "\x0A":
7722 $this->position++;
7723 $this->state = 'new_line';
7724 break;
7725
7726 case '\\':
7727 $this->position++;
7728 $this->state = 'quote_escaped';
7729 break;
7730
7731 default:
7732 $this->state = 'quote_char';
7733 break;
7734 }
7735 }
7736 }
7737
7738 /**
7739 * Parse a header value while within quotes
7740 */
7741 protected function quote_char()
7742 {
7743 $len = strcspn($this->data, "\x09\x20\x0A\"\\", $this->position);
7744 $this->value .= substr($this->data, $this->position, $len);
7745 $this->position += $len;
7746 $this->state = 'value';
7747 }
7748
7749 /**
7750 * Parse an escaped character within quotes
7751 */
7752 protected function quote_escaped()
7753 {
7754 $this->value .= $this->data[$this->position];
7755 $this->position++;
7756 $this->state = 'quote';
7757 }
7758
7759 /**
7760 * Parse the body
7761 */
7762 protected function body()
7763 {
7764 $this->body = substr($this->data, $this->position);
7765 if (!empty($this->headers['transfer-encoding']))
7766 {
7767 unset($this->headers['transfer-encoding']);
7768 $this->state = 'chunked';
7769 }
7770 else
7771 {
7772 $this->state = 'emit';
7773 }
7774 }
7775
7776 /**
7777 * Parsed a "Transfer-Encoding: chunked" body
7778 */
7779 protected function chunked()
7780 {
7781 if (!preg_match('/^([0-9a-f]+)[^\r\n]*\r\n/i', trim($this->body)))
7782 {
7783 $this->state = 'emit';
7784 return;
7785 }
7786
7787 $decoded = '';
7788 $encoded = $this->body;
7789
7790 while (true)
7791 {
7792 $is_chunked = (bool) preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', $encoded, $matches );
7793 if (!$is_chunked)
7794 {
7795 // Looks like it's not chunked after all
7796 $this->state = 'emit';
7797 return;
7798 }
7799
7800 $length = hexdec(trim($matches[1]));
7801 if ($length === 0)
7802 {
7803 // Ignore trailer headers
7804 $this->state = 'emit';
7805 $this->body = $decoded;
7806 return;
7807 }
7808
7809 $chunk_length = strlen($matches[0]);
7810 $decoded .= $part = substr($encoded, $chunk_length, $length);
7811 $encoded = substr($encoded, $chunk_length + $length + 2);
7812
7813 if (trim($encoded) === '0' || empty($encoded))
7814 {
7815 $this->state = 'emit';
7816 $this->body = $decoded;
7817 return;
7818 }
7819 }
7820 }
7821 }
7822
7823 /**
7824 * IRI parser/serialiser/normaliser
7825 *
7826 * @package SimplePie
7827 * @subpackage HTTP
7828 * @author Geoffrey Sneddon
7829 * @author Steve Minutillo
7830 * @author Ryan McCue
7831 * @copyright 2007-2012 Geoffrey Sneddon, Steve Minutillo, Ryan McCue
7832 * @license http://www.opensource.org/licenses/bsd-license.php
7833 */
7834 class SimplePie_IRI
7835 {
7836 /**
7837 * Scheme
7838 *
7839 * @var string
7840 */
7841 protected $scheme = null;
7842
7843 /**
7844 * User Information
7845 *
7846 * @var string
7847 */
7848 protected $iuserinfo = null;
7849
7850 /**
7851 * ihost
7852 *
7853 * @var string
7854 */
7855 protected $ihost = null;
7856
7857 /**
7858 * Port
7859 *
7860 * @var string
7861 */
7862 protected $port = null;
7863
7864 /**
7865 * ipath
7866 *
7867 * @var string
7868 */
7869 protected $ipath = '';
7870
7871 /**
7872 * iquery
7873 *
7874 * @var string
7875 */
7876 protected $iquery = null;
7877
7878 /**
7879 * ifragment
7880 *
7881 * @var string
7882 */
7883 protected $ifragment = null;
7884
7885 /**
7886 * Normalization database
7887 *
7888 * Each key is the scheme, each value is an array with each key as the IRI
7889 * part and value as the default value for that part.
7890 */
7891 protected $normalization = array(
7892 'acap' => array(
7893 'port' => 674
7894 ),
7895 'dict' => array(
7896 'port' => 2628
7897 ),
7898 'file' => array(
7899 'ihost' => 'localhost'
7900 ),
7901 'http' => array(
7902 'port' => 80,
7903 'ipath' => '/'
7904 ),
7905 'https' => array(
7906 'port' => 443,
7907 'ipath' => '/'
7908 ),
7909 );
7910
7911 /**
7912 * Return the entire IRI when you try and read the object as a string
7913 *
7914 * @return string
7915 */
7916 public function __toString()
7917 {
7918 return $this->get_iri();
7919 }
7920
7921 /**
7922 * Overload __set() to provide access via properties
7923 *
7924 * @param string $name Property name
7925 * @param mixed $value Property value
7926 */
7927 public function __set($name, $value)
7928 {
7929 if (method_exists($this, 'set_' . $name))
7930 {
7931 call_user_func(array($this, 'set_' . $name), $value);
7932 }
7933 elseif (
7934 $name === 'iauthority'
7935 || $name === 'iuserinfo'
7936 || $name === 'ihost'
7937 || $name === 'ipath'
7938 || $name === 'iquery'
7939 || $name === 'ifragment'
7940 )
7941 {
7942 call_user_func(array($this, 'set_' . substr($name, 1)), $value);
7943 }
7944 }
7945
7946 /**
7947 * Overload __get() to provide access via properties
7948 *
7949 * @param string $name Property name
7950 * @return mixed
7951 */
7952 public function __get($name)
7953 {
7954 // isset() returns false for null, we don't want to do that
7955 // Also why we use array_key_exists below instead of isset()
7956 $props = get_object_vars($this);
7957
7958 if (
7959 $name === 'iri' ||
7960 $name === 'uri' ||
7961 $name === 'iauthority' ||
7962 $name === 'authority'
7963 )
7964 {
7965 $return = $this->{"get_$name"}();
7966 }
7967 elseif (array_key_exists($name, $props))
7968 {
7969 $return = $this->$name;
7970 }
7971 // host -> ihost
7972 elseif (($prop = 'i' . $name) && array_key_exists($prop, $props))
7973 {
7974 $name = $prop;
7975 $return = $this->$prop;
7976 }
7977 // ischeme -> scheme
7978 elseif (($prop = substr($name, 1)) && array_key_exists($prop, $props))
7979 {
7980 $name = $prop;
7981 $return = $this->$prop;
7982 }
7983 else
7984 {
7985 trigger_error('Undefined property: ' . get_class($this) . '::' . $name, E_USER_NOTICE);
7986 $return = null;
7987 }
7988
7989 if ($return === null && isset($this->normalization[$this->scheme][$name]))
7990 {
7991 return $this->normalization[$this->scheme][$name];
7992 }
7993 else
7994 {
7995 return $return;
7996 }
7997 }
7998
7999 /**
8000 * Overload __isset() to provide access via properties
8001 *
8002 * @param string $name Property name
8003 * @return bool
8004 */
8005 public function __isset($name)
8006 {
8007 if (method_exists($this, 'get_' . $name) || isset($this->$name))
8008 {
8009 return true;
8010 }
8011 else
8012 {
8013 return false;
8014 }
8015 }
8016
8017 /**
8018 * Overload __unset() to provide access via properties
8019 *
8020 * @param string $name Property name
8021 */
8022 public function __unset($name)
8023 {
8024 if (method_exists($this, 'set_' . $name))
8025 {
8026 call_user_func(array($this, 'set_' . $name), '');
8027 }
8028 }
8029
8030 /**
8031 * Create a new IRI object, from a specified string
8032 *
8033 * @param string $iri
8034 */
8035 public function __construct($iri = null)
8036 {
8037 $this->set_iri($iri);
8038 }
8039
8040 /**
8041 * Create a new IRI object by resolving a relative IRI
8042 *
8043 * Returns false if $base is not absolute, otherwise an IRI.
8044 *
8045 * @param IRI|string $base (Absolute) Base IRI
8046 * @param IRI|string $relative Relative IRI
8047 * @return IRI|false
8048 */
8049 public static function absolutize($base, $relative)
8050 {
8051 if (!($relative instanceof SimplePie_IRI))
8052 {
8053 $relative = new SimplePie_IRI($relative);
8054 }
8055 if (!$relative->is_valid())
8056 {
8057 return false;
8058 }
8059 elseif ($relative->scheme !== null)
8060 {
8061 return clone $relative;
8062 }
8063 else
8064 {
8065 if (!($base instanceof SimplePie_IRI))
8066 {
8067 $base = new SimplePie_IRI($base);
8068 }
8069 if ($base->scheme !== null && $base->is_valid())
8070 {
8071 if ($relative->get_iri() !== '')
8072 {
8073 if ($relative->iuserinfo !== null || $relative->ihost !== null || $relative->port !== null)
8074 {
8075 $target = clone $relative;
8076 $target->scheme = $base->scheme;
8077 }
8078 else
8079 {
8080 $target = new SimplePie_IRI;
8081 $target->scheme = $base->scheme;
8082 $target->iuserinfo = $base->iuserinfo;
8083 $target->ihost = $base->ihost;
8084 $target->port = $base->port;
8085 if ($relative->ipath !== '')
8086 {
8087 if ($relative->ipath[0] === '/')
8088 {
8089 $target->ipath = $relative->ipath;
8090 }
8091 elseif (($base->iuserinfo !== null || $base->ihost !== null || $base->port !== null) && $base->ipath === '')
8092 {
8093 $target->ipath = '/' . $relative->ipath;
8094 }
8095 elseif (($last_segment = strrpos($base->ipath, '/')) !== false)
8096 {
8097 $target->ipath = substr($base->ipath, 0, $last_segment + 1) . $relative->ipath;
8098 }
8099 else
8100 {
8101 $target->ipath = $relative->ipath;
8102 }
8103 $target->ipath = $target->remove_dot_segments($target->ipath);
8104 $target->iquery = $relative->iquery;
8105 }
8106 else
8107 {
8108 $target->ipath = $base->ipath;
8109 if ($relative->iquery !== null)
8110 {
8111 $target->iquery = $relative->iquery;
8112 }
8113 elseif ($base->iquery !== null)
8114 {
8115 $target->iquery = $base->iquery;
8116 }
8117 }
8118 $target->ifragment = $relative->ifragment;
8119 }
8120 }
8121 else
8122 {
8123 $target = clone $base;
8124 $target->ifragment = null;
8125 }
8126 $target->scheme_normalization();
8127 return $target;
8128 }
8129 else
8130 {
8131 return false;
8132 }
8133 }
8134 }
8135
8136 /**
8137 * Parse an IRI into scheme/authority/path/query/fragment segments
8138 *
8139 * @param string $iri
8140 * @return array
8141 */
8142 protected function parse_iri($iri)
8143 {
8144 $iri = trim($iri, "\x20\x09\x0A\x0C\x0D");
8145 if (preg_match('/^((?P<scheme>[^:\/?#]+):)?(\/\/(?P<authority>[^\/?#]*))?(?P<path>[^?#]*)(\?(?P<query>[^#]*))?(#(?P<fragment>.*))?$/', $iri, $match))
8146 {
8147 if ($match[1] === '')
8148 {
8149 $match['scheme'] = null;
8150 }
8151 if (!isset($match[3]) || $match[3] === '')
8152 {
8153 $match['authority'] = null;
8154 }
8155 if (!isset($match[5]))
8156 {
8157 $match['path'] = '';
8158 }
8159 if (!isset($match[6]) || $match[6] === '')
8160 {
8161 $match['query'] = null;
8162 }
8163 if (!isset($match[8]) || $match[8] === '')
8164 {
8165 $match['fragment'] = null;
8166 }
8167 return $match;
8168 }
8169 else
8170 {
8171 // This can occur when a paragraph is accidentally parsed as a URI
8172 return false;
8173 }
8174 }
8175
8176 /**
8177 * Remove dot segments from a path
8178 *
8179 * @param string $input
8180 * @return string
8181 */
8182 protected function remove_dot_segments($input)
8183 {
8184 $output = '';
8185 while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..')
8186 {
8187 // A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise,
8188 if (strpos($input, '../') === 0)
8189 {
8190 $input = substr($input, 3);
8191 }
8192 elseif (strpos($input, './') === 0)
8193 {
8194 $input = substr($input, 2);
8195 }
8196 // B: if the input buffer begins with a prefix of "/./" or "/.", where "." is a complete path segment, then replace that prefix with "/" in the input buffer; otherwise,
8197 elseif (strpos($input, '/./') === 0)
8198 {
8199 $input = substr($input, 2);
8200 }
8201 elseif ($input === '/.')
8202 {
8203 $input = '/';
8204 }
8205 // C: if the input buffer begins with a prefix of "/../" or "/..", where ".." is a complete path segment, then replace that prefix with "/" in the input buffer and remove the last segment and its preceding "/" (if any) from the output buffer; otherwise,
8206 elseif (strpos($input, '/../') === 0)
8207 {
8208 $input = substr($input, 3);
8209 $output = substr_replace($output, '', strrpos($output, '/'));
8210 }
8211 elseif ($input === '/..')
8212 {
8213 $input = '/';
8214 $output = substr_replace($output, '', strrpos($output, '/'));
8215 }
8216 // D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise,
8217 elseif ($input === '.' || $input === '..')
8218 {
8219 $input = '';
8220 }
8221 // E: move the first path segment in the input buffer to the end of the output buffer, including the initial "/" character (if any) and any subsequent characters up to, but not including, the next "/" character or the end of the input buffer
8222 elseif (($pos = strpos($input, '/', 1)) !== false)
8223 {
8224 $output .= substr($input, 0, $pos);
8225 $input = substr_replace($input, '', 0, $pos);
8226 }
8227 else
8228 {
8229 $output .= $input;
8230 $input = '';
8231 }
8232 }
8233 return $output . $input;
8234 }
8235
8236 /**
8237 * Replace invalid character with percent encoding
8238 *
8239 * @param string $string Input string
8240 * @param string $extra_chars Valid characters not in iunreserved or
8241 * iprivate (this is ASCII-only)
8242 * @param bool $iprivate Allow iprivate
8243 * @return string
8244 */
8245 protected function replace_invalid_with_pct_encoding($string, $extra_chars, $iprivate = false)
8246 {
8247 // Normalize as many pct-encoded sections as possible
8248 $string = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', array($this, 'remove_iunreserved_percent_encoded'), $string);
8249
8250 // Replace invalid percent characters
8251 $string = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $string);
8252
8253 // Add unreserved and % to $extra_chars (the latter is safe because all
8254 // pct-encoded sections are now valid).
8255 $extra_chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~%';
8256
8257 // Now replace any bytes that aren't allowed with their pct-encoded versions
8258 $position = 0;
8259 $strlen = strlen($string);
8260 while (($position += strspn($string, $extra_chars, $position)) < $strlen)
8261 {
8262 $value = ord($string[$position]);
8263
8264 // Start position
8265 $start = $position;
8266
8267 // By default we are valid
8268 $valid = true;
8269
8270 // No one byte sequences are valid due to the while.
8271 // Two byte sequence:
8272 if (($value & 0xE0) === 0xC0)
8273 {
8274 $character = ($value & 0x1F) << 6;
8275 $length = 2;
8276 $remaining = 1;
8277 }
8278 // Three byte sequence:
8279 elseif (($value & 0xF0) === 0xE0)
8280 {
8281 $character = ($value & 0x0F) << 12;
8282 $length = 3;
8283 $remaining = 2;
8284 }
8285 // Four byte sequence:
8286 elseif (($value & 0xF8) === 0xF0)
8287 {
8288 $character = ($value & 0x07) << 18;
8289 $length = 4;
8290 $remaining = 3;
8291 }
8292 // Invalid byte:
8293 else
8294 {
8295 $valid = false;
8296 $length = 1;
8297 $remaining = 0;
8298 }
8299
8300 if ($remaining)
8301 {
8302 if ($position + $length <= $strlen)
8303 {
8304 for ($position++; $remaining; $position++)
8305 {
8306 $value = ord($string[$position]);
8307
8308 // Check that the byte is valid, then add it to the character:
8309 if (($value & 0xC0) === 0x80)
8310 {
8311 $character |= ($value & 0x3F) << (--$remaining * 6);
8312 }
8313 // If it is invalid, count the sequence as invalid and reprocess the current byte:
8314 else
8315 {
8316 $valid = false;
8317 $position--;
8318 break;
8319 }
8320 }
8321 }
8322 else
8323 {
8324 $position = $strlen - 1;
8325 $valid = false;
8326 }
8327 }
8328
8329 // Percent encode anything invalid or not in ucschar
8330 if (
8331 // Invalid sequences
8332 !$valid
8333 // Non-shortest form sequences are invalid
8334 || $length > 1 && $character <= 0x7F
8335 || $length > 2 && $character <= 0x7FF
8336 || $length > 3 && $character <= 0xFFFF
8337 // Outside of range of ucschar codepoints
8338 // Noncharacters
8339 || ($character & 0xFFFE) === 0xFFFE
8340 || $character >= 0xFDD0 && $character <= 0xFDEF
8341 || (
8342 // Everything else not in ucschar
8343 $character > 0xD7FF && $character < 0xF900
8344 || $character < 0xA0
8345 || $character > 0xEFFFD
8346 )
8347 && (
8348 // Everything not in iprivate, if it applies
8349 !$iprivate
8350 || $character < 0xE000
8351 || $character > 0x10FFFD
8352 )
8353 )
8354 {
8355 // If we were a character, pretend we weren't, but rather an error.
8356 if ($valid)
8357 $position--;
8358
8359 for ($j = $start; $j <= $position; $j++)
8360 {
8361 $string = substr_replace($string, sprintf('%%%02X', ord($string[$j])), $j, 1);
8362 $j += 2;
8363 $position += 2;
8364 $strlen += 2;
8365 }
8366 }
8367 }
8368
8369 return $string;
8370 }
8371
8372 /**
8373 * Callback function for preg_replace_callback.
8374 *
8375 * Removes sequences of percent encoded bytes that represent UTF-8
8376 * encoded characters in iunreserved
8377 *
8378 * @param array $match PCRE match
8379 * @return string Replacement
8380 */
8381 protected function remove_iunreserved_percent_encoded($match)
8382 {
8383 // As we just have valid percent encoded sequences we can just explode
8384 // and ignore the first member of the returned array (an empty string).
8385 $bytes = explode('%', $match[0]);
8386
8387 // Initialize the new string (this is what will be returned) and that
8388 // there are no bytes remaining in the current sequence (unsurprising
8389 // at the first byte!).
8390 $string = '';
8391 $remaining = 0;
8392
8393 // Loop over each and every byte, and set $value to its value
8394 for ($i = 1, $len = count($bytes); $i < $len; $i++)
8395 {
8396 $value = hexdec($bytes[$i]);
8397
8398 // If we're the first byte of sequence:
8399 if (!$remaining)
8400 {
8401 // Start position
8402 $start = $i;
8403
8404 // By default we are valid
8405 $valid = true;
8406
8407 // One byte sequence:
8408 if ($value <= 0x7F)
8409 {
8410 $character = $value;
8411 $length = 1;
8412 }
8413 // Two byte sequence:
8414 elseif (($value & 0xE0) === 0xC0)
8415 {
8416 $character = ($value & 0x1F) << 6;
8417 $length = 2;
8418 $remaining = 1;
8419 }
8420 // Three byte sequence:
8421 elseif (($value & 0xF0) === 0xE0)
8422 {
8423 $character = ($value & 0x0F) << 12;
8424 $length = 3;
8425 $remaining = 2;
8426 }
8427 // Four byte sequence:
8428 elseif (($value & 0xF8) === 0xF0)
8429 {
8430 $character = ($value & 0x07) << 18;
8431 $length = 4;
8432 $remaining = 3;
8433 }
8434 // Invalid byte:
8435 else
8436 {
8437 $valid = false;
8438 $remaining = 0;
8439 }
8440 }
8441 // Continuation byte:
8442 else
8443 {
8444 // Check that the byte is valid, then add it to the character:
8445 if (($value & 0xC0) === 0x80)
8446 {
8447 $remaining--;
8448 $character |= ($value & 0x3F) << ($remaining * 6);
8449 }
8450 // If it is invalid, count the sequence as invalid and reprocess the current byte as the start of a sequence:
8451 else
8452 {
8453 $valid = false;
8454 $remaining = 0;
8455 $i--;
8456 }
8457 }
8458
8459 // If we've reached the end of the current byte sequence, append it to Unicode::$data
8460 if (!$remaining)
8461 {
8462 // Percent encode anything invalid or not in iunreserved
8463 if (
8464 // Invalid sequences
8465 !$valid
8466 // Non-shortest form sequences are invalid
8467 || $length > 1 && $character <= 0x7F
8468 || $length > 2 && $character <= 0x7FF
8469 || $length > 3 && $character <= 0xFFFF
8470 // Outside of range of iunreserved codepoints
8471 || $character < 0x2D
8472 || $character > 0xEFFFD
8473 // Noncharacters
8474 || ($character & 0xFFFE) === 0xFFFE
8475 || $character >= 0xFDD0 && $character <= 0xFDEF
8476 // Everything else not in iunreserved (this is all BMP)
8477 || $character === 0x2F
8478 || $character > 0x39 && $character < 0x41
8479 || $character > 0x5A && $character < 0x61
8480 || $character > 0x7A && $character < 0x7E
8481 || $character > 0x7E && $character < 0xA0
8482 || $character > 0xD7FF && $character < 0xF900
8483 )
8484 {
8485 for ($j = $start; $j <= $i; $j++)
8486 {
8487 $string .= '%' . strtoupper($bytes[$j]);
8488 }
8489 }
8490 else
8491 {
8492 for ($j = $start; $j <= $i; $j++)
8493 {
8494 $string .= chr(hexdec($bytes[$j]));
8495 }
8496 }
8497 }
8498 }
8499
8500 // If we have any bytes left over they are invalid (i.e., we are
8501 // mid-way through a multi-byte sequence)
8502 if ($remaining)
8503 {
8504 for ($j = $start; $j < $len; $j++)
8505 {
8506 $string .= '%' . strtoupper($bytes[$j]);
8507 }
8508 }
8509
8510 return $string;
8511 }
8512
8513 protected function scheme_normalization()
8514 {
8515 if (isset($this->normalization[$this->scheme]['iuserinfo']) && $this->iuserinfo === $this->normalization[$this->scheme]['iuserinfo'])
8516 {
8517 $this->iuserinfo = null;
8518 }
8519 if (isset($this->normalization[$this->scheme]['ihost']) && $this->ihost === $this->normalization[$this->scheme]['ihost'])
8520 {
8521 $this->ihost = null;
8522 }
8523 if (isset($this->normalization[$this->scheme]['port']) && $this->port === $this->normalization[$this->scheme]['port'])
8524 {
8525 $this->port = null;
8526 }
8527 if (isset($this->normalization[$this->scheme]['ipath']) && $this->ipath === $this->normalization[$this->scheme]['ipath'])
8528 {
8529 $this->ipath = '';
8530 }
8531 if (isset($this->normalization[$this->scheme]['iquery']) && $this->iquery === $this->normalization[$this->scheme]['iquery'])
8532 {
8533 $this->iquery = null;
8534 }
8535 if (isset($this->normalization[$this->scheme]['ifragment']) && $this->ifragment === $this->normalization[$this->scheme]['ifragment'])
8536 {
8537 $this->ifragment = null;
8538 }
8539 }
8540
8541 /**
8542 * Check if the object represents a valid IRI. This needs to be done on each
8543 * call as some things change depending on another part of the IRI.
8544 *
8545 * @return bool
8546 */
8547 public function is_valid()
8548 {
8549 $isauthority = $this->iuserinfo !== null || $this->ihost !== null || $this->port !== null;
8550 if ($this->ipath !== '' &&
8551 (
8552 $isauthority && (
8553 $this->ipath[0] !== '/' ||
8554 substr($this->ipath, 0, 2) === '//'
8555 ) ||
8556 (
8557 $this->scheme === null &&
8558 !$isauthority &&
8559 strpos($this->ipath, ':') !== false &&
8560 (strpos($this->ipath, '/') === false ? true : strpos($this->ipath, ':') < strpos($this->ipath, '/'))
8561 )
8562 )
8563 )
8564 {
8565 return false;
8566 }
8567
8568 return true;
8569 }
8570
8571 /**
8572 * Set the entire IRI. Returns true on success, false on failure (if there
8573 * are any invalid characters).
8574 *
8575 * @param string $iri
8576 * @return bool
8577 */
8578 public function set_iri($iri)
8579 {
8580 static $cache;
8581 if (!$cache)
8582 {
8583 $cache = array();
8584 }
8585
8586 if ($iri === null)
8587 {
8588 return true;
8589 }
8590 elseif (isset($cache[$iri]))
8591 {
8592 list($this->scheme,
8593 $this->iuserinfo,
8594 $this->ihost,
8595 $this->port,
8596 $this->ipath,
8597 $this->iquery,
8598 $this->ifragment,
8599 $return) = $cache[$iri];
8600 return $return;
8601 }
8602 else
8603 {
8604 $parsed = $this->parse_iri((string) $iri);
8605 if (!$parsed)
8606 {
8607 return false;
8608 }
8609
8610 $return = $this->set_scheme($parsed['scheme'])
8611 && $this->set_authority($parsed['authority'])
8612 && $this->set_path($parsed['path'])
8613 && $this->set_query($parsed['query'])
8614 && $this->set_fragment($parsed['fragment']);
8615
8616 $cache[$iri] = array($this->scheme,
8617 $this->iuserinfo,
8618 $this->ihost,
8619 $this->port,
8620 $this->ipath,
8621 $this->iquery,
8622 $this->ifragment,
8623 $return);
8624 return $return;
8625 }
8626 }
8627
8628 /**
8629 * Set the scheme. Returns true on success, false on failure (if there are
8630 * any invalid characters).
8631 *
8632 * @param string $scheme
8633 * @return bool
8634 */
8635 public function set_scheme($scheme)
8636 {
8637 if ($scheme === null)
8638 {
8639 $this->scheme = null;
8640 }
8641 elseif (!preg_match('/^[A-Za-z][0-9A-Za-z+\-.]*$/', $scheme))
8642 {
8643 $this->scheme = null;
8644 return false;
8645 }
8646 else
8647 {
8648 $this->scheme = strtolower($scheme);
8649 }
8650 return true;
8651 }
8652
8653 /**
8654 * Set the authority. Returns true on success, false on failure (if there are
8655 * any invalid characters).
8656 *
8657 * @param string $authority
8658 * @return bool
8659 */
8660 public function set_authority($authority)
8661 {
8662 static $cache;
8663 if (!$cache)
8664 $cache = array();
8665
8666 if ($authority === null)
8667 {
8668 $this->iuserinfo = null;
8669 $this->ihost = null;
8670 $this->port = null;
8671 return true;
8672 }
8673 elseif (isset($cache[$authority]))
8674 {
8675 list($this->iuserinfo,
8676 $this->ihost,
8677 $this->port,
8678 $return) = $cache[$authority];
8679
8680 return $return;
8681 }
8682 else
8683 {
8684 $remaining = $authority;
8685 if (($iuserinfo_end = strrpos($remaining, '@')) !== false)
8686 {
8687 $iuserinfo = substr($remaining, 0, $iuserinfo_end);
8688 $remaining = substr($remaining, $iuserinfo_end + 1);
8689 }
8690 else
8691 {
8692 $iuserinfo = null;
8693 }
8694 if (($port_start = strpos($remaining, ':', strpos($remaining, ']'))) !== false)
8695 {
8696 if (($port = substr($remaining, $port_start + 1)) === false)
8697 {
8698 $port = null;
8699 }
8700 $remaining = substr($remaining, 0, $port_start);
8701 }
8702 else
8703 {
8704 $port = null;
8705 }
8706
8707 $return = $this->set_userinfo($iuserinfo) &&
8708 $this->set_host($remaining) &&
8709 $this->set_port($port);
8710
8711 $cache[$authority] = array($this->iuserinfo,
8712 $this->ihost,
8713 $this->port,
8714 $return);
8715
8716 return $return;
8717 }
8718 }
8719
8720 /**
8721 * Set the iuserinfo.
8722 *
8723 * @param string $iuserinfo
8724 * @return bool
8725 */
8726 public function set_userinfo($iuserinfo)
8727 {
8728 if ($iuserinfo === null)
8729 {
8730 $this->iuserinfo = null;
8731 }
8732 else
8733 {
8734 $this->iuserinfo = $this->replace_invalid_with_pct_encoding($iuserinfo, '!$&\'()*+,;=:');
8735 $this->scheme_normalization();
8736 }
8737
8738 return true;
8739 }
8740
8741 /**
8742 * Set the ihost. Returns true on success, false on failure (if there are
8743 * any invalid characters).
8744 *
8745 * @param string $ihost
8746 * @return bool
8747 */
8748 public function set_host($ihost)
8749 {
8750 if ($ihost === null)
8751 {
8752 $this->ihost = null;
8753 return true;
8754 }
8755 elseif (substr($ihost, 0, 1) === '[' && substr($ihost, -1) === ']')
8756 {
8757 if (SimplePie_Net_IPv6::check_ipv6(substr($ihost, 1, -1)))
8758 {
8759 $this->ihost = '[' . SimplePie_Net_IPv6::compress(substr($ihost, 1, -1)) . ']';
8760 }
8761 else
8762 {
8763 $this->ihost = null;
8764 return false;
8765 }
8766 }
8767 else
8768 {
8769 $ihost = $this->replace_invalid_with_pct_encoding($ihost, '!$&\'()*+,;=');
8770
8771 // Lowercase, but ignore pct-encoded sections (as they should
8772 // remain uppercase). This must be done after the previous step
8773 // as that can add unescaped characters.
8774 $position = 0;
8775 $strlen = strlen($ihost);
8776 while (($position += strcspn($ihost, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ%', $position)) < $strlen)
8777 {
8778 if ($ihost[$position] === '%')
8779 {
8780 $position += 3;
8781 }
8782 else
8783 {
8784 $ihost[$position] = strtolower($ihost[$position]);
8785 $position++;
8786 }
8787 }
8788
8789 $this->ihost = $ihost;
8790 }
8791
8792 $this->scheme_normalization();
8793
8794 return true;
8795 }
8796
8797 /**
8798 * Set the port. Returns true on success, false on failure (if there are
8799 * any invalid characters).
8800 *
8801 * @param string $port
8802 * @return bool
8803 */
8804 public function set_port($port)
8805 {
8806 if ($port === null)
8807 {
8808 $this->port = null;
8809 return true;
8810 }
8811 elseif (strspn($port, '0123456789') === strlen($port))
8812 {
8813 $this->port = (int) $port;
8814 $this->scheme_normalization();
8815 return true;
8816 }
8817 else
8818 {
8819 $this->port = null;
8820 return false;
8821 }
8822 }
8823
8824 /**
8825 * Set the ipath.
8826 *
8827 * @param string $ipath
8828 * @return bool
8829 */
8830 public function set_path($ipath)
8831 {
8832 static $cache;
8833 if (!$cache)
8834 {
8835 $cache = array();
8836 }
8837
8838 $ipath = (string) $ipath;
8839
8840 if (isset($cache[$ipath]))
8841 {
8842 $this->ipath = $cache[$ipath][(int) ($this->scheme !== null)];
8843 }
8844 else
8845 {
8846 $valid = $this->replace_invalid_with_pct_encoding($ipath, '!$&\'()*+,;=@:/');
8847 $removed = $this->remove_dot_segments($valid);
8848
8849 $cache[$ipath] = array($valid, $removed);
8850 $this->ipath = ($this->scheme !== null) ? $removed : $valid;
8851 }
8852
8853 $this->scheme_normalization();
8854 return true;
8855 }
8856
8857 /**
8858 * Set the iquery.
8859 *
8860 * @param string $iquery
8861 * @return bool
8862 */
8863 public function set_query($iquery)
8864 {
8865 if ($iquery === null)
8866 {
8867 $this->iquery = null;
8868 }
8869 else
8870 {
8871 $this->iquery = $this->replace_invalid_with_pct_encoding($iquery, '!$&\'()*+,;=:@/?', true);
8872 $this->scheme_normalization();
8873 }
8874 return true;
8875 }
8876
8877 /**
8878 * Set the ifragment.
8879 *
8880 * @param string $ifragment
8881 * @return bool
8882 */
8883 public function set_fragment($ifragment)
8884 {
8885 if ($ifragment === null)
8886 {
8887 $this->ifragment = null;
8888 }
8889 else
8890 {
8891 $this->ifragment = $this->replace_invalid_with_pct_encoding($ifragment, '!$&\'()*+,;=:@/?');
8892 $this->scheme_normalization();
8893 }
8894 return true;
8895 }
8896
8897 /**
8898 * Convert an IRI to a URI (or parts thereof)
8899 *
8900 * @return string
8901 */
8902 public function to_uri($string)
8903 {
8904 static $non_ascii;
8905 if (!$non_ascii)
8906 {
8907 $non_ascii = implode('', range("\x80", "\xFF"));
8908 }
8909
8910 $position = 0;
8911 $strlen = strlen($string);
8912 while (($position += strcspn($string, $non_ascii, $position)) < $strlen)
8913 {
8914 $string = substr_replace($string, sprintf('%%%02X', ord($string[$position])), $position, 1);
8915 $position += 3;
8916 $strlen += 2;
8917 }
8918
8919 return $string;
8920 }
8921
8922 /**
8923 * Get the complete IRI
8924 *
8925 * @return string
8926 */
8927 public function get_iri()
8928 {
8929 if (!$this->is_valid())
8930 {
8931 return false;
8932 }
8933
8934 $iri = '';
8935 if ($this->scheme !== null)
8936 {
8937 $iri .= $this->scheme . ':';
8938 }
8939 if (($iauthority = $this->get_iauthority()) !== null)
8940 {
8941 $iri .= '//' . $iauthority;
8942 }
8943 if ($this->ipath !== '')
8944 {
8945 $iri .= $this->ipath;
8946 }
8947 elseif (!empty($this->normalization[$this->scheme]['ipath']) && $iauthority !== null && $iauthority !== '')
8948 {
8949 $iri .= $this->normalization[$this->scheme]['ipath'];
8950 }
8951 if ($this->iquery !== null)
8952 {
8953 $iri .= '?' . $this->iquery;
8954 }
8955 if ($this->ifragment !== null)
8956 {
8957 $iri .= '#' . $this->ifragment;
8958 }
8959
8960 return $iri;
8961 }
8962
8963 /**
8964 * Get the complete URI
8965 *
8966 * @return string
8967 */
8968 public function get_uri()
8969 {
8970 return $this->to_uri($this->get_iri());
8971 }
8972
8973 /**
8974 * Get the complete iauthority
8975 *
8976 * @return string
8977 */
8978 protected function get_iauthority()
8979 {
8980 if ($this->iuserinfo !== null || $this->ihost !== null || $this->port !== null)
8981 {
8982 $iauthority = '';
8983 if ($this->iuserinfo !== null)
8984 {
8985 $iauthority .= $this->iuserinfo . '@';
8986 }
8987 if ($this->ihost !== null)
8988 {
8989 $iauthority .= $this->ihost;
8990 }
8991 if ($this->port !== null)
8992 {
8993 $iauthority .= ':' . $this->port;
8994 }
8995 return $iauthority;
8996 }
8997 else
8998 {
8999 return null;
9000 }
9001 }
9002
9003 /**
9004 * Get the complete authority
9005 *
9006 * @return string
9007 */
9008 protected function get_authority()
9009 {
9010 $iauthority = $this->get_iauthority();
9011 if (is_string($iauthority))
9012 return $this->to_uri($iauthority);
9013 else
9014 return $iauthority;
9015 }
9016 }
9017
9018 /**
9019 * Manages all item-related data
9020 *
9021 * Used by {@see SimplePie::get_item()} and {@see SimplePie::get_items()}
9022 *
9023 * This class can be overloaded with {@see SimplePie::set_item_class()}
9024 *
9025 * @package SimplePie
9026 * @subpackage API
9027 */
9028 class SimplePie_Item
9029 {
9030 /**
9031 * Parent feed
9032 *
9033 * @access private
9034 * @var SimplePie
9035 */
9036 var $feed;
9037
9038 /**
9039 * Raw data
9040 *
9041 * @access private
9042 * @var array
9043 */
9044 var $data = array();
9045
9046 /**
9047 * Registry object
9048 *
9049 * @see set_registry
9050 * @var SimplePie_Registry
9051 */
9052 protected $registry;
9053
9054 /**
9055 * Create a new item object
9056 *
9057 * This is usually used by {@see SimplePie::get_items} and
9058 * {@see SimplePie::get_item}. Avoid creating this manually.
9059 *
9060 * @param SimplePie $feed Parent feed
9061 * @param array $data Raw data
9062 */
9063 public function __construct($feed, $data)
9064 {
9065 $this->feed = $feed;
9066 $this->data = $data;
9067 }
9068
9069 /**
9070 * Set the registry handler
9071 *
9072 * This is usually used by {@see SimplePie_Registry::create}
9073 *
9074 * @since 1.3
9075 * @param SimplePie_Registry $registry
9076 */
9077 public function set_registry(SimplePie_Registry $registry)
9078 {
9079 $this->registry = $registry;
9080 }
9081
9082 /**
9083 * Get a string representation of the item
9084 *
9085 * @return string
9086 */
9087 public function __toString()
9088 {
9089 return md5(serialize($this->data));
9090 }
9091
9092 /**
9093 * Remove items that link back to this before destroying this object
9094 */
9095 public function __destruct()
9096 {
9097 if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode'))
9098 {
9099 unset($this->feed);
9100 }
9101 }
9102
9103 /**
9104 * Get data for an item-level element
9105 *
9106 * This method allows you to get access to ANY element/attribute that is a
9107 * sub-element of the item/entry tag.
9108 *
9109 * See {@see SimplePie::get_feed_tags()} for a description of the return value
9110 *
9111 * @since 1.0
9112 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces
9113 * @param string $namespace The URL of the XML namespace of the elements you're trying to access
9114 * @param string $tag Tag name
9115 * @return array
9116 */
9117 public function get_item_tags($namespace, $tag)
9118 {
9119 if (isset($this->data['child'][$namespace][$tag]))
9120 {
9121 return $this->data['child'][$namespace][$tag];
9122 }
9123 else
9124 {
9125 return null;
9126 }
9127 }
9128
9129 /**
9130 * Get the base URL value from the parent feed
9131 *
9132 * Uses `<xml:base>`
9133 *
9134 * @param array $element
9135 * @return string
9136 */
9137 public function get_base($element = array())
9138 {
9139 return $this->feed->get_base($element);
9140 }
9141
9142 /**
9143 * Sanitize feed data
9144 *
9145 * @access private
9146 * @see SimplePie::sanitize()
9147 * @param string $data Data to sanitize
9148 * @param int $type One of the SIMPLEPIE_CONSTRUCT_* constants
9149 * @param string $base Base URL to resolve URLs against
9150 * @return string Sanitized data
9151 */
9152 public function sanitize($data, $type, $base = '')
9153 {
9154 return $this->feed->sanitize($data, $type, $base);
9155 }
9156
9157 /**
9158 * Get the parent feed
9159 *
9160 * Note: this may not work as you think for multifeeds!
9161 *
9162 * @link http://simplepie.org/faq/typical_multifeed_gotchas#missing_data_from_feed
9163 * @since 1.0
9164 * @return SimplePie
9165 */
9166 public function get_feed()
9167 {
9168 return $this->feed;
9169 }
9170
9171 /**
9172 * Get the unique identifier for the item
9173 *
9174 * This is usually used when writing code to check for new items in a feed.
9175 *
9176 * Uses `<atom:id>`, `<guid>`, `<dc:identifier>` or the `about` attribute
9177 * for RDF. If none of these are supplied (or `$hash` is true), creates an
9178 * MD5 hash based on the permalink and title. If either of those are not
9179 * supplied, creates a hash based on the full feed data.
9180 *
9181 * @since Beta 2
9182 * @param boolean $hash Should we force using a hash instead of the supplied ID?
9183 * @return string
9184 */
9185 public function get_id($hash = false)
9186 {
9187 if (!$hash)
9188 {
9189 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'id'))
9190 {
9191 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9192 }
9193 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'id'))
9194 {
9195 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9196 }
9197 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid'))
9198 {
9199 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9200 }
9201 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'identifier'))
9202 {
9203 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9204 }
9205 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'identifier'))
9206 {
9207 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9208 }
9209 elseif (isset($this->data['attribs'][SIMPLEPIE_NAMESPACE_RDF]['about']))
9210 {
9211 return $this->sanitize($this->data['attribs'][SIMPLEPIE_NAMESPACE_RDF]['about'], SIMPLEPIE_CONSTRUCT_TEXT);
9212 }
9213 elseif (($return = $this->get_permalink()) !== null)
9214 {
9215 return $return;
9216 }
9217 elseif (($return = $this->get_title()) !== null)
9218 {
9219 return $return;
9220 }
9221 }
9222 if ($this->get_permalink() !== null || $this->get_title() !== null)
9223 {
9224 return md5($this->get_permalink() . $this->get_title());
9225 }
9226 else
9227 {
9228 return md5(serialize($this->data));
9229 }
9230 }
9231
9232 /**
9233 * Get the title of the item
9234 *
9235 * Uses `<atom:title>`, `<title>` or `<dc:title>`
9236 *
9237 * @since Beta 2 (previously called `get_item_title` since 0.8)
9238 * @return string|null
9239 */
9240 public function get_title()
9241 {
9242 if (!isset($this->data['title']))
9243 {
9244 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title'))
9245 {
9246 $this->data['title'] = $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
9247 }
9248 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title'))
9249 {
9250 $this->data['title'] = $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
9251 }
9252 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
9253 {
9254 $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
9255 }
9256 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
9257 {
9258 $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
9259 }
9260 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
9261 {
9262 $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
9263 }
9264 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
9265 {
9266 $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9267 }
9268 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
9269 {
9270 $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9271 }
9272 else
9273 {
9274 $this->data['title'] = null;
9275 }
9276 }
9277 return $this->data['title'];
9278 }
9279
9280 /**
9281 * Get the content for the item
9282 *
9283 * Prefers summaries over full content , but will return full content if a
9284 * summary does not exist.
9285 *
9286 * To prefer full content instead, use {@see get_content}
9287 *
9288 * Uses `<atom:summary>`, `<description>`, `<dc:description>` or
9289 * `<itunes:subtitle>`
9290 *
9291 * @since 0.8
9292 * @param boolean $description_only Should we avoid falling back to the content?
9293 * @return string|null
9294 */
9295 public function get_description($description_only = false)
9296 {
9297 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'summary'))
9298 {
9299 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
9300 }
9301 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'summary'))
9302 {
9303 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
9304 }
9305 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description'))
9306 {
9307 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
9308 }
9309 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description'))
9310 {
9311 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
9312 }
9313 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description'))
9314 {
9315 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9316 }
9317 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description'))
9318 {
9319 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9320 }
9321 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary'))
9322 {
9323 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
9324 }
9325 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle'))
9326 {
9327 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9328 }
9329 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description'))
9330 {
9331 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML);
9332 }
9333
9334 elseif (!$description_only)
9335 {
9336 return $this->get_content(true);
9337 }
9338 else
9339 {
9340 return null;
9341 }
9342 }
9343
9344 /**
9345 * Get the content for the item
9346 *
9347 * Prefers full content over summaries, but will return a summary if full
9348 * content does not exist.
9349 *
9350 * To prefer summaries instead, use {@see get_description}
9351 *
9352 * Uses `<atom:content>` or `<content:encoded>` (RSS 1.0 Content Module)
9353 *
9354 * @since 1.0
9355 * @param boolean $content_only Should we avoid falling back to the description?
9356 * @return string|null
9357 */
9358 public function get_content($content_only = false)
9359 {
9360 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'content'))
9361 {
9362 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_content_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
9363 }
9364 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'content'))
9365 {
9366 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
9367 }
9368 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT, 'encoded'))
9369 {
9370 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
9371 }
9372 elseif (!$content_only)
9373 {
9374 return $this->get_description(true);
9375 }
9376 else
9377 {
9378 return null;
9379 }
9380 }
9381
9382 /**
9383 * Get a category for the item
9384 *
9385 * @since Beta 3 (previously called `get_categories()` since Beta 2)
9386 * @param int $key The category that you want to return. Remember that arrays begin with 0, not 1
9387 * @return SimplePie_Category|null
9388 */
9389 public function get_category($key = 0)
9390 {
9391 $categories = $this->get_categories();
9392 if (isset($categories[$key]))
9393 {
9394 return $categories[$key];
9395 }
9396 else
9397 {
9398 return null;
9399 }
9400 }
9401
9402 /**
9403 * Get all categories for the item
9404 *
9405 * Uses `<atom:category>`, `<category>` or `<dc:subject>`
9406 *
9407 * @since Beta 3
9408 * @return array|null List of {@see SimplePie_Category} objects
9409 */
9410 public function get_categories()
9411 {
9412 $categories = array();
9413
9414 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category)
9415 {
9416 $term = null;
9417 $scheme = null;
9418 $label = null;
9419 if (isset($category['attribs']['']['term']))
9420 {
9421 $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT);
9422 }
9423 if (isset($category['attribs']['']['scheme']))
9424 {
9425 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
9426 }
9427 if (isset($category['attribs']['']['label']))
9428 {
9429 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
9430 }
9431 $categories[] = $this->registry->create('Category', array($term, $scheme, $label));
9432 }
9433 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category)
9434 {
9435 // This is really the label, but keep this as the term also for BC.
9436 // Label will also work on retrieving because that falls back to term.
9437 $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9438 if (isset($category['attribs']['']['domain']))
9439 {
9440 $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT);
9441 }
9442 else
9443 {
9444 $scheme = null;
9445 }
9446 $categories[] = $this->registry->create('Category', array($term, $scheme, null));
9447 }
9448 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category)
9449 {
9450 $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
9451 }
9452 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category)
9453 {
9454 $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
9455 }
9456
9457 if (!empty($categories))
9458 {
9459 return array_unique($categories);
9460 }
9461 else
9462 {
9463 return null;
9464 }
9465 }
9466
9467 /**
9468 * Get an author for the item
9469 *
9470 * @since Beta 2
9471 * @param int $key The author that you want to return. Remember that arrays begin with 0, not 1
9472 * @return SimplePie_Author|null
9473 */
9474 public function get_author($key = 0)
9475 {
9476 $authors = $this->get_authors();
9477 if (isset($authors[$key]))
9478 {
9479 return $authors[$key];
9480 }
9481 else
9482 {
9483 return null;
9484 }
9485 }
9486
9487 /**
9488 * Get a contributor for the item
9489 *
9490 * @since 1.1
9491 * @param int $key The contrbutor that you want to return. Remember that arrays begin with 0, not 1
9492 * @return SimplePie_Author|null
9493 */
9494 public function get_contributor($key = 0)
9495 {
9496 $contributors = $this->get_contributors();
9497 if (isset($contributors[$key]))
9498 {
9499 return $contributors[$key];
9500 }
9501 else
9502 {
9503 return null;
9504 }
9505 }
9506
9507 /**
9508 * Get all contributors for the item
9509 *
9510 * Uses `<atom:contributor>`
9511 *
9512 * @since 1.1
9513 * @return array|null List of {@see SimplePie_Author} objects
9514 */
9515 public function get_contributors()
9516 {
9517 $contributors = array();
9518 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor)
9519 {
9520 $name = null;
9521 $uri = null;
9522 $email = null;
9523 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
9524 {
9525 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9526 }
9527 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
9528 {
9529 $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
9530 }
9531 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
9532 {
9533 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9534 }
9535 if ($name !== null || $email !== null || $uri !== null)
9536 {
9537 $contributors[] = $this->registry->create('Author', array($name, $uri, $email));
9538 }
9539 }
9540 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor)
9541 {
9542 $name = null;
9543 $url = null;
9544 $email = null;
9545 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
9546 {
9547 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9548 }
9549 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
9550 {
9551 $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
9552 }
9553 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
9554 {
9555 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9556 }
9557 if ($name !== null || $email !== null || $url !== null)
9558 {
9559 $contributors[] = $this->registry->create('Author', array($name, $url, $email));
9560 }
9561 }
9562
9563 if (!empty($contributors))
9564 {
9565 return array_unique($contributors);
9566 }
9567 else
9568 {
9569 return null;
9570 }
9571 }
9572
9573 /**
9574 * Get all authors for the item
9575 *
9576 * Uses `<atom:author>`, `<author>`, `<dc:creator>` or `<itunes:author>`
9577 *
9578 * @since Beta 2
9579 * @return array|null List of {@see SimplePie_Author} objects
9580 */
9581 public function get_authors()
9582 {
9583 $authors = array();
9584 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author)
9585 {
9586 $name = null;
9587 $uri = null;
9588 $email = null;
9589 if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
9590 {
9591 $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9592 }
9593 if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
9594 {
9595 $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
9596 }
9597 if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
9598 {
9599 $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9600 }
9601 if ($name !== null || $email !== null || $uri !== null)
9602 {
9603 $authors[] = $this->registry->create('Author', array($name, $uri, $email));
9604 }
9605 }
9606 if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author'))
9607 {
9608 $name = null;
9609 $url = null;
9610 $email = null;
9611 if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
9612 {
9613 $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9614 }
9615 if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
9616 {
9617 $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
9618 }
9619 if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
9620 {
9621 $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9622 }
9623 if ($name !== null || $email !== null || $url !== null)
9624 {
9625 $authors[] = $this->registry->create('Author', array($name, $url, $email));
9626 }
9627 }
9628 if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'author'))
9629 {
9630 $authors[] = $this->registry->create('Author', array(null, null, $this->sanitize($author[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)));
9631 }
9632 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author)
9633 {
9634 $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
9635 }
9636 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author)
9637 {
9638 $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
9639 }
9640 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author)
9641 {
9642 $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
9643 }
9644
9645 if (!empty($authors))
9646 {
9647 return array_unique($authors);
9648 }
9649 elseif (($source = $this->get_source()) && ($authors = $source->get_authors()))
9650 {
9651 return $authors;
9652 }
9653 elseif ($authors = $this->feed->get_authors())
9654 {
9655 return $authors;
9656 }
9657 else
9658 {
9659 return null;
9660 }
9661 }
9662
9663 /**
9664 * Get the copyright info for the item
9665 *
9666 * Uses `<atom:rights>` or `<dc:rights>`
9667 *
9668 * @since 1.1
9669 * @return string
9670 */
9671 public function get_copyright()
9672 {
9673 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights'))
9674 {
9675 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
9676 }
9677 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights'))
9678 {
9679 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9680 }
9681 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights'))
9682 {
9683 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
9684 }
9685 else
9686 {
9687 return null;
9688 }
9689 }
9690
9691 /**
9692 * Get the posting date/time for the item
9693 *
9694 * Uses `<atom:published>`, `<atom:updated>`, `<atom:issued>`,
9695 * `<atom:modified>`, `<pubDate>` or `<dc:date>`
9696 *
9697 * Note: obeys PHP's timezone setting. To get a UTC date/time, use
9698 * {@see get_gmdate}
9699 *
9700 * @since Beta 2 (previously called `get_item_date` since 0.8)
9701 *
9702 * @param string $date_format Supports any PHP date format from {@see http://php.net/date} (empty for the raw data)
9703 * @return int|string|null
9704 */
9705 public function get_date($date_format = 'j F Y, g:i a')
9706 {
9707 if (!isset($this->data['date']))
9708 {
9709 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'published'))
9710 {
9711 $this->data['date']['raw'] = $return[0]['data'];
9712 }
9713 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated'))
9714 {
9715 $this->data['date']['raw'] = $return[0]['data'];
9716 }
9717 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'issued'))
9718 {
9719 $this->data['date']['raw'] = $return[0]['data'];
9720 }
9721 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'created'))
9722 {
9723 $this->data['date']['raw'] = $return[0]['data'];
9724 }
9725 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'modified'))
9726 {
9727 $this->data['date']['raw'] = $return[0]['data'];
9728 }
9729 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'pubDate'))
9730 {
9731 $this->data['date']['raw'] = $return[0]['data'];
9732 }
9733 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'date'))
9734 {
9735 $this->data['date']['raw'] = $return[0]['data'];
9736 }
9737 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'date'))
9738 {
9739 $this->data['date']['raw'] = $return[0]['data'];
9740 }
9741
9742 if (!empty($this->data['date']['raw']))
9743 {
9744 $parser = $this->registry->call('Parse_Date', 'get');
9745 $this->data['date']['parsed'] = $parser->parse($this->data['date']['raw']);
9746 }
9747 else
9748 {
9749 $this->data['date'] = null;
9750 }
9751 }
9752 if ($this->data['date'])
9753 {
9754 $date_format = (string) $date_format;
9755 switch ($date_format)
9756 {
9757 case '':
9758 return $this->sanitize($this->data['date']['raw'], SIMPLEPIE_CONSTRUCT_TEXT);
9759
9760 case 'U':
9761 return $this->data['date']['parsed'];
9762
9763 default:
9764 return date($date_format, $this->data['date']['parsed']);
9765 }
9766 }
9767 else
9768 {
9769 return null;
9770 }
9771 }
9772
9773 /**
9774 * Get the update date/time for the item
9775 *
9776 * Uses `<atom:updated>`
9777 *
9778 * Note: obeys PHP's timezone setting. To get a UTC date/time, use
9779 * {@see get_gmdate}
9780 *
9781 * @param string $date_format Supports any PHP date format from {@see http://php.net/date} (empty for the raw data)
9782 * @return int|string|null
9783 */
9784 public function get_updated_date($date_format = 'j F Y, g:i a')
9785 {
9786 if (!isset($this->data['updated']))
9787 {
9788 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated'))
9789 {
9790 $this->data['updated']['raw'] = $return[0]['data'];
9791 }
9792
9793 if (!empty($this->data['updated']['raw']))
9794 {
9795 $parser = $this->registry->call('Parse_Date', 'get');
9796 $this->data['updated']['parsed'] = $parser->parse($this->data['date']['raw']);
9797 }
9798 else
9799 {
9800 $this->data['updated'] = null;
9801 }
9802 }
9803 if ($this->data['updated'])
9804 {
9805 $date_format = (string) $date_format;
9806 switch ($date_format)
9807 {
9808 case '':
9809 return $this->sanitize($this->data['updated']['raw'], SIMPLEPIE_CONSTRUCT_TEXT);
9810
9811 case 'U':
9812 return $this->data['updated']['parsed'];
9813
9814 default:
9815 return date($date_format, $this->data['updated']['parsed']);
9816 }
9817 }
9818 else
9819 {
9820 return null;
9821 }
9822 }
9823
9824 /**
9825 * Get the localized posting date/time for the item
9826 *
9827 * Returns the date formatted in the localized language. To display in
9828 * languages other than the server's default, you need to change the locale
9829 * with {@link http://php.net/setlocale setlocale()}. The available
9830 * localizations depend on which ones are installed on your web server.
9831 *
9832 * @since 1.0
9833 *
9834 * @param string $date_format Supports any PHP date format from {@see http://php.net/strftime} (empty for the raw data)
9835 * @return int|string|null
9836 */
9837 public function get_local_date($date_format = '%c')
9838 {
9839 if (!$date_format)
9840 {
9841 return $this->sanitize($this->get_date(''), SIMPLEPIE_CONSTRUCT_TEXT);
9842 }
9843 elseif (($date = $this->get_date('U')) !== null && $date !== false)
9844 {
9845 return strftime($date_format, $date);
9846 }
9847 else
9848 {
9849 return null;
9850 }
9851 }
9852
9853 /**
9854 * Get the posting date/time for the item (UTC time)
9855 *
9856 * @see get_date
9857 * @param string $date_format Supports any PHP date format from {@see http://php.net/date}
9858 * @return int|string|null
9859 */
9860 public function get_gmdate($date_format = 'j F Y, g:i a')
9861 {
9862 $date = $this->get_date('U');
9863 if ($date === null)
9864 {
9865 return null;
9866 }
9867
9868 return gmdate($date_format, $date);
9869 }
9870
9871 /**
9872 * Get the update date/time for the item (UTC time)
9873 *
9874 * @see get_updated_date
9875 * @param string $date_format Supports any PHP date format from {@see http://php.net/date}
9876 * @return int|string|null
9877 */
9878 public function get_updated_gmdate($date_format = 'j F Y, g:i a')
9879 {
9880 $date = $this->get_updated_date('U');
9881 if ($date === null)
9882 {
9883 return null;
9884 }
9885
9886 return gmdate($date_format, $date);
9887 }
9888
9889 /**
9890 * Get the permalink for the item
9891 *
9892 * Returns the first link available with a relationship of "alternate".
9893 * Identical to {@see get_link()} with key 0
9894 *
9895 * @see get_link
9896 * @since 0.8
9897 * @return string|null Permalink URL
9898 */
9899 public function get_permalink()
9900 {
9901 $link = $this->get_link();
9902 $enclosure = $this->get_enclosure(0);
9903 if ($link !== null)
9904 {
9905 return $link;
9906 }
9907 elseif ($enclosure !== null)
9908 {
9909 return $enclosure->get_link();
9910 }
9911 else
9912 {
9913 return null;
9914 }
9915 }
9916
9917 /**
9918 * Get a single link for the item
9919 *
9920 * @since Beta 3
9921 * @param int $key The link that you want to return. Remember that arrays begin with 0, not 1
9922 * @param string $rel The relationship of the link to return
9923 * @return string|null Link URL
9924 */
9925 public function get_link($key = 0, $rel = 'alternate')
9926 {
9927 $links = $this->get_links($rel);
9928 if ($links[$key] !== null)
9929 {
9930 return $links[$key];
9931 }
9932 else
9933 {
9934 return null;
9935 }
9936 }
9937
9938 /**
9939 * Get all links for the item
9940 *
9941 * Uses `<atom:link>`, `<link>` or `<guid>`
9942 *
9943 * @since Beta 2
9944 * @param string $rel The relationship of links to return
9945 * @return array|null Links found for the item (strings)
9946 */
9947 public function get_links($rel = 'alternate')
9948 {
9949 if (!isset($this->data['links']))
9950 {
9951 $this->data['links'] = array();
9952 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link)
9953 {
9954 if (isset($link['attribs']['']['href']))
9955 {
9956 $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
9957 $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
9958
9959 }
9960 }
9961 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link)
9962 {
9963 if (isset($link['attribs']['']['href']))
9964 {
9965 $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
9966 $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
9967 }
9968 }
9969 if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
9970 {
9971 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
9972 }
9973 if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
9974 {
9975 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
9976 }
9977 if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
9978 {
9979 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
9980 }
9981 if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid'))
9982 {
9983 if (!isset($links[0]['attribs']['']['isPermaLink']) || strtolower(trim($links[0]['attribs']['']['isPermaLink'])) === 'true')
9984 {
9985 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
9986 }
9987 }
9988
9989 $keys = array_keys($this->data['links']);
9990 foreach ($keys as $key)
9991 {
9992 if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key)))
9993 {
9994 if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]))
9995 {
9996 $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]);
9997 $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key];
9998 }
9999 else
10000 {
10001 $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key];
10002 }
10003 }
10004 elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY)
10005 {
10006 $this->data['links'][substr($key, 41)] =& $this->data['links'][$key];
10007 }
10008 $this->data['links'][$key] = array_unique($this->data['links'][$key]);
10009 }
10010 }
10011 if (isset($this->data['links'][$rel]))
10012 {
10013 return $this->data['links'][$rel];
10014 }
10015 else
10016 {
10017 return null;
10018 }
10019 }
10020
10021 /**
10022 * Get an enclosure from the item
10023 *
10024 * Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS.
10025 *
10026 * @since Beta 2
10027 * @todo Add ability to prefer one type of content over another (in a media group).
10028 * @param int $key The enclosure that you want to return. Remember that arrays begin with 0, not 1
10029 * @return SimplePie_Enclosure|null
10030 */
10031 public function get_enclosure($key = 0, $prefer = null)
10032 {
10033 $enclosures = $this->get_enclosures();
10034 if (isset($enclosures[$key]))
10035 {
10036 return $enclosures[$key];
10037 }
10038 else
10039 {
10040 return null;
10041 }
10042 }
10043
10044 /**
10045 * Get all available enclosures (podcasts, etc.)
10046 *
10047 * Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS.
10048 *
10049 * At this point, we're pretty much assuming that all enclosures for an item
10050 * are the same content. Anything else is too complicated to
10051 * properly support.
10052 *
10053 * @since Beta 2
10054 * @todo Add support for end-user defined sorting of enclosures by type/handler (so we can prefer the faster-loading FLV over MP4).
10055 * @todo If an element exists at a level, but it's value is empty, we should fall back to the value from the parent (if it exists).
10056 * @return array|null List of SimplePie_Enclosure items
10057 */
10058 public function get_enclosures()
10059 {
10060 if (!isset($this->data['enclosures']))
10061 {
10062 $this->data['enclosures'] = array();
10063
10064 // Elements
10065 $captions_parent = null;
10066 $categories_parent = null;
10067 $copyrights_parent = null;
10068 $credits_parent = null;
10069 $description_parent = null;
10070 $duration_parent = null;
10071 $hashes_parent = null;
10072 $keywords_parent = null;
10073 $player_parent = null;
10074 $ratings_parent = null;
10075 $restrictions_parent = null;
10076 $thumbnails_parent = null;
10077 $title_parent = null;
10078
10079 // Let's do the channel and item-level ones first, and just re-use them if we need to.
10080 $parent = $this->get_feed();
10081
10082 // CAPTIONS
10083 if ($captions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text'))
10084 {
10085 foreach ($captions as $caption)
10086 {
10087 $caption_type = null;
10088 $caption_lang = null;
10089 $caption_startTime = null;
10090 $caption_endTime = null;
10091 $caption_text = null;
10092 if (isset($caption['attribs']['']['type']))
10093 {
10094 $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10095 }
10096 if (isset($caption['attribs']['']['lang']))
10097 {
10098 $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
10099 }
10100 if (isset($caption['attribs']['']['start']))
10101 {
10102 $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
10103 }
10104 if (isset($caption['attribs']['']['end']))
10105 {
10106 $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
10107 }
10108 if (isset($caption['data']))
10109 {
10110 $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10111 }
10112 $captions_parent[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
10113 }
10114 }
10115 elseif ($captions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text'))
10116 {
10117 foreach ($captions as $caption)
10118 {
10119 $caption_type = null;
10120 $caption_lang = null;
10121 $caption_startTime = null;
10122 $caption_endTime = null;
10123 $caption_text = null;
10124 if (isset($caption['attribs']['']['type']))
10125 {
10126 $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10127 }
10128 if (isset($caption['attribs']['']['lang']))
10129 {
10130 $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
10131 }
10132 if (isset($caption['attribs']['']['start']))
10133 {
10134 $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
10135 }
10136 if (isset($caption['attribs']['']['end']))
10137 {
10138 $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
10139 }
10140 if (isset($caption['data']))
10141 {
10142 $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10143 }
10144 $captions_parent[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
10145 }
10146 }
10147 if (is_array($captions_parent))
10148 {
10149 $captions_parent = array_values(array_unique($captions_parent));
10150 }
10151
10152 // CATEGORIES
10153 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category)
10154 {
10155 $term = null;
10156 $scheme = null;
10157 $label = null;
10158 if (isset($category['data']))
10159 {
10160 $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10161 }
10162 if (isset($category['attribs']['']['scheme']))
10163 {
10164 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10165 }
10166 else
10167 {
10168 $scheme = 'http://search.yahoo.com/mrss/category_schema';
10169 }
10170 if (isset($category['attribs']['']['label']))
10171 {
10172 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
10173 }
10174 $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label));
10175 }
10176 foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category)
10177 {
10178 $term = null;
10179 $scheme = null;
10180 $label = null;
10181 if (isset($category['data']))
10182 {
10183 $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10184 }
10185 if (isset($category['attribs']['']['scheme']))
10186 {
10187 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10188 }
10189 else
10190 {
10191 $scheme = 'http://search.yahoo.com/mrss/category_schema';
10192 }
10193 if (isset($category['attribs']['']['label']))
10194 {
10195 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
10196 }
10197 $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label));
10198 }
10199 foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'category') as $category)
10200 {
10201 $term = null;
10202 $scheme = 'http://www.itunes.com/dtds/podcast-1.0.dtd';
10203 $label = null;
10204 if (isset($category['attribs']['']['text']))
10205 {
10206 $label = $this->sanitize($category['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT);
10207 }
10208 $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label));
10209
10210 if (isset($category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category']))
10211 {
10212 foreach ((array) $category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'] as $subcategory)
10213 {
10214 if (isset($subcategory['attribs']['']['text']))
10215 {
10216 $label = $this->sanitize($subcategory['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT);
10217 }
10218 $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label));
10219 }
10220 }
10221 }
10222 if (is_array($categories_parent))
10223 {
10224 $categories_parent = array_values(array_unique($categories_parent));
10225 }
10226
10227 // COPYRIGHT
10228 if ($copyright = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright'))
10229 {
10230 $copyright_url = null;
10231 $copyright_label = null;
10232 if (isset($copyright[0]['attribs']['']['url']))
10233 {
10234 $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
10235 }
10236 if (isset($copyright[0]['data']))
10237 {
10238 $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10239 }
10240 $copyrights_parent = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
10241 }
10242 elseif ($copyright = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright'))
10243 {
10244 $copyright_url = null;
10245 $copyright_label = null;
10246 if (isset($copyright[0]['attribs']['']['url']))
10247 {
10248 $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
10249 }
10250 if (isset($copyright[0]['data']))
10251 {
10252 $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10253 }
10254 $copyrights_parent = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
10255 }
10256
10257 // CREDITS
10258 if ($credits = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit'))
10259 {
10260 foreach ($credits as $credit)
10261 {
10262 $credit_role = null;
10263 $credit_scheme = null;
10264 $credit_name = null;
10265 if (isset($credit['attribs']['']['role']))
10266 {
10267 $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
10268 }
10269 if (isset($credit['attribs']['']['scheme']))
10270 {
10271 $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10272 }
10273 else
10274 {
10275 $credit_scheme = 'urn:ebu';
10276 }
10277 if (isset($credit['data']))
10278 {
10279 $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10280 }
10281 $credits_parent[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
10282 }
10283 }
10284 elseif ($credits = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit'))
10285 {
10286 foreach ($credits as $credit)
10287 {
10288 $credit_role = null;
10289 $credit_scheme = null;
10290 $credit_name = null;
10291 if (isset($credit['attribs']['']['role']))
10292 {
10293 $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
10294 }
10295 if (isset($credit['attribs']['']['scheme']))
10296 {
10297 $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10298 }
10299 else
10300 {
10301 $credit_scheme = 'urn:ebu';
10302 }
10303 if (isset($credit['data']))
10304 {
10305 $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10306 }
10307 $credits_parent[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
10308 }
10309 }
10310 if (is_array($credits_parent))
10311 {
10312 $credits_parent = array_values(array_unique($credits_parent));
10313 }
10314
10315 // DESCRIPTION
10316 if ($description_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description'))
10317 {
10318 if (isset($description_parent[0]['data']))
10319 {
10320 $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10321 }
10322 }
10323 elseif ($description_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description'))
10324 {
10325 if (isset($description_parent[0]['data']))
10326 {
10327 $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10328 }
10329 }
10330
10331 // DURATION
10332 if ($duration_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'duration'))
10333 {
10334 $seconds = null;
10335 $minutes = null;
10336 $hours = null;
10337 if (isset($duration_parent[0]['data']))
10338 {
10339 $temp = explode(':', $this->sanitize($duration_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
10340 if (sizeof($temp) > 0)
10341 {
10342 $seconds = (int) array_pop($temp);
10343 }
10344 if (sizeof($temp) > 0)
10345 {
10346 $minutes = (int) array_pop($temp);
10347 $seconds += $minutes * 60;
10348 }
10349 if (sizeof($temp) > 0)
10350 {
10351 $hours = (int) array_pop($temp);
10352 $seconds += $hours * 3600;
10353 }
10354 unset($temp);
10355 $duration_parent = $seconds;
10356 }
10357 }
10358
10359 // HASHES
10360 if ($hashes_iterator = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash'))
10361 {
10362 foreach ($hashes_iterator as $hash)
10363 {
10364 $value = null;
10365 $algo = null;
10366 if (isset($hash['data']))
10367 {
10368 $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10369 }
10370 if (isset($hash['attribs']['']['algo']))
10371 {
10372 $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
10373 }
10374 else
10375 {
10376 $algo = 'md5';
10377 }
10378 $hashes_parent[] = $algo.':'.$value;
10379 }
10380 }
10381 elseif ($hashes_iterator = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash'))
10382 {
10383 foreach ($hashes_iterator as $hash)
10384 {
10385 $value = null;
10386 $algo = null;
10387 if (isset($hash['data']))
10388 {
10389 $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10390 }
10391 if (isset($hash['attribs']['']['algo']))
10392 {
10393 $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
10394 }
10395 else
10396 {
10397 $algo = 'md5';
10398 }
10399 $hashes_parent[] = $algo.':'.$value;
10400 }
10401 }
10402 if (is_array($hashes_parent))
10403 {
10404 $hashes_parent = array_values(array_unique($hashes_parent));
10405 }
10406
10407 // KEYWORDS
10408 if ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords'))
10409 {
10410 if (isset($keywords[0]['data']))
10411 {
10412 $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
10413 foreach ($temp as $word)
10414 {
10415 $keywords_parent[] = trim($word);
10416 }
10417 }
10418 unset($temp);
10419 }
10420 elseif ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords'))
10421 {
10422 if (isset($keywords[0]['data']))
10423 {
10424 $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
10425 foreach ($temp as $word)
10426 {
10427 $keywords_parent[] = trim($word);
10428 }
10429 }
10430 unset($temp);
10431 }
10432 elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords'))
10433 {
10434 if (isset($keywords[0]['data']))
10435 {
10436 $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
10437 foreach ($temp as $word)
10438 {
10439 $keywords_parent[] = trim($word);
10440 }
10441 }
10442 unset($temp);
10443 }
10444 elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords'))
10445 {
10446 if (isset($keywords[0]['data']))
10447 {
10448 $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
10449 foreach ($temp as $word)
10450 {
10451 $keywords_parent[] = trim($word);
10452 }
10453 }
10454 unset($temp);
10455 }
10456 if (is_array($keywords_parent))
10457 {
10458 $keywords_parent = array_values(array_unique($keywords_parent));
10459 }
10460
10461 // PLAYER
10462 if ($player_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player'))
10463 {
10464 if (isset($player_parent[0]['attribs']['']['url']))
10465 {
10466 $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
10467 }
10468 }
10469 elseif ($player_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player'))
10470 {
10471 if (isset($player_parent[0]['attribs']['']['url']))
10472 {
10473 $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
10474 }
10475 }
10476
10477 // RATINGS
10478 if ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating'))
10479 {
10480 foreach ($ratings as $rating)
10481 {
10482 $rating_scheme = null;
10483 $rating_value = null;
10484 if (isset($rating['attribs']['']['scheme']))
10485 {
10486 $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10487 }
10488 else
10489 {
10490 $rating_scheme = 'urn:simple';
10491 }
10492 if (isset($rating['data']))
10493 {
10494 $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10495 }
10496 $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
10497 }
10498 }
10499 elseif ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit'))
10500 {
10501 foreach ($ratings as $rating)
10502 {
10503 $rating_scheme = 'urn:itunes';
10504 $rating_value = null;
10505 if (isset($rating['data']))
10506 {
10507 $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10508 }
10509 $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
10510 }
10511 }
10512 elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating'))
10513 {
10514 foreach ($ratings as $rating)
10515 {
10516 $rating_scheme = null;
10517 $rating_value = null;
10518 if (isset($rating['attribs']['']['scheme']))
10519 {
10520 $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10521 }
10522 else
10523 {
10524 $rating_scheme = 'urn:simple';
10525 }
10526 if (isset($rating['data']))
10527 {
10528 $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10529 }
10530 $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
10531 }
10532 }
10533 elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit'))
10534 {
10535 foreach ($ratings as $rating)
10536 {
10537 $rating_scheme = 'urn:itunes';
10538 $rating_value = null;
10539 if (isset($rating['data']))
10540 {
10541 $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10542 }
10543 $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
10544 }
10545 }
10546 if (is_array($ratings_parent))
10547 {
10548 $ratings_parent = array_values(array_unique($ratings_parent));
10549 }
10550
10551 // RESTRICTIONS
10552 if ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction'))
10553 {
10554 foreach ($restrictions as $restriction)
10555 {
10556 $restriction_relationship = null;
10557 $restriction_type = null;
10558 $restriction_value = null;
10559 if (isset($restriction['attribs']['']['relationship']))
10560 {
10561 $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
10562 }
10563 if (isset($restriction['attribs']['']['type']))
10564 {
10565 $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10566 }
10567 if (isset($restriction['data']))
10568 {
10569 $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10570 }
10571 $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
10572 }
10573 }
10574 elseif ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block'))
10575 {
10576 foreach ($restrictions as $restriction)
10577 {
10578 $restriction_relationship = 'allow';
10579 $restriction_type = null;
10580 $restriction_value = 'itunes';
10581 if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes')
10582 {
10583 $restriction_relationship = 'deny';
10584 }
10585 $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
10586 }
10587 }
10588 elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction'))
10589 {
10590 foreach ($restrictions as $restriction)
10591 {
10592 $restriction_relationship = null;
10593 $restriction_type = null;
10594 $restriction_value = null;
10595 if (isset($restriction['attribs']['']['relationship']))
10596 {
10597 $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
10598 }
10599 if (isset($restriction['attribs']['']['type']))
10600 {
10601 $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10602 }
10603 if (isset($restriction['data']))
10604 {
10605 $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10606 }
10607 $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
10608 }
10609 }
10610 elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block'))
10611 {
10612 foreach ($restrictions as $restriction)
10613 {
10614 $restriction_relationship = 'allow';
10615 $restriction_type = null;
10616 $restriction_value = 'itunes';
10617 if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes')
10618 {
10619 $restriction_relationship = 'deny';
10620 }
10621 $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
10622 }
10623 }
10624 if (is_array($restrictions_parent))
10625 {
10626 $restrictions_parent = array_values(array_unique($restrictions_parent));
10627 }
10628 else
10629 {
10630 $restrictions_parent = array(new SimplePie_Restriction('allow', null, 'default'));
10631 }
10632
10633 // THUMBNAILS
10634 if ($thumbnails = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail'))
10635 {
10636 foreach ($thumbnails as $thumbnail)
10637 {
10638 if (isset($thumbnail['attribs']['']['url']))
10639 {
10640 $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
10641 }
10642 }
10643 }
10644 elseif ($thumbnails = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail'))
10645 {
10646 foreach ($thumbnails as $thumbnail)
10647 {
10648 if (isset($thumbnail['attribs']['']['url']))
10649 {
10650 $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
10651 }
10652 }
10653 }
10654
10655 // TITLES
10656 if ($title_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title'))
10657 {
10658 if (isset($title_parent[0]['data']))
10659 {
10660 $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10661 }
10662 }
10663 elseif ($title_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title'))
10664 {
10665 if (isset($title_parent[0]['data']))
10666 {
10667 $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10668 }
10669 }
10670
10671 // Clear the memory
10672 unset($parent);
10673
10674 // Attributes
10675 $bitrate = null;
10676 $channels = null;
10677 $duration = null;
10678 $expression = null;
10679 $framerate = null;
10680 $height = null;
10681 $javascript = null;
10682 $lang = null;
10683 $length = null;
10684 $medium = null;
10685 $samplingrate = null;
10686 $type = null;
10687 $url = null;
10688 $width = null;
10689
10690 // Elements
10691 $captions = null;
10692 $categories = null;
10693 $copyrights = null;
10694 $credits = null;
10695 $description = null;
10696 $hashes = null;
10697 $keywords = null;
10698 $player = null;
10699 $ratings = null;
10700 $restrictions = null;
10701 $thumbnails = null;
10702 $title = null;
10703
10704 // If we have media:group tags, loop through them.
10705 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group') as $group)
10706 {
10707 if(isset($group['child']) && isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content']))
10708 {
10709 // If we have media:content tags, loop through them.
10710 foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content)
10711 {
10712 if (isset($content['attribs']['']['url']))
10713 {
10714 // Attributes
10715 $bitrate = null;
10716 $channels = null;
10717 $duration = null;
10718 $expression = null;
10719 $framerate = null;
10720 $height = null;
10721 $javascript = null;
10722 $lang = null;
10723 $length = null;
10724 $medium = null;
10725 $samplingrate = null;
10726 $type = null;
10727 $url = null;
10728 $width = null;
10729
10730 // Elements
10731 $captions = null;
10732 $categories = null;
10733 $copyrights = null;
10734 $credits = null;
10735 $description = null;
10736 $hashes = null;
10737 $keywords = null;
10738 $player = null;
10739 $ratings = null;
10740 $restrictions = null;
10741 $thumbnails = null;
10742 $title = null;
10743
10744 // Start checking the attributes of media:content
10745 if (isset($content['attribs']['']['bitrate']))
10746 {
10747 $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT);
10748 }
10749 if (isset($content['attribs']['']['channels']))
10750 {
10751 $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT);
10752 }
10753 if (isset($content['attribs']['']['duration']))
10754 {
10755 $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT);
10756 }
10757 else
10758 {
10759 $duration = $duration_parent;
10760 }
10761 if (isset($content['attribs']['']['expression']))
10762 {
10763 $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT);
10764 }
10765 if (isset($content['attribs']['']['framerate']))
10766 {
10767 $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT);
10768 }
10769 if (isset($content['attribs']['']['height']))
10770 {
10771 $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT);
10772 }
10773 if (isset($content['attribs']['']['lang']))
10774 {
10775 $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
10776 }
10777 if (isset($content['attribs']['']['fileSize']))
10778 {
10779 $length = ceil($content['attribs']['']['fileSize']);
10780 }
10781 if (isset($content['attribs']['']['medium']))
10782 {
10783 $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT);
10784 }
10785 if (isset($content['attribs']['']['samplingrate']))
10786 {
10787 $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT);
10788 }
10789 if (isset($content['attribs']['']['type']))
10790 {
10791 $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10792 }
10793 if (isset($content['attribs']['']['width']))
10794 {
10795 $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT);
10796 }
10797 $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
10798
10799 // Checking the other optional media: elements. Priority: media:content, media:group, item, channel
10800
10801 // CAPTIONS
10802 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text']))
10803 {
10804 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption)
10805 {
10806 $caption_type = null;
10807 $caption_lang = null;
10808 $caption_startTime = null;
10809 $caption_endTime = null;
10810 $caption_text = null;
10811 if (isset($caption['attribs']['']['type']))
10812 {
10813 $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10814 }
10815 if (isset($caption['attribs']['']['lang']))
10816 {
10817 $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
10818 }
10819 if (isset($caption['attribs']['']['start']))
10820 {
10821 $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
10822 }
10823 if (isset($caption['attribs']['']['end']))
10824 {
10825 $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
10826 }
10827 if (isset($caption['data']))
10828 {
10829 $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10830 }
10831 $captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
10832 }
10833 if (is_array($captions))
10834 {
10835 $captions = array_values(array_unique($captions));
10836 }
10837 }
10838 elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text']))
10839 {
10840 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption)
10841 {
10842 $caption_type = null;
10843 $caption_lang = null;
10844 $caption_startTime = null;
10845 $caption_endTime = null;
10846 $caption_text = null;
10847 if (isset($caption['attribs']['']['type']))
10848 {
10849 $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
10850 }
10851 if (isset($caption['attribs']['']['lang']))
10852 {
10853 $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
10854 }
10855 if (isset($caption['attribs']['']['start']))
10856 {
10857 $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
10858 }
10859 if (isset($caption['attribs']['']['end']))
10860 {
10861 $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
10862 }
10863 if (isset($caption['data']))
10864 {
10865 $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10866 }
10867 $captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
10868 }
10869 if (is_array($captions))
10870 {
10871 $captions = array_values(array_unique($captions));
10872 }
10873 }
10874 else
10875 {
10876 $captions = $captions_parent;
10877 }
10878
10879 // CATEGORIES
10880 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category']))
10881 {
10882 foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category)
10883 {
10884 $term = null;
10885 $scheme = null;
10886 $label = null;
10887 if (isset($category['data']))
10888 {
10889 $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10890 }
10891 if (isset($category['attribs']['']['scheme']))
10892 {
10893 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10894 }
10895 else
10896 {
10897 $scheme = 'http://search.yahoo.com/mrss/category_schema';
10898 }
10899 if (isset($category['attribs']['']['label']))
10900 {
10901 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
10902 }
10903 $categories[] = $this->registry->create('Category', array($term, $scheme, $label));
10904 }
10905 }
10906 if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category']))
10907 {
10908 foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category)
10909 {
10910 $term = null;
10911 $scheme = null;
10912 $label = null;
10913 if (isset($category['data']))
10914 {
10915 $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10916 }
10917 if (isset($category['attribs']['']['scheme']))
10918 {
10919 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10920 }
10921 else
10922 {
10923 $scheme = 'http://search.yahoo.com/mrss/category_schema';
10924 }
10925 if (isset($category['attribs']['']['label']))
10926 {
10927 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
10928 }
10929 $categories[] = $this->registry->create('Category', array($term, $scheme, $label));
10930 }
10931 }
10932 if (is_array($categories) && is_array($categories_parent))
10933 {
10934 $categories = array_values(array_unique(array_merge($categories, $categories_parent)));
10935 }
10936 elseif (is_array($categories))
10937 {
10938 $categories = array_values(array_unique($categories));
10939 }
10940 elseif (is_array($categories_parent))
10941 {
10942 $categories = array_values(array_unique($categories_parent));
10943 }
10944
10945 // COPYRIGHTS
10946 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright']))
10947 {
10948 $copyright_url = null;
10949 $copyright_label = null;
10950 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url']))
10951 {
10952 $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
10953 }
10954 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data']))
10955 {
10956 $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10957 }
10958 $copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
10959 }
10960 elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright']))
10961 {
10962 $copyright_url = null;
10963 $copyright_label = null;
10964 if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url']))
10965 {
10966 $copyright_url = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
10967 }
10968 if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data']))
10969 {
10970 $copyright_label = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
10971 }
10972 $copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
10973 }
10974 else
10975 {
10976 $copyrights = $copyrights_parent;
10977 }
10978
10979 // CREDITS
10980 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit']))
10981 {
10982 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit)
10983 {
10984 $credit_role = null;
10985 $credit_scheme = null;
10986 $credit_name = null;
10987 if (isset($credit['attribs']['']['role']))
10988 {
10989 $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
10990 }
10991 if (isset($credit['attribs']['']['scheme']))
10992 {
10993 $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
10994 }
10995 else
10996 {
10997 $credit_scheme = 'urn:ebu';
10998 }
10999 if (isset($credit['data']))
11000 {
11001 $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11002 }
11003 $credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
11004 }
11005 if (is_array($credits))
11006 {
11007 $credits = array_values(array_unique($credits));
11008 }
11009 }
11010 elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit']))
11011 {
11012 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit)
11013 {
11014 $credit_role = null;
11015 $credit_scheme = null;
11016 $credit_name = null;
11017 if (isset($credit['attribs']['']['role']))
11018 {
11019 $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
11020 }
11021 if (isset($credit['attribs']['']['scheme']))
11022 {
11023 $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11024 }
11025 else
11026 {
11027 $credit_scheme = 'urn:ebu';
11028 }
11029 if (isset($credit['data']))
11030 {
11031 $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11032 }
11033 $credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
11034 }
11035 if (is_array($credits))
11036 {
11037 $credits = array_values(array_unique($credits));
11038 }
11039 }
11040 else
11041 {
11042 $credits = $credits_parent;
11043 }
11044
11045 // DESCRIPTION
11046 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description']))
11047 {
11048 $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11049 }
11050 elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description']))
11051 {
11052 $description = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11053 }
11054 else
11055 {
11056 $description = $description_parent;
11057 }
11058
11059 // HASHES
11060 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash']))
11061 {
11062 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash)
11063 {
11064 $value = null;
11065 $algo = null;
11066 if (isset($hash['data']))
11067 {
11068 $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11069 }
11070 if (isset($hash['attribs']['']['algo']))
11071 {
11072 $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
11073 }
11074 else
11075 {
11076 $algo = 'md5';
11077 }
11078 $hashes[] = $algo.':'.$value;
11079 }
11080 if (is_array($hashes))
11081 {
11082 $hashes = array_values(array_unique($hashes));
11083 }
11084 }
11085 elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash']))
11086 {
11087 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash)
11088 {
11089 $value = null;
11090 $algo = null;
11091 if (isset($hash['data']))
11092 {
11093 $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11094 }
11095 if (isset($hash['attribs']['']['algo']))
11096 {
11097 $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
11098 }
11099 else
11100 {
11101 $algo = 'md5';
11102 }
11103 $hashes[] = $algo.':'.$value;
11104 }
11105 if (is_array($hashes))
11106 {
11107 $hashes = array_values(array_unique($hashes));
11108 }
11109 }
11110 else
11111 {
11112 $hashes = $hashes_parent;
11113 }
11114
11115 // KEYWORDS
11116 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords']))
11117 {
11118 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data']))
11119 {
11120 $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
11121 foreach ($temp as $word)
11122 {
11123 $keywords[] = trim($word);
11124 }
11125 unset($temp);
11126 }
11127 if (is_array($keywords))
11128 {
11129 $keywords = array_values(array_unique($keywords));
11130 }
11131 }
11132 elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords']))
11133 {
11134 if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data']))
11135 {
11136 $temp = explode(',', $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
11137 foreach ($temp as $word)
11138 {
11139 $keywords[] = trim($word);
11140 }
11141 unset($temp);
11142 }
11143 if (is_array($keywords))
11144 {
11145 $keywords = array_values(array_unique($keywords));
11146 }
11147 }
11148 else
11149 {
11150 $keywords = $keywords_parent;
11151 }
11152
11153 // PLAYER
11154 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
11155 {
11156 $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11157 }
11158 elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
11159 {
11160 $player = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11161 }
11162 else
11163 {
11164 $player = $player_parent;
11165 }
11166
11167 // RATINGS
11168 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating']))
11169 {
11170 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating)
11171 {
11172 $rating_scheme = null;
11173 $rating_value = null;
11174 if (isset($rating['attribs']['']['scheme']))
11175 {
11176 $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11177 }
11178 else
11179 {
11180 $rating_scheme = 'urn:simple';
11181 }
11182 if (isset($rating['data']))
11183 {
11184 $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11185 }
11186 $ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
11187 }
11188 if (is_array($ratings))
11189 {
11190 $ratings = array_values(array_unique($ratings));
11191 }
11192 }
11193 elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating']))
11194 {
11195 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating)
11196 {
11197 $rating_scheme = null;
11198 $rating_value = null;
11199 if (isset($rating['attribs']['']['scheme']))
11200 {
11201 $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11202 }
11203 else
11204 {
11205 $rating_scheme = 'urn:simple';
11206 }
11207 if (isset($rating['data']))
11208 {
11209 $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11210 }
11211 $ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
11212 }
11213 if (is_array($ratings))
11214 {
11215 $ratings = array_values(array_unique($ratings));
11216 }
11217 }
11218 else
11219 {
11220 $ratings = $ratings_parent;
11221 }
11222
11223 // RESTRICTIONS
11224 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction']))
11225 {
11226 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction)
11227 {
11228 $restriction_relationship = null;
11229 $restriction_type = null;
11230 $restriction_value = null;
11231 if (isset($restriction['attribs']['']['relationship']))
11232 {
11233 $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
11234 }
11235 if (isset($restriction['attribs']['']['type']))
11236 {
11237 $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11238 }
11239 if (isset($restriction['data']))
11240 {
11241 $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11242 }
11243 $restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
11244 }
11245 if (is_array($restrictions))
11246 {
11247 $restrictions = array_values(array_unique($restrictions));
11248 }
11249 }
11250 elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction']))
11251 {
11252 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction)
11253 {
11254 $restriction_relationship = null;
11255 $restriction_type = null;
11256 $restriction_value = null;
11257 if (isset($restriction['attribs']['']['relationship']))
11258 {
11259 $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
11260 }
11261 if (isset($restriction['attribs']['']['type']))
11262 {
11263 $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11264 }
11265 if (isset($restriction['data']))
11266 {
11267 $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11268 }
11269 $restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
11270 }
11271 if (is_array($restrictions))
11272 {
11273 $restrictions = array_values(array_unique($restrictions));
11274 }
11275 }
11276 else
11277 {
11278 $restrictions = $restrictions_parent;
11279 }
11280
11281 // THUMBNAILS
11282 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail']))
11283 {
11284 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail)
11285 {
11286 $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11287 }
11288 if (is_array($thumbnails))
11289 {
11290 $thumbnails = array_values(array_unique($thumbnails));
11291 }
11292 }
11293 elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail']))
11294 {
11295 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail)
11296 {
11297 $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11298 }
11299 if (is_array($thumbnails))
11300 {
11301 $thumbnails = array_values(array_unique($thumbnails));
11302 }
11303 }
11304 else
11305 {
11306 $thumbnails = $thumbnails_parent;
11307 }
11308
11309 // TITLES
11310 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title']))
11311 {
11312 $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11313 }
11314 elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title']))
11315 {
11316 $title = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11317 }
11318 else
11319 {
11320 $title = $title_parent;
11321 }
11322
11323 $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width));
11324 }
11325 }
11326 }
11327 }
11328
11329 // If we have standalone media:content tags, loop through them.
11330 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content']))
11331 {
11332 foreach ((array) $this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content)
11333 {
11334 if (isset($content['attribs']['']['url']) || isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
11335 {
11336 // Attributes
11337 $bitrate = null;
11338 $channels = null;
11339 $duration = null;
11340 $expression = null;
11341 $framerate = null;
11342 $height = null;
11343 $javascript = null;
11344 $lang = null;
11345 $length = null;
11346 $medium = null;
11347 $samplingrate = null;
11348 $type = null;
11349 $url = null;
11350 $width = null;
11351
11352 // Elements
11353 $captions = null;
11354 $categories = null;
11355 $copyrights = null;
11356 $credits = null;
11357 $description = null;
11358 $hashes = null;
11359 $keywords = null;
11360 $player = null;
11361 $ratings = null;
11362 $restrictions = null;
11363 $thumbnails = null;
11364 $title = null;
11365
11366 // Start checking the attributes of media:content
11367 if (isset($content['attribs']['']['bitrate']))
11368 {
11369 $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT);
11370 }
11371 if (isset($content['attribs']['']['channels']))
11372 {
11373 $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT);
11374 }
11375 if (isset($content['attribs']['']['duration']))
11376 {
11377 $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT);
11378 }
11379 else
11380 {
11381 $duration = $duration_parent;
11382 }
11383 if (isset($content['attribs']['']['expression']))
11384 {
11385 $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT);
11386 }
11387 if (isset($content['attribs']['']['framerate']))
11388 {
11389 $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT);
11390 }
11391 if (isset($content['attribs']['']['height']))
11392 {
11393 $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT);
11394 }
11395 if (isset($content['attribs']['']['lang']))
11396 {
11397 $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
11398 }
11399 if (isset($content['attribs']['']['fileSize']))
11400 {
11401 $length = ceil($content['attribs']['']['fileSize']);
11402 }
11403 if (isset($content['attribs']['']['medium']))
11404 {
11405 $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT);
11406 }
11407 if (isset($content['attribs']['']['samplingrate']))
11408 {
11409 $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT);
11410 }
11411 if (isset($content['attribs']['']['type']))
11412 {
11413 $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11414 }
11415 if (isset($content['attribs']['']['width']))
11416 {
11417 $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT);
11418 }
11419 if (isset($content['attribs']['']['url']))
11420 {
11421 $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11422 }
11423 // Checking the other optional media: elements. Priority: media:content, media:group, item, channel
11424
11425 // CAPTIONS
11426 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text']))
11427 {
11428 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption)
11429 {
11430 $caption_type = null;
11431 $caption_lang = null;
11432 $caption_startTime = null;
11433 $caption_endTime = null;
11434 $caption_text = null;
11435 if (isset($caption['attribs']['']['type']))
11436 {
11437 $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11438 }
11439 if (isset($caption['attribs']['']['lang']))
11440 {
11441 $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
11442 }
11443 if (isset($caption['attribs']['']['start']))
11444 {
11445 $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
11446 }
11447 if (isset($caption['attribs']['']['end']))
11448 {
11449 $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
11450 }
11451 if (isset($caption['data']))
11452 {
11453 $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11454 }
11455 $captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
11456 }
11457 if (is_array($captions))
11458 {
11459 $captions = array_values(array_unique($captions));
11460 }
11461 }
11462 else
11463 {
11464 $captions = $captions_parent;
11465 }
11466
11467 // CATEGORIES
11468 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category']))
11469 {
11470 foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category)
11471 {
11472 $term = null;
11473 $scheme = null;
11474 $label = null;
11475 if (isset($category['data']))
11476 {
11477 $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11478 }
11479 if (isset($category['attribs']['']['scheme']))
11480 {
11481 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11482 }
11483 else
11484 {
11485 $scheme = 'http://search.yahoo.com/mrss/category_schema';
11486 }
11487 if (isset($category['attribs']['']['label']))
11488 {
11489 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
11490 }
11491 $categories[] = $this->registry->create('Category', array($term, $scheme, $label));
11492 }
11493 }
11494 if (is_array($categories) && is_array($categories_parent))
11495 {
11496 $categories = array_values(array_unique(array_merge($categories, $categories_parent)));
11497 }
11498 elseif (is_array($categories))
11499 {
11500 $categories = array_values(array_unique($categories));
11501 }
11502 elseif (is_array($categories_parent))
11503 {
11504 $categories = array_values(array_unique($categories_parent));
11505 }
11506 else
11507 {
11508 $categories = null;
11509 }
11510
11511 // COPYRIGHTS
11512 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright']))
11513 {
11514 $copyright_url = null;
11515 $copyright_label = null;
11516 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url']))
11517 {
11518 $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
11519 }
11520 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data']))
11521 {
11522 $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11523 }
11524 $copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
11525 }
11526 else
11527 {
11528 $copyrights = $copyrights_parent;
11529 }
11530
11531 // CREDITS
11532 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit']))
11533 {
11534 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit)
11535 {
11536 $credit_role = null;
11537 $credit_scheme = null;
11538 $credit_name = null;
11539 if (isset($credit['attribs']['']['role']))
11540 {
11541 $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
11542 }
11543 if (isset($credit['attribs']['']['scheme']))
11544 {
11545 $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11546 }
11547 else
11548 {
11549 $credit_scheme = 'urn:ebu';
11550 }
11551 if (isset($credit['data']))
11552 {
11553 $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11554 }
11555 $credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
11556 }
11557 if (is_array($credits))
11558 {
11559 $credits = array_values(array_unique($credits));
11560 }
11561 }
11562 else
11563 {
11564 $credits = $credits_parent;
11565 }
11566
11567 // DESCRIPTION
11568 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description']))
11569 {
11570 $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11571 }
11572 else
11573 {
11574 $description = $description_parent;
11575 }
11576
11577 // HASHES
11578 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash']))
11579 {
11580 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash)
11581 {
11582 $value = null;
11583 $algo = null;
11584 if (isset($hash['data']))
11585 {
11586 $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11587 }
11588 if (isset($hash['attribs']['']['algo']))
11589 {
11590 $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
11591 }
11592 else
11593 {
11594 $algo = 'md5';
11595 }
11596 $hashes[] = $algo.':'.$value;
11597 }
11598 if (is_array($hashes))
11599 {
11600 $hashes = array_values(array_unique($hashes));
11601 }
11602 }
11603 else
11604 {
11605 $hashes = $hashes_parent;
11606 }
11607
11608 // KEYWORDS
11609 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords']))
11610 {
11611 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data']))
11612 {
11613 $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
11614 foreach ($temp as $word)
11615 {
11616 $keywords[] = trim($word);
11617 }
11618 unset($temp);
11619 }
11620 if (is_array($keywords))
11621 {
11622 $keywords = array_values(array_unique($keywords));
11623 }
11624 }
11625 else
11626 {
11627 $keywords = $keywords_parent;
11628 }
11629
11630 // PLAYER
11631 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
11632 {
11633 $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11634 }
11635 else
11636 {
11637 $player = $player_parent;
11638 }
11639
11640 // RATINGS
11641 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating']))
11642 {
11643 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating)
11644 {
11645 $rating_scheme = null;
11646 $rating_value = null;
11647 if (isset($rating['attribs']['']['scheme']))
11648 {
11649 $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
11650 }
11651 else
11652 {
11653 $rating_scheme = 'urn:simple';
11654 }
11655 if (isset($rating['data']))
11656 {
11657 $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11658 }
11659 $ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
11660 }
11661 if (is_array($ratings))
11662 {
11663 $ratings = array_values(array_unique($ratings));
11664 }
11665 }
11666 else
11667 {
11668 $ratings = $ratings_parent;
11669 }
11670
11671 // RESTRICTIONS
11672 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction']))
11673 {
11674 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction)
11675 {
11676 $restriction_relationship = null;
11677 $restriction_type = null;
11678 $restriction_value = null;
11679 if (isset($restriction['attribs']['']['relationship']))
11680 {
11681 $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
11682 }
11683 if (isset($restriction['attribs']['']['type']))
11684 {
11685 $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11686 }
11687 if (isset($restriction['data']))
11688 {
11689 $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11690 }
11691 $restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
11692 }
11693 if (is_array($restrictions))
11694 {
11695 $restrictions = array_values(array_unique($restrictions));
11696 }
11697 }
11698 else
11699 {
11700 $restrictions = $restrictions_parent;
11701 }
11702
11703 // THUMBNAILS
11704 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail']))
11705 {
11706 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail)
11707 {
11708 $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
11709 }
11710 if (is_array($thumbnails))
11711 {
11712 $thumbnails = array_values(array_unique($thumbnails));
11713 }
11714 }
11715 else
11716 {
11717 $thumbnails = $thumbnails_parent;
11718 }
11719
11720 // TITLES
11721 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title']))
11722 {
11723 $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
11724 }
11725 else
11726 {
11727 $title = $title_parent;
11728 }
11729
11730 $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width));
11731 }
11732 }
11733 }
11734
11735 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link)
11736 {
11737 if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure')
11738 {
11739 // Attributes
11740 $bitrate = null;
11741 $channels = null;
11742 $duration = null;
11743 $expression = null;
11744 $framerate = null;
11745 $height = null;
11746 $javascript = null;
11747 $lang = null;
11748 $length = null;
11749 $medium = null;
11750 $samplingrate = null;
11751 $type = null;
11752 $url = null;
11753 $width = null;
11754
11755 $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
11756 if (isset($link['attribs']['']['type']))
11757 {
11758 $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11759 }
11760 if (isset($link['attribs']['']['length']))
11761 {
11762 $length = ceil($link['attribs']['']['length']);
11763 }
11764
11765 // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
11766 $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width));
11767 }
11768 }
11769
11770 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link)
11771 {
11772 if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure')
11773 {
11774 // Attributes
11775 $bitrate = null;
11776 $channels = null;
11777 $duration = null;
11778 $expression = null;
11779 $framerate = null;
11780 $height = null;
11781 $javascript = null;
11782 $lang = null;
11783 $length = null;
11784 $medium = null;
11785 $samplingrate = null;
11786 $type = null;
11787 $url = null;
11788 $width = null;
11789
11790 $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
11791 if (isset($link['attribs']['']['type']))
11792 {
11793 $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11794 }
11795 if (isset($link['attribs']['']['length']))
11796 {
11797 $length = ceil($link['attribs']['']['length']);
11798 }
11799
11800 // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
11801 $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width));
11802 }
11803 }
11804
11805 if ($enclosure = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'enclosure'))
11806 {
11807 if (isset($enclosure[0]['attribs']['']['url']))
11808 {
11809 // Attributes
11810 $bitrate = null;
11811 $channels = null;
11812 $duration = null;
11813 $expression = null;
11814 $framerate = null;
11815 $height = null;
11816 $javascript = null;
11817 $lang = null;
11818 $length = null;
11819 $medium = null;
11820 $samplingrate = null;
11821 $type = null;
11822 $url = null;
11823 $width = null;
11824
11825 $url = $this->sanitize($enclosure[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($enclosure[0]));
11826 if (isset($enclosure[0]['attribs']['']['type']))
11827 {
11828 $type = $this->sanitize($enclosure[0]['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
11829 }
11830 if (isset($enclosure[0]['attribs']['']['length']))
11831 {
11832 $length = ceil($enclosure[0]['attribs']['']['length']);
11833 }
11834
11835 // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
11836 $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width));
11837 }
11838 }
11839
11840 if (sizeof($this->data['enclosures']) === 0 && ($url || $type || $length || $bitrate || $captions_parent || $categories_parent || $channels || $copyrights_parent || $credits_parent || $description_parent || $duration_parent || $expression || $framerate || $hashes_parent || $height || $keywords_parent || $lang || $medium || $player_parent || $ratings_parent || $restrictions_parent || $samplingrate || $thumbnails_parent || $title_parent || $width))
11841 {
11842 // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
11843 $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width));
11844 }
11845
11846 $this->data['enclosures'] = array_values(array_unique($this->data['enclosures']));
11847 }
11848 if (!empty($this->data['enclosures']))
11849 {
11850 return $this->data['enclosures'];
11851 }
11852 else
11853 {
11854 return null;
11855 }
11856 }
11857
11858 /**
11859 * Get the latitude coordinates for the item
11860 *
11861 * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
11862 *
11863 * Uses `<geo:lat>` or `<georss:point>`
11864 *
11865 * @since 1.0
11866 * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
11867 * @link http://www.georss.org/ GeoRSS
11868 * @return string|null
11869 */
11870 public function get_latitude()
11871 {
11872 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat'))
11873 {
11874 return (float) $return[0]['data'];
11875 }
11876 elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
11877 {
11878 return (float) $match[1];
11879 }
11880 else
11881 {
11882 return null;
11883 }
11884 }
11885
11886 /**
11887 * Get the longitude coordinates for the item
11888 *
11889 * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
11890 *
11891 * Uses `<geo:long>`, `<geo:lon>` or `<georss:point>`
11892 *
11893 * @since 1.0
11894 * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
11895 * @link http://www.georss.org/ GeoRSS
11896 * @return string|null
11897 */
11898 public function get_longitude()
11899 {
11900 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long'))
11901 {
11902 return (float) $return[0]['data'];
11903 }
11904 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon'))
11905 {
11906 return (float) $return[0]['data'];
11907 }
11908 elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
11909 {
11910 return (float) $match[2];
11911 }
11912 else
11913 {
11914 return null;
11915 }
11916 }
11917
11918 /**
11919 * Get the `<atom:source>` for the item
11920 *
11921 * @since 1.1
11922 * @return SimplePie_Source|null
11923 */
11924 public function get_source()
11925 {
11926 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'source'))
11927 {
11928 return $this->registry->create('Source', array($this, $return[0]));
11929 }
11930 else
11931 {
11932 return null;
11933 }
11934 }
11935 }
11936
11937 /**
11938 * Used for feed auto-discovery
11939 *
11940 *
11941 * This class can be overloaded with {@see SimplePie::set_locator_class()}
11942 *
11943 * @package SimplePie
11944 */
11945 class SimplePie_Locator
11946 {
11947 var $useragent;
11948 var $timeout;
11949 var $file;
11950 var $local = array();
11951 var $elsewhere = array();
11952 var $cached_entities = array();
11953 var $http_base;
11954 var $base;
11955 var $base_location = 0;
11956 var $checked_feeds = 0;
11957 var $max_checked_feeds = 10;
11958 protected $registry;
11959
11960 public function __construct(SimplePie_File $file, $timeout = 10, $useragent = null, $max_checked_feeds = 10)
11961 {
11962 $this->file = $file;
11963 $this->useragent = $useragent;
11964 $this->timeout = $timeout;
11965 $this->max_checked_feeds = $max_checked_feeds;
11966
11967 if (class_exists('DOMDocument'))
11968 {
11969 $this->dom = new DOMDocument();
11970
11971 set_error_handler(array('SimplePie_Misc', 'silence_errors'));
11972 $this->dom->loadHTML($this->file->body);
11973 restore_error_handler();
11974 }
11975 else
11976 {
11977 $this->dom = null;
11978 }
11979 }
11980
11981 public function set_registry(SimplePie_Registry $registry)
11982 {
11983 $this->registry = $registry;
11984 }
11985
11986 public function find($type = SIMPLEPIE_LOCATOR_ALL, &$working)
11987 {
11988 if ($this->is_feed($this->file))
11989 {
11990 return $this->file;
11991 }
11992
11993 if ($this->file->method & SIMPLEPIE_FILE_SOURCE_REMOTE)
11994 {
11995 $sniffer = $this->registry->create('Content_Type_Sniffer', array($this->file));
11996 if ($sniffer->get_type() !== 'text/html')
11997 {
11998 return null;
11999 }
12000 }
12001
12002 if ($type & ~SIMPLEPIE_LOCATOR_NONE)
12003 {
12004 $this->get_base();
12005 }
12006
12007 if ($type & SIMPLEPIE_LOCATOR_AUTODISCOVERY && $working = $this->autodiscovery())
12008 {
12009 return $working[0];
12010 }
12011
12012 if ($type & (SIMPLEPIE_LOCATOR_LOCAL_EXTENSION | SIMPLEPIE_LOCATOR_LOCAL_BODY | SIMPLEPIE_LOCATOR_REMOTE_EXTENSION | SIMPLEPIE_LOCATOR_REMOTE_BODY) && $this->get_links())
12013 {
12014 if ($type & SIMPLEPIE_LOCATOR_LOCAL_EXTENSION && $working = $this->extension($this->local))
12015 {
12016 return $working;
12017 }
12018
12019 if ($type & SIMPLEPIE_LOCATOR_LOCAL_BODY && $working = $this->body($this->local))
12020 {
12021 return $working;
12022 }
12023
12024 if ($type & SIMPLEPIE_LOCATOR_REMOTE_EXTENSION && $working = $this->extension($this->elsewhere))
12025 {
12026 return $working;
12027 }
12028
12029 if ($type & SIMPLEPIE_LOCATOR_REMOTE_BODY && $working = $this->body($this->elsewhere))
12030 {
12031 return $working;
12032 }
12033 }
12034 return null;
12035 }
12036
12037 public function is_feed($file)
12038 {
12039 if ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE)
12040 {
12041 $sniffer = $this->registry->create('Content_Type_Sniffer', array($file));
12042 $sniffed = $sniffer->get_type();
12043 if (in_array($sniffed, array('application/rss+xml', 'application/rdf+xml', 'text/rdf', 'application/atom+xml', 'text/xml', 'application/xml')))
12044 {
12045 return true;
12046 }
12047 else
12048 {
12049 return false;
12050 }
12051 }
12052 elseif ($file->method & SIMPLEPIE_FILE_SOURCE_LOCAL)
12053 {
12054 return true;
12055 }
12056 else
12057 {
12058 return false;
12059 }
12060 }
12061
12062 public function get_base()
12063 {
12064 if ($this->dom === null)
12065 {
12066 throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
12067 }
12068 $this->http_base = $this->file->url;
12069 $this->base = $this->http_base;
12070 $elements = $this->dom->getElementsByTagName('base');
12071 foreach ($elements as $element)
12072 {
12073 if ($element->hasAttribute('href'))
12074 {
12075 $base = $this->registry->call('Misc', 'absolutize_url', array(trim($element->getAttribute('href')), $this->http_base));
12076 if ($base === false)
12077 {
12078 continue;
12079 }
12080 $this->base = $base;
12081 $this->base_location = method_exists($element, 'getLineNo') ? $element->getLineNo() : 0;
12082 break;
12083 }
12084 }
12085 }
12086
12087 public function autodiscovery()
12088 {
12089 $done = array();
12090 $feeds = array();
12091 $feeds = array_merge($feeds, $this->search_elements_by_tag('link', $done, $feeds));
12092 $feeds = array_merge($feeds, $this->search_elements_by_tag('a', $done, $feeds));
12093 $feeds = array_merge($feeds, $this->search_elements_by_tag('area', $done, $feeds));
12094
12095 if (!empty($feeds))
12096 {
12097 return array_values($feeds);
12098 }
12099 else
12100 {
12101 return null;
12102 }
12103 }
12104
12105 protected function search_elements_by_tag($name, &$done, $feeds)
12106 {
12107 if ($this->dom === null)
12108 {
12109 throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
12110 }
12111
12112 $links = $this->dom->getElementsByTagName($name);
12113 foreach ($links as $link)
12114 {
12115 if ($this->checked_feeds === $this->max_checked_feeds)
12116 {
12117 break;
12118 }
12119 if ($link->hasAttribute('href') && $link->hasAttribute('rel'))
12120 {
12121 $rel = array_unique($this->registry->call('Misc', 'space_seperated_tokens', array(strtolower($link->getAttribute('rel')))));
12122 $line = method_exists($link, 'getLineNo') ? $link->getLineNo() : 1;
12123
12124 if ($this->base_location < $line)
12125 {
12126 $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->base));
12127 }
12128 else
12129 {
12130 $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->http_base));
12131 }
12132 if ($href === false)
12133 {
12134 continue;
12135 }
12136
12137 if (!in_array($href, $done) && in_array('feed', $rel) || (in_array('alternate', $rel) && !in_array('stylesheet', $rel) && $link->hasAttribute('type') && in_array(strtolower($this->registry->call('Misc', 'parse_mime', array($link->getAttribute('type')))), array('application/rss+xml', 'application/atom+xml'))) && !isset($feeds[$href]))
12138 {
12139 $this->checked_feeds++;
12140 $headers = array(
12141 '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',
12142 );
12143 $feed = $this->registry->create('File', array($href, $this->timeout, 5, $headers, $this->useragent));
12144 if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed))
12145 {
12146 $feeds[$href] = $feed;
12147 }
12148 }
12149 $done[] = $href;
12150 }
12151 }
12152
12153 return $feeds;
12154 }
12155
12156 public function get_links()
12157 {
12158 if ($this->dom === null)
12159 {
12160 throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
12161 }
12162
12163 $links = $this->dom->getElementsByTagName('a');
12164 foreach ($links as $link)
12165 {
12166 if ($link->hasAttribute('href'))
12167 {
12168 $href = trim($link->getAttribute('href'));
12169 $parsed = $this->registry->call('Misc', 'parse_url', array($href));
12170 if ($parsed['scheme'] === '' || preg_match('/^(http(s)|feed)?$/i', $parsed['scheme']))
12171 {
12172 if ($this->base_location < $link->getLineNo())
12173 {
12174 $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->base));
12175 }
12176 else
12177 {
12178 $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->http_base));
12179 }
12180 if ($href === false)
12181 {
12182 continue;
12183 }
12184
12185 $current = $this->registry->call('Misc', 'parse_url', array($this->file->url));
12186
12187 if ($parsed['authority'] === '' || $parsed['authority'] === $current['authority'])
12188 {
12189 $this->local[] = $href;
12190 }
12191 else
12192 {
12193 $this->elsewhere[] = $href;
12194 }
12195 }
12196 }
12197 }
12198 $this->local = array_unique($this->local);
12199 $this->elsewhere = array_unique($this->elsewhere);
12200 if (!empty($this->local) || !empty($this->elsewhere))
12201 {
12202 return true;
12203 }
12204 return null;
12205 }
12206
12207 public function extension(&$array)
12208 {
12209 foreach ($array as $key => $value)
12210 {
12211 if ($this->checked_feeds === $this->max_checked_feeds)
12212 {
12213 break;
12214 }
12215 if (in_array(strtolower(strrchr($value, '.')), array('.rss', '.rdf', '.atom', '.xml')))
12216 {
12217 $this->checked_feeds++;
12218
12219 $headers = array(
12220 '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',
12221 );
12222 $feed = $this->registry->create('File', array($value, $this->timeout, 5, $headers, $this->useragent));
12223 if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed))
12224 {
12225 return $feed;
12226 }
12227 else
12228 {
12229 unset($array[$key]);
12230 }
12231 }
12232 }
12233 return null;
12234 }
12235
12236 public function body(&$array)
12237 {
12238 foreach ($array as $key => $value)
12239 {
12240 if ($this->checked_feeds === $this->max_checked_feeds)
12241 {
12242 break;
12243 }
12244 if (preg_match('/(rss|rdf|atom|xml)/i', $value))
12245 {
12246 $this->checked_feeds++;
12247 $headers = array(
12248 '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',
12249 );
12250 $feed = $this->registry->create('File', array($value, $this->timeout, 5, null, $this->useragent));
12251 if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed))
12252 {
12253 return $feed;
12254 }
12255 else
12256 {
12257 unset($array[$key]);
12258 }
12259 }
12260 }
12261 return null;
12262 }
12263 }
12264
12265 /**
12266 * Miscellanous utilities
12267 *
12268 * @package SimplePie
12269 */
12270 class SimplePie_Misc
12271 {
12272 public static function time_hms($seconds)
12273 {
12274 $time = '';
12275
12276 $hours = floor($seconds / 3600);
12277 $remainder = $seconds % 3600;
12278 if ($hours > 0)
12279 {
12280 $time .= $hours.':';
12281 }
12282
12283 $minutes = floor($remainder / 60);
12284 $seconds = $remainder % 60;
12285 if ($minutes < 10 && $hours > 0)
12286 {
12287 $minutes = '0' . $minutes;
12288 }
12289 if ($seconds < 10)
12290 {
12291 $seconds = '0' . $seconds;
12292 }
12293
12294 $time .= $minutes.':';
12295 $time .= $seconds;
12296
12297 return $time;
12298 }
12299
12300 public static function absolutize_url($relative, $base)
12301 {
12302 $iri = SimplePie_IRI::absolutize(new SimplePie_IRI($base), $relative);
12303 if ($iri === false)
12304 {
12305 return false;
12306 }
12307 return $iri->get_uri();
12308 }
12309
12310 /**
12311 * Get a HTML/XML element from a HTML string
12312 *
12313 * @deprecated Use DOMDocument instead (parsing HTML with regex is bad!)
12314 * @param string $realname Element name (including namespace prefix if applicable)
12315 * @param string $string HTML document
12316 * @return array
12317 */
12318 public static function get_element($realname, $string)
12319 {
12320 $return = array();
12321 $name = preg_quote($realname, '/');
12322 if (preg_match_all("/<($name)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$name>|(\/)?>)/siU", $string, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE))
12323 {
12324 for ($i = 0, $total_matches = count($matches); $i < $total_matches; $i++)
12325 {
12326 $return[$i]['tag'] = $realname;
12327 $return[$i]['full'] = $matches[$i][0][0];
12328 $return[$i]['offset'] = $matches[$i][0][1];
12329 if (strlen($matches[$i][3][0]) <= 2)
12330 {
12331 $return[$i]['self_closing'] = true;
12332 }
12333 else
12334 {
12335 $return[$i]['self_closing'] = false;
12336 $return[$i]['content'] = $matches[$i][4][0];
12337 }
12338 $return[$i]['attribs'] = array();
12339 if (isset($matches[$i][2][0]) && preg_match_all('/[\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]*)?))?/', ' ' . $matches[$i][2][0] . ' ', $attribs, PREG_SET_ORDER))
12340 {
12341 for ($j = 0, $total_attribs = count($attribs); $j < $total_attribs; $j++)
12342 {
12343 if (count($attribs[$j]) === 2)
12344 {
12345 $attribs[$j][2] = $attribs[$j][1];
12346 }
12347 $return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = SimplePie_Misc::entities_decode(end($attribs[$j]), 'UTF-8');
12348 }
12349 }
12350 }
12351 }
12352 return $return;
12353 }
12354
12355 public static function element_implode($element)
12356 {
12357 $full = "<$element[tag]";
12358 foreach ($element['attribs'] as $key => $value)
12359 {
12360 $key = strtolower($key);
12361 $full .= " $key=\"" . htmlspecialchars($value['data']) . '"';
12362 }
12363 if ($element['self_closing'])
12364 {
12365 $full .= ' />';
12366 }
12367 else
12368 {
12369 $full .= ">$element[content]</$element[tag]>";
12370 }
12371 return $full;
12372 }
12373
12374 public static function error($message, $level, $file, $line)
12375 {
12376 if ((ini_get('error_reporting') & $level) > 0)
12377 {
12378 switch ($level)
12379 {
12380 case E_USER_ERROR:
12381 $note = 'PHP Error';
12382 break;
12383 case E_USER_WARNING:
12384 $note = 'PHP Warning';
12385 break;
12386 case E_USER_NOTICE:
12387 $note = 'PHP Notice';
12388 break;
12389 default:
12390 $note = 'Unknown Error';
12391 break;
12392 }
12393
12394 $log_error = true;
12395 if (!function_exists('error_log'))
12396 {
12397 $log_error = false;
12398 }
12399
12400 $log_file = @ini_get('error_log');
12401 if (!empty($log_file) && ('syslog' !== $log_file) && !@is_writable($log_file))
12402 {
12403 $log_error = false;
12404 }
12405
12406 if ($log_error)
12407 {
12408 @error_log("$note: $message in $file on line $line", 0);
12409 }
12410 }
12411
12412 return $message;
12413 }
12414
12415 public static function fix_protocol($url, $http = 1)
12416 {
12417 $url = SimplePie_Misc::normalize_url($url);
12418 $parsed = SimplePie_Misc::parse_url($url);
12419 if ($parsed['scheme'] !== '' && $parsed['scheme'] !== 'http' && $parsed['scheme'] !== 'https')
12420 {
12421 return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['authority'], $parsed['path'], $parsed['query'], $parsed['fragment']), $http);
12422 }
12423
12424 if ($parsed['scheme'] === '' && $parsed['authority'] === '' && !file_exists($url))
12425 {
12426 return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['path'], '', $parsed['query'], $parsed['fragment']), $http);
12427 }
12428
12429 if ($http === 2 && $parsed['scheme'] !== '')
12430 {
12431 return "feed:$url";
12432 }
12433 elseif ($http === 3 && strtolower($parsed['scheme']) === 'http')
12434 {
12435 return substr_replace($url, 'podcast', 0, 4);
12436 }
12437 elseif ($http === 4 && strtolower($parsed['scheme']) === 'http')
12438 {
12439 return substr_replace($url, 'itpc', 0, 4);
12440 }
12441 else
12442 {
12443 return $url;
12444 }
12445 }
12446
12447 public static function parse_url($url)
12448 {
12449 $iri = new SimplePie_IRI($url);
12450 return array(
12451 'scheme' => (string) $iri->scheme,
12452 'authority' => (string) $iri->authority,
12453 'path' => (string) $iri->path,
12454 'query' => (string) $iri->query,
12455 'fragment' => (string) $iri->fragment
12456 );
12457 }
12458
12459 public static function compress_parse_url($scheme = '', $authority = '', $path = '', $query = '', $fragment = '')
12460 {
12461 $iri = new SimplePie_IRI('');
12462 $iri->scheme = $scheme;
12463 $iri->authority = $authority;
12464 $iri->path = $path;
12465 $iri->query = $query;
12466 $iri->fragment = $fragment;
12467 return $iri->get_uri();
12468 }
12469
12470 public static function normalize_url($url)
12471 {
12472 $iri = new SimplePie_IRI($url);
12473 return $iri->get_uri();
12474 }
12475
12476 public static function percent_encoding_normalization($match)
12477 {
12478 $integer = hexdec($match[1]);
12479 if ($integer >= 0x41 && $integer <= 0x5A || $integer >= 0x61 && $integer <= 0x7A || $integer >= 0x30 && $integer <= 0x39 || $integer === 0x2D || $integer === 0x2E || $integer === 0x5F || $integer === 0x7E)
12480 {
12481 return chr($integer);
12482 }
12483 else
12484 {
12485 return strtoupper($match[0]);
12486 }
12487 }
12488
12489 /**
12490 * Converts a Windows-1252 encoded string to a UTF-8 encoded string
12491 *
12492 * @static
12493 * @param string $string Windows-1252 encoded string
12494 * @return string UTF-8 encoded string
12495 */
12496 public static function windows_1252_to_utf8($string)
12497 {
12498 static $convert_table = array("\x80" => "\xE2\x82\xAC", "\x81" => "\xEF\xBF\xBD", "\x82" => "\xE2\x80\x9A", "\x83" => "\xC6\x92", "\x84" => "\xE2\x80\x9E", "\x85" => "\xE2\x80\xA6", "\x86" => "\xE2\x80\xA0", "\x87" => "\xE2\x80\xA1", "\x88" => "\xCB\x86", "\x89" => "\xE2\x80\xB0", "\x8A" => "\xC5\xA0", "\x8B" => "\xE2\x80\xB9", "\x8C" => "\xC5\x92", "\x8D" => "\xEF\xBF\xBD", "\x8E" => "\xC5\xBD", "\x8F" => "\xEF\xBF\xBD", "\x90" => "\xEF\xBF\xBD", "\x91" => "\xE2\x80\x98", "\x92" => "\xE2\x80\x99", "\x93" => "\xE2\x80\x9C", "\x94" => "\xE2\x80\x9D", "\x95" => "\xE2\x80\xA2", "\x96" => "\xE2\x80\x93", "\x97" => "\xE2\x80\x94", "\x98" => "\xCB\x9C", "\x99" => "\xE2\x84\xA2", "\x9A" => "\xC5\xA1", "\x9B" => "\xE2\x80\xBA", "\x9C" => "\xC5\x93", "\x9D" => "\xEF\xBF\xBD", "\x9E" => "\xC5\xBE", "\x9F" => "\xC5\xB8", "\xA0" => "\xC2\xA0", "\xA1" => "\xC2\xA1", "\xA2" => "\xC2\xA2", "\xA3" => "\xC2\xA3", "\xA4" => "\xC2\xA4", "\xA5" => "\xC2\xA5", "\xA6" => "\xC2\xA6", "\xA7" => "\xC2\xA7", "\xA8" => "\xC2\xA8", "\xA9" => "\xC2\xA9", "\xAA" => "\xC2\xAA", "\xAB" => "\xC2\xAB", "\xAC" => "\xC2\xAC", "\xAD" => "\xC2\xAD", "\xAE" => "\xC2\xAE", "\xAF" => "\xC2\xAF", "\xB0" => "\xC2\xB0", "\xB1" => "\xC2\xB1", "\xB2" => "\xC2\xB2", "\xB3" => "\xC2\xB3", "\xB4" => "\xC2\xB4", "\xB5" => "\xC2\xB5", "\xB6" => "\xC2\xB6", "\xB7" => "\xC2\xB7", "\xB8" => "\xC2\xB8", "\xB9" => "\xC2\xB9", "\xBA" => "\xC2\xBA", "\xBB" => "\xC2\xBB", "\xBC" => "\xC2\xBC", "\xBD" => "\xC2\xBD", "\xBE" => "\xC2\xBE", "\xBF" => "\xC2\xBF", "\xC0" => "\xC3\x80", "\xC1" => "\xC3\x81", "\xC2" => "\xC3\x82", "\xC3" => "\xC3\x83", "\xC4" => "\xC3\x84", "\xC5" => "\xC3\x85", "\xC6" => "\xC3\x86", "\xC7" => "\xC3\x87", "\xC8" => "\xC3\x88", "\xC9" => "\xC3\x89", "\xCA" => "\xC3\x8A", "\xCB" => "\xC3\x8B", "\xCC" => "\xC3\x8C", "\xCD" => "\xC3\x8D", "\xCE" => "\xC3\x8E", "\xCF" => "\xC3\x8F", "\xD0" => "\xC3\x90", "\xD1" => "\xC3\x91", "\xD2" => "\xC3\x92", "\xD3" => "\xC3\x93", "\xD4" => "\xC3\x94", "\xD5" => "\xC3\x95", "\xD6" => "\xC3\x96", "\xD7" => "\xC3\x97", "\xD8" => "\xC3\x98", "\xD9" => "\xC3\x99", "\xDA" => "\xC3\x9A", "\xDB" => "\xC3\x9B", "\xDC" => "\xC3\x9C", "\xDD" => "\xC3\x9D", "\xDE" => "\xC3\x9E", "\xDF" => "\xC3\x9F", "\xE0" => "\xC3\xA0", "\xE1" => "\xC3\xA1", "\xE2" => "\xC3\xA2", "\xE3" => "\xC3\xA3", "\xE4" => "\xC3\xA4", "\xE5" => "\xC3\xA5", "\xE6" => "\xC3\xA6", "\xE7" => "\xC3\xA7", "\xE8" => "\xC3\xA8", "\xE9" => "\xC3\xA9", "\xEA" => "\xC3\xAA", "\xEB" => "\xC3\xAB", "\xEC" => "\xC3\xAC", "\xED" => "\xC3\xAD", "\xEE" => "\xC3\xAE", "\xEF" => "\xC3\xAF", "\xF0" => "\xC3\xB0", "\xF1" => "\xC3\xB1", "\xF2" => "\xC3\xB2", "\xF3" => "\xC3\xB3", "\xF4" => "\xC3\xB4", "\xF5" => "\xC3\xB5", "\xF6" => "\xC3\xB6", "\xF7" => "\xC3\xB7", "\xF8" => "\xC3\xB8", "\xF9" => "\xC3\xB9", "\xFA" => "\xC3\xBA", "\xFB" => "\xC3\xBB", "\xFC" => "\xC3\xBC", "\xFD" => "\xC3\xBD", "\xFE" => "\xC3\xBE", "\xFF" => "\xC3\xBF");
12499
12500 return strtr($string, $convert_table);
12501 }
12502
12503 /**
12504 * Change a string from one encoding to another
12505 *
12506 * @param string $data Raw data in $input encoding
12507 * @param string $input Encoding of $data
12508 * @param string $output Encoding you want
12509 * @return string|boolean False if we can't convert it
12510 */
12511 public static function change_encoding($data, $input, $output)
12512 {
12513 $input = SimplePie_Misc::encoding($input);
12514 $output = SimplePie_Misc::encoding($output);
12515
12516 // We fail to fail on non US-ASCII bytes
12517 if ($input === 'US-ASCII')
12518 {
12519 static $non_ascii_octects = '';
12520 if (!$non_ascii_octects)
12521 {
12522 for ($i = 0x80; $i <= 0xFF; $i++)
12523 {
12524 $non_ascii_octects .= chr($i);
12525 }
12526 }
12527 $data = substr($data, 0, strcspn($data, $non_ascii_octects));
12528 }
12529
12530 // This is first, as behaviour of this is completely predictable
12531 if ($input === 'windows-1252' && $output === 'UTF-8')
12532 {
12533 return SimplePie_Misc::windows_1252_to_utf8($data);
12534 }
12535 // This is second, as behaviour of this varies only with PHP version (the middle part of this expression checks the encoding is supported).
12536 elseif (function_exists('mb_convert_encoding') && ($return = SimplePie_Misc::change_encoding_mbstring($data, $input, $output)))
12537 {
12538 return $return;
12539 }
12540 // This is last, as behaviour of this varies with OS userland and PHP version
12541 elseif (function_exists('iconv') && ($return = SimplePie_Misc::change_encoding_iconv($data, $input, $output)))
12542 {
12543 return $return;
12544 }
12545 // If we can't do anything, just fail
12546 else
12547 {
12548 return false;
12549 }
12550 }
12551
12552 protected static function change_encoding_mbstring($data, $input, $output)
12553 {
12554 if ($input === 'windows-949')
12555 {
12556 $input = 'EUC-KR';
12557 }
12558 if ($output === 'windows-949')
12559 {
12560 $output = 'EUC-KR';
12561 }
12562 if ($input === 'Windows-31J')
12563 {
12564 $input = 'SJIS';
12565 }
12566 if ($output === 'Windows-31J')
12567 {
12568 $output = 'SJIS';
12569 }
12570
12571 // Check that the encoding is supported
12572 if (@mb_convert_encoding("\x80", 'UTF-16BE', $input) === "\x00\x80")
12573 {
12574 return false;
12575 }
12576 if (!in_array($input, mb_list_encodings()))
12577 {
12578 return false;
12579 }
12580
12581 // Let's do some conversion
12582 if ($return = @mb_convert_encoding($data, $output, $input))
12583 {
12584 return $return;
12585 }
12586
12587 return false;
12588 }
12589
12590 protected static function change_encoding_iconv($data, $input, $output)
12591 {
12592 return @iconv($input, $output, $data);
12593 }
12594
12595 /**
12596 * Normalize an encoding name
12597 *
12598 * This is automatically generated by create.php
12599 *
12600 * To generate it, run `php create.php` on the command line, and copy the
12601 * output to replace this function.
12602 *
12603 * @param string $charset Character set to standardise
12604 * @return string Standardised name
12605 */
12606 public static function encoding($charset)
12607 {
12608 // Normalization from UTS #22
12609 switch (strtolower(preg_replace('/(?:[^a-zA-Z0-9]+|([^0-9])0+)/', '\1', $charset)))
12610 {
12611 case 'adobestandardencoding':
12612 case 'csadobestandardencoding':
12613 return 'Adobe-Standard-Encoding';
12614
12615 case 'adobesymbolencoding':
12616 case 'cshppsmath':
12617 return 'Adobe-Symbol-Encoding';
12618
12619 case 'ami1251':
12620 case 'amiga1251':
12621 return 'Amiga-1251';
12622
12623 case 'ansix31101983':
12624 case 'csat5001983':
12625 case 'csiso99naplps':
12626 case 'isoir99':
12627 case 'naplps':
12628 return 'ANSI_X3.110-1983';
12629
12630 case 'arabic7':
12631 case 'asmo449':
12632 case 'csiso89asmo449':
12633 case 'iso9036':
12634 case 'isoir89':
12635 return 'ASMO_449';
12636
12637 case 'big5':
12638 case 'csbig5':
12639 return 'Big5';
12640
12641 case 'big5hkscs':
12642 return 'Big5-HKSCS';
12643
12644 case 'bocu1':
12645 case 'csbocu1':
12646 return 'BOCU-1';
12647
12648 case 'brf':
12649 case 'csbrf':
12650 return 'BRF';
12651
12652 case 'bs4730':
12653 case 'csiso4unitedkingdom':
12654 case 'gb':
12655 case 'iso646gb':
12656 case 'isoir4':
12657 case 'uk':
12658 return 'BS_4730';
12659
12660 case 'bsviewdata':
12661 case 'csiso47bsviewdata':
12662 case 'isoir47':
12663 return 'BS_viewdata';
12664
12665 case 'cesu8':
12666 case 'cscesu8':
12667 return 'CESU-8';
12668
12669 case 'ca':
12670 case 'csa71':
12671 case 'csaz243419851':
12672 case 'csiso121canadian1':
12673 case 'iso646ca':
12674 case 'isoir121':
12675 return 'CSA_Z243.4-1985-1';
12676
12677 case 'csa72':
12678 case 'csaz243419852':
12679 case 'csiso122canadian2':
12680 case 'iso646ca2':
12681 case 'isoir122':
12682 return 'CSA_Z243.4-1985-2';
12683
12684 case 'csaz24341985gr':
12685 case 'csiso123csaz24341985gr':
12686 case 'isoir123':
12687 return 'CSA_Z243.4-1985-gr';
12688
12689 case 'csiso139csn369103':
12690 case 'csn369103':
12691 case 'isoir139':
12692 return 'CSN_369103';
12693
12694 case 'csdecmcs':
12695 case 'dec':
12696 case 'decmcs':
12697 return 'DEC-MCS';
12698
12699 case 'csiso21german':
12700 case 'de':
12701 case 'din66003':
12702 case 'iso646de':
12703 case 'isoir21':
12704 return 'DIN_66003';
12705
12706 case 'csdkus':
12707 case 'dkus':
12708 return 'dk-us';
12709
12710 case 'csiso646danish':
12711 case 'dk':
12712 case 'ds2089':
12713 case 'iso646dk':
12714 return 'DS_2089';
12715
12716 case 'csibmebcdicatde':
12717 case 'ebcdicatde':
12718 return 'EBCDIC-AT-DE';
12719
12720 case 'csebcdicatdea':
12721 case 'ebcdicatdea':
12722 return 'EBCDIC-AT-DE-A';
12723
12724 case 'csebcdiccafr':
12725 case 'ebcdiccafr':
12726 return 'EBCDIC-CA-FR';
12727
12728 case 'csebcdicdkno':
12729 case 'ebcdicdkno':
12730 return 'EBCDIC-DK-NO';
12731
12732 case 'csebcdicdknoa':
12733 case 'ebcdicdknoa':
12734 return 'EBCDIC-DK-NO-A';
12735
12736 case 'csebcdices':
12737 case 'ebcdices':
12738 return 'EBCDIC-ES';
12739
12740 case 'csebcdicesa':
12741 case 'ebcdicesa':
12742 return 'EBCDIC-ES-A';
12743
12744 case 'csebcdicess':
12745 case 'ebcdicess':
12746 return 'EBCDIC-ES-S';
12747
12748 case 'csebcdicfise':
12749 case 'ebcdicfise':
12750 return 'EBCDIC-FI-SE';
12751
12752 case 'csebcdicfisea':
12753 case 'ebcdicfisea':
12754 return 'EBCDIC-FI-SE-A';
12755
12756 case 'csebcdicfr':
12757 case 'ebcdicfr':
12758 return 'EBCDIC-FR';
12759
12760 case 'csebcdicit':
12761 case 'ebcdicit':
12762 return 'EBCDIC-IT';
12763
12764 case 'csebcdicpt':
12765 case 'ebcdicpt':
12766 return 'EBCDIC-PT';
12767
12768 case 'csebcdicuk':
12769 case 'ebcdicuk':
12770 return 'EBCDIC-UK';
12771
12772 case 'csebcdicus':
12773 case 'ebcdicus':
12774 return 'EBCDIC-US';
12775
12776 case 'csiso111ecmacyrillic':
12777 case 'ecmacyrillic':
12778 case 'isoir111':
12779 case 'koi8e':
12780 return 'ECMA-cyrillic';
12781
12782 case 'csiso17spanish':
12783 case 'es':
12784 case 'iso646es':
12785 case 'isoir17':
12786 return 'ES';
12787
12788 case 'csiso85spanish2':
12789 case 'es2':
12790 case 'iso646es2':
12791 case 'isoir85':
12792 return 'ES2';
12793
12794 case 'cseucpkdfmtjapanese':
12795 case 'eucjp':
12796 case 'extendedunixcodepackedformatforjapanese':
12797 return 'EUC-JP';
12798
12799 case 'cseucfixwidjapanese':
12800 case 'extendedunixcodefixedwidthforjapanese':
12801 return 'Extended_UNIX_Code_Fixed_Width_for_Japanese';
12802
12803 case 'gb18030':
12804 return 'GB18030';
12805
12806 case 'chinese':
12807 case 'cp936':
12808 case 'csgb2312':
12809 case 'csiso58gb231280':
12810 case 'gb2312':
12811 case 'gb231280':
12812 case 'gbk':
12813 case 'isoir58':
12814 case 'ms936':
12815 case 'windows936':
12816 return 'GBK';
12817
12818 case 'cn':
12819 case 'csiso57gb1988':
12820 case 'gb198880':
12821 case 'iso646cn':
12822 case 'isoir57':
12823 return 'GB_1988-80';
12824
12825 case 'csiso153gost1976874':
12826 case 'gost1976874':
12827 case 'isoir153':
12828 case 'stsev35888':
12829 return 'GOST_19768-74';
12830
12831 case 'csiso150':
12832 case 'csiso150greekccitt':
12833 case 'greekccitt':
12834 case 'isoir150':
12835 return 'greek-ccitt';
12836
12837 case 'csiso88greek7':
12838 case 'greek7':
12839 case 'isoir88':
12840 return 'greek7';
12841
12842 case 'csiso18greek7old':
12843 case 'greek7old':
12844 case 'isoir18':
12845 return 'greek7-old';
12846
12847 case 'cshpdesktop':
12848 case 'hpdesktop':
12849 return 'HP-DeskTop';
12850
12851 case 'cshplegal':
12852 case 'hplegal':
12853 return 'HP-Legal';
12854
12855 case 'cshpmath8':
12856 case 'hpmath8':
12857 return 'HP-Math8';
12858
12859 case 'cshppifont':
12860 case 'hppifont':
12861 return 'HP-Pi-font';
12862
12863 case 'cshproman8':
12864 case 'hproman8':
12865 case 'r8':
12866 case 'roman8':
12867 return 'hp-roman8';
12868
12869 case 'hzgb2312':
12870 return 'HZ-GB-2312';
12871
12872 case 'csibmsymbols':
12873 case 'ibmsymbols':
12874 return 'IBM-Symbols';
12875
12876 case 'csibmthai':
12877 case 'ibmthai':
12878 return 'IBM-Thai';
12879
12880 case 'cp37':
12881 case 'csibm37':
12882 case 'ebcdiccpca':
12883 case 'ebcdiccpnl':
12884 case 'ebcdiccpus':
12885 case 'ebcdiccpwt':
12886 case 'ibm37':
12887 return 'IBM037';
12888
12889 case 'cp38':
12890 case 'csibm38':
12891 case 'ebcdicint':
12892 case 'ibm38':
12893 return 'IBM038';
12894
12895 case 'cp273':
12896 case 'csibm273':
12897 case 'ibm273':
12898 return 'IBM273';
12899
12900 case 'cp274':
12901 case 'csibm274':
12902 case 'ebcdicbe':
12903 case 'ibm274':
12904 return 'IBM274';
12905
12906 case 'cp275':
12907 case 'csibm275':
12908 case 'ebcdicbr':
12909 case 'ibm275':
12910 return 'IBM275';
12911
12912 case 'csibm277':
12913 case 'ebcdiccpdk':
12914 case 'ebcdiccpno':
12915 case 'ibm277':
12916 return 'IBM277';
12917
12918 case 'cp278':
12919 case 'csibm278':
12920 case 'ebcdiccpfi':
12921 case 'ebcdiccpse':
12922 case 'ibm278':
12923 return 'IBM278';
12924
12925 case 'cp280':
12926 case 'csibm280':
12927 case 'ebcdiccpit':
12928 case 'ibm280':
12929 return 'IBM280';
12930
12931 case 'cp281':
12932 case 'csibm281':
12933 case 'ebcdicjpe':
12934 case 'ibm281':
12935 return 'IBM281';
12936
12937 case 'cp284':
12938 case 'csibm284':
12939 case 'ebcdiccpes':
12940 case 'ibm284':
12941 return 'IBM284';
12942
12943 case 'cp285':
12944 case 'csibm285':
12945 case 'ebcdiccpgb':
12946 case 'ibm285':
12947 return 'IBM285';
12948
12949 case 'cp290':
12950 case 'csibm290':
12951 case 'ebcdicjpkana':
12952 case 'ibm290':
12953 return 'IBM290';
12954
12955 case 'cp297':
12956 case 'csibm297':
12957 case 'ebcdiccpfr':
12958 case 'ibm297':
12959 return 'IBM297';
12960
12961 case 'cp420':
12962 case 'csibm420':
12963 case 'ebcdiccpar1':
12964 case 'ibm420':
12965 return 'IBM420';
12966
12967 case 'cp423':
12968 case 'csibm423':
12969 case 'ebcdiccpgr':
12970 case 'ibm423':
12971 return 'IBM423';
12972
12973 case 'cp424':
12974 case 'csibm424':
12975 case 'ebcdiccphe':
12976 case 'ibm424':
12977 return 'IBM424';
12978
12979 case '437':
12980 case 'cp437':
12981 case 'cspc8codepage437':
12982 case 'ibm437':
12983 return 'IBM437';
12984
12985 case 'cp500':
12986 case 'csibm500':
12987 case 'ebcdiccpbe':
12988 case 'ebcdiccpch':
12989 case 'ibm500':
12990 return 'IBM500';
12991
12992 case 'cp775':
12993 case 'cspc775baltic':
12994 case 'ibm775':
12995 return 'IBM775';
12996
12997 case '850':
12998 case 'cp850':
12999 case 'cspc850multilingual':
13000 case 'ibm850':
13001 return 'IBM850';
13002
13003 case '851':
13004 case 'cp851':
13005 case 'csibm851':
13006 case 'ibm851':
13007 return 'IBM851';
13008
13009 case '852':
13010 case 'cp852':
13011 case 'cspcp852':
13012 case 'ibm852':
13013 return 'IBM852';
13014
13015 case '855':
13016 case 'cp855':
13017 case 'csibm855':
13018 case 'ibm855':
13019 return 'IBM855';
13020
13021 case '857':
13022 case 'cp857':
13023 case 'csibm857':
13024 case 'ibm857':
13025 return 'IBM857';
13026
13027 case 'ccsid858':
13028 case 'cp858':
13029 case 'ibm858':
13030 case 'pcmultilingual850euro':
13031 return 'IBM00858';
13032
13033 case '860':
13034 case 'cp860':
13035 case 'csibm860':
13036 case 'ibm860':
13037 return 'IBM860';
13038
13039 case '861':
13040 case 'cp861':
13041 case 'cpis':
13042 case 'csibm861':
13043 case 'ibm861':
13044 return 'IBM861';
13045
13046 case '862':
13047 case 'cp862':
13048 case 'cspc862latinhebrew':
13049 case 'ibm862':
13050 return 'IBM862';
13051
13052 case '863':
13053 case 'cp863':
13054 case 'csibm863':
13055 case 'ibm863':
13056 return 'IBM863';
13057
13058 case 'cp864':
13059 case 'csibm864':
13060 case 'ibm864':
13061 return 'IBM864';
13062
13063 case '865':
13064 case 'cp865':
13065 case 'csibm865':
13066 case 'ibm865':
13067 return 'IBM865';
13068
13069 case '866':
13070 case 'cp866':
13071 case 'csibm866':
13072 case 'ibm866':
13073 return 'IBM866';
13074
13075 case 'cp868':
13076 case 'cpar':
13077 case 'csibm868':
13078 case 'ibm868':
13079 return 'IBM868';
13080
13081 case '869':
13082 case 'cp869':
13083 case 'cpgr':
13084 case 'csibm869':
13085 case 'ibm869':
13086 return 'IBM869';
13087
13088 case 'cp870':
13089 case 'csibm870':
13090 case 'ebcdiccproece':
13091 case 'ebcdiccpyu':
13092 case 'ibm870':
13093 return 'IBM870';
13094
13095 case 'cp871':
13096 case 'csibm871':
13097 case 'ebcdiccpis':
13098 case 'ibm871':
13099 return 'IBM871';
13100
13101 case 'cp880':
13102 case 'csibm880':
13103 case 'ebcdiccyrillic':
13104 case 'ibm880':
13105 return 'IBM880';
13106
13107 case 'cp891':
13108 case 'csibm891':
13109 case 'ibm891':
13110 return 'IBM891';
13111
13112 case 'cp903':
13113 case 'csibm903':
13114 case 'ibm903':
13115 return 'IBM903';
13116
13117 case '904':
13118 case 'cp904':
13119 case 'csibbm904':
13120 case 'ibm904':
13121 return 'IBM904';
13122
13123 case 'cp905':
13124 case 'csibm905':
13125 case 'ebcdiccptr':
13126 case 'ibm905':
13127 return 'IBM905';
13128
13129 case 'cp918':
13130 case 'csibm918':
13131 case 'ebcdiccpar2':
13132 case 'ibm918':
13133 return 'IBM918';
13134
13135 case 'ccsid924':
13136 case 'cp924':
13137 case 'ebcdiclatin9euro':
13138 case 'ibm924':
13139 return 'IBM00924';
13140
13141 case 'cp1026':
13142 case 'csibm1026':
13143 case 'ibm1026':
13144 return 'IBM1026';
13145
13146 case 'ibm1047':
13147 return 'IBM1047';
13148
13149 case 'ccsid1140':
13150 case 'cp1140':
13151 case 'ebcdicus37euro':
13152 case 'ibm1140':
13153 return 'IBM01140';
13154
13155 case 'ccsid1141':
13156 case 'cp1141':
13157 case 'ebcdicde273euro':
13158 case 'ibm1141':
13159 return 'IBM01141';
13160
13161 case 'ccsid1142':
13162 case 'cp1142':
13163 case 'ebcdicdk277euro':
13164 case 'ebcdicno277euro':
13165 case 'ibm1142':
13166 return 'IBM01142';
13167
13168 case 'ccsid1143':
13169 case 'cp1143':
13170 case 'ebcdicfi278euro':
13171 case 'ebcdicse278euro':
13172 case 'ibm1143':
13173 return 'IBM01143';
13174
13175 case 'ccsid1144':
13176 case 'cp1144':
13177 case 'ebcdicit280euro':
13178 case 'ibm1144':
13179 return 'IBM01144';
13180
13181 case 'ccsid1145':
13182 case 'cp1145':
13183 case 'ebcdices284euro':
13184 case 'ibm1145':
13185 return 'IBM01145';
13186
13187 case 'ccsid1146':
13188 case 'cp1146':
13189 case 'ebcdicgb285euro':
13190 case 'ibm1146':
13191 return 'IBM01146';
13192
13193 case 'ccsid1147':
13194 case 'cp1147':
13195 case 'ebcdicfr297euro':
13196 case 'ibm1147':
13197 return 'IBM01147';
13198
13199 case 'ccsid1148':
13200 case 'cp1148':
13201 case 'ebcdicinternational500euro':
13202 case 'ibm1148':
13203 return 'IBM01148';
13204
13205 case 'ccsid1149':
13206 case 'cp1149':
13207 case 'ebcdicis871euro':
13208 case 'ibm1149':
13209 return 'IBM01149';
13210
13211 case 'csiso143iecp271':
13212 case 'iecp271':
13213 case 'isoir143':
13214 return 'IEC_P27-1';
13215
13216 case 'csiso49inis':
13217 case 'inis':
13218 case 'isoir49':
13219 return 'INIS';
13220
13221 case 'csiso50inis8':
13222 case 'inis8':
13223 case 'isoir50':
13224 return 'INIS-8';
13225
13226 case 'csiso51iniscyrillic':
13227 case 'iniscyrillic':
13228 case 'isoir51':
13229 return 'INIS-cyrillic';
13230
13231 case 'csinvariant':
13232 case 'invariant':
13233 return 'INVARIANT';
13234
13235 case 'iso2022cn':
13236 return 'ISO-2022-CN';
13237
13238 case 'iso2022cnext':
13239 return 'ISO-2022-CN-EXT';
13240
13241 case 'csiso2022jp':
13242 case 'iso2022jp':
13243 return 'ISO-2022-JP';
13244
13245 case 'csiso2022jp2':
13246 case 'iso2022jp2':
13247 return 'ISO-2022-JP-2';
13248
13249 case 'csiso2022kr':
13250 case 'iso2022kr':
13251 return 'ISO-2022-KR';
13252
13253 case 'cswindows30latin1':
13254 case 'iso88591windows30latin1':
13255 return 'ISO-8859-1-Windows-3.0-Latin-1';
13256
13257 case 'cswindows31latin1':
13258 case 'iso88591windows31latin1':
13259 return 'ISO-8859-1-Windows-3.1-Latin-1';
13260
13261 case 'csisolatin2':
13262 case 'iso88592':
13263 case 'iso885921987':
13264 case 'isoir101':
13265 case 'l2':
13266 case 'latin2':
13267 return 'ISO-8859-2';
13268
13269 case 'cswindows31latin2':
13270 case 'iso88592windowslatin2':
13271 return 'ISO-8859-2-Windows-Latin-2';
13272
13273 case 'csisolatin3':
13274 case 'iso88593':
13275 case 'iso885931988':
13276 case 'isoir109':
13277 case 'l3':
13278 case 'latin3':
13279 return 'ISO-8859-3';
13280
13281 case 'csisolatin4':
13282 case 'iso88594':
13283 case 'iso885941988':
13284 case 'isoir110':
13285 case 'l4':
13286 case 'latin4':
13287 return 'ISO-8859-4';
13288
13289 case 'csisolatincyrillic':
13290 case 'cyrillic':
13291 case 'iso88595':
13292 case 'iso885951988':
13293 case 'isoir144':
13294 return 'ISO-8859-5';
13295
13296 case 'arabic':
13297 case 'asmo708':
13298 case 'csisolatinarabic':
13299 case 'ecma114':
13300 case 'iso88596':
13301 case 'iso885961987':
13302 case 'isoir127':
13303 return 'ISO-8859-6';
13304
13305 case 'csiso88596e':
13306 case 'iso88596e':
13307 return 'ISO-8859-6-E';
13308
13309 case 'csiso88596i':
13310 case 'iso88596i':
13311 return 'ISO-8859-6-I';
13312
13313 case 'csisolatingreek':
13314 case 'ecma118':
13315 case 'elot928':
13316 case 'greek':
13317 case 'greek8':
13318 case 'iso88597':
13319 case 'iso885971987':
13320 case 'isoir126':
13321 return 'ISO-8859-7';
13322
13323 case 'csisolatinhebrew':
13324 case 'hebrew':
13325 case 'iso88598':
13326 case 'iso885981988':
13327 case 'isoir138':
13328 return 'ISO-8859-8';
13329
13330 case 'csiso88598e':
13331 case 'iso88598e':
13332 return 'ISO-8859-8-E';
13333
13334 case 'csiso88598i':
13335 case 'iso88598i':
13336 return 'ISO-8859-8-I';
13337
13338 case 'cswindows31latin5':
13339 case 'iso88599windowslatin5':
13340 return 'ISO-8859-9-Windows-Latin-5';
13341
13342 case 'csisolatin6':
13343 case 'iso885910':
13344 case 'iso8859101992':
13345 case 'isoir157':
13346 case 'l6':
13347 case 'latin6':
13348 return 'ISO-8859-10';
13349
13350 case 'iso885913':
13351 return 'ISO-8859-13';
13352
13353 case 'iso885914':
13354 case 'iso8859141998':
13355 case 'isoceltic':
13356 case 'isoir199':
13357 case 'l8':
13358 case 'latin8':
13359 return 'ISO-8859-14';
13360
13361 case 'iso885915':
13362 case 'latin9':
13363 return 'ISO-8859-15';
13364
13365 case 'iso885916':
13366 case 'iso8859162001':
13367 case 'isoir226':
13368 case 'l10':
13369 case 'latin10':
13370 return 'ISO-8859-16';
13371
13372 case 'iso10646j1':
13373 return 'ISO-10646-J-1';
13374
13375 case 'csunicode':
13376 case 'iso10646ucs2':
13377 return 'ISO-10646-UCS-2';
13378
13379 case 'csucs4':
13380 case 'iso10646ucs4':
13381 return 'ISO-10646-UCS-4';
13382
13383 case 'csunicodeascii':
13384 case 'iso10646ucsbasic':
13385 return 'ISO-10646-UCS-Basic';
13386
13387 case 'csunicodelatin1':
13388 case 'iso10646':
13389 case 'iso10646unicodelatin1':
13390 return 'ISO-10646-Unicode-Latin1';
13391
13392 case 'csiso10646utf1':
13393 case 'iso10646utf1':
13394 return 'ISO-10646-UTF-1';
13395
13396 case 'csiso115481':
13397 case 'iso115481':
13398 case 'isotr115481':
13399 return 'ISO-11548-1';
13400
13401 case 'csiso90':
13402 case 'isoir90':
13403 return 'iso-ir-90';
13404
13405 case 'csunicodeibm1261':
13406 case 'isounicodeibm1261':
13407 return 'ISO-Unicode-IBM-1261';
13408
13409 case 'csunicodeibm1264':
13410 case 'isounicodeibm1264':
13411 return 'ISO-Unicode-IBM-1264';
13412
13413 case 'csunicodeibm1265':
13414 case 'isounicodeibm1265':
13415 return 'ISO-Unicode-IBM-1265';
13416
13417 case 'csunicodeibm1268':
13418 case 'isounicodeibm1268':
13419 return 'ISO-Unicode-IBM-1268';
13420
13421 case 'csunicodeibm1276':
13422 case 'isounicodeibm1276':
13423 return 'ISO-Unicode-IBM-1276';
13424
13425 case 'csiso646basic1983':
13426 case 'iso646basic1983':
13427 case 'ref':
13428 return 'ISO_646.basic:1983';
13429
13430 case 'csiso2intlrefversion':
13431 case 'irv':
13432 case 'iso646irv1983':
13433 case 'isoir2':
13434 return 'ISO_646.irv:1983';
13435
13436 case 'csiso2033':
13437 case 'e13b':
13438 case 'iso20331983':
13439 case 'isoir98':
13440 return 'ISO_2033-1983';
13441
13442 case 'csiso5427cyrillic':
13443 case 'iso5427':
13444 case 'isoir37':
13445 return 'ISO_5427';
13446
13447 case 'iso5427cyrillic1981':
13448 case 'iso54271981':
13449 case 'isoir54':
13450 return 'ISO_5427:1981';
13451
13452 case 'csiso5428greek':
13453 case 'iso54281980':
13454 case 'isoir55':
13455 return 'ISO_5428:1980';
13456
13457 case 'csiso6937add':
13458 case 'iso6937225':
13459 case 'isoir152':
13460 return 'ISO_6937-2-25';
13461
13462 case 'csisotextcomm':
13463 case 'iso69372add':
13464 case 'isoir142':
13465 return 'ISO_6937-2-add';
13466
13467 case 'csiso8859supp':
13468 case 'iso8859supp':
13469 case 'isoir154':
13470 case 'latin125':
13471 return 'ISO_8859-supp';
13472
13473 case 'csiso10367box':
13474 case 'iso10367box':
13475 case 'isoir155':
13476 return 'ISO_10367-box';
13477
13478 case 'csiso15italian':
13479 case 'iso646it':
13480 case 'isoir15':
13481 case 'it':
13482 return 'IT';
13483
13484 case 'csiso13jisc6220jp':
13485 case 'isoir13':
13486 case 'jisc62201969':
13487 case 'jisc62201969jp':
13488 case 'katakana':
13489 case 'x2017':
13490 return 'JIS_C6220-1969-jp';
13491
13492 case 'csiso14jisc6220ro':
13493 case 'iso646jp':
13494 case 'isoir14':
13495 case 'jisc62201969ro':
13496 case 'jp':
13497 return 'JIS_C6220-1969-ro';
13498
13499 case 'csiso42jisc62261978':
13500 case 'isoir42':
13501 case 'jisc62261978':
13502 return 'JIS_C6226-1978';
13503
13504 case 'csiso87jisx208':
13505 case 'isoir87':
13506 case 'jisc62261983':
13507 case 'jisx2081983':
13508 case 'x208':
13509 return 'JIS_C6226-1983';
13510
13511 case 'csiso91jisc62291984a':
13512 case 'isoir91':
13513 case 'jisc62291984a':
13514 case 'jpocra':
13515 return 'JIS_C6229-1984-a';
13516
13517 case 'csiso92jisc62991984b':
13518 case 'iso646jpocrb':
13519 case 'isoir92':
13520 case 'jisc62291984b':
13521 case 'jpocrb':
13522 return 'JIS_C6229-1984-b';
13523
13524 case 'csiso93jis62291984badd':
13525 case 'isoir93':
13526 case 'jisc62291984badd':
13527 case 'jpocrbadd':
13528 return 'JIS_C6229-1984-b-add';
13529
13530 case 'csiso94jis62291984hand':
13531 case 'isoir94':
13532 case 'jisc62291984hand':
13533 case 'jpocrhand':
13534 return 'JIS_C6229-1984-hand';
13535
13536 case 'csiso95jis62291984handadd':
13537 case 'isoir95':
13538 case 'jisc62291984handadd':
13539 case 'jpocrhandadd':
13540 return 'JIS_C6229-1984-hand-add';
13541
13542 case 'csiso96jisc62291984kana':
13543 case 'isoir96':
13544 case 'jisc62291984kana':
13545 return 'JIS_C6229-1984-kana';
13546
13547 case 'csjisencoding':
13548 case 'jisencoding':
13549 return 'JIS_Encoding';
13550
13551 case 'cshalfwidthkatakana':
13552 case 'jisx201':
13553 case 'x201':
13554 return 'JIS_X0201';
13555
13556 case 'csiso159jisx2121990':
13557 case 'isoir159':
13558 case 'jisx2121990':
13559 case 'x212':
13560 return 'JIS_X0212-1990';
13561
13562 case 'csiso141jusib1002':
13563 case 'iso646yu':
13564 case 'isoir141':
13565 case 'js':
13566 case 'jusib1002':
13567 case 'yu':
13568 return 'JUS_I.B1.002';
13569
13570 case 'csiso147macedonian':
13571 case 'isoir147':
13572 case 'jusib1003mac':
13573 case 'macedonian':
13574 return 'JUS_I.B1.003-mac';
13575
13576 case 'csiso146serbian':
13577 case 'isoir146':
13578 case 'jusib1003serb':
13579 case 'serbian':
13580 return 'JUS_I.B1.003-serb';
13581
13582 case 'koi7switched':
13583 return 'KOI7-switched';
13584
13585 case 'cskoi8r':
13586 case 'koi8r':
13587 return 'KOI8-R';
13588
13589 case 'koi8u':
13590 return 'KOI8-U';
13591
13592 case 'csksc5636':
13593 case 'iso646kr':
13594 case 'ksc5636':
13595 return 'KSC5636';
13596
13597 case 'cskz1048':
13598 case 'kz1048':
13599 case 'rk1048':
13600 case 'strk10482002':
13601 return 'KZ-1048';
13602
13603 case 'csiso19latingreek':
13604 case 'isoir19':
13605 case 'latingreek':
13606 return 'latin-greek';
13607
13608 case 'csiso27latingreek1':
13609 case 'isoir27':
13610 case 'latingreek1':
13611 return 'Latin-greek-1';
13612
13613 case 'csiso158lap':
13614 case 'isoir158':
13615 case 'lap':
13616 case 'latinlap':
13617 return 'latin-lap';
13618
13619 case 'csmacintosh':
13620 case 'mac':
13621 case 'macintosh':
13622 return 'macintosh';
13623
13624 case 'csmicrosoftpublishing':
13625 case 'microsoftpublishing':
13626 return 'Microsoft-Publishing';
13627
13628 case 'csmnem':
13629 case 'mnem':
13630 return 'MNEM';
13631
13632 case 'csmnemonic':
13633 case 'mnemonic':
13634 return 'MNEMONIC';
13635
13636 case 'csiso86hungarian':
13637 case 'hu':
13638 case 'iso646hu':
13639 case 'isoir86':
13640 case 'msz77953':
13641 return 'MSZ_7795.3';
13642
13643 case 'csnatsdano':
13644 case 'isoir91':
13645 case 'natsdano':
13646 return 'NATS-DANO';
13647
13648 case 'csnatsdanoadd':
13649 case 'isoir92':
13650 case 'natsdanoadd':
13651 return 'NATS-DANO-ADD';
13652
13653 case 'csnatssefi':
13654 case 'isoir81':
13655 case 'natssefi':
13656 return 'NATS-SEFI';
13657
13658 case 'csnatssefiadd':
13659 case 'isoir82':
13660 case 'natssefiadd':
13661 return 'NATS-SEFI-ADD';
13662
13663 case 'csiso151cuba':
13664 case 'cuba':
13665 case 'iso646cu':
13666 case 'isoir151':
13667 case 'ncnc1081':
13668 return 'NC_NC00-10:81';
13669
13670 case 'csiso69french':
13671 case 'fr':
13672 case 'iso646fr':
13673 case 'isoir69':
13674 case 'nfz62010':
13675 return 'NF_Z_62-010';
13676
13677 case 'csiso25french':
13678 case 'iso646fr1':
13679 case 'isoir25':
13680 case 'nfz620101973':
13681 return 'NF_Z_62-010_(1973)';
13682
13683 case 'csiso60danishnorwegian':
13684 case 'csiso60norwegian1':
13685 case 'iso646no':
13686 case 'isoir60':
13687 case 'no':
13688 case 'ns45511':
13689 return 'NS_4551-1';
13690
13691 case 'csiso61norwegian2':
13692 case 'iso646no2':
13693 case 'isoir61':
13694 case 'no2':
13695 case 'ns45512':
13696 return 'NS_4551-2';
13697
13698 case 'osdebcdicdf3irv':
13699 return 'OSD_EBCDIC_DF03_IRV';
13700
13701 case 'osdebcdicdf41':
13702 return 'OSD_EBCDIC_DF04_1';
13703
13704 case 'osdebcdicdf415':
13705 return 'OSD_EBCDIC_DF04_15';
13706
13707 case 'cspc8danishnorwegian':
13708 case 'pc8danishnorwegian':
13709 return 'PC8-Danish-Norwegian';
13710
13711 case 'cspc8turkish':
13712 case 'pc8turkish':
13713 return 'PC8-Turkish';
13714
13715 case 'csiso16portuguese':
13716 case 'iso646pt':
13717 case 'isoir16':
13718 case 'pt':
13719 return 'PT';
13720
13721 case 'csiso84portuguese2':
13722 case 'iso646pt2':
13723 case 'isoir84':
13724 case 'pt2':
13725 return 'PT2';
13726
13727 case 'cp154':
13728 case 'csptcp154':
13729 case 'cyrillicasian':
13730 case 'pt154':
13731 case 'ptcp154':
13732 return 'PTCP154';
13733
13734 case 'scsu':
13735 return 'SCSU';
13736
13737 case 'csiso10swedish':
13738 case 'fi':
13739 case 'iso646fi':
13740 case 'iso646se':
13741 case 'isoir10':
13742 case 'se':
13743 case 'sen850200b':
13744 return 'SEN_850200_B';
13745
13746 case 'csiso11swedishfornames':
13747 case 'iso646se2':
13748 case 'isoir11':
13749 case 'se2':
13750 case 'sen850200c':
13751 return 'SEN_850200_C';
13752
13753 case 'csiso102t617bit':
13754 case 'isoir102':
13755 case 't617bit':
13756 return 'T.61-7bit';
13757
13758 case 'csiso103t618bit':
13759 case 'isoir103':
13760 case 't61':
13761 case 't618bit':
13762 return 'T.61-8bit';
13763
13764 case 'csiso128t101g2':
13765 case 'isoir128':
13766 case 't101g2':
13767 return 'T.101-G2';
13768
13769 case 'cstscii':
13770 case 'tscii':
13771 return 'TSCII';
13772
13773 case 'csunicode11':
13774 case 'unicode11':
13775 return 'UNICODE-1-1';
13776
13777 case 'csunicode11utf7':
13778 case 'unicode11utf7':
13779 return 'UNICODE-1-1-UTF-7';
13780
13781 case 'csunknown8bit':
13782 case 'unknown8bit':
13783 return 'UNKNOWN-8BIT';
13784
13785 case 'ansix341968':
13786 case 'ansix341986':
13787 case 'ascii':
13788 case 'cp367':
13789 case 'csascii':
13790 case 'ibm367':
13791 case 'iso646irv1991':
13792 case 'iso646us':
13793 case 'isoir6':
13794 case 'us':
13795 case 'usascii':
13796 return 'US-ASCII';
13797
13798 case 'csusdk':
13799 case 'usdk':
13800 return 'us-dk';
13801
13802 case 'utf7':
13803 return 'UTF-7';
13804
13805 case 'utf8':
13806 return 'UTF-8';
13807
13808 case 'utf16':
13809 return 'UTF-16';
13810
13811 case 'utf16be':
13812 return 'UTF-16BE';
13813
13814 case 'utf16le':
13815 return 'UTF-16LE';
13816
13817 case 'utf32':
13818 return 'UTF-32';
13819
13820 case 'utf32be':
13821 return 'UTF-32BE';
13822
13823 case 'utf32le':
13824 return 'UTF-32LE';
13825
13826 case 'csventurainternational':
13827 case 'venturainternational':
13828 return 'Ventura-International';
13829
13830 case 'csventuramath':
13831 case 'venturamath':
13832 return 'Ventura-Math';
13833
13834 case 'csventuraus':
13835 case 'venturaus':
13836 return 'Ventura-US';
13837
13838 case 'csiso70videotexsupp1':
13839 case 'isoir70':
13840 case 'videotexsuppl':
13841 return 'videotex-suppl';
13842
13843 case 'csviqr':
13844 case 'viqr':
13845 return 'VIQR';
13846
13847 case 'csviscii':
13848 case 'viscii':
13849 return 'VISCII';
13850
13851 case 'csshiftjis':
13852 case 'cswindows31j':
13853 case 'mskanji':
13854 case 'shiftjis':
13855 case 'windows31j':
13856 return 'Windows-31J';
13857
13858 case 'iso885911':
13859 case 'tis620':
13860 return 'windows-874';
13861
13862 case 'cseuckr':
13863 case 'csksc56011987':
13864 case 'euckr':
13865 case 'isoir149':
13866 case 'korean':
13867 case 'ksc5601':
13868 case 'ksc56011987':
13869 case 'ksc56011989':
13870 case 'windows949':
13871 return 'windows-949';
13872
13873 case 'windows1250':
13874 return 'windows-1250';
13875
13876 case 'windows1251':
13877 return 'windows-1251';
13878
13879 case 'cp819':
13880 case 'csisolatin1':
13881 case 'ibm819':
13882 case 'iso88591':
13883 case 'iso885911987':
13884 case 'isoir100':
13885 case 'l1':
13886 case 'latin1':
13887 case 'windows1252':
13888 return 'windows-1252';
13889
13890 case 'windows1253':
13891 return 'windows-1253';
13892
13893 case 'csisolatin5':
13894 case 'iso88599':
13895 case 'iso885991989':
13896 case 'isoir148':
13897 case 'l5':
13898 case 'latin5':
13899 case 'windows1254':
13900 return 'windows-1254';
13901
13902 case 'windows1255':
13903 return 'windows-1255';
13904
13905 case 'windows1256':
13906 return 'windows-1256';
13907
13908 case 'windows1257':
13909 return 'windows-1257';
13910
13911 case 'windows1258':
13912 return 'windows-1258';
13913
13914 default:
13915 return $charset;
13916 }
13917 }
13918
13919 public static function get_curl_version()
13920 {
13921 if (is_array($curl = curl_version()))
13922 {
13923 $curl = $curl['version'];
13924 }
13925 elseif (substr($curl, 0, 5) === 'curl/')
13926 {
13927 $curl = substr($curl, 5, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 5));
13928 }
13929 elseif (substr($curl, 0, 8) === 'libcurl/')
13930 {
13931 $curl = substr($curl, 8, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 8));
13932 }
13933 else
13934 {
13935 $curl = 0;
13936 }
13937 return $curl;
13938 }
13939
13940 /**
13941 * Strip HTML comments
13942 *
13943 * @param string $data Data to strip comments from
13944 * @return string Comment stripped string
13945 */
13946 public static function strip_comments($data)
13947 {
13948 $output = '';
13949 while (($start = strpos($data, '<!--')) !== false)
13950 {
13951 $output .= substr($data, 0, $start);
13952 if (($end = strpos($data, '-->', $start)) !== false)
13953 {
13954 $data = substr_replace($data, '', 0, $end + 3);
13955 }
13956 else
13957 {
13958 $data = '';
13959 }
13960 }
13961 return $output . $data;
13962 }
13963
13964 public static function parse_date($dt)
13965 {
13966 $parser = SimplePie_Parse_Date::get();
13967 return $parser->parse($dt);
13968 }
13969
13970 /**
13971 * Decode HTML entities
13972 *
13973 * @deprecated Use DOMDocument instead
13974 * @param string $data Input data
13975 * @return string Output data
13976 */
13977 public static function entities_decode($data)
13978 {
13979 $decoder = new SimplePie_Decode_HTML_Entities($data);
13980 return $decoder->parse();
13981 }
13982
13983 /**
13984 * Remove RFC822 comments
13985 *
13986 * @param string $data Data to strip comments from
13987 * @return string Comment stripped string
13988 */
13989 public static function uncomment_rfc822($string)
13990 {
13991 $string = (string) $string;
13992 $position = 0;
13993 $length = strlen($string);
13994 $depth = 0;
13995
13996 $output = '';
13997
13998 while ($position < $length && ($pos = strpos($string, '(', $position)) !== false)
13999 {
14000 $output .= substr($string, $position, $pos - $position);
14001 $position = $pos + 1;
14002 if ($string[$pos - 1] !== '\\')
14003 {
14004 $depth++;
14005 while ($depth && $position < $length)
14006 {
14007 $position += strcspn($string, '()', $position);
14008 if ($string[$position - 1] === '\\')
14009 {
14010 $position++;
14011 continue;
14012 }
14013 elseif (isset($string[$position]))
14014 {
14015 switch ($string[$position])
14016 {
14017 case '(':
14018 $depth++;
14019 break;
14020
14021 case ')':
14022 $depth--;
14023 break;
14024 }
14025 $position++;
14026 }
14027 else
14028 {
14029 break;
14030 }
14031 }
14032 }
14033 else
14034 {
14035 $output .= '(';
14036 }
14037 }
14038 $output .= substr($string, $position);
14039
14040 return $output;
14041 }
14042
14043 public static function parse_mime($mime)
14044 {
14045 if (($pos = strpos($mime, ';')) === false)
14046 {
14047 return trim($mime);
14048 }
14049 else
14050 {
14051 return trim(substr($mime, 0, $pos));
14052 }
14053 }
14054
14055 public static function atom_03_construct_type($attribs)
14056 {
14057 if (isset($attribs['']['mode']) && strtolower(trim($attribs['']['mode']) === 'base64'))
14058 {
14059 $mode = SIMPLEPIE_CONSTRUCT_BASE64;
14060 }
14061 else
14062 {
14063 $mode = SIMPLEPIE_CONSTRUCT_NONE;
14064 }
14065 if (isset($attribs['']['type']))
14066 {
14067 switch (strtolower(trim($attribs['']['type'])))
14068 {
14069 case 'text':
14070 case 'text/plain':
14071 return SIMPLEPIE_CONSTRUCT_TEXT | $mode;
14072
14073 case 'html':
14074 case 'text/html':
14075 return SIMPLEPIE_CONSTRUCT_HTML | $mode;
14076
14077 case 'xhtml':
14078 case 'application/xhtml+xml':
14079 return SIMPLEPIE_CONSTRUCT_XHTML | $mode;
14080
14081 default:
14082 return SIMPLEPIE_CONSTRUCT_NONE | $mode;
14083 }
14084 }
14085 else
14086 {
14087 return SIMPLEPIE_CONSTRUCT_TEXT | $mode;
14088 }
14089 }
14090
14091 public static function atom_10_construct_type($attribs)
14092 {
14093 if (isset($attribs['']['type']))
14094 {
14095 switch (strtolower(trim($attribs['']['type'])))
14096 {
14097 case 'text':
14098 return SIMPLEPIE_CONSTRUCT_TEXT;
14099
14100 case 'html':
14101 return SIMPLEPIE_CONSTRUCT_HTML;
14102
14103 case 'xhtml':
14104 return SIMPLEPIE_CONSTRUCT_XHTML;
14105
14106 default:
14107 return SIMPLEPIE_CONSTRUCT_NONE;
14108 }
14109 }
14110 return SIMPLEPIE_CONSTRUCT_TEXT;
14111 }
14112
14113 public static function atom_10_content_construct_type($attribs)
14114 {
14115 if (isset($attribs['']['type']))
14116 {
14117 $type = strtolower(trim($attribs['']['type']));
14118 switch ($type)
14119 {
14120 case 'text':
14121 return SIMPLEPIE_CONSTRUCT_TEXT;
14122
14123 case 'html':
14124 return SIMPLEPIE_CONSTRUCT_HTML;
14125
14126 case 'xhtml':
14127 return SIMPLEPIE_CONSTRUCT_XHTML;
14128 }
14129 if (in_array(substr($type, -4), array('+xml', '/xml')) || substr($type, 0, 5) === 'text/')
14130 {
14131 return SIMPLEPIE_CONSTRUCT_NONE;
14132 }
14133 else
14134 {
14135 return SIMPLEPIE_CONSTRUCT_BASE64;
14136 }
14137 }
14138 else
14139 {
14140 return SIMPLEPIE_CONSTRUCT_TEXT;
14141 }
14142 }
14143
14144 public static function is_isegment_nz_nc($string)
14145 {
14146 return (bool) preg_match('/^([A-Za-z0-9\-._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!$&\'()*+,;=@]|(%[0-9ABCDEF]{2}))+$/u', $string);
14147 }
14148
14149 public static function space_seperated_tokens($string)
14150 {
14151 $space_characters = "\x20\x09\x0A\x0B\x0C\x0D";
14152 $string_length = strlen($string);
14153
14154 $position = strspn($string, $space_characters);
14155 $tokens = array();
14156
14157 while ($position < $string_length)
14158 {
14159 $len = strcspn($string, $space_characters, $position);
14160 $tokens[] = substr($string, $position, $len);
14161 $position += $len;
14162 $position += strspn($string, $space_characters, $position);
14163 }
14164
14165 return $tokens;
14166 }
14167
14168 /**
14169 * Converts a unicode codepoint to a UTF-8 character
14170 *
14171 * @static
14172 * @param int $codepoint Unicode codepoint
14173 * @return string UTF-8 character
14174 */
14175 public static function codepoint_to_utf8($codepoint)
14176 {
14177 $codepoint = (int) $codepoint;
14178 if ($codepoint < 0)
14179 {
14180 return false;
14181 }
14182 else if ($codepoint <= 0x7f)
14183 {
14184 return chr($codepoint);
14185 }
14186 else if ($codepoint <= 0x7ff)
14187 {
14188 return chr(0xc0 | ($codepoint >> 6)) . chr(0x80 | ($codepoint & 0x3f));
14189 }
14190 else if ($codepoint <= 0xffff)
14191 {
14192 return chr(0xe0 | ($codepoint >> 12)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f));
14193 }
14194 else if ($codepoint <= 0x10ffff)
14195 {
14196 return chr(0xf0 | ($codepoint >> 18)) . chr(0x80 | (($codepoint >> 12) & 0x3f)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f));
14197 }
14198 else
14199 {
14200 // U+FFFD REPLACEMENT CHARACTER
14201 return "\xEF\xBF\xBD";
14202 }
14203 }
14204
14205 /**
14206 * Similar to parse_str()
14207 *
14208 * Returns an associative array of name/value pairs, where the value is an
14209 * array of values that have used the same name
14210 *
14211 * @static
14212 * @param string $str The input string.
14213 * @return array
14214 */
14215 public static function parse_str($str)
14216 {
14217 $return = array();
14218 $str = explode('&', $str);
14219
14220 foreach ($str as $section)
14221 {
14222 if (strpos($section, '=') !== false)
14223 {
14224 list($name, $value) = explode('=', $section, 2);
14225 $return[urldecode($name)][] = urldecode($value);
14226 }
14227 else
14228 {
14229 $return[urldecode($section)][] = null;
14230 }
14231 }
14232
14233 return $return;
14234 }
14235
14236 /**
14237 * Detect XML encoding, as per XML 1.0 Appendix F.1
14238 *
14239 * @todo Add support for EBCDIC
14240 * @param string $data XML data
14241 * @param SimplePie_Registry $registry Class registry
14242 * @return array Possible encodings
14243 */
14244 public static function xml_encoding($data, $registry)
14245 {
14246 // UTF-32 Big Endian BOM
14247 if (substr($data, 0, 4) === "\x00\x00\xFE\xFF")
14248 {
14249 $encoding[] = 'UTF-32BE';
14250 }
14251 // UTF-32 Little Endian BOM
14252 elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00")
14253 {
14254 $encoding[] = 'UTF-32LE';
14255 }
14256 // UTF-16 Big Endian BOM
14257 elseif (substr($data, 0, 2) === "\xFE\xFF")
14258 {
14259 $encoding[] = 'UTF-16BE';
14260 }
14261 // UTF-16 Little Endian BOM
14262 elseif (substr($data, 0, 2) === "\xFF\xFE")
14263 {
14264 $encoding[] = 'UTF-16LE';
14265 }
14266 // UTF-8 BOM
14267 elseif (substr($data, 0, 3) === "\xEF\xBB\xBF")
14268 {
14269 $encoding[] = 'UTF-8';
14270 }
14271 // UTF-32 Big Endian Without BOM
14272 elseif (substr($data, 0, 20) === "\x00\x00\x00\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C")
14273 {
14274 if ($pos = strpos($data, "\x00\x00\x00\x3F\x00\x00\x00\x3E"))
14275 {
14276 $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32BE', 'UTF-8')));
14277 if ($parser->parse())
14278 {
14279 $encoding[] = $parser->encoding;
14280 }
14281 }
14282 $encoding[] = 'UTF-32BE';
14283 }
14284 // UTF-32 Little Endian Without BOM
14285 elseif (substr($data, 0, 20) === "\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C\x00\x00\x00")
14286 {
14287 if ($pos = strpos($data, "\x3F\x00\x00\x00\x3E\x00\x00\x00"))
14288 {
14289 $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32LE', 'UTF-8')));
14290 if ($parser->parse())
14291 {
14292 $encoding[] = $parser->encoding;
14293 }
14294 }
14295 $encoding[] = 'UTF-32LE';
14296 }
14297 // UTF-16 Big Endian Without BOM
14298 elseif (substr($data, 0, 10) === "\x00\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C")
14299 {
14300 if ($pos = strpos($data, "\x00\x3F\x00\x3E"))
14301 {
14302 $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16BE', 'UTF-8')));
14303 if ($parser->parse())
14304 {
14305 $encoding[] = $parser->encoding;
14306 }
14307 }
14308 $encoding[] = 'UTF-16BE';
14309 }
14310 // UTF-16 Little Endian Without BOM
14311 elseif (substr($data, 0, 10) === "\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C\x00")
14312 {
14313 if ($pos = strpos($data, "\x3F\x00\x3E\x00"))
14314 {
14315 $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16LE', 'UTF-8')));
14316 if ($parser->parse())
14317 {
14318 $encoding[] = $parser->encoding;
14319 }
14320 }
14321 $encoding[] = 'UTF-16LE';
14322 }
14323 // US-ASCII (or superset)
14324 elseif (substr($data, 0, 5) === "\x3C\x3F\x78\x6D\x6C")
14325 {
14326 if ($pos = strpos($data, "\x3F\x3E"))
14327 {
14328 $parser = $registry->create('XML_Declaration_Parser', array(substr($data, 5, $pos - 5)));
14329 if ($parser->parse())
14330 {
14331 $encoding[] = $parser->encoding;
14332 }
14333 }
14334 $encoding[] = 'UTF-8';
14335 }
14336 // Fallback to UTF-8
14337 else
14338 {
14339 $encoding[] = 'UTF-8';
14340 }
14341 return $encoding;
14342 }
14343
14344 public static function output_javascript()
14345 {
14346 if (function_exists('ob_gzhandler'))
14347 {
14348 ob_start('ob_gzhandler');
14349 }
14350 header('Content-type: text/javascript; charset: UTF-8');
14351 header('Cache-Control: must-revalidate');
14352 header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days
14353 ?>
14354 function embed_quicktime(type, bgcolor, width, height, link, placeholder, loop) {
14355 if (placeholder != '') {
14356 document.writeln('<embed type="'+type+'" style="cursor:hand; cursor:pointer;" href="'+link+'" src="'+placeholder+'" width="'+width+'" height="'+height+'" autoplay="false" target="myself" controller="false" loop="'+loop+'" scale="aspect" bgcolor="'+bgcolor+'" pluginspage="http://www.apple.com/quicktime/download/"></embed>');
14357 }
14358 else {
14359 document.writeln('<embed type="'+type+'" style="cursor:hand; cursor:pointer;" src="'+link+'" width="'+width+'" height="'+height+'" autoplay="false" target="myself" controller="true" loop="'+loop+'" scale="aspect" bgcolor="'+bgcolor+'" pluginspage="http://www.apple.com/quicktime/download/"></embed>');
14360 }
14361 }
14362
14363 function embed_flash(bgcolor, width, height, link, loop, type) {
14364 document.writeln('<embed src="'+link+'" pluginspage="http://www.macromedia.com/go/getflashplayer" type="'+type+'" quality="high" width="'+width+'" height="'+height+'" bgcolor="'+bgcolor+'" loop="'+loop+'"></embed>');
14365 }
14366
14367 function embed_flv(width, height, link, placeholder, loop, player) {
14368 document.writeln('<embed src="'+player+'" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" quality="high" width="'+width+'" height="'+height+'" wmode="transparent" flashvars="file='+link+'&autostart=false&repeat='+loop+'&showdigits=true&showfsbutton=false"></embed>');
14369 }
14370
14371 function embed_wmedia(width, height, link) {
14372 document.writeln('<embed type="application/x-mplayer2" src="'+link+'" autosize="1" width="'+width+'" height="'+height+'" showcontrols="1" showstatusbar="0" showdisplay="0" autostart="0"></embed>');
14373 }
14374 <?php
14375 }
14376
14377 /**
14378 * Get the SimplePie build timestamp
14379 *
14380 * Uses the git index if it exists, otherwise uses the modification time
14381 * of the newest file.
14382 */
14383 public static function get_build()
14384 {
14385 $root = dirname(dirname(__FILE__));
14386 if (file_exists($root . '/.git/index'))
14387 {
14388 return filemtime($root . '/.git/index');
14389 }
14390 elseif (file_exists($root . '/SimplePie'))
14391 {
14392 $time = 0;
14393 foreach (glob($root . '/SimplePie/*.php') as $file)
14394 {
14395 if (($mtime = filemtime($file)) > $time)
14396 {
14397 $time = $mtime;
14398 }
14399 }
14400 return $time;
14401 }
14402 elseif (file_exists(dirname(__FILE__) . '/Core.php'))
14403 {
14404 return filemtime(dirname(__FILE__) . '/Core.php');
14405 }
14406 else
14407 {
14408 return filemtime(__FILE__);
14409 }
14410 }
14411
14412 /**
14413 * Format debugging information
14414 */
14415 public static function debug(&$sp)
14416 {
14417 $info = 'SimplePie ' . SIMPLEPIE_VERSION . ' Build ' . SIMPLEPIE_BUILD . "\n";
14418 $info .= 'PHP ' . PHP_VERSION . "\n";
14419 if ($sp->error() !== null)
14420 {
14421 $info .= 'Error occurred: ' . $sp->error() . "\n";
14422 }
14423 else
14424 {
14425 $info .= "No error found.\n";
14426 }
14427 $info .= "Extensions:\n";
14428 $extensions = array('pcre', 'curl', 'zlib', 'mbstring', 'iconv', 'xmlreader', 'xml');
14429 foreach ($extensions as $ext)
14430 {
14431 if (extension_loaded($ext))
14432 {
14433 $info .= " $ext loaded\n";
14434 switch ($ext)
14435 {
14436 case 'pcre':
14437 $info .= ' Version ' . PCRE_VERSION . "\n";
14438 break;
14439 case 'curl':
14440 $version = curl_version();
14441 $info .= ' Version ' . $version['version'] . "\n";
14442 break;
14443 case 'mbstring':
14444 $info .= ' Overloading: ' . mb_get_info('func_overload') . "\n";
14445 break;
14446 case 'iconv':
14447 $info .= ' Version ' . ICONV_VERSION . "\n";
14448 break;
14449 case 'xml':
14450 $info .= ' Version ' . LIBXML_DOTTED_VERSION . "\n";
14451 break;
14452 }
14453 }
14454 else
14455 {
14456 $info .= " $ext not loaded\n";
14457 }
14458 }
14459 return $info;
14460 }
14461
14462 public static function silence_errors($num, $str)
14463 {
14464 // No-op
14465 }
14466 }
14467
14468 /**
14469 * Class to validate and to work with IPv6 addresses.
14470 *
14471 * @package SimplePie
14472 * @subpackage HTTP
14473 * @copyright 2003-2005 The PHP Group
14474 * @license http://www.opensource.org/licenses/bsd-license.php
14475 * @link http://pear.php.net/package/Net_IPv6
14476 * @author Alexander Merz <alexander.merz@web.de>
14477 * @author elfrink at introweb dot nl
14478 * @author Josh Peck <jmp at joshpeck dot org>
14479 * @author Geoffrey Sneddon <geoffers@gmail.com>
14480 */
14481 class SimplePie_Net_IPv6
14482 {
14483 /**
14484 * Uncompresses an IPv6 address
14485 *
14486 * RFC 4291 allows you to compress concecutive zero pieces in an address to
14487 * '::'. This method expects a valid IPv6 address and expands the '::' to
14488 * the required number of zero pieces.
14489 *
14490 * Example: FF01::101 -> FF01:0:0:0:0:0:0:101
14491 * ::1 -> 0:0:0:0:0:0:0:1
14492 *
14493 * @author Alexander Merz <alexander.merz@web.de>
14494 * @author elfrink at introweb dot nl
14495 * @author Josh Peck <jmp at joshpeck dot org>
14496 * @copyright 2003-2005 The PHP Group
14497 * @license http://www.opensource.org/licenses/bsd-license.php
14498 * @param string $ip An IPv6 address
14499 * @return string The uncompressed IPv6 address
14500 */
14501 public static function uncompress($ip)
14502 {
14503 $c1 = -1;
14504 $c2 = -1;
14505 if (substr_count($ip, '::') === 1)
14506 {
14507 list($ip1, $ip2) = explode('::', $ip);
14508 if ($ip1 === '')
14509 {
14510 $c1 = -1;
14511 }
14512 else
14513 {
14514 $c1 = substr_count($ip1, ':');
14515 }
14516 if ($ip2 === '')
14517 {
14518 $c2 = -1;
14519 }
14520 else
14521 {
14522 $c2 = substr_count($ip2, ':');
14523 }
14524 if (strpos($ip2, '.') !== false)
14525 {
14526 $c2++;
14527 }
14528 // ::
14529 if ($c1 === -1 && $c2 === -1)
14530 {
14531 $ip = '0:0:0:0:0:0:0:0';
14532 }
14533 // ::xxx
14534 else if ($c1 === -1)
14535 {
14536 $fill = str_repeat('0:', 7 - $c2);
14537 $ip = str_replace('::', $fill, $ip);
14538 }
14539 // xxx::
14540 else if ($c2 === -1)
14541 {
14542 $fill = str_repeat(':0', 7 - $c1);
14543 $ip = str_replace('::', $fill, $ip);
14544 }
14545 // xxx::xxx
14546 else
14547 {
14548 $fill = ':' . str_repeat('0:', 6 - $c2 - $c1);
14549 $ip = str_replace('::', $fill, $ip);
14550 }
14551 }
14552 return $ip;
14553 }
14554
14555 /**
14556 * Compresses an IPv6 address
14557 *
14558 * RFC 4291 allows you to compress concecutive zero pieces in an address to
14559 * '::'. This method expects a valid IPv6 address and compresses consecutive
14560 * zero pieces to '::'.
14561 *
14562 * Example: FF01:0:0:0:0:0:0:101 -> FF01::101
14563 * 0:0:0:0:0:0:0:1 -> ::1
14564 *
14565 * @see uncompress()
14566 * @param string $ip An IPv6 address
14567 * @return string The compressed IPv6 address
14568 */
14569 public static function compress($ip)
14570 {
14571 // Prepare the IP to be compressed
14572 $ip = self::uncompress($ip);
14573 $ip_parts = self::split_v6_v4($ip);
14574
14575 // Replace all leading zeros
14576 $ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]);
14577
14578 // Find bunches of zeros
14579 if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE))
14580 {
14581 $max = 0;
14582 $pos = null;
14583 foreach ($matches[0] as $match)
14584 {
14585 if (strlen($match[0]) > $max)
14586 {
14587 $max = strlen($match[0]);
14588 $pos = $match[1];
14589 }
14590 }
14591
14592 $ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max);
14593 }
14594
14595 if ($ip_parts[1] !== '')
14596 {
14597 return implode(':', $ip_parts);
14598 }
14599 else
14600 {
14601 return $ip_parts[0];
14602 }
14603 }
14604
14605 /**
14606 * Splits an IPv6 address into the IPv6 and IPv4 representation parts
14607 *
14608 * RFC 4291 allows you to represent the last two parts of an IPv6 address
14609 * using the standard IPv4 representation
14610 *
14611 * Example: 0:0:0:0:0:0:13.1.68.3
14612 * 0:0:0:0:0:FFFF:129.144.52.38
14613 *
14614 * @param string $ip An IPv6 address
14615 * @return array [0] contains the IPv6 represented part, and [1] the IPv4 represented part
14616 */
14617 private static function split_v6_v4($ip)
14618 {
14619 if (strpos($ip, '.') !== false)
14620 {
14621 $pos = strrpos($ip, ':');
14622 $ipv6_part = substr($ip, 0, $pos);
14623 $ipv4_part = substr($ip, $pos + 1);
14624 return array($ipv6_part, $ipv4_part);
14625 }
14626 else
14627 {
14628 return array($ip, '');
14629 }
14630 }
14631
14632 /**
14633 * Checks an IPv6 address
14634 *
14635 * Checks if the given IP is a valid IPv6 address
14636 *
14637 * @param string $ip An IPv6 address
14638 * @return bool true if $ip is a valid IPv6 address
14639 */
14640 public static function check_ipv6($ip)
14641 {
14642 $ip = self::uncompress($ip);
14643 list($ipv6, $ipv4) = self::split_v6_v4($ip);
14644 $ipv6 = explode(':', $ipv6);
14645 $ipv4 = explode('.', $ipv4);
14646 if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4)
14647 {
14648 foreach ($ipv6 as $ipv6_part)
14649 {
14650 // The section can't be empty
14651 if ($ipv6_part === '')
14652 return false;
14653
14654 // Nor can it be over four characters
14655 if (strlen($ipv6_part) > 4)
14656 return false;
14657
14658 // Remove leading zeros (this is safe because of the above)
14659 $ipv6_part = ltrim($ipv6_part, '0');
14660 if ($ipv6_part === '')
14661 $ipv6_part = '0';
14662
14663 // Check the value is valid
14664 $value = hexdec($ipv6_part);
14665 if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF)
14666 return false;
14667 }
14668 if (count($ipv4) === 4)
14669 {
14670 foreach ($ipv4 as $ipv4_part)
14671 {
14672 $value = (int) $ipv4_part;
14673 if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF)
14674 return false;
14675 }
14676 }
14677 return true;
14678 }
14679 else
14680 {
14681 return false;
14682 }
14683 }
14684
14685 /**
14686 * Checks if the given IP is a valid IPv6 address
14687 *
14688 * @codeCoverageIgnore
14689 * @deprecated Use {@see SimplePie_Net_IPv6::check_ipv6()} instead
14690 * @see check_ipv6
14691 * @param string $ip An IPv6 address
14692 * @return bool true if $ip is a valid IPv6 address
14693 */
14694 public static function checkIPv6($ip)
14695 {
14696 return self::check_ipv6($ip);
14697 }
14698 }
14699
14700 /**
14701 * Date Parser
14702 *
14703 * @package SimplePie
14704 * @subpackage Parsing
14705 */
14706 class SimplePie_Parse_Date
14707 {
14708 /**
14709 * Input data
14710 *
14711 * @access protected
14712 * @var string
14713 */
14714 var $date;
14715
14716 /**
14717 * List of days, calendar day name => ordinal day number in the week
14718 *
14719 * @access protected
14720 * @var array
14721 */
14722 var $day = array(
14723 // English
14724 'mon' => 1,
14725 'monday' => 1,
14726 'tue' => 2,
14727 'tuesday' => 2,
14728 'wed' => 3,
14729 'wednesday' => 3,
14730 'thu' => 4,
14731 'thursday' => 4,
14732 'fri' => 5,
14733 'friday' => 5,
14734 'sat' => 6,
14735 'saturday' => 6,
14736 'sun' => 7,
14737 'sunday' => 7,
14738 // Dutch
14739 'maandag' => 1,
14740 'dinsdag' => 2,
14741 'woensdag' => 3,
14742 'donderdag' => 4,
14743 'vrijdag' => 5,
14744 'zaterdag' => 6,
14745 'zondag' => 7,
14746 // French
14747 'lundi' => 1,
14748 'mardi' => 2,
14749 'mercredi' => 3,
14750 'jeudi' => 4,
14751 'vendredi' => 5,
14752 'samedi' => 6,
14753 'dimanche' => 7,
14754 // German
14755 'montag' => 1,
14756 'dienstag' => 2,
14757 'mittwoch' => 3,
14758 'donnerstag' => 4,
14759 'freitag' => 5,
14760 'samstag' => 6,
14761 'sonnabend' => 6,
14762 'sonntag' => 7,
14763 // Italian
14764 'lunedì' => 1,
14765 'martedì' => 2,
14766 'mercoledì' => 3,
14767 'giovedì' => 4,
14768 'venerdì' => 5,
14769 'sabato' => 6,
14770 'domenica' => 7,
14771 // Spanish
14772 'lunes' => 1,
14773 'martes' => 2,
14774 'miércoles' => 3,
14775 'jueves' => 4,
14776 'viernes' => 5,
14777 'sábado' => 6,
14778 'domingo' => 7,
14779 // Finnish
14780 'maanantai' => 1,
14781 'tiistai' => 2,
14782 'keskiviikko' => 3,
14783 'torstai' => 4,
14784 'perjantai' => 5,
14785 'lauantai' => 6,
14786 'sunnuntai' => 7,
14787 // Hungarian
14788 'hétfő' => 1,
14789 'kedd' => 2,
14790 'szerda' => 3,
14791 'csütörtok' => 4,
14792 'péntek' => 5,
14793 'szombat' => 6,
14794 'vasárnap' => 7,
14795 // Greek
14796 'Δευ' => 1,
14797 'Τρι' => 2,
14798 'Τετ' => 3,
14799 'Πεμ' => 4,
14800 'Παρ' => 5,
14801 'Σαβ' => 6,
14802 'Κυρ' => 7,
14803 );
14804
14805 /**
14806 * List of months, calendar month name => calendar month number
14807 *
14808 * @access protected
14809 * @var array
14810 */
14811 var $month = array(
14812 // English
14813 'jan' => 1,
14814 'january' => 1,
14815 'feb' => 2,
14816 'february' => 2,
14817 'mar' => 3,
14818 'march' => 3,
14819 'apr' => 4,
14820 'april' => 4,
14821 'may' => 5,
14822 // No long form of May
14823 'jun' => 6,
14824 'june' => 6,
14825 'jul' => 7,
14826 'july' => 7,
14827 'aug' => 8,
14828 'august' => 8,
14829 'sep' => 9,
14830 'september' => 8,
14831 'oct' => 10,
14832 'october' => 10,
14833 'nov' => 11,
14834 'november' => 11,
14835 'dec' => 12,
14836 'december' => 12,
14837 // Dutch
14838 'januari' => 1,
14839 'februari' => 2,
14840 'maart' => 3,
14841 'april' => 4,
14842 'mei' => 5,
14843 'juni' => 6,
14844 'juli' => 7,
14845 'augustus' => 8,
14846 'september' => 9,
14847 'oktober' => 10,
14848 'november' => 11,
14849 'december' => 12,
14850 // French
14851 'janvier' => 1,
14852 'février' => 2,
14853 'mars' => 3,
14854 'avril' => 4,
14855 'mai' => 5,
14856 'juin' => 6,
14857 'juillet' => 7,
14858 'août' => 8,
14859 'septembre' => 9,
14860 'octobre' => 10,
14861 'novembre' => 11,
14862 'décembre' => 12,
14863 // German
14864 'januar' => 1,
14865 'februar' => 2,
14866 'märz' => 3,
14867 'april' => 4,
14868 'mai' => 5,
14869 'juni' => 6,
14870 'juli' => 7,
14871 'august' => 8,
14872 'september' => 9,
14873 'oktober' => 10,
14874 'november' => 11,
14875 'dezember' => 12,
14876 // Italian
14877 'gennaio' => 1,
14878 'febbraio' => 2,
14879 'marzo' => 3,
14880 'aprile' => 4,
14881 'maggio' => 5,
14882 'giugno' => 6,
14883 'luglio' => 7,
14884 'agosto' => 8,
14885 'settembre' => 9,
14886 'ottobre' => 10,
14887 'novembre' => 11,
14888 'dicembre' => 12,
14889 // Spanish
14890 'enero' => 1,
14891 'febrero' => 2,
14892 'marzo' => 3,
14893 'abril' => 4,
14894 'mayo' => 5,
14895 'junio' => 6,
14896 'julio' => 7,
14897 'agosto' => 8,
14898 'septiembre' => 9,
14899 'setiembre' => 9,
14900 'octubre' => 10,
14901 'noviembre' => 11,
14902 'diciembre' => 12,
14903 // Finnish
14904 'tammikuu' => 1,
14905 'helmikuu' => 2,
14906 'maaliskuu' => 3,
14907 'huhtikuu' => 4,
14908 'toukokuu' => 5,
14909 'kesäkuu' => 6,
14910 'heinäkuu' => 7,
14911 'elokuu' => 8,
14912 'suuskuu' => 9,
14913 'lokakuu' => 10,
14914 'marras' => 11,
14915 'joulukuu' => 12,
14916 // Hungarian
14917 'január' => 1,
14918 'február' => 2,
14919 'március' => 3,
14920 'április' => 4,
14921 'május' => 5,
14922 'június' => 6,
14923 'július' => 7,
14924 'augusztus' => 8,
14925 'szeptember' => 9,
14926 'október' => 10,
14927 'november' => 11,
14928 'december' => 12,
14929 // Greek
14930 'Ιαν' => 1,
14931 'Φεβ' => 2,
14932 'Μάώ' => 3,
14933 'Μαώ' => 3,
14934 'Απρ' => 4,
14935 'Μάι' => 5,
14936 'Μαϊ' => 5,
14937 'Μαι' => 5,
14938 'Ιούν' => 6,
14939 'Ιον' => 6,
14940 'Ιούλ' => 7,
14941 'Ιολ' => 7,
14942 'Αύγ' => 8,
14943 'Αυγ' => 8,
14944 'Σεπ' => 9,
14945 'Οκτ' => 10,
14946 'Νοέ' => 11,
14947 'Δεκ' => 12,
14948 );
14949
14950 /**
14951 * List of timezones, abbreviation => offset from UTC
14952 *
14953 * @access protected
14954 * @var array
14955 */
14956 var $timezone = array(
14957 'ACDT' => 37800,
14958 'ACIT' => 28800,
14959 'ACST' => 34200,
14960 'ACT' => -18000,
14961 'ACWDT' => 35100,
14962 'ACWST' => 31500,
14963 'AEDT' => 39600,
14964 'AEST' => 36000,
14965 'AFT' => 16200,
14966 'AKDT' => -28800,
14967 'AKST' => -32400,
14968 'AMDT' => 18000,
14969 'AMT' => -14400,
14970 'ANAST' => 46800,
14971 'ANAT' => 43200,
14972 'ART' => -10800,
14973 'AZOST' => -3600,
14974 'AZST' => 18000,
14975 'AZT' => 14400,
14976 'BIOT' => 21600,
14977 'BIT' => -43200,
14978 'BOT' => -14400,
14979 'BRST' => -7200,
14980 'BRT' => -10800,
14981 'BST' => 3600,
14982 'BTT' => 21600,
14983 'CAST' => 18000,
14984 'CAT' => 7200,
14985 'CCT' => 23400,
14986 'CDT' => -18000,
14987 'CEDT' => 7200,
14988 'CET' => 3600,
14989 'CGST' => -7200,
14990 'CGT' => -10800,
14991 'CHADT' => 49500,
14992 'CHAST' => 45900,
14993 'CIST' => -28800,
14994 'CKT' => -36000,
14995 'CLDT' => -10800,
14996 'CLST' => -14400,
14997 'COT' => -18000,
14998 'CST' => -21600,
14999 'CVT' => -3600,
15000 'CXT' => 25200,
15001 'DAVT' => 25200,
15002 'DTAT' => 36000,
15003 'EADT' => -18000,
15004 'EAST' => -21600,
15005 'EAT' => 10800,
15006 'ECT' => -18000,
15007 'EDT' => -14400,
15008 'EEST' => 10800,
15009 'EET' => 7200,
15010 'EGT' => -3600,
15011 'EKST' => 21600,
15012 'EST' => -18000,
15013 'FJT' => 43200,
15014 'FKDT' => -10800,
15015 'FKST' => -14400,
15016 'FNT' => -7200,
15017 'GALT' => -21600,
15018 'GEDT' => 14400,
15019 'GEST' => 10800,
15020 'GFT' => -10800,
15021 'GILT' => 43200,
15022 'GIT' => -32400,
15023 'GST' => 14400,
15024 'GST' => -7200,
15025 'GYT' => -14400,
15026 'HAA' => -10800,
15027 'HAC' => -18000,
15028 'HADT' => -32400,
15029 'HAE' => -14400,
15030 'HAP' => -25200,
15031 'HAR' => -21600,
15032 'HAST' => -36000,
15033 'HAT' => -9000,
15034 'HAY' => -28800,
15035 'HKST' => 28800,
15036 'HMT' => 18000,
15037 'HNA' => -14400,
15038 'HNC' => -21600,
15039 'HNE' => -18000,
15040 'HNP' => -28800,
15041 'HNR' => -25200,
15042 'HNT' => -12600,
15043 'HNY' => -32400,
15044 'IRDT' => 16200,
15045 'IRKST' => 32400,
15046 'IRKT' => 28800,
15047 'IRST' => 12600,
15048 'JFDT' => -10800,
15049 'JFST' => -14400,
15050 'JST' => 32400,
15051 'KGST' => 21600,
15052 'KGT' => 18000,
15053 'KOST' => 39600,
15054 'KOVST' => 28800,
15055 'KOVT' => 25200,
15056 'KRAST' => 28800,
15057 'KRAT' => 25200,
15058 'KST' => 32400,
15059 'LHDT' => 39600,
15060 'LHST' => 37800,
15061 'LINT' => 50400,
15062 'LKT' => 21600,
15063 'MAGST' => 43200,
15064 'MAGT' => 39600,
15065 'MAWT' => 21600,
15066 'MDT' => -21600,
15067 'MESZ' => 7200,
15068 'MEZ' => 3600,
15069 'MHT' => 43200,
15070 'MIT' => -34200,
15071 'MNST' => 32400,
15072 'MSDT' => 14400,
15073 'MSST' => 10800,
15074 'MST' => -25200,
15075 'MUT' => 14400,
15076 'MVT' => 18000,
15077 'MYT' => 28800,
15078 'NCT' => 39600,
15079 'NDT' => -9000,
15080 'NFT' => 41400,
15081 'NMIT' => 36000,
15082 'NOVST' => 25200,
15083 'NOVT' => 21600,
15084 'NPT' => 20700,
15085 'NRT' => 43200,
15086 'NST' => -12600,
15087 'NUT' => -39600,
15088 'NZDT' => 46800,
15089 'NZST' => 43200,
15090 'OMSST' => 25200,
15091 'OMST' => 21600,
15092 'PDT' => -25200,
15093 'PET' => -18000,
15094 'PETST' => 46800,
15095 'PETT' => 43200,
15096 'PGT' => 36000,
15097 'PHOT' => 46800,
15098 'PHT' => 28800,
15099 'PKT' => 18000,
15100 'PMDT' => -7200,
15101 'PMST' => -10800,
15102 'PONT' => 39600,
15103 'PST' => -28800,
15104 'PWT' => 32400,
15105 'PYST' => -10800,
15106 'PYT' => -14400,
15107 'RET' => 14400,
15108 'ROTT' => -10800,
15109 'SAMST' => 18000,
15110 'SAMT' => 14400,
15111 'SAST' => 7200,
15112 'SBT' => 39600,
15113 'SCDT' => 46800,
15114 'SCST' => 43200,
15115 'SCT' => 14400,
15116 'SEST' => 3600,
15117 'SGT' => 28800,
15118 'SIT' => 28800,
15119 'SRT' => -10800,
15120 'SST' => -39600,
15121 'SYST' => 10800,
15122 'SYT' => 7200,
15123 'TFT' => 18000,
15124 'THAT' => -36000,
15125 'TJT' => 18000,
15126 'TKT' => -36000,
15127 'TMT' => 18000,
15128 'TOT' => 46800,
15129 'TPT' => 32400,
15130 'TRUT' => 36000,
15131 'TVT' => 43200,
15132 'TWT' => 28800,
15133 'UYST' => -7200,
15134 'UYT' => -10800,
15135 'UZT' => 18000,
15136 'VET' => -14400,
15137 'VLAST' => 39600,
15138 'VLAT' => 36000,
15139 'VOST' => 21600,
15140 'VUT' => 39600,
15141 'WAST' => 7200,
15142 'WAT' => 3600,
15143 'WDT' => 32400,
15144 'WEST' => 3600,
15145 'WFT' => 43200,
15146 'WIB' => 25200,
15147 'WIT' => 32400,
15148 'WITA' => 28800,
15149 'WKST' => 18000,
15150 'WST' => 28800,
15151 'YAKST' => 36000,
15152 'YAKT' => 32400,
15153 'YAPT' => 36000,
15154 'YEKST' => 21600,
15155 'YEKT' => 18000,
15156 );
15157
15158 /**
15159 * Cached PCRE for SimplePie_Parse_Date::$day
15160 *
15161 * @access protected
15162 * @var string
15163 */
15164 var $day_pcre;
15165
15166 /**
15167 * Cached PCRE for SimplePie_Parse_Date::$month
15168 *
15169 * @access protected
15170 * @var string
15171 */
15172 var $month_pcre;
15173
15174 /**
15175 * Array of user-added callback methods
15176 *
15177 * @access private
15178 * @var array
15179 */
15180 var $built_in = array();
15181
15182 /**
15183 * Array of user-added callback methods
15184 *
15185 * @access private
15186 * @var array
15187 */
15188 var $user = array();
15189
15190 /**
15191 * Create new SimplePie_Parse_Date object, and set self::day_pcre,
15192 * self::month_pcre, and self::built_in
15193 *
15194 * @access private
15195 */
15196 public function __construct()
15197 {
15198 $this->day_pcre = '(' . implode(array_keys($this->day), '|') . ')';
15199 $this->month_pcre = '(' . implode(array_keys($this->month), '|') . ')';
15200
15201 static $cache;
15202 if (!isset($cache[get_class($this)]))
15203 {
15204 $all_methods = get_class_methods($this);
15205
15206 foreach ($all_methods as $method)
15207 {
15208 if (strtolower(substr($method, 0, 5)) === 'date_')
15209 {
15210 $cache[get_class($this)][] = $method;
15211 }
15212 }
15213 }
15214
15215 foreach ($cache[get_class($this)] as $method)
15216 {
15217 $this->built_in[] = $method;
15218 }
15219 }
15220
15221 /**
15222 * Get the object
15223 *
15224 * @access public
15225 */
15226 public static function get()
15227 {
15228 static $object;
15229 if (!$object)
15230 {
15231 $object = new SimplePie_Parse_Date;
15232 }
15233 return $object;
15234 }
15235
15236 /**
15237 * Parse a date
15238 *
15239 * @final
15240 * @access public
15241 * @param string $date Date to parse
15242 * @return int Timestamp corresponding to date string, or false on failure
15243 */
15244 public function parse($date)
15245 {
15246 foreach ($this->user as $method)
15247 {
15248 if (($returned = call_user_func($method, $date)) !== false)
15249 {
15250 return $returned;
15251 }
15252 }
15253
15254 foreach ($this->built_in as $method)
15255 {
15256 if (($returned = call_user_func(array($this, $method), $date)) !== false)
15257 {
15258 return $returned;
15259 }
15260 }
15261
15262 return false;
15263 }
15264
15265 /**
15266 * Add a callback method to parse a date
15267 *
15268 * @final
15269 * @access public
15270 * @param callback $callback
15271 */
15272 public function add_callback($callback)
15273 {
15274 if (is_callable($callback))
15275 {
15276 $this->user[] = $callback;
15277 }
15278 else
15279 {
15280 trigger_error('User-supplied function must be a valid callback', E_USER_WARNING);
15281 }
15282 }
15283
15284 /**
15285 * Parse a superset of W3C-DTF (allows hyphens and colons to be omitted, as
15286 * well as allowing any of upper or lower case "T", horizontal tabs, or
15287 * spaces to be used as the time seperator (including more than one))
15288 *
15289 * @access protected
15290 * @return int Timestamp
15291 */
15292 public function date_w3cdtf($date)
15293 {
15294 static $pcre;
15295 if (!$pcre)
15296 {
15297 $year = '([0-9]{4})';
15298 $month = $day = $hour = $minute = $second = '([0-9]{2})';
15299 $decimal = '([0-9]*)';
15300 $zone = '(?:(Z)|([+\-])([0-9]{1,2}):?([0-9]{1,2}))';
15301 $pcre = '/^' . $year . '(?:-?' . $month . '(?:-?' . $day . '(?:[Tt\x09\x20]+' . $hour . '(?::?' . $minute . '(?::?' . $second . '(?:.' . $decimal . ')?)?)?' . $zone . ')?)?)?$/';
15302 }
15303 if (preg_match($pcre, $date, $match))
15304 {
15305 /*
15306 Capturing subpatterns:
15307 1: Year
15308 2: Month
15309 3: Day
15310 4: Hour
15311 5: Minute
15312 6: Second
15313 7: Decimal fraction of a second
15314 8: Zulu
15315 9: Timezone ±
15316 10: Timezone hours
15317 11: Timezone minutes
15318 */
15319
15320 // Fill in empty matches
15321 for ($i = count($match); $i <= 3; $i++)
15322 {
15323 $match[$i] = '1';
15324 }
15325
15326 for ($i = count($match); $i <= 7; $i++)
15327 {
15328 $match[$i] = '0';
15329 }
15330
15331 // Numeric timezone
15332 if (isset($match[9]) && $match[9] !== '')
15333 {
15334 $timezone = $match[10] * 3600;
15335 $timezone += $match[11] * 60;
15336 if ($match[9] === '-')
15337 {
15338 $timezone = 0 - $timezone;
15339 }
15340 }
15341 else
15342 {
15343 $timezone = 0;
15344 }
15345
15346 // Convert the number of seconds to an integer, taking decimals into account
15347 $second = round($match[6] + $match[7] / pow(10, strlen($match[7])));
15348
15349 return gmmktime($match[4], $match[5], $second, $match[2], $match[3], $match[1]) - $timezone;
15350 }
15351 else
15352 {
15353 return false;
15354 }
15355 }
15356
15357 /**
15358 * Remove RFC822 comments
15359 *
15360 * @access protected
15361 * @param string $data Data to strip comments from
15362 * @return string Comment stripped string
15363 */
15364 public function remove_rfc2822_comments($string)
15365 {
15366 $string = (string) $string;
15367 $position = 0;
15368 $length = strlen($string);
15369 $depth = 0;
15370
15371 $output = '';
15372
15373 while ($position < $length && ($pos = strpos($string, '(', $position)) !== false)
15374 {
15375 $output .= substr($string, $position, $pos - $position);
15376 $position = $pos + 1;
15377 if ($string[$pos - 1] !== '\\')
15378 {
15379 $depth++;
15380 while ($depth && $position < $length)
15381 {
15382 $position += strcspn($string, '()', $position);
15383 if ($string[$position - 1] === '\\')
15384 {
15385 $position++;
15386 continue;
15387 }
15388 elseif (isset($string[$position]))
15389 {
15390 switch ($string[$position])
15391 {
15392 case '(':
15393 $depth++;
15394 break;
15395
15396 case ')':
15397 $depth--;
15398 break;
15399 }
15400 $position++;
15401 }
15402 else
15403 {
15404 break;
15405 }
15406 }
15407 }
15408 else
15409 {
15410 $output .= '(';
15411 }
15412 }
15413 $output .= substr($string, $position);
15414
15415 return $output;
15416 }
15417
15418 /**
15419 * Parse RFC2822's date format
15420 *
15421 * @access protected
15422 * @return int Timestamp
15423 */
15424 public function date_rfc2822($date)
15425 {
15426 static $pcre;
15427 if (!$pcre)
15428 {
15429 $wsp = '[\x09\x20]';
15430 $fws = '(?:' . $wsp . '+|' . $wsp . '*(?:\x0D\x0A' . $wsp . '+)+)';
15431 $optional_fws = $fws . '?';
15432 $day_name = $this->day_pcre;
15433 $month = $this->month_pcre;
15434 $day = '([0-9]{1,2})';
15435 $hour = $minute = $second = '([0-9]{2})';
15436 $year = '([0-9]{2,4})';
15437 $num_zone = '([+\-])([0-9]{2})([0-9]{2})';
15438 $character_zone = '([A-Z]{1,5})';
15439 $zone = '(?:' . $num_zone . '|' . $character_zone . ')';
15440 $pcre = '/(?:' . $optional_fws . $day_name . $optional_fws . ',)?' . $optional_fws . $day . $fws . $month . $fws . $year . $fws . $hour . $optional_fws . ':' . $optional_fws . $minute . '(?:' . $optional_fws . ':' . $optional_fws . $second . ')?' . $fws . $zone . '/i';
15441 }
15442 if (preg_match($pcre, $this->remove_rfc2822_comments($date), $match))
15443 {
15444 /*
15445 Capturing subpatterns:
15446 1: Day name
15447 2: Day
15448 3: Month
15449 4: Year
15450 5: Hour
15451 6: Minute
15452 7: Second
15453 8: Timezone ±
15454 9: Timezone hours
15455 10: Timezone minutes
15456 11: Alphabetic timezone
15457 */
15458
15459 // Find the month number
15460 $month = $this->month[strtolower($match[3])];
15461
15462 // Numeric timezone
15463 if ($match[8] !== '')
15464 {
15465 $timezone = $match[9] * 3600;
15466 $timezone += $match[10] * 60;
15467 if ($match[8] === '-')
15468 {
15469 $timezone = 0 - $timezone;
15470 }
15471 }
15472 // Character timezone
15473 elseif (isset($this->timezone[strtoupper($match[11])]))
15474 {
15475 $timezone = $this->timezone[strtoupper($match[11])];
15476 }
15477 // Assume everything else to be -0000
15478 else
15479 {
15480 $timezone = 0;
15481 }
15482
15483 // Deal with 2/3 digit years
15484 if ($match[4] < 50)
15485 {
15486 $match[4] += 2000;
15487 }
15488 elseif ($match[4] < 1000)
15489 {
15490 $match[4] += 1900;
15491 }
15492
15493 // Second is optional, if it is empty set it to zero
15494 if ($match[7] !== '')
15495 {
15496 $second = $match[7];
15497 }
15498 else
15499 {
15500 $second = 0;
15501 }
15502
15503 return gmmktime($match[5], $match[6], $second, $month, $match[2], $match[4]) - $timezone;
15504 }
15505 else
15506 {
15507 return false;
15508 }
15509 }
15510
15511 /**
15512 * Parse RFC850's date format
15513 *
15514 * @access protected
15515 * @return int Timestamp
15516 */
15517 public function date_rfc850($date)
15518 {
15519 static $pcre;
15520 if (!$pcre)
15521 {
15522 $space = '[\x09\x20]+';
15523 $day_name = $this->day_pcre;
15524 $month = $this->month_pcre;
15525 $day = '([0-9]{1,2})';
15526 $year = $hour = $minute = $second = '([0-9]{2})';
15527 $zone = '([A-Z]{1,5})';
15528 $pcre = '/^' . $day_name . ',' . $space . $day . '-' . $month . '-' . $year . $space . $hour . ':' . $minute . ':' . $second . $space . $zone . '$/i';
15529 }
15530 if (preg_match($pcre, $date, $match))
15531 {
15532 /*
15533 Capturing subpatterns:
15534 1: Day name
15535 2: Day
15536 3: Month
15537 4: Year
15538 5: Hour
15539 6: Minute
15540 7: Second
15541 8: Timezone
15542 */
15543
15544 // Month
15545 $month = $this->month[strtolower($match[3])];
15546
15547 // Character timezone
15548 if (isset($this->timezone[strtoupper($match[8])]))
15549 {
15550 $timezone = $this->timezone[strtoupper($match[8])];
15551 }
15552 // Assume everything else to be -0000
15553 else
15554 {
15555 $timezone = 0;
15556 }
15557
15558 // Deal with 2 digit year
15559 if ($match[4] < 50)
15560 {
15561 $match[4] += 2000;
15562 }
15563 else
15564 {
15565 $match[4] += 1900;
15566 }
15567
15568 return gmmktime($match[5], $match[6], $match[7], $month, $match[2], $match[4]) - $timezone;
15569 }
15570 else
15571 {
15572 return false;
15573 }
15574 }
15575
15576 /**
15577 * Parse C99's asctime()'s date format
15578 *
15579 * @access protected
15580 * @return int Timestamp
15581 */
15582 public function date_asctime($date)
15583 {
15584 static $pcre;
15585 if (!$pcre)
15586 {
15587 $space = '[\x09\x20]+';
15588 $wday_name = $this->day_pcre;
15589 $mon_name = $this->month_pcre;
15590 $day = '([0-9]{1,2})';
15591 $hour = $sec = $min = '([0-9]{2})';
15592 $year = '([0-9]{4})';
15593 $terminator = '\x0A?\x00?';
15594 $pcre = '/^' . $wday_name . $space . $mon_name . $space . $day . $space . $hour . ':' . $min . ':' . $sec . $space . $year . $terminator . '$/i';
15595 }
15596 if (preg_match($pcre, $date, $match))
15597 {
15598 /*
15599 Capturing subpatterns:
15600 1: Day name
15601 2: Month
15602 3: Day
15603 4: Hour
15604 5: Minute
15605 6: Second
15606 7: Year
15607 */
15608
15609 $month = $this->month[strtolower($match[2])];
15610 return gmmktime($match[4], $match[5], $match[6], $month, $match[3], $match[7]);
15611 }
15612 else
15613 {
15614 return false;
15615 }
15616 }
15617
15618 /**
15619 * Parse dates using strtotime()
15620 *
15621 * @access protected
15622 * @return int Timestamp
15623 */
15624 public function date_strtotime($date)
15625 {
15626 $strtotime = strtotime($date);
15627 if ($strtotime === -1 || $strtotime === false)
15628 {
15629 return false;
15630 }
15631 else
15632 {
15633 return $strtotime;
15634 }
15635 }
15636 }
15637
15638 /**
15639 * Parses XML into something sane
15640 *
15641 *
15642 * This class can be overloaded with {@see SimplePie::set_parser_class()}
15643 *
15644 * @package SimplePie
15645 * @subpackage Parsing
15646 */
15647 class SimplePie_Parser
15648 {
15649 var $error_code;
15650 var $error_string;
15651 var $current_line;
15652 var $current_column;
15653 var $current_byte;
15654 var $separator = ' ';
15655 var $namespace = array('');
15656 var $element = array('');
15657 var $xml_base = array('');
15658 var $xml_base_explicit = array(false);
15659 var $xml_lang = array('');
15660 var $data = array();
15661 var $datas = array(array());
15662 var $current_xhtml_construct = -1;
15663 var $encoding;
15664 protected $registry;
15665
15666 public function set_registry(SimplePie_Registry $registry)
15667 {
15668 $this->registry = $registry;
15669 }
15670
15671 public function parse(&$data, $encoding)
15672 {
15673 // Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character
15674 if (strtoupper($encoding) === 'US-ASCII')
15675 {
15676 $this->encoding = 'UTF-8';
15677 }
15678 else
15679 {
15680 $this->encoding = $encoding;
15681 }
15682
15683 // Strip BOM:
15684 // UTF-32 Big Endian BOM
15685 if (substr($data, 0, 4) === "\x00\x00\xFE\xFF")
15686 {
15687 $data = substr($data, 4);
15688 }
15689 // UTF-32 Little Endian BOM
15690 elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00")
15691 {
15692 $data = substr($data, 4);
15693 }
15694 // UTF-16 Big Endian BOM
15695 elseif (substr($data, 0, 2) === "\xFE\xFF")
15696 {
15697 $data = substr($data, 2);
15698 }
15699 // UTF-16 Little Endian BOM
15700 elseif (substr($data, 0, 2) === "\xFF\xFE")
15701 {
15702 $data = substr($data, 2);
15703 }
15704 // UTF-8 BOM
15705 elseif (substr($data, 0, 3) === "\xEF\xBB\xBF")
15706 {
15707 $data = substr($data, 3);
15708 }
15709
15710 if (substr($data, 0, 5) === '<?xml' && strspn(substr($data, 5, 1), "\x09\x0A\x0D\x20") && ($pos = strpos($data, '?>')) !== false)
15711 {
15712 $declaration = $this->registry->create('XML_Declaration_Parser', array(substr($data, 5, $pos - 5)));
15713 if ($declaration->parse())
15714 {
15715 $data = substr($data, $pos + 2);
15716 $data = '<?xml version="' . $declaration->version . '" encoding="' . $encoding . '" standalone="' . (($declaration->standalone) ? 'yes' : 'no') . '"?>' . $data;
15717 }
15718 else
15719 {
15720 $this->error_string = 'SimplePie bug! Please report this!';
15721 return false;
15722 }
15723 }
15724
15725 $return = true;
15726
15727 static $xml_is_sane = null;
15728 if ($xml_is_sane === null)
15729 {
15730 $parser_check = xml_parser_create();
15731 xml_parse_into_struct($parser_check, '<foo>&amp;</foo>', $values);
15732 xml_parser_free($parser_check);
15733 $xml_is_sane = isset($values[0]['value']);
15734 }
15735
15736 // Create the parser
15737 if ($xml_is_sane)
15738 {
15739 $xml = xml_parser_create_ns($this->encoding, $this->separator);
15740 xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1);
15741 xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0);
15742 xml_set_object($xml, $this);
15743 xml_set_character_data_handler($xml, 'cdata');
15744 xml_set_element_handler($xml, 'tag_open', 'tag_close');
15745
15746 // Parse!
15747 if (!xml_parse($xml, $data, true))
15748 {
15749 $this->error_code = xml_get_error_code($xml);
15750 $this->error_string = xml_error_string($this->error_code);
15751 $return = false;
15752 }
15753 $this->current_line = xml_get_current_line_number($xml);
15754 $this->current_column = xml_get_current_column_number($xml);
15755 $this->current_byte = xml_get_current_byte_index($xml);
15756 xml_parser_free($xml);
15757 return $return;
15758 }
15759 else
15760 {
15761 libxml_clear_errors();
15762 $xml = new XMLReader();
15763 $xml->xml($data);
15764 while (@$xml->read())
15765 {
15766 switch ($xml->nodeType)
15767 {
15768
15769 case constant('XMLReader::END_ELEMENT'):
15770 if ($xml->namespaceURI !== '')
15771 {
15772 $tagName = $xml->namespaceURI . $this->separator . $xml->localName;
15773 }
15774 else
15775 {
15776 $tagName = $xml->localName;
15777 }
15778 $this->tag_close(null, $tagName);
15779 break;
15780 case constant('XMLReader::ELEMENT'):
15781 $empty = $xml->isEmptyElement;
15782 if ($xml->namespaceURI !== '')
15783 {
15784 $tagName = $xml->namespaceURI . $this->separator . $xml->localName;
15785 }
15786 else
15787 {
15788 $tagName = $xml->localName;
15789 }
15790 $attributes = array();
15791 while ($xml->moveToNextAttribute())
15792 {
15793 if ($xml->namespaceURI !== '')
15794 {
15795 $attrName = $xml->namespaceURI . $this->separator . $xml->localName;
15796 }
15797 else
15798 {
15799 $attrName = $xml->localName;
15800 }
15801 $attributes[$attrName] = $xml->value;
15802 }
15803 $this->tag_open(null, $tagName, $attributes);
15804 if ($empty)
15805 {
15806 $this->tag_close(null, $tagName);
15807 }
15808 break;
15809 case constant('XMLReader::TEXT'):
15810
15811 case constant('XMLReader::CDATA'):
15812 $this->cdata(null, $xml->value);
15813 break;
15814 }
15815 }
15816 if ($error = libxml_get_last_error())
15817 {
15818 $this->error_code = $error->code;
15819 $this->error_string = $error->message;
15820 $this->current_line = $error->line;
15821 $this->current_column = $error->column;
15822 return false;
15823 }
15824 else
15825 {
15826 return true;
15827 }
15828 }
15829 }
15830
15831 public function get_error_code()
15832 {
15833 return $this->error_code;
15834 }
15835
15836 public function get_error_string()
15837 {
15838 return $this->error_string;
15839 }
15840
15841 public function get_current_line()
15842 {
15843 return $this->current_line;
15844 }
15845
15846 public function get_current_column()
15847 {
15848 return $this->current_column;
15849 }
15850
15851 public function get_current_byte()
15852 {
15853 return $this->current_byte;
15854 }
15855
15856 public function get_data()
15857 {
15858 return $this->data;
15859 }
15860
15861 public function tag_open($parser, $tag, $attributes)
15862 {
15863 list($this->namespace[], $this->element[]) = $this->split_ns($tag);
15864
15865 $attribs = array();
15866 foreach ($attributes as $name => $value)
15867 {
15868 list($attrib_namespace, $attribute) = $this->split_ns($name);
15869 $attribs[$attrib_namespace][$attribute] = $value;
15870 }
15871
15872 if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['base']))
15873 {
15874 $base = $this->registry->call('Misc', 'absolutize_url', array($attribs[SIMPLEPIE_NAMESPACE_XML]['base'], end($this->xml_base)));
15875 if ($base !== false)
15876 {
15877 $this->xml_base[] = $base;
15878 $this->xml_base_explicit[] = true;
15879 }
15880 }
15881 else
15882 {
15883 $this->xml_base[] = end($this->xml_base);
15884 $this->xml_base_explicit[] = end($this->xml_base_explicit);
15885 }
15886
15887 if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['lang']))
15888 {
15889 $this->xml_lang[] = $attribs[SIMPLEPIE_NAMESPACE_XML]['lang'];
15890 }
15891 else
15892 {
15893 $this->xml_lang[] = end($this->xml_lang);
15894 }
15895
15896 if ($this->current_xhtml_construct >= 0)
15897 {
15898 $this->current_xhtml_construct++;
15899 if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML)
15900 {
15901 $this->data['data'] .= '<' . end($this->element);
15902 if (isset($attribs['']))
15903 {
15904 foreach ($attribs[''] as $name => $value)
15905 {
15906 $this->data['data'] .= ' ' . $name . '="' . htmlspecialchars($value, ENT_COMPAT, $this->encoding) . '"';
15907 }
15908 }
15909 $this->data['data'] .= '>';
15910 }
15911 }
15912 else
15913 {
15914 $this->datas[] =& $this->data;
15915 $this->data =& $this->data['child'][end($this->namespace)][end($this->element)][];
15916 $this->data = array('data' => '', 'attribs' => $attribs, 'xml_base' => end($this->xml_base), 'xml_base_explicit' => end($this->xml_base_explicit), 'xml_lang' => end($this->xml_lang));
15917 if ((end($this->namespace) === SIMPLEPIE_NAMESPACE_ATOM_03 && in_array(end($this->element), array('title', 'tagline', 'copyright', 'info', 'summary', 'content')) && isset($attribs['']['mode']) && $attribs['']['mode'] === 'xml')
15918 || (end($this->namespace) === SIMPLEPIE_NAMESPACE_ATOM_10 && in_array(end($this->element), array('rights', 'subtitle', 'summary', 'info', 'title', 'content')) && isset($attribs['']['type']) && $attribs['']['type'] === 'xhtml')
15919 || (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_20 && in_array(end($this->element), array('title')))
15920 || (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_090 && in_array(end($this->element), array('title')))
15921 || (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_10 && in_array(end($this->element), array('title'))))
15922 {
15923 $this->current_xhtml_construct = 0;
15924 }
15925 }
15926 }
15927
15928 public function cdata($parser, $cdata)
15929 {
15930 if ($this->current_xhtml_construct >= 0)
15931 {
15932 $this->data['data'] .= htmlspecialchars($cdata, ENT_QUOTES, $this->encoding);
15933 }
15934 else
15935 {
15936 $this->data['data'] .= $cdata;
15937 }
15938 }
15939
15940 public function tag_close($parser, $tag)
15941 {
15942 if ($this->current_xhtml_construct >= 0)
15943 {
15944 $this->current_xhtml_construct--;
15945 if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML && !in_array(end($this->element), array('area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param')))
15946 {
15947 $this->data['data'] .= '</' . end($this->element) . '>';
15948 }
15949 }
15950 if ($this->current_xhtml_construct === -1)
15951 {
15952 $this->data =& $this->datas[count($this->datas) - 1];
15953 array_pop($this->datas);
15954 }
15955
15956 array_pop($this->element);
15957 array_pop($this->namespace);
15958 array_pop($this->xml_base);
15959 array_pop($this->xml_base_explicit);
15960 array_pop($this->xml_lang);
15961 }
15962
15963 public function split_ns($string)
15964 {
15965 static $cache = array();
15966 if (!isset($cache[$string]))
15967 {
15968 if ($pos = strpos($string, $this->separator))
15969 {
15970 static $separator_length;
15971 if (!$separator_length)
15972 {
15973 $separator_length = strlen($this->separator);
15974 }
15975 $namespace = substr($string, 0, $pos);
15976 $local_name = substr($string, $pos + $separator_length);
15977 if (strtolower($namespace) === SIMPLEPIE_NAMESPACE_ITUNES)
15978 {
15979 $namespace = SIMPLEPIE_NAMESPACE_ITUNES;
15980 }
15981
15982 // Normalize the Media RSS namespaces
15983 if ($namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG ||
15984 $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG2 ||
15985 $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG3 ||
15986 $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG4 ||
15987 $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG5 )
15988 {
15989 $namespace = SIMPLEPIE_NAMESPACE_MEDIARSS;
15990 }
15991 $cache[$string] = array($namespace, $local_name);
15992 }
15993 else
15994 {
15995 $cache[$string] = array('', $string);
15996 }
15997 }
15998 return $cache[$string];
15999 }
16000 }
16001
16002 /**
16003 * Handles `<media:rating>` or `<itunes:explicit>` tags as defined in Media RSS and iTunes RSS respectively
16004 *
16005 * Used by {@see SimplePie_Enclosure::get_rating()} and {@see SimplePie_Enclosure::get_ratings()}
16006 *
16007 * This class can be overloaded with {@see SimplePie::set_rating_class()}
16008 *
16009 * @package SimplePie
16010 * @subpackage API
16011 */
16012 class SimplePie_Rating
16013 {
16014 /**
16015 * Rating scheme
16016 *
16017 * @var string
16018 * @see get_scheme()
16019 */
16020 var $scheme;
16021
16022 /**
16023 * Rating value
16024 *
16025 * @var string
16026 * @see get_value()
16027 */
16028 var $value;
16029
16030 /**
16031 * Constructor, used to input the data
16032 *
16033 * For documentation on all the parameters, see the corresponding
16034 * properties and their accessors
16035 */
16036 public function __construct($scheme = null, $value = null)
16037 {
16038 $this->scheme = $scheme;
16039 $this->value = $value;
16040 }
16041
16042 /**
16043 * String-ified version
16044 *
16045 * @return string
16046 */
16047 public function __toString()
16048 {
16049 // There is no $this->data here
16050 return md5(serialize($this));
16051 }
16052
16053 /**
16054 * Get the organizational scheme for the rating
16055 *
16056 * @return string|null
16057 */
16058 public function get_scheme()
16059 {
16060 if ($this->scheme !== null)
16061 {
16062 return $this->scheme;
16063 }
16064 else
16065 {
16066 return null;
16067 }
16068 }
16069
16070 /**
16071 * Get the value of the rating
16072 *
16073 * @return string|null
16074 */
16075 public function get_value()
16076 {
16077 if ($this->value !== null)
16078 {
16079 return $this->value;
16080 }
16081 else
16082 {
16083 return null;
16084 }
16085 }
16086 }
16087
16088 /**
16089 * Handles creating objects and calling methods
16090 *
16091 * Access this via {@see SimplePie::get_registry()}
16092 *
16093 * @package SimplePie
16094 */
16095 class SimplePie_Registry
16096 {
16097 /**
16098 * Default class mapping
16099 *
16100 * Overriding classes *must* subclass these.
16101 *
16102 * @var array
16103 */
16104 protected $default = array(
16105 'Cache' => 'SimplePie_Cache',
16106 'Locator' => 'SimplePie_Locator',
16107 'Parser' => 'SimplePie_Parser',
16108 'File' => 'SimplePie_File',
16109 'Sanitize' => 'SimplePie_Sanitize',
16110 'Item' => 'SimplePie_Item',
16111 'Author' => 'SimplePie_Author',
16112 'Category' => 'SimplePie_Category',
16113 'Enclosure' => 'SimplePie_Enclosure',
16114 'Caption' => 'SimplePie_Caption',
16115 'Copyright' => 'SimplePie_Copyright',
16116 'Credit' => 'SimplePie_Credit',
16117 'Rating' => 'SimplePie_Rating',
16118 'Restriction' => 'SimplePie_Restriction',
16119 'Content_Type_Sniffer' => 'SimplePie_Content_Type_Sniffer',
16120 'Source' => 'SimplePie_Source',
16121 'Misc' => 'SimplePie_Misc',
16122 'XML_Declaration_Parser' => 'SimplePie_XML_Declaration_Parser',
16123 'Parse_Date' => 'SimplePie_Parse_Date',
16124 );
16125
16126 /**
16127 * Class mapping
16128 *
16129 * @see register()
16130 * @var array
16131 */
16132 protected $classes = array();
16133
16134 /**
16135 * Legacy classes
16136 *
16137 * @see register()
16138 * @var array
16139 */
16140 protected $legacy = array();
16141
16142 /**
16143 * Constructor
16144 *
16145 * No-op
16146 */
16147 public function __construct() { }
16148
16149 /**
16150 * Register a class
16151 *
16152 * @param string $type See {@see $default} for names
16153 * @param string $class Class name, must subclass the corresponding default
16154 * @param bool $legacy Whether to enable legacy support for this class
16155 * @return bool Successfulness
16156 */
16157 public function register($type, $class, $legacy = false)
16158 {
16159 if (!is_subclass_of($class, $this->default[$type]))
16160 {
16161 return false;
16162 }
16163
16164 $this->classes[$type] = $class;
16165
16166 if ($legacy)
16167 {
16168 $this->legacy[] = $class;
16169 }
16170
16171 return true;
16172 }
16173
16174 /**
16175 * Get the class registered for a type
16176 *
16177 * Where possible, use {@see create()} or {@see call()} instead
16178 *
16179 * @param string $type
16180 * @return string|null
16181 */
16182 public function get_class($type)
16183 {
16184 if (!empty($this->classes[$type]))
16185 {
16186 return $this->classes[$type];
16187 }
16188 if (!empty($this->default[$type]))
16189 {
16190 return $this->default[$type];
16191 }
16192
16193 return null;
16194 }
16195
16196 /**
16197 * Create a new instance of a given type
16198 *
16199 * @param string $type
16200 * @param array $parameters Parameters to pass to the constructor
16201 * @return object Instance of class
16202 */
16203 public function &create($type, $parameters = array())
16204 {
16205 $class = $this->get_class($type);
16206
16207 if (in_array($class, $this->legacy))
16208 {
16209 switch ($type)
16210 {
16211 case 'locator':
16212 // Legacy: file, timeout, useragent, file_class, max_checked_feeds, content_type_sniffer_class
16213 // Specified: file, timeout, useragent, max_checked_feeds
16214 $replacement = array($this->get_class('file'), $parameters[3], $this->get_class('content_type_sniffer'));
16215 array_splice($parameters, 3, 1, $replacement);
16216 break;
16217 }
16218 }
16219
16220 if (!method_exists($class, '__construct'))
16221 {
16222 $instance = new $class;
16223 }
16224 else
16225 {
16226 $reflector = new ReflectionClass($class);
16227 $instance = $reflector->newInstanceArgs($parameters);
16228 }
16229
16230 if (method_exists($instance, 'set_registry'))
16231 {
16232 $instance->set_registry($this);
16233 }
16234 return $instance;
16235 }
16236
16237 /**
16238 * Call a static method for a type
16239 *
16240 * @param string $type
16241 * @param string $method
16242 * @param array $parameters
16243 * @return mixed
16244 */
16245 public function &call($type, $method, $parameters = array())
16246 {
16247 $class = $this->get_class($type);
16248
16249 if (in_array($class, $this->legacy))
16250 {
16251 switch ($type)
16252 {
16253 case 'Cache':
16254 // For backwards compatibility with old non-static
16255 // Cache::create() methods
16256 if ($method === 'get_handler')
16257 {
16258 $result = @call_user_func_array(array($class, 'create'), $parameters);
16259 return $result;
16260 }
16261 break;
16262 }
16263 }
16264
16265 $result = call_user_func_array(array($class, $method), $parameters);
16266 return $result;
16267 }
16268 }
16269
16270 /**
16271 * Handles `<media:restriction>` as defined in Media RSS
16272 *
16273 * Used by {@see SimplePie_Enclosure::get_restriction()} and {@see SimplePie_Enclosure::get_restrictions()}
16274 *
16275 * This class can be overloaded with {@see SimplePie::set_restriction_class()}
16276 *
16277 * @package SimplePie
16278 * @subpackage API
16279 */
16280 class SimplePie_Restriction
16281 {
16282 /**
16283 * Relationship ('allow'/'deny')
16284 *
16285 * @var string
16286 * @see get_relationship()
16287 */
16288 var $relationship;
16289
16290 /**
16291 * Type of restriction
16292 *
16293 * @var string
16294 * @see get_type()
16295 */
16296 var $type;
16297
16298 /**
16299 * Restricted values
16300 *
16301 * @var string
16302 * @see get_value()
16303 */
16304 var $value;
16305
16306 /**
16307 * Constructor, used to input the data
16308 *
16309 * For documentation on all the parameters, see the corresponding
16310 * properties and their accessors
16311 */
16312 public function __construct($relationship = null, $type = null, $value = null)
16313 {
16314 $this->relationship = $relationship;
16315 $this->type = $type;
16316 $this->value = $value;
16317 }
16318
16319 /**
16320 * String-ified version
16321 *
16322 * @return string
16323 */
16324 public function __toString()
16325 {
16326 // There is no $this->data here
16327 return md5(serialize($this));
16328 }
16329
16330 /**
16331 * Get the relationship
16332 *
16333 * @return string|null Either 'allow' or 'deny'
16334 */
16335 public function get_relationship()
16336 {
16337 if ($this->relationship !== null)
16338 {
16339 return $this->relationship;
16340 }
16341 else
16342 {
16343 return null;
16344 }
16345 }
16346
16347 /**
16348 * Get the type
16349 *
16350 * @return string|null
16351 */
16352 public function get_type()
16353 {
16354 if ($this->type !== null)
16355 {
16356 return $this->type;
16357 }
16358 else
16359 {
16360 return null;
16361 }
16362 }
16363
16364 /**
16365 * Get the list of restricted things
16366 *
16367 * @return string|null
16368 */
16369 public function get_value()
16370 {
16371 if ($this->value !== null)
16372 {
16373 return $this->value;
16374 }
16375 else
16376 {
16377 return null;
16378 }
16379 }
16380 }
16381
16382 /**
16383 * Used for data cleanup and post-processing
16384 *
16385 *
16386 * This class can be overloaded with {@see SimplePie::set_sanitize_class()}
16387 *
16388 * @package SimplePie
16389 * @todo Move to using an actual HTML parser (this will allow tags to be properly stripped, and to switch between HTML and XHTML), this will also make it easier to shorten a string while preserving HTML tags
16390 */
16391 class SimplePie_Sanitize
16392 {
16393 // Private vars
16394 var $base;
16395
16396 // Options
16397 var $remove_div = true;
16398 var $image_handler = '';
16399 var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style');
16400 var $encode_instead_of_strip = false;
16401 var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc');
16402 var $strip_comments = false;
16403 var $output_encoding = 'UTF-8';
16404 var $enable_cache = true;
16405 var $cache_location = './cache';
16406 var $cache_name_function = 'md5';
16407 var $timeout = 10;
16408 var $useragent = '';
16409 var $force_fsockopen = false;
16410 var $replace_url_attributes = null;
16411
16412 public function __construct()
16413 {
16414 // Set defaults
16415 $this->set_url_replacements(null);
16416 }
16417
16418 public function remove_div($enable = true)
16419 {
16420 $this->remove_div = (bool) $enable;
16421 }
16422
16423 public function set_image_handler($page = false)
16424 {
16425 if ($page)
16426 {
16427 $this->image_handler = (string) $page;
16428 }
16429 else
16430 {
16431 $this->image_handler = false;
16432 }
16433 }
16434
16435 public function set_registry(SimplePie_Registry $registry)
16436 {
16437 $this->registry = $registry;
16438 }
16439
16440 public function pass_cache_data($enable_cache = true, $cache_location = './cache', $cache_name_function = 'md5', $cache_class = 'SimplePie_Cache')
16441 {
16442 if (isset($enable_cache))
16443 {
16444 $this->enable_cache = (bool) $enable_cache;
16445 }
16446
16447 if ($cache_location)
16448 {
16449 $this->cache_location = (string) $cache_location;
16450 }
16451
16452 if ($cache_name_function)
16453 {
16454 $this->cache_name_function = (string) $cache_name_function;
16455 }
16456 }
16457
16458 public function pass_file_data($file_class = 'SimplePie_File', $timeout = 10, $useragent = '', $force_fsockopen = false)
16459 {
16460 if ($timeout)
16461 {
16462 $this->timeout = (string) $timeout;
16463 }
16464
16465 if ($useragent)
16466 {
16467 $this->useragent = (string) $useragent;
16468 }
16469
16470 if ($force_fsockopen)
16471 {
16472 $this->force_fsockopen = (string) $force_fsockopen;
16473 }
16474 }
16475
16476 public function strip_htmltags($tags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'))
16477 {
16478 if ($tags)
16479 {
16480 if (is_array($tags))
16481 {
16482 $this->strip_htmltags = $tags;
16483 }
16484 else
16485 {
16486 $this->strip_htmltags = explode(',', $tags);
16487 }
16488 }
16489 else
16490 {
16491 $this->strip_htmltags = false;
16492 }
16493 }
16494
16495 public function encode_instead_of_strip($encode = false)
16496 {
16497 $this->encode_instead_of_strip = (bool) $encode;
16498 }
16499
16500 public function strip_attributes($attribs = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'))
16501 {
16502 if ($attribs)
16503 {
16504 if (is_array($attribs))
16505 {
16506 $this->strip_attributes = $attribs;
16507 }
16508 else
16509 {
16510 $this->strip_attributes = explode(',', $attribs);
16511 }
16512 }
16513 else
16514 {
16515 $this->strip_attributes = false;
16516 }
16517 }
16518
16519 public function strip_comments($strip = false)
16520 {
16521 $this->strip_comments = (bool) $strip;
16522 }
16523
16524 public function set_output_encoding($encoding = 'UTF-8')
16525 {
16526 $this->output_encoding = (string) $encoding;
16527 }
16528
16529 /**
16530 * Set element/attribute key/value pairs of HTML attributes
16531 * containing URLs that need to be resolved relative to the feed
16532 *
16533 * Defaults to |a|@href, |area|@href, |blockquote|@cite, |del|@cite,
16534 * |form|@action, |img|@longdesc, |img|@src, |input|@src, |ins|@cite,
16535 * |q|@cite
16536 *
16537 * @since 1.0
16538 * @param array|null $element_attribute Element/attribute key/value pairs, null for default
16539 */
16540 public function set_url_replacements($element_attribute = null)
16541 {
16542 if ($element_attribute === null)
16543 {
16544 $element_attribute = array(
16545 'a' => 'href',
16546 'area' => 'href',
16547 'blockquote' => 'cite',
16548 'del' => 'cite',
16549 'form' => 'action',
16550 'img' => array(
16551 'longdesc',
16552 'src'
16553 ),
16554 'input' => 'src',
16555 'ins' => 'cite',
16556 'q' => 'cite'
16557 );
16558 }
16559 $this->replace_url_attributes = (array) $element_attribute;
16560 }
16561
16562 public function sanitize($data, $type, $base = '')
16563 {
16564 $data = trim($data);
16565 if ($data !== '' || $type & SIMPLEPIE_CONSTRUCT_IRI)
16566 {
16567 if ($type & SIMPLEPIE_CONSTRUCT_MAYBE_HTML)
16568 {
16569 if (preg_match('/(&(#(x[0-9a-fA-F]+|[0-9]+)|[a-zA-Z0-9]+)|<\/[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>)/', $data))
16570 {
16571 $type |= SIMPLEPIE_CONSTRUCT_HTML;
16572 }
16573 else
16574 {
16575 $type |= SIMPLEPIE_CONSTRUCT_TEXT;
16576 }
16577 }
16578
16579 if ($type & SIMPLEPIE_CONSTRUCT_BASE64)
16580 {
16581 $data = base64_decode($data);
16582 }
16583
16584 if ($type & (SIMPLEPIE_CONSTRUCT_HTML | SIMPLEPIE_CONSTRUCT_XHTML))
16585 {
16586
16587 $document = new DOMDocument();
16588 $document->encoding = 'UTF-8';
16589 $data = $this->preprocess($data, $type);
16590
16591 set_error_handler(array('SimplePie_Misc', 'silence_errors'));
16592 $document->loadHTML($data);
16593 restore_error_handler();
16594
16595 // Strip comments
16596 if ($this->strip_comments)
16597 {
16598 $xpath = new DOMXPath($document);
16599 $comments = $xpath->query('//comment()');
16600
16601 foreach ($comments as $comment)
16602 {
16603 $comment->parentNode->removeChild($comment);
16604 }
16605 }
16606
16607 // Strip out HTML tags and attributes that might cause various security problems.
16608 // Based on recommendations by Mark Pilgrim at:
16609 // http://diveintomark.org/archives/2003/06/12/how_to_consume_rss_safely
16610 if ($this->strip_htmltags)
16611 {
16612 foreach ($this->strip_htmltags as $tag)
16613 {
16614 $this->strip_tag($tag, $document, $type);
16615 }
16616 }
16617
16618 if ($this->strip_attributes)
16619 {
16620 foreach ($this->strip_attributes as $attrib)
16621 {
16622 $this->strip_attr($attrib, $document);
16623 }
16624 }
16625
16626 // Replace relative URLs
16627 $this->base = $base;
16628 foreach ($this->replace_url_attributes as $element => $attributes)
16629 {
16630 $this->replace_urls($document, $element, $attributes);
16631 }
16632
16633 // If image handling (caching, etc.) is enabled, cache and rewrite all the image tags.
16634 if (isset($this->image_handler) && ((string) $this->image_handler) !== '' && $this->enable_cache)
16635 {
16636 $images = $document->getElementsByTagName('img');
16637 foreach ($images as $img)
16638 {
16639 if ($img->hasAttribute('src'))
16640 {
16641 $image_url = call_user_func($this->cache_name_function, $img->getAttribute('src'));
16642 $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, $image_url, 'spi'));
16643
16644 if ($cache->load())
16645 {
16646 $img->setAttribute('src', $this->image_handler . $image_url);
16647 }
16648 else
16649 {
16650 $file = $this->registry->create('File', array($img['attribs']['src']['data'], $this->timeout, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $this->useragent, $this->force_fsockopen));
16651 $headers = $file->headers;
16652
16653 if ($file->success && ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300)))
16654 {
16655 if ($cache->save(array('headers' => $file->headers, 'body' => $file->body)))
16656 {
16657 $img->setAttribute('src', $this->image_handler . $image_url);
16658 }
16659 else
16660 {
16661 trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING);
16662 }
16663 }
16664 }
16665 }
16666 }
16667 }
16668
16669 // Remove the DOCTYPE
16670 // Seems to cause segfaulting if we don't do this
16671 if ($document->firstChild instanceof DOMDocumentType)
16672 {
16673 $document->removeChild($document->firstChild);
16674 }
16675
16676 // Move everything from the body to the root
16677 $real_body = $document->getElementsByTagName('body')->item(0)->childNodes->item(0);
16678 $document->replaceChild($real_body, $document->firstChild);
16679
16680 // Finally, convert to a HTML string
16681 $data = trim($document->saveHTML());
16682
16683 if ($this->remove_div)
16684 {
16685 $data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '', $data);
16686 $data = preg_replace('/<\/div>$/', '', $data);
16687 }
16688 else
16689 {
16690 $data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '<div>', $data);
16691 }
16692 }
16693
16694 if ($type & SIMPLEPIE_CONSTRUCT_IRI)
16695 {
16696 $absolute = $this->registry->call('Misc', 'absolutize_url', array($data, $base));
16697 if ($absolute !== false)
16698 {
16699 $data = $absolute;
16700 }
16701 }
16702
16703 if ($type & (SIMPLEPIE_CONSTRUCT_TEXT | SIMPLEPIE_CONSTRUCT_IRI))
16704 {
16705 $data = htmlspecialchars($data, ENT_COMPAT, 'UTF-8');
16706 }
16707
16708 if ($this->output_encoding !== 'UTF-8')
16709 {
16710 $data = $this->registry->call('Misc', 'change_encoding', array($data, 'UTF-8', $this->output_encoding));
16711 }
16712 }
16713 return $data;
16714 }
16715
16716 protected function preprocess($html, $type)
16717 {
16718 $ret = '';
16719 if ($type & ~SIMPLEPIE_CONSTRUCT_XHTML)
16720 {
16721 // Atom XHTML constructs are wrapped with a div by default
16722 // Note: No protection if $html contains a stray </div>!
16723 $html = '<div>' . $html . '</div>';
16724 $ret .= '<!DOCTYPE html>';
16725 $content_type = 'text/html';
16726 }
16727 else
16728 {
16729 $ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
16730 $content_type = 'application/xhtml+xml';
16731 }
16732
16733 $ret .= '<html><head>';
16734 $ret .= '<meta http-equiv="Content-Type" content="' . $content_type . '; charset=utf-8" />';
16735 $ret .= '</head><body>' . $html . '</body></html>';
16736 return $ret;
16737 }
16738
16739 public function replace_urls($document, $tag, $attributes)
16740 {
16741 if (!is_array($attributes))
16742 {
16743 $attributes = array($attributes);
16744 }
16745
16746 if (!is_array($this->strip_htmltags) || !in_array($tag, $this->strip_htmltags))
16747 {
16748 $elements = $document->getElementsByTagName($tag);
16749 foreach ($elements as $element)
16750 {
16751 foreach ($attributes as $attribute)
16752 {
16753 if ($element->hasAttribute($attribute))
16754 {
16755 $value = $this->registry->call('Misc', 'absolutize_url', array($element->getAttribute($attribute), $this->base));
16756 if ($value !== false)
16757 {
16758 $element->setAttribute($attribute, $value);
16759 }
16760 }
16761 }
16762 }
16763 }
16764 }
16765
16766 public function do_strip_htmltags($match)
16767 {
16768 if ($this->encode_instead_of_strip)
16769 {
16770 if (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style')))
16771 {
16772 $match[1] = htmlspecialchars($match[1], ENT_COMPAT, 'UTF-8');
16773 $match[2] = htmlspecialchars($match[2], ENT_COMPAT, 'UTF-8');
16774 return "&lt;$match[1]$match[2]&gt;$match[3]&lt;/$match[1]&gt;";
16775 }
16776 else
16777 {
16778 return htmlspecialchars($match[0], ENT_COMPAT, 'UTF-8');
16779 }
16780 }
16781 elseif (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style')))
16782 {
16783 return $match[4];
16784 }
16785 else
16786 {
16787 return '';
16788 }
16789 }
16790
16791 protected function strip_tag($tag, $document, $type)
16792 {
16793 $xpath = new DOMXPath($document);
16794 $elements = $xpath->query('body//' . $tag);
16795 if ($this->encode_instead_of_strip)
16796 {
16797 foreach ($elements as $element)
16798 {
16799 $fragment = $document->createDocumentFragment();
16800
16801 // For elements which aren't script or style, include the tag itself
16802 if (!in_array($tag, array('script', 'style')))
16803 {
16804 $text = '<' . $tag;
16805 if ($element->hasAttributes())
16806 {
16807 $attrs = array();
16808 foreach ($element->attributes as $name => $attr)
16809 {
16810 $value = $attr->value;
16811
16812 // In XHTML, empty values should never exist, so we repeat the value
16813 if (empty($value) && ($type & SIMPLEPIE_CONSTRUCT_XHTML))
16814 {
16815 $value = $name;
16816 }
16817 // For HTML, empty is fine
16818 elseif (empty($value) && ($type & SIMPLEPIE_CONSTRUCT_HTML))
16819 {
16820 $attrs[] = $name;
16821 continue;
16822 }
16823
16824 // Standard attribute text
16825 $attrs[] = $name . '="' . $attr->value . '"';
16826 }
16827 $text .= ' ' . implode(' ', $attrs);
16828 }
16829 $text .= '>';
16830 $fragment->appendChild(new DOMText($text));
16831 }
16832
16833 $number = $element->childNodes->length;
16834 for ($i = $number; $i > 0; $i--)
16835 {
16836 $child = $element->childNodes->item(0);
16837 $fragment->appendChild($child);
16838 }
16839
16840 if (!in_array($tag, array('script', 'style')))
16841 {
16842 $fragment->appendChild(new DOMText('</' . $tag . '>'));
16843 }
16844
16845 $element->parentNode->replaceChild($fragment, $element);
16846 }
16847
16848 return;
16849 }
16850 elseif (in_array($tag, array('script', 'style')))
16851 {
16852 foreach ($elements as $element)
16853 {
16854 $element->parentNode->removeChild($element);
16855 }
16856
16857 return;
16858 }
16859 else
16860 {
16861 foreach ($elements as $element)
16862 {
16863 $fragment = $document->createDocumentFragment();
16864 $number = $element->childNodes->length;
16865 for ($i = $number; $i > 0; $i--)
16866 {
16867 $child = $element->childNodes->item(0);
16868 $fragment->appendChild($child);
16869 }
16870
16871 $element->parentNode->replaceChild($fragment, $element);
16872 }
16873 }
16874 }
16875
16876 protected function strip_attr($attrib, $document)
16877 {
16878 $xpath = new DOMXPath($document);
16879 $elements = $xpath->query('//*[@' . $attrib . ']');
16880
16881 foreach ($elements as $element)
16882 {
16883 $element->removeAttribute($attrib);
16884 }
16885 }
16886 }
16887
16888 /**
16889 * Handles `<atom:source>`
16890 *
16891 * Used by {@see SimplePie_Item::get_source()}
16892 *
16893 * This class can be overloaded with {@see SimplePie::set_source_class()}
16894 *
16895 * @package SimplePie
16896 * @subpackage API
16897 */
16898 class SimplePie_Source
16899 {
16900 var $item;
16901 var $data = array();
16902 protected $registry;
16903
16904 public function __construct($item, $data)
16905 {
16906 $this->item = $item;
16907 $this->data = $data;
16908 }
16909
16910 public function set_registry(SimplePie_Registry $registry)
16911 {
16912 $this->registry = $registry;
16913 }
16914
16915 public function __toString()
16916 {
16917 return md5(serialize($this->data));
16918 }
16919
16920 public function get_source_tags($namespace, $tag)
16921 {
16922 if (isset($this->data['child'][$namespace][$tag]))
16923 {
16924 return $this->data['child'][$namespace][$tag];
16925 }
16926 else
16927 {
16928 return null;
16929 }
16930 }
16931
16932 public function get_base($element = array())
16933 {
16934 return $this->item->get_base($element);
16935 }
16936
16937 public function sanitize($data, $type, $base = '')
16938 {
16939 return $this->item->sanitize($data, $type, $base);
16940 }
16941
16942 public function get_item()
16943 {
16944 return $this->item;
16945 }
16946
16947 public function get_title()
16948 {
16949 if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title'))
16950 {
16951 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
16952 }
16953 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title'))
16954 {
16955 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
16956 }
16957 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
16958 {
16959 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
16960 }
16961 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
16962 {
16963 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
16964 }
16965 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
16966 {
16967 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
16968 }
16969 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
16970 {
16971 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
16972 }
16973 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
16974 {
16975 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
16976 }
16977 else
16978 {
16979 return null;
16980 }
16981 }
16982
16983 public function get_category($key = 0)
16984 {
16985 $categories = $this->get_categories();
16986 if (isset($categories[$key]))
16987 {
16988 return $categories[$key];
16989 }
16990 else
16991 {
16992 return null;
16993 }
16994 }
16995
16996 public function get_categories()
16997 {
16998 $categories = array();
16999
17000 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category)
17001 {
17002 $term = null;
17003 $scheme = null;
17004 $label = null;
17005 if (isset($category['attribs']['']['term']))
17006 {
17007 $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT);
17008 }
17009 if (isset($category['attribs']['']['scheme']))
17010 {
17011 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
17012 }
17013 if (isset($category['attribs']['']['label']))
17014 {
17015 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
17016 }
17017 $categories[] = $this->registry->create('Category', array($term, $scheme, $label));
17018 }
17019 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category)
17020 {
17021 // This is really the label, but keep this as the term also for BC.
17022 // Label will also work on retrieving because that falls back to term.
17023 $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17024 if (isset($category['attribs']['']['domain']))
17025 {
17026 $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT);
17027 }
17028 else
17029 {
17030 $scheme = null;
17031 }
17032 $categories[] = $this->registry->create('Category', array($term, $scheme, null));
17033 }
17034 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category)
17035 {
17036 $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
17037 }
17038 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category)
17039 {
17040 $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
17041 }
17042
17043 if (!empty($categories))
17044 {
17045 return array_unique($categories);
17046 }
17047 else
17048 {
17049 return null;
17050 }
17051 }
17052
17053 public function get_author($key = 0)
17054 {
17055 $authors = $this->get_authors();
17056 if (isset($authors[$key]))
17057 {
17058 return $authors[$key];
17059 }
17060 else
17061 {
17062 return null;
17063 }
17064 }
17065
17066 public function get_authors()
17067 {
17068 $authors = array();
17069 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author)
17070 {
17071 $name = null;
17072 $uri = null;
17073 $email = null;
17074 if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
17075 {
17076 $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17077 }
17078 if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
17079 {
17080 $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
17081 }
17082 if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
17083 {
17084 $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17085 }
17086 if ($name !== null || $email !== null || $uri !== null)
17087 {
17088 $authors[] = $this->registry->create('Author', array($name, $uri, $email));
17089 }
17090 }
17091 if ($author = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author'))
17092 {
17093 $name = null;
17094 $url = null;
17095 $email = null;
17096 if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
17097 {
17098 $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17099 }
17100 if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
17101 {
17102 $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
17103 }
17104 if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
17105 {
17106 $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17107 }
17108 if ($name !== null || $email !== null || $url !== null)
17109 {
17110 $authors[] = $this->registry->create('Author', array($name, $url, $email));
17111 }
17112 }
17113 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author)
17114 {
17115 $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
17116 }
17117 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author)
17118 {
17119 $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
17120 }
17121 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author)
17122 {
17123 $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
17124 }
17125
17126 if (!empty($authors))
17127 {
17128 return array_unique($authors);
17129 }
17130 else
17131 {
17132 return null;
17133 }
17134 }
17135
17136 public function get_contributor($key = 0)
17137 {
17138 $contributors = $this->get_contributors();
17139 if (isset($contributors[$key]))
17140 {
17141 return $contributors[$key];
17142 }
17143 else
17144 {
17145 return null;
17146 }
17147 }
17148
17149 public function get_contributors()
17150 {
17151 $contributors = array();
17152 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor)
17153 {
17154 $name = null;
17155 $uri = null;
17156 $email = null;
17157 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
17158 {
17159 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17160 }
17161 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
17162 {
17163 $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
17164 }
17165 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
17166 {
17167 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17168 }
17169 if ($name !== null || $email !== null || $uri !== null)
17170 {
17171 $contributors[] = $this->registry->create('Author', array($name, $uri, $email));
17172 }
17173 }
17174 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor)
17175 {
17176 $name = null;
17177 $url = null;
17178 $email = null;
17179 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
17180 {
17181 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17182 }
17183 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
17184 {
17185 $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
17186 }
17187 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
17188 {
17189 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17190 }
17191 if ($name !== null || $email !== null || $url !== null)
17192 {
17193 $contributors[] = $this->registry->create('Author', array($name, $url, $email));
17194 }
17195 }
17196
17197 if (!empty($contributors))
17198 {
17199 return array_unique($contributors);
17200 }
17201 else
17202 {
17203 return null;
17204 }
17205 }
17206
17207 public function get_link($key = 0, $rel = 'alternate')
17208 {
17209 $links = $this->get_links($rel);
17210 if (isset($links[$key]))
17211 {
17212 return $links[$key];
17213 }
17214 else
17215 {
17216 return null;
17217 }
17218 }
17219
17220 /**
17221 * Added for parity between the parent-level and the item/entry-level.
17222 */
17223 public function get_permalink()
17224 {
17225 return $this->get_link(0);
17226 }
17227
17228 public function get_links($rel = 'alternate')
17229 {
17230 if (!isset($this->data['links']))
17231 {
17232 $this->data['links'] = array();
17233 if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link'))
17234 {
17235 foreach ($links as $link)
17236 {
17237 if (isset($link['attribs']['']['href']))
17238 {
17239 $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
17240 $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
17241 }
17242 }
17243 }
17244 if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link'))
17245 {
17246 foreach ($links as $link)
17247 {
17248 if (isset($link['attribs']['']['href']))
17249 {
17250 $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
17251 $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
17252
17253 }
17254 }
17255 }
17256 if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
17257 {
17258 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
17259 }
17260 if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
17261 {
17262 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
17263 }
17264 if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
17265 {
17266 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
17267 }
17268
17269 $keys = array_keys($this->data['links']);
17270 foreach ($keys as $key)
17271 {
17272 if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key)))
17273 {
17274 if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]))
17275 {
17276 $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]);
17277 $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key];
17278 }
17279 else
17280 {
17281 $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key];
17282 }
17283 }
17284 elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY)
17285 {
17286 $this->data['links'][substr($key, 41)] =& $this->data['links'][$key];
17287 }
17288 $this->data['links'][$key] = array_unique($this->data['links'][$key]);
17289 }
17290 }
17291
17292 if (isset($this->data['links'][$rel]))
17293 {
17294 return $this->data['links'][$rel];
17295 }
17296 else
17297 {
17298 return null;
17299 }
17300 }
17301
17302 public function get_description()
17303 {
17304 if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle'))
17305 {
17306 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
17307 }
17308 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline'))
17309 {
17310 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
17311 }
17312 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description'))
17313 {
17314 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
17315 }
17316 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description'))
17317 {
17318 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
17319 }
17320 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description'))
17321 {
17322 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
17323 }
17324 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description'))
17325 {
17326 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17327 }
17328 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description'))
17329 {
17330 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17331 }
17332 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary'))
17333 {
17334 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
17335 }
17336 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle'))
17337 {
17338 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
17339 }
17340 else
17341 {
17342 return null;
17343 }
17344 }
17345
17346 public function get_copyright()
17347 {
17348 if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights'))
17349 {
17350 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
17351 }
17352 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright'))
17353 {
17354 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
17355 }
17356 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright'))
17357 {
17358 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17359 }
17360 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights'))
17361 {
17362 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17363 }
17364 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights'))
17365 {
17366 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17367 }
17368 else
17369 {
17370 return null;
17371 }
17372 }
17373
17374 public function get_language()
17375 {
17376 if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language'))
17377 {
17378 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17379 }
17380 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language'))
17381 {
17382 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17383 }
17384 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language'))
17385 {
17386 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
17387 }
17388 elseif (isset($this->data['xml_lang']))
17389 {
17390 return $this->sanitize($this->data['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
17391 }
17392 else
17393 {
17394 return null;
17395 }
17396 }
17397
17398 public function get_latitude()
17399 {
17400 if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat'))
17401 {
17402 return (float) $return[0]['data'];
17403 }
17404 elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
17405 {
17406 return (float) $match[1];
17407 }
17408 else
17409 {
17410 return null;
17411 }
17412 }
17413
17414 public function get_longitude()
17415 {
17416 if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long'))
17417 {
17418 return (float) $return[0]['data'];
17419 }
17420 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon'))
17421 {
17422 return (float) $return[0]['data'];
17423 }
17424 elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
17425 {
17426 return (float) $match[2];
17427 }
17428 else
17429 {
17430 return null;
17431 }
17432 }
17433
17434 public function get_image_url()
17435 {
17436 if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image'))
17437 {
17438 return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI);
17439 }
17440 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo'))
17441 {
17442 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
17443 }
17444 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon'))
17445 {
17446 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
17447 }
17448 else
17449 {
17450 return null;
17451 }
17452 }
17453 }
17454
17455 /**
17456 * Parses the XML Declaration
17457 *
17458 * @package SimplePie
17459 * @subpackage Parsing
17460 */
17461 class SimplePie_XML_Declaration_Parser
17462 {
17463 /**
17464 * XML Version
17465 *
17466 * @access public
17467 * @var string
17468 */
17469 var $version = '1.0';
17470
17471 /**
17472 * Encoding
17473 *
17474 * @access public
17475 * @var string
17476 */
17477 var $encoding = 'UTF-8';
17478
17479 /**
17480 * Standalone
17481 *
17482 * @access public
17483 * @var bool
17484 */
17485 var $standalone = false;
17486
17487 /**
17488 * Current state of the state machine
17489 *
17490 * @access private
17491 * @var string
17492 */
17493 var $state = 'before_version_name';
17494
17495 /**
17496 * Input data
17497 *
17498 * @access private
17499 * @var string
17500 */
17501 var $data = '';
17502
17503 /**
17504 * Input data length (to avoid calling strlen() everytime this is needed)
17505 *
17506 * @access private
17507 * @var int
17508 */
17509 var $data_length = 0;
17510
17511 /**
17512 * Current position of the pointer
17513 *
17514 * @var int
17515 * @access private
17516 */
17517 var $position = 0;
17518
17519 /**
17520 * Create an instance of the class with the input data
17521 *
17522 * @access public
17523 * @param string $data Input data
17524 */
17525 public function __construct($data)
17526 {
17527 $this->data = $data;
17528 $this->data_length = strlen($this->data);
17529 }
17530
17531 /**
17532 * Parse the input data
17533 *
17534 * @access public
17535 * @return bool true on success, false on failure
17536 */
17537 public function parse()
17538 {
17539 while ($this->state && $this->state !== 'emit' && $this->has_data())
17540 {
17541 $state = $this->state;
17542 $this->$state();
17543 }
17544 $this->data = '';
17545 if ($this->state === 'emit')
17546 {
17547 return true;
17548 }
17549 else
17550 {
17551 $this->version = '';
17552 $this->encoding = '';
17553 $this->standalone = '';
17554 return false;
17555 }
17556 }
17557
17558 /**
17559 * Check whether there is data beyond the pointer
17560 *
17561 * @access private
17562 * @return bool true if there is further data, false if not
17563 */
17564 public function has_data()
17565 {
17566 return (bool) ($this->position < $this->data_length);
17567 }
17568
17569 /**
17570 * Advance past any whitespace
17571 *
17572 * @return int Number of whitespace characters passed
17573 */
17574 public function skip_whitespace()
17575 {
17576 $whitespace = strspn($this->data, "\x09\x0A\x0D\x20", $this->position);
17577 $this->position += $whitespace;
17578 return $whitespace;
17579 }
17580
17581 /**
17582 * Read value
17583 */
17584 public function get_value()
17585 {
17586 $quote = substr($this->data, $this->position, 1);
17587 if ($quote === '"' || $quote === "'")
17588 {
17589 $this->position++;
17590 $len = strcspn($this->data, $quote, $this->position);
17591 if ($this->has_data())
17592 {
17593 $value = substr($this->data, $this->position, $len);
17594 $this->position += $len + 1;
17595 return $value;
17596 }
17597 }
17598 return false;
17599 }
17600
17601 public function before_version_name()
17602 {
17603 if ($this->skip_whitespace())
17604 {
17605 $this->state = 'version_name';
17606 }
17607 else
17608 {
17609 $this->state = false;
17610 }
17611 }
17612
17613 public function version_name()
17614 {
17615 if (substr($this->data, $this->position, 7) === 'version')
17616 {
17617 $this->position += 7;
17618 $this->skip_whitespace();
17619 $this->state = 'version_equals';
17620 }
17621 else
17622 {
17623 $this->state = false;
17624 }
17625 }
17626
17627 public function version_equals()
17628 {
17629 if (substr($this->data, $this->position, 1) === '=')
17630 {
17631 $this->position++;
17632 $this->skip_whitespace();
17633 $this->state = 'version_value';
17634 }
17635 else
17636 {
17637 $this->state = false;
17638 }
17639 }
17640
17641 public function version_value()
17642 {
17643 if ($this->version = $this->get_value())
17644 {
17645 $this->skip_whitespace();
17646 if ($this->has_data())
17647 {
17648 $this->state = 'encoding_name';
17649 }
17650 else
17651 {
17652 $this->state = 'emit';
17653 }
17654 }
17655 else
17656 {
17657 $this->state = false;
17658 }
17659 }
17660
17661 public function encoding_name()
17662 {
17663 if (substr($this->data, $this->position, 8) === 'encoding')
17664 {
17665 $this->position += 8;
17666 $this->skip_whitespace();
17667 $this->state = 'encoding_equals';
17668 }
17669 else
17670 {
17671 $this->state = 'standalone_name';
17672 }
17673 }
17674
17675 public function encoding_equals()
17676 {
17677 if (substr($this->data, $this->position, 1) === '=')
17678 {
17679 $this->position++;
17680 $this->skip_whitespace();
17681 $this->state = 'encoding_value';
17682 }
17683 else
17684 {
17685 $this->state = false;
17686 }
17687 }
17688
17689 public function encoding_value()
17690 {
17691 if ($this->encoding = $this->get_value())
17692 {
17693 $this->skip_whitespace();
17694 if ($this->has_data())
17695 {
17696 $this->state = 'standalone_name';
17697 }
17698 else
17699 {
17700 $this->state = 'emit';
17701 }
17702 }
17703 else
17704 {
17705 $this->state = false;
17706 }
17707 }
17708
17709 public function standalone_name()
17710 {
17711 if (substr($this->data, $this->position, 10) === 'standalone')
17712 {
17713 $this->position += 10;
17714 $this->skip_whitespace();
17715 $this->state = 'standalone_equals';
17716 }
17717 else
17718 {
17719 $this->state = false;
17720 }
17721 }
17722
17723 public function standalone_equals()
17724 {
17725 if (substr($this->data, $this->position, 1) === '=')
17726 {
17727 $this->position++;
17728 $this->skip_whitespace();
17729 $this->state = 'standalone_value';
17730 }
17731 else
17732 {
17733 $this->state = false;
17734 }
17735 }
17736
17737 public function standalone_value()
17738 {
17739 if ($standalone = $this->get_value())
17740 {
17741 switch ($standalone)
17742 {
17743 case 'yes':
17744 $this->standalone = true;
17745 break;
17746
17747 case 'no':
17748 $this->standalone = false;
17749 break;
17750
17751 default:
17752 $this->state = false;
17753 return;
17754 }
17755
17756 $this->skip_whitespace();
17757 if ($this->has_data())
17758 {
17759 $this->state = false;
17760 }
17761 else
17762 {
17763 $this->state = 'emit';
17764 }
17765 }
17766 else
17767 {
17768 $this->state = false;
17769 }
17770 }
17771 }
17772