MediaWiki master
ApiContinuationManager.php
Go to the documentation of this file.
1<?php
21namespace MediaWiki\Api;
22
23use UnexpectedValueException;
24
32 private $source;
33
35 private $allModules = [];
37 private $generatedModules;
38
40 private $continuationData = [];
42 private $generatorContinuationData = [];
44 private $generatorNonContinuationData = [];
45
47 private $generatorParams = [];
49 private $generatorDone = false;
50
57 public function __construct(
58 ApiBase $module, array $allModules = [], array $generatedModules = []
59 ) {
60 $this->source = get_class( $module );
61 $request = $module->getRequest();
62
63 $this->generatedModules = $generatedModules
64 ? array_combine( $generatedModules, $generatedModules )
65 : [];
66
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' );
73 }
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 )
81 );
82 } else {
83 // When the generator is complete, don't run any modules that
84 // depend on it.
85 $skip += $this->generatedModules;
86 }
87 }
88
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;
97 }
98 }
99 }
100
105 public function getSource() {
106 return $this->source;
107 }
108
112 public function isGeneratorDone() {
113 return $this->generatorDone;
114 }
115
120 public function getRunModules() {
121 return array_values( array_filter( $this->allModules ) );
122 }
123
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'
137 );
138 }
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'
143 );
144 }
145 $paramName = $module->encodeParamName( $paramName );
146 if ( is_array( $paramValue ) ) {
147 $paramValue = implode( '|', $paramValue );
148 }
149 $this->continuationData[$name][$paramName] = $paramValue;
150 }
151
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 );
168 }
169 $this->generatorNonContinuationData[$name][$paramName] = $paramValue;
170 }
171
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 );
183 }
184 $this->generatorContinuationData[$name][$paramName] = $paramValue;
185 }
186
191 public function getRawContinuation() {
192 return array_merge_recursive( $this->continuationData, $this->generatorContinuationData );
193 }
194
200 public function getRawNonContinuation() {
201 return $this->generatorNonContinuationData;
202 }
203
208 public function getContinuation() {
209 $data = [];
210 $batchcomplete = false;
211
212 $finishedModules = array_diff(
213 array_keys( $this->allModules ),
214 array_keys( $this->continuationData )
215 );
216
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;
221 }
222
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;
230 }
231 $generatorParams = [];
232 foreach ( $this->generatorNonContinuationData as $kvp ) {
233 $generatorParams += $kvp;
234 }
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;
246 }
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;
255 }
256
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 );
261 }
262
263 return [ $data, $batchcomplete ];
264 }
265
270 public function setContinuationIntoResult( ApiResult $result ) {
271 [ $data, $batchcomplete ] = $this->getContinuation();
272 if ( $data ) {
273 $result->addValue( null, 'continue', $data,
275 }
276 if ( $batchcomplete ) {
277 $result->addValue( null, 'batchcomplete', true,
279 }
280 }
281}
282
284class_alias( ApiContinuationManager::class, 'ApiContinuationManager' );
array $params
The job parameters.
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:76
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:571
getMain()
Get the main module.
Definition ApiBase.php:589
encodeParamName( $paramName)
This method mangles parameter name based on the prefix supplied to the constructor.
Definition ApiBase.php:829
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:851
addGeneratorNonContinueParam(ApiBase $module, $paramName, $paramValue)
Set the non-continuation parameter for the generator module.
setContinuationIntoResult(ApiResult $result)
Store the continuation data into the result.
getSource()
Get the class that created this manager.
getRawContinuation()
Fetch raw continuation data.
getRawNonContinuation()
Fetch raw non-continuation data.
getContinuation()
Fetch continuation result data.
__construct(ApiBase $module, array $allModules=[], array $generatedModules=[])
addContinueParam(ApiBase $module, $paramName, $paramValue)
Set the continuation parameter for a module.
addGeneratorContinueParam(ApiBase $module, $paramName, $paramValue)
Set the continuation parameter for the generator module.
getRunModules()
Get the list of modules that should actually be run.
This class represents the result of the API operations.
Definition ApiResult.php:43
const ADD_ON_TOP
For addValue(), setValue() and similar functions, if the value does not exist, add it as the first el...
Definition ApiResult.php:57
const NO_SIZE_CHECK
For addValue() and similar functions, do not check size while adding a value Don't use this unless yo...
Definition ApiResult.php:66
addValue( $path, $name, $value, $flags=0)
Add value to the output data at the given path.
static newWithMessage(?ApiBase $module, $msg, $code=null, $data=null, $httpCode=0, Throwable $previous=null)