* API: General query modules order of execution
* API: Moved title parsing logic to ApiPageSet
This commit is contained in:
parent
200a2652ef
commit
fd68ee851a
3 changed files with 139 additions and 70 deletions
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
|
@ -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'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue