MediaWiki master
ApiContinuationManager.php
Go to the documentation of this file.
1<?php
7namespace MediaWiki\Api;
8
9use UnexpectedValueException;
10
18 private $source;
19
21 private $allModules = [];
23 private $generatedModules;
24
26 private $continuationData = [];
28 private $generatorContinuationData = [];
30 private $generatorNonContinuationData = [];
31
33 private $generatorParams = [];
35 private $generatorDone = false;
36
43 public function __construct(
44 ApiBase $module, array $allModules = [], array $generatedModules = []
45 ) {
46 $this->source = get_class( $module );
47 $request = $module->getRequest();
48
49 $this->generatedModules = $generatedModules
50 ? array_combine( $generatedModules, $generatedModules )
51 : [];
52
53 $skip = [];
54 $continue = $request->getVal( 'continue', '' );
55 if ( $continue !== '' ) {
56 $continue = explode( '||', $continue );
57 if ( count( $continue ) !== 2 ) {
58 throw ApiUsageException::newWithMessage( $module->getMain(), 'apierror-badcontinue' );
59 }
60 $this->generatorDone = ( $continue[0] === '-' );
61 $skip = explode( '|', $continue[1] );
62 if ( !$this->generatorDone ) {
63 $params = explode( '|', $continue[0] );
64 $this->generatorParams = array_intersect_key(
65 $request->getValues(),
66 array_fill_keys( $params, true )
67 );
68 } else {
69 // When the generator is complete, don't run any modules that
70 // depend on it.
71 $skip += $this->generatedModules;
72 }
73 }
74
75 foreach ( $allModules as $module ) {
76 $name = $module->getModuleName();
77 if ( in_array( $name, $skip, true ) ) {
78 $this->allModules[$name] = false;
79 // Prevent spurious "unused parameter" warnings
80 $module->extractRequestParams();
81 } else {
82 $this->allModules[$name] = $module;
83 }
84 }
85 }
86
91 public function getSource() {
92 return $this->source;
93 }
94
98 public function isGeneratorDone() {
99 return $this->generatorDone;
100 }
101
106 public function getRunModules() {
107 return array_values( array_filter( $this->allModules ) );
108 }
109
117 public function addContinueParam( ApiBase $module, $paramName, $paramValue ) {
118 $name = $module->getModuleName();
119 if ( !isset( $this->allModules[$name] ) ) {
120 throw new UnexpectedValueException(
121 "Module '$name' called " . __METHOD__ .
122 ' but was not passed to ' . __CLASS__ . '::__construct'
123 );
124 }
125 if ( !$this->allModules[$name] ) {
126 throw new UnexpectedValueException(
127 "Module '$name' was not supposed to have been executed, but " .
128 'it was executed anyway'
129 );
130 }
131 $paramName = $module->encodeParamName( $paramName );
132 if ( is_array( $paramValue ) ) {
133 $paramValue = implode( '|', $paramValue );
134 }
135 $this->continuationData[$name][$paramName] = $paramValue;
136 }
137
149 public function addGeneratorNonContinueParam( ApiBase $module, $paramName, $paramValue ) {
150 $name = $module->getModuleName();
151 $paramName = $module->encodeParamName( $paramName );
152 if ( is_array( $paramValue ) ) {
153 $paramValue = implode( '|', $paramValue );
154 }
155 $this->generatorNonContinuationData[$name][$paramName] = $paramValue;
156 }
157
164 public function addGeneratorContinueParam( ApiBase $module, $paramName, $paramValue ) {
165 $name = $module->getModuleName();
166 $paramName = $module->encodeParamName( $paramName );
167 if ( is_array( $paramValue ) ) {
168 $paramValue = implode( '|', $paramValue );
169 }
170 $this->generatorContinuationData[$name][$paramName] = $paramValue;
171 }
172
177 public function getRawContinuation() {
178 return array_merge_recursive( $this->continuationData, $this->generatorContinuationData );
179 }
180
186 public function getRawNonContinuation() {
187 return $this->generatorNonContinuationData;
188 }
189
194 public function getContinuation() {
195 $data = [];
196 $batchcomplete = false;
197
198 $finishedModules = array_diff(
199 array_keys( $this->allModules ),
200 array_keys( $this->continuationData )
201 );
202
203 // First, grab the non-generator-using continuation data
204 $continuationData = array_diff_key( $this->continuationData, $this->generatedModules );
205 foreach ( $continuationData as $kvp ) {
206 $data += $kvp;
207 }
208
209 // Next, handle the generator-using continuation data
210 $continuationData = array_intersect_key( $this->continuationData, $this->generatedModules );
211 if ( $continuationData ) {
212 // Some modules are unfinished: include those params, and copy
213 // the generator params.
214 foreach ( $continuationData as $kvp ) {
215 $data += $kvp;
216 }
217 $generatorParams = [];
218 foreach ( $this->generatorNonContinuationData as $kvp ) {
219 $generatorParams += $kvp;
220 }
221 $generatorParams += $this->generatorParams;
222 // @phan-suppress-next-line PhanTypeInvalidLeftOperand False positive in phan
223 $data += $generatorParams;
224 $generatorKeys = implode( '|', array_keys( $generatorParams ) );
225 } elseif ( $this->generatorContinuationData ) {
226 // All the generator-using modules are complete, but the
227 // generator isn't. Continue the generator and restart the
228 // generator-using modules
229 $generatorParams = [];
230 foreach ( $this->generatorContinuationData as $kvp ) {
231 $generatorParams += $kvp;
232 }
233 $data += $generatorParams;
234 $finishedModules = array_diff( $finishedModules, $this->generatedModules );
235 $generatorKeys = implode( '|', array_keys( $generatorParams ) );
236 $batchcomplete = true;
237 } else {
238 // Generator and prop modules are all done. Mark it so.
239 $generatorKeys = '-';
240 $batchcomplete = true;
241 }
242
243 // Set 'continue' if any continuation data is set or if the generator
244 // still needs to run
245 if ( $data || $generatorKeys !== '-' ) {
246 $data['continue'] = $generatorKeys . '||' . implode( '|', $finishedModules );
247 }
248
249 return [ $data, $batchcomplete ];
250 }
251
255 public function setContinuationIntoResult( ApiResult $result ) {
256 [ $data, $batchcomplete ] = $this->getContinuation();
257 if ( $data ) {
258 $result->addValue( null, 'continue', $data,
260 }
261 if ( $batchcomplete ) {
262 $result->addValue( null, 'batchcomplete', true,
264 }
265 }
266}
267
269class_alias( ApiContinuationManager::class, 'ApiContinuationManager' );
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:61
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:543
getMain()
Get the main module.
Definition ApiBase.php:561
encodeParamName( $paramName)
This method mangles parameter name based on the prefix supplied to the constructor.
Definition ApiBase.php:801
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:823
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:34
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:48
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:57
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)