Localisation updates from https://translatewiki.net.
[mediawiki.git] / includes / api / ApiContinuationManager.php
blob0a6e84f52b4f4f58cbf38d1afa71ba43a599f355
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.
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.
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
18 * @file
21 namespace MediaWiki\Api;
23 use UnexpectedValueException;
25 /**
26 * This manages continuation state.
27 * @since 1.25 this is no longer a subclass of ApiBase
28 * @ingroup API
30 class ApiContinuationManager {
31 /** @var string */
32 private $source;
34 /** @var (ApiBase|false)[] */
35 private $allModules = [];
36 /** @var string[] */
37 private $generatedModules;
39 /** @var array[] */
40 private $continuationData = [];
41 /** @var array[] */
42 private $generatorContinuationData = [];
43 /** @var array[] */
44 private $generatorNonContinuationData = [];
46 /** @var array */
47 private $generatorParams = [];
48 /** @var bool */
49 private $generatorDone = false;
51 /**
52 * @param ApiBase $module Module starting the continuation
53 * @param ApiBase[] $allModules Contains ApiBase instances that will be executed
54 * @param string[] $generatedModules Names of modules that depend on the generator
55 * @throws ApiUsageException
57 public function __construct(
58 ApiBase $module, array $allModules = [], array $generatedModules = []
59 ) {
60 $this->source = get_class( $module );
61 $request = $module->getRequest();
63 $this->generatedModules = $generatedModules
64 ? array_combine( $generatedModules, $generatedModules )
65 : [];
67 $skip = [];
68 $continue = $request->getVal( 'continue', '' );
69 if ( $continue !== '' ) {
70 $continue = explode( '||', $continue );
71 if ( count( $continue ) !== 2 ) {
72 throw ApiUsageException::newWithMessage( $module->getMain(), 'apierror-badcontinue' );
74 $this->generatorDone = ( $continue[0] === '-' );
75 $skip = explode( '|', $continue[1] );
76 if ( !$this->generatorDone ) {
77 $params = explode( '|', $continue[0] );
78 $this->generatorParams = array_intersect_key(
79 $request->getValues(),
80 array_fill_keys( $params, true )
82 } else {
83 // When the generator is complete, don't run any modules that
84 // depend on it.
85 $skip += $this->generatedModules;
89 foreach ( $allModules as $module ) {
90 $name = $module->getModuleName();
91 if ( in_array( $name, $skip, true ) ) {
92 $this->allModules[$name] = false;
93 // Prevent spurious "unused parameter" warnings
94 $module->extractRequestParams();
95 } else {
96 $this->allModules[$name] = $module;
102 * Get the class that created this manager
103 * @return string
105 public function getSource() {
106 return $this->source;
110 * @return bool
112 public function isGeneratorDone() {
113 return $this->generatorDone;
117 * Get the list of modules that should actually be run
118 * @return ApiBase[]
120 public function getRunModules() {
121 return array_values( array_filter( $this->allModules ) );
125 * Set the continuation parameter for a module
126 * @param ApiBase $module
127 * @param string $paramName
128 * @param string|array $paramValue
129 * @throws UnexpectedValueException
131 public function addContinueParam( ApiBase $module, $paramName, $paramValue ) {
132 $name = $module->getModuleName();
133 if ( !isset( $this->allModules[$name] ) ) {
134 throw new UnexpectedValueException(
135 "Module '$name' called " . __METHOD__ .
136 ' but was not passed to ' . __CLASS__ . '::__construct'
139 if ( !$this->allModules[$name] ) {
140 throw new UnexpectedValueException(
141 "Module '$name' was not supposed to have been executed, but " .
142 'it was executed anyway'
145 $paramName = $module->encodeParamName( $paramName );
146 if ( is_array( $paramValue ) ) {
147 $paramValue = implode( '|', $paramValue );
149 $this->continuationData[$name][$paramName] = $paramValue;
153 * Set the non-continuation parameter for the generator module
155 * In case the generator isn't going to be continued, this sets the fields
156 * to return.
158 * @since 1.28
159 * @param ApiBase $module
160 * @param string $paramName
161 * @param string|array $paramValue
163 public function addGeneratorNonContinueParam( ApiBase $module, $paramName, $paramValue ) {
164 $name = $module->getModuleName();
165 $paramName = $module->encodeParamName( $paramName );
166 if ( is_array( $paramValue ) ) {
167 $paramValue = implode( '|', $paramValue );
169 $this->generatorNonContinuationData[$name][$paramName] = $paramValue;
173 * Set the continuation parameter for the generator module
174 * @param ApiBase $module
175 * @param string $paramName
176 * @param int|string|array $paramValue
178 public function addGeneratorContinueParam( ApiBase $module, $paramName, $paramValue ) {
179 $name = $module->getModuleName();
180 $paramName = $module->encodeParamName( $paramName );
181 if ( is_array( $paramValue ) ) {
182 $paramValue = implode( '|', $paramValue );
184 $this->generatorContinuationData[$name][$paramName] = $paramValue;
188 * Fetch raw continuation data
189 * @return array[]
191 public function getRawContinuation() {
192 return array_merge_recursive( $this->continuationData, $this->generatorContinuationData );
196 * Fetch raw non-continuation data
197 * @since 1.28
198 * @return array[]
200 public function getRawNonContinuation() {
201 return $this->generatorNonContinuationData;
205 * Fetch continuation result data
206 * @return array [ (array)$data, (bool)$batchcomplete ]
208 public function getContinuation() {
209 $data = [];
210 $batchcomplete = false;
212 $finishedModules = array_diff(
213 array_keys( $this->allModules ),
214 array_keys( $this->continuationData )
217 // First, grab the non-generator-using continuation data
218 $continuationData = array_diff_key( $this->continuationData, $this->generatedModules );
219 foreach ( $continuationData as $kvp ) {
220 $data += $kvp;
223 // Next, handle the generator-using continuation data
224 $continuationData = array_intersect_key( $this->continuationData, $this->generatedModules );
225 if ( $continuationData ) {
226 // Some modules are unfinished: include those params, and copy
227 // the generator params.
228 foreach ( $continuationData as $kvp ) {
229 $data += $kvp;
231 $generatorParams = [];
232 foreach ( $this->generatorNonContinuationData as $kvp ) {
233 $generatorParams += $kvp;
235 $generatorParams += $this->generatorParams;
236 // @phan-suppress-next-line PhanTypeInvalidLeftOperand False positive in phan
237 $data += $generatorParams;
238 $generatorKeys = implode( '|', array_keys( $generatorParams ) );
239 } elseif ( $this->generatorContinuationData ) {
240 // All the generator-using modules are complete, but the
241 // generator isn't. Continue the generator and restart the
242 // generator-using modules
243 $generatorParams = [];
244 foreach ( $this->generatorContinuationData as $kvp ) {
245 $generatorParams += $kvp;
247 $data += $generatorParams;
248 $finishedModules = array_diff( $finishedModules, $this->generatedModules );
249 $generatorKeys = implode( '|', array_keys( $generatorParams ) );
250 $batchcomplete = true;
251 } else {
252 // Generator and prop modules are all done. Mark it so.
253 $generatorKeys = '-';
254 $batchcomplete = true;
257 // Set 'continue' if any continuation data is set or if the generator
258 // still needs to run
259 if ( $data || $generatorKeys !== '-' ) {
260 $data['continue'] = $generatorKeys . '||' . implode( '|', $finishedModules );
263 return [ $data, $batchcomplete ];
267 * Store the continuation data into the result
268 * @param ApiResult $result
270 public function setContinuationIntoResult( ApiResult $result ) {
271 [ $data, $batchcomplete ] = $this->getContinuation();
272 if ( $data ) {
273 $result->addValue( null, 'continue', $data,
274 ApiResult::ADD_ON_TOP | ApiResult::NO_SIZE_CHECK );
276 if ( $batchcomplete ) {
277 $result->addValue( null, 'batchcomplete', true,
278 ApiResult::ADD_ON_TOP | ApiResult::NO_SIZE_CHECK );
283 /** @deprecated class alias since 1.43 */
284 class_alias( ApiContinuationManager::class, 'ApiContinuationManager' );