Search moodle.org's
Developer Documentation

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.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  }