1<?php 2/** 3 * Portions Copyright 2005-2007 Zend Technologies USA Inc. (http://www.zend.com) 4 * Copyright 2007-2016 Horde LLC (http://www.horde.org/) 5 * 6 * @author Chuck Hagenbuch <chuck@horde.org> 7 * @license http://www.horde.org/licenses/bsd BSD 8 * @category Horde 9 * @package Feed 10 */ 11 12/** 13 * @author Chuck Hagenbuch <chuck@horde.org> 14 * @license http://www.horde.org/licenses/bsd BSD 15 * @category Horde 16 * @package Feed 17 */ 18class Horde_Feed 19{ 20 /** 21 * Create a Feed object based on a DOMDocument. 22 * 23 * @param DOMDocument $doc The DOMDocument object to import. 24 * 25 * @throws Horde_Feed_Exception 26 * 27 * @return Horde_Feed_Base The feed object imported from $doc 28 */ 29 public static function create(DOMDocument $doc, $uri = null) 30 { 31 // Try to find the base feed element or a single <entry> of an 32 // Atom feed. 33 if ($feed = $doc->getElementsByTagName('feed')->item(0)) { 34 // Return an Atom feed. 35 return new Horde_Feed_Atom($feed, $uri); 36 } elseif ($entry = $doc->getElementsByTagName('entry')->item(0)) { 37 // Return an Atom single-entry feed. 38 $feeddoc = new DOMDocument($doc->version, 39 $doc->actualEncoding); 40 $feed = $feeddoc->appendChild($feeddoc->createElement('feed')); 41 $feed->appendChild($feeddoc->importNode($entry, true)); 42 43 return new Horde_Feed_Atom($feed, $uri); 44 } 45 46 // Try to find the base feed element of an RSS feed. 47 if ($channel = $doc->getElementsByTagName('channel')->item(0)) { 48 // Return an RSS feed. 49 return new Horde_Feed_Rss($channel, $uri); 50 } 51 52 // Try to find an outline element of an OPML blogroll. 53 if ($outline = $doc->getElementsByTagName('outline')->item(0)) { 54 // Return a blogroll feed. 55 return new Horde_Feed_Blogroll($doc->documentElement, $uri); 56 } 57 58 // $doc does not appear to be a valid feed of the supported 59 // types. 60 throw new Horde_Feed_Exception('Invalid or unsupported feed format: ' 61 . substr($doc->saveXML(), 0, 80) . '...'); 62 } 63 64 /** 65 * Reads a feed represented by $string. 66 * 67 * @param string $string The XML content of the feed. 68 * @param string $uri The feed's URI location, if known. 69 * 70 * @throws Horde_Feed_Exception 71 * 72 * @return Horde_Feed_Base 73 */ 74 public static function read($string, $uri = null) 75 { 76 // Load the feed as a DOMDocument object. 77 libxml_use_internal_errors(true); 78 $doc = new DOMDocument; 79 $doc->recover = true; 80 $loaded = $doc->loadXML($string); 81 if (!$loaded) { 82 $loaded = $doc->loadHTML($string); 83 if (!$loaded) { 84 self::_exception('DOMDocument cannot parse XML', libxml_get_last_error()); 85 } 86 } 87 88 return self::create($doc); 89 } 90 91 /** 92 * Read a feed located at $uri 93 * 94 * @param string $uri The URI to fetch the feed from. 95 * @param Horde_Http_Client $httpclient The HTTP client to use. 96 * 97 * @throws Horde_Feed_Exception 98 * 99 * @return Horde_Feed_Base 100 */ 101 public static function readUri($uri, Horde_Http_Client $httpclient = null) 102 { 103 if (is_null($httpclient)) { 104 $httpclient = new Horde_Http_Client(); 105 } 106 107 try { 108 $response = $httpclient->get($uri); 109 } catch (Horde_Http_Exception $e) { 110 throw new Horde_Feed_Exception('Error reading feed: ' . $e->getMessage()); 111 } 112 if ($response->code != 200) { 113 throw new Horde_Feed_Exception('Unable to read feed, got response code ' . $response->code); 114 } 115 $feed = $response->getBody(); 116 return self::read($feed, $uri); 117 } 118 119 /** 120 * Read a feed from $filename 121 * 122 * @param string $filename The location of the feed file on an accessible 123 * filesystem or through an available stream wrapper. 124 * 125 * @throws Horde_Feed_Exception 126 * 127 * @return Horde_Feed_Base 128 */ 129 public static function readFile($filename) 130 { 131 libxml_use_internal_errors(true); 132 $doc = new DOMDocument; 133 $doc->recover = true; 134 $contents = file_get_contents($filename); 135 $loaded = $doc->loadXML($contents); 136 if (!$loaded) { 137 $loaded = $doc->loadHTML($contents); 138 if (!$loaded) { 139 self::_exception('File could not be read or parsed', libxml_get_last_error()); 140 } 141 } 142 143 return self::create($doc); 144 } 145 146 /** 147 * Builds an exception message from a libXMLError object. 148 * 149 * @param string $msg An error message. 150 * @param libXMLError $error An error object. 151 * 152 * @throws Horde_Feed_Exception 153 */ 154 protected static function _exception($msg, $error) 155 { 156 if ($error) { 157 $msg .= ': ' . $error->message; 158 if ($error->file) { 159 $msg .= sprintf(' in file %s, line %d, column %d', 160 $error->file, $error->line, $error->column); 161 } 162 } 163 throw new Horde_Feed_Exception($msg); 164 } 165} 166