MediaWiki  REL1_19
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                 return $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 );
00374                 $result->enableSizeCheck();
00375         }
00376 
00381         protected function getDB() {
00382                 if ( is_null( $this->mDb ) ) {
00383                         $apiQuery = $this->getQuery();
00384                         $this->mDb = $apiQuery->getDB();
00385                 }
00386                 return $this->mDb;
00387         }
00388 
00397         public function selectNamedDB( $name, $db, $groups ) {
00398                 $this->mDb = $this->getQuery()->getNamedDB( $name, $db, $groups );
00399         }
00400 
00405         protected function getPageSet() {
00406                 return $this->getQuery()->getPageSet();
00407         }
00408 
00414         public function titleToKey( $title ) {
00415                 // Don't throw an error if we got an empty string
00416                 if ( trim( $title ) == '' ) {
00417                         return '';
00418                 }
00419                 $t = Title::newFromText( $title );
00420                 if ( !$t ) {
00421                         $this->dieUsageMsg( array( 'invalidtitle', $title ) );
00422                 }
00423                 return $t->getPrefixedDbKey();
00424         }
00425 
00431         public function keyToTitle( $key ) {
00432                 // Don't throw an error if we got an empty string
00433                 if ( trim( $key ) == '' ) {
00434                         return '';
00435                 }
00436                 $t = Title::newFromDbKey( $key );
00437                 // This really shouldn't happen but we gotta check anyway
00438                 if ( !$t ) {
00439                         $this->dieUsageMsg( array( 'invalidtitle', $key ) );
00440                 }
00441                 return $t->getPrefixedText();
00442         }
00443 
00449         public function titlePartToKey( $titlePart ) {
00450                 return substr( $this->titleToKey( $titlePart . 'x' ), 0, - 1 );
00451         }
00452 
00458         public function keyPartToTitle( $keyPart ) {
00459                 return substr( $this->keyToTitle( $keyPart . 'x' ), 0, - 1 );
00460         }
00461 
00469         public function getDirectionDescription( $p = '', $extraDirText = '' ) {
00470                 return array(
00471                                 "In which direction to enumerate{$extraDirText}",
00472                                 " newer          - List oldest first. Note: {$p}start has to be before {$p}end.",
00473                                 " older          - List newest first (default). Note: {$p}start has to be later than {$p}end.",
00474                         );
00475         }
00476 
00482         public function prepareUrlQuerySearchString( $query = null, $protocol = null) {
00483                 $db = $this->getDb();
00484                 if ( !is_null( $query ) || $query != '' ) {
00485                         if ( is_null( $protocol ) ) {
00486                                 $protocol = 'http://';
00487                         }
00488 
00489                         $likeQuery = LinkFilter::makeLikeArray( $query, $protocol );
00490                         if ( !$likeQuery ) {
00491                                 $this->dieUsage( 'Invalid query', 'bad_query' );
00492                         }
00493 
00494                         $likeQuery = LinkFilter::keepOneWildcard( $likeQuery );
00495                         return 'el_index ' . $db->buildLike( $likeQuery );
00496                 } elseif ( !is_null( $protocol ) ) {
00497                         return 'el_index ' . $db->buildLike( "$protocol", $db->anyString() );
00498                 }
00499 
00500                 return null;
00501         }
00502 
00510         public function showHiddenUsersAddBlockInfo( $showBlockInfo ) {
00511                 $userCanViewHiddenUsers = $this->getUser()->isAllowed( 'hideuser' );
00512 
00513                 if ( $showBlockInfo || !$userCanViewHiddenUsers ) {
00514                         $this->addTables( 'ipblocks' );
00515                         $this->addJoinConds( array(
00516                                 'ipblocks' => array( 'LEFT JOIN', 'ipb_user=user_id' ),
00517                         ) );
00518 
00519                         $this->addFields( 'ipb_deleted' );
00520 
00521                         if ( $showBlockInfo ) {
00522                                 $this->addFields( array( 'ipb_reason', 'ipb_by_text', 'ipb_expiry' ) );
00523                         }
00524 
00525                         // Don't show hidden names
00526                         if ( !$userCanViewHiddenUsers ) {
00527                                 $this->addWhere( 'ipb_deleted = 0 OR ipb_deleted IS NULL' );
00528                         }
00529                 }
00530         }
00531 
00536         public function validateSha1Hash( $hash ) {
00537                 return preg_match( '/[a-fA-F0-9]{40}/', $hash );
00538         }
00539 
00544         public function validateSha1Base36Hash( $hash ) {
00545                 return preg_match( '/[a-zA-Z0-9]{31}/', $hash );
00546         }
00547 
00551         public function getPossibleErrors() {
00552                 return array_merge( parent::getPossibleErrors(), array(
00553                         array( 'invalidtitle', 'title' ),
00554                         array( 'invalidtitle', 'key' ),
00555                 ) );
00556         }
00557 
00562         public static function getBaseVersion() {
00563                 return __CLASS__ . ': $Id$';
00564         }
00565 }
00566 
00570 abstract class ApiQueryGeneratorBase extends ApiQueryBase {
00571 
00572         private $mIsGenerator;
00573 
00574         public function __construct( $query, $moduleName, $paramPrefix = '' ) {
00575                 parent::__construct( $query, $moduleName, $paramPrefix );
00576                 $this->mIsGenerator = false;
00577         }
00578 
00583         public function setGeneratorMode() {
00584                 $this->mIsGenerator = true;
00585         }
00586 
00592         public function encodeParamName( $paramName ) {
00593                 if ( $this->mIsGenerator ) {
00594                         return 'g' . parent::encodeParamName( $paramName );
00595                 } else {
00596                         return parent::encodeParamName( $paramName );
00597                 }
00598         }
00599 
00605         public abstract function executeGenerator( $resultPageSet );
00606 }