Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

Differences Between: [Versions 39 and 400]

   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   * Class containing utility methods for dataformats
  19   *
  20   * @package     core
  21   * @copyright   2020 Paul Holden <paulh@moodle.com>
  22   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace core;
  26  
  27  use coding_exception;
  28  use core_php_time_limit;
  29  use stored_file;
  30  
  31  /**
  32   * Dataformat utility class
  33   *
  34   * @package     core
  35   * @copyright   2020 Paul Holden <paulh@moodle.com>
  36   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  37   */
  38  class dataformat {
  39  
  40      /**
  41       * Return an instance of a dataformat writer from given dataformat type
  42       *
  43       * @param string $dataformat
  44       * @return dataformat\base
  45       * @throws coding_exception
  46       */
  47      protected static function get_format_instance(string $dataformat): \core\dataformat\base {
  48          $classname = 'dataformat_' . $dataformat . '\writer';
  49          if (!class_exists($classname)) {
  50              throw new coding_exception('Invalid dataformat', $dataformat);
  51          }
  52  
  53          return new $classname();
  54      }
  55  
  56      /**
  57       * Sends a formatted data file to the browser
  58       *
  59       * @param string $filename
  60       * @param string $dataformat
  61       * @param array $columns
  62       * @param Iterable $iterator
  63       * @param callable|null $callback Optional callback method to apply to each record prior to writing, which accepts two
  64       *      parameters as such: function($record, bool $supportshtml) returning formatted record
  65       * @throws coding_exception
  66       */
  67      public static function download_data(string $filename, string $dataformat, array $columns, Iterable $iterator,
  68              callable $callback = null): void {
  69  
  70          if (ob_get_length()) {
  71              throw new coding_exception('Output can not be buffered before calling download_data()');
  72          }
  73  
  74          $format = self::get_format_instance($dataformat);
  75  
  76          // The data format export could take a while to generate.
  77          core_php_time_limit::raise();
  78  
  79          // Close the session so that the users other tabs in the same session are not blocked.
  80          \core\session\manager::write_close();
  81  
  82          // If this file was requested from a form, then mark download as complete (before sending headers).
  83          \core_form\util::form_download_complete();
  84  
  85          $format->set_filename($filename);
  86          $format->send_http_headers();
  87  
  88          $format->start_output();
  89          $format->start_sheet($columns);
  90  
  91          $rownum = 0;
  92          foreach ($iterator as $row) {
  93              if (is_callable($callback)) {
  94                  $row = $callback($row, $format->supports_html());
  95              }
  96              if ($row === null) {
  97                  continue;
  98              }
  99              $format->write_record($row, $rownum++);
 100          }
 101  
 102          $format->close_sheet($columns);
 103          $format->close_output();
 104      }
 105  
 106      /**
 107       * Writes a formatted data file with specified filename
 108       *
 109       * @param string $filename
 110       * @param string $dataformat
 111       * @param array $columns
 112       * @param Iterable $iterator
 113       * @param callable|null $callback
 114       * @return string Complete path to the file on disk
 115       */
 116      public static function write_data(string $filename, string $dataformat, array $columns, Iterable $iterator,
 117              callable $callback = null): string {
 118  
 119          $format = self::get_format_instance($dataformat);
 120  
 121          // The data format export could take a while to generate.
 122          core_php_time_limit::raise();
 123  
 124          // Close the session so that the users other tabs in the same session are not blocked.
 125          \core\session\manager::write_close();
 126  
 127          $filepath = make_request_directory() . '/' . $filename . $format->get_extension();
 128          $format->set_filepath($filepath);
 129  
 130          $format->start_output_to_file();
 131          $format->start_sheet($columns);
 132  
 133          $rownum = 0;
 134          foreach ($iterator as $row) {
 135              if (is_callable($callback)) {
 136                  $row = $callback($row, $format->supports_html());
 137              }
 138              if ($row === null) {
 139                  continue;
 140              }
 141              $format->write_record($row, $rownum++);
 142          }
 143  
 144          $format->close_sheet($columns);
 145          $format->close_output_to_file();
 146  
 147          return $filepath;
 148      }
 149  
 150      /**
 151       * Writes a formatted data file to file storage
 152       *
 153       * @param array $filerecord File record for storage, 'filename' extension should be omitted as it's added by the dataformat
 154       * @param string $dataformat
 155       * @param array $columns
 156       * @param Iterable $iterator Iterable set of records to write
 157       * @param callable|null $callback Optional callback method to apply to each record prior to writing
 158       * @return stored_file
 159       */
 160      public static function write_data_to_filearea(array $filerecord, string $dataformat, array $columns, Iterable $iterator,
 161              callable $callback = null): stored_file {
 162  
 163          $filepath = self::write_data($filerecord['filename'], $dataformat, $columns, $iterator, $callback);
 164  
 165          // Update filename of returned file record.
 166          $filerecord['filename'] = basename($filepath);
 167  
 168          return get_file_storage()->create_file_from_pathname($filerecord, $filepath);
 169      }
 170  }