]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - includes/api/ApiContinuationManager.php
MediaWiki 1.30.2-scripts2
[autoinstalls/mediawiki.git] / includes / api / ApiContinuationManager.php
1 <?php
2 /**
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16  * http://www.gnu.org/copyleft/gpl.html
17  *
18  * @file
19  */
20
21 /**
22  * This manages continuation state.
23  * @since 1.25 this is no longer a subclass of ApiBase
24  * @ingroup API
25  */
26 class ApiContinuationManager {
27         private $source;
28
29         private $allModules = [];
30         private $generatedModules = [];
31
32         private $continuationData = [];
33         private $generatorContinuationData = [];
34         private $generatorNonContinuationData = [];
35
36         private $generatorParams = [];
37         private $generatorDone = false;
38
39         /**
40          * @param ApiBase $module Module starting the continuation
41          * @param ApiBase[] $allModules Contains ApiBase instances that will be executed
42          * @param array $generatedModules Names of modules that depend on the generator
43          * @throws ApiUsageException
44          */
45         public function __construct(
46                 ApiBase $module, array $allModules = [], array $generatedModules = []
47         ) {
48                 $this->source = get_class( $module );
49                 $request = $module->getRequest();
50
51                 $this->generatedModules = $generatedModules
52                         ? array_combine( $generatedModules, $generatedModules )
53                         : [];
54
55                 $skip = [];
56                 $continue = $request->getVal( 'continue', '' );
57                 if ( $continue !== '' ) {
58                         $continue = explode( '||', $continue );
59                         if ( count( $continue ) !== 2 ) {
60                                 throw ApiUsageException::newWithMessage( $module->getMain(), 'apierror-badcontinue' );
61                         }
62                         $this->generatorDone = ( $continue[0] === '-' );
63                         $skip = explode( '|', $continue[1] );
64                         if ( !$this->generatorDone ) {
65                                 $params = explode( '|', $continue[0] );
66                                 if ( $params ) {
67                                         $this->generatorParams = array_intersect_key(
68                                                 $request->getValues(),
69                                                 array_flip( $params )
70                                         );
71                                 }
72                         } else {
73                                 // When the generator is complete, don't run any modules that
74                                 // depend on it.
75                                 $skip += $this->generatedModules;
76                         }
77                 }
78
79                 foreach ( $allModules as $module ) {
80                         $name = $module->getModuleName();
81                         if ( in_array( $name, $skip, true ) ) {
82                                 $this->allModules[$name] = false;
83                                 // Prevent spurious "unused parameter" warnings
84                                 $module->extractRequestParams();
85                         } else {
86                                 $this->allModules[$name] = $module;
87                         }
88                 }
89         }
90
91         /**
92          * Get the class that created this manager
93          * @return string
94          */
95         public function getSource() {
96                 return $this->source;
97         }
98
99         /**
100          * Is the generator done?
101          * @return bool
102          */
103         public function isGeneratorDone() {
104                 return $this->generatorDone;
105         }
106
107         /**
108          * Get the list of modules that should actually be run
109          * @return ApiBase[]
110          */
111         public function getRunModules() {
112                 return array_values( array_filter( $this->allModules ) );
113         }
114
115         /**
116          * Set the continuation parameter for a module
117          * @param ApiBase $module
118          * @param string $paramName
119          * @param string|array $paramValue
120          * @throws UnexpectedValueException
121          */
122         public function addContinueParam( ApiBase $module, $paramName, $paramValue ) {
123                 $name = $module->getModuleName();
124                 if ( !isset( $this->allModules[$name] ) ) {
125                         throw new UnexpectedValueException(
126                                 "Module '$name' called " . __METHOD__ .
127                                         ' but was not passed to ' . __CLASS__ . '::__construct'
128                         );
129                 }
130                 if ( !$this->allModules[$name] ) {
131                         throw new UnexpectedValueException(
132                                 "Module '$name' was not supposed to have been executed, but " .
133                                         'it was executed anyway'
134                         );
135                 }
136                 $paramName = $module->encodeParamName( $paramName );
137                 if ( is_array( $paramValue ) ) {
138                         $paramValue = implode( '|', $paramValue );
139                 }
140                 $this->continuationData[$name][$paramName] = $paramValue;
141         }
142
143         /**
144          * Set the non-continuation parameter for the generator module
145          *
146          * In case the generator isn't going to be continued, this sets the fields
147          * to return.
148          *
149          * @since 1.28
150          * @param ApiBase $module
151          * @param string $paramName
152          * @param string|array $paramValue
153          */
154         public function addGeneratorNonContinueParam( ApiBase $module, $paramName, $paramValue ) {
155                 $name = $module->getModuleName();
156                 $paramName = $module->encodeParamName( $paramName );
157                 if ( is_array( $paramValue ) ) {
158                         $paramValue = implode( '|', $paramValue );
159                 }
160                 $this->generatorNonContinuationData[$name][$paramName] = $paramValue;
161         }
162
163         /**
164          * Set the continuation parameter for the generator module
165          * @param ApiBase $module
166          * @param string $paramName
167          * @param string|array $paramValue
168          */
169         public function addGeneratorContinueParam( ApiBase $module, $paramName, $paramValue ) {
170                 $name = $module->getModuleName();
171                 $paramName = $module->encodeParamName( $paramName );
172                 if ( is_array( $paramValue ) ) {
173                         $paramValue = implode( '|', $paramValue );
174                 }
175                 $this->generatorContinuationData[$name][$paramName] = $paramValue;
176         }
177
178         /**
179          * Fetch raw continuation data
180          * @return array
181          */
182         public function getRawContinuation() {
183                 return array_merge_recursive( $this->continuationData, $this->generatorContinuationData );
184         }
185
186         /**
187          * Fetch raw non-continuation data
188          * @since 1.28
189          * @return array
190          */
191         public function getRawNonContinuation() {
192                 return $this->generatorNonContinuationData;
193         }
194
195         /**
196          * Fetch continuation result data
197          * @return array [ (array)$data, (bool)$batchcomplete ]
198          */
199         public function getContinuation() {
200                 $data = [];
201                 $batchcomplete = false;
202
203                 $finishedModules = array_diff(
204                         array_keys( $this->allModules ),
205                         array_keys( $this->continuationData )
206                 );
207
208                 // First, grab the non-generator-using continuation data
209                 $continuationData = array_diff_key( $this->continuationData, $this->generatedModules );
210                 foreach ( $continuationData as $module => $kvp ) {
211                         $data += $kvp;
212                 }
213
214                 // Next, handle the generator-using continuation data
215                 $continuationData = array_intersect_key( $this->continuationData, $this->generatedModules );
216                 if ( $continuationData ) {
217                         // Some modules are unfinished: include those params, and copy
218                         // the generator params.
219                         foreach ( $continuationData as $module => $kvp ) {
220                                 $data += $kvp;
221                         }
222                         $generatorParams = [];
223                         foreach ( $this->generatorNonContinuationData as $kvp ) {
224                                 $generatorParams += $kvp;
225                         }
226                         $generatorParams += $this->generatorParams;
227                         $data += $generatorParams;
228                         $generatorKeys = implode( '|', array_keys( $generatorParams ) );
229                 } elseif ( $this->generatorContinuationData ) {
230                         // All the generator-using modules are complete, but the
231                         // generator isn't. Continue the generator and restart the
232                         // generator-using modules
233                         $generatorParams = [];
234                         foreach ( $this->generatorContinuationData as $kvp ) {
235                                 $generatorParams += $kvp;
236                         }
237                         $data += $generatorParams;
238                         $finishedModules = array_diff( $finishedModules, $this->generatedModules );
239                         $generatorKeys = implode( '|', array_keys( $generatorParams ) );
240                         $batchcomplete = true;
241                 } else {
242                         // Generator and prop modules are all done. Mark it so.
243                         $generatorKeys = '-';
244                         $batchcomplete = true;
245                 }
246
247                 // Set 'continue' if any continuation data is set or if the generator
248                 // still needs to run
249                 if ( $data || $generatorKeys !== '-' ) {
250                         $data['continue'] = $generatorKeys . '||' . implode( '|', $finishedModules );
251                 }
252
253                 return [ $data, $batchcomplete ];
254         }
255
256         /**
257          * Store the continuation data into the result
258          * @param ApiResult $result
259          */
260         public function setContinuationIntoResult( ApiResult $result ) {
261                 list( $data, $batchcomplete ) = $this->getContinuation();
262                 if ( $data ) {
263                         $result->addValue( null, 'continue', $data,
264                                 ApiResult::ADD_ON_TOP | ApiResult::NO_SIZE_CHECK );
265                 }
266                 if ( $batchcomplete ) {
267                         $result->addValue( null, 'batchcomplete', true,
268                                 ApiResult::ADD_ON_TOP | ApiResult::NO_SIZE_CHECK );
269                 }
270         }
271 }