Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.
   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle 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  // Moodle 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 Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Task for updating RSS feeds for rss client block
  19   *
  20   * @package   block_rss_client
  21   * @author    Farhan Karmali <farhan6318@gmail.com>
  22   * @copyright Farhan Karmali 2018
  23   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  namespace block_rss_client\task;
  27  
  28  defined('MOODLE_INTERNAL') || die();
  29  
  30  /**
  31   * Task for updating RSS feeds for rss client block
  32   *
  33   * @package   block_rss_client
  34   * @author    Farhan Karmali <farhan6318@gmail.com>
  35   * @copyright Farhan Karmali 2018
  36   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  37   */
  38  class refreshfeeds extends \core\task\scheduled_task {
  39  
  40      /** The maximum time in seconds that cron will wait between attempts to retry failing RSS feeds. */
  41      const CLIENT_MAX_SKIPTIME = HOURSECS * 12;
  42  
  43      /**
  44       * Name for this task.
  45       *
  46       * @return string
  47       */
  48      public function get_name() {
  49          return get_string('refreshfeedstask', 'block_rss_client');
  50      }
  51  
  52      /**
  53       * This task goes through all the feeds. If the feed has a skipuntil value
  54       * that is less than the current time cron will attempt to retrieve it
  55       * with the cache duration set to 0 in order to force the retrieval of
  56       * the item and refresh the cache.
  57       *
  58       * If a feed fails then the skipuntil time of that feed is set to be
  59       * later than the next expected task time. The amount of time will
  60       * increase each time the fetch fails until the maximum is reached.
  61       *
  62       * If a feed that has been failing is successfully retrieved it will
  63       * go back to being handled as though it had never failed.
  64       *
  65       * Task should therefore process requests for permanently broken RSS
  66       * feeds infrequently, and temporarily unavailable feeds will be tried
  67       * less often until they become available again.
  68       */
  69      public function execute() {
  70          global $CFG, $DB;
  71          require_once("{$CFG->libdir}/simplepie/moodle_simplepie.php");
  72  
  73          // We are going to measure execution times.
  74          $starttime = microtime();
  75          $starttimesec = time();
  76  
  77          // Fetch all site feeds.
  78          $rs = $DB->get_recordset('block_rss_client');
  79          $counter = 0;
  80          mtrace('');
  81          foreach ($rs as $rec) {
  82              mtrace('    ' . $rec->url . ' ', '');
  83  
  84              // Skip feed if it failed recently.
  85              if ($starttimesec < $rec->skipuntil) {
  86                  mtrace('skipping until ' . userdate($rec->skipuntil));
  87                  continue;
  88              }
  89  
  90              $feed = $this->fetch_feed($rec->url);
  91  
  92              if ($feed->error()) {
  93                  // Skip this feed (for an ever-increasing time if it keeps failing).
  94                  $rec->skiptime = $this->calculate_skiptime($rec->skiptime);
  95                  $rec->skipuntil = time() + $rec->skiptime;
  96                  $DB->update_record('block_rss_client', $rec);
  97                  mtrace("Error: could not load/find the RSS feed - skipping for {$rec->skiptime} seconds.");
  98              } else {
  99                  mtrace ('ok');
 100                  // It worked this time, so reset the skiptime.
 101                  if ($rec->skiptime > 0) {
 102                      $rec->skiptime = 0;
 103                      $rec->skipuntil = 0;
 104                      $DB->update_record('block_rss_client', $rec);
 105                  }
 106                  // Only increase the counter when a feed is sucesfully refreshed.
 107                  $counter ++;
 108              }
 109          }
 110          $rs->close();
 111  
 112          // Show times.
 113          mtrace($counter . ' feeds refreshed (took ' . microtime_diff($starttime, microtime()) . ' seconds)');
 114      }
 115  
 116      /**
 117       * Fetch a feed for the specified URL.
 118       *
 119       * @param   string  $url The URL to fetch
 120       * @return  \moodle_simplepie
 121       */
 122      protected function fetch_feed(string $url) : \moodle_simplepie {
 123          // Fetch the rss feed, using standard simplepie caching so feeds will be renewed only if cache has expired.
 124          \core_php_time_limit::raise(60);
 125  
 126          $feed = new \moodle_simplepie();
 127  
 128          // Set timeout for longer than normal to be agressive at fetching feeds if possible..
 129          $feed->set_timeout(40);
 130          $feed->set_cache_duration(0);
 131          $feed->set_feed_url($url);
 132          $feed->init();
 133  
 134          return $feed;
 135      }
 136  
 137      /**
 138       * Calculates a new skip time for a record based on the current skip time.
 139       *
 140       * @param   int     $currentskip The current skip time of a record.
 141       * @return  int     The newly calculated skip time.
 142       */
 143      protected function calculate_skiptime(int $currentskip) : int {
 144          // If the feed has never failed, then the initial skiptime will be 0. We use a default of 5 minutes in this case.
 145          // If the feed has previously failed then we double that time.
 146          $newskiptime = max(MINSECS * 5, ($currentskip * 2));
 147  
 148          // Max out at the CLIENT_MAX_SKIPTIME.
 149          return min($newskiptime, self::CLIENT_MAX_SKIPTIME);
 150      }
 151  }