rssparser.class.php 22 KB

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