MediaWiki
REL1_24
|
00001 <?php 00046 class ApiResult extends ApiBase { 00047 00052 const OVERRIDE = 1; 00053 00059 const ADD_ON_TOP = 2; 00060 00067 const NO_SIZE_CHECK = 4; 00068 00069 private $mData, $mIsRawMode, $mSize, $mCheckingSize; 00070 00071 private $continueAllModules = array(); 00072 private $continueGeneratedModules = array(); 00073 private $continuationData = array(); 00074 private $generatorContinuationData = array(); 00075 private $generatorParams = array(); 00076 private $generatorDone = false; 00077 00081 public function __construct( ApiMain $main ) { 00082 parent::__construct( $main, 'result' ); 00083 $this->mIsRawMode = false; 00084 $this->mCheckingSize = true; 00085 $this->reset(); 00086 } 00087 00091 public function reset() { 00092 $this->mData = array(); 00093 $this->mSize = 0; 00094 } 00095 00102 public function setRawMode( $flag = true ) { 00103 $this->mIsRawMode = $flag; 00104 } 00105 00110 public function getIsRawMode() { 00111 return $this->mIsRawMode; 00112 } 00113 00118 public function getData() { 00119 return $this->mData; 00120 } 00121 00128 public static function size( $value ) { 00129 $s = 0; 00130 if ( is_array( $value ) ) { 00131 foreach ( $value as $v ) { 00132 $s += self::size( $v ); 00133 } 00134 } elseif ( !is_object( $value ) ) { 00135 // Objects can't always be cast to string 00136 $s = strlen( $value ); 00137 } 00138 00139 return $s; 00140 } 00141 00146 public function getSize() { 00147 return $this->mSize; 00148 } 00149 00156 public function disableSizeCheck() { 00157 $this->mCheckingSize = false; 00158 } 00159 00164 public function enableSizeCheck() { 00165 $this->mCheckingSize = true; 00166 } 00167 00181 public static function setElement( &$arr, $name, $value, $flags = 0 ) { 00182 if ( $arr === null || $name === null || $value === null 00183 || !is_array( $arr ) || is_array( $name ) 00184 ) { 00185 ApiBase::dieDebug( __METHOD__, 'Bad parameter' ); 00186 } 00187 00188 $exists = isset( $arr[$name] ); 00189 if ( !$exists || ( $flags & ApiResult::OVERRIDE ) ) { 00190 if ( !$exists && ( $flags & ApiResult::ADD_ON_TOP ) ) { 00191 $arr = array( $name => $value ) + $arr; 00192 } else { 00193 $arr[$name] = $value; 00194 } 00195 } elseif ( is_array( $arr[$name] ) && is_array( $value ) ) { 00196 $merged = array_intersect_key( $arr[$name], $value ); 00197 if ( !count( $merged ) ) { 00198 $arr[$name] += $value; 00199 } else { 00200 ApiBase::dieDebug( __METHOD__, "Attempting to merge element $name" ); 00201 } 00202 } else { 00203 ApiBase::dieDebug( 00204 __METHOD__, 00205 "Attempting to add element $name=$value, existing value is {$arr[$name]}" 00206 ); 00207 } 00208 } 00209 00219 public static function setContent( &$arr, $value, $subElemName = null ) { 00220 if ( is_array( $value ) ) { 00221 ApiBase::dieDebug( __METHOD__, 'Bad parameter' ); 00222 } 00223 if ( is_null( $subElemName ) ) { 00224 ApiResult::setElement( $arr, '*', $value ); 00225 } else { 00226 if ( !isset( $arr[$subElemName] ) ) { 00227 $arr[$subElemName] = array(); 00228 } 00229 ApiResult::setElement( $arr[$subElemName], '*', $value ); 00230 } 00231 } 00232 00239 public function setSubelements( &$arr, $names ) { 00240 // In raw mode, add the '_subelements', otherwise just ignore 00241 if ( !$this->getIsRawMode() ) { 00242 return; 00243 } 00244 if ( $arr === null || $names === null || !is_array( $arr ) ) { 00245 ApiBase::dieDebug( __METHOD__, 'Bad parameter' ); 00246 } 00247 if ( !is_array( $names ) ) { 00248 $names = array( $names ); 00249 } 00250 if ( !isset( $arr['_subelements'] ) ) { 00251 $arr['_subelements'] = $names; 00252 } else { 00253 $arr['_subelements'] = array_merge( $arr['_subelements'], $names ); 00254 } 00255 } 00256 00264 public function setIndexedTagName( &$arr, $tag ) { 00265 // In raw mode, add the '_element', otherwise just ignore 00266 if ( !$this->getIsRawMode() ) { 00267 return; 00268 } 00269 if ( $arr === null || $tag === null || !is_array( $arr ) || is_array( $tag ) ) { 00270 ApiBase::dieDebug( __METHOD__, 'Bad parameter' ); 00271 } 00272 // Do not use setElement() as it is ok to call this more than once 00273 $arr['_element'] = $tag; 00274 } 00275 00281 public function setIndexedTagName_recursive( &$arr, $tag ) { 00282 if ( !is_array( $arr ) ) { 00283 return; 00284 } 00285 foreach ( $arr as &$a ) { 00286 if ( !is_array( $a ) ) { 00287 continue; 00288 } 00289 $this->setIndexedTagName( $a, $tag ); 00290 $this->setIndexedTagName_recursive( $a, $tag ); 00291 } 00292 } 00293 00301 public function setIndexedTagName_internal( $path, $tag ) { 00302 $data = &$this->mData; 00303 foreach ( (array)$path as $p ) { 00304 if ( !isset( $data[$p] ) ) { 00305 $data[$p] = array(); 00306 } 00307 $data = &$data[$p]; 00308 } 00309 if ( is_null( $data ) ) { 00310 return; 00311 } 00312 $this->setIndexedTagName( $data, $tag ); 00313 } 00314 00332 public function addValue( $path, $name, $value, $flags = 0 ) { 00333 $data = &$this->mData; 00334 if ( $this->mCheckingSize && !( $flags & ApiResult::NO_SIZE_CHECK ) ) { 00335 $newsize = $this->mSize + self::size( $value ); 00336 $maxResultSize = $this->getConfig()->get( 'APIMaxResultSize' ); 00337 if ( $newsize > $maxResultSize ) { 00338 $this->setWarning( 00339 "This result was truncated because it would otherwise be larger than the " . 00340 "limit of {$maxResultSize} bytes" ); 00341 00342 return false; 00343 } 00344 $this->mSize = $newsize; 00345 } 00346 00347 $addOnTop = $flags & ApiResult::ADD_ON_TOP; 00348 if ( $path !== null ) { 00349 foreach ( (array)$path as $p ) { 00350 if ( !isset( $data[$p] ) ) { 00351 if ( $addOnTop ) { 00352 $data = array( $p => array() ) + $data; 00353 $addOnTop = false; 00354 } else { 00355 $data[$p] = array(); 00356 } 00357 } 00358 $data = &$data[$p]; 00359 } 00360 } 00361 00362 if ( !$name ) { 00363 // Add list element 00364 if ( $addOnTop ) { 00365 // This element needs to be inserted in the beginning 00366 // Numerical indexes will be renumbered 00367 array_unshift( $data, $value ); 00368 } else { 00369 // Add new value at the end 00370 $data[] = $value; 00371 } 00372 } else { 00373 // Add named element 00374 self::setElement( $data, $name, $value, $flags ); 00375 } 00376 00377 return true; 00378 } 00379 00386 public function setParsedLimit( $moduleName, $limit ) { 00387 // Add value, allowing overwriting 00388 $this->addValue( 'limits', $moduleName, $limit, ApiResult::OVERRIDE ); 00389 } 00390 00398 public function unsetValue( $path, $name ) { 00399 $data = &$this->mData; 00400 if ( $path !== null ) { 00401 foreach ( (array)$path as $p ) { 00402 if ( !isset( $data[$p] ) ) { 00403 return; 00404 } 00405 $data = &$data[$p]; 00406 } 00407 } 00408 $this->mSize -= self::size( $data[$name] ); 00409 unset( $data[$name] ); 00410 } 00411 00415 public function cleanUpUTF8() { 00416 array_walk_recursive( $this->mData, array( 'ApiResult', 'cleanUp_helper' ) ); 00417 } 00418 00424 private static function cleanUp_helper( &$s ) { 00425 if ( !is_string( $s ) ) { 00426 return; 00427 } 00428 global $wgContLang; 00429 $s = $wgContLang->normalize( $s ); 00430 } 00431 00438 public function convertStatusToArray( $status, $errorType = 'error' ) { 00439 if ( $status->isGood() ) { 00440 return array(); 00441 } 00442 00443 $result = array(); 00444 foreach ( $status->getErrorsByType( $errorType ) as $error ) { 00445 $this->setIndexedTagName( $error['params'], 'param' ); 00446 $result[] = $error; 00447 } 00448 $this->setIndexedTagName( $result, $errorType ); 00449 00450 return $result; 00451 } 00452 00453 public function execute() { 00454 ApiBase::dieDebug( __METHOD__, 'execute() is not supported on Result object' ); 00455 } 00456 00469 public function beginContinuation( 00470 $continue, array $allModules = array(), array $generatedModules = array() 00471 ) { 00472 $this->continueGeneratedModules = $generatedModules 00473 ? array_combine( $generatedModules, $generatedModules ) 00474 : array(); 00475 $this->continuationData = array(); 00476 $this->generatorContinuationData = array(); 00477 $this->generatorParams = array(); 00478 00479 $skip = array(); 00480 if ( is_string( $continue ) && $continue !== '' ) { 00481 $continue = explode( '||', $continue ); 00482 $this->dieContinueUsageIf( count( $continue ) !== 2 ); 00483 $this->generatorDone = ( $continue[0] === '-' ); 00484 if ( !$this->generatorDone ) { 00485 $this->generatorParams = explode( '|', $continue[0] ); 00486 } 00487 $skip = explode( '|', $continue[1] ); 00488 } 00489 00490 $this->continueAllModules = array(); 00491 $runModules = array(); 00492 foreach ( $allModules as $module ) { 00493 $name = $module->getModuleName(); 00494 if ( in_array( $name, $skip ) ) { 00495 $this->continueAllModules[$name] = false; 00496 // Prevent spurious "unused parameter" warnings 00497 $module->extractRequestParams(); 00498 } else { 00499 $this->continueAllModules[$name] = true; 00500 $runModules[] = $module; 00501 } 00502 } 00503 00504 return array( 00505 $this->generatorDone, 00506 $runModules, 00507 ); 00508 } 00509 00518 public function setContinueParam( ApiBase $module, $paramName, $paramValue ) { 00519 $name = $module->getModuleName(); 00520 if ( !isset( $this->continueAllModules[$name] ) ) { 00521 throw new MWException( 00522 "Module '$name' called ApiResult::setContinueParam but was not " . 00523 'passed to ApiResult::beginContinuation' 00524 ); 00525 } 00526 if ( !$this->continueAllModules[$name] ) { 00527 throw new MWException( 00528 "Module '$name' was not supposed to have been executed, but " . 00529 'it was executed anyway' 00530 ); 00531 } 00532 $paramName = $module->encodeParamName( $paramName ); 00533 if ( is_array( $paramValue ) ) { 00534 $paramValue = join( '|', $paramValue ); 00535 } 00536 $this->continuationData[$name][$paramName] = $paramValue; 00537 } 00538 00547 public function setGeneratorContinueParam( ApiBase $module, $paramName, $paramValue ) { 00548 $name = $module->getModuleName(); 00549 $paramName = $module->encodeParamName( $paramName ); 00550 if ( is_array( $paramValue ) ) { 00551 $paramValue = join( '|', $paramValue ); 00552 } 00553 $this->generatorContinuationData[$name][$paramName] = $paramValue; 00554 } 00555 00563 public function endContinuation( $style = 'standard' ) { 00564 if ( $style === 'raw' ) { 00565 $key = 'query-continue'; 00566 $data = array_merge_recursive( 00567 $this->continuationData, $this->generatorContinuationData 00568 ); 00569 } else { 00570 $key = 'continue'; 00571 $data = array(); 00572 00573 $finishedModules = array_diff( 00574 array_keys( $this->continueAllModules ), 00575 array_keys( $this->continuationData ) 00576 ); 00577 00578 // First, grab the non-generator-using continuation data 00579 $continuationData = array_diff_key( 00580 $this->continuationData, $this->continueGeneratedModules 00581 ); 00582 foreach ( $continuationData as $module => $kvp ) { 00583 $data += $kvp; 00584 } 00585 00586 // Next, handle the generator-using continuation data 00587 $continuationData = array_intersect_key( 00588 $this->continuationData, $this->continueGeneratedModules 00589 ); 00590 if ( $continuationData ) { 00591 // Some modules are unfinished: include those params, and copy 00592 // the generator params. 00593 foreach ( $continuationData as $module => $kvp ) { 00594 $data += $kvp; 00595 } 00596 $data += array_intersect_key( 00597 $this->getMain()->getRequest()->getValues(), 00598 array_flip( $this->generatorParams ) 00599 ); 00600 } elseif ( $this->generatorContinuationData ) { 00601 // All the generator-using modules are complete, but the 00602 // generator isn't. Continue the generator and restart the 00603 // generator-using modules 00604 $this->generatorParams = array(); 00605 foreach ( $this->generatorContinuationData as $kvp ) { 00606 $this->generatorParams = array_merge( 00607 $this->generatorParams, array_keys( $kvp ) 00608 ); 00609 $data += $kvp; 00610 } 00611 $finishedModules = array_diff( 00612 $finishedModules, $this->continueGeneratedModules 00613 ); 00614 } else { 00615 // Generator and prop modules are all done. Mark it so. 00616 $this->generatorDone = true; 00617 } 00618 00619 // Set 'continue' if any continuation data is set or if the generator 00620 // still needs to run 00621 if ( $data || !$this->generatorDone ) { 00622 $data['continue'] = 00623 ( $this->generatorDone ? '-' : join( '|', $this->generatorParams ) ) . 00624 '||' . join( '|', $finishedModules ); 00625 } 00626 } 00627 if ( $data ) { 00628 $this->addValue( null, $key, $data, ApiResult::ADD_ON_TOP | ApiResult::NO_SIZE_CHECK ); 00629 } 00630 } 00631 }