MediaWiki  master
ApiContinuationManager.php
Go to the documentation of this file.
1 <?php
27  private $source;
28 
29  private $allModules = [];
30  private $generatedModules = [];
31 
32  private $continuationData = [];
35 
36  private $generatorParams = [];
37  private $generatorDone = false;
38 
45  public function __construct(
46  ApiBase $module, array $allModules = [], array $generatedModules = []
47  ) {
48  $this->source = get_class( $module );
49  $request = $module->getRequest();
50 
51  $this->generatedModules = $generatedModules
52  ? array_combine( $generatedModules, $generatedModules )
53  : [];
54 
55  $skip = [];
56  $continue = $request->getVal( 'continue', '' );
57  if ( $continue !== '' ) {
58  $continue = explode( '||', $continue );
59  if ( count( $continue ) !== 2 ) {
60  throw ApiUsageException::newWithMessage( $module->getMain(), 'apierror-badcontinue' );
61  }
62  $this->generatorDone = ( $continue[0] === '-' );
63  $skip = explode( '|', $continue[1] );
64  if ( !$this->generatorDone ) {
65  $params = explode( '|', $continue[0] );
66  if ( $params ) {
67  $this->generatorParams = array_intersect_key(
68  $request->getValues(),
69  array_flip( $params )
70  );
71  }
72  } else {
73  // When the generator is complete, don't run any modules that
74  // depend on it.
75  $skip += $this->generatedModules;
76  }
77  }
78 
79  foreach ( $allModules as $module ) {
80  $name = $module->getModuleName();
81  if ( in_array( $name, $skip, true ) ) {
82  $this->allModules[$name] = false;
83  // Prevent spurious "unused parameter" warnings
84  $module->extractRequestParams();
85  } else {
86  $this->allModules[$name] = $module;
87  }
88  }
89  }
90 
95  public function getSource() {
96  return $this->source;
97  }
98 
103  public function isGeneratorDone() {
104  return $this->generatorDone;
105  }
106 
111  public function getRunModules() {
112  return array_values( array_filter( $this->allModules ) );
113  }
114 
122  public function addContinueParam( ApiBase $module, $paramName, $paramValue ) {
123  $name = $module->getModuleName();
124  if ( !isset( $this->allModules[$name] ) ) {
125  throw new UnexpectedValueException(
126  "Module '$name' called " . __METHOD__ .
127  ' but was not passed to ' . __CLASS__ . '::__construct'
128  );
129  }
130  if ( !$this->allModules[$name] ) {
131  throw new UnexpectedValueException(
132  "Module '$name' was not supposed to have been executed, but " .
133  'it was executed anyway'
134  );
135  }
136  $paramName = $module->encodeParamName( $paramName );
137  if ( is_array( $paramValue ) ) {
138  $paramValue = implode( '|', $paramValue );
139  }
140  $this->continuationData[$name][$paramName] = $paramValue;
141  }
142 
154  public function addGeneratorNonContinueParam( ApiBase $module, $paramName, $paramValue ) {
155  $name = $module->getModuleName();
156  $paramName = $module->encodeParamName( $paramName );
157  if ( is_array( $paramValue ) ) {
158  $paramValue = implode( '|', $paramValue );
159  }
160  $this->generatorNonContinuationData[$name][$paramName] = $paramValue;
161  }
162 
169  public function addGeneratorContinueParam( ApiBase $module, $paramName, $paramValue ) {
170  $name = $module->getModuleName();
171  $paramName = $module->encodeParamName( $paramName );
172  if ( is_array( $paramValue ) ) {
173  $paramValue = implode( '|', $paramValue );
174  }
175  $this->generatorContinuationData[$name][$paramName] = $paramValue;
176  }
177 
182  public function getRawContinuation() {
183  return array_merge_recursive( $this->continuationData, $this->generatorContinuationData );
184  }
185 
191  public function getRawNonContinuation() {
193  }
194 
199  public function getContinuation() {
200  $data = [];
201  $batchcomplete = false;
202 
203  $finishedModules = array_diff(
204  array_keys( $this->allModules ),
205  array_keys( $this->continuationData )
206  );
207 
208  // First, grab the non-generator-using continuation data
209  $continuationData = array_diff_key( $this->continuationData, $this->generatedModules );
210  foreach ( $continuationData as $module => $kvp ) {
211  $data += $kvp;
212  }
213 
214  // Next, handle the generator-using continuation data
215  $continuationData = array_intersect_key( $this->continuationData, $this->generatedModules );
216  if ( $continuationData ) {
217  // Some modules are unfinished: include those params, and copy
218  // the generator params.
219  foreach ( $continuationData as $module => $kvp ) {
220  // XXX: Not sure why phan is complaining here...
221  // @phan-suppress-next-line PhanTypeInvalidLeftOperand
222  $data += $kvp;
223  }
224  $generatorParams = [];
225  foreach ( $this->generatorNonContinuationData as $kvp ) {
226  $generatorParams += $kvp;
227  }
229  $data += $generatorParams;
230  $generatorKeys = implode( '|', array_keys( $generatorParams ) );
231  } elseif ( $this->generatorContinuationData ) {
232  // All the generator-using modules are complete, but the
233  // generator isn't. Continue the generator and restart the
234  // generator-using modules
235  $generatorParams = [];
236  foreach ( $this->generatorContinuationData as $kvp ) {
237  $generatorParams += $kvp;
238  }
239  $data += $generatorParams;
240  $finishedModules = array_diff( $finishedModules, $this->generatedModules );
241  $generatorKeys = implode( '|', array_keys( $generatorParams ) );
242  $batchcomplete = true;
243  } else {
244  // Generator and prop modules are all done. Mark it so.
245  $generatorKeys = '-';
246  $batchcomplete = true;
247  }
248 
249  // Set 'continue' if any continuation data is set or if the generator
250  // still needs to run
251  if ( $data || $generatorKeys !== '-' ) {
252  $data['continue'] = $generatorKeys . '||' . implode( '|', $finishedModules );
253  }
254 
255  return [ $data, $batchcomplete ];
256  }
257 
262  public function setContinuationIntoResult( ApiResult $result ) {
263  list( $data, $batchcomplete ) = $this->getContinuation();
264  if ( $data ) {
265  $result->addValue( null, 'continue', $data,
267  }
268  if ( $batchcomplete ) {
269  $result->addValue( null, 'batchcomplete', true,
271  }
272  }
273 }
getMain()
Get the main module.
Definition: ApiBase.php:536
This manages continuation state.
addValue( $path, $name, $value, $flags=0)
Add value to the output data at the given path.
Definition: ApiResult.php:405
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
addGeneratorNonContinueParam(ApiBase $module, $paramName, $paramValue)
Set the non-continuation parameter for the generator module.
static newWithMessage(?ApiBase $module, $msg, $code=null, $data=null, $httpCode=0)
addContinueParam(ApiBase $module, $paramName, $paramValue)
Set the continuation parameter for a module.
isGeneratorDone()
Is the generator done?
const NO_SIZE_CHECK
For addValue() and similar functions, do not check size while adding a value Don&#39;t use this unless yo...
Definition: ApiResult.php:58
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:520
This class represents the result of the API operations.
Definition: ApiResult.php:35
__construct(ApiBase $module, array $allModules=[], array $generatedModules=[])
getRawContinuation()
Fetch raw continuation data.
getSource()
Get the class that created this manager.
addGeneratorContinueParam(ApiBase $module, $paramName, $paramValue)
Set the continuation parameter for the generator module.
encodeParamName( $paramName)
This method mangles parameter name based on the prefix supplied to the constructor.
Definition: ApiBase.php:739
setContinuationIntoResult(ApiResult $result)
Store the continuation data into the result.
This abstract class implements many basic API functions, and is the base of all API classes...
Definition: ApiBase.php:42
getRunModules()
Get the list of modules that should actually be run.
getRawNonContinuation()
Fetch raw non-continuation data.
getContinuation()
Fetch continuation result data.