* API: General query modules order of execution

* API: Moved title parsing logic to ApiPageSet
This commit is contained in:
Yuri Astrakhan 2006-09-25 06:10:16 +00:00
parent 200a2652ef
commit fd68ee851a
3 changed files with 139 additions and 70 deletions

View file

@ -139,7 +139,7 @@ abstract class ApiFormatBase extends ApiBase {
// identify URLs
$text = ereg_replace("[a-zA-Z]+://[^ '()<\n]+", '<a href="\\0">\\0</a>', $text);
// identify requests to api.php
$text = ereg_replace("<api\\.php\\?[^ ()<\n]+", '<a href="\\0">\\0</a>', $text);
$text = ereg_replace("api\\.php\\?[^ ()<\n\t]+", '<a href="\\0">\\0</a>', $text);
// make strings inside * bold
$text = ereg_replace("\\*[^<>\n]+\\*", '<b>\\0</b>', $text);

View file

@ -29,24 +29,22 @@ if (!defined('MEDIAWIKI')) {
require_once ("ApiBase.php");
}
class ApiPageSet {
class ApiPageSet extends ApiBase {
private $allPages; // [ns][dbkey] => page_id or 0 when missing
private $db, $resolveRedirs;
private $goodTitles, $missingTitles, $redirectTitles;
private $goodTitles, $missingTitles, $redirectTitles, $normalizedTitles;
public function __construct($db, $resolveRedirs) {
public function __construct($main, $db, $resolveRedirs) {
parent :: __construct($main);
$this->db = $db;
$this->resolveRedirs = $resolveRedirs;
$this->allPages = array ();
$this->goodTitles = array ();
$this->missingTitles = array ();
// only when resolving redirects:
if ($resolveRedirs) {
$this->redirectTitles = array ();
}
$this->redirectTitles = array ();
$this->normalizedTitles = array();
}
/**
@ -73,9 +71,53 @@ class ApiPageSet {
return $this->redirectTitles;
}
/**
* Get a list of title normalizations - maps the title given
* with its normalized version.
* @return array raw_prefixed_title (string) => prefixed_title (string)
*/
public function GetNormalizedTitles() {
return $this->normalizedTitles;
}
/**
* Given an array of title strings, convert them into Title objects.
* This method validates access rights for the title,
* and appends normalization values to the output.
*
* @return LinkBatch of title objects.
*/
private function ProcessTitlesStrings($titles) {
$linkBatch = new LinkBatch();
foreach ($titles as $titleString) {
$titleObj = Title :: newFromText($titleString);
// Validation
if (!$titleObj)
$this->dieUsage("bad title $titleString", 'invalidtitle');
if ($titleObj->getNamespace() < 0)
$this->dieUsage("No support for special page $titleString has been implemented", 'unsupportednamespace');
if (!$titleObj->userCanRead())
$this->dieUsage("No read permission for $titleString", 'titleaccessdenied');
$linkBatch->addObj($titleObj);
// Make sure we remember the original title that was given to us
// This way the caller can correlate new titles with the originally requested,
// i.e. namespace is localized or capitalization is different
if ($titleString !== $titleObj->getPrefixedText()) {
$this->normalizedTitles[$titleString] = $titleObj->getPrefixedText();
}
}
return $linkBatch;
}
/**
* This method populates internal variables with page information
* based on the list of page titles given as a LinkBatch object.
* based on the given array of title strings.
*
* Steps:
* #1 For each title, get data from `page` table
@ -87,7 +129,7 @@ class ApiPageSet {
* #5 Substitute the original LinkBatch object with the new list
* #6 Repeat from step #1
*/
public function PopulateTitles($linkBatch) {
public function PopulateTitles($titles) {
$pageFlds = array (
'page_id',
'page_namespace',
@ -97,6 +139,9 @@ class ApiPageSet {
$pageFlds[] = 'page_is_redirect';
}
// Get validated and normalized title objects
$linkBatch = $this->ProcessTitlesStrings($titles);
//
// Repeat until all redirects have been resolved
//
@ -105,8 +150,7 @@ class ApiPageSet {
// Hack: Get the ns:titles stored in array(ns => array(titles)) format
$remaining = $linkBatch->data;
if ($this->resolveRedirs)
$redirectIds = array ();
$redirectIds = array ();
//
// Get data about $linkBatch from `page` table
@ -178,5 +222,9 @@ class ApiPageSet {
$this->db->freeResult($res);
}
}
public function Execute() {
$this->DieDebug("Execute() is not supported on this object");
}
}
?>

View file

@ -41,8 +41,8 @@ class ApiQuery extends ApiBase {
);
private $mQueryPropModules = array (
// 'info' => 'ApiQueryInfo',
// 'categories' => 'ApiQueryCategories',
'info' => 'ApiQueryInfo',
// 'categories' => 'ApiQueryCategories',
// 'imageinfo' => 'ApiQueryImageinfo',
// 'langlinks' => 'ApiQueryLanglinks',
// 'links' => 'ApiQueryLinks',
@ -85,14 +85,25 @@ class ApiQuery extends ApiBase {
return $this->mSlaveDB;
}
/**
* Query execution happens in the following steps:
* #1 Create a PageSet object with any pages requested by the user
* #2 If using generator, execute it to get a new PageSet object
* #3 Instantiate all requested modules.
* This way the PageSet object will know what shared data is required,
* and minimize DB calls.
* #4 Output all normalization and redirect resolution information
* #5 Execute all requested modules
*/
public function Execute() {
$meta = $prop = $list = $generator = $titles = $pageids = $revids = null;
$redirects = null;
extract($this->ExtractRequestParams());
//
// Only one of the titles/pageids/revids is allowed at the same time
// Create and initialize PageSet
//
// Only one of the titles/pageids/revids is allowed at the same time
$dataSource = null;
if (isset ($titles))
$dataSource = 'titles';
@ -107,17 +118,52 @@ class ApiQuery extends ApiBase {
$dataSource = 'revids';
}
if (isset($dataSource) && $dataSource !== 'titles')
$this->DieUsage('Currently only titles= parameter is supported.', 'notimplemented');
$data = new ApiPageSet($this->GetMain(), $this->GetDB(), $redirects);
// Normalize titles
$linkBatch = $this->ProcessTitles($titles);
switch ($dataSource) {
case 'titles' :
$data->PopulateTitles($titles);
break;
case 'pageids' :
$data->PopulatePageIDs($pageids);
break;
case 'titles' :
$data->PopulateRevIDs($revids);
break;
default :
// Do nothing - some queries do not need any of the data sources.
break;
}
// Get titles info from DB
$data = new ApiPageSet($this->GetDB(), $redirects);
$data->PopulateTitles($linkBatch);
//
// If generator is provided, get a new dataset to work on
//
if (isset ($generator))
$data = $this->ExecuteGenerator($generator, $data, $redirects);
// Show redirects information
// Instantiate required modules
// During instantiation, modules may optimize data requests through the $data object
// $data will be lazy loaded when modules begin to request data during execution
$modules = array ();
if (isset($meta))
foreach ($meta as $moduleName)
$modules[] = new $this->mQueryMetaModules[$moduleName] ($this, $moduleName, $data);
if (isset($prop))
foreach ($prop as $moduleName)
$modules[] = new $this->mQueryPropModules[$moduleName] ($this, $moduleName, $data);
if (isset($list))
foreach ($list as $moduleName)
$modules[] = new $this->mQueryListModules[$moduleName] ($this, $moduleName, $data);
// Title normalizations
foreach ($data->GetNormalizedTitles() as $rawTitleStr => $titleStr) {
$this->GetResult()->AddMessage('query', 'normalized', array (
'from' => $rawTitleStr,
'to' => $titleStr
), 'n');
}
// Show redirect information
if ($redirects) {
foreach ($data->GetRedirectTitles() as $titleStrFrom => $titleStrTo) {
$this->GetResult()->AddMessage('query', 'redirects', array (
@ -126,41 +172,15 @@ class ApiQuery extends ApiBase {
), 'r');
}
}
// Execute all requested modules.
foreach ($modules as $module)
$module->Execute();
}
/**
* Given an array of title strings, convert them into Title objects.
* This method validates access rights for the title,
* and appends normalization values to the output.
* @return LinkBatch of title objects.
*/
protected function ProcessTitles($titles) {
$linkBatch = new LinkBatch();
foreach ($titles as $titleString) {
$titleObj = Title :: newFromText($titleString);
// Validation
if (!$titleObj)
$this->dieUsage("bad title $titleString", 'invalidtitle');
if ($titleObj->getNamespace() < 0)
$this->dieUsage("No support for special page $titleString has been implemented", 'unsupportednamespace');
if (!$titleObj->userCanRead())
$this->dieUsage("No read permission for $titleString", 'titleaccessdenied');
$linkBatch->addObj($titleObj);
// Make sure we remember the original title that was given to us
// This way the caller can correlate new titles with the originally requested, i.e. namespace is localized or capitalization
if ($titleString !== $titleObj->getPrefixedText()) {
$this->GetResult()->AddMessage('query', 'normalized', array (
'from' => $titleString,
'to' => $titleObj->getPrefixedText()), 'n');
}
}
return $linkBatch;
protected function ExecuteGenerator($generator, $data, $redirects) {
// TODO: implement
$this->DieUsage("Generator execution has not been implemented", 'notimplemented');
}
protected function GetAllowedParams() {
@ -177,20 +197,20 @@ class ApiQuery extends ApiBase {
GN_ENUM_ISMULTI => true,
GN_ENUM_CHOICES => $this->mListModuleNames
),
'generator' => array (
GN_ENUM_CHOICES => $this->mAllowedGenerators
),
// 'generator' => array (
// GN_ENUM_CHOICES => $this->mAllowedGenerators
// ),
'titles' => array (
GN_ENUM_ISMULTI => true
),
'pageids' => array (
GN_ENUM_TYPE => 'integer',
GN_ENUM_ISMULTI => true
),
'revids' => array (
GN_ENUM_TYPE => 'integer',
GN_ENUM_ISMULTI => true
),
// 'pageids' => array (
// GN_ENUM_TYPE => 'integer',
// GN_ENUM_ISMULTI => true
// ),
// 'revids' => array (
// GN_ENUM_TYPE => 'integer',
// GN_ENUM_ISMULTI => true
// ),
'redirects' => false
);
}
@ -203,7 +223,8 @@ class ApiQuery extends ApiBase {
'generator' => 'Use the output of a list as the input for other prop/list/meta items',
'titles' => 'A list of titles to work on',
'pageids' => 'A list of page IDs to work on',
'revids' => 'A list of revision IDs to work on'
'revids' => 'A list of revision IDs to work on',
'redirects' => 'Automatically resolve redirects'
);
}
@ -217,7 +238,7 @@ class ApiQuery extends ApiBase {
protected function GetExamples() {
return array (
'api.php ? action=query & what=content & titles=ArticleA|ArticleB'
'api.php?action=query&what=content&titles=ArticleA|ArticleB'
);
}
}