dolibarr  20.0.0-beta
rssparser.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2011-2012 Laurent Destailleur <eldy@users.sourceforge.net>
3  * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <https://www.gnu.org/licenses/>.
17  */
18 
25 // @phan-file-suppress PhanPluginPHPDocInWrongComment
26 
30 class RssParser
31 {
35  public $db;
36 
40  public $error = '';
41 
42  public $feed_version;
43 
44  private $_format = '';
45  private $_urlRSS;
46  private $_language;
47  private $_generator;
48  private $_copyright;
49  private $_lastbuilddate;
50  private $_imageurl;
51  private $_link;
52  private $_title;
53  private $_description;
54  private $_lastfetchdate; // Last successful fetch
55  private $_rssarray = array();
56 
57  private $current_namespace;
58  public $items = array();
59  public $current_item = array();
60  public $channel = array();
61  public $textinput = array();
62  public $image = array();
63 
64  private $initem;
65  private $intextinput;
66  private $incontent;
67  private $inimage;
68  private $inchannel;
69 
70  // For parsing with xmlparser
71  public $stack = array(); // parser stack
72  private $_CONTENT_CONSTRUCTS = array('content', 'summary', 'info', 'title', 'tagline', 'copyright');
73 
74 
80  public function __construct($db)
81  {
82  $this->db = $db;
83  }
84 
90  public function getFormat()
91  {
92  return $this->_format;
93  }
94 
100  public function getUrlRss()
101  {
102  return $this->_urlRSS;
103  }
109  public function getLanguage()
110  {
111  return $this->_language;
112  }
118  public function getGenerator()
119  {
120  return $this->_generator;
121  }
127  public function getCopyright()
128  {
129  return $this->_copyright;
130  }
136  public function getLastBuildDate()
137  {
138  return $this->_lastbuilddate;
139  }
145  public function getImageUrl()
146  {
147  return $this->_imageurl;
148  }
154  public function getLink()
155  {
156  return $this->_link;
157  }
163  public function getTitle()
164  {
165  return $this->_title;
166  }
172  public function getDescription()
173  {
174  return $this->_description;
175  }
181  public function getLastFetchDate()
182  {
183  return $this->_lastfetchdate;
184  }
190  public function getItems()
191  {
192  return $this->_rssarray;
193  }
194 
204  public function parser($urlRSS, $maxNb = 0, $cachedelay = 60, $cachedir = '')
205  {
206  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
207  include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
208 
209  $rss = '';
210  $str = ''; // This will contain content of feed
211 
212  // Check parameters
213  if (!dol_is_url($urlRSS)) {
214  $this->error = "ErrorBadUrl";
215  return -1;
216  }
217 
218  $this->_urlRSS = $urlRSS;
219  $newpathofdestfile = $cachedir.'/'.dol_hash($this->_urlRSS, 3); // Force md5 hash (does not contain special chars)
220  $newmask = '0644';
221 
222  //dol_syslog("RssParser::parser parse url=".$urlRSS." => cache file=".$newpathofdestfile);
223  $nowgmt = dol_now();
224 
225  // Search into cache
226  $foundintocache = 0;
227  if ($cachedelay > 0 && $cachedir) {
228  $filedate = dol_filemtime($newpathofdestfile);
229  if ($filedate >= ($nowgmt - $cachedelay)) {
230  //dol_syslog("RssParser::parser cache file ".$newpathofdestfile." is not older than now - cachedelay (".$nowgmt." - ".$cachedelay.") so we use it.");
231  $foundintocache = 1;
232 
233  $this->_lastfetchdate = $filedate;
234  } else {
235  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.");
236  }
237  }
238 
239  // Load file into $str
240  if ($foundintocache) { // Cache file found and is not too old
241  $str = file_get_contents($newpathofdestfile);
242  } else {
243  try {
244  $result = getURLContent($this->_urlRSS, 'GET', '', 1, array(), array('http', 'https'), 0);
245 
246  if (!empty($result['content'])) {
247  $str = $result['content'];
248  } elseif (!empty($result['curl_error_msg'])) {
249  $this->error = 'Error retrieving URL '.$this->_urlRSS.' - '.$result['curl_error_msg'];
250  return -1;
251  }
252  } catch (Exception $e) {
253  $this->error = 'Error retrieving URL '.$this->_urlRSS.' - '.$e->getMessage();
254  return -2;
255  }
256  }
257 
258  if ($str !== false) {
259  // Convert $str into xml
260  if (getDolGlobalString('EXTERNALRSS_USE_SIMPLEXML')) {
261  //print 'xx'.LIBXML_NOCDATA;
262  libxml_use_internal_errors(false);
263  if (LIBXML_VERSION < 20900) {
264  // Avoid load of external entities (security problem).
265  // Required only if LIBXML_VERSION < 20900
266  // @phan-suppress-next-line PhanDeprecatedFunctionInternal
267  libxml_disable_entity_loader(true);
268  }
269 
270  $rss = simplexml_load_string($str, "SimpleXMLElement", LIBXML_NOCDATA);
271  } else {
272  if (!function_exists('xml_parser_create')) {
273  $this->error = 'Function xml_parser_create are not supported by your PHP';
274  return -1;
275  }
276 
277  try {
278  // @phan-suppress-next-line PhanTypeMismatchArgumentInternalProbablyReal
279  $xmlparser = xml_parser_create(null);
280 
281  xml_parser_set_option($xmlparser, XML_OPTION_CASE_FOLDING, 0);
282  xml_parser_set_option($xmlparser, XML_OPTION_SKIP_WHITE, 1);
283  xml_parser_set_option($xmlparser, XML_OPTION_TARGET_ENCODING, "UTF-8");
284  //xml_set_external_entity_ref_handler($xmlparser, 'extEntHandler'); // Seems to have no effect even when function extEntHandler exists.
285 
286  if (!is_resource($xmlparser) && !is_object($xmlparser)) {
287  $this->error = "ErrorFailedToCreateParser";
288  return -1;
289  }
290 
291  xml_set_object($xmlparser, $this);
292  // @phan-suppress-next-line PhanUndeclaredFunctionInCallable
293  xml_set_element_handler($xmlparser, 'feed_start_element', 'feed_end_element');
294  // @phan-suppress-next-line PhanUndeclaredFunctionInCallable
295  xml_set_character_data_handler($xmlparser, 'feed_cdata');
296 
297  $status = xml_parse($xmlparser, $str, false);
298 
299  xml_parser_free($xmlparser);
300 
301  $rss = $this;
302  //var_dump($status.' '.$rss->_format);exit;
303  } catch (Exception $e) {
304  $rss = null;
305  }
306  }
307  }
308 
309  // If $rss loaded
310  if ($rss) {
311  // Save file into cache
312  if (empty($foundintocache) && $cachedir) {
313  dol_syslog(get_class($this)."::parser cache file ".$newpathofdestfile." is saved onto disk.");
314  if (!dol_is_dir($cachedir)) {
315  dol_mkdir($cachedir);
316  }
317  $fp = fopen($newpathofdestfile, 'w');
318  if ($fp) {
319  fwrite($fp, $str);
320  fclose($fp);
321  dolChmod($newpathofdestfile);
322 
323  $this->_lastfetchdate = $nowgmt;
324  } else {
325  print 'Error, failed to open file '.$newpathofdestfile.' for write';
326  }
327  }
328 
329  unset($str); // Free memory
330 
331  if (empty($rss->_format)) { // If format not detected automatically
332  $rss->_format = 'rss';
333  if (empty($rss->channel)) {
334  $rss->_format = 'atom';
335  }
336  }
337 
338  $items = array();
339 
340  // Save description entries
341  if ($rss->_format == 'rss') {
342  //var_dump($rss);
343  if (getDolGlobalString('EXTERNALRSS_USE_SIMPLEXML')) {
344  if (!empty($rss->channel->language)) {
345  $this->_language = sanitizeVal((string) $rss->channel->language);
346  }
347  if (!empty($rss->channel->generator)) {
348  $this->_generator = sanitizeVal((string) $rss->channel->generator);
349  }
350  if (!empty($rss->channel->copyright)) {
351  $this->_copyright = sanitizeVal((string) $rss->channel->copyright);
352  }
353  if (!empty($rss->channel->lastbuilddate)) {
354  $this->_lastbuilddate = sanitizeVal((string) $rss->channel->lastbuilddate);
355  }
356  if (!empty($rss->channel->image->url[0])) {
357  $this->_imageurl = sanitizeVal((string) $rss->channel->image->url[0]);
358  }
359  if (!empty($rss->channel->link)) {
360  $this->_link = sanitizeVal((string) $rss->channel->link);
361  }
362  if (!empty($rss->channel->title)) {
363  $this->_title = sanitizeVal((string) $rss->channel->title);
364  }
365  if (!empty($rss->channel->description)) {
366  $this->_description = sanitizeVal((string) $rss->channel->description);
367  }
368  } else {
369  //var_dump($rss->channel);
370  if (!empty($rss->channel['language'])) {
371  $this->_language = sanitizeVal((string) $rss->channel['language']);
372  }
373  if (!empty($rss->channel['generator'])) {
374  $this->_generator = sanitizeVal((string) $rss->channel['generator']);
375  }
376  if (!empty($rss->channel['copyright'])) {
377  $this->_copyright = sanitizeVal((string) $rss->channel['copyright']);
378  }
379  if (!empty($rss->channel['lastbuilddate'])) {
380  $this->_lastbuilddate = sanitizeVal((string) $rss->channel['lastbuilddate']);
381  }
382  if (!empty($rss->image['url'])) {
383  $this->_imageurl = sanitizeVal((string) $rss->image['url']);
384  }
385  if (!empty($rss->channel['link'])) {
386  $this->_link = sanitizeVal((string) $rss->channel['link']);
387  }
388  if (!empty($rss->channel['title'])) {
389  $this->_title = sanitizeVal((string) $rss->channel['title']);
390  }
391  if (!empty($rss->channel['description'])) {
392  $this->_description = sanitizeVal((string) $rss->channel['description']);
393  }
394  }
395 
396  if (getDolGlobalString('EXTERNALRSS_USE_SIMPLEXML')) {
397  $items = $rss->channel->item; // With simplexml
398  } else {
399  $items = $rss->items; // With xmlparse
400  }
401  //var_dump($items);exit;
402  } elseif ($rss->_format == 'atom') {
403  //var_dump($rss);
404  if (getDolGlobalString('EXTERNALRSS_USE_SIMPLEXML')) {
405  if (!empty($rss->generator)) {
406  $this->_generator = sanitizeVal((string) $rss->generator);
407  }
408  if (!empty($rss->lastbuilddate)) {
409  $this->_lastbuilddate = sanitizeVal((string) $rss->modified);
410  }
411  if (!empty($rss->link->href)) {
412  $this->_link = sanitizeVal((string) $rss->link->href);
413  }
414  if (!empty($rss->title)) {
415  $this->_title = sanitizeVal((string) $rss->title);
416  }
417  if (!empty($rss->description)) {
418  $this->_description = sanitizeVal((string) $rss->description);
419  }
420  } else {
421  //if (!empty($rss->channel['rss_language'])) $this->_language = (string) $rss->channel['rss_language'];
422  if (!empty($rss->channel['generator'])) {
423  $this->_generator = sanitizeVal((string) $rss->channel['generator']);
424  }
425  //if (!empty($rss->channel['rss_copyright'])) $this->_copyright = (string) $rss->channel['rss_copyright'];
426  if (!empty($rss->channel['modified'])) {
427  $this->_lastbuilddate = sanitizeVal((string) $rss->channel['modified']);
428  }
429  //if (!empty($rss->image['rss_url'])) $this->_imageurl = (string) $rss->image['rss_url'];
430  if (!empty($rss->channel['link'])) {
431  $this->_link = sanitizeVal((string) $rss->channel['link']);
432  }
433  if (!empty($rss->channel['title'])) {
434  $this->_title = sanitizeVal((string) $rss->channel['title']);
435  }
436  //if (!empty($rss->channel['rss_description'])) $this->_description = (string) $rss->channel['rss_description'];
437 
438  if (!empty($rss->channel)) {
439  $this->_imageurl = sanitizeVal($this->getAtomImageUrl($rss->channel));
440  }
441  }
442  if (getDolGlobalString('EXTERNALRSS_USE_SIMPLEXML')) {
443  $tmprss = xml2php($rss);
444  $items = $tmprss['entry'];
445  } else {
446  // With simplexml
447  $items = $rss->items; // With xmlparse
448  }
449  //var_dump($items);exit;
450  }
451 
452  $i = 0;
453 
454  // Loop on each record
455  if (is_array($items)) {
456  foreach ($items as $item) {
457  //var_dump($item);exit;
458  if ($rss->_format == 'rss') {
459  if (getDolGlobalString('EXTERNALRSS_USE_SIMPLEXML')) {
460  $itemLink = sanitizeVal((string) $item->link);
461  $itemTitle = sanitizeVal((string) $item->title);
462  $itemDescription = sanitizeVal((string) $item->description);
463  $itemPubDate = sanitizeVal((string) $item->pubDate);
464  $itemId = '';
465  $itemAuthor = '';
466  } else {
467  $itemLink = sanitizeVal((string) $item['link']);
468  $itemTitle = sanitizeVal((string) $item['title']);
469  $itemDescription = sanitizeVal((string) $item['description']);
470  $itemPubDate = sanitizeVal((string) $item['pubdate']);
471  $itemId = sanitizeVal((string) $item['guid']);
472  $itemAuthor = sanitizeVal((string) ($item['author'] ?? ''));
473  }
474 
475  // Loop on each category
476  $itemCategory = array();
477  if (!empty($item->category) && is_array($item->category)) {
478  foreach ($item->category as $cat) {
479  $itemCategory[] = (string) $cat;
480  }
481  }
482  } elseif ($rss->_format == 'atom') {
483  $itemLink = (isset($item['link']) ? sanitizeVal((string) $item['link']) : '');
484  $itemTitle = sanitizeVal((string) $item['title']);
485  $itemDescription = sanitizeVal($this->getAtomItemDescription($item));
486  $itemPubDate = sanitizeVal((string) $item['created']);
487  $itemId = sanitizeVal((string) $item['id']);
488  $itemAuthor = sanitizeVal((string) ($item['author'] ? $item['author'] : $item['author_name']));
489  $itemCategory = array();
490  } else {
491  $itemLink = '';
492  $itemTitle = '';
493  $itemDescription = '';
494  $itemPubDate = '';
495  $itemId = '';
496  $itemAuthor = '';
497  $itemCategory = array();
498  print 'ErrorBadFeedFormat';
499  }
500 
501  // Add record to result array
502  $this->_rssarray[$i] = array(
503  'link' => $itemLink,
504  'title' => $itemTitle,
505  'description' => $itemDescription,
506  'pubDate' => $itemPubDate,
507  'category' => $itemCategory,
508  'id' => $itemId,
509  'author' => $itemAuthor
510  );
511  //var_dump($this->_rssarray);
512 
513  $i++;
514 
515  if ($i > $maxNb) {
516  break; // We get all records we want
517  }
518  }
519  }
520 
521  return 1;
522  } else {
523  $this->error = 'ErrorFailedToLoadRSSFile';
524  return -1;
525  }
526  }
527 
528 
529 
530  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
539  public function feed_start_element($p, $element, $attrs)
540  {
541  // phpcs:enable
542  $el = $element = strtolower($element);
543  $attrs = array_change_key_case($attrs, CASE_LOWER);
544 
545  // check for a namespace, and split if found
546  $ns = false;
547  if (strpos($element, ':')) {
548  list($ns, $el) = explode(':', $element, 2);
549  }
550  if ($ns and $ns != 'rdf') {
551  $this->current_namespace = $ns;
552  }
553 
554  // if feed type isn't set, then this is first element of feed identify feed from root element
555  if (empty($this->_format)) {
556  if ($el == 'rdf') {
557  $this->_format = 'rss';
558  $this->feed_version = '1.0';
559  } elseif ($el == 'rss') {
560  $this->_format = 'rss';
561  $this->feed_version = $attrs['version'];
562  } elseif ($el == 'feed') {
563  $this->_format = 'atom';
564  $this->feed_version = $attrs['version'];
565  $this->inchannel = true;
566  }
567  return;
568  }
569 
570  if ($el == 'channel') {
571  $this->inchannel = true;
572  } elseif ($el == 'item' || $el == 'entry') {
573  $this->initem = true;
574  if (isset($attrs['rdf:about'])) {
575  $this->current_item['about'] = $attrs['rdf:about'];
576  }
577  } elseif ($this->_format == 'rss' && $this->current_namespace == '' && $el == 'textinput') {
578  // if we're in the default namespace of an RSS feed,
579  // record textinput or image fields
580  $this->intextinput = true;
581  } elseif ($this->_format == 'rss' && $this->current_namespace == '' && $el == 'image') {
582  $this->inimage = true;
583  } elseif ($this->_format == 'atom' && in_array($el, $this->_CONTENT_CONSTRUCTS)) {
584  // handle atom content constructs
585  // avoid clashing w/ RSS mod_content
586  if ($el == 'content') {
587  $el = 'atom_content';
588  }
589 
590  $this->incontent = $el;
591  } elseif ($this->_format == 'atom' && $this->incontent) {
592  // if inside an Atom content construct (e.g. content or summary) field treat tags as text
593  // if tags are inlined, then flatten
594  $attrs_str = implode(' ', array_map('rss_map_attrs', array_keys($attrs), array_values($attrs)));
595 
596  $this->append_content("<$element $attrs_str>");
597 
598  array_unshift($this->stack, $el);
599  } elseif ($this->_format == 'atom' && $el == 'link') {
600  // Atom support many links per containing element.
601  // Magpie treats link elements of type rel='alternate'
602  // as being equivalent to RSS's simple link element.
603  if (isset($attrs['rel']) && $attrs['rel'] == 'alternate') {
604  $link_el = 'link';
605  } elseif (!isset($attrs['rel'])) {
606  $link_el = 'link';
607  } else {
608  $link_el = 'link_'.$attrs['rel'];
609  }
610 
611  $this->append($link_el, $attrs['href']);
612  } else {
613  // set stack[0] to current element
614  array_unshift($this->stack, $el);
615  }
616  }
617 
618 
619  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
627  public function feed_cdata($p, $text)
628  {
629  // phpcs:enable
630  if ($this->_format == 'atom' and $this->incontent) {
631  $this->append_content($text);
632  } else {
633  $current_el = implode('_', array_reverse($this->stack));
634  $this->append($current_el, $text);
635  }
636  }
637 
638  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
646  public function feed_end_element($p, $el)
647  {
648  // phpcs:enable
649  $el = strtolower($el);
650 
651  if ($el == 'item' or $el == 'entry') {
652  $this->items[] = $this->current_item;
653  $this->current_item = array();
654  $this->initem = false;
655  } elseif ($this->_format == 'rss' and $this->current_namespace == '' and $el == 'textinput') {
656  $this->intextinput = false;
657  } elseif ($this->_format == 'rss' and $this->current_namespace == '' and $el == 'image') {
658  $this->inimage = false;
659  } elseif ($this->_format == 'atom' and in_array($el, $this->_CONTENT_CONSTRUCTS)) {
660  $this->incontent = false;
661  } elseif ($el == 'channel' or $el == 'feed') {
662  $this->inchannel = false;
663  } elseif ($this->_format == 'atom' and $this->incontent) {
664  // balance tags properly
665  // note: i don't think this is actually necessary
666  if ($this->stack[0] == $el) {
667  $this->append_content("</$el>");
668  } else {
669  $this->append_content("<$el />");
670  }
671 
672  array_shift($this->stack);
673  } else {
674  array_shift($this->stack);
675  }
676 
677  $this->current_namespace = false;
678  }
679 
680 
688  public function concat(&$str1, $str2 = "")
689  {
690  if (!isset($str1)) {
691  $str1 = "";
692  }
693  $str1 .= $str2;
694  return $str1;
695  }
696 
697  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
704  public function append_content($text)
705  {
706  // phpcs:enable
707  if (!empty($this->initem)) {
708  $this->concat($this->current_item[$this->incontent], $text);
709  } elseif (!empty($this->inchannel)) {
710  $this->concat($this->channel[$this->incontent], $text);
711  }
712  }
713 
721  public function append($el, $text)
722  {
723  if (!$el) {
724  return;
725  }
726  if (!empty($this->current_namespace)) {
727  if (!empty($this->initem)) {
728  $this->concat($this->current_item[$this->current_namespace][$el], $text);
729  } elseif (!empty($this->inchannel)) {
730  $this->concat($this->channel[$this->current_namespace][$el], $text);
731  } elseif (!empty($this->intextinput)) {
732  $this->concat($this->textinput[$this->current_namespace][$el], $text);
733  } elseif (!empty($this->inimage)) {
734  $this->concat($this->image[$this->current_namespace][$el], $text);
735  }
736  } else {
737  if (!empty($this->initem)) {
738  $this->concat($this->current_item[$el], $text);
739  } elseif (!empty($this->intextinput)) {
740  $this->concat($this->textinput[$el], $text);
741  } elseif (!empty($this->inimage)) {
742  $this->concat($this->image[$el], $text);
743  } elseif (!empty($this->inchannel)) {
744  $this->concat($this->channel[$el], $text);
745  }
746  }
747  }
748 
756  private function getAtomItemDescription(array $item, $maxlength = 500)
757  {
758  $result = "";
759 
760  if (isset($item['summary'])) {
761  $result = $item['summary'];
762  } elseif (isset($item['atom_content'])) {
763  $result = $item['atom_content'];
764  }
765 
766  // remove all HTML elements that can possible break the maximum size of a tooltip,
767  // like headings, image, video etc. and allow only simple style elements
768  $result = strip_tags($result, "<br><p><ul><ol><li>");
769 
770  $result = str_replace("\n", "", $result);
771 
772  if (strlen($result) > $maxlength) {
773  $result = substr($result, 0, $maxlength);
774  $result .= "...";
775  }
776 
777  return $result;
778  }
779 
786  private function getAtomImageUrl(array $feed)
787  {
788  if (isset($feed['icon'])) {
789  return $feed['logo'];
790  }
791 
792  if (isset($feed['icon'])) {
793  return $feed['logo'];
794  }
795 
796  if (isset($feed['webfeeds:logo'])) {
797  return $feed['webfeeds:logo'];
798  }
799 
800  if (isset($feed['webfeeds:icon'])) {
801  return $feed['webfeeds:icon'];
802  }
803 
804  if (isset($feed['webfeeds:wordmark'])) {
805  return $feed['webfeeds:wordmark'];
806  }
807 
808  return "";
809  }
810 }
811 
812 /*
813  * A method for the xml_set_external_entity_ref_handler()
814  *
815  * @param XMLParser $parser
816  * @param string $ent
817  * @param string|false $base
818  * @param string $sysID
819  * @param string|false $pubID
820  * @return bool
821 function extEntHandler($parser, $ent, $base, $sysID, $pubID) {
822  print 'extEntHandler ran';
823  return true;
824 }
825 */
826 
834 function rss_map_attrs($k, $v)
835 {
836  return "$k=\"$v\"";
837 }
838 
845 function xml2php($xml)
846 {
847  $threads = 0;
848  $tab = false;
849  $array = array();
850  foreach ($xml->children() as $key => $value) {
851  $child = xml2php($value);
852 
853  //To deal with the attributes
854  foreach ($value->attributes() as $ak => $av) {
855  $child[$ak] = (string) $av;
856  }
857 
858  //Let see if the new child is not in the array
859  if ($tab === false && in_array($key, array_keys($array))) {
860  //If this element is already in the array we will create an indexed array
861  $tmp = $array[$key];
862  $array[$key] = null;
863  $array[$key][] = $tmp;
864  $array[$key][] = $child;
865  $tab = true;
866  } elseif ($tab === true) {
867  //Add an element in an existing array
868  $array[$key][] = $child;
869  } else {
870  //Add a simple element
871  $array[$key] = $child;
872  }
873 
874  $threads++;
875  }
876 
877 
878  if ($threads == 0) {
879  return (string) $xml;
880  }
881 
882  return $array;
883 }
Class to parse RSS files.
feed_start_element($p, $element, $attrs)
Triggered when opened tag is found.
getAtomItemDescription(array $item, $maxlength=500)
Return a description/summary for one item from a ATOM feed.
concat(&$str1, $str2="")
To concat 2 strings with no warning if an operand is not defined.
getImageUrl()
getImageUrl
feed_end_element($p, $el)
Triggered when closed tag is found.
__construct($db)
Constructor.
getLanguage()
getLanguage
getTitle()
getTitle
getLastBuildDate()
getLastBuildDate
getItems()
getItems
getLink()
getLink
getGenerator()
getGenerator
getCopyright()
getCopyright
feed_cdata($p, $text)
Triggered when CDATA is found.
getLastFetchDate()
getLastFetchDate
getUrlRss()
getUrlRss
parser($urlRSS, $maxNb=0, $cachedelay=60, $cachedir='')
Parse rss URL.
append_content($text)
Enter description here ...
append($el, $text)
smart append - field and namespace aware
getDescription()
getDescription
getFormat()
getFormat
getAtomImageUrl(array $feed)
Return a URL to a image of the given ATOM feed.
dol_is_url($uri)
Return if path is an URI (the name of the method is misleading).
Definition: files.lib.php:557
dol_filemtime($pathoffile)
Return time of a file.
Definition: files.lib.php:647
dol_is_dir($folder)
Test if filename is a directory.
Definition: files.lib.php:489
dolChmod($filepath, $newmask='')
Change mod of a file.
dol_now($mode='auto')
Return date for now.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
sanitizeVal($out='', $check='alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
getURLContent($url, $postorget='GET', $param='', $followlocation=1, $addheaders=array(), $allowedschemes=array('http', 'https'), $localurl=0, $ssl_verifypeer=-1)
Function to get a content from an URL (use proxy if proxy defined).
Definition: geturl.lib.php:42
xml2php($xml)
Function to convert an XML object into an array.
rss_map_attrs($k, $v)
Function to convert an XML object into an array.
dol_hash($chain, $type='0', $nosalt=0)
Returns a hash (non reversible encryption) of a string.