* This means queries could possibly return fewer results than the limit and still set a query-continue
* Add iicontinue, rvcontinue, cicontinue, incontinue, amfrom to faciliate query-continue for these modules
* Implemented by blocking additions to the ApiResult object if they would make it too large
** Important things like query-continue values and warnings are exempt from this check
** RSS feeds and exported XML are also exempted (size-checking them would be too messy)
** Result size is checked against $wgAPIMaxResultSize, which defaults to 8 MB
For those who really care, per-file details follow:
ApiResult.php:
* Introduced ApiResult::$mSize which keeps track of the result size.
* Introduced ApiResult::size() which calculates an array's size
(which is the sum of the strlen()s of its elements).
* ApiResult::addValue() now checks that the result size stays below
$wgAPIMaxResultSize. If the item won't fit, it won't be added and addValue()
will return false. Callers should check the return value and set a
query-continue if it's false.
* Closed the back door that is ApiResult::getData(): callers can't manipulate
the data array directly anymore so they can't bypass the result size limit.
* Added ApiResult::setIndexedTagName_internal() which will call
setIndexedTagName() on an array already in the result. This is needed for the
'new' order of adding results, which means addValue()ing one result at a time
until you hit the limit or run out, then calling this function to set the tag
name.
* Added ApiResult::disableSizeCheck() and enableSizeCheck() which disable and
enable size checking in addValue(). This is used for stuff like query-continue
elements and warnings which shouldn't count towards the result size.
* Added ApiResult::unsetValue() which removes an element from the result and
decreases $mSize.
ApiBase.php:
* Like ApiResult::getData(), ApiBase::getResultData() no longer returns a
reference.
* Use ApiResult::disableSizeCheck() in ApiBase::setWarning()
ApiQueryBase.php:
* Added ApiQueryBase::addPageSubItem(), which adds page subitems one item
at a time.
* addPageSubItem() and addPageSubItems() now return whether the subitem
fit in the result.
* Use ApiResult::disableSizeCheck() in setContinueEnumParameter()
ApiMain.php:
* Use ApiResult::disableSizeCheck() in ApiMain::substituteResultWithError()
* Use getParameter() rather than $mRequest to obtain requestid
DefaultSettings.php:
* Added $wgAPIMaxResultSize, with a default value of 8 MB
ApiQuery*.php:
* Added results one at a time, and set a query-continue if the result is full.
ApiQueryLangLinks.php and friends:
* Migrated from addPageSubItems() to addPageSubItem(). This eliminates the
need for $lastId.
ApiQueryAllLinks.php, ApiQueryWatchlist.php, ApiQueryAllimages.php, ApiQuerySearch.php:
* Renamed $data to something more appropriate ($pageids, $ids or $titles)
ApiQuerySiteinfo.php:
* Abuse siprop as a query-continue parameter and set it to all props that
couldn't be processed.
ApiQueryRandom.php:
* Doesn't do continuations, because the result is supposed to be random.
* Be smart enough to not run the second query if the results of the first
didn't fit.
ApiQueryImageInfo.php, ApiQueryRevisions.php, ApiQueryCategoryInfo.php, ApiQueryInfo.php:
* Added continue parameter which basically skips the first so many items
ApiQueryBacklinks.php:
* Throw the result in a big array first and addValue() that one element at a time if necessary
** This is necessary because the results aren't retrieved in order
* Introduced $this->pageMap to map namespace and title to page ID
* Rewritten extractRowInfo() and extractRedirRowInfo() a little
* Declared all private member variables explicitly
ApiQueryDeletedrevs.php:
* Use a pagemap just like in Backlinks
* Introduce fake page IDs and keep track of them so we know where to add what
** This doesn't change the output format, because the fake page IDs start at 0 and are consecutive
ApiQueryAllmessages.php:
* Add amfrom to facilitate query-continue
ApiQueryUsers.php:
* Rewrite: put the getOtherUsersInfo() code in execute()
*/
$wgAPIMaxDBRows = 5000;
+/**
+ * The maximum size (in bytes) of an API result.
+ * Don't set this lower than $wgMaxArticleSize*1024
+ */
+$wgAPIMaxResultSize = 8388608;
+
/**
* Parser test suite files to be run by parserTests.php when no specific
* filename is passed to it.
/**
* Get the result data array
*/
- public function & getResultData() {
+ public function getResultData() {
return $this->getResult()->getData();
}
* notice any changes in API.
*/
public function setWarning($warning) {
- # If there is a warning already, append it to the existing one
- $data =& $this->getResult()->getData();
+ $data = $this->getResult()->getData();
if(isset($data['warnings'][$this->getModuleName()]))
{
# Don't add duplicate warnings
$warn_regex = preg_quote($warning, '/');
if(preg_match("/{$warn_regex}(\\n|$)/", $data['warnings'][$this->getModuleName()]['*']))
return;
- $warning = "{$data['warnings'][$this->getModuleName()]['*']}\n$warning";
- unset($data['warnings'][$this->getModuleName()]);
+ $oldwarning = $data['warnings'][$this->getModuleName()]['*'];
+ # If there is a warning already, append it to the existing one
+ $warning = "$oldwarning\n$warning";
+ $this->getResult()->unsetValue('warnings', $this->getModuleName());
}
$msg = array();
ApiResult :: setContent($msg, $warning);
+ $this->getResult()->disableSizeCheck();
$this->getResult()->addValue('warnings', $this->getModuleName(), $msg);
+ $this->getResult()->enableSizeCheck();
}
/**
public static function getBaseVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
public static function setResult($result, $feed, $feedItems) {
// Store output in the Result data.
// This way we can check during execution if any error has occured
- $data = & $result->getData();
- $data['_feed'] = $feed;
- $data['_feeditems'] = $feedItems;
+ // Disable size checking for this because we can't continue
+ // cleanly; size checking would cause more problems than it'd
+ // solve
+ $result->disableSizeCheck();
+ $result->addValue(null, '_feed', $feed);
+ $result->addValue(null, '_feeditems', $feedItems);
+ $result->enableSizeCheck();
}
/**
/**
* This class expects the result data to be in a custom format set by self::setResult()
- * $result['_feed'] - an instance of one of the $wgFeedClasses classes
- * $result['_feeditems'] - an array of FeedItem instances
+ * $result['_feed'] - an instance of one of the $wgFeedClasses classes
+ * $result['_feeditems'] - an array of FeedItem instances
*/
public function execute() {
$data = $this->getResultData();
public function getVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
}
$this->getResult()->reset();
+ $this->getResult()->disableSizeCheck();
// Re-add the id
- if($this->mRequest->getCheck('requestid'))
- $this->getResult()->addValue(null, 'requestid', $this->mRequest->getVal('requestid'));
+ $requestid = $this->getParameter('requestid');
+ if(!is_null($requestid))
+ $this->getResult()->addValue(null, 'requestid', $requestid);
$this->getResult()->addValue(null, 'error', $errMessage);
return $errMessage['code'];
*/
protected function executeAction() {
// First add the id to the top element
- if($this->mRequest->getCheck('requestid'))
- $this->getResult()->addValue(null, 'requestid', $this->mRequest->getVal('requestid'));
+ $requestid = $this->getParameter('requestid');
+ if(!is_null($requestid))
+ $this->getResult()->addValue(null, 'requestid', $requestid);
$params = $this->extractRequestParams();
public function __toString() {
return "{$this->getCodeString()}: {$this->getMessage()}";
}
-}
+}
\ No newline at end of file
$pageSet = $this->getPageSet();
$result = $this->getResult();
+ # We don't check for a full result set here because we can't be adding
+ # more than 380K. The maximum revision size is in the megabyte range,
+ # and the maximum result size must be even higher than that.
+
// Title normalizations
$normValues = array ();
foreach ($pageSet->getNormalizedTitles() as $rawTitleStr => $titleStr) {
$exporter->closeStream();
$exportxml = ob_get_contents();
ob_end_clean();
+ // Don't check the size of exported stuff
+ // It's not continuable, so it would cause more
+ // problems than it'd solve
+ $result->disableSizeCheck();
if ($this->params['exportnowrap']) {
$result->reset();
// Raw formatter will handle this
ApiResult::setContent($r, $exportxml);
$result->addValue('query', 'export', $r);
}
+ $result->enableSizeCheck();
}
}
}
$vers[] = $psModule->getVersion();
return $vers;
}
-}
+}
\ No newline at end of file
}
if( isset( $prop['hidden'] ) && $row->cat_hidden )
$item['hidden'] = '';
- $categories[] = $item;
+ $fit = $result->addValue(array('query', $this->getModuleName()), null, $item);
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('from', $this->keyToTitle($row->cat_title));
+ break;
+ }
}
}
$db->freeResult($res);
if (is_null($resultPageSet)) {
- $result->setIndexedTagName($categories, 'c');
- $result->addValue('query', $this->getModuleName(), $categories);
+ $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'c');
} else {
$resultPageSet->populateFromTitles($pages);
}
public function getVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
$res = $this->select(__METHOD__);
- $data = array ();
+ $pageids = array ();
$count = 0;
+ $result = $this->getResult();
while ($row = $db->fetchObject($res)) {
if (++ $count > $limit) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
$vals['ns'] = intval($title->getNamespace());
$vals['title'] = $title->getPrefixedText();
}
- $data[] = $vals;
+ $fit = $result->addValue(array('query', $this->getModuleName()), null, $vals);
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('continue',
+ $this->keyToTitle($row->pl_title) . "|" . $row->pl_from);
+ break;
+ }
} else {
$pageids[] = $row->pl_from;
}
$db->freeResult($res);
if (is_null($resultPageSet)) {
- $result = $this->getResult();
- $result->setIndexedTagName($data, 'l');
- $result->addValue('query', $this->getModuleName(), $data);
+ $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'l');
} else {
$resultPageSet->populateFromPageIDs($pageids);
}
public function getVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
if (!$row || $lastUser !== $row->user_name) {
// Save the last pass's user data
if (is_array($lastUserData))
- $data[] = $lastUserData;
+ {
+ $fit = $result->addValue(array('query', $this->getModuleName()),
+ null, $lastUserData);
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('from',
+ $this->keyToTitle($lastUserData['name']));
+ break;
+ }
+ }
// No more rows left
if (!$row)
$db->freeResult($res);
- $result->setIndexedTagName($data, 'u');
- $result->addValue('query', $this->getModuleName(), $data);
+ $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'u');
}
public function getAllowedParams() {
public function getVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
$res = $this->select(__METHOD__);
- $data = array ();
+ $titles = array();
$count = 0;
$result = $this->getResult();
while ($row = $db->fetchObject($res)) {
if (is_null($resultPageSet)) {
$file = $repo->newFileFromRow( $row );
- $data[] = array_merge(array('name' => $row->img_name),
+ $info = array_merge(array('name' => $row->img_name),
ApiQueryImageInfo::getInfo($file, $prop, $result));
+ $fit = $result->addValue(array('query', $this->getModuleName()), null, $info);
+ if( !$fit ) {
+ $this->setContinueEnumParameter('from', $this->keyToTitle($row->img_name));
+ break;
+ }
} else {
- $data[] = Title::makeTitle(NS_FILE, $row->img_name);
+ $titles[] = Title::makeTitle(NS_IMAGE, $row->img_name);
}
}
$db->freeResult($res);
if (is_null($resultPageSet)) {
- $result = $this->getResult();
- $result->setIndexedTagName($data, 'img');
- $result->addValue('query', $this->getModuleName(), $data);
+ $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'img');
} else {
- $resultPageSet->populateFromTitles( $data );
+ $resultPageSet->populateFromTitles($titles);
}
}
//Get all requested messages
$messages = array();
foreach( $messages_target as $message ) {
+ if(!is_null($params['from']))
+ if($message < $params['from'])
+ continue;
$messages[$message] = wfMsg( $message );
}
} else {
$result->setContent( $message, $value );
}
- $messages_out[] = $message;
+ $fit = $result->addValue(array('query', $this->getModuleName()), null, $message);
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('from', $name);
+ break;
+ }
}
- $result->setIndexedTagName( $messages_out, 'message' );
- $result->addValue( 'query', $this->getModuleName(), $messages_out );
+ $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'message');
}
public function getAllowedParams() {
),
'filter' => array(),
'lang' => null,
+ 'from' => null,
);
}
'messages' => 'Which messages to output. "*" means all messages',
'filter' => 'Return only messages that contain this string',
'lang' => 'Return messages in this language',
+ 'from' => 'Return messages starting at this message',
);
}
public function getVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
$this->addOption('LIMIT', $limit+1);
$res = $this->select(__METHOD__);
- $data = array ();
$count = 0;
+ $result = $this->getResult();
while ($row = $db->fetchObject($res)) {
if (++ $count > $limit) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
if (is_null($resultPageSet)) {
$title = Title :: makeTitle($row->page_namespace, $row->page_title);
- $data[] = array(
+ $vals = array(
'pageid' => intval($row->page_id),
'ns' => intval($title->getNamespace()),
'title' => $title->getPrefixedText());
+ $fit = $result->addValue(array('query', $this->getModuleName()), null, $vals);
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('from', $this->keyToTitle($row->page_title));
+ break;
+ }
} else {
$resultPageSet->processDbRow($row);
}
$db->freeResult($res);
if (is_null($resultPageSet)) {
- $result = $this->getResult();
- $result->setIndexedTagName($data, 'p');
- $result->addValue('query', $this->getModuleName(), $data);
+ $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'p');
}
}
public function getVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
*/
class ApiQueryBacklinks extends ApiQueryGeneratorBase {
- private $params, $rootTitle, $contRedirs, $contLevel, $contTitle, $contID, $redirID;
+ private $params, $rootTitle, $contRedirs, $contLevel, $contTitle, $contID, $redirID, $redirect;
+ private $bl_ns, $bl_from, $bl_table, $bl_code, $bl_title, $bl_sort, $bl_fields, $hasNS;
+ private $pageMap, $resultArr;
// output element name, database column field prefix, database table
private $backlinksSettings = array (
public function __construct($query, $moduleName) {
extract($this->backlinksSettings[$moduleName]);
+ $this->resultArr = array();
parent :: __construct($query, $moduleName, $code);
$this->bl_ns = $prefix . '_namespace';
$res = $this->select(__METHOD__.'::firstQuery');
$count = 0;
- $this->data = array ();
+ $this->pageMap = array(); // Maps ns and title to pageid
$this->continueStr = null;
$this->redirTitles = array();
while ($row = $db->fetchObject($res)) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
// We need to keep the parent page of this redir in
if($this->hasNS)
- $contTitle = Title::makeTitle($row->{$this->bl_ns}, $row->{$this->bl_title});
+ $parentID = $this->pageMap[$row->{$this->bl_ns}][$row->{$this->bl_title}];
else
- $contTitle = Title::makeTitle(NS_FILE, $row->{$this->bl_title});
- $this->continueStr = $this->getContinueRedirStr($contTitle->getArticleID(), $row->page_id);
+ $parentID = $this->pageMap[NS_IMAGE][$row->{$this->bl_title}];
+ $this->continueStr = $this->getContinueRedirStr($parentID, $row->page_id);
break;
}
}
$db->freeResult($res);
}
+ // Try to add the result data in one go and pray that it fits
+ $fit = $this->getResult()->addValue('query', $this->getModuleName(), $this->resultArr);
+ if(!$fit)
+ {
+ // It didn't fit. Add elements one by one until the
+ // result is full.
+ foreach($this->resultArr as $pageID => $arr)
+ {
+ // Add the basic entry without redirlinks first
+ $fit = $this->getResult()->addValue(
+ array('query', $this->getModuleName()),
+ $pageID, array_diff_key($arr, array('redirlinks' => '')));
+ if(!$fit)
+ {
+ $this->continueStr = $this->getContinueStr($pageID);
+ break;
+ }
+
+ $hasRedirs = false;
+ foreach((array)@$arr['redirlinks'] as $key => $redir)
+ {
+ $fit = $this->getResult()->addValue(
+ array('query', $this->getModuleName(), $pageID, 'redirlinks'),
+ $key, $redir);
+ if(!$fit)
+ {
+ $this->continueStr = $this->getContinueRedirStr($pageID, $redir['pageid']);
+ break;
+ }
+ $hasRedirs = true;
+ }
+ if($hasRedirs)
+ $this->getResult()->setIndexedTagName_internal(
+ array('query', $this->getModuleName(), $pageID, 'redirlinks'),
+ $this->bl_code);
+ if(!$fit)
+ break;
+ }
+ }
if(!is_null($this->continueStr))
$this->setContinueEnumParameter('continue', $this->continueStr);
if (is_null($resultPageSet)) {
- $resultData = array();
- foreach($this->data as $ns => $a)
- foreach($a as $title => $arr)
- $resultData[] = $arr;
- $result = $this->getResult();
- $result->setIndexedTagName($resultData, $this->bl_code);
- $result->addValue('query', $this->getModuleName(), $resultData);
+ $this->getResult()->setIndexedTagName_internal(
+ array('query', $this->getModuleName()),
+ $this->bl_code);
}
}
private function extractRowInfo($row) {
- if(!isset($this->data[$row->page_namespace][$row->page_title])) {
- $this->data[$row->page_namespace][$row->page_title]['pageid'] = $row->page_id;
- ApiQueryBase::addTitleInfo($this->data[$row->page_namespace][$row->page_title], Title::makeTitle($row->page_namespace, $row->page_title));
- if($row->page_is_redirect)
- {
- $this->data[$row->page_namespace][$row->page_title]['redirect'] = '';
- $this->redirTitles[] = Title::makeTitle($row->page_namespace, $row->page_title);
- }
+ $this->pageMap[$row->page_namespace][$row->page_title] = $row->page_id;
+ $t = Title::makeTitle($row->page_namespace, $row->page_title);
+ $a = array('pageid' => $row->page_id);
+ ApiQueryBase::addTitleInfo($a, $t);
+ if($row->page_is_redirect)
+ {
+ $a['redirect'] = '';
+ $this->redirTitles[] = $t;
}
+ // Put all the results in an array first
+ $this->resultArr[$a['pageid']] = $a;
}
private function extractRedirRowInfo($row)
if($row->page_is_redirect)
$a['redirect'] = '';
$ns = $this->hasNS ? $row->{$this->bl_ns} : NS_FILE;
- $this->data[$ns][$row->{$this->bl_title}]['redirlinks'][] = $a;
- $this->getResult()->setIndexedTagName($this->data[$ns][$row->{$this->bl_title}]['redirlinks'], $this->bl_code);
+ $parentID = $this->pageMap[$ns][$row->{$this->bl_title}];
+ // Put all the results in an array first
+ $this->resultArr[$parentID]['redirlinks'][] = $a;
+ $this->getResult()->setIndexedTagName($this->resultArr[$parentID]['redirlinks'], $this->bl_code);
}
protected function processContinue() {
/**
* Add a sub-element under the page element with the given page ID
* @param int $pageId Page ID
- * @param array $data Data array à la ApiResult
+ * @param array $data Data array à la ApiResult
+ * @return bool Whether the element fit in the result
*/
protected function addPageSubItems($pageId, $data) {
$result = $this->getResult();
$result->setIndexedTagName($data, $this->getModulePrefix());
- $result->addValue(array ('query', 'pages', intval($pageId)),
+ return $result->addValue(array('query', 'pages', intval($pageId)),
$this->getModuleName(),
$data);
}
+
+ /**
+ * Same as addPageSubItems(), but one element of $data
+ * at a time
+ * @param int $pageId Page ID
+ * @param array $data Data array à la ApiResult
+ * @param string $elemname XML element name. If null, getModuleName() is used
+ * @return bool Whether the element fit in the result
+ */
+ protected function addPageSubItem($pageId, $item, $elemname = null) {
+ if(is_null($elemname))
+ $elemname = $this->getModulePrefix();
+ $result = $this->getResult();
+ $fit = $result->addValue(array('query', 'pages', $pageId,
+ $this->getModuleName()), null, $item);
+ if(!$fit)
+ return false;
+ $result->setIndexedTagName_internal(array('query', 'pages', $pageId,
+ $this->getModuleName()), $elemname);
+ return true;
+ }
/**
* Set a query-continue value
* @param $paramValue Parameter value
*/
protected function setContinueEnumParameter($paramName, $paramValue) {
-
$paramName = $this->encodeParamName($paramName);
$msg = array( $paramName => $paramValue );
+ $this->getResult()->disableSizeCheck();
$this->getResult()->addValue('query-continue', $this->getModuleName(), $msg);
+ $this->getResult()->enableSizeCheck();
}
/**
if($row->ipb_allow_usertalk)
$block['allowusertalk'] = '';
}
- $data[] = $block;
+ $fit = $result->addValue(array('query', $this->getModuleName()), null, $block);
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->ipb_timestamp));
+ break;
+ }
}
- $result->setIndexedTagName($data, 'block');
- $result->addValue('query', $this->getModuleName(), $data);
+ $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'block');
}
protected function prepareUsername($user)
public function getVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
if (is_null($resultPageSet)) {
- $data = array();
- $lastId = 0; // database has no ID 0
$count = 0;
while ($row = $db->fetchObject($res)) {
if (++$count > $params['limit']) {
'|' . $this->keyToTitle($row->cl_to));
break;
}
- if ($lastId != $row->cl_from) {
- if($lastId != 0) {
- $this->addPageSubItems($lastId, $data);
- $data = array();
- }
- $lastId = $row->cl_from;
- }
$title = Title :: makeTitle(NS_CATEGORY, $row->cl_to);
-
$vals = array();
ApiQueryBase :: addTitleInfo($vals, $title);
if ($fld_sortkey)
if ($fld_timestamp)
$vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->cl_timestamp);
- $data[] = $vals;
- }
-
- if($lastId != 0) {
- $this->addPageSubItems($lastId, $data);
+ $fit = $this->addPageSubItem($row->cl_from, $vals);
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('continue', $row->cl_from .
+ '|' . $this->keyToTitle($row->cl_to));
+ break;
+ }
}
-
} else {
$titles = array();
public function getVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
}
/**
- * This query adds <categories> subelement to all pages with the list of images embedded into those pages.
+ * This query adds the <categories> subelement to all pages with the list of categories the page is in
*
* @ingroup API
*/
parent :: __construct($query, $moduleName, 'ci');
}
- public function execute() {
+ public function execute() {
+ $params = $this->extractRequestParams();
$alltitles = $this->getPageSet()->getAllTitlesByNamespace();
if ( empty( $alltitles[NS_CATEGORY] ) ) {
return;
'pp_propname' => 'hiddencat')),
));
$this->addFields(array('cat_title', 'cat_pages', 'cat_subcats', 'cat_files', 'pp_propname AS cat_hidden'));
- $this->addWhere(array('cat_title' => $cattitles));
+ $this->addWhere(array('cat_title' => $cattitles));
+ if(!is_null($params['continue']))
+ {
+ // We need to set a LIMIT in order to be able to set
+ // an OFFSET
+ $this->addOption('LIMIT', count($titles));
+ $this->addOption('OFFSET', $params['continue']);
+ }
$db = $this->getDB();
$res = $this->select(__METHOD__);
- $data = array();
$catids = array_flip($cattitles);
+ $count = (int)@$params['continue'];
while($row = $db->fetchObject($res))
{
$vals = array();
$vals['subcats'] = $row->cat_subcats;
if($row->cat_hidden)
$vals['hidden'] = '';
- $this->addPageSubItems($catids[$row->cat_title], $vals);
+ $fit = $this->addPageSubItems($catids[$row->cat_title], $vals);
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('continue', $count);
+ break;
+ }
+ $count++;
}
$db->freeResult($res);
}
+ public function getAllowedParams() {
+ return array (
+ 'continue' => null,
+ );
+ }
+
+ public function getParamDescription() {
+ return array (
+ 'continue' => 'When more results are available, use this to continue',
+ );
+ }
+
public function getDescription() {
return 'Returns information about the given categories';
}
public function getVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
break;
}
- $lastSortKey = $row->cl_sortkey; // detect duplicate sortkeys
-
if (is_null($resultPageSet)) {
$vals = array();
if ($fld_ids)
$vals['sortkey'] = $row->cl_sortkey;
if ($fld_timestamp)
$vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->cl_timestamp);
- $data[] = $vals;
+ $fit = $this->getResult()->addValue(array('query', $this->getModuleName()),
+ null, $vals);
+ if(!$fit)
+ {
+ if ($params['sort'] == 'timestamp')
+ $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->cl_timestamp));
+ else
+ $this->setContinueEnumParameter('continue', $this->getContinueStr($row, $lastSortKey));
+ break;
+ }
} else {
$resultPageSet->processDbRow($row);
}
+ $lastSortKey = $row->cl_sortkey; // detect duplicate sortkeys
}
$db->freeResult($res);
if (is_null($resultPageSet)) {
- $this->getResult()->setIndexedTagName($data, 'cm');
- $this->getResult()->addValue('query', $this->getModuleName(), $data);
+ $this->getResult()->setIndexedTagName_internal(
+ array('query', $this->getModuleName()), 'cm');
}
}
public function getVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
$this->addWhereRange('ar_timestamp', $params['dir'], $params['start'], $params['end']);
}
$res = $this->select(__METHOD__);
- $pages = array();
+ $pageMap = array(); // Maps ns&title to (fake) pageid
$count = 0;
- // First populate the $pages array
+ $newPageID = 0;
while($row = $db->fetchObject($res))
{
if(++$count > $limit)
if($fld_content)
ApiResult::setContent($rev, Revision::getRevisionText($row));
- $t = Title::makeTitle($row->ar_namespace, $row->ar_title);
- if(!isset($pages[$t->getPrefixedText()]))
+ if(!isset($pageMap[$row->ar_namespace][$row->ar_title]))
{
- $pages[$t->getPrefixedText()] = array(
+ $pageID = $newPageID++;
+ $pageMap[$row->ar_namespace][$row->ar_title] = $pageID;
+ $t = Title::makeTitle($row->ar_namespace, $row->ar_title);
+ $a = array(
'title' => $t->getPrefixedText(),
'ns' => intval($row->ar_namespace),
'revisions' => array($rev)
);
+ $result->setIndexedTagName($a['revisions'], 'rev');
if($fld_token)
- $pages[$t->getPrefixedText()]['token'] = $token;
+ $a['token'] = $token;
+ $fit = $result->addValue(array('query', $this->getModuleName()), $pageID, $a);
}
else
- $pages[$t->getPrefixedText()]['revisions'][] = $rev;
+ {
+ $pageID = $pageMap[$row->ar_namespace][$row->ar_title];
+ $fit = $result->addValue(
+ array('query', $this->getModuleName(), $pageID, 'revisions'),
+ null, $rev);
+ }
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->ar_timestamp));
+ break;
+ }
}
$db->freeResult($res);
-
- // We don't want entire pagenames as keys, so let's make this array indexed
- foreach($pages as $page)
- {
- $result->setIndexedTagName($page['revisions'], 'rev');
- $data[] = $page;
- }
- $result->setIndexedTagName($data, 'page');
- $result->addValue('query', $this->getModuleName(), $data);
- }
+ $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'page');
+ }
public function getAllowedParams() {
return array (
public function getVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
$res = $this->select(__METHOD__);
$db = $this->getDB();
$count = 0;
- $data = array();
$titles = array();
- $lastName = '';
while($row = $db->fetchObject($res))
{
if(++$count > $params['limit'])
$titles[] = Title::makeTitle(NS_FILE, $row->dup_name);
else
{
- if($row->orig_name != $lastName)
- {
- if($lastName != '')
- {
- $this->addPageSubItems($images[$lastName], $data);
- $data = array();
- }
- $lastName = $row->orig_name;
- }
-
- $data[] = array(
+ $r = array(
'name' => $row->dup_name,
'user' => $row->dup_user_text,
'timestamp' => wfTimestamp(TS_ISO_8601, $row->dup_timestamp)
);
+ $fit = $this->addPageSubItem($images[$row->orig_name], $r);
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('continue',
+ $this->keyToTitle($row->orig_name) . '|' .
+ $this->keyToTitle($row->dup_name));
+ break;
+ }
}
}
if(!is_null($resultPageSet))
$resultPageSet->populateFromTitles($titles);
- else if($lastName != '')
- $this->addPageSubItems($images[$lastName], $data);
$db->freeResult($res);
}
$res = $this->select(__METHOD__);
- $data = array ();
+ $result = $this->getResult();
$count = 0;
while ($row = $db->fetchObject($res)) {
if (++ $count > $limit) {
}
if ($fld_url)
$vals['url'] = $row->el_to;
- $data[] = $vals;
+ $fit = $result->addValue(array('query', $this->getModuleName()), null, $vals);
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('offset', $offset + $count - 1);
+ break;
+ }
} else {
$resultPageSet->processDbRow($row);
}
$db->freeResult($res);
if (is_null($resultPageSet)) {
- $result = $this->getResult();
- $result->setIndexedTagName($data, $this->getModulePrefix());
- $result->addValue('query', $this->getModuleName(), $data);
+ $result->setIndexedTagName_internal(array('query', $this->getModuleName()),
+ $this->getModulePrefix());
}
}
public function getVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
$db = $this->getDB();
$res = $this->select(__METHOD__);
- $data = array();
- $lastId = 0; // database has no ID 0
$count = 0;
while ($row = $db->fetchObject($res)) {
if (++$count > $params['limit']) {
$this->setContinueEnumParameter('offset', @$params['offset'] + $params['limit']);
break;
}
- if ($lastId != $row->el_from) {
- if($lastId != 0) {
- $this->addPageSubItems($lastId, $data);
- $data = array();
- }
- $lastId = $row->el_from;
- }
-
$entry = array();
ApiResult :: setContent($entry, $row->el_to);
- $data[] = $entry;
- }
-
- if($lastId != 0) {
- $this->addPageSubItems($lastId, $data);
+ $fit = $this->addPageSubItem($row->el_from, $entry);
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('offset', @$params['offset'] + $count - 1);
+ break;
+ }
}
-
$db->freeResult($res);
}
public function getVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
}
$pageIds = $this->getPageSet()->getAllTitlesByNamespace();
+ $cnt = 0;
if (!empty($pageIds[NS_FILE])) {
-
$result = $this->getResult();
$images = RepoGroup::singleton()->findFiles( array_keys( $pageIds[NS_FILE] ) );
foreach ( $images as $img ) {
- $data = array();
-
+ $cnt++;
+ if(!is_null($params['continue']))
+ if($cnt < $params['continue'])
+ continue;
+ $pageId = $pageIds[NS_IMAGE][ $img->getOriginalTitle()->getDBkey() ];
+
+ $fit = $result->addValue(
+ array('query', 'pages', intval($pageId)),
+ 'imagerepository', $img->getRepoName()
+ );
+ if(!$fit)
+ {
+ if(count($pageIds[NS_IMAGE]) == 1)
+ # The user is screwed. imageinfo can't be solely
+ # responsible for exceeding the limit in this case,
+ # so set a query-continue that just returns the same
+ # thing again. When the violating queries have been
+ # out-continued, the result will get through
+ $this->setContinueEnumParameter('start',
+ wfTimestamp(TS_ISO_8601, $img->getTimestamp()));
+ else
+ $this->setContinueEnumParameter('continue', $cnt);
+ break;
+ }
+
// Get information about the current version first
// Check that the current version is within the start-end boundaries
+ $gotOne = false;
if((is_null($params['start']) || $img->getTimestamp() <= $params['start']) &&
(is_null($params['end']) || $img->getTimestamp() >= $params['end'])) {
- $data[] = self::getInfo( $img, $prop, $result, $scale );
+ $gotOne = true;
+ $fit = $this->addPageSubItem($pageId,
+ self::getInfo( $img, $prop, $result, $scale));
+ if(!$fit)
+ {
+ if(count($pageIds[NS_IMAGE]) == 1)
+ # See the 'the user is screwed' comment above
+ $this->setContinueEnumParameter('start',
+ wfTimestamp(TS_ISO_8601, $img->getTimestamp()));
+ else
+ $this->setContinueEnumParameter('continue', $cnt);
+ break;
+ }
}
// Now get the old revisions
// Get one more to facilitate query-continue functionality
- $count = count($data);
+ $count = ($gotOne ? 1 : 0);
$oldies = $img->getHistory($params['limit'] - $count + 1, $params['start'], $params['end']);
foreach($oldies as $oldie) {
if(++$count > $params['limit']) {
// We've reached the extra one which shows that there are additional pages to be had. Stop here...
// Only set a query-continue if there was only one title
if(count($pageIds[NS_FILE]) == 1)
- $this->setContinueEnumParameter('start', $oldie->getTimestamp());
+ $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $oldie->getTimestamp()));
+ break;
+ }
+ $fit = $this->addPageSubItem($pageId,
+ self::getInfo($oldie, $prop, $result));
+ if(!$fit)
+ {
+ if(count($pageIds[NS_IMAGE]) == 1)
+ $this->setContinueEnumParameter('start',
+ wfTimestamp(TS_ISO_8601, $oldie->getTimestamp()));
+ else
+ $this->setContinueEnumParameter('continue', $cnt);
break;
}
- $data[] = self::getInfo( $oldie, $prop, $result );
}
-
- $pageId = $pageIds[NS_FILE][ $img->getOriginalTitle()->getDBkey() ];
- $result->addValue(
- array( 'query', 'pages', intval( $pageId ) ),
- 'imagerepository', $img->getRepoName()
- );
- $this->addPageSubItems($pageId, $data);
+ if(!$fit)
+ break;
}
$missing = array_diff( array_keys( $pageIds[NS_FILE] ), array_keys( $images ) );
- foreach ( $missing as $title )
- $result->addValue(
- array( 'query', 'pages', intval( $pageIds[NS_FILE][$title] ) ),
+ foreach ($missing as $title) {
+ $cnt++;
+ if(!is_null($params['continue']))
+ if($count < $params['continue'])
+ continue;
+ $fit = $result->addValue(
+ array('query', 'pages', intval($pageIds[NS_FILE][$title])),
'imagerepository', ''
);
+ if(!$fit)
+ $this->setContinueEnumParameter('continue', $cnt);
+ }
}
}
'urlheight' => array(
ApiBase :: PARAM_TYPE => 'integer',
ApiBase :: PARAM_DFLT => -1
- )
+ ),
+ 'continue' => null,
);
}
'urlwidth' => array('If iiprop=url is set, a URL to an image scaled to this width will be returned.',
'Only the current version of the image can be scaled.'),
'urlheight' => 'Similar to iiurlwidth. Cannot be used without iiurlwidth',
+ 'continue' => 'When more results are available, use this to continue',
);
}
$res = $this->select(__METHOD__);
if (is_null($resultPageSet)) {
-
- $data = array();
- $lastId = 0; // database has no ID 0
$count = 0;
while ($row = $db->fetchObject($res)) {
if (++$count > $params['limit']) {
'|' . $this->keyToTitle($row->il_to));
break;
}
- if ($lastId != $row->il_from) {
- if($lastId != 0) {
- $this->addPageSubItems($lastId, $data);
- $data = array();
- }
- $lastId = $row->il_from;
- }
-
$vals = array();
ApiQueryBase :: addTitleInfo($vals, Title :: makeTitle(NS_FILE, $row->il_to));
- $data[] = $vals;
- }
-
- if($lastId != 0) {
- $this->addPageSubItems($lastId, $data);
+ $fit = $this->addPageSubItem($row->il_from, $vals);
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('continue', $row->il_from .
+ '|' . $this->keyToTitle($row->il_to));
+ break;
+ }
}
-
} else {
$titles = array();
public function getVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
}
}
+ $count = 0;
foreach ( $titles as $pageid => $title ) {
+ $count++;
+ if(!is_null($params['continue']))
+ if($count < $params['continue'])
+ continue;
$pageInfo = array (
'touched' => wfTimestamp(TS_ISO_8601, $pageTouched[$pageid]),
'lastrevid' => intval($pageLatest[$pageid]),
if($title->userCanRead())
$pageInfo['readable'] = '';
- $result->addValue(array (
+ $fit = $result->addValue(array (
'query',
'pages'
), $pageid, $pageInfo);
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('continue', $count);
+ break;
+ }
}
// Get properties for missing titles if requested
if(!is_null($params['token']) || $fld_protection || $fld_talkid || $fld_subjectid ||
$fld_url || $fld_readable)
{
- $res = &$result->getData();
foreach($missing as $pageid => $title) {
+ $count++;
+ if(!is_null($params['continue']))
+ if($count < $params['continue'])
+ continue;
+ $fit = true;
if(!is_null($params['token']))
{
$tokenFunctions = $this->getTokenFunctions();
if($val === false)
$this->setWarning("Action '$t' is not allowed for the current user");
else
- $res['query']['pages'][$pageid][$t . 'token'] = $val;
+ $fit = $result->addValue(
+ array('query', 'pages', $pageid),
+ $t . 'token', $val);
}
}
- if($fld_protection)
+ if($fld_protection && $fit)
{
// Apparently the XML formatting code doesn't like array(null)
// This is painful to fix, so we'll just work around it
if(isset($prottitles[$title->getNamespace()][$title->getDBkey()]))
- $res['query']['pages'][$pageid]['protection'] = $prottitles[$title->getNamespace()][$title->getDBkey()];
+ $val = $prottitles[$title->getNamespace()][$title->getDBkey()];
else
- $res['query']['pages'][$pageid]['protection'] = array();
- $result->setIndexedTagName($res['query']['pages'][$pageid]['protection'], 'pr');
+ $val = array();
+ $result->setIndexedTagName($val, 'pr');
+ $fit = $result->addValue(
+ array('query', 'pages', $pageid),
+ 'protection', $val);
}
- if($fld_talkid && isset($talkids[$title->getNamespace()][$title->getDBKey()]))
- $res['query']['pages'][$pageid]['talkid'] = $talkids[$title->getNamespace()][$title->getDBKey()];
- if($fld_subjectid && isset($subjectids[$title->getNamespace()][$title->getDBKey()]))
- $res['query']['pages'][$pageid]['subjectid'] = $subjectids[$title->getNamespace()][$title->getDBKey()];
- if($fld_url) {
- $res['query']['pages'][$pageid]['fullurl'] = $title->getFullURL();
- $res['query']['pages'][$pageid]['editurl'] = $title->getFullURL('action=edit');
+ if($fld_talkid && isset($talkids[$title->getNamespace()][$title->getDbKey()]) && $fit)
+ $fit = $result->addValue(array('query', 'pages', $pageid), 'talkid',
+ $talkids[$title->getNamespace()][$title->getDbKey()]);
+ if($fld_subjectid && isset($subjectids[$title->getNamespace()][$title->getDbKey()]) && $fit)
+ $fit = $result->addValue(array('query', 'pages', $pageid), 'subjectid',
+ $subjectids[$title->getNamespace()][$title->getDbKey()]);
+ if($fld_url && $fit) {
+ $fit = $result->addValue(array('query', 'pages', $pageid), 'fullurl',
+ $title->getFullURL());
+ if($fit)
+ $fit = $result->addValue(array('query', 'pages', $pageid), 'editurl',
+ $title->getFullURL('action=edit'));
}
- if($fld_readable)
+ if($fld_readable && $fit)
if($title->userCanRead())
- $res['query']['pages'][$pageid]['readable'] = '';
+ $fit = $result->addValue(array('query', 'pages', $pageid), 'readable', '');
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('continue', $count);
+ break;
+ }
}
}
}
ApiBase :: PARAM_DFLT => NULL,
ApiBase :: PARAM_ISMULTI => true,
ApiBase :: PARAM_TYPE => array_keys($this->getTokenFunctions())
- )
+ ),
+ 'continue' => null,
);
}
' subjectid - The page ID of the parent page for each talk page'
),
'token' => 'Request a token to perform a data-modifying action on a page',
+ 'continue' => 'When more results are available, use this to continue',
);
}
$this->addOption('LIMIT', $params['limit'] + 1);
$res = $this->select(__METHOD__);
- $data = array();
- $lastId = 0; // database has no ID 0
$count = 0;
$db = $this->getDB();
while ($row = $db->fetchObject($res)) {
$this->setContinueEnumParameter('continue', "{$row->ll_from}|{$row->ll_lang}");
break;
}
- if ($lastId != $row->ll_from) {
- if($lastId != 0) {
- $this->addPageSubItems($lastId, $data);
- $data = array();
- }
- $lastId = $row->ll_from;
- }
-
$entry = array('lang' => $row->ll_lang);
ApiResult :: setContent($entry, $row->ll_title);
- $data[] = $entry;
- }
-
- if($lastId != 0) {
- $this->addPageSubItems($lastId, $data);
+ $fit = $this->addPageSubItem($row->ll_from, $entry);
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('continue', "{$row->ll_from}|{$row->ll_lang}");
+ break;
+ }
}
-
$db->freeResult($res);
}
public function getVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
$res = $this->select(__METHOD__);
if (is_null($resultPageSet)) {
-
- $data = array();
- $lastId = 0; // database has no ID 0
$count = 0;
while ($row = $db->fetchObject($res)) {
if(++$count > $params['limit']) {
$this->keyToTitle($row->pl_title));
break;
}
- if ($lastId != $row->pl_from) {
- if($lastId != 0) {
- $this->addPageSubItems($lastId, $data);
- $data = array();
- }
- $lastId = $row->pl_from;
- }
-
$vals = array();
ApiQueryBase :: addTitleInfo($vals, Title :: makeTitle($row->pl_namespace, $row->pl_title));
- $data[] = $vals;
- }
-
- if($lastId != 0) {
- $this->addPageSubItems($lastId, $data);
+ $fit = $this->addPageSubItem($row->pl_from, $vals);
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('continue',
+ "{$row->pl_from}|{$row->pl_namespace}|" .
+ $this->keyToTitle($row->pl_title));
+ break;
+ }
}
-
} else {
$titles = array();
public function getVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
$this->addWhere('log_deleted & ' . LogPage::DELETED_USER . ' = 0');
}
- $data = array ();
$count = 0;
$res = $this->select(__METHOD__);
while ($row = $db->fetchObject($res)) {
}
$vals = $this->extractRowInfo($row);
- if($vals)
- $data[] = $vals;
+ if(!$vals)
+ continue;
+ $fit = $this->getResult()->addValue(array('query', $this->getModuleName()), null, $vals);
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->log_timestamp));
+ break;
+ }
}
$db->freeResult($res);
- $this->getResult()->setIndexedTagName($data, 'item');
- $this->getResult()->addValue('query', $this->getModuleName(), $data);
+ $this->getResult()->setIndexedTagName_internal(array('query', $this->getModuleName()), 'item');
}
public static function addLogParams($result, &$vals, $params, $type, $ts) {
$this->addFields($resultPageSet->getPageTableFields());
}
- protected function runQuery(&$data, &$resultPageSet) {
+ protected function runQuery(&$resultPageSet) {
$db = $this->getDB();
$res = $this->select(__METHOD__);
$count = 0;
// Prevent duplicates
if(!in_array($row->page_id, $this->pageIDs))
{
- $data[] = $this->extractRowInfo($row);
+ $fit = $this->getResult()->addValue(
+ array('query', $this->getModuleName()),
+ null, $this->extractRowInfo($row));
+ if(!$fit)
+ # We can't really query-continue a random list.
+ # Return an insanely high value so
+ # $count < $limit is false
+ return 1E9;
$this->pageIDs[] = $row->page_id;
}
}
public function run($resultPageSet = null) {
$params = $this->extractRequestParams();
$result = $this->getResult();
- $data = array();
$this->pageIDs = array();
$this->prepareQuery(wfRandom(), $params['limit'], $params['namespace'], $resultPageSet, $params['redirect']);
- $count = $this->runQuery($data, $resultPageSet);
+ $count = $this->runQuery($resultPageSet);
if($count < $params['limit'])
{
/* We got too few pages, we probably picked a high value
* also the comment in Title::getRandomTitle()
*/
$this->prepareQuery(0, $params['limit'] - $count, $params['namespace'], $resultPageSet, $params['redirect']);
- $this->runQuery($data, $resultPageSet);
+ $this->runQuery($resultPageSet);
}
if(is_null($resultPageSet)) {
- $result->setIndexedTagName($data, 'page');
- $result->addValue('query', $this->getModuleName(), $data);
+ $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'page');
}
}
public function getVersion() {
return __CLASS__ . ': $Id: ApiQueryRandom.php overlordq$';
}
-}
+}
\ No newline at end of file
$this->token = $params['token'];
$this->addOption('LIMIT', $params['limit'] +1);
- $data = array ();
$count = 0;
-
/* Perform the actual query. */
$db = $this->getDB();
$res = $this->select(__METHOD__);
$vals = $this->extractRowInfo($row);
/* Add that row's data to our final output. */
- if($vals)
- $data[] = $vals;
+ if(!$vals)
+ continue;
+ $fit = $this->getResult()->addValue(array('query', $this->getModuleName()), null, $vals);
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->rc_timestamp));
+ break;
+ }
}
$db->freeResult($res);
/* Format the result */
- $result = $this->getResult();
- $result->setIndexedTagName($data, 'rc');
- $result->addValue('query', $this->getModuleName(), $data);
+ $this->getResult()->setIndexedTagName_internal(array('query', $this->getModuleName()), 'rc');
}
/**
public function getVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
ApiBase :: dieDebug(__METHOD__, 'param validation?');
$this->addOption('LIMIT', $limit +1);
+ if(!is_null($continue))
+ $this->addOption('OFFSET', $continue);
$data = array ();
$count = 0;
$this->setContinueEnumParameter('startid', intval($row->rev_id));
break;
}
-
$revision = new Revision( $row );
- $this->getResult()->addValue(
- array (
- 'query',
- 'pages',
- $revision->getPage(),
- 'revisions'),
- null,
- $this->extractRowInfo( $revision ));
- }
- $db->freeResult($res);
-
- // Ensure that all revisions are shown as '<rev>' elements
- $result = $this->getResult();
- if ($result->getIsRawMode()) {
- $data =& $result->getData();
- foreach ($data['query']['pages'] as & $page) {
- if (is_array($page) && array_key_exists('revisions', $page)) {
- $result->setIndexedTagName($page['revisions'], 'rev');
- }
+ //
+ $fit = $this->addPageSubItem($revision->getPage(), $this->extractRowInfo($revision), 'rev');
+ if(!$fit)
+ {
+ if($enumRevMode)
+ $this->setContinueEnumParameter('startid', intval($row->rev_id));
+ else
+ $this->setContinueEnumParameter('continue', $continue + $count - 1);
+ break;
}
}
+ $db->freeResult($res);
}
private function extractRowInfo( $revision ) {
ApiBase :: PARAM_TYPE => array_keys($this->getTokenFunctions()),
ApiBase :: PARAM_ISMULTI => true
),
+ 'continue' => null,
);
}
'generatexml' => 'generate XML parse tree for revision content',
'section' => 'only retrieve the content of this section',
'token' => 'Which tokens to obtain for each revision',
+ 'continue' => 'When more results are available, use this to continue',
);
}
$this->dieUsage("{$what} search is disabled",
"search-{$what}-disabled");
- $data = array ();
+ $titles = array ();
$count = 0;
while( $result = $matches->next() ) {
if (++ $count > $limit) {
$title = $result->getTitle();
if (is_null($resultPageSet)) {
- $data[] = array(
+ $vals = array(
'ns' => intval($title->getNamespace()),
'title' => $title->getPrefixedText());
+ $fit = $this->getResult()->addValue(array('query', $this->getModuleName()), null, $vals);
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('offset', $params['offset'] + $count - 1);
+ break;
+ }
} else {
- $data[] = $title;
+ $titles[] = $title;
}
}
if (is_null($resultPageSet)) {
- $result = $this->getResult();
- $result->setIndexedTagName($data, 'p');
- $result->addValue('query', $this->getModuleName(), $data);
+ $this->getResult()->setIndexedTagName_internal(array('query', $this->getModuleName()), 'p');
} else {
- $resultPageSet->populateFromTitles($data);
+ $resultPageSet->populateFromTitles($titles);
}
}
public function getVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
public function execute() {
$params = $this->extractRequestParams();
+ $done = array();
foreach( $params['prop'] as $p )
{
switch ( $p )
{
case 'general':
- $this->appendGeneralInfo( $p );
+ $fit = $this->appendGeneralInfo( $p );
break;
case 'namespaces':
- $this->appendNamespaces( $p );
+ $fit = $this->appendNamespaces( $p );
break;
case 'namespacealiases':
- $this->appendNamespaceAliases( $p );
+ $fit = $this->appendNamespaceAliases( $p );
break;
case 'specialpagealiases':
- $this->appendSpecialPageAliases( $p );
+ $fit = $this->appendSpecialPageAliases( $p );
break;
case 'magicwords':
- $this->appendMagicWords( $p );
+ $fit = $this->appendMagicWords( $p );
break;
case 'interwikimap':
$filteriw = isset( $params['filteriw'] ) ? $params['filteriw'] : false;
- $this->appendInterwikiMap( $p, $filteriw );
+ $fit = $this->appendInterwikiMap( $p, $filteriw );
break;
case 'dbrepllag':
- $this->appendDbReplLagInfo( $p, $params['showalldb'] );
+ $fit = $this->appendDbReplLagInfo( $p, $params['showalldb'] );
break;
case 'statistics':
- $this->appendStatistics( $p );
+ $fit = $this->appendStatistics( $p );
break;
case 'usergroups':
- $this->appendUserGroups( $p );
+ $fit = $this->appendUserGroups( $p );
break;
case 'extensions':
- $this->appendExtensions( $p );
+ $fit = $this->appendExtensions( $p );
break;
case 'fileextensions':
- $this->appendFileExtensions( $p );
+ $fit = $this->appendFileExtensions( $p );
break;
case 'rightsinfo':
- $this->appendRightsInfo( $p );
+ $fit = $this->appendRightsInfo( $p );
break;
default :
ApiBase :: dieDebug( __METHOD__, "Unknown prop=$p" );
}
+ if(!$fit)
+ {
+ # Abuse siprop as a query-continue parameter
+ # and set it to all unprocessed props
+ $this->setContinueEnumParameter('prop', implode('|',
+ array_diff($params['prop'], $done)));
+ break;
+ }
+ $done[] = $p;
}
}
$data['timezone'] = $tz;
$data['timeoffset'] = $offset;
- $this->getResult()->addValue( 'query', $property, $data );
+ return $this->getResult()->addValue( 'query', $property, $data );
}
protected function appendNamespaces( $property ) {
}
$this->getResult()->setIndexedTagName( $data, 'ns' );
- $this->getResult()->addValue( 'query', $property, $data );
+ return $this->getResult()->addValue( 'query', $property, $data );
}
protected function appendNamespaceAliases( $property ) {
}
$this->getResult()->setIndexedTagName( $data, 'ns' );
- $this->getResult()->addValue( 'query', $property, $data );
+ return $this->getResult()->addValue( 'query', $property, $data );
}
protected function appendSpecialPageAliases( $property ) {
$data[] = $arr;
}
$this->getResult()->setIndexedTagName( $data, 'specialpage' );
- $this->getResult()->addValue( 'query', $property, $data );
+ return $this->getResult()->addValue( 'query', $property, $data );
}
protected function appendMagicWords( $property ) {
$data[] = $arr;
}
$this->getResult()->setIndexedTagName($data, 'magicword');
- $this->getResult()->addValue('query', $property, $data);
+ return $this->getResult()->addValue( 'query', $property, $data );
}
protected function appendInterwikiMap( $property, $filter ) {
$db->freeResult( $res );
$this->getResult()->setIndexedTagName( $data, 'iw' );
- $this->getResult()->addValue( 'query', $property, $data );
+ return $this->getResult()->addValue( 'query', $property, $data );
}
protected function appendDbReplLagInfo( $property, $includeAll ) {
$result = $this->getResult();
$result->setIndexedTagName( $data, 'db' );
- $result->addValue( 'query', $property, $data );
+ return $this->getResult()->addValue( 'query', $property, $data );
}
protected function appendStatistics( $property ) {
$data['activeusers'] = intval( SiteStats::activeUsers() );
$data['admins'] = intval( SiteStats::numberingroup('sysop') );
$data['jobs'] = intval( SiteStats::jobs() );
- $this->getResult()->addValue( 'query', $property, $data );
+ return $this->getResult()->addValue( 'query', $property, $data );
}
protected function appendUserGroups( $property ) {
}
$this->getResult()->setIndexedTagName( $data, 'group' );
- $this->getResult()->addValue( 'query', $property, $data );
+ return $this->getResult()->addValue( 'query', $property, $data );
}
protected function appendFileExtensions( $property ) {
$data[] = array( 'ext' => $ext );
}
$this->getResult()->setIndexedTagName( $data, 'fe' );
- $this->getResult()->addValue( 'query', $property, $data );
+ return $this->getResult()->addValue( 'query', $property, $data );
}
protected function appendExtensions( $property ) {
}
$this->getResult()->setIndexedTagName( $data, 'ext' );
- $this->getResult()->addValue( 'query', $property, $data );
+ return $this->getResult()->addValue( 'query', $property, $data );
}
'text' => $text ? $text : ''
);
- $this->getResult()->addValue( 'query', $property, $data );
+ return $this->getResult()->addValue( 'query', $property, $data );
}
public function getVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
$res = $this->select( __METHOD__ );
//Initialise some variables
- $data = array ();
$count = 0;
$limit = $this->params['limit'];
}
$vals = $this->extractRowInfo($row);
- if ($vals)
- $data[] = $vals;
+ $fit = $this->getResult()->addValue(array('query', $this->getModuleName()), null, $vals);
+ if(!$fit)
+ {
+ if($this->multiUserMode)
+ $this->setContinueEnumParameter('continue', $this->continueStr($row));
+ else
+ $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->rev_timestamp));
+ break;
+ }
}
//Free the database record so the connection can get on with other stuff
$db->freeResult($res);
- //And send the whole shebang out as output.
- $this->getResult()->setIndexedTagName($data, 'item');
- $this->getResult()->addValue('query', $this->getModuleName(), $data);
+ $this->getResult()->setIndexedTagName_internal(array('query', $this->getModuleName()), 'item');
}
/**
public function getVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
$this->prop = array();
}
- if(is_array($params['users'])) {
- $r = $this->getOtherUsersInfo($params['users']);
- $result->setIndexedTagName($r, 'user');
- }
- $result->addValue("query", $this->getModuleName(), $r);
- }
-
- protected function getOtherUsersInfo($users) {
- $goodNames = $retval = array();
+ $users = (array)$params['users'];
+ $goodNames = $done = array();
+ $result = $this->getResult();
// Canonicalize user names
foreach($users as $u) {
$n = User::getCanonicalName($u);
if($n === false || $n === '')
- $retval[] = array('name' => $u, 'invalid' => '');
+ {
+ $vals = array('name' => $u, 'invalid' => '');
+ $fit = $result->addValue(array('query', $this->getModuleName()),
+ null, $vals);
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('users',
+ implode('|', array_diff($users, $done)));
+ $goodNames = array();
+ break;
+ }
+ $done[] = $u;
+ }
else
$goodNames[] = $n;
}
- if(!count($goodNames))
- return $retval;
-
- $db = $this->getDB();
- $this->addTables('user', 'u1');
- $this->addFields('u1.*');
- $this->addWhereFld('u1.user_name', $goodNames);
-
- if(isset($this->prop['groups'])) {
- $this->addTables('user_groups');
- $this->addJoinConds(array('user_groups' => array('LEFT JOIN', 'ug_user=u1.user_id')));
- $this->addFields('ug_group');
- }
- if(isset($this->prop['blockinfo'])) {
- $this->addTables('ipblocks');
- $this->addTables('user', 'u2');
- $u2 = $this->getAliasedName('user', 'u2');
- $this->addJoinConds(array(
- 'ipblocks' => array('LEFT JOIN', 'ipb_user=u1.user_id'),
- $u2 => array('LEFT JOIN', 'ipb_by=u2.user_id')));
- $this->addFields(array('ipb_reason', 'u2.user_name blocker_name'));
- }
+ if(count($goodNames))
+ {
+ $db = $this->getDb();
+ $this->addTables('user', 'u1');
+ $this->addFields('u1.user_name');
+ $this->addWhereFld('u1.user_name', $goodNames);
+ $this->addFieldsIf('u1.user_editcount', isset($this->prop['editcount']));
+ $this->addFieldsIf('u1.user_registration', isset($this->prop['registration']));
+
+ if(isset($this->prop['groups'])) {
+ $this->addTables('user_groups');
+ $this->addJoinConds(array('user_groups' => array('LEFT JOIN', 'ug_user=u1.user_id')));
+ $this->addFields('ug_group');
+ }
+ if(isset($this->prop['blockinfo'])) {
+ $this->addTables('ipblocks');
+ $this->addTables('user', 'u2');
+ $u2 = $this->getAliasedName('user', 'u2');
+ $this->addJoinConds(array(
+ 'ipblocks' => array('LEFT JOIN', 'ipb_user=u1.user_id'),
+ $u2 => array('LEFT JOIN', 'ipb_by=u2.user_id')));
+ $this->addFields(array('ipb_reason', 'u2.user_name blocker_name'));
+ }
- $data = array();
- $res = $this->select(__METHOD__);
- while(($r = $db->fetchObject($res))) {
- $user = User::newFromRow($r);
- $name = $user->getName();
- $data[$name]['name'] = $name;
- if(isset($this->prop['editcount']))
- // No proper member function in User class for this
- $data[$name]['editcount'] = $r->user_editcount;
- if(isset($this->prop['registration']))
- // Nor for this one
- $data[$name]['registration'] = wfTimestampOrNull(TS_ISO_8601, $r->user_registration);
- if(isset($this->prop['groups']))
- // This row contains only one group, others will be added from other rows
- if(!is_null($r->ug_group))
- $data[$name]['groups'][] = $r->ug_group;
- if(isset($this->prop['blockinfo']))
- if(!is_null($r->blocker_name)) {
- $data[$name]['blockedby'] = $r->blocker_name;
- $data[$name]['blockreason'] = $r->ipb_reason;
- }
- if(isset($this->prop['emailable']) && $user->canReceiveEmail())
- $data[$name]['emailable'] = '';
+ $data = array();
+ $res = $this->select(__METHOD__);
+ while(($r = $db->fetchObject($res))) {
+ $data[$r->user_name]['name'] = $r->user_name;
+ if(isset($this->prop['editcount']))
+ $data[$r->user_name]['editcount'] = $r->user_editcount;
+ if(isset($this->prop['registration']))
+ $data[$r->user_name]['registration'] = wfTimestampOrNull(TS_ISO_8601, $r->user_registration);
+ if(isset($this->prop['groups']))
+ // This row contains only one group, others will be added from other rows
+ if(!is_null($r->ug_group))
+ $data[$r->user_name]['groups'][] = $r->ug_group;
+ if(isset($this->prop['blockinfo']))
+ if(!is_null($r->blocker_name)) {
+ $data[$r->user_name]['blockedby'] = $r->blocker_name;
+ $data[$r->user_name]['blockreason'] = $r->ipb_reason;
+ }
+ }
}
-
// Second pass: add result data to $retval
foreach($goodNames as $u) {
if(!isset($data[$u]))
$this->getResult()->setIndexedTagName($data[$u]['groups'], 'g');
$retval[] = $data[$u];
}
+ $done[] = $u;
}
- return $retval;
+ return $this->getResult()->setIndexedTagName_internal(array('query', $this->getModuleName()), 'user');
}
public function getAllowedParams() {
$this->addOption('LIMIT', $params['limit'] +1);
- $data = array ();
+ $ids = array ();
$count = 0;
$res = $this->select(__METHOD__);
if (is_null($resultPageSet)) {
$vals = $this->extractRowInfo($row);
- if ($vals)
- $data[] = $vals;
+ $fit = $this->getResult()->addValue(array('query', $this->getModuleName()), null, $vals);
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('start',
+ wfTimestamp(TS_ISO_8601, $row->rc_timestamp));
+ break;
+ }
} else {
if ($params['allrev']) {
- $data[] = intval($row->rc_this_oldid);
+ $ids[] = intval($row->rc_this_oldid);
} else {
- $data[] = intval($row->rc_cur_id);
+ $ids[] = intval($row->rc_cur_id);
}
}
}
$db->freeResult($res);
if (is_null($resultPageSet)) {
- $this->getResult()->setIndexedTagName($data, 'item');
- $this->getResult()->addValue('query', $this->getModuleName(), $data);
+ $this->getResult()->setIndexedTagName_internal(array('query', $this->getModuleName()), 'item');
}
elseif ($params['allrev']) {
- $resultPageSet->populateFromRevisionIDs($data);
+ $resultPageSet->populateFromRevisionIDs($ids);
} else {
- $resultPageSet->populateFromPageIDs($data);
+ $resultPageSet->populateFromPageIDs($ids);
}
}
public function getVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
$res = $this->select(__METHOD__);
$db = $this->getDB();
- $data = array();
$titles = array();
$count = 0;
while($row = $db->fetchObject($res))
ApiQueryBase::addTitleInfo($vals, $t);
if(isset($prop['changed']) && !is_null($row->wl_notificationtimestamp))
$vals['changed'] = wfTimestamp(TS_ISO_8601, $row->wl_notificationtimestamp);
- $data[] = $vals;
+ $fit = $this->getResult()->addValue($this->getModuleName(), null, $vals);
+ if(!$fit)
+ {
+ $this->setContinueEnumParameter('continue', $row->wl_namespace . '|' .
+ $this->keyToTitle($row->wl_title));
+ break;
+ }
}
else
$titles[] = $t;
}
if(is_null($resultPageSet))
- {
- $this->getResult()->setIndexedTagName($data, 'wr');
- $this->getResult()->addValue(null, $this->getModuleName(), $data);
- }
+ $this->getResult()->setIndexedTagName_internal($this->getModuleName(), 'wr');
else
$resultPageSet->populateFromTitles($titles);
}
public function getVersion() {
return __CLASS__ . ': $Id$';
}
-}
+}
\ No newline at end of file
*/
class ApiResult extends ApiBase {
- private $mData, $mIsRawMode;
+ private $mData, $mIsRawMode, $mSize, $mCheckingSize;
/**
* Constructor
public function __construct($main) {
parent :: __construct($main, 'result');
$this->mIsRawMode = false;
+ $this->mCheckingSize = true;
$this->reset();
}
*/
public function reset() {
$this->mData = array ();
+ $this->mSize = 0;
}
/**
}
/**
- * Get result's internal data array
+ * Get the result's internal data array (read-only)
*/
- public function & getData() {
+ public function getData() {
return $this->mData;
}
+
+ /**
+ * Get the 'real' size of a result item. This means the strlen() of the item,
+ * or the sum of the strlen()s of the elements if the item is an array.
+ * @param mixed $value
+ * @return int
+ */
+ public static function size($value) {
+ $s = 0;
+ if(is_array($value))
+ foreach($value as $v)
+ $s += self::size($v);
+ else if(!is_object($value))
+ // Objects can't always be cast to string
+ $s = strlen($value);
+ return $s;
+ }
+
+ /**
+ * Get the size of the result, i.e. the amount of bytes in it
+ * @return int
+ */
+ public function getSize() {
+ return $this->mSize;
+ }
+
+ /**
+ * Disable size checking in addValue(). Don't use this unless you
+ * REALLY know what you're doing. Values added while size checking
+ * was disabled will not be counted (ever)
+ */
+ public function disableSizeCheck() {
+ $this->mCheckingSize = false;
+ }
+
+ /**
+ * Re-enable size checking in addValue()
+ */
+ public function enableSizeCheck() {
+ $this->mCheckingSize = true;
+ }
/**
* Add an output value to the array by name.
}
}
+ /**
+ * Calls setIndexedTagName() on an array already in the result.
+ * Don't specify a path to a value that's not in the result, or
+ * you'll get nasty errors.
+ * @param array $path Path to the array, like addValue()'s path
+ * @param string $tag
+ */
+ public function setIndexedTagName_internal( $path, $tag ) {
+ $data = & $this->mData;
+ foreach((array)$path as $p)
+ $data = & $data[$p];
+ if(is_null($data))
+ return;
+ $this->setIndexedTagName($data, $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
* If $name is empty, the $value is added as a next list element data[] = $value
+ * @return bool True if $value fits in the result, false if not
*/
public function addValue($path, $name, $value) {
-
- $data = & $this->getData();
+ global $wgAPIMaxResultSize;
+ $data = & $this->mData;
+ if( $this->mCheckingSize ) {
+ $newsize = $this->mSize + self::size($value);
+ if($newsize > $wgAPIMaxResultSize)
+ return false;
+ $this->mSize = $newsize;
+ }
if (!is_null($path)) {
if (is_array($path)) {
$data[] = $value; // Add list element
else
ApiResult :: setElement($data, $name, $value); // Add named element
+ return true;
+ }
+
+ /**
+ * Unset a value previously added to the result set.
+ * Fails silently if the value isn't found.
+ * For parameters, see addValue()
+ */
+ public function unsetValue($path, $name) {
+ $data = & $this->mData;
+ if(!is_null($path))
+ foreach((array)$path as $p) {
+ if(!isset($data[$p]))
+ return;
+ $data = & $data[$p];
+ }
+ $this->mSize -= self::size($data[$name]);
+ unset($data[$name]);
}
/**