: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
// RFC 3023 (only applies to sniffed content)
if (in_array($sniffed, $application_types) || substr($sniffed, 0, 12) === 'application/' && substr($sniffed, -4) === '+xml')
if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset))
$encodings[] = strtoupper($charset[1]);
$encodings = array_merge($encodings, $this->registry->call('Misc', 'xml_encoding', array($this->raw_data, &$this->registry)));
elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml')
if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset))
$encodings[] = strtoupper($charset[1]);
$encodings[] = 'US-ASCII';
// Text MIME-type default
elseif (substr($sniffed, 0, 5) === 'text/')
// Fallback to XML 1.0 Appendix F.1/UTF-8/ISO-8859-1
$encodings = array_merge($encodings, $this->registry->call('Misc', 'xml_encoding', array($this->raw_data, &$this->registry)));
$encodings[] = 'ISO-8859-1';
// There's no point in trying an encoding twice
$encodings = array_unique($encodings);
// Loop through each possible encoding, till we return something, or run out of possibilities
foreach ($encodings as $encoding)
// Change the encoding to UTF-8 (as we always use UTF-8 internally)
if ($utf8_data = $this->registry->call('Misc', 'change_encoding', array($this->raw_data, $encoding, 'UTF-8')))
$parser = $this->registry->create('Parser');
if ($parser->parse($utf8_data, 'UTF-8', $this->permanent_url))
$this->data = $parser->get_data();
if (!($this->get_type() & ~SIMPLEPIE_TYPE_NONE))
$this->error = "A feed could not be found at `$this->feed_url`. This does not appear to be a valid RSS or Atom feed.";
$this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__));
$this->data['headers'] = $headers;
$this->data['build'] = SIMPLEPIE_BUILD;
// Cache the file if caching is enabled
if ($cache && !$cache->save($this))
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);
// We have an error, just set SimplePie_Misc::error to it and quit
$this->error = $this->feed_url;
$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());
$this->error = 'The data could not be converted to UTF-8.';
if (!extension_loaded('mbstring') && !extension_loaded('iconv') && !class_exists('\UConverter')) {
$this->error .= ' You MUST have either the iconv, mbstring or intl (PHP 5.5+) extension installed and enabled.';
$missingExtensions = array();
if (!extension_loaded('iconv')) {
$missingExtensions[] = 'iconv';
if (!extension_loaded('mbstring')) {
$missingExtensions[] = 'mbstring';
if (!class_exists('\UConverter')) {
$missingExtensions[] = 'intl (PHP 5.5+)';
$this->error .= ' Try installing/enabling the ' . implode(' or ', $missingExtensions) . ' extension.';
$this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__));
* Fetch the data via SimplePie_File
* If the data is already cached, attempt to fetch it from there instead
* @param SimplePie_Cache_Base|false $cache Cache handler, or false to not load from the cache
* @return array|true Returns true if the data was loaded from the cache, or an array of HTTP headers and sniffed type
protected function fetch_data(&$cache)
// If it's enabled, use the cache
$this->data = $cache->load();
// If the cache is for an outdated build of SimplePie
if (!isset($this->data['build']) || $this->data['build'] !== SIMPLEPIE_BUILD)
// If we've hit a collision just rerun it with caching disabled
elseif (isset($this->data['url']) && $this->data['url'] !== $this->feed_url)
// If we've got a non feed_url stored (if the page isn't actually a feed, or is a redirect) use that URL.
elseif (isset($this->data['feed_url']))
// If the autodiscovery cache is still valid use it.
if ($cache->mtime() + $this->autodiscovery_cache_duration > time())
// Do not need to do feed autodiscovery yet.
if ($this->data['feed_url'] !== $this->data['url'])
$this->set_feed_url($this->data['feed_url']);
// Check if the cache has been updated
elseif ($cache->mtime() + $this->cache_duration < time())
// Want to know if we tried to send last-modified and/or etag headers
// when requesting this file. (Note that it's up to the file to
// support this, but we don't always send the headers either.)
$this->check_modified = true;
if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag']))
'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',
if (isset($this->data['headers']['last-modified']))
$headers['if-modified-since'] = $this->data['headers']['last-modified'];
if (isset($this->data['headers']['etag']))
$headers['if-none-match'] = $this->data['headers']['etag'];
$file = $this->registry->create('File', array($this->feed_url, $this->timeout/10, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options));
$this->status_code = $file->status_code;
if ($file->status_code === 304)
// Set raw_data to false here too, to signify that the cache
$this->check_modified = false;
if($this->force_cache_fallback)
// If the cache is still valid, just return true
// If the cache is empty, delete it
// 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.
if ($this->file instanceof SimplePie_File && $this->file->url === $this->feed_url)
'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',
$file = $this->registry->create('File', array($this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options));
$this->status_code = $file->status_code;
// If the file connection has an error, set SimplePie::error to that and quit
if (!$file->success && !($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300)))
$this->error = $file->error;
return !empty($this->data);
// Check if the supplied URL is a feed, if it isn't, look for it.
$locate = $this->registry->create('Locator', array(&$file, $this->timeout, $this->useragent, $this->max_checked_feeds, $this->force_fsockopen, $this->curl_options));
if (!$locate->is_feed($file))
$copyStatusCode = $file->status_code;
$copyContentType = $file->headers['content-type'];
if (class_exists('DOMXpath') && function_exists('Mf2\parse')) {
$doc = new DOMDocument();
@$doc->loadHTML($file->body);
$xpath = new DOMXpath($doc);
// Check for both h-feed and h-entry, as both a feed with no entries
// and a list of entries without an h-feed wrapper are both valid.
$query = '//*[contains(concat(" ", @class, " "), " h-feed ") or '.
'contains(concat(" ", @class, " "), " h-entry ")]';
$result = $xpath->query($query);
$microformats = $result->length !== 0;
// Now also do feed discovery, but if microformats were found don't
// overwrite the current value of file.
$discovered = $locate->find($this->autodiscovery,
$this->all_discovered_feeds);
if ($hub = $locate->get_rel_link('hub'))
$self = $locate->get_rel_link('self');
$this->store_links($file, $hub, $self);
// Push the current file onto all_discovered feeds so the user can
// be shown this as one of the options.
if (isset($this->all_discovered_feeds)) {
$this->all_discovered_feeds[] = $file;
// We need to unset this so that if SimplePie::set_file() has
// been called that object is untouched
$this->error = "A feed could not be found at `$this->feed_url`; the status code is `$copyStatusCode` and content-type is `$copyContentType`";
$this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__));
catch (SimplePie_Exception $e)
// We need to unset this so that if SimplePie::set_file() has been called that object is untouched
// This is usually because DOMDocument doesn't exist
$this->error = $e->getMessage();
$this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, $e->getFile(), $e->getLine()));
$this->data = array('url' => $this->feed_url, 'feed_url' => $file->url, 'build' => SIMPLEPIE_BUILD);
if (!$cache->save($this))
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);
$cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $file->url), 'spc'));
$this->feed_url = $file->url;
$this->raw_data = $file->body;
$this->permanent_url = $file->permanent_url;
$headers = $file->headers;
$sniffer = $this->registry->create('Content_Type_Sniffer', array(&$file));
$sniffed = $sniffer->get_type();
return array($headers, $sniffed);
* Get the error message for the occurred error
* @return string|array Error message, or array of messages for multifeeds
* Get the last HTTP status code
* @return int Status code
public function status_code()
return $this->status_code;
* This is the same as the old `$feed->enable_xml_dump(true)`, but returns
* the data instead of printing it.
* @return string|boolean Raw XML data, false if the cache is used
public function get_raw_data()
* Get the character encoding used for output
public function get_encoding()
return $this->sanitize->output_encoding;
* Send the Content-Type header with correct encoding
* This method ensures that the SimplePie-enabled page is being served with
* the correct {@link http://www.iana.org/assignments/media-types/ mime-type}
* and character encoding HTTP headers (character encoding determined by the
* {@see set_output_encoding} config option).
* This won't work properly if any content or whitespace has already been
* sent to the browser, because it relies on PHP's
* {@link http://php.net/header header()} function, and these are the
* circumstances under which the function works.
* Because it's setting these settings for the entire page (as is the nature
* of HTTP headers), this should only be used once per page (again, at the
* @param string $mime MIME type to serve the page as
public function handle_content_type($mime = 'text/html')
$header = "Content-Type: $mime;";
if ($this->get_encoding())
$header .= ' charset=' . $this->get_encoding();
$header .= ' charset=UTF-8';
* Get the type of the feed
* This returns a SIMPLEPIE_TYPE_* constant, which can be tested against
* using {@link http://php.net/language.operators.bitwise bitwise operators}
* @since 0.8 (usage changed to using constants in 1.0)
* @see SIMPLEPIE_TYPE_NONE Unknown.
* @see SIMPLEPIE_TYPE_RSS_090 RSS 0.90.
* @see SIMPLEPIE_TYPE_RSS_091_NETSCAPE RSS 0.91 (Netscape).
* @see SIMPLEPIE_TYPE_RSS_091_USERLAND RSS 0.91 (Userland).
* @see SIMPLEPIE_TYPE_RSS_091 RSS 0.91.
* @see SIMPLEPIE_TYPE_RSS_092 RSS 0.92.
* @see SIMPLEPIE_TYPE_RSS_093 RSS 0.93.
* @see SIMPLEPIE_TYPE_RSS_094 RSS 0.94.
* @see SIMPLEPIE_TYPE_RSS_10 RSS 1.0.
* @see SIMPLEPIE_TYPE_RSS_20 RSS 2.0.x.
* @see SIMPLEPIE_TYPE_RSS_RDF RDF-based RSS.
* @see SIMPLEPIE_TYPE_RSS_SYNDICATION Non-RDF-based RSS (truly intended as syndication format).
* @see SIMPLEPIE_TYPE_RSS_ALL Any version of RSS.
* @see SIMPLEPIE_TYPE_ATOM_03 Atom 0.3.
* @see SIMPLEPIE_TYPE_ATOM_10 Atom 1.0.
* @see SIMPLEPIE_TYPE_ATOM_ALL Any version of Atom.
* @see SIMPLEPIE_TYPE_ALL Any known/supported feed type.
* @return int SIMPLEPIE_TYPE_* constant
public function get_type()
if (!isset($this->data['type']))
$this->data['type'] = SIMPLEPIE_TYPE_ALL;
if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed']))
$this->data['type'] &= SIMPLEPIE_TYPE_ATOM_10;
elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed']))
$this->data['type'] &= SIMPLEPIE_TYPE_ATOM_03;
elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF']))
if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['channel'])
|| isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['image'])
|| isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item'])
|| isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['textinput']))
$this->data['type'] &= SIMPLEPIE_TYPE_RSS_10;
if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['channel'])
|| isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['image'])
|| isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item'])
|| isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['textinput']))
$this->data['type'] &= SIMPLEPIE_TYPE_RSS_090;
elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss']))
$this->data['type'] &= SIMPLEPIE_TYPE_RSS_ALL;
if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version']))
switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version']))
$this->data['type'] &= SIMPLEPIE_TYPE_RSS_091;
if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data']))
switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data']))
$this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_NETSCAPE;
$this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_USERLAND;
$this->data['type'] &= SIMPLEPIE_TYPE_RSS_092;
$this->data['type'] &= SIMPLEPIE_TYPE_RSS_093;
$this->data['type'] &= SIMPLEPIE_TYPE_RSS_094;
$this->data['type'] &= SIMPLEPIE_TYPE_RSS_20;