MediaWiki  REL1_21
ApiQueryBase.php
Go to the documentation of this file.
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                         return true;
00135                 }
00136                 return false;
00137         }
00138 
00150         protected function addWhere( $value ) {
00151                 if ( is_array( $value ) ) {
00152                         // Sanity check: don't insert empty arrays,
00153                         // Database::makeList() chokes on them
00154                         if ( count( $value ) ) {
00155                                 $this->where = array_merge( $this->where, $value );
00156                         }
00157                 } else {
00158                         $this->where[] = $value;
00159                 }
00160         }
00161 
00168         protected function addWhereIf( $value, $condition ) {
00169                 if ( $condition ) {
00170                         $this->addWhere( $value );
00171                         return true;
00172                 }
00173                 return false;
00174         }
00175 
00181         protected function addWhereFld( $field, $value ) {
00182                 // Use count() to its full documented capabilities to simultaneously
00183                 // test for null, empty array or empty countable object
00184                 if ( count( $value ) ) {
00185                         $this->where[$field] = $value;
00186                 }
00187         }
00188 
00201         protected function addWhereRange( $field, $dir, $start, $end, $sort = true ) {
00202                 $isDirNewer = ( $dir === 'newer' );
00203                 $after = ( $isDirNewer ? '>=' : '<=' );
00204                 $before = ( $isDirNewer ? '<=' : '>=' );
00205                 $db = $this->getDB();
00206 
00207                 if ( !is_null( $start ) ) {
00208                         $this->addWhere( $field . $after . $db->addQuotes( $start ) );
00209                 }
00210 
00211                 if ( !is_null( $end ) ) {
00212                         $this->addWhere( $field . $before . $db->addQuotes( $end ) );
00213                 }
00214 
00215                 if ( $sort ) {
00216                         $order = $field . ( $isDirNewer ? '' : ' DESC' );
00217                         // Append ORDER BY
00218                         $optionOrderBy = isset( $this->options['ORDER BY'] ) ? (array)$this->options['ORDER BY'] : array();
00219                         $optionOrderBy[] = $order;
00220                         $this->addOption( 'ORDER BY', $optionOrderBy );
00221                 }
00222         }
00223 
00234         protected function addTimestampWhereRange( $field, $dir, $start, $end, $sort = true ) {
00235                 $db = $this->getDb();
00236                 $this->addWhereRange( $field, $dir,
00237                         $db->timestampOrNull( $start ), $db->timestampOrNull( $end ), $sort );
00238         }
00239 
00246         protected function addOption( $name, $value = null ) {
00247                 if ( is_null( $value ) ) {
00248                         $this->options[] = $name;
00249                 } else {
00250                         $this->options[$name] = $value;
00251                 }
00252         }
00253 
00262         protected function select( $method, $extraQuery = array() ) {
00263 
00264                 $tables = array_merge( $this->tables, isset( $extraQuery['tables'] ) ? (array)$extraQuery['tables'] : array() );
00265                 $fields = array_merge( $this->fields, isset( $extraQuery['fields'] ) ? (array)$extraQuery['fields'] : array() );
00266                 $where = array_merge( $this->where, isset( $extraQuery['where'] ) ? (array)$extraQuery['where'] : array() );
00267                 $options = array_merge( $this->options, isset( $extraQuery['options'] ) ? (array)$extraQuery['options'] : array() );
00268                 $join_conds = array_merge( $this->join_conds, isset( $extraQuery['join_conds'] ) ? (array)$extraQuery['join_conds'] : array() );
00269 
00270                 // getDB has its own profileDBIn/Out calls
00271                 $db = $this->getDB();
00272 
00273                 $this->profileDBIn();
00274                 $res = $db->select( $tables, $fields, $where, $method, $options, $join_conds );
00275                 $this->profileDBOut();
00276 
00277                 return $res;
00278         }
00279 
00285         protected function checkRowCount() {
00286                 $db = $this->getDB();
00287                 $this->profileDBIn();
00288                 $rowcount = $db->estimateRowCount( $this->tables, $this->fields, $this->where, __METHOD__, $this->options );
00289                 $this->profileDBOut();
00290 
00291                 global $wgAPIMaxDBRows;
00292                 if ( $rowcount > $wgAPIMaxDBRows ) {
00293                         return false;
00294                 }
00295                 return true;
00296         }
00297 
00305         public static function addTitleInfo( &$arr, $title, $prefix = '' ) {
00306                 $arr[$prefix . 'ns'] = intval( $title->getNamespace() );
00307                 $arr[$prefix . 'title'] = $title->getPrefixedText();
00308         }
00309 
00315         public function requestExtraData( $pageSet ) {
00316         }
00317 
00322         public function getQuery() {
00323                 return $this->mQueryModule;
00324         }
00325 
00332         protected function addPageSubItems( $pageId, $data ) {
00333                 $result = $this->getResult();
00334                 $result->setIndexedTagName( $data, $this->getModulePrefix() );
00335                 return $result->addValue( array( 'query', 'pages', intval( $pageId ) ),
00336                         $this->getModuleName(),
00337                         $data );
00338         }
00339 
00348         protected function addPageSubItem( $pageId, $item, $elemname = null ) {
00349                 if ( is_null( $elemname ) ) {
00350                         $elemname = $this->getModulePrefix();
00351                 }
00352                 $result = $this->getResult();
00353                 $fit = $result->addValue( array( 'query', 'pages', $pageId,
00354                         $this->getModuleName() ), null, $item );
00355                 if ( !$fit ) {
00356                         return false;
00357                 }
00358                 $result->setIndexedTagName_internal( array( 'query', 'pages', $pageId,
00359                                 $this->getModuleName() ), $elemname );
00360                 return true;
00361         }
00362 
00368         protected function setContinueEnumParameter( $paramName, $paramValue ) {
00369                 $paramName = $this->encodeParamName( $paramName );
00370                 $msg = array( $paramName => $paramValue );
00371                 $result = $this->getResult();
00372                 $result->disableSizeCheck();
00373                 $result->addValue( 'query-continue', $this->getModuleName(), $msg, ApiResult::ADD_ON_TOP );
00374                 $result->enableSizeCheck();
00375         }
00376 
00381         protected function getDB() {
00382                 if ( is_null( $this->mDb ) ) {
00383                         $this->mDb = $this->getQuery()->getDB();
00384                 }
00385                 return $this->mDb;
00386         }
00387 
00396         public function selectNamedDB( $name, $db, $groups ) {
00397                 $this->mDb = $this->getQuery()->getNamedDB( $name, $db, $groups );
00398         }
00399 
00404         protected function getPageSet() {
00405                 return $this->getQuery()->getPageSet();
00406         }
00407 
00413         public function titleToKey( $title ) {
00414                 // Don't throw an error if we got an empty string
00415                 if ( trim( $title ) == '' ) {
00416                         return '';
00417                 }
00418                 $t = Title::newFromText( $title );
00419                 if ( !$t ) {
00420                         $this->dieUsageMsg( array( 'invalidtitle', $title ) );
00421                 }
00422                 return $t->getPrefixedDBkey();
00423         }
00424 
00430         public function keyToTitle( $key ) {
00431                 // Don't throw an error if we got an empty string
00432                 if ( trim( $key ) == '' ) {
00433                         return '';
00434                 }
00435                 $t = Title::newFromDbKey( $key );
00436                 // This really shouldn't happen but we gotta check anyway
00437                 if ( !$t ) {
00438                         $this->dieUsageMsg( array( 'invalidtitle', $key ) );
00439                 }
00440                 return $t->getPrefixedText();
00441         }
00442 
00448         public function titlePartToKey( $titlePart ) {
00449                 return substr( $this->titleToKey( $titlePart . 'x' ), 0, - 1 );
00450         }
00451 
00457         public function keyPartToTitle( $keyPart ) {
00458                 return substr( $this->keyToTitle( $keyPart . 'x' ), 0, - 1 );
00459         }
00460 
00468         public function getDirectionDescription( $p = '', $extraDirText = '' ) {
00469                 return array(
00470                                 "In which direction to enumerate{$extraDirText}",
00471                                 " newer          - List oldest first. Note: {$p}start has to be before {$p}end.",
00472                                 " older          - List newest first (default). Note: {$p}start has to be later than {$p}end.",
00473                         );
00474         }
00475 
00481         public function prepareUrlQuerySearchString( $query = null, $protocol = null) {
00482                 $db = $this->getDb();
00483                 if ( !is_null( $query ) || $query != '' ) {
00484                         if ( is_null( $protocol ) ) {
00485                                 $protocol = 'http://';
00486                         }
00487 
00488                         $likeQuery = LinkFilter::makeLikeArray( $query, $protocol );
00489                         if ( !$likeQuery ) {
00490                                 $this->dieUsage( 'Invalid query', 'bad_query' );
00491                         }
00492 
00493                         $likeQuery = LinkFilter::keepOneWildcard( $likeQuery );
00494                         return 'el_index ' . $db->buildLike( $likeQuery );
00495                 } elseif ( !is_null( $protocol ) ) {
00496                         return 'el_index ' . $db->buildLike( "$protocol", $db->anyString() );
00497                 }
00498 
00499                 return null;
00500         }
00501 
00509         public function showHiddenUsersAddBlockInfo( $showBlockInfo ) {
00510                 $userCanViewHiddenUsers = $this->getUser()->isAllowed( 'hideuser' );
00511 
00512                 if ( $showBlockInfo || !$userCanViewHiddenUsers ) {
00513                         $this->addTables( 'ipblocks' );
00514                         $this->addJoinConds( array(
00515                                 'ipblocks' => array( 'LEFT JOIN', 'ipb_user=user_id' ),
00516                         ) );
00517 
00518                         $this->addFields( 'ipb_deleted' );
00519 
00520                         if ( $showBlockInfo ) {
00521                                 $this->addFields( array( 'ipb_id', 'ipb_by', 'ipb_by_text', 'ipb_reason', 'ipb_expiry' ) );
00522                         }
00523 
00524                         // Don't show hidden names
00525                         if ( !$userCanViewHiddenUsers ) {
00526                                 $this->addWhere( 'ipb_deleted = 0 OR ipb_deleted IS NULL' );
00527                         }
00528                 }
00529         }
00530 
00535         public function validateSha1Hash( $hash ) {
00536                 return preg_match( '/^[a-f0-9]{40}$/', $hash );
00537         }
00538 
00543         public function validateSha1Base36Hash( $hash ) {
00544                 return preg_match( '/^[a-z0-9]{31}$/', $hash );
00545         }
00546 
00550         public function getPossibleErrors() {
00551                 $errors = parent::getPossibleErrors();
00552                 $errors = array_merge( $errors, array(
00553                         array( 'invalidtitle', 'title' ),
00554                         array( 'invalidtitle', 'key' ),
00555                 ) );
00556                 return $errors;
00557         }
00558 }
00559 
00563 abstract class ApiQueryGeneratorBase extends ApiQueryBase {
00564 
00565         private $mGeneratorPageSet = null;
00566 
00574         public function setGeneratorMode( ApiPageSet $generatorPageSet ) {
00575                 if ( $generatorPageSet === null ) {
00576                         ApiBase::dieDebug( __METHOD__, 'Required parameter missing - $generatorPageSet' );
00577                 }
00578                 $this->mGeneratorPageSet = $generatorPageSet;
00579         }
00580 
00586         protected function getPageSet() {
00587                 if ( $this->mGeneratorPageSet !== null ) {
00588                         return $this->mGeneratorPageSet;
00589                 }
00590                 return parent::getPageSet();
00591         }
00592 
00598         public function encodeParamName( $paramName ) {
00599                 if ( $this->mGeneratorPageSet !== null ) {
00600                         return 'g' . parent::encodeParamName( $paramName );
00601                 } else {
00602                         return parent::encodeParamName( $paramName );
00603                 }
00604         }
00605 
00612         protected function setContinueEnumParameter( $paramName, $paramValue ) {
00613                 // If this is a generator and query->setGeneratorContinue() returns false, treat as before
00614                 if ( $this->mGeneratorPageSet === null
00615                         || !$this->getQuery()->setGeneratorContinue( $this, $paramName, $paramValue )
00616                 ) {
00617                         parent::setContinueEnumParameter( $paramName, $paramValue );
00618                 }
00619         }
00620 
00626         abstract public function executeGenerator( $resultPageSet );
00627 }