MediaWiki
REL1_23
|
00001 <?php 00034 abstract class ApiQueryBase extends ApiBase { 00035 00036 private $mQueryModule, $mDb, $tables, $where, $fields, $options, $join_conds; 00037 00043 public function __construct( ApiBase $query, $moduleName, $paramPrefix = '' ) { 00044 parent::__construct( $query->getMain(), $moduleName, $paramPrefix ); 00045 $this->mQueryModule = $query; 00046 $this->mDb = null; 00047 $this->resetQueryParams(); 00048 } 00049 00061 public function getCacheMode( $params ) { 00062 return 'private'; 00063 } 00064 00068 protected function resetQueryParams() { 00069 $this->tables = array(); 00070 $this->where = array(); 00071 $this->fields = array(); 00072 $this->options = array(); 00073 $this->join_conds = array(); 00074 } 00075 00082 protected function addTables( $tables, $alias = null ) { 00083 if ( is_array( $tables ) ) { 00084 if ( !is_null( $alias ) ) { 00085 ApiBase::dieDebug( __METHOD__, 'Multiple table aliases not supported' ); 00086 } 00087 $this->tables = array_merge( $this->tables, $tables ); 00088 } else { 00089 if ( !is_null( $alias ) ) { 00090 $this->tables[$alias] = $tables; 00091 } else { 00092 $this->tables[] = $tables; 00093 } 00094 } 00095 } 00096 00106 protected function addJoinConds( $join_conds ) { 00107 if ( !is_array( $join_conds ) ) { 00108 ApiBase::dieDebug( __METHOD__, 'Join conditions have to be arrays' ); 00109 } 00110 $this->join_conds = array_merge( $this->join_conds, $join_conds ); 00111 } 00112 00117 protected function addFields( $value ) { 00118 if ( is_array( $value ) ) { 00119 $this->fields = array_merge( $this->fields, $value ); 00120 } else { 00121 $this->fields[] = $value; 00122 } 00123 } 00124 00131 protected function addFieldsIf( $value, $condition ) { 00132 if ( $condition ) { 00133 $this->addFields( $value ); 00134 00135 return true; 00136 } 00137 00138 return false; 00139 } 00140 00152 protected function addWhere( $value ) { 00153 if ( is_array( $value ) ) { 00154 // Sanity check: don't insert empty arrays, 00155 // Database::makeList() chokes on them 00156 if ( count( $value ) ) { 00157 $this->where = array_merge( $this->where, $value ); 00158 } 00159 } else { 00160 $this->where[] = $value; 00161 } 00162 } 00163 00170 protected function addWhereIf( $value, $condition ) { 00171 if ( $condition ) { 00172 $this->addWhere( $value ); 00173 00174 return true; 00175 } 00176 00177 return false; 00178 } 00179 00185 protected function addWhereFld( $field, $value ) { 00186 // Use count() to its full documented capabilities to simultaneously 00187 // test for null, empty array or empty countable object 00188 if ( count( $value ) ) { 00189 $this->where[$field] = $value; 00190 } 00191 } 00192 00205 protected function addWhereRange( $field, $dir, $start, $end, $sort = true ) { 00206 $isDirNewer = ( $dir === 'newer' ); 00207 $after = ( $isDirNewer ? '>=' : '<=' ); 00208 $before = ( $isDirNewer ? '<=' : '>=' ); 00209 $db = $this->getDB(); 00210 00211 if ( !is_null( $start ) ) { 00212 $this->addWhere( $field . $after . $db->addQuotes( $start ) ); 00213 } 00214 00215 if ( !is_null( $end ) ) { 00216 $this->addWhere( $field . $before . $db->addQuotes( $end ) ); 00217 } 00218 00219 if ( $sort ) { 00220 $order = $field . ( $isDirNewer ? '' : ' DESC' ); 00221 // Append ORDER BY 00222 $optionOrderBy = isset( $this->options['ORDER BY'] ) 00223 ? (array)$this->options['ORDER BY'] 00224 : array(); 00225 $optionOrderBy[] = $order; 00226 $this->addOption( 'ORDER BY', $optionOrderBy ); 00227 } 00228 } 00229 00240 protected function addTimestampWhereRange( $field, $dir, $start, $end, $sort = true ) { 00241 $db = $this->getDb(); 00242 $this->addWhereRange( $field, $dir, 00243 $db->timestampOrNull( $start ), $db->timestampOrNull( $end ), $sort ); 00244 } 00245 00252 protected function addOption( $name, $value = null ) { 00253 if ( is_null( $value ) ) { 00254 $this->options[] = $name; 00255 } else { 00256 $this->options[$name] = $value; 00257 } 00258 } 00259 00274 protected function select( $method, $extraQuery = array() ) { 00275 00276 $tables = array_merge( 00277 $this->tables, 00278 isset( $extraQuery['tables'] ) ? (array)$extraQuery['tables'] : array() 00279 ); 00280 $fields = array_merge( 00281 $this->fields, 00282 isset( $extraQuery['fields'] ) ? (array)$extraQuery['fields'] : array() 00283 ); 00284 $where = array_merge( 00285 $this->where, 00286 isset( $extraQuery['where'] ) ? (array)$extraQuery['where'] : array() 00287 ); 00288 $options = array_merge( 00289 $this->options, 00290 isset( $extraQuery['options'] ) ? (array)$extraQuery['options'] : array() 00291 ); 00292 $join_conds = array_merge( 00293 $this->join_conds, 00294 isset( $extraQuery['join_conds'] ) ? (array)$extraQuery['join_conds'] : array() 00295 ); 00296 00297 // getDB has its own profileDBIn/Out calls 00298 $db = $this->getDB(); 00299 00300 $this->profileDBIn(); 00301 $res = $db->select( $tables, $fields, $where, $method, $options, $join_conds ); 00302 $this->profileDBOut(); 00303 00304 return $res; 00305 } 00306 00312 protected function checkRowCount() { 00313 $db = $this->getDB(); 00314 $this->profileDBIn(); 00315 $rowcount = $db->estimateRowCount( 00316 $this->tables, 00317 $this->fields, 00318 $this->where, 00319 __METHOD__, 00320 $this->options 00321 ); 00322 $this->profileDBOut(); 00323 00324 global $wgAPIMaxDBRows; 00325 if ( $rowcount > $wgAPIMaxDBRows ) { 00326 return false; 00327 } 00328 00329 return true; 00330 } 00331 00339 public static function addTitleInfo( &$arr, $title, $prefix = '' ) { 00340 $arr[$prefix . 'ns'] = intval( $title->getNamespace() ); 00341 $arr[$prefix . 'title'] = $title->getPrefixedText(); 00342 } 00343 00349 public function requestExtraData( $pageSet ) { 00350 } 00351 00356 public function getQuery() { 00357 return $this->mQueryModule; 00358 } 00359 00366 protected function addPageSubItems( $pageId, $data ) { 00367 $result = $this->getResult(); 00368 $result->setIndexedTagName( $data, $this->getModulePrefix() ); 00369 00370 return $result->addValue( array( 'query', 'pages', intval( $pageId ) ), 00371 $this->getModuleName(), 00372 $data ); 00373 } 00374 00383 protected function addPageSubItem( $pageId, $item, $elemname = null ) { 00384 if ( is_null( $elemname ) ) { 00385 $elemname = $this->getModulePrefix(); 00386 } 00387 $result = $this->getResult(); 00388 $fit = $result->addValue( array( 'query', 'pages', $pageId, 00389 $this->getModuleName() ), null, $item ); 00390 if ( !$fit ) { 00391 return false; 00392 } 00393 $result->setIndexedTagName_internal( array( 'query', 'pages', $pageId, 00394 $this->getModuleName() ), $elemname ); 00395 00396 return true; 00397 } 00398 00404 protected function setContinueEnumParameter( $paramName, $paramValue ) { 00405 $paramName = $this->encodeParamName( $paramName ); 00406 $msg = array( $paramName => $paramValue ); 00407 $result = $this->getResult(); 00408 $result->disableSizeCheck(); 00409 $result->addValue( 'query-continue', $this->getModuleName(), $msg, ApiResult::ADD_ON_TOP ); 00410 $result->enableSizeCheck(); 00411 } 00412 00417 protected function getDB() { 00418 if ( is_null( $this->mDb ) ) { 00419 $this->mDb = $this->getQuery()->getDB(); 00420 } 00421 00422 return $this->mDb; 00423 } 00424 00433 public function selectNamedDB( $name, $db, $groups ) { 00434 $this->mDb = $this->getQuery()->getNamedDB( $name, $db, $groups ); 00435 } 00436 00441 protected function getPageSet() { 00442 return $this->getQuery()->getPageSet(); 00443 } 00444 00450 public function titleToKey( $title ) { 00451 // Don't throw an error if we got an empty string 00452 if ( trim( $title ) == '' ) { 00453 return ''; 00454 } 00455 $t = Title::newFromText( $title ); 00456 if ( !$t ) { 00457 $this->dieUsageMsg( array( 'invalidtitle', $title ) ); 00458 } 00459 00460 return $t->getPrefixedDBkey(); 00461 } 00462 00468 public function keyToTitle( $key ) { 00469 // Don't throw an error if we got an empty string 00470 if ( trim( $key ) == '' ) { 00471 return ''; 00472 } 00473 $t = Title::newFromDBkey( $key ); 00474 // This really shouldn't happen but we gotta check anyway 00475 if ( !$t ) { 00476 $this->dieUsageMsg( array( 'invalidtitle', $key ) ); 00477 } 00478 00479 return $t->getPrefixedText(); 00480 } 00481 00491 public function titlePartToKey( $titlePart, $defaultNamespace = NS_MAIN ) { 00492 $t = Title::makeTitleSafe( $defaultNamespace, $titlePart . 'x' ); 00493 if ( !$t ) { 00494 $this->dieUsageMsg( array( 'invalidtitle', $titlePart ) ); 00495 } 00496 if ( $defaultNamespace != $t->getNamespace() || $t->isExternal() ) { 00497 // This can happen in two cases. First, if you call titlePartToKey with a title part 00498 // that looks like a namespace, but with $defaultNamespace = NS_MAIN. It would be very 00499 // difficult to handle such a case. Such cases cannot exist and are therefore treated 00500 // as invalid user input. The second case is when somebody specifies a title interwiki 00501 // prefix. 00502 $this->dieUsageMsg( array( 'invalidtitle', $titlePart ) ); 00503 } 00504 00505 return substr( $t->getDbKey(), 0, -1 ); 00506 } 00507 00513 public function keyPartToTitle( $keyPart ) { 00514 return substr( $this->keyToTitle( $keyPart . 'x' ), 0, -1 ); 00515 } 00516 00524 public function getDirectionDescription( $p = '', $extraDirText = '' ) { 00525 return array( 00526 "In which direction to enumerate{$extraDirText}", 00527 " newer - List oldest first. Note: {$p}start has to be before {$p}end.", 00528 " older - List newest first (default). Note: {$p}start has to be later than {$p}end.", 00529 ); 00530 } 00531 00537 public function prepareUrlQuerySearchString( $query = null, $protocol = null ) { 00538 $db = $this->getDb(); 00539 if ( !is_null( $query ) || $query != '' ) { 00540 if ( is_null( $protocol ) ) { 00541 $protocol = 'http://'; 00542 } 00543 00544 $likeQuery = LinkFilter::makeLikeArray( $query, $protocol ); 00545 if ( !$likeQuery ) { 00546 $this->dieUsage( 'Invalid query', 'bad_query' ); 00547 } 00548 00549 $likeQuery = LinkFilter::keepOneWildcard( $likeQuery ); 00550 00551 return 'el_index ' . $db->buildLike( $likeQuery ); 00552 } elseif ( !is_null( $protocol ) ) { 00553 return 'el_index ' . $db->buildLike( "$protocol", $db->anyString() ); 00554 } 00555 00556 return null; 00557 } 00558 00566 public function showHiddenUsersAddBlockInfo( $showBlockInfo ) { 00567 $this->addTables( 'ipblocks' ); 00568 $this->addJoinConds( array( 00569 'ipblocks' => array( 'LEFT JOIN', 'ipb_user=user_id' ), 00570 ) ); 00571 00572 $this->addFields( 'ipb_deleted' ); 00573 00574 if ( $showBlockInfo ) { 00575 $this->addFields( array( 'ipb_id', 'ipb_by', 'ipb_by_text', 'ipb_reason', 'ipb_expiry' ) ); 00576 } 00577 00578 // Don't show hidden names 00579 if ( !$this->getUser()->isAllowed( 'hideuser' ) ) { 00580 $this->addWhere( 'ipb_deleted = 0 OR ipb_deleted IS NULL' ); 00581 } 00582 } 00583 00588 public function validateSha1Hash( $hash ) { 00589 return preg_match( '/^[a-f0-9]{40}$/', $hash ); 00590 } 00591 00596 public function validateSha1Base36Hash( $hash ) { 00597 return preg_match( '/^[a-z0-9]{31}$/', $hash ); 00598 } 00599 00603 public function getPossibleErrors() { 00604 $errors = parent::getPossibleErrors(); 00605 $errors = array_merge( $errors, array( 00606 array( 'invalidtitle', 'title' ), 00607 array( 'invalidtitle', 'key' ), 00608 ) ); 00609 00610 return $errors; 00611 } 00612 00618 public function userCanSeeRevDel() { 00619 return $this->getUser()->isAllowedAny( 'deletedhistory', 'deletedtext', 'suppressrevision' ); 00620 } 00621 } 00622 00626 abstract class ApiQueryGeneratorBase extends ApiQueryBase { 00627 00628 private $mGeneratorPageSet = null; 00629 00637 public function setGeneratorMode( ApiPageSet $generatorPageSet ) { 00638 if ( $generatorPageSet === null ) { 00639 ApiBase::dieDebug( __METHOD__, 'Required parameter missing - $generatorPageSet' ); 00640 } 00641 $this->mGeneratorPageSet = $generatorPageSet; 00642 } 00643 00649 protected function getPageSet() { 00650 if ( $this->mGeneratorPageSet !== null ) { 00651 return $this->mGeneratorPageSet; 00652 } 00653 00654 return parent::getPageSet(); 00655 } 00656 00662 public function encodeParamName( $paramName ) { 00663 if ( $this->mGeneratorPageSet !== null ) { 00664 return 'g' . parent::encodeParamName( $paramName ); 00665 } else { 00666 return parent::encodeParamName( $paramName ); 00667 } 00668 } 00669 00676 protected function setContinueEnumParameter( $paramName, $paramValue ) { 00677 // If this is a generator and query->setGeneratorContinue() returns false, treat as before 00678 if ( $this->mGeneratorPageSet === null 00679 || !$this->getQuery()->setGeneratorContinue( $this, $paramName, $paramValue ) 00680 ) { 00681 parent::setContinueEnumParameter( $paramName, $paramValue ); 00682 } 00683 } 00684 00690 abstract public function executeGenerator( $resultPageSet ); 00691 }