Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 402] [Versions 401 and 403]

   1  <?php
   2  /**
   3   * SimplePie
   4   *
   5   * A PHP-Based RSS and Atom Feed Framework.
   6   * Takes the hard work out of managing a complete RSS/Atom solution.
   7   *
   8   * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
   9   * All rights reserved.
  10   *
  11   * Redistribution and use in source and binary forms, with or without modification, are
  12   * permitted provided that the following conditions are met:
  13   *
  14   * 	 * Redistributions of source code must retain the above copyright notice, this list of
  15   * 	   conditions and the following disclaimer.
  16   *
  17   * 	 * Redistributions in binary form must reproduce the above copyright notice, this list
  18   * 	   of conditions and the following disclaimer in the documentation and/or other materials
  19   * 	   provided with the distribution.
  20   *
  21   * 	 * Neither the name of the SimplePie Team nor the names of its contributors may be used
  22   * 	   to endorse or promote products derived from this software without specific prior
  23   * 	   written permission.
  24   *
  25   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
  26   * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  27   * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
  28   * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  29   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  30   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  31   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  32   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33   * POSSIBILITY OF SUCH DAMAGE.
  34   *
  35   * @package SimplePie
  36   * @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
  37   * @author Ryan Parman
  38   * @author Sam Sneddon
  39   * @author Ryan McCue
  40   * @link http://simplepie.org/ SimplePie
  41   * @license http://www.opensource.org/licenses/bsd-license.php BSD License
  42   */
  43  
  44  /**
  45   * Used for feed auto-discovery
  46   *
  47   *
  48   * This class can be overloaded with {@see SimplePie::set_locator_class()}
  49   *
  50   * @package SimplePie
  51   */
  52  class SimplePie_Locator
  53  {
  54  	 var $useragent;
  55  	 var $timeout;
  56  	 var $file;
  57  	 var $local = array();
  58  	 var $elsewhere = array();
  59  	 var $cached_entities = array();
  60  	 var $http_base;
  61  	 var $base;
  62  	 var $base_location = 0;
  63  	 var $checked_feeds = 0;
  64  	 var $max_checked_feeds = 10;
  65  	 var $force_fsockopen = false;
  66  	 var $curl_options = array();
  67  	 var $dom;
  68  	 protected $registry;
  69  
  70  	public function __construct(SimplePie_File $file, $timeout = 10, $useragent = null, $max_checked_feeds = 10, $force_fsockopen = false, $curl_options = array())
  71  	 {
  72  	 	 $this->file = $file;
  73  	 	 $this->useragent = $useragent;
  74  	 	 $this->timeout = $timeout;
  75  	 	 $this->max_checked_feeds = $max_checked_feeds;
  76  	 	 $this->force_fsockopen = $force_fsockopen;
  77  	 	 $this->curl_options = $curl_options;
  78  
  79  	 	 if (class_exists('DOMDocument') && $this->file->body != '')
  80  	 	 {
  81  	 	 	 $this->dom = new DOMDocument();
  82  
  83  	 	 	 set_error_handler(array('SimplePie_Misc', 'silence_errors'));
  84  	 	 	 try
  85  	 	 	 {
  86  	 	 	 	 $this->dom->loadHTML($this->file->body);
  87  	 	 	 }
  88  	 	 	 catch (Throwable $ex)
  89  	 	 	 {
  90  	 	 	 	 $this->dom = null;
  91  	 	 	 }
  92  	 	 	 restore_error_handler();
  93  	 	 }
  94  	 	 else
  95  	 	 {
  96  	 	 	 $this->dom = null;
  97  	 	 }
  98  	 }
  99  
 100  	public function set_registry(SimplePie_Registry $registry)
 101  	 {
 102  	 	 $this->registry = $registry;
 103  	 }
 104  
 105  	public function find($type = SIMPLEPIE_LOCATOR_ALL, &$working = null)
 106  	 {
 107  	 	 if ($this->is_feed($this->file))
 108  	 	 {
 109  	 	 	 return $this->file;
 110  	 	 }
 111  
 112  	 	 if ($this->file->method & SIMPLEPIE_FILE_SOURCE_REMOTE)
 113  	 	 {
 114  	 	 	 $sniffer = $this->registry->create('Content_Type_Sniffer', array($this->file));
 115  	 	 	 if ($sniffer->get_type() !== 'text/html')
 116  	 	 	 {
 117  	 	 	 	 return null;
 118  	 	 	 }
 119  	 	 }
 120  
 121  	 	 if ($type & ~SIMPLEPIE_LOCATOR_NONE)
 122  	 	 {
 123  	 	 	 $this->get_base();
 124  	 	 }
 125  
 126  	 	 if ($type & SIMPLEPIE_LOCATOR_AUTODISCOVERY && $working = $this->autodiscovery())
 127  	 	 {
 128  	 	 	 return $working[0];
 129  	 	 }
 130  
 131  	 	 if ($type & (SIMPLEPIE_LOCATOR_LOCAL_EXTENSION | SIMPLEPIE_LOCATOR_LOCAL_BODY | SIMPLEPIE_LOCATOR_REMOTE_EXTENSION | SIMPLEPIE_LOCATOR_REMOTE_BODY) && $this->get_links())
 132  	 	 {
 133  	 	 	 if ($type & SIMPLEPIE_LOCATOR_LOCAL_EXTENSION && $working = $this->extension($this->local))
 134  	 	 	 {
 135  	 	 	 	 return $working[0];
 136  	 	 	 }
 137  
 138  	 	 	 if ($type & SIMPLEPIE_LOCATOR_LOCAL_BODY && $working = $this->body($this->local))
 139  	 	 	 {
 140  	 	 	 	 return $working[0];
 141  	 	 	 }
 142  
 143  	 	 	 if ($type & SIMPLEPIE_LOCATOR_REMOTE_EXTENSION && $working = $this->extension($this->elsewhere))
 144  	 	 	 {
 145  	 	 	 	 return $working[0];
 146  	 	 	 }
 147  
 148  	 	 	 if ($type & SIMPLEPIE_LOCATOR_REMOTE_BODY && $working = $this->body($this->elsewhere))
 149  	 	 	 {
 150  	 	 	 	 return $working[0];
 151  	 	 	 }
 152  	 	 }
 153  	 	 return null;
 154  	 }
 155  
 156  	public function is_feed($file, $check_html = false)
 157  	 {
 158  	 	 if ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE)
 159  	 	 {
 160  	 	 	 $sniffer = $this->registry->create('Content_Type_Sniffer', array($file));
 161  	 	 	 $sniffed = $sniffer->get_type();
 162  	 	 	 $mime_types = array('application/rss+xml', 'application/rdf+xml',
 163  	 	 	                     'text/rdf', 'application/atom+xml', 'text/xml',
 164  	 	 	                     'application/xml', 'application/x-rss+xml');
 165  	 	 	 if ($check_html)
 166  	 	 	 {
 167  	 	 	 	 $mime_types[] = 'text/html';
 168  	 	 	 }
 169  
 170  	 	 	 return in_array($sniffed, $mime_types);
 171  	 	 }
 172  	 	 elseif ($file->method & SIMPLEPIE_FILE_SOURCE_LOCAL)
 173  	 	 {
 174  	 	 	 return true;
 175  	 	 }
 176  	 	 else
 177  	 	 {
 178  	 	 	 return false;
 179  	 	 }
 180  	 }
 181  
 182  	public function get_base()
 183  	 {
 184  	 	 if ($this->dom === null)
 185  	 	 {
 186  	 	 	 throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
 187  	 	 }
 188  	 	 $this->http_base = $this->file->url;
 189  	 	 $this->base = $this->http_base;
 190  	 	 $elements = $this->dom->getElementsByTagName('base');
 191  	 	 foreach ($elements as $element)
 192  	 	 {
 193  	 	 	 if ($element->hasAttribute('href'))
 194  	 	 	 {
 195  	 	 	 	 $base = $this->registry->call('Misc', 'absolutize_url', array(trim($element->getAttribute('href')), $this->http_base));
 196  	 	 	 	 if ($base === false)
 197  	 	 	 	 {
 198  	 	 	 	 	 continue;
 199  	 	 	 	 }
 200  	 	 	 	 $this->base = $base;
 201  	 	 	 	 $this->base_location = method_exists($element, 'getLineNo') ? $element->getLineNo() : 0;
 202  	 	 	 	 break;
 203  	 	 	 }
 204  	 	 }
 205  	 }
 206  
 207  	public function autodiscovery()
 208  	 {
 209  	 	 $done = array();
 210  	 	 $feeds = array();
 211  	 	 $feeds = array_merge($feeds, $this->search_elements_by_tag('link', $done, $feeds));
 212  	 	 $feeds = array_merge($feeds, $this->search_elements_by_tag('a', $done, $feeds));
 213  	 	 $feeds = array_merge($feeds, $this->search_elements_by_tag('area', $done, $feeds));
 214  
 215  	 	 if (!empty($feeds))
 216  	 	 {
 217  	 	 	 return array_values($feeds);
 218  	 	 }
 219  
 220  	 	 return null;
 221  	 }
 222  
 223  	protected function search_elements_by_tag($name, &$done, $feeds)
 224  	 {
 225  	 	 if ($this->dom === null)
 226  	 	 {
 227  	 	 	 throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
 228  	 	 }
 229  
 230  	 	 $links = $this->dom->getElementsByTagName($name);
 231  	 	 foreach ($links as $link)
 232  	 	 {
 233  	 	 	 if ($this->checked_feeds === $this->max_checked_feeds)
 234  	 	 	 {
 235  	 	 	 	 break;
 236  	 	 	 }
 237  	 	 	 if ($link->hasAttribute('href') && $link->hasAttribute('rel'))
 238  	 	 	 {
 239  	 	 	 	 $rel = array_unique($this->registry->call('Misc', 'space_separated_tokens', array(strtolower($link->getAttribute('rel')))));
 240  	 	 	 	 $line = method_exists($link, 'getLineNo') ? $link->getLineNo() : 1;
 241  
 242  	 	 	 	 if ($this->base_location < $line)
 243  	 	 	 	 {
 244  	 	 	 	 	 $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->base));
 245  	 	 	 	 }
 246  	 	 	 	 else
 247  	 	 	 	 {
 248  	 	 	 	 	 $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->http_base));
 249  	 	 	 	 }
 250  	 	 	 	 if ($href === false)
 251  	 	 	 	 {
 252  	 	 	 	 	 continue;
 253  	 	 	 	 }
 254  
 255  	 	 	 	 if (!in_array($href, $done) && in_array('feed', $rel) || (in_array('alternate', $rel) && !in_array('stylesheet', $rel) && $link->hasAttribute('type') && in_array(strtolower($this->registry->call('Misc', 'parse_mime', array($link->getAttribute('type')))), array('text/html', 'application/rss+xml', 'application/atom+xml'))) && !isset($feeds[$href]))
 256  	 	 	 	 {
 257  	 	 	 	 	 $this->checked_feeds++;
 258  	 	 	 	 	 $headers = array(
 259  	 	 	 	 	 	 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
 260  	 	 	 	 	 );
 261  	 	 	 	 	 $feed = $this->registry->create('File', array($href, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options));
 262  	 	 	 	 	 if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed, true))
 263  	 	 	 	 	 {
 264  	 	 	 	 	 	 $feeds[$href] = $feed;
 265  	 	 	 	 	 }
 266  	 	 	 	 }
 267  	 	 	 	 $done[] = $href;
 268  	 	 	 }
 269  	 	 }
 270  
 271  	 	 return $feeds;
 272  	 }
 273  
 274  	 public function get_links()
 275  	 {
 276  	 	 if ($this->dom === null)
 277  	 	 {
 278  	 	 	 throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
 279  	 	 }
 280  
 281  	 	 $links = $this->dom->getElementsByTagName('a');
 282  	 	 foreach ($links as $link)
 283  	 	 {
 284  	 	 	 if ($link->hasAttribute('href'))
 285  	 	 	 {
 286  	 	 	 	 $href = trim($link->getAttribute('href'));
 287  	 	 	 	 $parsed = $this->registry->call('Misc', 'parse_url', array($href));
 288  	 	 	 	 if ($parsed['scheme'] === '' || preg_match('/^(https?|feed)?$/i', $parsed['scheme']))
 289  	 	 	 	 {
 290  	 	 	 	 	 if (method_exists($link, 'getLineNo') && $this->base_location < $link->getLineNo())
 291  	 	 	 	 	 {
 292  	 	 	 	 	 	 $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->base));
 293  	 	 	 	 	 }
 294  	 	 	 	 	 else
 295  	 	 	 	 	 {
 296  	 	 	 	 	 	 $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->http_base));
 297  	 	 	 	 	 }
 298  	 	 	 	 	 if ($href === false)
 299  	 	 	 	 	 {
 300  	 	 	 	 	 	 continue;
 301  	 	 	 	 	 }
 302  
 303  	 	 	 	 	 $current = $this->registry->call('Misc', 'parse_url', array($this->file->url));
 304  
 305  	 	 	 	 	 if ($parsed['authority'] === '' || $parsed['authority'] === $current['authority'])
 306  	 	 	 	 	 {
 307  	 	 	 	 	 	 $this->local[] = $href;
 308  	 	 	 	 	 }
 309  	 	 	 	 	 else
 310  	 	 	 	 	 {
 311  	 	 	 	 	 	 $this->elsewhere[] = $href;
 312  	 	 	 	 	 }
 313  	 	 	 	 }
 314  	 	 	 }
 315  	 	 }
 316  	 	 $this->local = array_unique($this->local);
 317  	 	 $this->elsewhere = array_unique($this->elsewhere);
 318  	 	 if (!empty($this->local) || !empty($this->elsewhere))
 319  	 	 {
 320  	 	 	 return true;
 321  	 	 }
 322  	 	 return null;
 323  	 }
 324  
 325  	 public function get_rel_link($rel)
 326  	 {
 327  	 	 if ($this->dom === null)
 328  	 	 {
 329  	 	 	 throw new SimplePie_Exception('DOMDocument not found, unable to use '.
 330  	 	 	                               'locator');
 331  	 	 }
 332  	 	 if (!class_exists('DOMXpath'))
 333  	 	 {
 334  	 	 	 throw new SimplePie_Exception('DOMXpath not found, unable to use '.
 335  	 	 	                               'get_rel_link');
 336  	 	 }
 337  
 338  	 	 $xpath = new DOMXpath($this->dom);
 339  	 	 $query = '//a[@rel and @href] | //link[@rel and @href]';
 340  	 	 foreach ($xpath->query($query) as $link)
 341  	 	 {
 342  	 	 	 $href = trim($link->getAttribute('href'));
 343  	 	 	 $parsed = $this->registry->call('Misc', 'parse_url', array($href));
 344  	 	 	 if ($parsed['scheme'] === '' ||
 345  	 	 	     preg_match('/^https?$/i', $parsed['scheme']))
 346  	 	 	 {
 347  	 	 	 	 if (method_exists($link, 'getLineNo') &&
 348  	 	 	 	     $this->base_location < $link->getLineNo())
 349  	 	 	 	 {
 350  	 	 	 	 	 $href =
 351  	 	 	 	 	 	 $this->registry->call('Misc', 'absolutize_url',
 352  	 	 	 	 	 	                       array(trim($link->getAttribute('href')),
 353  	 	 	 	 	 	                             $this->base));
 354  	 	 	 	 }
 355  	 	 	 	 else
 356  	 	 	 	 {
 357  	 	 	 	 	 $href =
 358  	 	 	 	 	 	 $this->registry->call('Misc', 'absolutize_url',
 359  	 	 	 	 	 	                       array(trim($link->getAttribute('href')),
 360  	 	 	 	 	 	                             $this->http_base));
 361  	 	 	 	 }
 362  	 	 	 	 if ($href === false)
 363  	 	 	 	 {
 364  	 	 	 	 	 return null;
 365  	 	 	 	 }
 366  	 	 	 	 $rel_values = explode(' ', strtolower($link->getAttribute('rel')));
 367  	 	 	 	 if (in_array($rel, $rel_values))
 368  	 	 	 	 {
 369  	 	 	 	 	 return $href;
 370  	 	 	 	 }
 371  	 	 	 }
 372  	 	 }
 373  	 	 return null;
 374  	 }
 375  
 376  	 public function extension(&$array)
 377  	 {
 378  	 	 foreach ($array as $key => $value)
 379  	 	 {
 380  	 	 	 if ($this->checked_feeds === $this->max_checked_feeds)
 381  	 	 	 {
 382  	 	 	 	 break;
 383  	 	 	 }
 384  	 	 	 if (in_array(strtolower(strrchr($value, '.')), array('.rss', '.rdf', '.atom', '.xml')))
 385  	 	 	 {
 386  	 	 	 	 $this->checked_feeds++;
 387  
 388  	 	 	 	 $headers = array(
 389  	 	 	 	 	 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
 390  	 	 	 	 );
 391  	 	 	 	 $feed = $this->registry->create('File', array($value, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options));
 392  	 	 	 	 if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed))
 393  	 	 	 	 {
 394  	 	 	 	 	 return array($feed);
 395  	 	 	 	 }
 396  	 	 	 	 else
 397  	 	 	 	 {
 398  	 	 	 	 	 unset($array[$key]);
 399  	 	 	 	 }
 400  	 	 	 }
 401  	 	 }
 402  	 	 return null;
 403  	 }
 404  
 405  	public function body(&$array)
 406  	 {
 407  	 	 foreach ($array as $key => $value)
 408  	 	 {
 409  	 	 	 if ($this->checked_feeds === $this->max_checked_feeds)
 410  	 	 	 {
 411  	 	 	 	 break;
 412  	 	 	 }
 413  	 	 	 if (preg_match('/(feed|rss|rdf|atom|xml)/i', $value))
 414  	 	 	 {
 415  	 	 	 	 $this->checked_feeds++;
 416  	 	 	 	 $headers = array(
 417  	 	 	 	 	 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
 418  	 	 	 	 );
 419  	 	 	 	 $feed = $this->registry->create('File', array($value, $this->timeout, 5, null, $this->useragent, $this->force_fsockopen, $this->curl_options));
 420  	 	 	 	 if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed))
 421  	 	 	 	 {
 422  	 	 	 	 	 return array($feed);
 423  	 	 	 	 }
 424  	 	 	 	 else
 425  	 	 	 	 {
 426  	 	 	 	 	 unset($array[$key]);
 427  	 	 	 	 }
 428  	 	 	 }
 429  	 	 }
 430  	 	 return null;
 431  	 }
 432  }
 433  
 434  class_alias('SimplePie_Locator', 'SimplePie\Locator', false);