MediaWiki  REL1_24
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( ApiQuery $queryModule, $moduleName, $paramPrefix = '' ) {
00044         parent::__construct( $queryModule->getMain(), $moduleName, $paramPrefix );
00045         $this->mQueryModule = $queryModule;
00046         $this->mDb = null;
00047         $this->resetQueryParams();
00048     }
00049 
00050     /************************************************************************/
00066     public function getCacheMode( $params ) {
00067         return 'private';
00068     }
00069 
00075     public function requestExtraData( $pageSet ) {
00076     }
00077 
00080     /************************************************************************/
00089     public function getQuery() {
00090         return $this->mQueryModule;
00091     }
00092 
00097     protected function getDB() {
00098         if ( is_null( $this->mDb ) ) {
00099             $this->mDb = $this->getQuery()->getDB();
00100         }
00101 
00102         return $this->mDb;
00103     }
00104 
00113     public function selectNamedDB( $name, $db, $groups ) {
00114         $this->mDb = $this->getQuery()->getNamedDB( $name, $db, $groups );
00115     }
00116 
00121     protected function getPageSet() {
00122         return $this->getQuery()->getPageSet();
00123     }
00124 
00127     /************************************************************************/
00135     protected function resetQueryParams() {
00136         $this->tables = array();
00137         $this->where = array();
00138         $this->fields = array();
00139         $this->options = array();
00140         $this->join_conds = array();
00141     }
00142 
00149     protected function addTables( $tables, $alias = null ) {
00150         if ( is_array( $tables ) ) {
00151             if ( !is_null( $alias ) ) {
00152                 ApiBase::dieDebug( __METHOD__, 'Multiple table aliases not supported' );
00153             }
00154             $this->tables = array_merge( $this->tables, $tables );
00155         } else {
00156             if ( !is_null( $alias ) ) {
00157                 $this->tables[$alias] = $tables;
00158             } else {
00159                 $this->tables[] = $tables;
00160             }
00161         }
00162     }
00163 
00173     protected function addJoinConds( $join_conds ) {
00174         if ( !is_array( $join_conds ) ) {
00175             ApiBase::dieDebug( __METHOD__, 'Join conditions have to be arrays' );
00176         }
00177         $this->join_conds = array_merge( $this->join_conds, $join_conds );
00178     }
00179 
00184     protected function addFields( $value ) {
00185         if ( is_array( $value ) ) {
00186             $this->fields = array_merge( $this->fields, $value );
00187         } else {
00188             $this->fields[] = $value;
00189         }
00190     }
00191 
00198     protected function addFieldsIf( $value, $condition ) {
00199         if ( $condition ) {
00200             $this->addFields( $value );
00201 
00202             return true;
00203         }
00204 
00205         return false;
00206     }
00207 
00219     protected function addWhere( $value ) {
00220         if ( is_array( $value ) ) {
00221             // Sanity check: don't insert empty arrays,
00222             // Database::makeList() chokes on them
00223             if ( count( $value ) ) {
00224                 $this->where = array_merge( $this->where, $value );
00225             }
00226         } else {
00227             $this->where[] = $value;
00228         }
00229     }
00230 
00237     protected function addWhereIf( $value, $condition ) {
00238         if ( $condition ) {
00239             $this->addWhere( $value );
00240 
00241             return true;
00242         }
00243 
00244         return false;
00245     }
00246 
00252     protected function addWhereFld( $field, $value ) {
00253         // Use count() to its full documented capabilities to simultaneously
00254         // test for null, empty array or empty countable object
00255         if ( count( $value ) ) {
00256             $this->where[$field] = $value;
00257         }
00258     }
00259 
00272     protected function addWhereRange( $field, $dir, $start, $end, $sort = true ) {
00273         $isDirNewer = ( $dir === 'newer' );
00274         $after = ( $isDirNewer ? '>=' : '<=' );
00275         $before = ( $isDirNewer ? '<=' : '>=' );
00276         $db = $this->getDB();
00277 
00278         if ( !is_null( $start ) ) {
00279             $this->addWhere( $field . $after . $db->addQuotes( $start ) );
00280         }
00281 
00282         if ( !is_null( $end ) ) {
00283             $this->addWhere( $field . $before . $db->addQuotes( $end ) );
00284         }
00285 
00286         if ( $sort ) {
00287             $order = $field . ( $isDirNewer ? '' : ' DESC' );
00288             // Append ORDER BY
00289             $optionOrderBy = isset( $this->options['ORDER BY'] )
00290                 ? (array)$this->options['ORDER BY']
00291                 : array();
00292             $optionOrderBy[] = $order;
00293             $this->addOption( 'ORDER BY', $optionOrderBy );
00294         }
00295     }
00296 
00307     protected function addTimestampWhereRange( $field, $dir, $start, $end, $sort = true ) {
00308         $db = $this->getDb();
00309         $this->addWhereRange( $field, $dir,
00310             $db->timestampOrNull( $start ), $db->timestampOrNull( $end ), $sort );
00311     }
00312 
00319     protected function addOption( $name, $value = null ) {
00320         if ( is_null( $value ) ) {
00321             $this->options[] = $name;
00322         } else {
00323             $this->options[$name] = $value;
00324         }
00325     }
00326 
00341     protected function select( $method, $extraQuery = array() ) {
00342 
00343         $tables = array_merge(
00344             $this->tables,
00345             isset( $extraQuery['tables'] ) ? (array)$extraQuery['tables'] : array()
00346         );
00347         $fields = array_merge(
00348             $this->fields,
00349             isset( $extraQuery['fields'] ) ? (array)$extraQuery['fields'] : array()
00350         );
00351         $where = array_merge(
00352             $this->where,
00353             isset( $extraQuery['where'] ) ? (array)$extraQuery['where'] : array()
00354         );
00355         $options = array_merge(
00356             $this->options,
00357             isset( $extraQuery['options'] ) ? (array)$extraQuery['options'] : array()
00358         );
00359         $join_conds = array_merge(
00360             $this->join_conds,
00361             isset( $extraQuery['join_conds'] ) ? (array)$extraQuery['join_conds'] : array()
00362         );
00363 
00364         // getDB has its own profileDBIn/Out calls
00365         $db = $this->getDB();
00366 
00367         $this->profileDBIn();
00368         $res = $db->select( $tables, $fields, $where, $method, $options, $join_conds );
00369         $this->profileDBOut();
00370 
00371         return $res;
00372     }
00373 
00379     public function prepareUrlQuerySearchString( $query = null, $protocol = null ) {
00380         $db = $this->getDb();
00381         if ( !is_null( $query ) || $query != '' ) {
00382             if ( is_null( $protocol ) ) {
00383                 $protocol = 'http://';
00384             }
00385 
00386             $likeQuery = LinkFilter::makeLikeArray( $query, $protocol );
00387             if ( !$likeQuery ) {
00388                 $this->dieUsage( 'Invalid query', 'bad_query' );
00389             }
00390 
00391             $likeQuery = LinkFilter::keepOneWildcard( $likeQuery );
00392 
00393             return 'el_index ' . $db->buildLike( $likeQuery );
00394         } elseif ( !is_null( $protocol ) ) {
00395             return 'el_index ' . $db->buildLike( "$protocol", $db->anyString() );
00396         }
00397 
00398         return null;
00399     }
00400 
00408     public function showHiddenUsersAddBlockInfo( $showBlockInfo ) {
00409         $this->addTables( 'ipblocks' );
00410         $this->addJoinConds( array(
00411             'ipblocks' => array( 'LEFT JOIN', 'ipb_user=user_id' ),
00412         ) );
00413 
00414         $this->addFields( 'ipb_deleted' );
00415 
00416         if ( $showBlockInfo ) {
00417             $this->addFields( array( 'ipb_id', 'ipb_by', 'ipb_by_text', 'ipb_reason', 'ipb_expiry', 'ipb_timestamp' ) );
00418         }
00419 
00420         // Don't show hidden names
00421         if ( !$this->getUser()->isAllowed( 'hideuser' ) ) {
00422             $this->addWhere( 'ipb_deleted = 0 OR ipb_deleted IS NULL' );
00423         }
00424     }
00425 
00428     /************************************************************************/
00440     public static function addTitleInfo( &$arr, $title, $prefix = '' ) {
00441         $arr[$prefix . 'ns'] = intval( $title->getNamespace() );
00442         $arr[$prefix . 'title'] = $title->getPrefixedText();
00443     }
00444 
00451     protected function addPageSubItems( $pageId, $data ) {
00452         $result = $this->getResult();
00453         $result->setIndexedTagName( $data, $this->getModulePrefix() );
00454 
00455         return $result->addValue( array( 'query', 'pages', intval( $pageId ) ),
00456             $this->getModuleName(),
00457             $data );
00458     }
00459 
00468     protected function addPageSubItem( $pageId, $item, $elemname = null ) {
00469         if ( is_null( $elemname ) ) {
00470             $elemname = $this->getModulePrefix();
00471         }
00472         $result = $this->getResult();
00473         $fit = $result->addValue( array( 'query', 'pages', $pageId,
00474             $this->getModuleName() ), null, $item );
00475         if ( !$fit ) {
00476             return false;
00477         }
00478         $result->setIndexedTagName_internal( array( 'query', 'pages', $pageId,
00479             $this->getModuleName() ), $elemname );
00480 
00481         return true;
00482     }
00483 
00489     protected function setContinueEnumParameter( $paramName, $paramValue ) {
00490         $this->getResult()->setContinueParam( $this, $paramName, $paramValue );
00491     }
00492 
00503     public function titlePartToKey( $titlePart, $namespace = NS_MAIN ) {
00504         $t = Title::makeTitleSafe( $namespace, $titlePart . 'x' );
00505         if ( !$t ) {
00506             $this->dieUsageMsg( array( 'invalidtitle', $titlePart ) );
00507         }
00508         if ( $namespace != $t->getNamespace() || $t->isExternal() ) {
00509             // This can happen in two cases. First, if you call titlePartToKey with a title part
00510             // that looks like a namespace, but with $defaultNamespace = NS_MAIN. It would be very
00511             // difficult to handle such a case. Such cases cannot exist and are therefore treated
00512             // as invalid user input. The second case is when somebody specifies a title interwiki
00513             // prefix.
00514             $this->dieUsageMsg( array( 'invalidtitle', $titlePart ) );
00515         }
00516 
00517         return substr( $t->getDbKey(), 0, -1 );
00518     }
00519 
00527     public function getDirectionDescription( $p = '', $extraDirText = '' ) {
00528         return array(
00529             "In which direction to enumerate{$extraDirText}",
00530             " newer          - List oldest first. Note: {$p}start has to be before {$p}end.",
00531             " older          - List newest first (default). Note: {$p}start has to be later than {$p}end.",
00532         );
00533     }
00534 
00539     public function validateSha1Hash( $hash ) {
00540         return preg_match( '/^[a-f0-9]{40}$/', $hash );
00541     }
00542 
00547     public function validateSha1Base36Hash( $hash ) {
00548         return preg_match( '/^[a-z0-9]{31}$/', $hash );
00549     }
00550 
00556     public function userCanSeeRevDel() {
00557         return $this->getUser()->isAllowedAny(
00558             'deletedhistory',
00559             'deletedtext',
00560             'suppressrevision',
00561             'viewsuppressed'
00562         );
00563     }
00564 
00567     /************************************************************************/
00578     protected function checkRowCount() {
00579         wfDeprecated( __METHOD__, '1.24' );
00580         $db = $this->getDB();
00581         $this->profileDBIn();
00582         $rowcount = $db->estimateRowCount(
00583             $this->tables,
00584             $this->fields,
00585             $this->where,
00586             __METHOD__,
00587             $this->options
00588         );
00589         $this->profileDBOut();
00590 
00591         if ( $rowcount > $this->getConfig()->get( 'APIMaxDBRows' ) ) {
00592             return false;
00593         }
00594 
00595         return true;
00596     }
00597 
00605     public function titleToKey( $title ) {
00606         wfDeprecated( __METHOD__, '1.24' );
00607         // Don't throw an error if we got an empty string
00608         if ( trim( $title ) == '' ) {
00609             return '';
00610         }
00611         $t = Title::newFromText( $title );
00612         if ( !$t ) {
00613             $this->dieUsageMsg( array( 'invalidtitle', $title ) );
00614         }
00615 
00616         return $t->getPrefixedDBkey();
00617     }
00618 
00625     public function keyToTitle( $key ) {
00626         wfDeprecated( __METHOD__, '1.24' );
00627         // Don't throw an error if we got an empty string
00628         if ( trim( $key ) == '' ) {
00629             return '';
00630         }
00631         $t = Title::newFromDBkey( $key );
00632         // This really shouldn't happen but we gotta check anyway
00633         if ( !$t ) {
00634             $this->dieUsageMsg( array( 'invalidtitle', $key ) );
00635         }
00636 
00637         return $t->getPrefixedText();
00638     }
00639 
00646     public function keyPartToTitle( $keyPart ) {
00647         wfDeprecated( __METHOD__, '1.24' );
00648         return substr( $this->keyToTitle( $keyPart . 'x' ), 0, -1 );
00649     }
00650 
00652 }
00653 
00657 abstract class ApiQueryGeneratorBase extends ApiQueryBase {
00658 
00659     private $mGeneratorPageSet = null;
00660 
00668     public function setGeneratorMode( ApiPageSet $generatorPageSet ) {
00669         if ( $generatorPageSet === null ) {
00670             ApiBase::dieDebug( __METHOD__, 'Required parameter missing - $generatorPageSet' );
00671         }
00672         $this->mGeneratorPageSet = $generatorPageSet;
00673     }
00674 
00680     protected function getPageSet() {
00681         if ( $this->mGeneratorPageSet !== null ) {
00682             return $this->mGeneratorPageSet;
00683         }
00684 
00685         return parent::getPageSet();
00686     }
00687 
00693     public function encodeParamName( $paramName ) {
00694         if ( $this->mGeneratorPageSet !== null ) {
00695             return 'g' . parent::encodeParamName( $paramName );
00696         } else {
00697             return parent::encodeParamName( $paramName );
00698         }
00699     }
00700 
00706     protected function setContinueEnumParameter( $paramName, $paramValue ) {
00707         if ( $this->mGeneratorPageSet !== null ) {
00708             $this->getResult()->setGeneratorContinueParam( $this, $paramName, $paramValue );
00709         } else {
00710             parent::setContinueEnumParameter( $paramName, $paramValue );
00711         }
00712     }
00713 
00718     abstract public function executeGenerator( $resultPageSet );
00719 }