'http://a9.com/-/spec/opensearchrss/1.0/', 'atom' => 'http://www.w3.org/2005/Atom', 'rss' => 'http://blogs.law.harvard.edu/tech/rss', ); /** * Set the HTTP client instance * * Sets the HTTP client object to use for retrieving the feeds. * * @param Zend_Http_Client $httpClient * @return void */ public static function setHttpClient(Zend_Http_Client $httpClient) { self::$_httpClient = $httpClient; } /** * Gets the HTTP client object. If none is set, a new Zend_Http_Client will be used. * * @return Zend_Http_Client_Abstract */ public static function getHttpClient() { if (!self::$_httpClient instanceof Zend_Http_Client) { /** * @see Zend_Http_Client */ require_once 'Zend/Http/Client.php'; self::$_httpClient = new Zend_Http_Client(); } return self::$_httpClient; } /** * Toggle using POST instead of PUT and DELETE HTTP methods * * Some feed implementations do not accept PUT and DELETE HTTP * methods, or they can't be used because of proxies or other * measures. This allows turning on using POST where PUT and * DELETE would normally be used; in addition, an * X-Method-Override header will be sent with a value of PUT or * DELETE as appropriate. * * @param boolean $override Whether to override PUT and DELETE. * @return void */ public static function setHttpMethodOverride($override = true) { self::$_httpMethodOverride = $override; } /** * Get the HTTP override state * * @return boolean */ public static function getHttpMethodOverride() { return self::$_httpMethodOverride; } /** * Get the full version of a namespace prefix * * Looks up a prefix (atom:, etc.) in the list of registered * namespaces and returns the full namespace URI if * available. Returns the prefix, unmodified, if it's not * registered. * * @return string */ public static function lookupNamespace($prefix) { return isset(self::$_namespaces[$prefix]) ? self::$_namespaces[$prefix] : $prefix; } /** * Add a namespace and prefix to the registered list * * Takes a prefix and a full namespace URI and adds them to the * list of registered namespaces for use by * Zend_Feed::lookupNamespace(). * * @param string $prefix The namespace prefix * @param string $namespaceURI The full namespace URI * @return void */ public static function registerNamespace($prefix, $namespaceURI) { self::$_namespaces[$prefix] = $namespaceURI; } /** * Imports a feed located at $uri. * * @param string $uri * @throws Zend_Feed_Exception * @return Zend_Feed_Abstract */ public static function import($uri) { $client = self::getHttpClient(); $client->setUri($uri); $response = $client->request('GET'); if ($response->getStatus() !== 200) { /** * @see Zend_Feed_Exception */ require_once 'Zend/Feed/Exception.php'; throw new Zend_Feed_Exception('Feed failed to load, got response code ' . $response->getStatus()); } $feed = $response->getBody(); return self::importString($feed); } /** * Imports a feed represented by $string. * * @param string $string * @throws Zend_Feed_Exception * @return Zend_Feed_Abstract */ public static function importString($string) { // Load the feed as an XML DOMDocument object $libxml_errflag = libxml_use_internal_errors(true); $doc = new DOMDocument; if (trim($string) == '') { require_once 'Zend/Feed/Exception.php'; throw new Zend_Feed_Exception('Document/string being imported' . ' is an Empty string or comes from an empty HTTP response'); } $status = $doc->loadXML($string); libxml_use_internal_errors($libxml_errflag); if (!$status) { // prevent the class to generate an undefined variable notice (ZF-2590) // Build error message $error = libxml_get_last_error(); if ($error && $error->message) { $errormsg = "DOMDocument cannot parse XML: {$error->message}"; } else { $errormsg = "DOMDocument cannot parse XML"; } /** * @see Zend_Feed_Exception */ require_once 'Zend/Feed/Exception.php'; throw new Zend_Feed_Exception($errormsg); } // Try to find the base feed element or a single of an Atom feed if ($doc->getElementsByTagName('feed')->item(0) || $doc->getElementsByTagName('entry')->item(0)) { /** * @see Zend_Feed_Atom */ require_once 'Zend/Feed/Atom.php'; // return a newly created Zend_Feed_Atom object return new Zend_Feed_Atom(null, $string); } // Try to find the base feed element of an RSS feed if ($doc->getElementsByTagName('channel')->item(0)) { /** * @see Zend_Feed_Rss */ require_once 'Zend/Feed/Rss.php'; // return a newly created Zend_Feed_Rss object return new Zend_Feed_Rss(null, $string); } // $string does not appear to be a valid feed of the supported types /** * @see Zend_Feed_Exception */ require_once 'Zend/Feed/Exception.php'; throw new Zend_Feed_Exception('Invalid or unsupported feed format'); } /** * Imports a feed from a file located at $filename. * * @param string $filename * @throws Zend_Feed_Exception * @return Zend_Feed_Abstract */ public static function importFile($filename) { @ini_set('track_errors', 1); $feed = @file_get_contents($filename); @ini_restore('track_errors'); if ($feed === false) { /** * @see Zend_Feed_Exception */ require_once 'Zend/Feed/Exception.php'; throw new Zend_Feed_Exception("File could not be loaded: $php_errormsg"); } return self::importString($feed); } /** * Attempts to find feeds at $uri referenced by tags. Returns an * array of the feeds referenced at $uri. * * @todo Allow findFeeds() to follow one, but only one, code 302. * * @param string $uri * @throws Zend_Feed_Exception * @return array */ public static function findFeeds($uri) { // Get the HTTP response from $uri and save the contents $client = self::getHttpClient(); $client->setUri($uri); $response = $client->request(); if ($response->getStatus() !== 200) { /** * @see Zend_Feed_Exception */ require_once 'Zend/Feed/Exception.php'; throw new Zend_Feed_Exception("Failed to access $uri, got response code " . $response->getStatus()); } $contents = $response->getBody(); // Parse the contents for appropriate tags @ini_set('track_errors', 1); $pattern = '~(]+)/?>~i'; $result = @preg_match_all($pattern, $contents, $matches); @ini_restore('track_errors'); if ($result === false) { /** * @see Zend_Feed_Exception */ require_once 'Zend/Feed/Exception.php'; throw new Zend_Feed_Exception("Internal error: $php_errormsg"); } // Try to fetch a feed for each link tag that appears to refer to a feed $feeds = array(); if (isset($matches[1]) && count($matches[1]) > 0) { foreach ($matches[1] as $link) { // force string to be an utf-8 one if (!mb_check_encoding($link, 'UTF-8')) { $link = mb_convert_encoding($link, 'UTF-8'); } $xml = @simplexml_load_string(rtrim($link, ' /') . ' />'); if ($xml === false) { continue; } $attributes = $xml->attributes(); if (!isset($attributes['rel']) || !@preg_match('~^(?:alternate|service\.feed)~i', $attributes['rel'])) { continue; } if (!isset($attributes['type']) || !@preg_match('~^application/(?:atom|rss|rdf)\+xml~', $attributes['type'])) { continue; } if (!isset($attributes['href'])) { continue; } try { // checks if we need to canonize the given uri try { $uri = Zend_Uri::factory((string) $attributes['href']); } catch (Zend_Uri_Exception $e) { // canonize the uri $path = (string) $attributes['href']; $query = $fragment = ''; if (substr($path, 0, 1) != '/') { // add the current root path to this one $path = rtrim($client->getUri()->getPath(), '/') . '/' . $path; } if (strpos($path, '?') !== false) { list($path, $query) = explode('?', $path, 2); } if (strpos($query, '#') !== false) { list($query, $fragment) = explode('#', $query, 2); } $uri = Zend_Uri::factory($client->getUri(true)); $uri->setPath($path); $uri->setQuery($query); $uri->setFragment($fragment); } $feed = self::import($uri); } catch (Exception $e) { continue; } $feeds[$uri->getUri()] = $feed; } } // Return the fetched feeds return $feeds; } /** * Construct a new Zend_Feed_Abstract object from a custom array * * @param array $data * @param string $format (rss|atom) the requested output format * @return Zend_Feed_Abstract */ public static function importArray(array $data, $format = 'atom') { $obj = 'Zend_Feed_' . ucfirst(strtolower($format)); if (!class_exists($obj)) { require_once 'Zend/Loader.php'; Zend_Loader::loadClass($obj); } /** * @see Zend_Feed_Builder */ require_once 'Zend/Feed/Builder.php'; return new $obj(null, null, new Zend_Feed_Builder($data)); } /** * Construct a new Zend_Feed_Abstract object from a Zend_Feed_Builder_Interface data source * * @param Zend_Feed_Builder_Interface $builder this object will be used to extract the data of the feed * @param string $format (rss|atom) the requested output format * @return Zend_Feed_Abstract */ public static function importBuilder(Zend_Feed_Builder_Interface $builder, $format = 'atom') { $obj = 'Zend_Feed_' . ucfirst(strtolower($format)); if (!class_exists($obj)) { require_once 'Zend/Loader.php'; Zend_Loader::loadClass($obj); } return new $obj(null, null, $builder); } }