diff options
author | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-05-01 15:12:12 -0400 |
---|---|---|
committer | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-05-01 15:12:12 -0400 |
commit | c9aa36da061816dee256a979c2ff8d2ee41824d9 (patch) | |
tree | 29f7002b80ee984b488bd047dbbd80b36bf892e9 /includes/api/ApiQueryBase.php | |
parent | b4274e0e33eafb5e9ead9d949ebf031a9fb8363b (diff) | |
parent | d1ba966140d7a60cd5ae4e8667ceb27c1a138592 (diff) |
Merge branch 'archwiki'
# Conflicts:
# skins/ArchLinux.php
# skins/ArchLinux/archlogo.gif
Diffstat (limited to 'includes/api/ApiQueryBase.php')
-rw-r--r-- | includes/api/ApiQueryBase.php | 490 |
1 files changed, 291 insertions, 199 deletions
diff --git a/includes/api/ApiQueryBase.php b/includes/api/ApiQueryBase.php index 8668e04b..65e10ab7 100644 --- a/includes/api/ApiQueryBase.php +++ b/includes/api/ApiQueryBase.php @@ -36,17 +36,22 @@ abstract class ApiQueryBase extends ApiBase { private $mQueryModule, $mDb, $tables, $where, $fields, $options, $join_conds; /** - * @param $query ApiBase - * @param $moduleName string - * @param $paramPrefix string + * @param ApiQuery $queryModule + * @param string $moduleName + * @param string $paramPrefix */ - public function __construct( ApiBase $query, $moduleName, $paramPrefix = '' ) { - parent::__construct( $query->getMain(), $moduleName, $paramPrefix ); - $this->mQueryModule = $query; + public function __construct( ApiQuery $queryModule, $moduleName, $paramPrefix = '' ) { + parent::__construct( $queryModule->getMain(), $moduleName, $paramPrefix ); + $this->mQueryModule = $queryModule; $this->mDb = null; $this->resetQueryParams(); } + /************************************************************************//** + * @name Methods to implement + * @{ + */ + /** * Get the cache mode for the data generated by this module. Override * this in the module subclass. For possible return values and other @@ -55,7 +60,7 @@ abstract class ApiQueryBase extends ApiBase { * Public caching will only be allowed if *all* the modules that supply * data for a given request return a cache mode of public. * - * @param $params + * @param array $params * @return string */ public function getCacheMode( $params ) { @@ -63,6 +68,68 @@ abstract class ApiQueryBase extends ApiBase { } /** + * Override this method to request extra fields from the pageSet + * using $pageSet->requestField('fieldName') + * @param ApiPageSet $pageSet + */ + public function requestExtraData( $pageSet ) { + } + + /**@}*/ + + /************************************************************************//** + * @name Data access + * @{ + */ + + /** + * Get the main Query module + * @return ApiQuery + */ + public function getQuery() { + return $this->mQueryModule; + } + + /** + * Get the Query database connection (read-only) + * @return DatabaseBase + */ + protected function getDB() { + if ( is_null( $this->mDb ) ) { + $this->mDb = $this->getQuery()->getDB(); + } + + return $this->mDb; + } + + /** + * Selects the query database connection with the given name. + * See ApiQuery::getNamedDB() for more information + * @param string $name Name to assign to the database connection + * @param int $db One of the DB_* constants + * @param array $groups Query groups + * @return DatabaseBase + */ + public function selectNamedDB( $name, $db, $groups ) { + $this->mDb = $this->getQuery()->getNamedDB( $name, $db, $groups ); + } + + /** + * Get the PageSet object to work on + * @return ApiPageSet + */ + protected function getPageSet() { + return $this->getQuery()->getPageSet(); + } + + /**@}*/ + + /************************************************************************//** + * @name Querying + * @{ + */ + + /** * Blank the internal arrays with query parameters */ protected function resetQueryParams() { @@ -75,8 +142,8 @@ abstract class ApiQueryBase extends ApiBase { /** * Add a set of tables to the internal array - * @param $tables mixed Table name or array of table names - * @param $alias mixed Table alias, or null for no alias. Cannot be + * @param string|string[] $tables Table name or array of table names + * @param string|null $alias Table alias, or null for no alias. Cannot be * used with multiple tables */ protected function addTables( $tables, $alias = null ) { @@ -101,7 +168,7 @@ abstract class ApiQueryBase extends ApiBase { * conditions) e.g. array('page' => array('LEFT JOIN', * 'page_id=rev_page')) . conditions may be a string or an * addWhere()-style array - * @param $join_conds array JOIN conditions + * @param array $join_conds JOIN conditions */ protected function addJoinConds( $join_conds ) { if ( !is_array( $join_conds ) ) { @@ -131,8 +198,10 @@ abstract class ApiQueryBase extends ApiBase { protected function addFieldsIf( $value, $condition ) { if ( $condition ) { $this->addFields( $value ); + return true; } + return false; } @@ -145,7 +214,7 @@ abstract class ApiQueryBase extends ApiBase { * * For example, array('foo=bar', 'baz' => 3, 'bla' => 'foo') translates * to "foo=bar AND baz='3' AND bla='foo'" - * @param $value mixed String or array + * @param string|array $value */ protected function addWhere( $value ) { if ( is_array( $value ) ) { @@ -161,15 +230,17 @@ abstract class ApiQueryBase extends ApiBase { /** * Same as addWhere(), but add the WHERE clauses only if a condition is met - * @param $value mixed See addWhere() + * @param string|array $value * @param bool $condition If false, do nothing * @return bool $condition */ protected function addWhereIf( $value, $condition ) { if ( $condition ) { $this->addWhere( $value ); + return true; } + return false; } @@ -215,7 +286,9 @@ abstract class ApiQueryBase extends ApiBase { if ( $sort ) { $order = $field . ( $isDirNewer ? '' : ' DESC' ); // Append ORDER BY - $optionOrderBy = isset( $this->options['ORDER BY'] ) ? (array)$this->options['ORDER BY'] : array(); + $optionOrderBy = isset( $this->options['ORDER BY'] ) + ? (array)$this->options['ORDER BY'] + : array(); $optionOrderBy[] = $order; $this->addOption( 'ORDER BY', $optionOrderBy ); } @@ -225,11 +298,11 @@ abstract class ApiQueryBase extends ApiBase { * Add a WHERE clause corresponding to a range, similar to addWhereRange, * but converts $start and $end to database timestamps. * @see addWhereRange - * @param $field - * @param $dir - * @param $start - * @param $end - * @param $sort bool + * @param string $field + * @param string $dir + * @param string $start + * @param string $end + * @param bool $sort */ protected function addTimestampWhereRange( $field, $dir, $start, $end, $sort = true ) { $db = $this->getDb(); @@ -256,16 +329,37 @@ abstract class ApiQueryBase extends ApiBase { * @param string $method Function the query should be attributed to. * You should usually use __METHOD__ here * @param array $extraQuery Query data to add but not store in the object - * Format is array( 'tables' => ..., 'fields' => ..., 'where' => ..., 'options' => ..., 'join_conds' => ... ) + * Format is array( + * 'tables' => ..., + * 'fields' => ..., + * 'where' => ..., + * 'options' => ..., + * 'join_conds' => ... + * ) * @return ResultWrapper */ protected function select( $method, $extraQuery = array() ) { - $tables = array_merge( $this->tables, isset( $extraQuery['tables'] ) ? (array)$extraQuery['tables'] : array() ); - $fields = array_merge( $this->fields, isset( $extraQuery['fields'] ) ? (array)$extraQuery['fields'] : array() ); - $where = array_merge( $this->where, isset( $extraQuery['where'] ) ? (array)$extraQuery['where'] : array() ); - $options = array_merge( $this->options, isset( $extraQuery['options'] ) ? (array)$extraQuery['options'] : array() ); - $join_conds = array_merge( $this->join_conds, isset( $extraQuery['join_conds'] ) ? (array)$extraQuery['join_conds'] : array() ); + $tables = array_merge( + $this->tables, + isset( $extraQuery['tables'] ) ? (array)$extraQuery['tables'] : array() + ); + $fields = array_merge( + $this->fields, + isset( $extraQuery['fields'] ) ? (array)$extraQuery['fields'] : array() + ); + $where = array_merge( + $this->where, + isset( $extraQuery['where'] ) ? (array)$extraQuery['where'] : array() + ); + $options = array_merge( + $this->options, + isset( $extraQuery['options'] ) ? (array)$extraQuery['options'] : array() + ); + $join_conds = array_merge( + $this->join_conds, + isset( $extraQuery['join_conds'] ) ? (array)$extraQuery['join_conds'] : array() + ); // getDB has its own profileDBIn/Out calls $db = $this->getDB(); @@ -278,28 +372,69 @@ abstract class ApiQueryBase extends ApiBase { } /** - * Estimate the row count for the SELECT query that would be run if we - * called select() right now, and check if it's acceptable. - * @return bool true if acceptable, false otherwise + * @param string $query + * @param string $protocol + * @return null|string */ - protected function checkRowCount() { - $db = $this->getDB(); - $this->profileDBIn(); - $rowcount = $db->estimateRowCount( $this->tables, $this->fields, $this->where, __METHOD__, $this->options ); - $this->profileDBOut(); + public function prepareUrlQuerySearchString( $query = null, $protocol = null ) { + $db = $this->getDb(); + if ( !is_null( $query ) || $query != '' ) { + if ( is_null( $protocol ) ) { + $protocol = 'http://'; + } - global $wgAPIMaxDBRows; - if ( $rowcount > $wgAPIMaxDBRows ) { - return false; + $likeQuery = LinkFilter::makeLikeArray( $query, $protocol ); + if ( !$likeQuery ) { + $this->dieUsage( 'Invalid query', 'bad_query' ); + } + + $likeQuery = LinkFilter::keepOneWildcard( $likeQuery ); + + return 'el_index ' . $db->buildLike( $likeQuery ); + } elseif ( !is_null( $protocol ) ) { + return 'el_index ' . $db->buildLike( "$protocol", $db->anyString() ); + } + + return null; + } + + /** + * Filters hidden users (where the user doesn't have the right to view them) + * Also adds relevant block information + * + * @param bool $showBlockInfo + * @return void + */ + public function showHiddenUsersAddBlockInfo( $showBlockInfo ) { + $this->addTables( 'ipblocks' ); + $this->addJoinConds( array( + 'ipblocks' => array( 'LEFT JOIN', 'ipb_user=user_id' ), + ) ); + + $this->addFields( 'ipb_deleted' ); + + if ( $showBlockInfo ) { + $this->addFields( array( 'ipb_id', 'ipb_by', 'ipb_by_text', 'ipb_reason', 'ipb_expiry', 'ipb_timestamp' ) ); + } + + // Don't show hidden names + if ( !$this->getUser()->isAllowed( 'hideuser' ) ) { + $this->addWhere( 'ipb_deleted = 0 OR ipb_deleted IS NULL' ); } - return true; } + /**@}*/ + + /************************************************************************//** + * @name Utility methods + * @{ + */ + /** * Add information (title and namespace) about a Title object to a * result array * @param array $arr Result array à la ApiResult - * @param $title Title + * @param Title $title * @param string $prefix Module prefix */ public static function addTitleInfo( &$arr, $title, $prefix = '' ) { @@ -308,22 +443,6 @@ abstract class ApiQueryBase extends ApiBase { } /** - * Override this method to request extra fields from the pageSet - * using $pageSet->requestField('fieldName') - * @param $pageSet ApiPageSet - */ - public function requestExtraData( $pageSet ) { - } - - /** - * Get the main Query module - * @return ApiQuery - */ - public function getQuery() { - return $this->mQueryModule; - } - - /** * Add a sub-element under the page element with the given page ID * @param int $pageId Page ID * @param array $data Data array à la ApiResult @@ -332,6 +451,7 @@ abstract class ApiQueryBase extends ApiBase { protected function addPageSubItems( $pageId, $data ) { $result = $this->getResult(); $result->setIndexedTagName( $data, $this->getModulePrefix() ); + return $result->addValue( array( 'query', 'pages', intval( $pageId ) ), $this->getModuleName(), $data ); @@ -356,61 +476,134 @@ abstract class ApiQueryBase extends ApiBase { return false; } $result->setIndexedTagName_internal( array( 'query', 'pages', $pageId, - $this->getModuleName() ), $elemname ); + $this->getModuleName() ), $elemname ); + return true; } /** * Set a query-continue value * @param string $paramName Parameter name - * @param string $paramValue Parameter value + * @param string|array $paramValue Parameter value */ protected function setContinueEnumParameter( $paramName, $paramValue ) { - $paramName = $this->encodeParamName( $paramName ); - $msg = array( $paramName => $paramValue ); - $result = $this->getResult(); - $result->disableSizeCheck(); - $result->addValue( 'query-continue', $this->getModuleName(), $msg, ApiResult::ADD_ON_TOP ); - $result->enableSizeCheck(); + $this->getResult()->setContinueParam( $this, $paramName, $paramValue ); } /** - * Get the Query database connection (read-only) - * @return DatabaseBase + * Convert an input title or title prefix into a dbkey. + * + * $namespace should always be specified in order to handle per-namespace + * capitalization settings. + * + * @param string $titlePart Title part + * @param int $defaultNamespace Namespace of the title + * @return string DBkey (no namespace prefix) */ - protected function getDB() { - if ( is_null( $this->mDb ) ) { - $this->mDb = $this->getQuery()->getDB(); + public function titlePartToKey( $titlePart, $namespace = NS_MAIN ) { + $t = Title::makeTitleSafe( $namespace, $titlePart . 'x' ); + if ( !$t ) { + $this->dieUsageMsg( array( 'invalidtitle', $titlePart ) ); } - return $this->mDb; + if ( $namespace != $t->getNamespace() || $t->isExternal() ) { + // This can happen in two cases. First, if you call titlePartToKey with a title part + // that looks like a namespace, but with $defaultNamespace = NS_MAIN. It would be very + // difficult to handle such a case. Such cases cannot exist and are therefore treated + // as invalid user input. The second case is when somebody specifies a title interwiki + // prefix. + $this->dieUsageMsg( array( 'invalidtitle', $titlePart ) ); + } + + return substr( $t->getDbKey(), 0, -1 ); } /** - * Selects the query database connection with the given name. - * See ApiQuery::getNamedDB() for more information - * @param string $name Name to assign to the database connection - * @param int $db One of the DB_* constants - * @param array $groups Query groups - * @return DatabaseBase + * Gets the personalised direction parameter description + * + * @param string $p ModulePrefix + * @param string $extraDirText Any extra text to be appended on the description + * @return array */ - public function selectNamedDB( $name, $db, $groups ) { - $this->mDb = $this->getQuery()->getNamedDB( $name, $db, $groups ); + public function getDirectionDescription( $p = '', $extraDirText = '' ) { + return array( + "In which direction to enumerate{$extraDirText}", + " newer - List oldest first. Note: {$p}start has to be before {$p}end.", + " older - List newest first (default). Note: {$p}start has to be later than {$p}end.", + ); } /** - * Get the PageSet object to work on - * @return ApiPageSet + * @param string $hash + * @return bool */ - protected function getPageSet() { - return $this->getQuery()->getPageSet(); + public function validateSha1Hash( $hash ) { + return preg_match( '/^[a-f0-9]{40}$/', $hash ); + } + + /** + * @param string $hash + * @return bool + */ + public function validateSha1Base36Hash( $hash ) { + return preg_match( '/^[a-z0-9]{31}$/', $hash ); + } + + /** + * Check whether the current user has permission to view revision-deleted + * fields. + * @return bool + */ + public function userCanSeeRevDel() { + return $this->getUser()->isAllowedAny( + 'deletedhistory', + 'deletedtext', + 'suppressrevision', + 'viewsuppressed' + ); + } + + /**@}*/ + + /************************************************************************//** + * @name Deprecated + * @{ + */ + + /** + * Estimate the row count for the SELECT query that would be run if we + * called select() right now, and check if it's acceptable. + * @deprecated since 1.24 + * @return bool True if acceptable, false otherwise + */ + protected function checkRowCount() { + wfDeprecated( __METHOD__, '1.24' ); + $db = $this->getDB(); + $this->profileDBIn(); + $rowcount = $db->estimateRowCount( + $this->tables, + $this->fields, + $this->where, + __METHOD__, + $this->options + ); + $this->profileDBOut(); + + if ( $rowcount > $this->getConfig()->get( 'APIMaxDBRows' ) ) { + return false; + } + + return true; } /** * Convert a title to a DB key + * @deprecated since 1.24, past uses of this were always incorrect and should + * have used self::titlePartToKey() instead * @param string $title Page title with spaces * @return string Page title with underscores */ public function titleToKey( $title ) { + wfDeprecated( __METHOD__, '1.24' ); // Don't throw an error if we got an empty string if ( trim( $title ) == '' ) { return ''; @@ -419,15 +612,18 @@ abstract class ApiQueryBase extends ApiBase { if ( !$t ) { $this->dieUsageMsg( array( 'invalidtitle', $title ) ); } + return $t->getPrefixedDBkey(); } /** * The inverse of titleToKey() + * @deprecated since 1.24, unused and probably never needed * @param string $key Page title with underscores * @return string Page title with spaces */ public function keyToTitle( $key ) { + wfDeprecated( __METHOD__, '1.24' ); // Don't throw an error if we got an empty string if ( trim( $key ) == '' ) { return ''; @@ -437,124 +633,22 @@ abstract class ApiQueryBase extends ApiBase { if ( !$t ) { $this->dieUsageMsg( array( 'invalidtitle', $key ) ); } - return $t->getPrefixedText(); - } - /** - * An alternative to titleToKey() that doesn't trim trailing spaces - * @param string $titlePart Title part with spaces - * @return string Title part with underscores - */ - public function titlePartToKey( $titlePart ) { - return substr( $this->titleToKey( $titlePart . 'x' ), 0, - 1 ); + return $t->getPrefixedText(); } /** - * An alternative to keyToTitle() that doesn't trim trailing spaces - * @param string $keyPart Key part with spaces + * Inverse of titlePartToKey() + * @deprecated since 1.24, unused and probably never needed + * @param string $keyPart DBkey, with prefix * @return string Key part with underscores */ public function keyPartToTitle( $keyPart ) { - return substr( $this->keyToTitle( $keyPart . 'x' ), 0, - 1 ); + wfDeprecated( __METHOD__, '1.24' ); + return substr( $this->keyToTitle( $keyPart . 'x' ), 0, -1 ); } - /** - * Gets the personalised direction parameter description - * - * @param string $p ModulePrefix - * @param string $extraDirText Any extra text to be appended on the description - * @return array - */ - public function getDirectionDescription( $p = '', $extraDirText = '' ) { - return array( - "In which direction to enumerate{$extraDirText}", - " newer - List oldest first. Note: {$p}start has to be before {$p}end.", - " older - List newest first (default). Note: {$p}start has to be later than {$p}end.", - ); - } - - /** - * @param $query String - * @param $protocol String - * @return null|string - */ - public function prepareUrlQuerySearchString( $query = null, $protocol = null ) { - $db = $this->getDb(); - if ( !is_null( $query ) || $query != '' ) { - if ( is_null( $protocol ) ) { - $protocol = 'http://'; - } - - $likeQuery = LinkFilter::makeLikeArray( $query, $protocol ); - if ( !$likeQuery ) { - $this->dieUsage( 'Invalid query', 'bad_query' ); - } - - $likeQuery = LinkFilter::keepOneWildcard( $likeQuery ); - return 'el_index ' . $db->buildLike( $likeQuery ); - } elseif ( !is_null( $protocol ) ) { - return 'el_index ' . $db->buildLike( "$protocol", $db->anyString() ); - } - - return null; - } - - /** - * Filters hidden users (where the user doesn't have the right to view them) - * Also adds relevant block information - * - * @param bool $showBlockInfo - * @return void - */ - public function showHiddenUsersAddBlockInfo( $showBlockInfo ) { - $userCanViewHiddenUsers = $this->getUser()->isAllowed( 'hideuser' ); - - if ( $showBlockInfo || !$userCanViewHiddenUsers ) { - $this->addTables( 'ipblocks' ); - $this->addJoinConds( array( - 'ipblocks' => array( 'LEFT JOIN', 'ipb_user=user_id' ), - ) ); - - $this->addFields( 'ipb_deleted' ); - - if ( $showBlockInfo ) { - $this->addFields( array( 'ipb_id', 'ipb_by', 'ipb_by_text', 'ipb_reason', 'ipb_expiry' ) ); - } - - // Don't show hidden names - if ( !$userCanViewHiddenUsers ) { - $this->addWhere( 'ipb_deleted = 0 OR ipb_deleted IS NULL' ); - } - } - } - - /** - * @param $hash string - * @return bool - */ - public function validateSha1Hash( $hash ) { - return preg_match( '/^[a-f0-9]{40}$/', $hash ); - } - - /** - * @param $hash string - * @return bool - */ - public function validateSha1Base36Hash( $hash ) { - return preg_match( '/^[a-z0-9]{31}$/', $hash ); - } - - /** - * @return array - */ - public function getPossibleErrors() { - $errors = parent::getPossibleErrors(); - $errors = array_merge( $errors, array( - array( 'invalidtitle', 'title' ), - array( 'invalidtitle', 'key' ), - ) ); - return $errors; - } + /**@}*/ } /** @@ -568,7 +662,7 @@ abstract class ApiQueryGeneratorBase extends ApiQueryBase { * Switch this module to generator mode. By default, generator mode is * switched off and the module acts like a normal query module. * @since 1.21 requires pageset parameter - * @param $generatorPageSet ApiPageSet object that the module will get + * @param ApiPageSet $generatorPageSet ApiPageSet object that the module will get * by calling getPageSet() when in generator mode. */ public function setGeneratorMode( ApiPageSet $generatorPageSet ) { @@ -587,11 +681,12 @@ abstract class ApiQueryGeneratorBase extends ApiQueryBase { if ( $this->mGeneratorPageSet !== null ) { return $this->mGeneratorPageSet; } + return parent::getPageSet(); } /** - * Overrides base class to prepend 'g' to every generator parameter + * Overrides ApiBase to prepend 'g' to every generator parameter * @param string $paramName Parameter name * @return string Prefixed parameter name */ @@ -604,24 +699,21 @@ abstract class ApiQueryGeneratorBase extends ApiQueryBase { } /** - * Overrides base in case of generator & smart continue to - * notify ApiQueryMain instead of adding them to the result right away. + * Overridden to set the generator param if in generator mode * @param string $paramName Parameter name - * @param string $paramValue Parameter value + * @param string|array $paramValue Parameter value */ protected function setContinueEnumParameter( $paramName, $paramValue ) { - // If this is a generator and query->setGeneratorContinue() returns false, treat as before - if ( $this->mGeneratorPageSet === null - || !$this->getQuery()->setGeneratorContinue( $this, $paramName, $paramValue ) - ) { + if ( $this->mGeneratorPageSet !== null ) { + $this->getResult()->setGeneratorContinueParam( $this, $paramName, $paramValue ); + } else { parent::setContinueEnumParameter( $paramName, $paramValue ); } } /** * Execute this module as a generator - * @param $resultPageSet ApiPageSet: All output should be appended to - * this object + * @param ApiPageSet $resultPageSet All output should be appended to this object */ abstract public function executeGenerator( $resultPageSet ); } |