rssparser.class.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825
  1. <?php
  2. /* Copyright (C) 2011-2012 Laurent Destailleur <eldy@users.sourceforge.net>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. */
  17. /**
  18. * \file htdocs/core/class/rssparser.class.php
  19. * \ingroup core
  20. * \brief File of class to parse RSS feeds
  21. */
  22. /**
  23. * Class to parse RSS files
  24. */
  25. class RssParser
  26. {
  27. /**
  28. * @var DoliDB Database handler.
  29. */
  30. public $db;
  31. /**
  32. * @var string Error code (or message)
  33. */
  34. public $error = '';
  35. private $_format = '';
  36. private $_urlRSS;
  37. private $_language;
  38. private $_generator;
  39. private $_copyright;
  40. private $_lastbuilddate;
  41. private $_imageurl;
  42. private $_link;
  43. private $_title;
  44. private $_description;
  45. private $_lastfetchdate; // Last successful fetch
  46. private $_rssarray = array();
  47. // For parsing with xmlparser
  48. public $stack = array(); // parser stack
  49. private $_CONTENT_CONSTRUCTS = array('content', 'summary', 'info', 'title', 'tagline', 'copyright');
  50. /**
  51. * Constructor
  52. *
  53. * @param DoliDB $db Database handler
  54. */
  55. public function __construct($db)
  56. {
  57. $this->db = $db;
  58. }
  59. /**
  60. * getFormat
  61. *
  62. * @return string
  63. */
  64. public function getFormat()
  65. {
  66. return $this->_format;
  67. }
  68. /**
  69. * getUrlRss
  70. *
  71. * @return string
  72. */
  73. public function getUrlRss()
  74. {
  75. return $this->_urlRSS;
  76. }
  77. /**
  78. * getLanguage
  79. *
  80. * @return string
  81. */
  82. public function getLanguage()
  83. {
  84. return $this->_language;
  85. }
  86. /**
  87. * getGenerator
  88. *
  89. * @return string
  90. */
  91. public function getGenerator()
  92. {
  93. return $this->_generator;
  94. }
  95. /**
  96. * getCopyright
  97. *
  98. * @return string
  99. */
  100. public function getCopyright()
  101. {
  102. return $this->_copyright;
  103. }
  104. /**
  105. * getLastBuildDate
  106. *
  107. * @return string
  108. */
  109. public function getLastBuildDate()
  110. {
  111. return $this->_lastbuilddate;
  112. }
  113. /**
  114. * getImageUrl
  115. *
  116. * @return string
  117. */
  118. public function getImageUrl()
  119. {
  120. return $this->_imageurl;
  121. }
  122. /**
  123. * getLink
  124. *
  125. * @return string
  126. */
  127. public function getLink()
  128. {
  129. return $this->_link;
  130. }
  131. /**
  132. * getTitle
  133. *
  134. * @return string
  135. */
  136. public function getTitle()
  137. {
  138. return $this->_title;
  139. }
  140. /**
  141. * getDescription
  142. *
  143. * @return string
  144. */
  145. public function getDescription()
  146. {
  147. return $this->_description;
  148. }
  149. /**
  150. * getLastFetchDate
  151. *
  152. * @return string
  153. */
  154. public function getLastFetchDate()
  155. {
  156. return $this->_lastfetchdate;
  157. }
  158. /**
  159. * getItems
  160. *
  161. * @return string
  162. */
  163. public function getItems()
  164. {
  165. return $this->_rssarray;
  166. }
  167. /**
  168. * Parse rss URL
  169. *
  170. * @param string $urlRSS Url to parse
  171. * @param int $maxNb Max nb of records to get (0 for no limit)
  172. * @param int $cachedelay 0=No cache, nb of seconds we accept cache files (cachedir must also be defined)
  173. * @param string $cachedir Directory where to save cache file (For example $conf->externalrss->dir_temp)
  174. * @return int <0 if KO, >0 if OK
  175. */
  176. public function parser($urlRSS, $maxNb = 0, $cachedelay = 60, $cachedir = '')
  177. {
  178. global $conf;
  179. include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
  180. include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
  181. $rss = '';
  182. $str = ''; // This will contain content of feed
  183. // Check parameters
  184. if (!dol_is_url($urlRSS)) {
  185. $this->error = "ErrorBadUrl";
  186. return -1;
  187. }
  188. $this->_urlRSS = $urlRSS;
  189. $newpathofdestfile = $cachedir.'/'.dol_hash($this->_urlRSS, 3); // Force md5 hash (does not contains special chars)
  190. $newmask = '0644';
  191. //dol_syslog("RssPArser::parser parse url=".$urlRSS." => cache file=".$newpathofdestfile);
  192. $nowgmt = dol_now();
  193. // Search into cache
  194. $foundintocache = 0;
  195. if ($cachedelay > 0 && $cachedir) {
  196. $filedate = dol_filemtime($newpathofdestfile);
  197. if ($filedate >= ($nowgmt - $cachedelay)) {
  198. //dol_syslog("RssParser::parser cache file ".$newpathofdestfile." is not older than now - cachedelay (".$nowgmt." - ".$cachedelay.") so we use it.");
  199. $foundintocache = 1;
  200. $this->_lastfetchdate = $filedate;
  201. } else {
  202. dol_syslog(get_class($this)."::parser cache file ".$newpathofdestfile." is not found or older than now - cachedelay (".$nowgmt." - ".$cachedelay.") so we can't use it.");
  203. }
  204. }
  205. // Load file into $str
  206. if ($foundintocache) { // Cache file found and is not too old
  207. $str = file_get_contents($newpathofdestfile);
  208. } else {
  209. try {
  210. $result = getURLContent($this->_urlRSS, 'GET', '', 1, array(), array('http', 'https'), 0);
  211. if (!empty($result['content'])) {
  212. $str = $result['content'];
  213. }
  214. } catch (Exception $e) {
  215. print 'Error retrieving URL '.$this->_urlRSS.' - '.$e->getMessage();
  216. }
  217. }
  218. if ($str !== false) {
  219. // Convert $str into xml
  220. if (!empty($conf->global->EXTERNALRSS_USE_SIMPLEXML)) {
  221. //print 'xx'.LIBXML_NOCDATA;
  222. libxml_use_internal_errors(false);
  223. $rss = simplexml_load_string($str, "SimpleXMLElement", LIBXML_NOCDATA);
  224. } else {
  225. if (!function_exists('xml_parser_create')) {
  226. $this->error = 'Function xml_parser_create are not supported by your PHP';
  227. return -1;
  228. }
  229. $xmlparser = xml_parser_create('');
  230. if (!is_resource($xmlparser)) {
  231. $this->error = "ErrorFailedToCreateParser";
  232. return -1;
  233. }
  234. xml_set_object($xmlparser, $this);
  235. xml_set_element_handler($xmlparser, 'feed_start_element', 'feed_end_element');
  236. xml_set_character_data_handler($xmlparser, 'feed_cdata');
  237. $status = xml_parse($xmlparser, $str);
  238. xml_parser_free($xmlparser);
  239. $rss = $this;
  240. //var_dump($rss->_format);exit;
  241. }
  242. }
  243. // If $rss loaded
  244. if ($rss) {
  245. // Save file into cache
  246. if (empty($foundintocache) && $cachedir) {
  247. dol_syslog(get_class($this)."::parser cache file ".$newpathofdestfile." is saved onto disk.");
  248. if (!dol_is_dir($cachedir)) {
  249. dol_mkdir($cachedir);
  250. }
  251. $fp = fopen($newpathofdestfile, 'w');
  252. if ($fp) {
  253. fwrite($fp, $str);
  254. fclose($fp);
  255. if (!empty($conf->global->MAIN_UMASK)) {
  256. $newmask = $conf->global->MAIN_UMASK;
  257. }
  258. @chmod($newpathofdestfile, octdec($newmask));
  259. $this->_lastfetchdate = $nowgmt;
  260. } else {
  261. print 'Error, failed to open file '.$newpathofdestfile.' for write';
  262. }
  263. }
  264. unset($str); // Free memory
  265. if (empty($rss->_format)) { // If format not detected automatically
  266. $rss->_format = 'rss';
  267. if (empty($rss->channel)) {
  268. $rss->_format = 'atom';
  269. }
  270. }
  271. $items = array();
  272. // Save description entries
  273. if ($rss->_format == 'rss') {
  274. //var_dump($rss);
  275. if (!empty($conf->global->EXTERNALRSS_USE_SIMPLEXML)) {
  276. if (!empty($rss->channel->language)) {
  277. $this->_language = (string) $rss->channel->language;
  278. }
  279. if (!empty($rss->channel->generator)) {
  280. $this->_generator = (string) $rss->channel->generator;
  281. }
  282. if (!empty($rss->channel->copyright)) {
  283. $this->_copyright = (string) $rss->channel->copyright;
  284. }
  285. if (!empty($rss->channel->lastbuilddate)) {
  286. $this->_lastbuilddate = (string) $rss->channel->lastbuilddate;
  287. }
  288. if (!empty($rss->channel->image->url[0])) {
  289. $this->_imageurl = (string) $rss->channel->image->url[0];
  290. }
  291. if (!empty($rss->channel->link)) {
  292. $this->_link = (string) $rss->channel->link;
  293. }
  294. if (!empty($rss->channel->title)) {
  295. $this->_title = (string) $rss->channel->title;
  296. }
  297. if (!empty($rss->channel->description)) {
  298. $this->_description = (string) $rss->channel->description;
  299. }
  300. } else {
  301. //var_dump($rss->channel);
  302. if (!empty($rss->channel['language'])) {
  303. $this->_language = (string) $rss->channel['language'];
  304. }
  305. if (!empty($rss->channel['generator'])) {
  306. $this->_generator = (string) $rss->channel['generator'];
  307. }
  308. if (!empty($rss->channel['copyright'])) {
  309. $this->_copyright = (string) $rss->channel['copyright'];
  310. }
  311. if (!empty($rss->channel['lastbuilddate'])) {
  312. $this->_lastbuilddate = (string) $rss->channel['lastbuilddate'];
  313. }
  314. if (!empty($rss->image['url'])) {
  315. $this->_imageurl = (string) $rss->image['url'];
  316. }
  317. if (!empty($rss->channel['link'])) {
  318. $this->_link = (string) $rss->channel['link'];
  319. }
  320. if (!empty($rss->channel['title'])) {
  321. $this->_title = (string) $rss->channel['title'];
  322. }
  323. if (!empty($rss->channel['description'])) {
  324. $this->_description = (string) $rss->channel['description'];
  325. }
  326. }
  327. if (!empty($conf->global->EXTERNALRSS_USE_SIMPLEXML)) {
  328. $items = $rss->channel->item; // With simplexml
  329. } else {
  330. $items = $rss->items; // With xmlparse
  331. }
  332. //var_dump($items);exit;
  333. } elseif ($rss->_format == 'atom') {
  334. //var_dump($rss);
  335. if (!empty($conf->global->EXTERNALRSS_USE_SIMPLEXML)) {
  336. if (!empty($rss->generator)) {
  337. $this->_generator = (string) $rss->generator;
  338. }
  339. if (!empty($rss->lastbuilddate)) {
  340. $this->_lastbuilddate = (string) $rss->modified;
  341. }
  342. if (!empty($rss->link->href)) {
  343. $this->_link = (string) $rss->link->href;
  344. }
  345. if (!empty($rss->title)) {
  346. $this->_title = (string) $rss->title;
  347. }
  348. if (!empty($rss->description)) {
  349. $this->_description = (string) $rss->description;
  350. }
  351. } else {
  352. //if (!empty($rss->channel['rss_language'])) $this->_language = (string) $rss->channel['rss_language'];
  353. if (!empty($rss->channel['generator'])) {
  354. $this->_generator = (string) $rss->channel['generator'];
  355. }
  356. //if (!empty($rss->channel['rss_copyright'])) $this->_copyright = (string) $rss->channel['rss_copyright'];
  357. if (!empty($rss->channel['modified'])) {
  358. $this->_lastbuilddate = (string) $rss->channel['modified'];
  359. }
  360. //if (!empty($rss->image['rss_url'])) $this->_imageurl = (string) $rss->image['rss_url'];
  361. if (!empty($rss->channel['link'])) {
  362. $this->_link = (string) $rss->channel['link'];
  363. }
  364. if (!empty($rss->channel['title'])) {
  365. $this->_title = (string) $rss->channel['title'];
  366. }
  367. //if (!empty($rss->channel['rss_description'])) $this->_description = (string) $rss->channel['rss_description'];
  368. if (!empty($rss->channel)) {
  369. $this->_imageurl = $this->getAtomImageUrl($rss->channel);
  370. }
  371. }
  372. if (!empty($conf->global->EXTERNALRSS_USE_SIMPLEXML)) {
  373. $tmprss = xml2php($rss);
  374. $items = $tmprss['entry'];
  375. } else {
  376. // With simplexml
  377. $items = $rss->items; // With xmlparse
  378. }
  379. //var_dump($items);exit;
  380. }
  381. $i = 0;
  382. // Loop on each record
  383. if (is_array($items)) {
  384. foreach ($items as $item) {
  385. //var_dump($item);exit;
  386. if ($rss->_format == 'rss') {
  387. if (!empty($conf->global->EXTERNALRSS_USE_SIMPLEXML)) {
  388. $itemLink = (string) $item->link;
  389. $itemTitle = (string) $item->title;
  390. $itemDescription = (string) $item->description;
  391. $itemPubDate = (string) $item->pubDate;
  392. $itemId = '';
  393. $itemAuthor = '';
  394. } else {
  395. $itemLink = (string) $item['link'];
  396. $itemTitle = (string) $item['title'];
  397. $itemDescription = (string) $item['description'];
  398. $itemPubDate = (string) $item['pubdate'];
  399. $itemId = (string) $item['guid'];
  400. $itemAuthor = (string) $item['author'];
  401. }
  402. // Loop on each category
  403. $itemCategory = array();
  404. if (is_array($item->category)) {
  405. foreach ($item->category as $cat) {
  406. $itemCategory[] = (string) $cat;
  407. }
  408. }
  409. } elseif ($rss->_format == 'atom') {
  410. if (!empty($conf->global->EXTERNALRSS_USE_SIMPLEXML)) {
  411. $itemLink = (isset($item['link']) ? (string) $item['link'] : '');
  412. $itemTitle = (string) $item['title'];
  413. $itemDescription = $this->getAtomItemDescription($item);
  414. $itemPubDate = (string) $item['created'];
  415. $itemId = (string) $item['id'];
  416. $itemAuthor = (string) ($item['author'] ? $item['author'] : $item['author_name']);
  417. } else {
  418. $itemLink = (isset($item['link']) ? (string) $item['link'] : '');
  419. $itemTitle = (string) $item['title'];
  420. $itemDescription = $this->getAtomItemDescription($item);
  421. $itemPubDate = (string) $item['created'];
  422. $itemId = (string) $item['id'];
  423. $itemAuthor = (string) ($item['author'] ? $item['author'] : $item['author_name']);
  424. }
  425. $itemCategory = array();
  426. } else {
  427. $itemCategory = array();
  428. $itemLink = '';
  429. $itemTitle = '';
  430. $itemDescription = '';
  431. $itemPubDate = '';
  432. $itemId = '';
  433. $itemAuthor = '';
  434. print 'ErrorBadFeedFormat';
  435. }
  436. // Add record to result array
  437. $this->_rssarray[$i] = array(
  438. 'link'=>$itemLink,
  439. 'title'=>$itemTitle,
  440. 'description'=>$itemDescription,
  441. 'pubDate'=>$itemPubDate,
  442. 'category'=>$itemCategory,
  443. 'id'=>$itemId,
  444. 'author'=>$itemAuthor
  445. );
  446. //var_dump($this->_rssarray);
  447. $i++;
  448. if ($i > $maxNb) {
  449. break; // We get all records we want
  450. }
  451. }
  452. }
  453. return 1;
  454. } else {
  455. $this->error = 'ErrorFailedToLoadRSSFile';
  456. return -1;
  457. }
  458. }
  459. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  460. /**
  461. * Triggered when opened tag is found
  462. *
  463. * @param string $p Start
  464. * @param string $element Tag
  465. * @param array $attrs Attributes of tags
  466. * @return void
  467. */
  468. public function feed_start_element($p, $element, &$attrs)
  469. {
  470. // phpcs:enable
  471. $el = $element = strtolower($element);
  472. $attrs = array_change_key_case($attrs, CASE_LOWER);
  473. // check for a namespace, and split if found
  474. $ns = false;
  475. if (strpos($element, ':')) {
  476. list($ns, $el) = explode(':', $element, 2);
  477. }
  478. if ($ns and $ns != 'rdf') {
  479. $this->current_namespace = $ns;
  480. }
  481. // if feed type isn't set, then this is first element of feed identify feed from root element
  482. if (empty($this->_format)) {
  483. if ($el == 'rdf') {
  484. $this->_format = 'rss';
  485. $this->feed_version = '1.0';
  486. } elseif ($el == 'rss') {
  487. $this->_format = 'rss';
  488. $this->feed_version = $attrs['version'];
  489. } elseif ($el == 'feed') {
  490. $this->_format = 'atom';
  491. $this->feed_version = $attrs['version'];
  492. $this->inchannel = true;
  493. }
  494. return;
  495. }
  496. if ($el == 'channel') {
  497. $this->inchannel = true;
  498. } elseif ($el == 'item' || $el == 'entry') {
  499. $this->initem = true;
  500. if (isset($attrs['rdf:about'])) {
  501. $this->current_item['about'] = $attrs['rdf:about'];
  502. }
  503. } elseif ($this->_format == 'rss' && $this->current_namespace == '' && $el == 'textinput') {
  504. // if we're in the default namespace of an RSS feed,
  505. // record textinput or image fields
  506. $this->intextinput = true;
  507. } elseif ($this->_format == 'rss' && $this->current_namespace == '' && $el == 'image') {
  508. $this->inimage = true;
  509. } elseif ($this->_format == 'atom' && in_array($el, $this->_CONTENT_CONSTRUCTS)) {
  510. // handle atom content constructs
  511. // avoid clashing w/ RSS mod_content
  512. if ($el == 'content') {
  513. $el = 'atom_content';
  514. }
  515. $this->incontent = $el;
  516. } elseif ($this->_format == 'atom' && $this->incontent) {
  517. // if inside an Atom content construct (e.g. content or summary) field treat tags as text
  518. // if tags are inlined, then flatten
  519. $attrs_str = join(' ', array_map('map_attrs', array_keys($attrs), array_values($attrs)));
  520. $this->append_content("<$element $attrs_str>");
  521. array_unshift($this->stack, $el);
  522. } elseif ($this->_format == 'atom' && $el == 'link') {
  523. // Atom support many links per containging element.
  524. // Magpie treats link elements of type rel='alternate'
  525. // as being equivalent to RSS's simple link element.
  526. if (isset($attrs['rel']) && $attrs['rel'] == 'alternate') {
  527. $link_el = 'link';
  528. } elseif (!isset($attrs['rel'])) {
  529. $link_el = 'link';
  530. } else {
  531. $link_el = 'link_'.$attrs['rel'];
  532. }
  533. $this->append($link_el, $attrs['href']);
  534. } else {
  535. // set stack[0] to current element
  536. array_unshift($this->stack, $el);
  537. }
  538. }
  539. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  540. /**
  541. * Triggered when CDATA is found
  542. *
  543. * @param string $p P
  544. * @param string $text Tag
  545. * @return void
  546. */
  547. public function feed_cdata($p, $text)
  548. {
  549. // phpcs:enable
  550. if ($this->_format == 'atom' and $this->incontent) {
  551. $this->append_content($text);
  552. } else {
  553. $current_el = join('_', array_reverse($this->stack));
  554. $this->append($current_el, $text);
  555. }
  556. }
  557. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  558. /**
  559. * Triggered when closed tag is found
  560. *
  561. * @param string $p P
  562. * @param string $el Tag
  563. * @return void
  564. */
  565. public function feed_end_element($p, $el)
  566. {
  567. // phpcs:enable
  568. $el = strtolower($el);
  569. if ($el == 'item' or $el == 'entry') {
  570. $this->items[] = $this->current_item;
  571. $this->current_item = array();
  572. $this->initem = false;
  573. } elseif ($this->_format == 'rss' and $this->current_namespace == '' and $el == 'textinput') {
  574. $this->intextinput = false;
  575. } elseif ($this->_format == 'rss' and $this->current_namespace == '' and $el == 'image') {
  576. $this->inimage = false;
  577. } elseif ($this->_format == 'atom' and in_array($el, $this->_CONTENT_CONSTRUCTS)) {
  578. $this->incontent = false;
  579. } elseif ($el == 'channel' or $el == 'feed') {
  580. $this->inchannel = false;
  581. } elseif ($this->_format == 'atom' and $this->incontent) {
  582. // balance tags properly
  583. // note: i don't think this is actually neccessary
  584. if ($this->stack[0] == $el) {
  585. $this->append_content("</$el>");
  586. } else {
  587. $this->append_content("<$el />");
  588. }
  589. array_shift($this->stack);
  590. } else {
  591. array_shift($this->stack);
  592. }
  593. $this->current_namespace = false;
  594. }
  595. /**
  596. * To concat 2 string with no warning if an operand is not defined
  597. *
  598. * @param string $str1 Str1
  599. * @param string $str2 Str2
  600. * @return string String cancatenated
  601. */
  602. public function concat(&$str1, $str2 = "")
  603. {
  604. if (!isset($str1)) {
  605. $str1 = "";
  606. }
  607. $str1 .= $str2;
  608. }
  609. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  610. /**
  611. * Enter description here ...
  612. *
  613. * @param string $text Text
  614. * @return void
  615. */
  616. public function append_content($text)
  617. {
  618. // phpcs:enable
  619. if ($this->initem) {
  620. $this->concat($this->current_item[$this->incontent], $text);
  621. } elseif ($this->inchannel) {
  622. $this->concat($this->channel[$this->incontent], $text);
  623. }
  624. }
  625. /**
  626. * smart append - field and namespace aware
  627. *
  628. * @param string $el El
  629. * @param string $text Text
  630. * @return void
  631. */
  632. public function append($el, $text)
  633. {
  634. if (!$el) {
  635. return;
  636. }
  637. if ($this->current_namespace) {
  638. if ($this->initem) {
  639. $this->concat($this->current_item[$this->current_namespace][$el], $text);
  640. } elseif ($this->inchannel) {
  641. $this->concat($this->channel[$this->current_namespace][$el], $text);
  642. } elseif ($this->intextinput) {
  643. $this->concat($this->textinput[$this->current_namespace][$el], $text);
  644. } elseif ($this->inimage) {
  645. $this->concat($this->image[$this->current_namespace][$el], $text);
  646. }
  647. } else {
  648. if ($this->initem) {
  649. $this->concat($this->current_item[$el], $text);
  650. } elseif ($this->intextinput) {
  651. $this->concat($this->textinput[$el], $text);
  652. } elseif ($this->inimage) {
  653. $this->concat($this->image[$el], $text);
  654. } elseif ($this->inchannel) {
  655. $this->concat($this->channel[$el], $text);
  656. }
  657. }
  658. }
  659. /**
  660. * Return a description/summary for one item from a ATOM feed
  661. *
  662. * @param array $item A parsed item of a ATOM feed
  663. * @param int $maxlength (optional) The maximum length for the description
  664. * @return string A summary description
  665. */
  666. private function getAtomItemDescription(array $item, $maxlength = 500)
  667. {
  668. $result = "";
  669. if (isset($item['summary'])) {
  670. $result = $item['summary'];
  671. } elseif (isset($item['atom_content'])) {
  672. $result = $item['atom_content'];
  673. }
  674. // remove all HTML elements that can possible break the maximum size of a tooltip,
  675. // like headings, image, video etc. and allow only simple style elements
  676. $result = strip_tags($result, "<br><p><ul><ol><li>");
  677. $result = str_replace("\n", "", $result);
  678. if (strlen($result) > $maxlength) {
  679. $result = substr($result, 0, $maxlength);
  680. $result .= "...";
  681. }
  682. return $result;
  683. }
  684. /**
  685. * Return a URL to a image of the given ATOM feed
  686. *
  687. * @param array $feed The ATOM feed that possible contain a link to a logo or icon
  688. * @return string A URL to a image from a ATOM feed when found, otherwise a empty string
  689. */
  690. private function getAtomImageUrl(array $feed)
  691. {
  692. if (isset($feed['icon'])) {
  693. return $feed['logo'];
  694. }
  695. if (isset($feed['icon'])) {
  696. return $feed['logo'];
  697. }
  698. if (isset($feed['webfeeds:logo'])) {
  699. return $feed['webfeeds:logo'];
  700. }
  701. if (isset($feed['webfeeds:icon'])) {
  702. return $feed['webfeeds:icon'];
  703. }
  704. if (isset($feed['webfeeds:wordmark'])) {
  705. return $feed['webfeeds:wordmark'];
  706. }
  707. return "";
  708. }
  709. }
  710. /**
  711. * Function to convert an XML object into an array
  712. *
  713. * @param SimpleXMLElement $xml Xml
  714. * @return void
  715. */
  716. function xml2php($xml)
  717. {
  718. $fils = 0;
  719. $tab = false;
  720. $array = array();
  721. foreach ($xml->children() as $key => $value) {
  722. $child = xml2php($value);
  723. //To deal with the attributes
  724. foreach ($value->attributes() as $ak => $av) {
  725. $child[$ak] = (string) $av;
  726. }
  727. //Let see if the new child is not in the array
  728. if ($tab === false && in_array($key, array_keys($array))) {
  729. //If this element is already in the array we will create an indexed array
  730. $tmp = $array[$key];
  731. $array[$key] = null;
  732. $array[$key][] = $tmp;
  733. $array[$key][] = $child;
  734. $tab = true;
  735. } elseif ($tab === true) {
  736. //Add an element in an existing array
  737. $array[$key][] = $child;
  738. } else {
  739. //Add a simple element
  740. $array[$key] = $child;
  741. }
  742. $fils++;
  743. }
  744. if ($fils == 0) {
  745. return (string) $xml;
  746. }
  747. return $array;
  748. }