MDL-48595 core_dml: Adding a recordset walker
[moodle.git] / lib / classes / dml / recordset_walk.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  * Applies the same callback to all recorset records.
19  *
20  * @since      Moodle 2.9
21  * @package    core
22  * @category   dml
23  * @copyright  2015 David Monllao
24  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25  */
27 namespace core\dml;
29 defined('MOODLE_INTERNAL') || die();
31 /**
32  * Iterator that walks through a moodle_recordset applying the provided function.
33  *
34  * The internal recordset can be closed using the close() function.
35  *
36  * Note that consumers of this class are responsible of closing the recordset,
37  * although there are some implicit closes under some ciscumstances:
38  * - Once all recordset records have been iterated
39  * - The object is destroyed
40  *
41  * @since      Moodle 2.9
42  * @package    core
43  * @category   dml
44  * @copyright  2015 David Monllao
45  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
46  */
47 class recordset_walk implements \Iterator {
49     /**
50      * @var \moodle_recordset The recordset.
51      */
52     protected $recordset;
54     /**
55      * @var callable The callback.
56      */
57     protected $callback;
59     /**
60      * @var array|false Extra params for the callback.
61      */
62     protected $callbackextra;
64     /**
65      * Create a new iterator applying the callback to each record.
66      *
67      * @param \moodle_recordset $recordset Recordset to iterate.
68      * @param callable $callback Apply this function to each record. If using a method, it should be public.
69      * @param array $callbackextra Array of arguments to pass to the callback.
70      */
71     public function __construct(\moodle_recordset $recordset, callable $callback, $callbackextra = false) {
72         $this->recordset = $recordset;
73         $this->callback = $callback;
74         $this->callbackextra = $callbackextra;
75     }
77     /**
78      * Closes the recordset.
79      *
80      * @return void
81      */
82     public function __destruct() {
83         $this->close();
84     }
86     /**
87      * Returns the current element after applying the callback.
88      *
89      * @return mixed|bool The returned value type will depend on the callback.
90      */
91     public function current() {
93         if (!$this->recordset->valid()) {
94             return false;
95         }
97         if (!$record = $this->recordset->current()) {
98             return false;
99         }
101         // Apply callback and return.
102         if ($this->callbackextra) {
103             return call_user_func($this->callback, $record);
104         } else {
105             return call_user_func($this->callback, $record, $this->callbackextra);
106         }
107     }
109     /**
110      * Moves the internal pointer to the next record.
111      *
112      * @return void
113      */
114     public function next() {
115         return $this->recordset->next();
116     }
118     /**
119      * Returns current record key.
120      *
121      * @return int
122      */
123     public function key() {
124         return $this->recordset->key();
125     }
127     /**
128      * Returns whether the current position is valid or not.
129      *
130      * If we reached the end of the recordset we close as we
131      * don't allow rewinds. Doing do so we reduce the chance
132      * of unclosed recordsets.
133      *
134      * @return bool
135      */
136     public function valid() {
137         if (!$valid = $this->recordset->valid()) {
138             $this->close();
139         }
140         return $valid;
141     }
143     /**
144      * Rewind is not supported.
145      *
146      * @return void
147      */
148     public function rewind() {
149         // No rewind as it is not implemented in moodle_recordset.
150         return;
151     }
153     /**
154      * Closes the recordset.
155      *
156      * @return void
157      */
158     public function close() {
159         $this->recordset->close();
160     }