* API: General query modules order of execution
authorYuri Astrakhan <yurik@users.mediawiki.org>
Mon, 25 Sep 2006 06:10:16 +0000 (06:10 +0000)
committerYuri Astrakhan <yurik@users.mediawiki.org>
Mon, 25 Sep 2006 06:10:16 +0000 (06:10 +0000)
* API: Moved title parsing logic to ApiPageSet

includes/api/ApiFormatBase.php
includes/api/ApiPageSet.php
includes/api/ApiQuery.php

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