From f97b323e003ff8007bff05f30156f38bc1a93b5a Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Sun, 1 Oct 2006 02:02:13 +0000 Subject: [PATCH] * API: result data generation cleanup, minor cleaning --- api.php | 4 +- includes/api/ApiBase.php | 104 ++++++++++++++--------------- includes/api/ApiFormatBase.php | 6 +- includes/api/ApiFormatJson.php | 2 +- includes/api/ApiFormatXml.php | 81 ++++++++++++++-------- includes/api/ApiFormatYaml.php | 2 +- includes/api/ApiHelp.php | 2 +- includes/api/ApiLogin.php | 8 +-- includes/api/ApiMain.php | 12 ++-- includes/api/ApiPageSet.php | 16 ++--- includes/api/ApiQuery.php | 47 +++++++++---- includes/api/ApiQueryAllpages.php | 11 ++- includes/api/ApiQueryBase.php | 2 +- includes/api/ApiQueryInfo.php | 2 +- includes/api/ApiQueryRevisions.php | 51 +++++++------- includes/api/ApiQuerySiteinfo.php | 19 +++--- includes/api/ApiResult.php | 91 +++++++++++++++++-------- 17 files changed, 272 insertions(+), 188 deletions(-) diff --git a/api.php b/api.php index d75ef100ab..c96c6470c2 100644 --- a/api.php +++ b/api.php @@ -84,7 +84,7 @@ $wgApiFormats = array ( ); // Initialise common code -require (dirname(__FILE__) . "/includes/WebStart.php"); +require (dirname(__FILE__) . '/includes/WebStart.php'); wfProfileIn('api.php'); // Verify that the API has not been disabled @@ -114,4 +114,4 @@ function apiInitAutoloadClasses($apiAutoloadClasses, $apiDirectory) { foreach ($apiAutoloadClasses as $className => $classFile) $wgAutoloadClasses[$className] = $apiDirectory . $classFile; } -?> \ No newline at end of file +?> diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php index c3ce8e511f..46aa903a17 100644 --- a/includes/api/ApiBase.php +++ b/includes/api/ApiBase.php @@ -69,11 +69,18 @@ abstract class ApiBase { // Main module has getResult() method overriden // Safety - avoid infinite loop: if ($this->isMain()) - $this->dieDebug(__METHOD__ . + ApiBase :: dieDebug(__METHOD__ . ' base method was called on main module. '); return $this->getMain()->getResult(); } + /** + * Get the result data array + */ + public function & getResultData() { + return $this->getResult()->getData(); + } + /** * Generates help message for this module, or false if there is no description */ @@ -115,7 +122,7 @@ abstract class ApiBase { public function makeHelpMsgParameters() { $params = $this->getAllowedParams(); if ($params !== false) { - + $paramsDescription = $this->getParamDescription(); $msg = ''; foreach (array_keys($params) as $paramName) { @@ -125,10 +132,9 @@ abstract class ApiBase { $msg .= sprintf(" %-14s - %s\n", $paramName, $desc); } return $msg; - - } - else - return false; + + } else + return false; } /** @@ -174,7 +180,7 @@ abstract class ApiBase { return $results; } - public function getParameter($paramName, $paramSettings){ + public function getParameter($paramName, $paramSettings) { global $wgRequest; if (!is_array($paramSettings)) { @@ -198,12 +204,12 @@ abstract class ApiBase { if ($type == 'boolean') { if (isset ($default) && $default !== false) { // Having a default value of anything other than 'false' is pointless - $this->dieDebug("Boolean param $paramName's default is set to '$default'"); + ApiBase :: dieDebug("Boolean param $paramName's default is set to '$default'"); } - $value = $wgRequest->getCheck($paramName); + $value = $wgRequest->getCheck($paramName); } else - $value = $wgRequest->getVal($paramName, $default); + $value = $wgRequest->getVal($paramName, $default); if (isset ($value) && ($multi || is_array($type))) $value = $this->parseMultiValue($paramName, $value, $multi, is_array($type) ? $type : null); @@ -222,33 +228,33 @@ abstract class ApiBase { break; case 'limit' : if (!isset ($paramSettings[GN_ENUM_MAX1]) || !isset ($paramSettings[GN_ENUM_MAX2])) - $this->dieDebug("MAX1 or MAX2 are not defined for the limit $paramName"); + ApiBase :: dieDebug("MAX1 or MAX2 are not defined for the limit $paramName"); if ($multi) - $this->dieDebug("Multi-values not supported for $paramName"); + ApiBase :: dieDebug("Multi-values not supported for $paramName"); $min = isset ($paramSettings[GN_ENUM_MIN]) ? $paramSettings[GN_ENUM_MIN] : 0; $value = intval($value); $this->validateLimit($paramName, $value, $min, $paramSettings[GN_ENUM_MAX1], $paramSettings[GN_ENUM_MAX2]); break; case 'boolean' : if ($multi) - $this->dieDebug("Multi-values not supported for $paramName"); + ApiBase :: dieDebug("Multi-values not supported for $paramName"); break; case 'timestamp' : if ($multi) - $this->dieDebug("Multi-values not supported for $paramName"); + ApiBase :: dieDebug("Multi-values not supported for $paramName"); $value = $this->prepareTimestamp($value); // Adds quotes around timestamp break; default : - $this->dieDebug("Param $paramName's type is unknown - $type"); + ApiBase :: dieDebug("Param $paramName's type is unknown - $type"); } } - return $value; + return $value; } - + /** - * Return an array of values that were given in a "a|b|c" notation, + * Return an array of values that were given in a 'a|b|c' notation, * after it optionally validates them against the list allowed values. * * @param valueName - The name of the parameter (for error reporting) @@ -266,7 +272,7 @@ abstract class ApiBase { if (is_array($allowedValues)) { $unknownValues = array_diff($valuesList, $allowedValues); if ($unknownValues) { - $this->dieUsage("Unrecognised value" . (count($unknownValues) > 1 ? "s '" : " '") . implode("', '", $unknownValues) . "' for parameter '$valueName'", "unknown_$valueName"); + $this->dieUsage('Unrecognised value' . (count($unknownValues) > 1 ? "s '" : " '") . implode("', '", $unknownValues) . "' for parameter '$valueName'", "unknown_$valueName"); } } @@ -315,75 +321,70 @@ abstract class ApiBase { /** * Internal code errors should be reported with this method */ - protected function dieDebug($message) { - wfDebugDieBacktrace("Internal error in '{get_class($this)}': $message"); + protected static function dieDebug($message) { + wfDebugDieBacktrace("Internal error: $message"); } - + /** * Profiling: total module execution time */ - private $mTimeIn = 0, $mModuleTime = 0; - + private $mTimeIn = 0, $mModuleTime = 0; + /** * Start module profiling */ - public function profileIn() - { + public function profileIn() { if ($this->mTimeIn !== 0) - $this->dieDebug(__FUNCTION__ . ' called twice without calling profileOut()'); + ApiBase :: dieDebug(__FUNCTION__ . ' called twice without calling profileOut()'); $this->mTimeIn = microtime(true); } - + /** * End module profiling */ - public function profileOut() - { + public function profileOut() { if ($this->mTimeIn === 0) - $this->dieDebug(__FUNCTION__ . ' called without calling profileIn() first'); + ApiBase :: dieDebug(__FUNCTION__ . ' called without calling profileIn() first'); if ($this->mDBTimeIn !== 0) - $this->dieDebug(__FUNCTION__ . ' must be called after database profiling is done with profileDBOut()'); + ApiBase :: dieDebug(__FUNCTION__ . ' must be called after database profiling is done with profileDBOut()'); $this->mModuleTime += microtime(true) - $this->mTimeIn; $this->mTimeIn = 0; } - + /** * Total time the module was executed */ - public function getProfileTime() - { + public function getProfileTime() { if ($this->mTimeIn !== 0) - $this->dieDebug(__FUNCTION__ . ' called without calling profileOut() first'); + ApiBase :: dieDebug(__FUNCTION__ . ' called without calling profileOut() first'); return $this->mModuleTime; } - + /** * Profiling: database execution time */ - private $mDBTimeIn = 0, $mDBTime = 0; - + private $mDBTimeIn = 0, $mDBTime = 0; + /** * Start module profiling */ - public function profileDBIn() - { + public function profileDBIn() { if ($this->mTimeIn === 0) - $this->dieDebug(__FUNCTION__ . ' must be called while profiling the entire module with profileIn()'); + ApiBase :: dieDebug(__FUNCTION__ . ' must be called while profiling the entire module with profileIn()'); if ($this->mDBTimeIn !== 0) - $this->dieDebug(__FUNCTION__ . ' called twice without calling profileDBOut()'); + ApiBase :: dieDebug(__FUNCTION__ . ' called twice without calling profileDBOut()'); $this->mDBTimeIn = microtime(true); } - + /** * End database profiling */ - public function profileDBOut() - { + public function profileDBOut() { if ($this->mTimeIn === 0) - $this->dieDebug(__FUNCTION__ . ' must be called while profiling the entire module with profileIn()'); + ApiBase :: dieDebug(__FUNCTION__ . ' must be called while profiling the entire module with profileIn()'); if ($this->mDBTimeIn === 0) - $this->dieDebug(__FUNCTION__ . ' called without calling profileDBIn() first'); + ApiBase :: dieDebug(__FUNCTION__ . ' called without calling profileDBIn() first'); $time = microtime(true) - $this->mDBTimeIn; $this->mDBTimeIn = 0; @@ -391,14 +392,13 @@ abstract class ApiBase { $this->mDBTime += $time; $this->getMain()->mDBTime += $time; } - + /** * Total time the module used the database */ - public function getProfileDBTime() - { + public function getProfileDBTime() { if ($this->mDBTimeIn !== 0) - $this->dieDebug(__FUNCTION__ . ' called without calling profileDBOut() first'); + ApiBase :: dieDebug(__FUNCTION__ . ' called without calling profileDBOut() first'); return $this->mDBTime; } } diff --git a/includes/api/ApiFormatBase.php b/includes/api/ApiFormatBase.php index f0e9921806..e960699b87 100644 --- a/includes/api/ApiFormatBase.php +++ b/includes/api/ApiFormatBase.php @@ -26,7 +26,7 @@ if (!defined('MEDIAWIKI')) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once ('ApiBase.php'); } abstract class ApiFormatBase extends ApiBase { @@ -94,7 +94,7 @@ abstract class ApiFormatBase extends ApiBase { This result is being shown in mFormat?> format, which might not be suitable for your application.
- See API help for more information.
+ See API help for more information.
printText(''); - $this->recXmlPrint('api', $this->getResult()->getData(), $xmlindent); + $this->recXmlPrint('api', $this->getResultData(), $xmlindent); } /** * This method takes an array and converts it into an xml. * There are several noteworthy cases: * - * If array contains a key "_element", then the code assumes that ALL other keys are not important and replaces them with the value['_element']. - * Example: name="root", value = array( "_element"=>"page", "x", "y", "z") creates x y z + * If array contains a key '_element', then the code assumes that ALL other keys are not important and replaces them with the value['_element']. + * Example: name='root', value = array( '_element'=>'page', 'x', 'y', 'z') creates x y z * - * If any of the array's element key is "*", then the code treats all other key->value pairs as attributes, and the value['*'] as the element's content. - * Example: name="root", value = array( "*"=>"text", "lang"=>"en", "id"=>10) creates text + * If any of the array's element key is '*', then the code treats all other key->value pairs as attributes, and the value['*'] as the element's content. + * Example: name='root', value = array( '*'=>'text', 'lang'=>'en', 'id'=>10) creates text * * If neither key is found, all keys become element names, and values become element content. * The method is recursive, so the same rules apply to any sub-arrays. */ function recXmlPrint($elemName, $elemValue, $indent) { - $indstr = ""; if (!is_null($indent)) { $indent += 2; $indstr = "\n" . str_repeat(" ", $indent); + } else { + $indstr = ''; } switch (gettype($elemValue)) { case 'array' : - if (array_key_exists('*', $elemValue)) { + + if (isset ($elemValue['*'])) { $subElemContent = $elemValue['*']; unset ($elemValue['*']); - if (gettype($subElemContent) === 'array') { - $this->printText($indstr . wfElement($elemName, $elemValue, null)); - $this->recXmlPrint($elemName, $subElemContent, $indent); - $this->printText($indstr . ""); - } else { - $this->printText($indstr . wfElement($elemName, $elemValue, $subElemContent)); - } } else { - $this->printText($indstr . wfElement($elemName, null, null)); - if (array_key_exists('_element', $elemValue)) { - $subElemName = $elemValue['_element']; - foreach ($elemValue as $subElemId => & $subElemValue) { - if ($subElemId !== '_element') { - $this->recXmlPrint($subElemName, $subElemValue, $indent); - } + $subElemContent = null; + } + + if (isset ($elemValue['_element'])) { + $subElemIndName = $elemValue['_element']; + unset ($elemValue['_element']); + } else { + $subElemIndName = null; + } + + $indElements = array (); + $subElements = array (); + foreach ($elemValue as $subElemId => & $subElemValue) { + if (gettype($subElemId) === 'integer') { + if (!is_array($subElemValue)) + ApiBase :: dieDebug(__FUNCTION__ . "($elemName, ...) has a scalar indexed value."); + $indElements[] = $subElemValue; + unset ($elemValue[$subElemId]); + } else + if (is_array($subElemValue)) { + $subElements[$subElemId] = $subElemValue; + unset ($elemValue[$subElemId]); } + } + + if (is_null($subElemIndName) && !empty ($indElements)) + ApiBase :: dieDebug(__FUNCTION__ . "($elemName, ...) has integer keys without _element value"); + + if (!empty ($subElements) && !empty ($indElements) && !is_null($subElemContent)) + ApiBase :: dieDebug(__FUNCTION__ . "($elemName, ...) has content and subelements"); + + if (!is_null($subElemContent)) { + $this->printText($indstr . wfElement($elemName, $elemValue, $subElemContent)); + } else + if (empty ($indElements) && empty ($subElements)) { + $this->printText($indstr . wfElement($elemName, $elemValue)); } else { - foreach ($elemValue as $subElemName => & $subElemValue) { - $this->recXmlPrint($subElemName, $subElemValue, $indent); - } + $this->printText($indstr . wfElement($elemName, $elemValue, null)); + + foreach ($subElements as $subElemId => & $subElemValue) + $this->recXmlPrint($subElemId, $subElemValue, $indent); + + foreach ($indElements as $subElemId => & $subElemValue) + $this->recXmlPrint($subElemIndName, $subElemValue, $indent); + + $this->printText($indstr . wfCloseElement($elemName)); } - $this->printText($indstr . ""); - } break; case 'object' : // ignore diff --git a/includes/api/ApiFormatYaml.php b/includes/api/ApiFormatYaml.php index 40b8375c93..8b74b0654a 100644 --- a/includes/api/ApiFormatYaml.php +++ b/includes/api/ApiFormatYaml.php @@ -26,7 +26,7 @@ if (!defined('MEDIAWIKI')) { // Eclipse helper - will be ignored in production - require_once ("ApiFormatBase.php"); + require_once ('ApiFormatBase.php'); } class ApiFormatYaml extends ApiFormatBase { diff --git a/includes/api/ApiHelp.php b/includes/api/ApiHelp.php index bc8aca2bd9..80ca546d30 100644 --- a/includes/api/ApiHelp.php +++ b/includes/api/ApiHelp.php @@ -26,7 +26,7 @@ if (!defined('MEDIAWIKI')) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once ('ApiBase.php'); } class ApiHelp extends ApiBase { diff --git a/includes/api/ApiLogin.php b/includes/api/ApiLogin.php index 4a245451c7..5158c35ec1 100644 --- a/includes/api/ApiLogin.php +++ b/includes/api/ApiLogin.php @@ -26,7 +26,7 @@ if (!defined('MEDIAWIKI')) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once ('ApiBase.php'); } class ApiLogin extends ApiBase { @@ -76,10 +76,10 @@ class ApiLogin extends ApiBase { $result['result'] = 'AuthEmptyPass'; break; default : - $this->dieDebug("Unhandled case value"); + $this->dieDebug('Unhandled case value'); } - $this->getResult()->addMessage('login', null, $result); + $this->getResult()->addValue(null, 'login', $result); } protected function getAllowedParams() { @@ -104,4 +104,4 @@ class ApiLogin extends ApiBase { ); } } -?> \ No newline at end of file +?> diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php index 2f2c12a7f6..f831fb5a8b 100644 --- a/includes/api/ApiMain.php +++ b/includes/api/ApiMain.php @@ -26,7 +26,7 @@ if (!defined('MEDIAWIKI')) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once ('ApiBase.php'); } class ApiMain extends ApiBase { @@ -50,7 +50,7 @@ class ApiMain extends ApiBase { $this->mResult = new ApiResult($this); } - public function getResult() { + public function & getResult() { return $this->mResult; } @@ -123,13 +123,16 @@ class ApiMain extends ApiBase { public function mainDieUsage($description, $errorCode, $httpRespCode = 0) { $this->mResult->Reset(); - $this->mResult->addMessage('error', null, $errorCode); if ($httpRespCode === 0) header($errorCode, true); else header($errorCode, true, $httpRespCode); - $this->mResult->addMessage('usage', null, $this->makeHelpMsg()); + $data = array ( + 'code' => $errorCode + ); + ApiResult :: addContent($data, $this->makeHelpMsg()); + $this->mResult->addValue(null, 'error', $data); throw new UsageException($description, $errorCode); } @@ -191,5 +194,4 @@ class UsageException extends Exception { return "{$this->codestr}: {$this->message}"; } } - ?> \ No newline at end of file diff --git a/includes/api/ApiPageSet.php b/includes/api/ApiPageSet.php index eaa97ae56d..f859d12889 100644 --- a/includes/api/ApiPageSet.php +++ b/includes/api/ApiPageSet.php @@ -26,7 +26,7 @@ if (!defined('MEDIAWIKI')) { // Eclipse helper - will be ignored in production - require_once ("ApiQueryBase.php"); + require_once ('ApiQueryBase.php'); } class ApiPageSet extends ApiQueryBase { @@ -83,14 +83,14 @@ class ApiPageSet extends ApiQueryBase { public function getGoodTitleCount() { return count($this->getGoodTitles()); } - + /** * Get the list of revision IDs (requested with revids= parameter) */ public function getRevisionIDs() { - $this->dieUsage(__FUNCTION__ . " is not implemented", 'notimplemented'); + $this->dieUsage(__FUNCTION__ . ' is not implemented', 'notimplemented'); } - + /** * Returns the number of revisions (requested with revids= parameter) */ @@ -113,7 +113,7 @@ class ApiPageSet extends ApiQueryBase { * #6 Repeat from step #1 */ private function populateTitles($titles, $redirects) { - + $pageFlds = array ( 'page_id', 'page_namespace', @@ -249,13 +249,13 @@ class ApiPageSet extends ApiQueryBase { } private function populatePageIDs($pageids) { - $this->dieUsage(__FUNCTION__ . " is not implemented", 'notimplemented'); + $this->dieUsage(__FUNCTION__ . ' is not implemented', 'notimplemented'); } public function execute() { $titles = $pageids = $revids = $redirects = null; extract($this->extractRequestParams()); - + // Only one of the titles/pageids/revids is allowed at the same time $dataSource = null; if (isset ($titles)) @@ -303,7 +303,7 @@ class ApiPageSet extends ApiQueryBase { 'redirects' => false ); } - + protected function getParamDescription() { return array ( 'titles' => 'A list of titles to work on', diff --git a/includes/api/ApiQuery.php b/includes/api/ApiQuery.php index d882a53da1..fe7dd82f78 100644 --- a/includes/api/ApiQuery.php +++ b/includes/api/ApiQuery.php @@ -26,7 +26,7 @@ if (!defined('MEDIAWIKI')) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once ('ApiBase.php'); } class ApiQuery extends ApiBase { @@ -128,21 +128,31 @@ class ApiQuery extends ApiBase { $modules[] = new $this->mQueryListModules[$moduleName] ($this, $moduleName); // Title normalizations + $normValues = array (); foreach ($this->mData->getNormalizedTitles() as $rawTitleStr => $titleStr) { - $this->getResult()->addMessage('query', 'normalized', array ( + $normValues[] = array ( 'from' => $rawTitleStr, - 'to' => $titleStr, - '*' => '' - ), 'n'); + 'to' => $titleStr + ); + } + + if (!empty ($normValues)) { + ApiResult :: setIndexedTagName($normValues, 'n'); + $this->getResult()->addValue('query', 'normalized', $normValues); } // Show redirect information + $redirValues = array (); foreach ($this->mData->getRedirectTitles() as $titleStrFrom => $titleStrTo) { - $this->getResult()->addMessage('query', 'redirects', array ( + $redirValues[] = array ( 'from' => $titleStrFrom, - 'to' => $titleStrTo, - '*' => '' - ), 'r'); + 'to' => $titleStrTo + ); + } + + if (!empty ($redirValues)) { + ApiResult :: setIndexedTagName($redirValues, 'r'); + $this->getResult()->addValue('query', 'redirects', $redirValues); } // Execute all requested modules. @@ -151,6 +161,12 @@ class ApiQuery extends ApiBase { $module->execute(); $module->profileOut(); } + + // Ensure that pages are shown as '' elements + $data = & $this->getResultData(); + if (isset ($data['query']['pages'])) { + ApiResult :: setIndexedTagName($data['query']['pages'], 'page'); + } } protected function executeGenerator($generator) { @@ -162,14 +178,14 @@ class ApiQuery extends ApiBase { if (isset ($this->mQueryPropModules[$generator])) $className = $this->mQueryPropModules[$generator]; else - $this->dieDebug("Unknown generator=$generator"); + ApiBase :: dieDebug("Unknown generator=$generator"); $module = new $className ($this, $generator, true); // change $this->mData // TODO: implement - $this->dieUsage("Generator execution has not been implemented", 'notimplemented'); + $this->dieUsage('Generator execution has not been implemented', 'notimplemented'); } protected function getAllowedParams() { @@ -189,6 +205,8 @@ class ApiQuery extends ApiBase { // 'generator' => array ( // GN_ENUM_TYPE => $this->mAllowedGenerators // ), + + ); } @@ -234,12 +252,11 @@ class ApiQuery extends ApiBase { /** * Override to add extra parameters from PageSet - */ + */ public function makeHelpMsgParameters() { $module = new ApiPageSet($this); return $module->makeHelpMsgParameters() . parent :: makeHelpMsgParameters(); } - protected function getParamDescription() { return array ( @@ -247,6 +264,8 @@ class ApiQuery extends ApiBase { 'prop' => 'Which properties to get for the titles/revisions/pageids', 'list' => 'Which lists to get', 'generator' => 'Use the output of a list as the input for other prop/list/meta items', + + ); } @@ -264,4 +283,4 @@ class ApiQuery extends ApiBase { ); } } -?> \ No newline at end of file +?> diff --git a/includes/api/ApiQueryAllpages.php b/includes/api/ApiQueryAllpages.php index 0a715eb818..5973250bfe 100644 --- a/includes/api/ApiQueryAllpages.php +++ b/includes/api/ApiQueryAllpages.php @@ -26,7 +26,7 @@ if (!defined('MEDIAWIKI')) { // Eclipse helper - will be ignored in production - require_once ("ApiQueryBase.php"); + require_once ('ApiQueryBase.php'); } class ApiQueryAllpages extends ApiQueryBase { @@ -63,9 +63,9 @@ class ApiQueryAllpages extends ApiQueryBase { 'ORDER BY' => 'page_namespace, page_title' )); $this->profileDBOut(); - + $data = array (); - $data['_element'] = 'p'; + ApiResult :: setIndexedTagName($data, 'p'); $count = 0; while ($row = $db->fetchObject($res)) { if (++ $count > $aplimit) { @@ -73,7 +73,7 @@ class ApiQueryAllpages extends ApiQueryBase { $msg = array ( 'continue' => 'apfrom=' . ApiQueryBase :: keyToTitle($row->page_title )); - $this->getResult()->addMessage('query-status', 'allpages', $msg); + $this->getResult()->addValue('query-status', 'allpages', $msg); break; } @@ -87,13 +87,12 @@ class ApiQueryAllpages extends ApiQueryBase { if ($title->getNamespace() !== 0) $pagedata['ns'] = $title->getNamespace(); $pagedata['title'] = $title->getPrefixedText(); - $pagedata['*'] = ''; $data[$id] = $pagedata; } } $db->freeResult($res); - $this->getResult()->addMessage('query', 'allpages', $data); + $this->getResult()->addValue('query', 'allpages', $data); } protected function getAllowedParams() { diff --git a/includes/api/ApiQueryBase.php b/includes/api/ApiQueryBase.php index 771c3edcc4..10ebc13a88 100644 --- a/includes/api/ApiQueryBase.php +++ b/includes/api/ApiQueryBase.php @@ -26,7 +26,7 @@ if (!defined('MEDIAWIKI')) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once ('ApiBase.php'); } abstract class ApiQueryBase extends ApiBase { diff --git a/includes/api/ApiQueryInfo.php b/includes/api/ApiQueryInfo.php index 2387189d1d..732ecf64df 100644 --- a/includes/api/ApiQueryInfo.php +++ b/includes/api/ApiQueryInfo.php @@ -26,7 +26,7 @@ if (!defined('MEDIAWIKI')) { // Eclipse helper - will be ignored in production - require_once ("ApiQueryBase.php"); + require_once ('ApiQueryBase.php'); } class ApiQueryInfo extends ApiQueryBase { diff --git a/includes/api/ApiQueryRevisions.php b/includes/api/ApiQueryRevisions.php index 83771b9e63..f35075fd7a 100644 --- a/includes/api/ApiQueryRevisions.php +++ b/includes/api/ApiQueryRevisions.php @@ -26,7 +26,7 @@ if (!defined('MEDIAWIKI')) { // Eclipse helper - will be ignored in production - require_once ("ApiQueryBase.php"); + require_once ('ApiQueryBase.php'); } class ApiQueryRevisions extends ApiQueryBase { @@ -42,7 +42,7 @@ class ApiQueryRevisions extends ApiQueryBase { // true when ordered by timestamp from older to newer, false otherwise $dirNewer = ($rvdir === 'newer'); - // If any of those parameters are used, work in "enumeration" mode. + // If any of those parameters are used, work in 'enumeration' mode. // Enum mode can only be used when exactly one page is provided. // Enumerating revisions on multiple pages make it extremelly // difficult to manage continuations and require additional sql indexes @@ -52,14 +52,15 @@ class ApiQueryRevisions extends ApiQueryBase { $pageCount = $data->getGoodTitleCount(); $revCount = $data->getRevisionCount(); + // Optimization -- nothing to do + if ($revCount === 0 && $pageCount === 0) + return; + if ($revCount > 0 && $pageCount > 0) - $this->dieUsage('The rvrevids= parameter may not be used with titles, pageids, and generator options.', 'rv_rvrevids'); + $this->dieUsage('The revids= parameter may not be used with titles, pageids, or generator options.', 'rv_revids'); if ($revCount > 0 && $enumRevMode) - $this->dieUsage('The rvrevids= parameter may not be used with the list options (rvlimit, rvstartid, rvendid, dirNewer, rvstart, rvend).', 'rv_rvrevids'); - - if ($revCount === 0 && $pageCount === 0) - $this->dieUsage('No pages were given. Please use titles, pageids or a generator to provide page(s) to work on.', 'rv_no_pages'); + $this->dieUsage('The revids= parameter may not be used with the list options (rvlimit, rvstartid, rvendid, dirNewer, rvstart, rvend).', 'rv_revids'); if ($revCount === 0 && $pageCount > 1 && $enumRevMode) $this->dieUsage('titles, pageids or a generator was used to supply multiple pages, but the rvlimit, rvstartid, rvendid, dirNewer, rvstart, and rvend parameters may only be used on a single page.', 'rv_multpages'); @@ -107,7 +108,7 @@ class ApiQueryRevisions extends ApiQueryBase { $showContent = true; break; default : - $this->dieDebug("unknown rvprop $prop"); + ApiBase :: dieDebug("unknown rvprop $prop"); } } } @@ -168,7 +169,7 @@ class ApiQueryRevisions extends ApiQueryBase { $rvlimit = $revCount; // assumption testing -- we should never get more then $revCount rows. } else - $this->dieDebug('param validation?'); + ApiBase :: dieDebug('param validation?'); $options['LIMIT'] = $rvlimit +1; @@ -184,13 +185,13 @@ class ApiQueryRevisions extends ApiQueryBase { if (++ $count > $rvlimit) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... if (!$enumRevMode) - $this->dieDebug('Got more rows then expected'); // bug report + ApiBase :: dieDebug('Got more rows then expected'); // bug report $startStr = 'rvstartid=' . $row->rev_id; $msg = array ( 'continue' => $startStr ); - $this->getResult()->addMessage('query-status', 'revisions', $msg); + $this->getResult()->addValue('query-status', 'revisions', $msg); break; } @@ -215,26 +216,28 @@ class ApiQueryRevisions extends ApiQueryBase { $vals['comment'] = $row->rev_comment; if ($showContent) { - $vals['xml:space'] = 'preserve'; - $vals['*'] = Revision :: getRevisionText($row); - } else { - $vals['*'] = ''; // Force all elements to be attributes + ApiResult :: addContent($vals, Revision :: getRevisionText($row)); } - $data[$row->rev_page]['revisions']['_element'] = 'rv'; - $data[$row->rev_page]['revisions'][$row->rev_id] = $vals; + $this->getResult()->addValue(array ( + 'query', + 'pages', + intval($row->rev_page + ), 'revisions'), intval($row->rev_id), $vals); } $db->freeResult($res); - $this->getResult()->addMessage('query', 'allpages', $data); + // Ensure that all revisions are shown as '' elements + $data = & $this->getResultData(); + foreach ($data['query']['pages'] as & $page) { + if (isset ($page['revisions'])) { + ApiResult :: setIndexedTagName($page['revisions'], 'rev'); + } + } } protected function getAllowedParams() { return array ( - 'rvrevids' => array ( - GN_ENUM_ISMULTI => true, - GN_ENUM_TYPE => 'integer' - ), 'rvlimit' => array ( GN_ENUM_DFLT => 0, GN_ENUM_TYPE => 'limit', @@ -274,14 +277,14 @@ class ApiQueryRevisions extends ApiQueryBase { 'Get revision information.', 'This module may be used in several ways:', ' 1) Get data about a set of pages (last revision), by setting titles or pageids parameter.', - ' 2) Get revisions for one given page, by using titles/pageids with rvstart*/rvend*/rvlimit params.', + ' 2) Get revisions for one given page, by using titles/pageids with rvstart/rvend/rvlimit params.', ' 3) Get data about a set of revisions by setting their IDs with revids parameter.' ); } protected function getExamples() { return array ( - 'api.php?action=query&prop=revisions&titles=ArticleA&rvprop=timestamp|user|comment|content' + 'api.php?action=query&prop=revisions&titles=Main%20Page&rvprop=timestamp|user|comment|content' ); } } diff --git a/includes/api/ApiQuerySiteinfo.php b/includes/api/ApiQuerySiteinfo.php index c561f50605..54d3a5cfb4 100644 --- a/includes/api/ApiQuerySiteinfo.php +++ b/includes/api/ApiQuerySiteinfo.php @@ -26,7 +26,7 @@ if (!defined('MEDIAWIKI')) { // Eclipse helper - will be ignored in production - require_once ("ApiQueryBase.php"); + require_once ('ApiQueryBase.php'); } class ApiQuerySiteinfo extends ApiQueryBase { @@ -51,25 +51,26 @@ class ApiQuerySiteinfo extends ApiQueryBase { $data['base'] = $mainPage->getFullUrl(); $data['sitename'] = $wgSitename; $data['generator'] = "MediaWiki $wgVersion"; - $data['case'] = $wgCapitalLinks ? 'first-letter' : 'case-sensitive'; // "case-insensitive" option is reserved for future - $this->getResult()->addMessage('query', $prop, $data); + $data['case'] = $wgCapitalLinks ? 'first-letter' : 'case-sensitive'; // 'case-insensitive' option is reserved for future + $this->getResult()->addValue('query', $prop, $data); break; case 'namespaces' : global $wgContLang; $data = array (); - $data['_element'] = 'ns'; - foreach ($wgContLang->getFormattedNamespaces() as $ns => $title) + foreach ($wgContLang->getFormattedNamespaces() as $ns => $title) { $data[$ns] = array ( - 'id' => $ns, - '*' => $title + 'id' => $ns ); - $this->getResult()->addMessage('query', $prop, $data); + ApiResult :: addContent($data[$ns], $title); + } + ApiResult :: setIndexedTagName($data, 'ns'); + $this->getResult()->addValue('query', $prop, $data); break; default : - $this->dieDebug("Unknown siprop=$prop"); + ApiBase :: dieDebug("Unknown siprop=$prop"); } } } diff --git a/includes/api/ApiResult.php b/includes/api/ApiResult.php index b35aed2f28..d99b759417 100644 --- a/includes/api/ApiResult.php +++ b/includes/api/ApiResult.php @@ -26,7 +26,7 @@ if (!defined('MEDIAWIKI')) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once ('ApiBase.php'); } class ApiResult extends ApiBase { @@ -45,39 +45,72 @@ class ApiResult extends ApiBase { $this->mData = array (); } - function getData() { + function & getData() { return $this->mData; } - function addMessage($mainSection, $subSection, $value, $multiitem = false, $preserveXmlSpacing = false) { - if (!array_key_exists($mainSection, $this->mData)) { - $this->mData[$mainSection] = array (); - } - if ($subSection !== null) { - if (!array_key_exists($subSection, $this->mData[$mainSection])) { - $this->mData[$mainSection][$subSection] = array (); - } - $element = & $this->mData[$mainSection][$subSection]; - } else { - $element = & $this->mData[$mainSection]; - } - if ($multiitem) { - $element['_element'] = $multiitem; - $element[] = $value; - } else { - if (is_array($value)) { - $element = array_merge($element, $value); - } else { - if (array_key_exists('*', $element)) { - $element['*'] .= $value; - } else { - $element['*'] = $value; - } - if ($preserveXmlSpacing) { - $element['xml:space'] = 'preserve'; + /** + * Add an output value to the array by name. + * Verifies that value with the same name has not been added before. + */ + public static function addElement(& $arr, $name, $value) { + if ($arr === null || $name === null || $value === null || !is_array($arr) || is_array($name)) + ApiBase :: dieDebug('Bad parameter for ' . __FUNCTION__); + if (isset ($arr[$name])) + ApiBase :: dieDebug("Attempting to add element $name=$value, existing value is {$arr[$name]}"); + $arr[$name] = $value; + } + + /** + * Adds the content element to the array. + * Use this function instead of hardcoding the '*' element. + */ + public static function addContent(& $arr, $value) { + if (is_array($value)) + ApiBase :: dieDebug('Bad parameter for ' . __FUNCTION__); + ApiResult :: addElement($arr, '*', $value); + } + + // public static function makeContentElement($tag, $value) { + // $result = array(); + // ApiResult::addContent($result, ) + // } + // + /** + * In case the array contains indexed values (in addition to named), + * all indexed values will have the given tag name. + */ + public static function setIndexedTagName(& $arr, $tag) { + // Do not use addElement() as it is ok to call this more than once + if ($arr === null || $tag === null || !is_array($arr) || is_array($tag)) + ApiBase :: dieDebug('Bad parameter for ' . __FUNCTION__); + $arr['_element'] = $tag; + } + + /** + * Add value to the output data at the given path. + * Path is an indexed array, each element specifing the branch at which to add the new value + * Setting $path to array('a','b','c') is equivalent to data['a']['b']['c'] = $value + */ + public function addValue($path, $name, $value) { + + $data = & $this->getData(); + + if (isset ($path)) { + if (is_array($path)) { + foreach ($path as $p) { + if (!isset ($data[$p])) + $data[$p] = array (); + $data = & $data[$p]; } + } else { + if (!isset ($data[$path])) + $data[$path] = array (); + $data = & $data[$path]; } } + + ApiResult :: addElement($data, $name, $value); } /** @@ -104,7 +137,7 @@ class ApiResult extends ApiBase { } public function execute() { - $this->dieDebug("execute() is not supported on Result object"); + $this->dieDebug('execute() is not supported on Result object'); } } ?> -- 2.20.1