From fd68ee851a3a9ff82c5c987ef08e9863f2d1b3b1 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Mon, 25 Sep 2006 06:10:16 +0000 Subject: [PATCH] * API: General query modules order of execution * API: Moved title parsing logic to ApiPageSet --- includes/api/ApiFormatBase.php | 2 +- includes/api/ApiPageSet.php | 72 ++++++++++++++--- includes/api/ApiQuery.php | 137 +++++++++++++++++++-------------- 3 files changed, 140 insertions(+), 71 deletions(-) diff --git a/includes/api/ApiFormatBase.php b/includes/api/ApiFormatBase.php index c26d62a7bc..7bfad21550 100644 --- a/includes/api/ApiFormatBase.php +++ b/includes/api/ApiFormatBase.php @@ -139,7 +139,7 @@ abstract class ApiFormatBase extends ApiBase { // identify URLs $text = ereg_replace("[a-zA-Z]+://[^ '()<\n]+", '\\0', $text); // identify requests to api.php - $text = ereg_replace("\\0', $text); + $text = ereg_replace("api\\.php\\?[^ ()<\n\t]+", '\\0', $text); // make strings inside * bold $text = ereg_replace("\\*[^<>\n]+\\*", '\\0', $text); diff --git a/includes/api/ApiPageSet.php b/includes/api/ApiPageSet.php index 20fae3d310..d19048c2c5 100644 --- a/includes/api/ApiPageSet.php +++ b/includes/api/ApiPageSet.php @@ -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"); + } } ?> \ No newline at end of file diff --git a/includes/api/ApiQuery.php b/includes/api/ApiQuery.php index bc73f4874c..46fdc24188 100644 --- a/includes/api/ApiQuery.php +++ b/includes/api/ApiQuery.php @@ -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'); - - // Normalize titles - $linkBatch = $this->ProcessTitles($titles); + $data = new ApiPageSet($this->GetMain(), $this->GetDB(), $redirects); + + 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); + + // 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 redirects information + // 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'); } } - } - - /** - * 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'); - } - } + // Execute all requested modules. + foreach ($modules as $module) + $module->Execute(); + } - 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' ); } } -- 2.20.1