0bc545363854ec3ac4076cb1da3f4749c88aa95d
[moodle.git] / lib / classes / dataformat.php
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/>.
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  */
25 namespace core;
27 use coding_exception;
28 use core_php_time_limit;
29 use stored_file;
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 {
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         }
53         return new $classname();
54     }
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
64      * @throws coding_exception
65      */
66     public static function download_data(string $filename, string $dataformat, array $columns, Iterable $iterator,
67             callable $callback = null): void {
69         if (ob_get_length()) {
70             throw new coding_exception('Output can not be buffered before calling download_data()');
71         }
73         $format = self::get_format_instance($dataformat);
75         // The data format export could take a while to generate.
76         core_php_time_limit::raise();
78         // Close the session so that the users other tabs in the same session are not blocked.
79         \core\session\manager::write_close();
81         // If this file was requested from a form, then mark download as complete (before sending headers).
82         \core_form\util::form_download_complete();
84         $format->set_filename($filename);
85         $format->send_http_headers();
87         $format->start_output();
88         $format->start_sheet($columns);
90         $rownum = 0;
91         foreach ($iterator as $row) {
92             if (is_callable($callback)) {
93                 $row = $callback($row);
94             }
95             if ($row === null) {
96                 continue;
97             }
98             $format->write_record($row, $rownum++);
99         }
101         $format->close_sheet($columns);
102         $format->close_output();
103     }
105     /**
106      * Writes a formatted data file with specified filename
107      *
108      * @param string $filename
109      * @param string $dataformat
110      * @param array $columns
111      * @param Iterable $iterator
112      * @param callable|null $callback
113      * @return string Complete path to the file on disk
114      */
115     public static function write_data(string $filename, string $dataformat, array $columns, Iterable $iterator,
116             callable $callback = null): string {
118         $format = self::get_format_instance($dataformat);
120         // The data format export could take a while to generate.
121         core_php_time_limit::raise();
123         // Close the session so that the users other tabs in the same session are not blocked.
124         \core\session\manager::write_close();
126         $filepath = make_request_directory() . '/' . $filename . $format->get_extension();
127         $format->set_filepath($filepath);
129         $format->start_output_to_file();
130         $format->start_sheet($columns);
132         $rownum = 0;
133         foreach ($iterator as $row) {
134             if (is_callable($callback)) {
135                 $row = $callback($row);
136             }
137             if ($row === null) {
138                 continue;
139             }
140             $format->write_record($row, $rownum++);
141         }
143         $format->close_sheet($columns);
144         $format->close_output_to_file();
146         return $filepath;
147     }
149     /**
150      * Writes a formatted data file to file storage
151      *
152      * @param array $filerecord File record for storage, 'filename' extension should be omitted as it's added by the dataformat
153      * @param string $dataformat
154      * @param array $columns
155      * @param Iterable $iterator Iterable set of records to write
156      * @param callable|null $callback Optional callback method to apply to each record prior to writing
157      * @return stored_file
158      */
159     public static function write_data_to_filearea(array $filerecord, string $dataformat, array $columns, Iterable $iterator,
160             callable $callback = null): stored_file {
162         $filepath = self::write_data($filerecord['filename'], $dataformat, $columns, $iterator, $callback);
164         // Update filename of returned file record.
165         $filerecord['filename'] = basename($filepath);
167         return get_file_storage()->create_file_from_pathname($filerecord, $filepath);
168     }