API module manager and help rewrite

This is a non-versioned part of the larger patch #41014
https://gerrit.wikimedia.org/r/#/c/41014
It will allow help subsystem optimization (merging paraminfo and help),
path towards per-module or per-system versioning, removal of the
manually maintained generator lists.

Changes:
* ApiModuleManager now handles all submodules (actions,props,lists) and instantiation
* ApiModuleManager maintains a cache of all instantiated modules
* Query stores prop/list/meta as submodules
* action=help suports generalized submodules (modules=query+value), querymodules obsolete

Change-Id: Ie2dee41e44a29cd5d5935eeaa5240b708d95a8f0
This commit is contained in:
Yuri Astrakhan 2013-02-05 01:52:55 -05:00
parent 549311b205
commit a97bb3acfa
8 changed files with 389 additions and 192 deletions

View file

@ -194,11 +194,14 @@ production.
* (bug 43849) ApiQueryImageInfo no longer throws exceptions with ForeignDBRepo
redirects.
* On error, any warnings generated before that error will be shown in the result.
* action=help suports generalized submodules (modules=query+value), querymodules obsolete
=== API internal changes in 1.21 ===
* For debugging only, a new global $wgDebugAPI removes many API restrictions when true.
Never use on the production servers, as this flag introduces security holes.
Whenever enabled, a warning will also be added to all output.
* ApiModuleManager now handles all submodules (actions,props,lists) and instantiation
* Query stores prop/list/meta as submodules
=== Languages updated in 1.21 ===

View file

