MediaWiki master
ApiContinuationManager.php
Go to the documentation of this file.
1<?php
28 private $source;
29
31 private $allModules = [];
33 private $generatedModules;
34
36 private $continuationData = [];
38 private $generatorContinuationData = [];
40 private $generatorNonContinuationData = [];
41
43 private $generatorParams = [];
45 private $generatorDone = false;
46
53 public function __construct(
54 ApiBase $module, array $allModules = [], array $generatedModules = []
55 ) {
56 $this->source = get_class( $module );
57 $request = $module->getRequest();
58
59 $this->generatedModules = $generatedModules
60 ? array_combine( $generatedModules, $generatedModules )
61 : [];
62
63 $skip = [];
64 $continue = $request->getVal( 'continue', '' );
65 if ( $continue !== '' ) {
66 $continue = explode( '||', $continue );
67 if ( count( $continue ) !== 2 ) {
68 throw ApiUsageException::newWithMessage( $module->getMain(), 'apierror-badcontinue' );
69 }
70 $this->generatorDone = ( $continue[0] === '-' );
71 $skip = explode( '|', $continue[1] );
72 if ( !$this->generatorDone ) {
73 $params = explode( '|', $continue[0] );
74 $this->generatorParams = array_intersect_key(
75 $request->getValues(),
76 array_fill_keys( $params, true )
77 );
78 } else {
79 // When the generator is complete, don't run any modules that
80 // depend on it.
81 $skip += $this->generatedModules;
82 }
83 }
84
85 foreach ( $allModules as $module ) {
86 $name = $module->getModuleName();
87 if ( in_array( $name, $skip, true ) ) {
88 $this->allModules[$name] = false;
89 // Prevent spurious "unused parameter" warnings
90 $module->extractRequestParams();
91 } else {
92 $this->allModules[$name] = $module;
93 }
94 }
95 }
96
101 public function getSource() {
102 return $this->source;
103 }
104
108 public function isGeneratorDone() {
109 return $this->generatorDone;
110 }
111
116 public function getRunModules() {
117 return array_values( array_filter( $this->allModules ) );
118 }
119
127 public function addContinueParam( ApiBase $module, $paramName, $paramValue ) {
128 $name = $module->getModuleName();
129 if ( !isset( $this->allModules[$name] ) ) {
130 throw new UnexpectedValueException(
131 "Module '$name' called " . __METHOD__ .
132 ' but was not passed to ' . __CLASS__ . '::__construct'
133 );
134 }
135 if ( !$this->allModules[$name] ) {
136 throw new UnexpectedValueException(
137 "Module '$name' was not supposed to have been executed, but " .
138 'it was executed anyway'
139 );
140 }
141 $paramName = $module->encodeParamName( $paramName );
142 if ( is_array( $paramValue ) ) {
143 $paramValue = implode( '|', $paramValue );
144 }
145 $this->continuationData[$name][$paramName] = $paramValue;
146 }
147
159 public function addGeneratorNonContinueParam( ApiBase $module, $paramName, $paramValue ) {
160 $name = $module->getModuleName();
161 $paramName = $module->encodeParamName( $paramName );
162 if ( is_array( $paramValue ) ) {
163 $paramValue = implode( '|', $paramValue );
164 }
165 $this->generatorNonContinuationData[$name][$paramName] = $paramValue;
166 }
167
174 public function addGeneratorContinueParam( ApiBase $module, $paramName, $paramValue ) {
175 $name = $module->getModuleName();
176 $paramName = $module->encodeParamName( $paramName );
177 if ( is_array( $paramValue ) ) {
178 $paramValue = implode( '|', $paramValue );
179 }
180 $this->generatorContinuationData[$name][$paramName] = $paramValue;
181 }
182
187 public function getRawContinuation() {
188 return array_merge_recursive( $this->continuationData, $this->generatorContinuationData );
189 }
190
196 public function getRawNonContinuation() {
197 return $this->generatorNonContinuationData;
198 }
199
204 public function getContinuation() {
205 $data = [];
206 $batchcomplete = false;
207
208 $finishedModules = array_diff(
209 array_keys( $this->allModules ),
210 array_keys( $this->continuationData )
211 );
212
213 // First, grab the non-generator-using continuation data
214 $continuationData = array_diff_key( $this->continuationData, $this->generatedModules );
215 foreach ( $continuationData as $kvp ) {
216 $data += $kvp;
217 }
218
219 // Next, handle the generator-using continuation data
220 $continuationData = array_intersect_key( $this->continuationData, $this->generatedModules );
221 if ( $continuationData ) {
222 // Some modules are unfinished: include those params, and copy
223 // the generator params.
224 foreach ( $continuationData as $kvp ) {
225 $data += $kvp;
226 }
227 $generatorParams = [];
228 foreach ( $this->generatorNonContinuationData as $kvp ) {
229 $generatorParams += $kvp;
230 }
231 $generatorParams += $this->generatorParams;
232 // @phan-suppress-next-line PhanTypeInvalidLeftOperand False positive in phan
233 $data += $generatorParams;
234 $generatorKeys = implode( '|', array_keys( $generatorParams ) );
235 } elseif ( $this->generatorContinuationData ) {
236 // All the generator-using modules are complete, but the
237 // generator isn't. Continue the generator and restart the
238 // generator-using modules
239 $generatorParams = [];
240 foreach ( $this->generatorContinuationData as $kvp ) {
241 $generatorParams += $kvp;
242 }
243 $data += $generatorParams;
244 $finishedModules = array_diff( $finishedModules, $this->generatedModules );
245 $generatorKeys = implode( '|', array_keys( $generatorParams ) );
246 $batchcomplete = true;
247 } else {
248 // Generator and prop modules are all done. Mark it so.
249 $generatorKeys = '-';
250 $batchcomplete = true;
251 }
252
253 // Set 'continue' if any continuation data is set or if the generator
254 // still needs to run
255 if ( $data || $generatorKeys !== '-' ) {
256 $data['continue'] = $generatorKeys . '||' . implode( '|', $finishedModules );
257 }
258
259 return [ $data, $batchcomplete ];
260 }
261
266 public function setContinuationIntoResult( ApiResult $result ) {
267 [ $data, $batchcomplete ] = $this->getContinuation();
268 if ( $data ) {
269 $result->addValue( null, 'continue', $data,
270 ApiResult::ADD_ON_TOP | ApiResult::NO_SIZE_CHECK );
271 }
272 if ( $batchcomplete ) {
273 $result->addValue( null, 'batchcomplete', true,
274 ApiResult::ADD_ON_TOP | ApiResult::NO_SIZE_CHECK );
275 }
276 }
277}
array $params
The job parameters.
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:64
encodeParamName( $paramName)
This method mangles parameter name based on the prefix supplied to the constructor.
Definition ApiBase.php:789
getMain()
Get the main module.
Definition ApiBase.php:550
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:811
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:532
This manages continuation state.
addGeneratorNonContinueParam(ApiBase $module, $paramName, $paramValue)
Set the non-continuation parameter for the generator module.
addGeneratorContinueParam(ApiBase $module, $paramName, $paramValue)
Set the continuation parameter for the generator module.
setContinuationIntoResult(ApiResult $result)
Store the continuation data into the result.
getRunModules()
Get the list of modules that should actually be run.
addContinueParam(ApiBase $module, $paramName, $paramValue)
Set the continuation parameter for a module.
getSource()
Get the class that created this manager.
getRawNonContinuation()
Fetch raw non-continuation data.
__construct(ApiBase $module, array $allModules=[], array $generatedModules=[])
getContinuation()
Fetch continuation result data.
getRawContinuation()
Fetch raw continuation data.
This class represents the result of the API operations.
Definition ApiResult.php:35
addValue( $path, $name, $value, $flags=0)
Add value to the output data at the given path.