Reader.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Feed_Reader
  17. * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @version $Id$
  20. */
  21. /**
  22. * @see Zend_Feed
  23. */
  24. require_once 'Zend/Feed.php';
  25. /**
  26. * @see Zend_Feed_Reader_Feed_Rss
  27. */
  28. require_once 'Zend/Feed/Reader/Feed/Rss.php';
  29. /**
  30. * @see Zend_Feed_Reader_Feed_Atom
  31. */
  32. require_once 'Zend/Feed/Reader/Feed/Atom.php';
  33. /**
  34. * @category Zend
  35. * @package Zend_Feed_Reader
  36. * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
  37. * @license http://framework.zend.com/license/new-bsd New BSD License
  38. */
  39. class Zend_Feed_Reader
  40. {
  41. /**
  42. * Namespace constants
  43. */
  44. const NAMESPACE_ATOM_03 = 'http://purl.org/atom/ns#';
  45. const NAMESPACE_ATOM_10 = 'http://www.w3.org/2005/Atom';
  46. const NAMESPACE_RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
  47. const NAMESPACE_RSS_090 = 'http://my.netscape.com/rdf/simple/0.9/';
  48. const NAMESPACE_RSS_10 = 'http://purl.org/rss/1.0/';
  49. /**
  50. * Feed type constants
  51. */
  52. const TYPE_ANY = 'any';
  53. const TYPE_ATOM_03 = 'atom-03';
  54. const TYPE_ATOM_10 = 'atom-10';
  55. const TYPE_ATOM_ANY = 'atom';
  56. const TYPE_RSS_090 = 'rss-090';
  57. const TYPE_RSS_091 = 'rss-091';
  58. const TYPE_RSS_091_NETSCAPE = 'rss-091n';
  59. const TYPE_RSS_091_USERLAND = 'rss-091u';
  60. const TYPE_RSS_092 = 'rss-092';
  61. const TYPE_RSS_093 = 'rss-093';
  62. const TYPE_RSS_094 = 'rss-094';
  63. const TYPE_RSS_10 = 'rss-10';
  64. const TYPE_RSS_20 = 'rss-20';
  65. const TYPE_RSS_ANY = 'rss';
  66. /**
  67. * Cache instance
  68. *
  69. * @var Zend_Cache_Core
  70. */
  71. protected static $_cache = null;
  72. /**
  73. * HTTP client object to use for retrieving feeds
  74. *
  75. * @var Zend_Http_Client
  76. */
  77. protected static $_httpClient = null;
  78. /**
  79. * Override HTTP PUT and DELETE request methods?
  80. *
  81. * @var boolean
  82. */
  83. protected static $_httpMethodOverride = false;
  84. protected static $_httpConditionalGet = false;
  85. protected static $_pluginLoader = null;
  86. protected static $_prefixPaths = array();
  87. protected static $_extensions = array(
  88. 'feed' => array(
  89. 'DublinCore_Feed',
  90. 'Atom_Feed'
  91. ),
  92. 'entry' => array(
  93. 'Content_Entry',
  94. 'DublinCore_Entry',
  95. 'Atom_Entry'
  96. ),
  97. 'core' => array(
  98. 'DublinCore_Feed',
  99. 'Atom_Feed',
  100. 'Content_Entry',
  101. 'DublinCore_Entry',
  102. 'Atom_Entry'
  103. )
  104. );
  105. /**
  106. * Get the Feed cache
  107. *
  108. * @return Zend_Cache_Core
  109. */
  110. public static function getCache()
  111. {
  112. return self::$_cache;
  113. }
  114. /**
  115. * Set the feed cache
  116. *
  117. * @param Zend_Cache_Core $cache
  118. * @return void
  119. */
  120. public static function setCache(Zend_Cache_Core $cache)
  121. {
  122. self::$_cache = $cache;
  123. }
  124. /**
  125. * Set the HTTP client instance
  126. *
  127. * Sets the HTTP client object to use for retrieving the feeds.
  128. *
  129. * @param Zend_Http_Client $httpClient
  130. * @return void
  131. */
  132. public static function setHttpClient(Zend_Http_Client $httpClient)
  133. {
  134. self::$_httpClient = $httpClient;
  135. }
  136. /**
  137. * Gets the HTTP client object. If none is set, a new Zend_Http_Client will be used.
  138. *
  139. * @return Zend_Http_Client_Abstract
  140. */
  141. public static function getHttpClient()
  142. {
  143. if (!self::$_httpClient instanceof Zend_Http_Client) {
  144. /**
  145. * @see Zend_Http_Client
  146. */
  147. require_once 'Zend/Http/Client.php';
  148. self::$_httpClient = new Zend_Http_Client();
  149. }
  150. return self::$_httpClient;
  151. }
  152. /**
  153. * Toggle using POST instead of PUT and DELETE HTTP methods
  154. *
  155. * Some feed implementations do not accept PUT and DELETE HTTP
  156. * methods, or they can't be used because of proxies or other
  157. * measures. This allows turning on using POST where PUT and
  158. * DELETE would normally be used; in addition, an
  159. * X-Method-Override header will be sent with a value of PUT or
  160. * DELETE as appropriate.
  161. *
  162. * @param boolean $override Whether to override PUT and DELETE.
  163. * @return void
  164. */
  165. public static function setHttpMethodOverride($override = true)
  166. {
  167. self::$_httpMethodOverride = $override;
  168. }
  169. /**
  170. * Get the HTTP override state
  171. *
  172. * @return boolean
  173. */
  174. public static function getHttpMethodOverride()
  175. {
  176. return self::$_httpMethodOverride;
  177. }
  178. /**
  179. * Set the flag indicating whether or not to use HTTP conditional GET
  180. *
  181. * @param bool $bool
  182. * @return void
  183. */
  184. public static function useHttpConditionalGet($bool = true)
  185. {
  186. self::$_httpConditionalGet = $bool;
  187. }
  188. /**
  189. * Import a feed by providing a URL
  190. *
  191. * @param string $url The URL to the feed
  192. * @param string $etag OPTIONAL Last received ETag for this resource
  193. * @param string $lastModified OPTIONAL Last-Modified value for this resource
  194. * @return Zend_Feed_Reader_Feed_Interface
  195. */
  196. public static function import($uri, $etag = null, $lastModified = null)
  197. {
  198. $cache = self::getCache();
  199. $feed = null;
  200. $responseXml = '';
  201. $client = self::getHttpClient();
  202. $client->resetParameters();
  203. $client->setHeaders('If-None-Match', null);
  204. $client->setHeaders('If-Modified-Since', null);
  205. $client->setUri($uri);
  206. $cacheId = 'Zend_Feed_Reader_' . md5($uri);
  207. if (self::$_httpConditionalGet && $cache) {
  208. $data = $cache->load($cacheId);
  209. if ($data) {
  210. if (is_null($etag)) {
  211. $etag = $cache->load($cacheId.'_etag');
  212. }
  213. if (is_null($lastModified)) {
  214. $lastModified = $cache->load($cacheId.'_lastmodified');;
  215. }
  216. if ($etag) {
  217. $client->setHeaders('If-None-Match', $etag);
  218. }
  219. if ($lastModified) {
  220. $client->setHeaders('If-Modified-Since', $lastModified);
  221. }
  222. }
  223. $response = $client->request('GET');
  224. if ($response->getStatus() !== 200 && $response->getStatus() !== 304) {
  225. require_once 'Zend/Feed/Exception.php';
  226. throw new Zend_Feed_Exception('Feed failed to load, got response code ' . $response->getStatus());
  227. }
  228. if ($response->getStatus() == 304) {
  229. $responseXml = $data;
  230. } else {
  231. $responseXml = $response->getBody();
  232. $cache->save($responseXml, $cacheId);
  233. if ($response->getHeader('ETag')) {
  234. $cache->save($response->getHeader('ETag'), $cacheId.'_etag');
  235. }
  236. if ($response->getHeader('Last-Modified')) {
  237. $cache->save($response->getHeader('Last-Modified'), $cacheId.'_lastmodified');
  238. }
  239. }
  240. return self::importString($responseXml);
  241. } elseif ($cache) {
  242. $data = $cache->load($cacheId);
  243. if ($data !== false) {
  244. return self::importString($data);
  245. }
  246. $response = $client->request('GET');
  247. if ($response->getStatus() !== 200) {
  248. require_once 'Zend/Feed/Exception.php';
  249. throw new Zend_Feed_Exception('Feed failed to load, got response code ' . $response->getStatus());
  250. }
  251. $responseXml = $response->getBody();
  252. $cache->save($responseXml, $cacheId);
  253. return self::importString($responseXml);
  254. } else {
  255. $response = $client->request('GET');
  256. if ($response->getStatus() !== 200) {
  257. require_once 'Zend/Feed/Exception.php';
  258. throw new Zend_Feed_Exception('Feed failed to load, got response code ' . $response->getStatus());
  259. }
  260. return self::importString($response->getBody());
  261. }
  262. }
  263. /**
  264. * Import a feed by providing a Zend_Feed_Abstract object
  265. *
  266. * @param Zend_Feed_Abstract $feed A fully instantiated Zend_Feed object
  267. * @return Zend_Feed_Reader_Feed_Interface
  268. */
  269. public static function importFeed(Zend_Feed_Abstract $feed)
  270. {
  271. $dom = $feed->getDOM()->ownerDocument;
  272. $type = self::detectType($dom);
  273. self::_registerCoreExtensions();
  274. if (substr($type, 0, 3) == 'rss') {
  275. $reader = new Zend_Feed_Reader_Feed_Rss($dom, $type);
  276. } else {
  277. $reader = new Zend_Feed_Reader_Feed_Atom($dom, $type);
  278. }
  279. return $reader;
  280. }
  281. /**
  282. * Import a feed froma string
  283. *
  284. * @param string $string
  285. * @return Zend_Feed_Reader_Feed_Interface
  286. */
  287. public static function importString($string)
  288. {
  289. $libxml_errflag = libxml_use_internal_errors(true);
  290. $dom = new DOMDocument;
  291. $status = $dom->loadXML($string);
  292. libxml_use_internal_errors($libxml_errflag);
  293. if (!$status) {
  294. // Build error message
  295. $error = libxml_get_last_error();
  296. if ($error && $error->message) {
  297. $errormsg = "DOMDocument cannot parse XML: {$error->message}";
  298. } else {
  299. $errormsg = "DOMDocument cannot parse XML: Please check the XML document's validity";
  300. }
  301. require_once 'Zend/Feed/Exception.php';
  302. throw new Zend_Feed_Exception($errormsg);
  303. }
  304. $type = self::detectType($dom);
  305. self::_registerCoreExtensions();
  306. if (substr($type, 0, 3) == 'rss') {
  307. $reader = new Zend_Feed_Reader_Feed_Rss($dom, $type);
  308. } else {
  309. $reader = new Zend_Feed_Reader_Feed_Atom($dom, $type);
  310. }
  311. return $reader;
  312. }
  313. /**
  314. * Imports a feed from a file located at $filename.
  315. *
  316. * @param string $filename
  317. * @throws Zend_Feed_Exception
  318. * @return Zend_Feed_Reader_FeedInterface
  319. */
  320. public static function importFile($filename)
  321. {
  322. @ini_set('track_errors', 1);
  323. $feed = @file_get_contents($filename);
  324. @ini_restore('track_errors');
  325. if ($feed === false) {
  326. /**
  327. * @see Zend_Feed_Exception
  328. */
  329. require_once 'Zend/Feed/Exception.php';
  330. throw new Zend_Feed_Exception("File could not be loaded: $php_errormsg");
  331. }
  332. return self::importString($feed);
  333. }
  334. public static function findFeedLinks($uri)
  335. {
  336. // Get the HTTP response from $uri and save the contents
  337. $client = self::getHttpClient();
  338. $client->setUri($uri);
  339. $response = $client->request();
  340. if ($response->getStatus() !== 200) {
  341. /**
  342. * @see Zend_Feed_Exception
  343. */
  344. require_once 'Zend/Feed/Exception.php';
  345. throw new Zend_Feed_Exception("Failed to access $uri, got response code " . $response->getStatus());
  346. }
  347. $responseHtml = $response->getBody();
  348. @ini_set('track_errors', 1);
  349. $dom = new DOMDocument;
  350. $status = @$dom->loadHTML($responseHtml);
  351. @ini_restore('track_errors');
  352. if (!$status) {
  353. if (!isset($php_errormsg)) {
  354. if (function_exists('xdebug_is_enabled')) {
  355. $php_errormsg = '(error message not available, when XDebug is running)';
  356. } else {
  357. $php_errormsg = '(error message not available)';
  358. }
  359. }
  360. require_once 'Zend/Feed/Exception.php';
  361. throw new Zend_Feed_Exception("DOMDocument cannot parse XML: $php_errormsg");
  362. }
  363. $feedLinks = new stdClass;
  364. $links = $dom->getElementsByTagName('link');
  365. foreach ($links as $link) {
  366. if (strtolower($link->getAttribute('rel')) !== 'alternate'
  367. || !$link->getAttribute('type') || !$link->getAttribute('href')) {
  368. continue;
  369. }
  370. if (!isset($feedLinks->rss) && $link->getAttribute('type') == 'application/rss+xml') {
  371. $feedLinks->rss = $link->getAttribute('href');
  372. } elseif(!isset($feedLinks->atom) && $link->getAttribute('type') == 'application/atom+xml') {
  373. $feedLinks->atom = $link->getAttribute('href');
  374. } elseif(!isset($feedLinks->rdf) && $link->getAttribute('type') == 'application/rdf+xml') {
  375. $feedLinks->rdf = $link->getAttribute('href');
  376. }
  377. if (isset($feedLinks->rss) && isset($feedLinks->atom) && isset($feedLinks->rdf)) {
  378. break;
  379. }
  380. }
  381. return $feedLinks;
  382. }
  383. /**
  384. * Detect the feed type of the provided feed
  385. *
  386. * @param Zend_Feed_Abstract $feed A fully instantiated Zend_Feed object
  387. * @return string
  388. */
  389. public static function detectType($feed)
  390. {
  391. if ($feed instanceof Zend_Feed_Reader_FeedInterface) {
  392. $dom = $feed->getDomDocument();
  393. } elseif($feed instanceof DomDocument) {
  394. $dom = $feed;
  395. } elseif(is_string($feed) && !empty($feed)) {
  396. @ini_set('track_errors', 1);
  397. $dom = new DOMDocument;
  398. $status = @$doc->loadXML($string);
  399. @ini_restore('track_errors');
  400. if (!$status) {
  401. if (!isset($php_errormsg)) {
  402. if (function_exists('xdebug_is_enabled')) {
  403. $php_errormsg = '(error message not available, when XDebug is running)';
  404. } else {
  405. $php_errormsg = '(error message not available)';
  406. }
  407. }
  408. require_once 'Zend/Feed/Exception.php';
  409. throw new Zend_Feed_Exception("DOMDocument cannot parse XML: $php_errormsg");
  410. }
  411. } else {
  412. require_once 'Zend/Feed/Exception.php';
  413. throw new Zend_Feed_Exception('Invalid object/scalar provided: must be of type Zend_Feed_Reader_FeedInterface, DomDocument or string');
  414. }
  415. $xpath = new DOMXPath($dom);
  416. if ($xpath->query('/rss')->length) {
  417. $type = self::TYPE_RSS_ANY;
  418. $version = $xpath->evaluate('string(/rss/@version)');
  419. if (strlen($version) > 0) {
  420. switch($version) {
  421. case '2.0':
  422. $type = self::TYPE_RSS_20;
  423. break;
  424. case '0.94':
  425. $type = self::TYPE_RSS_094;
  426. break;
  427. case '0.93':
  428. $type = self::TYPE_RSS_093;
  429. break;
  430. case '0.92':
  431. $type = self::TYPE_RSS_092;
  432. break;
  433. case '0.91':
  434. $type = self::TYPE_RSS_091;
  435. break;
  436. }
  437. }
  438. return $type;
  439. }
  440. $xpath->registerNamespace('rdf', self::NAMESPACE_RDF);
  441. if ($xpath->query('/rdf:RDF')->length) {
  442. $xpath->registerNamespace('rss', self::NAMESPACE_RSS_10);
  443. if ($xpath->query('/rdf:RDF/rss:channel')->length
  444. || $xpath->query('/rdf:RDF/rss:image')->length
  445. || $xpath->query('/rdf:RDF/rss:item')->length
  446. || $xpath->query('/rdf:RDF/rss:textinput')->length
  447. ) {
  448. return self::TYPE_RSS_10;
  449. }
  450. $xpath->registerNamespace('rss', self::NAMESPACE_RSS_090);
  451. if ($xpath->query('/rdf:RDF/rss:channel')->length
  452. || $xpath->query('/rdf:RDF/rss:image')->length
  453. || $xpath->query('/rdf:RDF/rss:item')->length
  454. || $xpath->query('/rdf:RDF/rss:textinput')->length
  455. ) {
  456. return self::TYPE_RSS_090;
  457. }
  458. }
  459. $type = self::TYPE_ATOM_ANY;
  460. $xpath->registerNamespace('atom', self::NAMESPACE_ATOM_10);
  461. if ($xpath->query('//atom:feed')->length) {
  462. return self::TYPE_ATOM_10;
  463. }
  464. $xpath->registerNamespace('atom', self::NAMESPACE_ATOM_03);
  465. if ($xpath->query('//atom:feed')->length) {
  466. return self::TYPE_ATOM_03;
  467. }
  468. return self::TYPE_ANY;
  469. }
  470. /**
  471. * Set plugin loader for use with Extensions
  472. *
  473. * @param Zend_Loader_PluginLoader_Interface $loader
  474. */
  475. public static function setPluginLoader(Zend_Loader_PluginLoader_Interface $loader)
  476. {
  477. self::$_pluginLoader = $loader;
  478. }
  479. /**
  480. * Get plugin loader for use with Extensions
  481. *
  482. * @return Zend_Loader_PluginLoader_Interface $loader
  483. */
  484. public static function getPluginLoader()
  485. {
  486. if (!isset(self::$_pluginLoader)) {
  487. require_once 'Zend/Loader/PluginLoader.php';
  488. self::$_pluginLoader = new Zend_Loader_PluginLoader(array(
  489. 'Zend_Feed_Reader_Extension_' => 'Zend/Feed/Reader/Extension/',
  490. ));
  491. }
  492. return self::$_pluginLoader;
  493. }
  494. /**
  495. * Add prefix path for loading Extensions
  496. *
  497. * @param string $prefix
  498. * @param string $path
  499. * @return void
  500. */
  501. public static function addPrefixPath($prefix, $path)
  502. {
  503. $prefix = rtrim($prefix, '_');
  504. $path = rtrim($path, DIRECTORY_SEPARATOR);
  505. self::getPluginLoader()->addPrefixPath($prefix, $path);
  506. }
  507. /**
  508. * Add multiple Extension prefix paths at once
  509. *
  510. * @param array $spec
  511. * @return void
  512. */
  513. public static function addPrefixPaths(array $spec)
  514. {
  515. if (isset($spec['prefix']) && isset($spec['path'])) {
  516. self::addPrefixPath($spec['prefix'], $spec['path']);
  517. }
  518. foreach ($spec as $prefixPath) {
  519. if (isset($prefixPath['prefix']) && isset($prefixPath['path'])) {
  520. self::addPrefixPath($prefixPath['prefix'], $prefixPath['path']);
  521. }
  522. }
  523. }
  524. /**
  525. * Register an Extension by name
  526. *
  527. * @param string $name
  528. * @return void
  529. * @throws Zend_Feed_Exception if unable to resolve Extension class
  530. */
  531. public static function registerExtension($name)
  532. {
  533. $feedName = $name . '_Feed';
  534. $entryName = $name . '_Entry';
  535. if (self::isRegistered($name)) {
  536. if (self::getPluginLoader()->isLoaded($feedName) ||
  537. self::getPluginLoader()->isLoaded($entryName)) {
  538. return;
  539. }
  540. }
  541. try {
  542. self::getPluginLoader()->load($feedName);
  543. self::$_extensions['feed'][] = $feedName;
  544. } catch (Zend_Loader_PluginLoader_Exception $e) {
  545. }
  546. try {
  547. self::getPluginLoader()->load($entryName);
  548. self::$_extensions['entry'][] = $entryName;
  549. } catch (Zend_Loader_PluginLoader_Exception $e) {
  550. }
  551. if (!self::getPluginLoader()->isLoaded($feedName)
  552. && !self::getPluginLoader()->isLoaded($entryName)
  553. ) {
  554. require_once 'Zend/Feed/Exception.php';
  555. throw new Zend_Feed_Exception('Could not load extension: ' . $name
  556. . 'using Plugin Loader. Check prefix paths are configured and extension exists.');
  557. }
  558. }
  559. /**
  560. * Is a given named Extension registered?
  561. *
  562. * @param string $extensionName
  563. * @return boolean
  564. */
  565. public static function isRegistered($extensionName)
  566. {
  567. $feedName = $extensionName . '_Feed';
  568. $entryName = $extensionName . '_Entry';
  569. if (in_array($feedName, self::$_extensions['feed'])
  570. || in_array($entryName, self::$_extensions['entry'])
  571. ) {
  572. return true;
  573. }
  574. return false;
  575. }
  576. /**
  577. * Get a list of extensions
  578. *
  579. * @return array
  580. */
  581. public static function getExtensions()
  582. {
  583. return self::$_extensions;
  584. }
  585. /**
  586. * Reset class state to defaults
  587. *
  588. * @return void
  589. */
  590. public static function reset()
  591. {
  592. self::$_cache = null;
  593. self::$_httpClient = null;
  594. self::$_httpMethodOverride = false;
  595. self::$_httpConditionalGet = false;
  596. self::$_pluginLoader = null;
  597. self::$_prefixPaths = array();
  598. self::$_extensions = array(
  599. 'feed' => array(
  600. 'DublinCore_Feed',
  601. 'Atom_Feed'
  602. ),
  603. 'entry' => array(
  604. 'Content_Entry',
  605. 'DublinCore_Entry',
  606. 'Atom_Entry'
  607. ),
  608. 'core' => array(
  609. 'DublinCore_Feed',
  610. 'Atom_Feed',
  611. 'Content_Entry',
  612. 'DublinCore_Entry',
  613. 'Atom_Entry'
  614. )
  615. );
  616. }
  617. /**
  618. * Register core (default) extensions
  619. *
  620. * @return void
  621. */
  622. protected static function _registerCoreExtensions()
  623. {
  624. self::registerExtension('DublinCore');
  625. self::registerExtension('Content');
  626. self::registerExtension('Atom');
  627. self::registerExtension('Slash');
  628. self::registerExtension('WellFormedWeb');
  629. self::registerExtension('Thread');
  630. self::registerExtension('Podcast');
  631. }
  632. }