@ -363,6 +363,7 @@ $wgAutoloadLocalClasses = array(
'ApiLogin' => 'includes/api/ApiLogin.php',
'ApiLogout' => 'includes/api/ApiLogout.php',
'ApiMain' => 'includes/api/ApiMain.php',
'ApiModuleManager' => 'includes/api/ApiModuleManager.php',
'ApiMove' => 'includes/api/ApiMove.php',
'ApiOpenSearch' => 'includes/api/ApiOpenSearch.php',
'ApiOptions' => 'includes/api/ApiOptions.php',

View file

@ -127,6 +127,16 @@ abstract class ApiBase extends ContextSource {
return $this->mModuleName;
}
/**
* Get the module manager, or null if this module has no sub-modules
* @since 1.21
* @return ApiModuleManager
*/
public function getModuleManager() {
return null;
}
/**
* Get parameter prefix (usually two letters or an empty string).
* @return string
@ -258,6 +268,8 @@ abstract class ApiBase extends ContextSource {
}
$msg = $lnPrfx . implode( $lnPrfx, $msg ) . "\n";
$msg .= $this->makeHelpArrayToString( $lnPrfx, false, $this->getHelpUrls() );
if ( $this->isReadMode() ) {
$msg .= "\nThis module requires read rights";
}
@ -301,8 +313,6 @@ abstract class ApiBase extends ContextSource {
}
}
}
$msg .= $this->makeHelpArrayToString( $lnPrfx, "Help page", $this->getHelpUrls() );
}
return $msg;

View file

@ -43,43 +43,62 @@ class ApiHelp extends ApiBase {
}
$this->getMain()->setHelp();
$result = $this->getResult();
$queryObj = new ApiQuery( $this->getMain(), 'query' );
$r = array();
if ( is_array( $params['modules'] ) ) {
$modArr = $this->getMain()->getModules();
foreach ( $params['modules'] as $m ) {
if ( !isset( $modArr[$m] ) ) {
$r[] = array( 'name' => $m, 'missing' => '' );
continue;
}
$module = new $modArr[$m]( $this->getMain(), $m );
$r[] = $this->buildModuleHelp( $module, 'action' );
}
$modules = $params['modules'];
} else {
$modules = array();
}
if ( is_array( $params['querymodules'] ) ) {
$qmodArr = $queryObj->getModules();
$queryModules = $params['querymodules'];
foreach ( $queryModules as $m ) {
$modules[] = 'query+' . $m;
}
} else {
$queryModules = array();
}
foreach ( $params['querymodules'] as $qm ) {
if ( !isset( $qmodArr[$qm] ) ) {
$r[] = array( 'name' => $qm, 'missing' => '' );
continue;
$r = array();
foreach ( $modules as $m ) {
// sub-modules could be given in the form of "name[+name[+name...]]"
$subNames = explode( '+', $m );
if ( count( $subNames ) === 1 ) {
// In case the '+' was typed into URL, it resolves as a space
$subNames = explode( ' ', $m );
}
$module = $this->getMain();
for ( $i = 0; $i < count( $subNames ); $i++ ) {
$subs = $module->getModuleManager();
if ( $subs === null ) {
$module = null;
} else {
$module = $subs->getModule( $subNames[$i] );
}
$module = new $qmodArr[$qm]( $this, $qm );
$type = $queryObj->getModuleType( $qm );
if ( $type === null ) {
$r[] = array( 'name' => $qm, 'missing' => '' );
continue;
if ( $module === null ) {
if ( count( $subNames ) === 2
&& $i === 1
&& $subNames[0] === 'query'
&& in_array( $subNames[1], $queryModules )
) {
// Legacy: This is one of the renamed 'querymodule=...' parameters,
// do not use '+' notation in the output, use submodule's name instead.
$name = $subNames[1];
} else {
$name = implode( '+', array_slice( $subNames, 0, $i + 1 ) );
}
$r[] = array( 'name' => $name, 'missing' => '' );
break;
} else {
$type = $subs->getModuleGroup( $subNames[$i] );
}
}
if ( $module !== null ) {
$r[] = $this->buildModuleHelp( $module, $type );
}
}
$result->setIndexedTagName( $r, 'module' );
$result->addValue( null, $this->getModuleName(), $r );
}
@ -114,15 +133,16 @@ class ApiHelp extends ApiBase {
ApiBase::PARAM_ISMULTI => true
),
'querymodules' => array(
ApiBase::PARAM_ISMULTI => true
ApiBase::PARAM_ISMULTI => true,
ApiBase::PARAM_DEPRECATED => true
),
);
}
public function getParamDescription() {
return array(
'modules' => 'List of module names (value of the action= parameter)',
'querymodules' => 'List of query module names (value of prop=, meta= or list= parameter)',
'modules' => 'List of module names (value of the action= parameter). Can specify submodules with a \'+\'',
'querymodules' => 'Use modules=query+value instead. List of query module names (value of prop=, meta= or list= parameter)',
);
}
@ -134,9 +154,8 @@ class ApiHelp extends ApiBase {
return array(
'api.php?action=help' => 'Whole help page',
'api.php?action=help&modules=protect' => 'Module (action) help page',
'api.php?action=help&querymodules=categorymembers' => 'Query (list) modules help page',
'api.php?action=help&querymodules=info' => 'Query (prop) modules help page',
'api.php?action=help&querymodules=siteinfo' => 'Query (meta) modules help page',
'api.php?action=help&modules=query+categorymembers' => 'Help for the query/categorymembers module',
'api.php?action=help&modules=login|query+info' => 'Help for the login and query/info modules',
);
}

View file

@ -131,8 +131,9 @@ class ApiMain extends ApiBase {
*/
private $mPrinter;
private $mModules, $mModuleNames, $mFormats, $mFormatNames;
private $mResult, $mAction, $mEnableWrite;
private $mModuleMgr, $mResult;
private $mAction;
private $mEnableWrite;
private $mInternalMode, $mSquidMaxage, $mModule;
private $mCacheMode = 'private';
@ -180,12 +181,11 @@ class ApiMain extends ApiBase {
}
}
global $wgAPIModules; // extension modules
$this->mModules = $wgAPIModules + self::$Modules;
$this->mModuleNames = array_keys( $this->mModules );
$this->mFormats = self::$Formats;
$this->mFormatNames = array_keys( $this->mFormats );
global $wgAPIModules;
$this->mModuleMgr = new ApiModuleManager( $this );
$this->mModuleMgr->addModules( self::$Modules, 'action' );
$this->mModuleMgr->addModules( $wgAPIModules, 'action' );
$this->mModuleMgr->addModules( self::$Formats, 'format' );
$this->mResult = new ApiResult( $this );
$this->mEnableWrite = $enableWrite;
@ -332,10 +332,11 @@ class ApiMain extends ApiBase {
* @return ApiFormatBase
*/
public function createPrinterByName( $format ) {
if ( !isset( $this->mFormats[$format] ) ) {
$printer = $this->mModuleMgr->getModule( $format, 'format' );
if ( $printer === null ) {
$this->dieUsage( "Unrecognized format: {$format}", 'unknown_format' );
}
return new $this->mFormats[$format] ( $this, $format );
return $printer;
}
/**
@ -604,7 +605,7 @@ class ApiMain extends ApiBase {
if ( !isset ( $this->mPrinter ) ) {
// The printer has not been created yet. Try to manually get formatter value.
$value = $this->getRequest()->getVal( 'format', self::API_DEFAULT_FORMAT );
if ( !in_array( $value, $this->mFormatNames ) ) {
if ( !$this->mModuleMgr->isDefined( $value, 'format' ) ) {
$value = self::API_DEFAULT_FORMAT;
}
@ -700,9 +701,10 @@ class ApiMain extends ApiBase {
*/
protected function setupModule() {
// Instantiate the module requested by the user
$module = new $this->mModules[$this->mAction] ( $this, $this->mAction );
$this->mModule = $module;
$module = $this->mModuleMgr->getModule( $this->mAction, 'action' );
if ( $module === null ) {
$this->dieUsage( 'The API requires a valid action parameter', 'unknown_action' );
}
$moduleParams = $module->extractRequestParams();
// Die if token required, but not provided (unless there is a gettoken parameter)
@ -814,6 +816,7 @@ class ApiMain extends ApiBase {
protected function executeAction() {
$params = $this->setupExecuteAction();
$module = $this->setupModule();
$this->mModule = $module;
$this->checkExecutePermissions( $module );
@ -971,11 +974,11 @@ class ApiMain extends ApiBase {
return array(
'format' => array(
ApiBase::PARAM_DFLT => ApiMain::API_DEFAULT_FORMAT,
ApiBase::PARAM_TYPE => $this->mFormatNames
ApiBase::PARAM_TYPE => $this->mModuleMgr->getNames( 'format' )
),
'action' => array(
ApiBase::PARAM_DFLT => 'help',
ApiBase::PARAM_TYPE => $this->mModuleNames
ApiBase::PARAM_TYPE => $this->mModuleMgr->getNames( 'action' )
),
'maxlag' => array(
ApiBase::PARAM_TYPE => 'integer'
@ -1092,7 +1095,7 @@ class ApiMain extends ApiBase {
' Victor Vasiliev - vasilvv at gee mail dot com',
' Bryan Tong Minh - bryan . tongminh @ gmail . com',
' Sam Reed - sam @ reedyboy . net',
' Yuri Astrakhan "<Firstname><Lastname>@gmail.com" (creator, lead developer Sep 2006-Sep 2007)',
' Yuri Astrakhan "<Firstname><Lastname>@gmail.com" (creator, lead developer Sep 2006-Sep 2007, 2012)',
'',
'Please send your comments, suggestions and questions to mediawiki-api@lists.wikimedia.org',
'or file a bug report at https://bugzilla.wikimedia.org/'
@ -1143,8 +1146,9 @@ class ApiMain extends ApiBase {
$astriks = str_repeat( '*** ', 14 );
$msg .= "\n\n$astriks Modules $astriks\n\n";
foreach ( array_keys( $this->mModules ) as $moduleName ) {
$module = new $this->mModules[$moduleName] ( $this, $moduleName );
foreach ( $this->mModuleMgr->getNames( 'action' ) as $name ) {
$module = $this->mModuleMgr->getModule( $name );
$msg .= self::makeHelpMsgHeader( $module, 'action' );
$msg2 = $module->makeHelpMsg();
@ -1162,8 +1166,8 @@ class ApiMain extends ApiBase {
}
$msg .= "\n$astriks Formats $astriks\n\n";
foreach ( array_keys( $this->mFormats ) as $formatName ) {
$module = $this->createPrinterByName( $formatName );
foreach ( $this->mModuleMgr->getNames( 'format' ) as $name ) {
$module = $this->mModuleMgr->getModule( $name );
$msg .= self::makeHelpMsgHeader( $module, 'format' );
$msg2 = $module->makeHelpMsg();
if ( $msg2 !== false ) {
@ -1215,45 +1219,57 @@ class ApiMain extends ApiBase {
return false;
}
/**
* Overrides to return this instance's module manager.
* @return ApiModuleManager
*/
public function getModuleManager() {
return $this->mModuleMgr;
}
/**
* Add or overwrite a module in this ApiMain instance. Intended for use by extending
* classes who wish to add their own modules to their lexicon or override the
* behavior of inherent ones.
*
* @param $mdlName String The identifier for this module.
* @param $mdlClass String The class where this module is implemented.
* @deprecated since 1.21, Use getModuleManager()->addModule() instead.
* @param $name string The identifier for this module.
* @param $class ApiBase The class where this module is implemented.
*/
protected function addModule( $mdlName, $mdlClass ) {
$this->mModules[$mdlName] = $mdlClass;
protected function addModule( $name, $class ) {
$this->getModuleManager()->addModule( $name, 'action', $class );
}
/**
* Add or overwrite an output format for this ApiMain. Intended for use by extending
* classes who wish to add to or modify current formatters.
*
* @param $fmtName string The identifier for this format.
* @param $fmtClass ApiFormatBase The class implementing this format.
* @deprecated since 1.21, Use getModuleManager()->addModule() instead.
* @param $name string The identifier for this format.
* @param $class ApiFormatBase The class implementing this format.
*/
protected function addFormat( $fmtName, $fmtClass ) {
$this->mFormats[$fmtName] = $fmtClass;
protected function addFormat( $name, $class ) {
$this->getModuleManager->addModule( $name, 'format', $class );
}
/**
* Get the array mapping module names to class names
* @deprecated since 1.21, Use getModuleManager()'s methods instead.
* @return array
*/
function getModules() {
return $this->mModules;
return $this->getModuleManager()->getNamesWithClasses( 'action' );
}
/**
* Returns the list of supported formats in form ( 'format' => 'ClassName' )
*
* @since 1.18
* @deprecated since 1.21, Use getModuleManager()'s methods instead.
* @return array
*/
public function getFormats() {
return $this->mFormats;
return $this->getModuleManager()->getNamesWithClasses( 'format' );
}
}

View file

@ -0,0 +1,171 @@
<?php
/**
*
*
* Created on Dec 27, 2012
*
* Copyright © 2012 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
* @since 1.21
*/
/**
* This class holds a list of modules and handles instantiation
*
* @since 1.21
* @ingroup API
*/
class ApiModuleManager extends ContextSource {
private $mParent;
private $mInstances = array();
private $mGroups = array();
private $mModules = array();
/**
* Construct new module manager
* @param ApiBase $parentModule Parent module instance will be used during instantiation
*/
public function __construct( ApiBase $parentModule ) {
$this->mParent = $parentModule;
}
/**
* Add a list of modules to the manager
* @param array $modules A map of ModuleName => ModuleClass
* @param string $group Which group modules belong to (action,format,...)
*/
public function addModules( array $modules, $group ) {
foreach ( $modules as $name => $class ) {
$this->addModule( $name, $group, $class );
}
}
/**
* Add or overwrite a module in this ApiMain instance. Intended for use by extending
* classes who wish to add their own modules to their lexicon or override the
* behavior of inherent ones.
*
* @param $group string Name of the module group
* @param $name string The identifier for this module.
* @param $class string The class where this module is implemented.
*/
public function addModule( $name, $group, $class ) {
$this->mGroups[$group] = null;
$this->mModules[$name] = array( $group, $class );
}
/**
* Get module instance by name, or instantiate it if it does not exist
* @param $moduleName string module name
* @param $group string optionally validate that the module is in a specific group
* @param $ignoreCache bool if true, force-creates a new instance and does not cache it
* @return mixed the new module instance, or null if failed
*/
public function getModule( $moduleName, $group = null, $ignoreCache = false ) {
if ( !isset( $this->mModules[$moduleName] ) ) {
return null;
}
$grpCls = $this->mModules[$moduleName];
if ( $group !== null && $grpCls[0] !== $group ) {
return null;
}
if ( !$ignoreCache && isset( $this->mInstances[$moduleName] ) ) {
// already exists
return $this->mInstances[$moduleName];
} else {
// new instance
$class = $grpCls[1];
$instance = new $class ( $this->mParent, $moduleName );
if ( !$ignoreCache ) {
// cache this instance in case it is needed later
$this->mInstances[$moduleName] = $instance;
}
return $instance;
}
}
/**
* Get an array of modules in a specific group or all if no group is set.
* @param string $group optional group filter
* @return array list of module names
*/
public function getNames( $group = null ) {
if ( $group === null ) {
return array_keys( $this->mModules );
}
$result = array();
foreach ( $this->mModules as $name => $grpCls ) {
if ( $grpCls[0] === $group ) {
$result[] = $name;
}
}
return $result;
}
/**
* Create an array of (moduleName => moduleClass) for a specific group or for all.
* @param string $group name of the group to get or null for all
* @return array name=>class map
*/
public function getNamesWithClasses( $group = null ) {
$result = array();
foreach ( $this->mModules as $name => $grpCls ) {
if ( $group === null || $grpCls[0] === $group ) {
$result[$name] = $grpCls[1];
}
}
return $result;
}
/**
* Returns true if the specific module is defined at all or in a specific group.
* @param string $moduleName module name
* @param string $group group name to check against, or null to check all groups,
* @return boolean true if defined
*/
public function isDefined( $moduleName, $group = null ) {
if ( isset( $this->mModules[$moduleName] ) ) {
return $group === null || $this->mModules[$moduleName][0] === $group;
} else {
return false;
}
}
/**
* Returns the group name for the given module
* @param string $moduleName
* @return string group name or null if missing
*/
public function getModuleGroup( $moduleName ) {
if ( isset( $this->mModules[$moduleName] ) ) {
return $this->mModules[$moduleName][0];
} else {
return null;
}
}
/**
* Get a list of groups this manager contains.
* @return array
*/
public function getGroups() {
return array_keys( $this->mGroups );
}
}

View file

@ -42,42 +42,13 @@ class ApiParamInfo extends ApiBase {
public function execute() {
// Get parameters
$params = $this->extractRequestParams();
$result = $this->getResult();
$resultObj = $this->getResult();
$res = array();
if ( is_array( $params['modules'] ) ) {
$modules = $this->getMain()->getModules();
$res['modules'] = array();
foreach ( $params['modules'] as $mod ) {
if ( !isset( $modules[$mod] ) ) {
$res['modules'][] = array( 'name' => $mod, 'missing' => '' );
continue;
}
$obj = new $modules[$mod]( $this->getMain(), $mod );
$item = $this->getClassInfo( $obj );
$item['name'] = $mod;
$res['modules'][] = $item;
}
$result->setIndexedTagName( $res['modules'], 'module' );
}
$this->addModulesInfo( $params, 'modules', $res, $resultObj );
if ( is_array( $params['querymodules'] ) ) {
$queryModules = $this->queryObj->getModules();
$res['querymodules'] = array();
foreach ( $params['querymodules'] as $qm ) {
if ( !isset( $queryModules[$qm] ) ) {
$res['querymodules'][] = array( 'name' => $qm, 'missing' => '' );
continue;
}
$obj = new $queryModules[$qm]( $this, $qm );
$item = $this->getClassInfo( $obj );
$item['name'] = $qm;
$item['querytype'] = $this->queryObj->getModuleType( $qm );
$res['querymodules'][] = $item;
}
$result->setIndexedTagName( $res['querymodules'], 'module' );
}
$this->addModulesInfo( $params, 'querymodules', $res, $resultObj );
if ( $params['mainmodule'] ) {
$res['mainmodule'] = $this->getClassInfo( $this->getMain() );
@ -88,29 +59,50 @@ class ApiParamInfo extends ApiBase {
$res['pagesetmodule'] = $this->getClassInfo( $pageSet );
}
if ( is_array( $params['formatmodules'] ) ) {
$formats = $this->getMain()->getFormats();
$res['formatmodules'] = array();
foreach ( $params['formatmodules'] as $f ) {
if ( !isset( $formats[$f] ) ) {
$res['formatmodules'][] = array( 'name' => $f, 'missing' => '' );
continue;
}
$obj = new $formats[$f]( $this, $f );
$item = $this->getClassInfo( $obj );
$item['name'] = $f;
$res['formatmodules'][] = $item;
}
$result->setIndexedTagName( $res['formatmodules'], 'module' );
$this->addModulesInfo( $params, 'formatmodules', $res, $resultObj );
$resultObj->addValue( null, $this->getModuleName(), $res );
}
/**
* If the type is requested in parameters, adds a section to res with module info.
* @param array $params user parameters array
* @param string $type parameter name
* @param array $res store results in this array
* @param array $resultObj results object to set indexed tag.
*/
private function addModulesInfo( $params, $type, &$res, $resultObj ) {
if ( !is_array( $params[$type] ) ) {
return;
}
$result->addValue( null, $this->getModuleName(), $res );
$isQuery = ( $type === 'querymodules' );
if ( $isQuery ) {
$mgr = $this->queryObj->getModuleManager();
} else {
$mgr = $this->getMain()->getModuleManager();
}
$res[$type] = array();
foreach ( $params[$type] as $mod ) {
if ( !$mgr->isDefined( $mod ) ) {
$res[$type][] = array( 'name' => $mod, 'missing' => '' );
continue;
}
$obj = $mgr->getModule( $mod );
$item = $this->getClassInfo( $obj );
$item['name'] = $mod;
if ( $isQuery ) {
$item['querytype'] = $mgr->getModuleGroup( $mod );
}
$res[$type][] = $item;
}
$resultObj->setIndexedTagName( $res[$type], 'module' );
}
/**
* @param $obj ApiBase
* @return ApiResult
*/
function getClassInfo( $obj ) {
private function getClassInfo( $obj ) {
$result = $this->getResult();
$retval['classname'] = get_class( $obj );
$retval['description'] = implode( "\n", (array)$obj->getFinalDescription() );
@ -150,7 +142,7 @@ class ApiParamInfo extends ApiBase {
if ( is_string( $examples ) ) {
$examples = array( $examples );
}
foreach( $examples as $k => $v ) {
foreach ( $examples as $k => $v ) {
if ( strlen( $retval['examples'] ) ) {
$retval['examples'] .= ' ';
}
@ -181,7 +173,7 @@ class ApiParamInfo extends ApiBase {
}
//handle shorthand
if( !is_array( $p ) ) {
if ( !is_array( $p ) ) {
$p = array(
ApiBase::PARAM_DFLT => $p,
);
@ -208,11 +200,11 @@ class ApiParamInfo extends ApiBase {
if ( isset( $p[ApiBase::PARAM_DFLT] ) ) {
$type = $p[ApiBase::PARAM_TYPE];
if( $type === 'boolean' ) {
if ( $type === 'boolean' ) {
$a['default'] = ( $p[ApiBase::PARAM_DFLT] ? 'true' : 'false' );
} elseif( $type === 'string' ) {
} elseif ( $type === 'string' ) {
$a['default'] = strval( $p[ApiBase::PARAM_DFLT] );
} elseif( $type === 'integer' ) {
} elseif ( $type === 'integer' ) {
$a['default'] = intval( $p[ApiBase::PARAM_DFLT] );
} else {
$a['default'] = $p[ApiBase::PARAM_DFLT];
@ -319,11 +311,11 @@ class ApiParamInfo extends ApiBase {
}
public function getAllowedParams() {
$modules = array_keys( $this->getMain()->getModules() );
$modules = $this->getMain()->getModuleManager()->getNames( 'action' );
sort( $modules );
$querymodules = array_keys( $this->queryObj->getModules() );
$querymodules = $this->queryObj->getModuleManager()->getNames();
sort( $querymodules );
$formatmodules = array_keys( $this->getMain()->getFormats() );
$formatmodules = $this->getMain()->getModuleManager()->getNames( 'format' );
sort( $formatmodules );
return array(
'modules' => array(

View file

@ -37,20 +37,11 @@
*/
class ApiQuery extends ApiBase {
private $mPropModuleNames, $mListModuleNames, $mMetaModuleNames;
/**
* @var ApiPageSet
*/
private $mPageSet;
private $params, $redirects, $convertTitles, $iwUrl;
/**
* List of Api Query prop modules
* @var array
*/
private $mQueryPropModules = array(
private static $QueryPropModules = array(
'categories' => 'ApiQueryCategories',
'categoryinfo' => 'ApiQueryCategoryInfo',
'duplicatefiles' => 'ApiQueryDuplicateFiles',
@ -71,7 +62,7 @@ class ApiQuery extends ApiBase {
* List of Api Query list modules
* @var array
*/
private $mQueryListModules = array(
private static $QueryListModules = array(
'allcategories' => 'ApiQueryAllCategories',
'allimages' => 'ApiQueryAllImages',
'alllinks' => 'ApiQueryAllLinks',
@ -105,7 +96,7 @@ class ApiQuery extends ApiBase {
* List of Api Query meta modules
* @var array
*/
private $mQueryMetaModules = array(
private static $QueryMetaModules = array(
'allmessages' => 'ApiQueryAllMessages',
'siteinfo' => 'ApiQuerySiteinfo',
'userinfo' => 'ApiQueryUserInfo',
@ -144,8 +135,15 @@ class ApiQuery extends ApiBase {
'watchlistraw' => 'ApiQueryWatchlistRaw',
);
/**
* @var ApiPageSet
*/
private $mPageSet;
private $params, $redirects, $convertTitles, $iwUrl;
private $mSlaveDB = null;
private $mNamedDB = array();
private $mModuleMgr;
protected $mAllowedGenerators;
@ -156,30 +154,32 @@ class ApiQuery extends ApiBase {
public function __construct( $main, $action ) {
parent::__construct( $main, $action );
// Allow custom modules to be added in LocalSettings.php
global $wgAPIPropModules, $wgAPIListModules, $wgAPIMetaModules, $wgAPIGeneratorModules;
self::appendUserModules( $this->mQueryPropModules, $wgAPIPropModules );
self::appendUserModules( $this->mQueryListModules, $wgAPIListModules );
self::appendUserModules( $this->mQueryMetaModules, $wgAPIMetaModules );
self::appendUserModules( $this->mQueryGenerators, $wgAPIGeneratorModules );
$this->mModuleMgr = new ApiModuleManager( $this );
$this->mPropModuleNames = array_keys( $this->mQueryPropModules );
$this->mListModuleNames = array_keys( $this->mQueryListModules );
$this->mMetaModuleNames = array_keys( $this->mQueryMetaModules );
// Allow custom modules to be added in LocalSettings.php
global $wgAPIPropModules, $wgAPIListModules, $wgAPIMetaModules;
$this->mModuleMgr->addModules( self::$QueryPropModules, 'prop' );
$this->mModuleMgr->addModules( $wgAPIPropModules, 'prop' );
$this->mModuleMgr->addModules( self::$QueryListModules, 'list' );
$this->mModuleMgr->addModules( $wgAPIListModules, 'list' );
$this->mModuleMgr->addModules( self::$QueryMetaModules, 'meta' );
$this->mModuleMgr->addModules( $wgAPIMetaModules, 'meta' );
global $wgAPIGeneratorModules;
if ( is_array( $wgAPIGeneratorModules ) ) {
foreach ( $wgAPIGeneratorModules as $moduleName => $moduleClass ) {
$this->mQueryGenerators[$moduleName] = $moduleClass;
}
}
$this->mAllowedGenerators = array_keys( $this->mQueryGenerators );
}
/**
* Helper function to append any add-in modules to the list
* @param $modules array Module array
* @param $newModules array Module array to add to $modules
* Overrides to return this instance's module manager.
* @return ApiModuleManager
*/
private static function appendUserModules( &$modules, $newModules ) {
if ( is_array( $newModules ) ) {
foreach ( $newModules as $moduleName => $moduleClass ) {
$modules[$moduleName] = $moduleClass;
}
}
public function getModuleManager() {
return $this->mModuleMgr;
}
/**
@ -224,10 +224,11 @@ class ApiQuery extends ApiBase {
/**
* Get the array mapping module names to class names
* @deprecated since 1.21, use getModuleManager()'s methods instead
* @return array array(modulename => classname)
*/
public function getModules() {
return array_merge( $this->mQueryPropModules, $this->mQueryListModules, $this->mQueryMetaModules );
return $this->getModuleManager()->getNamesWithClasses();
}
/**
@ -240,23 +241,12 @@ class ApiQuery extends ApiBase {
/**
* Get whether the specified module is a prop, list or a meta query module
* @deprecated since 1.21, use getModuleManager()->getModuleGroup()
* @param $moduleName string Name of the module to find type for
* @return mixed string or null
*/
function getModuleType( $moduleName ) {
if ( isset( $this->mQueryPropModules[$moduleName] ) ) {
return 'prop';
}
if ( isset( $this->mQueryListModules[$moduleName] ) ) {
return 'list';
}
if ( isset( $this->mQueryMetaModules[$moduleName] ) ) {
return 'meta';
}
return null;
return $this->getModuleManager()->getModuleGroup( $moduleName );
}
/**
@ -295,9 +285,9 @@ class ApiQuery extends ApiBase {
// Instantiate requested modules
$modules = array();
$this->instantiateModules( $modules, 'prop', $this->mQueryPropModules );
$this->instantiateModules( $modules, 'list', $this->mQueryListModules );
$this->instantiateModules( $modules, 'meta', $this->mQueryMetaModules );
$this->instantiateModules( $modules, 'prop' );
$this->instantiateModules( $modules, 'list' );
$this->instantiateModules( $modules, 'meta' );
$cacheMode = 'public';
@ -378,12 +368,11 @@ class ApiQuery extends ApiBase {
* Create instances of all modules requested by the client
* @param $modules Array to append instantiated modules to
* @param $param string Parameter name to read modules from
* @param $moduleList Array array(modulename => classname)
*/
private function instantiateModules( &$modules, $param, $moduleList ) {
private function instantiateModules( &$modules, $param ) {
if ( isset( $this->params[$param] ) ) {
foreach ( $this->params[$param] as $moduleName ) {
$modules[] = new $moduleList[$moduleName] ( $this, $moduleName );
$modules[] = $this->mModuleMgr->getModule( $moduleName );
}
}
}
@ -594,15 +583,10 @@ class ApiQuery extends ApiBase {
* @return ApiQueryGeneratorBase
*/
public function newGenerator( $generatorName ) {
// Find class that implements requested generator
if ( isset( $this->mQueryListModules[$generatorName] ) ) {
$className = $this->mQueryListModules[$generatorName];
} elseif ( isset( $this->mQueryPropModules[$generatorName] ) ) {
$className = $this->mQueryPropModules[$generatorName];
} else {
ApiBase::dieDebug( __METHOD__, "Unknown generator=$generatorName" );
$generator = $this->mModuleMgr->getModule( $generatorName );
if ( $generator === null ) {
$this->dieUsage( "Unknown generator=$generatorName", 'badgenerator' );
}
$generator = new $className ( $this, $generatorName );
if ( !$generator instanceof ApiQueryGeneratorBase ) {
$this->dieUsage( "Module $generatorName cannot be used as a generator", 'badgenerator' );
}
@ -642,15 +626,15 @@ class ApiQuery extends ApiBase {
return array(
'prop' => array(
ApiBase::PARAM_ISMULTI => true,
ApiBase::PARAM_TYPE => $this->mPropModuleNames
ApiBase::PARAM_TYPE => $this->mModuleMgr->getNames( 'prop' )
),
'list' => array(
ApiBase::PARAM_ISMULTI => true,
ApiBase::PARAM_TYPE => $this->mListModuleNames
ApiBase::PARAM_TYPE => $this->mModuleMgr->getNames( 'list' )
),
'meta' => array(
ApiBase::PARAM_ISMULTI => true,
ApiBase::PARAM_TYPE => $this->mMetaModuleNames
ApiBase::PARAM_TYPE => $this->mModuleMgr->getNames( 'meta' )
),
'generator' => array(
ApiBase::PARAM_TYPE => $this->mAllowedGenerators
@ -676,11 +660,11 @@ class ApiQuery extends ApiBase {
$querySeparator = str_repeat( '--- ', 12 );
$moduleSeparator = str_repeat( '*** ', 14 );
$msg = "\n$querySeparator Query: Prop $querySeparator\n\n";
$msg .= $this->makeHelpMsgHelper( $this->mQueryPropModules, 'prop' );
$msg .= $this->makeHelpMsgHelper( 'prop' );
$msg .= "\n$querySeparator Query: List $querySeparator\n\n";
$msg .= $this->makeHelpMsgHelper( $this->mQueryListModules, 'list' );
$msg .= $this->makeHelpMsgHelper( 'list' );
$msg .= "\n$querySeparator Query: Meta $querySeparator\n\n";
$msg .= $this->makeHelpMsgHelper( $this->mQueryMetaModules, 'meta' );
$msg .= $this->makeHelpMsgHelper( 'meta' );
$msg .= "\n\n$moduleSeparator Modules: continuation $moduleSeparator\n\n";
// Use parent to make default message for the query module
@ -690,21 +674,22 @@ class ApiQuery extends ApiBase {
}
/**
* For all modules in $moduleList, generate help messages and join them together
* @param $moduleList Array array(modulename => classname)
* @param $paramName string Parameter name
* For all modules of a given group, generate help messages and join them together
* @param $group string Module group
* @return string
*/
private function makeHelpMsgHelper( $moduleList, $paramName ) {
private function makeHelpMsgHelper( $group ) {
$moduleDescriptions = array();
foreach ( $moduleList as $moduleName => $moduleClass ) {
$moduleNames = $this->mModuleMgr->getNames( $group );
sort( $moduleNames );
foreach ( $moduleNames as $name ) {
/**
* @var $module ApiQueryBase
*/
$module = new $moduleClass( $this, $moduleName, null );
$module = $this->mModuleMgr->getModule( $name );
$msg = ApiMain::makeHelpMsgHeader( $module, $paramName );
$msg = ApiMain::makeHelpMsgHeader( $module, $group );
$msg2 = $module->makeHelpMsg();
if ( $msg2 !== false ) {
$msg .= $msg2;