MediaWiki  REL1_22
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 }