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,
271  }
272  if ( $batchcomplete ) {
273  $result->addValue( null, 'batchcomplete', true,
275  }
276  }
277 }
This abstract class implements many basic API functions, and is the base of all API classes.
Definition: ApiBase.php:63
encodeParamName( $paramName)
This method mangles parameter name based on the prefix supplied to the constructor.
Definition: ApiBase.php:786
getMain()
Get the main module.
Definition: ApiBase.php:547
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:808
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:529
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.
Definition: ApiResult.php:394
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:58
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:49
static newWithMessage(?ApiBase $module, $msg, $code=null, $data=null, $httpCode=0, Throwable $previous=null)