628 * @access private |
650 * @access private |
629 */ |
651 */ |
630 public $item_limit = 0; |
652 public $item_limit = 0; |
631 |
653 |
632 /** |
654 /** |
|
655 * @var bool Stores if last-modified and/or etag headers were sent with the |
|
656 * request when checking a feed. |
|
657 */ |
|
658 public $check_modified = false; |
|
659 |
|
660 /** |
633 * @var array Stores the default attributes to be stripped by strip_attributes(). |
661 * @var array Stores the default attributes to be stripped by strip_attributes(). |
634 * @see SimplePie::strip_attributes() |
662 * @see SimplePie::strip_attributes() |
635 * @access private |
663 * @access private |
636 */ |
664 */ |
637 public $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'); |
665 public $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'); |
638 |
666 |
639 /** |
667 /** |
|
668 * @var array Stores the default attributes to add to different tags by add_attributes(). |
|
669 * @see SimplePie::add_attributes() |
|
670 * @access private |
|
671 */ |
|
672 public $add_attributes = array('audio' => array('preload' => 'none'), 'iframe' => array('sandbox' => 'allow-scripts allow-same-origin'), 'video' => array('preload' => 'none')); |
|
673 |
|
674 /** |
640 * @var array Stores the default tags to be stripped by strip_htmltags(). |
675 * @var array Stores the default tags to be stripped by strip_htmltags(). |
641 * @see SimplePie::strip_htmltags() |
676 * @see SimplePie::strip_htmltags() |
642 * @access private |
677 * @access private |
643 */ |
678 */ |
644 public $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'); |
679 public $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'); |
|
680 |
|
681 /** |
|
682 * @var bool Should we throw exceptions, or use the old-style error property? |
|
683 * @access private |
|
684 */ |
|
685 public $enable_exceptions = false; |
645 |
686 |
646 /** |
687 /** |
647 * The SimplePie class contains feed level data and options |
688 * The SimplePie class contains feed level data and options |
648 * |
689 * |
649 * To use SimplePie, create the SimplePie object with no parameters. You can |
690 * To use SimplePie, create the SimplePie object with no parameters. You can |
657 * |
698 * |
658 * @since 1.0 Preview Release |
699 * @since 1.0 Preview Release |
659 */ |
700 */ |
660 public function __construct() |
701 public function __construct() |
661 { |
702 { |
662 if (version_compare(PHP_VERSION, '5.2', '<')) |
703 if (version_compare(PHP_VERSION, '5.6', '<')) |
663 { |
704 { |
664 trigger_error('PHP 4.x, 5.0 and 5.1 are no longer supported. Please upgrade to PHP 5.2 or newer.'); |
705 trigger_error('Please upgrade to PHP 5.6 or newer.'); |
665 die(); |
706 die(); |
666 } |
707 } |
667 |
708 |
668 // Other objects, instances created here so we can set options on them |
709 // Other objects, instances created here so we can set options on them |
669 $this->sanitize = new SimplePie_Sanitize(); |
710 $this->sanitize = new SimplePie_Sanitize(); |
670 $this->registry = new SimplePie_Registry(); |
711 $this->registry = new SimplePie_Registry(); |
671 |
712 |
672 if (func_num_args() > 0) |
713 if (func_num_args() > 0) |
673 { |
714 { |
674 $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING; |
715 $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING; |
675 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); |
716 trigger_error('Passing parameters to the constructor is no longer supported. Please use set_feed_url(), set_cache_location(), and set_cache_duration() directly.', $level); |
676 |
717 |
677 $args = func_get_args(); |
718 $args = func_get_args(); |
678 switch (count($args)) { |
719 switch (count($args)) { |
679 case 3: |
720 case 3: |
680 $this->set_cache_duration($args[2]); |
721 $this->set_cache_duration($args[2]); |
1152 $attribs = $this->strip_attributes; |
1224 $attribs = $this->strip_attributes; |
1153 } |
1225 } |
1154 $this->sanitize->strip_attributes($attribs); |
1226 $this->sanitize->strip_attributes($attribs); |
1155 } |
1227 } |
1156 |
1228 |
|
1229 public function add_attributes($attribs = '') |
|
1230 { |
|
1231 if ($attribs === '') |
|
1232 { |
|
1233 $attribs = $this->add_attributes; |
|
1234 } |
|
1235 $this->sanitize->add_attributes($attribs); |
|
1236 } |
|
1237 |
1157 /** |
1238 /** |
1158 * Set the output encoding |
1239 * Set the output encoding |
1159 * |
1240 * |
1160 * Allows you to override SimplePie's output to match that of your webpage. |
1241 * Allows you to override SimplePie's output to match that of your webpage. |
1161 * This is useful for times when your webpages are not being served as |
1242 * This is useful for times when your webpages are not being served as |
1162 * UTF-8. This setting will be obeyed by {@see handle_content_type()}, and |
1243 * UTF-8. This setting will be obeyed by {@see handle_content_type()}, and |
1163 * is similar to {@see set_input_encoding()}. |
1244 * is similar to {@see set_input_encoding()}. |
1164 * |
1245 * |
1165 * It should be noted, however, that not all character encodings can support |
1246 * It should be noted, however, that not all character encodings can support |
1166 * all characters. If your page is being served as ISO-8859-1 and you try |
1247 * all characters. If your page is being served as ISO-8859-1 and you try |
1167 * to display a Japanese feed, you'll likely see garbled characters. |
1248 * to display a Japanese feed, you'll likely see garbled characters. |
1168 * Because of this, it is highly recommended to ensure that your webpages |
1249 * Because of this, it is highly recommended to ensure that your webpages |
1169 * are served as UTF-8. |
1250 * are served as UTF-8. |
1170 * |
1251 * |
1171 * The number of supported character encodings depends on whether your web |
1252 * The number of supported character encodings depends on whether your web |
1229 { |
1310 { |
1230 $this->item_limit = (int) $limit; |
1311 $this->item_limit = (int) $limit; |
1231 } |
1312 } |
1232 |
1313 |
1233 /** |
1314 /** |
|
1315 * Enable throwing exceptions |
|
1316 * |
|
1317 * @param boolean $enable Should we throw exceptions, or use the old-style error property? |
|
1318 */ |
|
1319 public function enable_exceptions($enable = true) |
|
1320 { |
|
1321 $this->enable_exceptions = $enable; |
|
1322 } |
|
1323 |
|
1324 /** |
1234 * Initialize the feed object |
1325 * Initialize the feed object |
1235 * |
1326 * |
1236 * This is what makes everything happen. Period. This is where all of the |
1327 * This is what makes everything happen. Period. This is where all of the |
1237 * configuration options get processed, feeds are fetched, cached, and |
1328 * configuration options get processed, feeds are fetched, cached, and |
1238 * parsed, and all of that other good stuff. |
1329 * parsed, and all of that other good stuff. |
1239 * |
1330 * |
1240 * @return boolean True if successful, false otherwise |
1331 * @return boolean True if successful, false otherwise |
1241 */ |
1332 */ |
1242 public function init() |
1333 public function init() |
1243 { |
1334 { |
1244 // Check absolute bare minimum requirements. |
1335 // Check absolute bare minimum requirements. |
1245 if (!extension_loaded('xml') || !extension_loaded('pcre')) |
1336 if (!extension_loaded('xml') || !extension_loaded('pcre')) |
1246 { |
1337 { |
|
1338 $this->error = 'XML or PCRE extensions not loaded!'; |
1247 return false; |
1339 return false; |
1248 } |
1340 } |
1249 // 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. |
1341 // 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. |
1250 elseif (!extension_loaded('xmlreader')) |
1342 elseif (!extension_loaded('xmlreader')) |
1251 { |
1343 { |
1261 { |
1353 { |
1262 return false; |
1354 return false; |
1263 } |
1355 } |
1264 } |
1356 } |
1265 |
1357 |
|
1358 // The default sanitize class gets set in the constructor, check if it has |
|
1359 // changed. |
|
1360 if ($this->registry->get_class('Sanitize') !== 'SimplePie_Sanitize') { |
|
1361 $this->sanitize = $this->registry->create('Sanitize'); |
|
1362 } |
1266 if (method_exists($this->sanitize, 'set_registry')) |
1363 if (method_exists($this->sanitize, 'set_registry')) |
1267 { |
1364 { |
1268 $this->sanitize->set_registry($this->registry); |
1365 $this->sanitize->set_registry($this->registry); |
1269 } |
1366 } |
1270 |
1367 |
1271 // Pass whatever was set with config options over to the sanitizer. |
1368 // Pass whatever was set with config options over to the sanitizer. |
1272 // Pass the classes in for legacy support; new classes should use the registry instead |
1369 // Pass the classes in for legacy support; new classes should use the registry instead |
1273 $this->sanitize->pass_cache_data($this->cache, $this->cache_location, $this->cache_name_function, $this->registry->get_class('Cache')); |
1370 $this->sanitize->pass_cache_data($this->cache, $this->cache_location, $this->cache_name_function, $this->registry->get_class('Cache')); |
1274 $this->sanitize->pass_file_data($this->registry->get_class('File'), $this->timeout, $this->useragent, $this->force_fsockopen); |
1371 $this->sanitize->pass_file_data($this->registry->get_class('File'), $this->timeout, $this->useragent, $this->force_fsockopen, $this->curl_options); |
1275 |
1372 |
1276 if (!empty($this->multifeed_url)) |
1373 if (!empty($this->multifeed_url)) |
1277 { |
1374 { |
1278 $i = 0; |
1375 $i = 0; |
1279 $success = 0; |
1376 $success = 0; |
1298 return false; |
1395 return false; |
1299 } |
1396 } |
1300 |
1397 |
1301 $this->error = null; |
1398 $this->error = null; |
1302 $this->data = array(); |
1399 $this->data = array(); |
|
1400 $this->check_modified = false; |
1303 $this->multifeed_objects = array(); |
1401 $this->multifeed_objects = array(); |
1304 $cache = false; |
1402 $cache = false; |
1305 |
1403 |
1306 if ($this->feed_url !== null) |
1404 if ($this->feed_url !== null) |
1307 { |
1405 { |
1308 $parsed_feed_url = $this->registry->call('Misc', 'parse_url', array($this->feed_url)); |
1406 $parsed_feed_url = $this->registry->call('Misc', 'parse_url', array($this->feed_url)); |
1309 |
1407 |
1310 // Decide whether to enable caching |
1408 // Decide whether to enable caching |
1311 if ($this->cache && $parsed_feed_url['scheme'] !== '') |
1409 if ($this->cache && $parsed_feed_url['scheme'] !== '') |
1312 { |
1410 { |
1313 $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $this->feed_url), 'spc')); |
1411 $url = $this->feed_url . ($this->force_feed ? '#force_feed' : ''); |
|
1412 $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $url), 'spc')); |
1314 } |
1413 } |
1315 |
1414 |
1316 // Fetch the data via SimplePie_File into $this->raw_data |
1415 // Fetch the data via SimplePie_File into $this->raw_data |
1317 if (($fetched = $this->fetch_data($cache)) === true) |
1416 if (($fetched = $this->fetch_data($cache)) === true) |
1318 { |
1417 { |
1323 } |
1422 } |
1324 |
1423 |
1325 list($headers, $sniffed) = $fetched; |
1424 list($headers, $sniffed) = $fetched; |
1326 } |
1425 } |
1327 |
1426 |
|
1427 // Empty response check |
|
1428 if(empty($this->raw_data)){ |
|
1429 $this->error = "A feed could not be found at `$this->feed_url`. Empty body."; |
|
1430 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); |
|
1431 return false; |
|
1432 } |
|
1433 |
1328 // Set up array of possible encodings |
1434 // Set up array of possible encodings |
1329 $encodings = array(); |
1435 $encodings = array(); |
1330 |
1436 |
1331 // First check to see if input has been overridden. |
1437 // First check to see if input has been overridden. |
1332 if ($this->input_encoding !== false) |
1438 if ($this->input_encoding !== false) |
1333 { |
1439 { |
1334 $encodings[] = $this->input_encoding; |
1440 $encodings[] = strtoupper($this->input_encoding); |
1335 } |
1441 } |
1336 |
1442 |
1337 $application_types = array('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity'); |
1443 $application_types = array('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity'); |
1338 $text_types = array('text/xml', 'text/xml-external-parsed-entity'); |
1444 $text_types = array('text/xml', 'text/xml-external-parsed-entity'); |
1339 |
1445 |
1351 } |
1457 } |
1352 elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml') |
1458 elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml') |
1353 { |
1459 { |
1354 if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) |
1460 if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) |
1355 { |
1461 { |
1356 $encodings[] = $charset[1]; |
1462 $encodings[] = strtoupper($charset[1]); |
1357 } |
1463 } |
1358 $encodings[] = 'US-ASCII'; |
1464 $encodings[] = 'US-ASCII'; |
1359 } |
1465 } |
1360 // Text MIME-type default |
1466 // Text MIME-type default |
1361 elseif (substr($sniffed, 0, 5) === 'text/') |
1467 elseif (substr($sniffed, 0, 5) === 'text/') |
1362 { |
1468 { |
1363 $encodings[] = 'US-ASCII'; |
1469 $encodings[] = 'UTF-8'; |
1364 } |
1470 } |
1365 } |
1471 } |
1366 |
1472 |
1367 // Fallback to XML 1.0 Appendix F.1/UTF-8/ISO-8859-1 |
1473 // Fallback to XML 1.0 Appendix F.1/UTF-8/ISO-8859-1 |
1368 $encodings = array_merge($encodings, $this->registry->call('Misc', 'xml_encoding', array($this->raw_data, &$this->registry))); |
1474 $encodings = array_merge($encodings, $this->registry->call('Misc', 'xml_encoding', array($this->raw_data, &$this->registry))); |
1380 { |
1486 { |
1381 // Create new parser |
1487 // Create new parser |
1382 $parser = $this->registry->create('Parser'); |
1488 $parser = $this->registry->create('Parser'); |
1383 |
1489 |
1384 // If it's parsed fine |
1490 // If it's parsed fine |
1385 if ($parser->parse($utf8_data, 'UTF-8')) |
1491 if ($parser->parse($utf8_data, 'UTF-8', $this->permanent_url)) |
1386 { |
1492 { |
1387 $this->data = $parser->get_data(); |
1493 $this->data = $parser->get_data(); |
1388 if (!($this->get_type() & ~SIMPLEPIE_TYPE_NONE)) |
1494 if (!($this->get_type() & ~SIMPLEPIE_TYPE_NONE)) |
1389 { |
1495 { |
1390 $this->error = "A feed could not be found at $this->feed_url. This does not appear to be a valid RSS or Atom feed."; |
1496 $this->error = "A feed could not be found at `$this->feed_url`. This does not appear to be a valid RSS or Atom feed."; |
1391 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); |
1497 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); |
1392 return false; |
1498 return false; |
1393 } |
1499 } |
1394 |
1500 |
1395 if (isset($headers)) |
1501 if (isset($headers)) |
1399 $this->data['build'] = SIMPLEPIE_BUILD; |
1505 $this->data['build'] = SIMPLEPIE_BUILD; |
1400 |
1506 |
1401 // Cache the file if caching is enabled |
1507 // Cache the file if caching is enabled |
1402 if ($cache && !$cache->save($this)) |
1508 if ($cache && !$cache->save($this)) |
1403 { |
1509 { |
1404 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); |
1510 trigger_error("$this->cache_location is not writable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); |
1405 } |
1511 } |
1406 return true; |
1512 return true; |
1407 } |
1513 } |
1408 } |
1514 } |
1409 } |
1515 } |
1410 |
1516 |
1411 if (isset($parser)) |
1517 if (isset($parser)) |
1412 { |
1518 { |
1413 // We have an error, just set SimplePie_Misc::error to it and quit |
1519 // We have an error, just set SimplePie_Misc::error to it and quit |
1414 $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()); |
1520 $this->error = $this->feed_url; |
|
1521 $this->error .= sprintf(' is invalid XML, likely due to invalid characters. XML error: %s at line %d, column %d', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column()); |
1415 } |
1522 } |
1416 else |
1523 else |
1417 { |
1524 { |
1418 $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.'; |
1525 $this->error = 'The data could not be converted to UTF-8.'; |
|
1526 if (!extension_loaded('mbstring') && !extension_loaded('iconv') && !class_exists('\UConverter')) { |
|
1527 $this->error .= ' You MUST have either the iconv, mbstring or intl (PHP 5.5+) extension installed and enabled.'; |
|
1528 } else { |
|
1529 $missingExtensions = array(); |
|
1530 if (!extension_loaded('iconv')) { |
|
1531 $missingExtensions[] = 'iconv'; |
|
1532 } |
|
1533 if (!extension_loaded('mbstring')) { |
|
1534 $missingExtensions[] = 'mbstring'; |
|
1535 } |
|
1536 if (!class_exists('\UConverter')) { |
|
1537 $missingExtensions[] = 'intl (PHP 5.5+)'; |
|
1538 } |
|
1539 $this->error .= ' Try installing/enabling the ' . implode(' or ', $missingExtensions) . ' extension.'; |
|
1540 } |
1419 } |
1541 } |
1420 |
1542 |
1421 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); |
1543 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); |
1422 |
1544 |
1423 return false; |
1545 return false; |
1469 } |
1591 } |
1470 } |
1592 } |
1471 // Check if the cache has been updated |
1593 // Check if the cache has been updated |
1472 elseif ($cache->mtime() + $this->cache_duration < time()) |
1594 elseif ($cache->mtime() + $this->cache_duration < time()) |
1473 { |
1595 { |
1474 // If we have last-modified and/or etag set |
1596 // Want to know if we tried to send last-modified and/or etag headers |
|
1597 // when requesting this file. (Note that it's up to the file to |
|
1598 // support this, but we don't always send the headers either.) |
|
1599 $this->check_modified = true; |
1475 if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag'])) |
1600 if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag'])) |
1476 { |
1601 { |
1477 $headers = array( |
1602 $headers = array( |
1478 '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', |
1603 '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', |
1479 ); |
1604 ); |
1526 else |
1661 else |
1527 { |
1662 { |
1528 $headers = array( |
1663 $headers = array( |
1529 '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', |
1664 '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', |
1530 ); |
1665 ); |
1531 $file = $this->registry->create('File', array($this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen)); |
1666 $file = $this->registry->create('File', array($this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options)); |
1532 } |
1667 } |
1533 } |
1668 } |
1534 // If the file connection has an error, set SimplePie::error to that and quit |
1669 // If the file connection has an error, set SimplePie::error to that and quit |
1535 if (!$file->success && !($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300))) |
1670 if (!$file->success && !($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300))) |
1536 { |
1671 { |
1539 } |
1674 } |
1540 |
1675 |
1541 if (!$this->force_feed) |
1676 if (!$this->force_feed) |
1542 { |
1677 { |
1543 // Check if the supplied URL is a feed, if it isn't, look for it. |
1678 // Check if the supplied URL is a feed, if it isn't, look for it. |
1544 $locate = $this->registry->create('Locator', array(&$file, $this->timeout, $this->useragent, $this->max_checked_feeds)); |
1679 $locate = $this->registry->create('Locator', array(&$file, $this->timeout, $this->useragent, $this->max_checked_feeds, $this->force_fsockopen, $this->curl_options)); |
1545 |
1680 |
1546 if (!$locate->is_feed($file)) |
1681 if (!$locate->is_feed($file)) |
1547 { |
1682 { |
1548 // We need to unset this so that if SimplePie::set_file() has been called that object is untouched |
1683 $copyStatusCode = $file->status_code; |
1549 unset($file); |
1684 $copyContentType = $file->headers['content-type']; |
1550 try |
1685 try |
1551 { |
1686 { |
1552 if (!($file = $locate->find($this->autodiscovery, $this->all_discovered_feeds))) |
1687 $microformats = false; |
|
1688 if (class_exists('DOMXpath') && function_exists('Mf2\parse')) { |
|
1689 $doc = new DOMDocument(); |
|
1690 @$doc->loadHTML($file->body); |
|
1691 $xpath = new DOMXpath($doc); |
|
1692 // Check for both h-feed and h-entry, as both a feed with no entries |
|
1693 // and a list of entries without an h-feed wrapper are both valid. |
|
1694 $query = '//*[contains(concat(" ", @class, " "), " h-feed ") or '. |
|
1695 'contains(concat(" ", @class, " "), " h-entry ")]'; |
|
1696 $result = $xpath->query($query); |
|
1697 $microformats = $result->length !== 0; |
|
1698 } |
|
1699 // Now also do feed discovery, but if microformats were found don't |
|
1700 // overwrite the current value of file. |
|
1701 $discovered = $locate->find($this->autodiscovery, |
|
1702 $this->all_discovered_feeds); |
|
1703 if ($microformats) |
1553 { |
1704 { |
1554 $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."; |
1705 if ($hub = $locate->get_rel_link('hub')) |
1555 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); |
1706 { |
1556 return false; |
1707 $self = $locate->get_rel_link('self'); |
|
1708 $this->store_links($file, $hub, $self); |
|
1709 } |
|
1710 // Push the current file onto all_discovered feeds so the user can |
|
1711 // be shown this as one of the options. |
|
1712 if (isset($this->all_discovered_feeds)) { |
|
1713 $this->all_discovered_feeds[] = $file; |
|
1714 } |
1557 } |
1715 } |
|
1716 else |
|
1717 { |
|
1718 if ($discovered) |
|
1719 { |
|
1720 $file = $discovered; |
|
1721 } |
|
1722 else |
|
1723 { |
|
1724 // We need to unset this so that if SimplePie::set_file() has |
|
1725 // been called that object is untouched |
|
1726 unset($file); |
|
1727 $this->error = "A feed could not be found at `$this->feed_url`; the status code is `$copyStatusCode` and content-type is `$copyContentType`"; |
|
1728 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); |
|
1729 return false; |
|
1730 } |
|
1731 } |
1558 } |
1732 } |
1559 catch (SimplePie_Exception $e) |
1733 catch (SimplePie_Exception $e) |
1560 { |
1734 { |
|
1735 // We need to unset this so that if SimplePie::set_file() has been called that object is untouched |
|
1736 unset($file); |
1561 // This is usually because DOMDocument doesn't exist |
1737 // This is usually because DOMDocument doesn't exist |
1562 $this->error = $e->getMessage(); |
1738 $this->error = $e->getMessage(); |
1563 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, $e->getFile(), $e->getLine())); |
1739 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, $e->getFile(), $e->getLine())); |
1564 return false; |
1740 return false; |
1565 } |
1741 } |
1566 if ($cache) |
1742 if ($cache) |
1567 { |
1743 { |
1568 $this->data = array('url' => $this->feed_url, 'feed_url' => $file->url, 'build' => SIMPLEPIE_BUILD); |
1744 $this->data = array('url' => $this->feed_url, 'feed_url' => $file->url, 'build' => SIMPLEPIE_BUILD); |
1569 if (!$cache->save($this)) |
1745 if (!$cache->save($this)) |
1570 { |
1746 { |
1571 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); |
1747 trigger_error("$this->cache_location is not writable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); |
1572 } |
1748 } |
1573 $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $file->url), 'spc')); |
1749 $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $file->url), 'spc')); |
1574 } |
1750 } |
1575 $this->feed_url = $file->url; |
1751 } |
1576 } |
1752 $this->feed_url = $file->url; |
1577 $locate = null; |
1753 $locate = null; |
1578 } |
1754 } |
1579 |
1755 |
1580 $this->raw_data = $file->body; |
1756 $this->raw_data = $file->body; |
1581 |
1757 $this->permanent_url = $file->permanent_url; |
1582 $headers = $file->headers; |
1758 $headers = $file->headers; |
1583 $sniffer = $this->registry->create('Content_Type_Sniffer', array(&$file)); |
1759 $sniffer = $this->registry->create('Content_Type_Sniffer', array(&$file)); |
1584 $sniffed = $sniffer->get_type(); |
1760 $sniffed = $sniffer->get_type(); |
1585 |
1761 |
1586 return array($headers, $sniffed); |
1762 return array($headers, $sniffed); |
1587 } |
1763 } |
1588 |
1764 |
1589 /** |
1765 /** |
1590 * Get the error message for the occurred error. |
1766 * Get the error message for the occured error |
1591 * |
1767 * |
1592 * @return string|array Error message, or array of messages for multifeeds |
1768 * @return string|array Error message, or array of messages for multifeeds |
1593 */ |
1769 */ |
1594 public function error() |
1770 public function error() |
1595 { |
1771 { |
1763 } |
1939 } |
1764 |
1940 |
1765 /** |
1941 /** |
1766 * Get the URL for the feed |
1942 * Get the URL for the feed |
1767 * |
1943 * |
1768 * May or may not be different from the URL passed to {@see set_feed_url()}, |
1944 * When the 'permanent' mode is enabled, returns the original feed URL, |
1769 * depending on whether auto-discovery was used. |
1945 * except in the case of an `HTTP 301 Moved Permanently` status response, |
|
1946 * in which case the location of the first redirection is returned. |
|
1947 * |
|
1948 * When the 'permanent' mode is disabled (default), |
|
1949 * may or may not be different from the URL passed to {@see set_feed_url()}, |
|
1950 * depending on whether auto-discovery was used, and whether there were |
|
1951 * any redirects along the way. |
1770 * |
1952 * |
1771 * @since Preview Release (previously called `get_feed_url()` since SimplePie 0.8.) |
1953 * @since Preview Release (previously called `get_feed_url()` since SimplePie 0.8.) |
1772 * @todo If we have a perm redirect we should return the new URL |
1954 * @todo Support <itunes:new-feed-url> |
1773 * @todo When we make the above change, let's support <itunes:new-feed-url> as well |
|
1774 * @todo Also, |atom:link|@rel=self |
1955 * @todo Also, |atom:link|@rel=self |
|
1956 * @param bool $permanent Permanent mode to return only the original URL or the first redirection |
|
1957 * iff it is a 301 redirection |
1775 * @return string|null |
1958 * @return string|null |
1776 */ |
1959 */ |
1777 public function subscribe_url() |
1960 public function subscribe_url($permanent = false) |
1778 { |
1961 { |
1779 if ($this->feed_url !== null) |
1962 if ($permanent) |
1780 { |
1963 { |
1781 return $this->sanitize($this->feed_url, SIMPLEPIE_CONSTRUCT_IRI); |
1964 if ($this->permanent_url !== null) |
|
1965 { |
|
1966 // sanitize encodes ampersands which are required when used in a url. |
|
1967 return str_replace('&', '&', |
|
1968 $this->sanitize($this->permanent_url, |
|
1969 SIMPLEPIE_CONSTRUCT_IRI)); |
|
1970 } |
1782 } |
1971 } |
1783 else |
1972 else |
1784 { |
1973 { |
1785 return null; |
1974 if ($this->feed_url !== null) |
1786 } |
1975 { |
|
1976 return str_replace('&', '&', |
|
1977 $this->sanitize($this->feed_url, |
|
1978 SIMPLEPIE_CONSTRUCT_IRI)); |
|
1979 } |
|
1980 } |
|
1981 return null; |
1787 } |
1982 } |
1788 |
1983 |
1789 /** |
1984 /** |
1790 * Get data for an feed-level element |
1985 * Get data for an feed-level element |
1791 * |
1986 * |
2306 |
2501 |
2307 if (!empty($contributors)) |
2502 if (!empty($contributors)) |
2308 { |
2503 { |
2309 return array_unique($contributors); |
2504 return array_unique($contributors); |
2310 } |
2505 } |
2311 else |
2506 |
2312 { |
2507 return null; |
2313 return null; |
|
2314 } |
|
2315 } |
2508 } |
2316 |
2509 |
2317 /** |
2510 /** |
2318 * Get a single link for the feed |
2511 * Get a single link for the feed |
2319 * |
2512 * |
2320 * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8) |
2513 * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8) |
2321 * @param int $key The link that you want to return. Remember that arrays begin with 0, not 1 |
2514 * @param int $key The link that you want to return. Remember that arrays begin with 0, not 1 |
2322 * @param string $rel The relationship of the link to return |
2515 * @param string $rel The relationship of the link to return |
2323 * @return string|null Link URL |
2516 * @return string|null Link URL |
2324 */ |
2517 */ |
2325 public function get_link($key = 0, $rel = 'alternate') |
2518 public function get_link($key = 0, $rel = 'alternate') |
2326 { |
2519 { |
2327 $links = $this->get_links($rel); |
2520 $links = $this->get_links($rel); |
2328 if (isset($links[$key])) |
2521 if (isset($links[$key])) |
2329 { |
2522 { |
2330 return $links[$key]; |
2523 return $links[$key]; |
2331 } |
2524 } |
2332 else |
2525 |
2333 { |
2526 return null; |
2334 return null; |
|
2335 } |
|
2336 } |
2527 } |
2337 |
2528 |
2338 /** |
2529 /** |
2339 * Get the permalink for the item |
2530 * Get the permalink for the item |
2340 * |
2531 * |
2854 * |
3025 * |
2855 * @see get_item_quantity |
3026 * @see get_item_quantity |
2856 * @since Beta 2 |
3027 * @since Beta 2 |
2857 * @param int $start Index to start at |
3028 * @param int $start Index to start at |
2858 * @param int $end Number of items to return. 0 for all items after `$start` |
3029 * @param int $end Number of items to return. 0 for all items after `$start` |
2859 * @return array|null List of {@see SimplePie_Item} objects |
3030 * @return SimplePie_Item[]|null List of {@see SimplePie_Item} objects |
2860 */ |
3031 */ |
2861 public function get_items($start = 0, $end = 0) |
3032 public function get_items($start = 0, $end = 0) |
2862 { |
3033 { |
2863 if (!isset($this->data['items'])) |
3034 if (!isset($this->data['items'])) |
2864 { |
3035 { |
2865 if (!empty($this->multifeed_objects)) |
3036 if (!empty($this->multifeed_objects)) |
2866 { |
3037 { |
2867 $this->data['items'] = SimplePie::merge_items($this->multifeed_objects, $start, $end, $this->item_limit); |
3038 $this->data['items'] = SimplePie::merge_items($this->multifeed_objects, $start, $end, $this->item_limit); |
2868 } |
3039 if (empty($this->data['items'])) |
2869 else |
3040 { |
2870 { |
3041 return array(); |
2871 $this->data['items'] = array(); |
3042 } |
2872 if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'entry')) |
3043 return $this->data['items']; |
2873 { |
3044 } |
2874 $keys = array_keys($items); |
3045 $this->data['items'] = array(); |
2875 foreach ($keys as $key) |
3046 if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'entry')) |
2876 { |
3047 { |
2877 $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); |
3048 $keys = array_keys($items); |
2878 } |
3049 foreach ($keys as $key) |
2879 } |
3050 { |
2880 if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'entry')) |
3051 $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); |
2881 { |
3052 } |
2882 $keys = array_keys($items); |
3053 } |
2883 foreach ($keys as $key) |
3054 if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'entry')) |
2884 { |
3055 { |
2885 $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); |
3056 $keys = array_keys($items); |
2886 } |
3057 foreach ($keys as $key) |
2887 } |
3058 { |
2888 if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'item')) |
3059 $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); |
2889 { |
3060 } |
2890 $keys = array_keys($items); |
3061 } |
2891 foreach ($keys as $key) |
3062 if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'item')) |
2892 { |
3063 { |
2893 $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); |
3064 $keys = array_keys($items); |
2894 } |
3065 foreach ($keys as $key) |
2895 } |
3066 { |
2896 if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'item')) |
3067 $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); |
2897 { |
3068 } |
2898 $keys = array_keys($items); |
3069 } |
2899 foreach ($keys as $key) |
3070 if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'item')) |
2900 { |
3071 { |
2901 $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); |
3072 $keys = array_keys($items); |
2902 } |
3073 foreach ($keys as $key) |
2903 } |
3074 { |
2904 if ($items = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'item')) |
3075 $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); |
2905 { |
3076 } |
2906 $keys = array_keys($items); |
3077 } |
2907 foreach ($keys as $key) |
3078 if ($items = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'item')) |
2908 { |
3079 { |
2909 $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); |
3080 $keys = array_keys($items); |
2910 } |
3081 foreach ($keys as $key) |
2911 } |
3082 { |
2912 } |
3083 $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); |
2913 } |
3084 } |
2914 |
3085 } |
2915 if (!empty($this->data['items'])) |
3086 } |
2916 { |
3087 |
2917 // If we want to order it by date, check if all items have a date, and then sort it |
3088 if (empty($this->data['items'])) |
2918 if ($this->order_by_date && empty($this->multifeed_objects)) |
3089 { |
2919 { |
3090 return array(); |
2920 if (!isset($this->data['ordered_items'])) |
3091 } |
2921 { |
3092 |
2922 $do_sort = true; |
3093 if ($this->order_by_date) |
2923 foreach ($this->data['items'] as $item) |
3094 { |
2924 { |
3095 if (!isset($this->data['ordered_items'])) |
2925 if (!$item->get_date('U')) |
3096 { |
2926 { |
3097 $this->data['ordered_items'] = $this->data['items']; |
2927 $do_sort = false; |
3098 usort($this->data['ordered_items'], array(get_class($this), 'sort_items')); |
2928 break; |
3099 } |
2929 } |
3100 $items = $this->data['ordered_items']; |
2930 } |
|
2931 $item = null; |
|
2932 $this->data['ordered_items'] = $this->data['items']; |
|
2933 if ($do_sort) |
|
2934 { |
|
2935 usort($this->data['ordered_items'], array(get_class($this), 'sort_items')); |
|
2936 } |
|
2937 } |
|
2938 $items = $this->data['ordered_items']; |
|
2939 } |
|
2940 else |
|
2941 { |
|
2942 $items = $this->data['items']; |
|
2943 } |
|
2944 |
|
2945 // Slice the data as desired |
|
2946 if ($end === 0) |
|
2947 { |
|
2948 return array_slice($items, $start); |
|
2949 } |
|
2950 else |
|
2951 { |
|
2952 return array_slice($items, $start, $end); |
|
2953 } |
|
2954 } |
3101 } |
2955 else |
3102 else |
2956 { |
3103 { |
2957 return array(); |
3104 $items = $this->data['items']; |
2958 } |
3105 } |
|
3106 // Slice the data as desired |
|
3107 if ($end === 0) |
|
3108 { |
|
3109 return array_slice($items, $start); |
|
3110 } |
|
3111 |
|
3112 return array_slice($items, $start, $end); |
2959 } |
3113 } |
2960 |
3114 |
2961 /** |
3115 /** |
2962 * Set the favicon handler |
3116 * Set the favicon handler |
2963 * |
3117 * |
3058 { |
3224 { |
3059 trigger_error('Arguments must be SimplePie objects', E_USER_WARNING); |
3225 trigger_error('Arguments must be SimplePie objects', E_USER_WARNING); |
3060 } |
3226 } |
3061 } |
3227 } |
3062 |
3228 |
3063 $do_sort = true; |
3229 usort($items, array(get_class($urls[0]), 'sort_items')); |
3064 foreach ($items as $item) |
|
3065 { |
|
3066 if (!$item->get_date('U')) |
|
3067 { |
|
3068 $do_sort = false; |
|
3069 break; |
|
3070 } |
|
3071 } |
|
3072 $item = null; |
|
3073 if ($do_sort) |
|
3074 { |
|
3075 usort($items, array(get_class($urls[0]), 'sort_items')); |
|
3076 } |
|
3077 |
3230 |
3078 if ($end === 0) |
3231 if ($end === 0) |
3079 { |
3232 { |
3080 return array_slice($items, $start); |
3233 return array_slice($items, $start); |
3081 } |
3234 } |
|
3235 |
|
3236 return array_slice($items, $start, $end); |
|
3237 } |
|
3238 |
|
3239 trigger_error('Cannot merge zero SimplePie objects', E_USER_WARNING); |
|
3240 return array(); |
|
3241 } |
|
3242 |
|
3243 /** |
|
3244 * Store PubSubHubbub links as headers |
|
3245 * |
|
3246 * There is no way to find PuSH links in the body of a microformats feed, |
|
3247 * so they are added to the headers when found, to be used later by get_links. |
|
3248 * @param SimplePie_File $file |
|
3249 * @param string $hub |
|
3250 * @param string $self |
|
3251 */ |
|
3252 private function store_links(&$file, $hub, $self) { |
|
3253 if (isset($file->headers['link']['hub']) || |
|
3254 (isset($file->headers['link']) && |
|
3255 preg_match('/rel=hub/', $file->headers['link']))) |
|
3256 { |
|
3257 return; |
|
3258 } |
|
3259 |
|
3260 if ($hub) |
|
3261 { |
|
3262 if (isset($file->headers['link'])) |
|
3263 { |
|
3264 if ($file->headers['link'] !== '') |
|
3265 { |
|
3266 $file->headers['link'] = ', '; |
|
3267 } |
|
3268 } |
3082 else |
3269 else |
3083 { |
3270 { |
3084 return array_slice($items, $start, $end); |
3271 $file->headers['link'] = ''; |
3085 } |
3272 } |
3086 } |
3273 $file->headers['link'] .= '<'.$hub.'>; rel=hub'; |
3087 else |
3274 if ($self) |
3088 { |
3275 { |
3089 trigger_error('Cannot merge zero SimplePie objects', E_USER_WARNING); |
3276 $file->headers['link'] .= ', <'.$self.'>; rel=self'; |
3090 return array(); |
3277 } |
3091 } |
3278 } |
3092 } |
3279 } |
3093 } |
3280 } |
3094 endif; |
3281 endif; |