];
$cfg['suppress_issue_types'] = array_merge( $cfg['suppress_issue_types'], [
- // approximate error count: 22
- "PhanAccessMethodInternal",
// approximate error count: 19
- "PhanParamReqAfterOpt",
+ "PhanParamReqAfterOpt", // False positives with nullables, ref phan issue #3159
// approximate error count: 110
- "PhanParamTooMany",
- // approximate error count: 63
- "PhanTypeArraySuspicious",
- // approximate error count: 88
- "PhanTypeInvalidDimOffset",
- // approximate error count: 60
- "PhanTypeMismatchArgument",
+ "PhanParamTooMany", // False positives with variargs. Unsuppress after dropping HHVM
+
+ // approximate error count: 22
+ "PhanAccessMethodInternal",
// approximate error count: 36
"PhanUndeclaredConstant",
+ // approximate error count: 60
+ "PhanTypeMismatchArgument",
// approximate error count: 219
"PhanUndeclaredMethod",
// approximate error count: 752
"PhanUndeclaredProperty",
- // approximate error count: 53
- "PhanUndeclaredVariableDim",
] );
$cfg['ignore_undeclared_variables_in_global_scope'] = true;
'IP' => 'string',
'wgGalleryOptions' => 'array',
'wgDummyLanguageCodes' => 'string[]',
+ 'wgNamespaceProtection' => 'array<string,string|string[]>',
+ 'wgNamespaceAliases' => 'array<string,int>',
+ 'wgLockManagers' => 'array[]',
+ 'wgForeignFileRepos' => 'array[]',
+ 'wgDefaultUserOptions' => 'array',
+ 'wgSkipSkins' => 'string[]',
+ 'wgLogTypes' => 'string[]',
+ 'wgLogNames' => 'array<string,string>',
+ 'wgLogHeaders' => 'array<string,string>',
+ 'wgLogActionsHandlers' => 'array<string,class-string>',
+ 'wgPasswordPolicy' => 'array<string,array<string,string|array>>',
+ 'wgVirtualRestConfig' => 'array<string,array>',
+ 'wgWANObjectCaches' => 'array[]',
+ 'wgLocalInterwikis' => 'string[]',
] );
return $cfg;
* - 'legacy-name' (optional): short name for backwards-compatibility
* @param array $checked Array of checkbox name (matching the 'legacy-name') => bool,
* where bool indicates the checked status of the checkbox
- * @return array
+ * @return array[]
*/
public function getCheckboxesDefinition( $checked ) {
$checkboxes = [];
$logEntry->setPerformer( $user );
$logEntry->setTarget( $title );
$logEntry->setComment( $logComment );
- $logEntry->setTags( $tags );
+ $logEntry->addTags( $tags );
$logid = $logEntry->insert();
$logEntry->publish( $logid );
$logEntry->setPerformer( $user );
$logEntry->setTarget( clone $title );
$logEntry->setComment( $reason );
- $logEntry->setTags( $tags );
+ $logEntry->addTags( $tags );
$logid = $logEntry->insert();
$dbw->onTransactionPreCommitOrIdle(
function () use ( $logEntry, $logid ) {
);
$options = Xml::listDropDownOptionsOoui( $options );
+ $fields = [];
$fields[] = new OOUI\LabelWidget( [ 'label' => new OOUI\HtmlSnippet(
$this->prepareMessage( 'filedelete-intro' ) ) ]
);
* including errors from limit.sh
* - profileMethod: By default this function will profile based on the calling
* method. Set this to a string for an alternative method to profile from
+ * @phan-param array{duplicateStderr?:bool,profileMethod?:string} $options
*
* @return string Collected stdout as a string
* @deprecated since 1.30 use class MediaWiki\Shell\Shell
}
$includeStderr = isset( $options['duplicateStderr'] ) && $options['duplicateStderr'];
+ // @phan-suppress-next-line PhanTypeInvalidDimOffset
$profileMethod = $options['profileMethod'] ?? wfGetCaller();
try {
* @param array $options Associative array of options:
* 'php': The path to the php executable
* 'wrapper': Path to a PHP wrapper to handle the maintenance script
+ * @phan-param array{php?:string,wrapper?:string} $options
* @return string
*/
function wfShellWikiCmd( $script, array $parameters = [], array $options = [] ) {
// Give site config file a chance to run the script in a wrapper.
// The caller may likely want to call wfBasename() on $script.
Hooks::run( 'wfShellWikiCmd', [ &$script, &$parameters, &$options ] );
+ // @phan-suppress-next-line PhanTypeInvalidDimOffset
$cmd = [ $options['php'] ?? $wgPhpCli ];
if ( isset( $options['wrapper'] ) ) {
$cmd[] = $options['wrapper'];
}
}
+ $like = [];
$like[] = $bits['scheme'] . $bits['delimiter'] . $bits['host'];
if ( $subdomains ) {
}
$userTalkPage = new TitleValue( NS_USER_TALK, strtr( $userText, ' ', '_' ) );
- $moreLinkAttribs['class'] = 'mw-usertoollinks-talk';
+ $moreLinkAttribs = [ 'class' => 'mw-usertoollinks-talk' ];
return self::link( $userTalkPage,
wfMessage( 'talkpagelinktext' )->escaped(),
}
$blockPage = SpecialPage::getTitleFor( 'Block', $userText );
- $moreLinkAttribs['class'] = 'mw-usertoollinks-block';
+ $moreLinkAttribs = [ 'class' => 'mw-usertoollinks-block' ];
return self::link( $blockPage,
wfMessage( 'blocklink' )->escaped(),
}
$emailPage = SpecialPage::getTitleFor( 'Emailuser', $userText );
- $moreLinkAttribs['class'] = 'mw-usertoollinks-mail';
+ $moreLinkAttribs = [ 'class' => 'mw-usertoollinks-mail' ];
return self::link( $emailPage,
wfMessage( 'emaillink' )->escaped(),
$moreLinkAttribs
'4::oldtitle' => $this->oldTitle->getPrefixedText(),
] );
$logEntry->setRelations( [ 'pr_id' => $logRelationsValues ] );
- $logEntry->setTags( $changeTags );
+ $logEntry->addTags( $changeTags );
$logId = $logEntry->insert();
$logEntry->publish( $logId );
}
# Log the move
$logid = $logEntry->insert();
- $logEntry->setTags( $changeTags );
+ $logEntry->addTags( $changeTags );
$logEntry->publish( $logid );
return $nullRevision;
* @param array $hints Hints given as an associative array. Known keys:
* - 'generate-html' => bool: Whether the caller is interested in output HTML (as opposed
* to just meta-data). Default is to generate HTML.
+ * @phan-param array{generate-html?:bool} $hints
*
* @return ParserOutput
*/
* @param array $hints Hints given as an associative array. Known keys:
* - 'generate-html' => bool: Whether the caller is interested in output HTML (as opposed
* to just meta-data). Default is to generate HTML.
+ * @phan-param array{generate-html?:bool} $hints
*
* @throws SuppressedDataException if the content is not accessible for the audience
* specified in the constructor.
* matched the $rev and $options. This mechanism is intended as a temporary stop-gap,
* for the time until caches have been changed to store RenderedRevision states instead
* of ParserOutput objects.
+ * @phan-param array{use-master?:bool,audience?:int,known-revision-output?:ParserOutput} $hints
*
* @return RenderedRevision|null The rendered revision, or null if the audience checks fails.
*/
throw new InvalidArgumentException( 'Mismatching wiki ID ' . $rev->getWikiId() );
}
+ // @phan-suppress-next-line PhanTypeInvalidDimOffset
$audience = $hints['audience']
?? ( $forUser ? RevisionRecord::FOR_THIS_USER : RevisionRecord::FOR_PUBLIC );
$options = ParserOptions::newCanonical( $forUser ?: 'canonical' );
}
+ // @phan-suppress-next-line PhanTypeInvalidDimOffset
$useMaster = $hints['use-master'] ?? false;
$dbIndex = $useMaster
* - tables: (string[]) to include in the `$table` to `IDatabase->select()`
* - fields: (string[]) to include in the `$vars` to `IDatabase->select()`
* - joins: (array) to include in the `$join_conds` to `IDatabase->select()`
+ * @phan-return array{tables:string[],fields:string[],joins:array}
*/
public function getQueryInfo( $options = [] ) {
$ret = [
if ( $wgLocalInterwiki ) {
// Hard deprecated in 1.34.
wfDeprecated( '$wgLocalInterwiki – use $wgLocalInterwikis instead', '1.23' );
+ // @phan-suppress-next-line PhanUndeclaredVariableDim
array_unshift( $wgLocalInterwikis, $wgLocalInterwiki );
}
}
$dbr = wfGetDB( DB_REPLICA );
- $conds['page_namespace'] = $this->mNamespace;
+ $conds = [ 'page_namespace' => $this->mNamespace ];
$conds[] = 'page_title ' . $dbr->buildLike( $this->mDbkeyform . '/', $dbr->anyString() );
$options = [];
if ( $limit > -1 ) {
/**
* Clean up a field array for output
* @param array $fields
+ * @codingStandardsIgnoreStart
+ * @phan-param array{type:string,options:array,value:string,label:Message,help:Message,optional:bool,sensitive:bool,skippable:bool} $fields
+ * @codingStandardsIgnoreEnd
* @return array
*/
private function formatFields( array $fields ) {
}
list( $target, /*...*/ ) = SpecialBlock::getTargetAndType( $params['user'] );
+ $res = [];
$res['user'] = $params['user'];
$res['userID'] = $target instanceof User ? $target->getId() : 0;
$status = $ep->attemptSave( $result );
$wgRequest = $oldRequest;
+ $r = [];
switch ( $status->value ) {
case EditPage::AS_HOOK_ERROR:
case EditPage::AS_HOOK_ERROR_EXPECTED:
private $mModule;
private $mCacheMode = 'private';
+ /** @var array */
private $mCacheControl = [];
private $mParamsUsed = [];
private $mParamsSensitive = [];
// Trim extracts, if necessary
$length = $this->getConfig()->get( 'OpenSearchDescriptionLength' );
foreach ( $results as &$r ) {
+ // @phan-suppress-next-line PhanTypeInvalidDimOffset
if ( is_string( $r['extract'] ) && !$r['extract trimmed'] ) {
$r['extract'] = self::trimExtract( $r['extract'], $length );
}
private $mGeneratorData = []; // [ns][dbkey] => data array
private $mFakePageId = -1;
private $mCacheMode = 'public';
+ /** @var array */
private $mRequestedPageFields = [];
/** @var int */
private $mDefaultNamespace = NS_MAIN;
*/
private $rootTitle;
- private $params, $cont, $redirect;
+ private $params;
+ /** @var array */
+ private $cont;
+ private $redirect;
private $bl_ns, $bl_from, $bl_from_ns, $bl_table, $bl_code, $bl_title, $bl_fields, $hasNS;
/**
}
if ( is_null( $resultPageSet ) ) {
- $a['pageid'] = (int)$row->page_id;
+ $a = [ 'pageid' => (int)$row->page_id ];
ApiQueryBase::addTitleInfo( $a, Title::makeTitle( $row->page_namespace, $row->page_title ) );
if ( $row->page_is_redirect ) {
$a['redirect'] = true;
/**
* @param ApiPageSet $resultPageSet
* @return void
+ * @suppress PhanTypeInvalidDimOffset
*/
private function run( $resultPageSet = null ) {
$this->params = $this->extractRequestParams( false );
if ( !isset( $pageMap[$row->ar_namespace][$row->ar_title] ) ) {
$pageID = $newPageID++;
$pageMap[$row->ar_namespace][$row->ar_title] = $pageID;
- $a['revisions'] = [ $rev ];
+ $a = [ 'revisions' => [ $rev ] ];
ApiResult::setIndexedTagName( $a['revisions'], 'rev' );
$title = Title::makeTitle( $row->ar_namespace, $row->ar_title );
ApiQueryBase::addTitleInfo( $a, $title );
return $this->tokenFunctions;
}
+ /** @var string[] */
protected static $cachedTokens = [];
/**
const WL_UNREAD_LIMIT = 1000;
+ /** @var array */
private $params = [];
+ /** @var array */
private $prop = [];
public function __construct( ApiQuery $query, $moduleName ) {
$this->dieStatus( $this->errorArrayToStatus( $retval ) );
}
- $res['id'] = $block->getId();
$target = $block->getType() == DatabaseBlock::TYPE_AUTO ? '' : $block->getTarget();
- $res['user'] = $target instanceof User ? $target->getName() : $target;
- $res['userid'] = $target instanceof User ? $target->getId() : 0;
- $res['reason'] = $params['reason'];
+ $res = [
+ 'id' => $block->getId(),
+ 'user' => $target instanceof User ? $target->getName() : $target,
+ 'userid' => $target instanceof User ? $target->getId() : 0,
+ 'reason' => $params['reason']
+ ];
$this->getResult()->addValue( null, $this->getModuleName(), $res );
}
$this->setWatch( $params['watchlist'], $titleObj );
- $info['title'] = $titleObj->getPrefixedText();
- $info['revisions'] = (int)$retval[0];
- $info['fileversions'] = (int)$retval[1];
- $info['reason'] = $retval[2];
+ $info = [
+ 'title' => $titleObj->getPrefixedText(),
+ 'revisions' => (int)$retval[0],
+ 'fileversions' => (int)$retval[1],
+ 'reason' => $retval[2]
+ ];
$this->getResult()->addValue( null, $this->getModuleName(), $info );
}
}
}
+ $result = [];
// No errors, no warnings: do the upload
if ( $this->mParams['async'] ) {
$progress = UploadBase::getSessionStatus( $this->getUser(), $this->mParams['filekey'] );
$form = $this->getUserRightsPage();
$form->setContext( $this->getContext() );
+ $r = [];
$r['user'] = $user->getName();
$r['userid'] = $user->getId();
list( $r['added'], $r['removed'] ) = $form->doSaveUserGroups(
$user = $this->getUser();
}
+ $r = [];
$validity = $user->checkPasswordValidity( $params['password'] );
$r['validity'] = $validity->isGood() ? 'Good' : ( $validity->isOK() ? 'Change' : 'Invalid' );
$messages = array_merge(
*
* @return array array containing available additional api param definitions.
* Empty if profiles are not supported by the searchEngine implementation.
+ * @suppress PhanTypeMismatchDimFetch
*/
private function buildProfileApiParam() {
$configs = $this->getSearchProfileParams();
if ( isset( $profile['desc-message'] ) ) {
$helpMessages[$profile['name']] = $profile['desc-message'];
}
+
if ( !empty( $profile['default'] ) ) {
$defaultProfile = $profile['name'];
}
* a 'password' field).
*
* @return array As above
+ * @phan-return array<string,array{type:string,options?:array,value?:string,label:Message,help:Message,optional?:bool,sensitive?:bool,skippable?:bool}>
*/
abstract public function getFieldInfo();
* @param AuthenticationRequest[] $reqs
* @return array
* @throws \UnexpectedValueException If fields cannot be merged
+ * @suppress PhanTypeInvalidDimOffset
*/
public static function mergeFieldInfo( array $reqs ) {
$merged = [];
}
$options['sensitive'] = !empty( $options['sensitive'] );
- // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
$type = $options['type'];
if ( !array_key_exists( $name, $merged ) ) {
/**
* See documentation of $wgPasswordAttemptThrottle for format. Old (pre-1.27) format is not
* allowed here.
- * @var array
+ * @var array[]
* @see https://www.mediawiki.org/wiki/Manual:$wgPasswordAttemptThrottle
*/
protected $conditions;
/**
* Handles B/C for $wgPasswordAttemptThrottle.
* @param array $throttleConditions
- * @return array
+ * @return array[]
* @see $wgPasswordAttemptThrottle for structure
*/
protected static function normalizeThrottleConditions( $throttleConditions ) {
$block[0], $block[0]->unpatrolled, $block[0]->watched );
}
- $queryParams['curid'] = $curId;
+ $queryParams = [ 'curid' => $curId ];
# Sub-entries
$lines = [];
protected function recentChangesBlockLine( $rcObj ) {
$data = [];
- $query['curid'] = $rcObj->mAttribs['rc_cur_id'];
+ $query = [ 'curid' => $rcObj->mAttribs['rc_cur_id'] ];
$type = $rcObj->mAttribs['rc_type'];
$logType = $rcObj->mAttribs['rc_log_type'];
*/
const SEND_FEED = false;
+ /** @var array */
public $mAttribs = [];
public $mExtra = [];
}
$logEntry->setParameters( $params );
$logEntry->setRelations( [ 'Tag' => $tag ] );
- $logEntry->setTags( $logEntryTags );
+ $logEntry->addTags( $logEntryTags );
$logId = $logEntry->insert( $dbw );
$logEntry->publish( $logId );
* @since 1.28
*/
public function getFieldsForSearchIndex( SearchEngine $engine ) {
+ $fields = [];
$fields['category'] = $engine->makeSearchFieldMapping(
'category',
SearchIndexField::INDEX_TYPE_TEXT
class FileContentHandler extends WikitextContentHandler {
public function getFieldsForSearchIndex( SearchEngine $engine ) {
+ $fields = [];
$fields['file_media_type'] =
$engine->makeSearchFieldMapping( 'file_media_type', SearchIndexField::INDEX_TYPE_KEYWORD );
$fields['file_media_type']->setFlag( SearchIndexField::FLAG_CASEFOLD );
class DiffEngine {
// Input variables
+ /** @var string[] */
private $from;
+ /** @var string[] */
private $to;
private $m;
private $n;
*/
$max = min( $this->m, $this->n );
for ( $forwardBound = 0; $forwardBound < $max
+ // @phan-suppress-next-line PhanTypeInvalidDimOffset
&& $this->from[$forwardBound] === $this->to[$forwardBound];
++$forwardBound
) {
* user is allowed to view them. Otherwise, such files will not
* be found.
* latest: If true, load from the latest available data into File objects
+ * @phan-param array{time?:mixed,ignoreRedirect?:bool,private?:bool,latest?:bool} $options
+ * @suppress PhanTypeInvalidDimOffset
* @return File|bool False if title is not found
*/
function findFile( $title, $options = [] ) {
$this->loadFromDB( self::READ_NORMAL );
$fields = $this->getCacheFields( '' );
+ $cacheVal = [];
$cacheVal['fileExists'] = $this->fileExists;
if ( $this->fileExists ) {
foreach ( $fields as $field ) {
/**
* Delete cached transformed files for the current version only.
* @param array $options
+ * @phan-param array{forThumbRefresh?:bool} $options
*/
public function purgeThumbnails( $options = [] ) {
$files = $this->getThumbnails();
array_shift( $urls ); // don't purge directory
// Give media handler a chance to filter the file purge list
+ // @phan-suppress-next-line PhanTypeInvalidDimOffset
if ( !empty( $options['forThumbRefresh'] ) ) {
$handler = $this->getHandler();
if ( $handler ) {
# Add change tags, if any
if ( $tags ) {
- $logEntry->setTags( $tags );
+ $logEntry->addTags( $tags );
}
# Uploads can be patrolled
$seqName = 'main';
}
}
- $seq =& $sequences[$seqName];
- $tail = $seq['tail'];
+
+ $tail = $sequences[$seqName]['tail'];
$diff = $this->diff( $tail, $text );
- $seq['diffs'][] = $diff;
- $seq['map'][] = $i;
- $seq['tail'] = $text;
+ $sequences[$seqName]['diffs'][] = $diff;
+ $sequences[$seqName]['map'][] = $i;
+ $sequences[$seqName]['tail'] = $text;
}
- unset( $seq ); // unlink dangerous alias
// Knit the sequences together
$tail = '';
protected $mUseMultipart = false;
protected $mHiddenFields = [];
+ /**
+ * @var array[]
+ * @phan-var array<array{name:string,value:string,label-message?:string,label?:string,label-raw?:string,id?:string,attribs?:array,flags?:string|string[],framed?:bool}>
+ */
protected $mButtons = [];
protected $mWrapperLegend = false;
* - attribs: (array, optional) Additional HTML attributes.
* - flags: (string|string[], optional) OOUI flags.
* - framed: (boolean=true, optional) OOUI framed attribute.
+ * @codingStandardsIgnoreStart
+ * @phan-param array{name:string,value:string,label-message?:string,label?:string,label-raw?:string,id?:string,attribs?:array,flags?:string|string[],framed?:bool} $data
+ * @codingStandardsIgnoreEnd
* @return HTMLForm $this for chaining calls (since 1.20)
*/
public function addButton( $data ) {
* - password Password for HTTP Basic Authentication
* - originalRequest Information about the original request (as a WebRequest object or
* an associative array with 'ip' and 'userAgent').
+ * @codingStandardsIgnoreStart
+ * @phan-param array{timeout?:int,connectTimeout?:int,postData?:array,proxy?:string,noProxy?:bool,sslVerifyHost?:bool,sslVerifyCert?:bool,caInfo?:string,maxRedirects?:int,followRedirects?:bool,userAgent?:string,logger?:\Psr\Logger\LoggerInterface,username?:string,password?:string,originalRequest?:WebRequest|array{ip:string,userAgent:string}} $options
+ * @codingStandardsIgnoreEnd
* @param string $caller The method making this request, for profiling
* @throws RuntimeException
* @return MWHttpRequest
* @see MWHttpRequest::__construct
+ * @suppress PhanUndeclaredTypeParameter
*/
public function create( $url, array $options = [], $caller = __METHOD__ ) {
if ( !Http::$httpEngine ) {
protected $sslVerifyCert = true;
protected $caInfo = null;
protected $method = "GET";
+ /** @var array */
protected $reqHeaders = [];
protected $url;
protected $parsedUrl;
protected $headerList = [];
protected $respVersion = "0.9";
protected $respStatus = "200 Ok";
+ /** @var string[][] */
protected $respHeaders = [];
/** @var StatusValue */
/**
* @param string $url Url to use. If protocol-relative, will be expanded to an http:// URL
* @param array $options (optional) extra params to pass (see HttpRequestFactory::create())
+ * @codingStandardsIgnoreStart
+ * @phan-param array{timeout?:int,connectTimeout?:int,postData?:array,proxy?:string,noProxy?:bool,sslVerifyHost?:bool,sslVerifyCert?:bool,caInfo?:string,maxRedirects?:int,followRedirects?:bool,userAgent?:string,logger?:LoggerInterface,username?:string,password?:string,originalRequest?:WebRequest|array{ip:string,userAgent:string},method?:string} $options
+ * @codingStandardsIgnoreEnd
* @param string $caller The method making this request, for profiling
* @param Profiler|null $profiler An instance of the profiler for profiling, or null
* @throws Exception
$this->url = wfExpandUrl( $url, PROTO_HTTP );
$this->parsedUrl = wfParseUrl( $this->url );
+ // @phan-suppress-next-line PhanTypeInvalidDimOffset
$this->logger = $options['logger'] ?? new NullLogger();
if ( !$this->parsedUrl || !Http::isValidURI( $this->url ) ) {
// ensure that MWHttpRequest::method is always
// uppercased. T38137
if ( $o == 'method' ) {
+ // @phan-suppress-next-line PhanTypeInvalidDimOffset
$options[$o] = strtoupper( $options[$o] );
}
$this->$o = $options[$o];
return $this->logItemCallback( $revision );
}
+ /**
+ * @suppress PhanTypeInvalidDimOffset Phan not reading the reference inside the hook
+ */
private function handlePage() {
// Handle page data.
$this->debug( "Enter page handler." );
*
* @param string $directory Directory to search in, relative to $IP, must be either "extensions"
* or "skins"
- * @return array [ $extName => [ 'screenshots' => [ '...' ] ]
+ * @return array[][] [ $extName => [ 'screenshots' => [ '...' ] ]
*/
public function findExtensions( $directory = 'extensions' ) {
switch ( $directory ) {
}
/**
- * @return JobQueue[]
+ * @return array[]
+ * @phan-return array<string,array{queue:JobQueue,types:array<string,class-string>}>
*/
protected function getCoalescedQueues() {
global $wgJobTypeConf;
* the base iterator (post-callback) and will return true if that value should be
* included in iteration of the MappedIterator (otherwise it will be filtered out).
*
- * @param Iterator|Array $iter
+ * @param Iterator|array $iter
* @param callable $vCallback Value transformation callback
* @param array $options Options map (includes "accept") (since 1.22)
+ * @phan-param array{accept?:callable} $options
* @throws UnexpectedValueException
*/
public function __construct( $iter, $vCallback, array $options = [] ) {
}
parent::__construct( $baseIterator );
$this->vCallback = $vCallback;
+ // @phan-suppress-next-line PhanTypeInvalidDimOffset
$this->aCallback = $options['accept'] ?? null;
}
/**
* Per-function inclusive data.
- * @var array $inclusive
+ * @var array[] $inclusive
*/
protected $inclusive;
/**
* Per-function inclusive and exclusive data.
- * @var array $complete
+ * @var array[] $complete
*/
protected $complete;
* - max: Maximum value
* - variance: Variance (spread) of the values
*
- * @return array
+ * @return array[]
* @see getRawData()
* @see getCompleteMetrics()
*/
* metrics have an additional 'exclusive' measurement which is the total
* minus the totals of all child function calls.
*
- * @return array
+ * @return array[]
* @see getRawData()
* @see getInclusiveMetrics()
*/
* - b) predicted operation errors occurred and 'force' was not set
*
* @param array $ops List of operations to execute in order
+ * @codingStandardsIgnoreStart
+ * @phan-param array{ignoreMissingSource?:bool,overwrite?:bool,overwriteSame?:bool,headers?:bool} $ops
* @param array $opts Batch operation options
+ * @phan-param array{force?:bool,nonLocking?:bool,nonJournaled?:bool,parallelize?:bool,bypassReadOnly?:bool,preserveCache?:bool} $opts
+ * @codingStandardsIgnoreEnd
* @return StatusValue
*/
final public function doOperations( array $ops, array $opts = [] ) {
* considered "OK" as long as no fatal errors occurred.
*
* @param array $ops Set of operations to execute
+ * @phan-param array{ignoreMissingSource?:bool,headers?:bool} $ops
* @param array $opts Batch operation options
+ * @phan-param array{bypassReadOnly?:bool} $opts
* @return StatusValue
* @since 1.20
*/
* - reqTimeout : post-connection timeout per request (seconds)
* - usePipelining : whether to use HTTP pipelining if possible
* - maxConnsPerHost : maximum number of concurrent connections (per host)
+ * @codingStandardsIgnoreStart
+ * @phan-param array{connTimeout?:int,reqTimeout?:int,usePipelining?:bool,maxConnsPerHost?:int} $opts
+ * @codingStandardsIgnoreEnd
* @return array $reqs With response array populated for each
* @throws Exception
+ * @suppress PhanTypeInvalidDimOffset
*/
private function runMultiCurl( array $reqs, array $opts = [] ) {
$chm = $this->getCurlMulti();
$name = strtolower( $name );
$value = trim( $value );
if ( isset( $req['response']['headers'][$name] ) ) {
+ // @phan-suppress-next-line PhanTypeInvalidDimOffset
$req['response']['headers'][$name] .= ', ' . $value;
} else {
$req['response']['headers'][$name] = $value;
if ( isset( $svErrors[0]['params'][0] ) ) {
if ( is_numeric( $svErrors[0]['params'][0] ) ) {
if ( isset( $svErrors[0]['params'][1] ) ) {
+ // @phan-suppress-next-line PhanTypeInvalidDimOffset
$req['response']['reason'] = $svErrors[0]['params'][1];
}
} else {
* - asyncHandler: Callable to use for scheduling tasks after the web request ends.
* In CLI mode, it should run the task immediately.
* @param array $params
+ * @phan-param array{logger?:Psr\Log\LoggerInterface,asyncHandler?:callable} $params
*/
public function __construct( array $params = [] ) {
$this->setLogger( $params['logger'] ?? new NullLogger() );
/**
* @param array $params Additional parameters include:
* - maxKeys : only allow this many keys (using oldest-first eviction)
+ * @codingStandardsIgnoreStart
+ * @phan-param array{logger?:Psr\Log\LoggerInterface,asyncHandler?:callable,keyspace?:string,reportDupes?:bool,syncTimeout?:int,segmentationSize?:int,segmentedValueMaxSize?:int,maxKeys?:int} $params
+ * @codingStandardsIgnoreEnd
+ * @suppress PhanTypeInvalidDimOffset
*/
function __construct( $params = [] ) {
$params['segmentationSize'] = $params['segmentationSize'] ?? INF;
* This should be configured to a reasonable size give the site traffic and the
* amount of I/O between application and cache servers that the network can handle.
* @param array $params
+ * @codingStandardsIgnoreStart
+ * @phan-param array{logger?:Psr\Log\LoggerInterface,asyncHandler?:callable,keyspace?:string,reportDupes?:bool,syncTimeout?:int,segmentationSize?:int,segmentedValueMaxSize?:int} $params
+ * @codingStandardsIgnoreEnd
*/
public function __construct( array $params = [] ) {
parent::__construct( $params );
* invalidation uses logical TTLs, invalidation uses etag/timestamp
* validation against the DB, or merge() is used to handle races.
* @param array $params
+ * @phan-param array{caches:array<int,array|BagOStuff>,replication:string} $params
* @throws InvalidArgumentException
*/
public function __construct( $params ) {
* - version: Integer version number signifiying the format of the value.
* Default: null
* - walltime: How long the value took to generate in seconds. Default: 0.0
+ * @codingStandardsIgnoreStart
+ * @phan-param array{lag?:int,since?:int,pending?:bool,lockTSE?:int,staleTTL?:int,creating?:bool,version?:?string,walltime?:int|float} $opts
+ * @codingStandardsIgnoreEnd
* @note Options added in 1.28: staleTTL
* @note Options added in 1.33: creating
* @note Options added in 1.34: version, walltime
* @return bool Success
+ * @suppress PhanTypeInvalidDimOffset
*/
final public function set( $key, $value, $ttl = self::TTL_INDEFINITE, array $opts = [] ) {
$now = $this->getCurrentTime();
* most sense for values that are moderately to highly expensive to regenerate and easy
* to query for dependency timestamps. The use of "pcTTL" reduces timestamp queries.
* Default: null.
+ * @codingStandardsIgnoreStart
+ * @phan-param array{checkKeys?:string[],graceTTL?:int,lockTSE?:int,busyValue?:mixed,pcTTL?:int,pcGroup?:string,version?:int,minAsOf?:int,hotTTR?:int,lowTTL?:int,ageNew?:int,staleTTL?:int,touchedCallback?:callable} $opts
+ * @codingStandardsIgnoreEnd
* @return mixed Value found or written to the key
* @note Options added in 1.28: version, busyValue, hotTTR, ageNew, pcGroup, minAsOf
* @note Options added in 1.31: staleTTL, graceTTL
* @note Options added in 1.33: touchedCallback
* @note Callable type hints are not used to avoid class-autoloading
+ * @suppress PhanTypeInvalidDimOffset
*/
final public function getWithSetCallback( $key, $ttl, $callback, array $opts = [] ) {
$version = $opts['version'] ?? null;
* - Cached or regenerated value version number or null if not versioned
* - Timestamp of the current cached value at the key or null if there is no value
* @note Callable type hints are not used to avoid class-autoloading
+ * @suppress PhanTypeArraySuspicious
*/
private function fetchOrRegenerate( $key, $ttl, $callback, array $opts ) {
$checkKeys = $opts['checkKeys'] ?? [];
$this->setInterimValue( $key, $value, $lockTSE, $version, $walltime );
} else {
$finalSetOpts = [
+ // @phan-suppress-next-line PhanTypeInvalidDimOffset
'since' => $setOpts['since'] ?? $preCallbackTime,
'version' => $version,
'staleTTL' => $staleTTL,
* - curTTL: remaining time-to-live (negative if tombstoned) or null if there is no value
* - version: value version number or null if the if there is no value
* - tombAsOf: UNIX timestamp of the tombstone or null if there is no tombstone
+ * @phan-return array{0:mixed,1:array{asOf:?mixed,curTTL:?int|float,version:?mixed,tombAsOf:?mixed}}
*/
private function unwrap( $wrapped, $now ) {
$value = false;
return $params;
}
+ /**
+ * @inheritDoc
+ * @suppress PhanTypeInvalidDimOffset
+ */
public function formatParametersForApi() {
$ret = parent::formatParametersForApi();
if ( isset( $ret['flags'] ) ) {
wfDebug( 'Overwriting existing ManualLogEntry tags' );
}
$this->tags = [];
- if ( $tags !== null ) {
- $this->addTags( $tags );
- }
+ $this->addTags( $tags );
}
/**
* Add change tags for the log entry
*
* @since 1.33
- * @param string|string[] $tags Tags to apply
+ * @param string|string[]|null $tags Tags to apply
*/
public function addTags( $tags ) {
+ if ( $tags === null ) {
+ return;
+ }
+
if ( is_string( $tags ) ) {
$tags = [ $tags ];
}
$entry->setTarget( $rc->getTitle() );
$entry->setParameters( self::buildParams( $rc, $auto ) );
$entry->setPerformer( $user );
- $entry->setTags( $tags );
+ $entry->addTags( $tags );
$logid = $entry->insert();
if ( !$auto ) {
$entry->publish( $logid, 'udp' );
*
* @param string $rawData The app13 block from jpeg containing iptc/iim data
* @return array IPTC metadata array
+ * @suppress PhanTypeArraySuspicious
*/
static function parse( $rawData ) {
$parsed = iptcparse( $rawData );
);
$options = Xml::listDropDownOptionsOoui( $options );
+ $fields = [];
$fields[] = new OOUI\FieldLayout(
new OOUI\DropdownInputWidget( [
'name' => 'wpDeleteReasonList',
$logEntry->setPerformer( $user );
$logEntry->setTarget( $this->title );
$logEntry->setComment( $comment );
- $logEntry->setTags( $tags );
+ $logEntry->addTags( $tags );
$logEntry->setParameters( [
':assoc:count' => [
'revisions' => $textRestored,
if ( !is_null( $nullRevision ) ) {
$logEntry->setAssociatedRevId( $nullRevision->getId() );
}
- $logEntry->setTags( $tags );
+ $logEntry->addTags( $tags );
if ( $logRelationsField !== null && count( $logRelationsValues ) ) {
$logEntry->setRelations( [ $logRelationsField => $logRelationsValues ] );
}
$logEntry->setPerformer( $deleter );
$logEntry->setTarget( $logTitle );
$logEntry->setComment( $reason );
- $logEntry->setTags( $tags );
+ $logEntry->addTags( $tags );
$logid = $logEntry->insert();
$dbw->onTransactionPreCommitOrIdle(
* @param bool $isMain
* @return mixed|string
* @private
+ * @suppress PhanTypeInvalidDimOffset
*/
public function formatHeadings( $text, $origText, $isMain = true ) {
# Inhibit editsection links if requested in the page
Hooks::run( 'ParserMakeImageParams', [ $title, $file, &$params, $this ] );
# Linker does the rest
+ // @phan-suppress-next-line PhanTypeInvalidDimOffset
$time = $options['time'] ?? false;
$ret = Linker::makeImageLink( $this, $title, $file, $params['frame'], $params['handler'],
$time, $descQuery, $this->mOptions->getThumbSize() );
if ( $this->options->get( 'EnableEmail' ) ) {
if ( $canViewPrivateInfo ) {
+ $helpMessages = [];
$helpMessages[] = $this->options->get( 'EmailConfirmToEdit' )
? 'prefs-help-email-required'
: 'prefs-help-email';
$autoloadNamespaces
);
- if ( isset( $info['AutoloadClasses'] ) ) {
- $autoload = $this->processAutoLoader( $dir, $info['AutoloadClasses'] );
- $GLOBALS['wgAutoloadClasses'] += $autoload;
- $autoloadClasses += $autoload;
- }
- if ( isset( $info['AutoloadNamespaces'] ) ) {
- $autoloadNamespaces += $this->processAutoLoader( $dir, $info['AutoloadNamespaces'] );
- AutoLoader::$psr4Namespaces += $autoloadNamespaces;
- }
-
// get all requirements/dependencies for this extension
$requires = $processor->getRequirements( $info, $this->checkDev );
$idx = -1;
foreach ( $grpModules as $name => $module ) {
$shouldEmbed = $module->shouldEmbedModule( $context );
+ // @phan-suppress-next-line PhanTypeInvalidDimOffset
if ( !$moduleSets || $moduleSets[$idx][0] !== $shouldEmbed ) {
$moduleSets[++$idx] = [ $shouldEmbed, [] ];
}
}
$logEntry->setRelations( $relations );
// Apply change tags to the log entry
- $logEntry->setTags( $params['tags'] );
+ $logEntry->addTags( $params['tags'] );
$logId = $logEntry->insert();
$logEntry->publish( $logId );
}
* @param string $profileType the type of profiles
* @param User|null $user the user requesting the list of profiles
* @return array|null the list of profiles or null if none available
+ * @phan-return null|array{name:string,desc-message:string,default?:bool}
*/
public function getProfiles( $profileType, User $user = null ) {
return null;
"$provider returned empty session info with id flagged unsafe"
);
}
+ // @phan-suppress-next-line PhanTypeInvalidDimOffset
$compare = $infos ? SessionInfo::compare( $infos[0], $info ) : -1;
if ( $compare > 0 ) {
continue;
* @param array $options Associative array of options:
* 'php': The path to the php executable
* 'wrapper': Path to a PHP wrapper to handle the maintenance script
+ * @phan-param array{php?:string,wrapper?:string} $options
* @return Command
*/
public static function makeScriptCommand( $script, $parameters, $options = [] ): Command {
// Give site config file a chance to run the script in a wrapper.
// The caller may likely want to call wfBasename() on $script.
Hooks::run( 'wfShellWikiCmd', [ &$script, &$parameters, &$options ] );
+ // @phan-suppress-next-line PhanTypeInvalidDimOffset
$cmd = [ $options['php'] ?? $wgPhpCli ];
if ( isset( $options['wrapper'] ) ) {
$cmd[] = $options['wrapper'];
* @param array $item Array of list item data containing some of a specific set of keys.
* The "id", "class" and "itemtitle" keys will be used as attributes for the list item,
* if "active" contains a value of true a "active" class will also be appended to class.
+ * @phan-param array{id?:string,class?:string,itemtitle?:string,active?:bool} $item
*
* @param array $options
+ * @phan-param array{tag?:string} $options
*
* If you want something other than a "<li>" you can pass a tag name such as
* "tag" => "span" in the $options array to change the tag used.
if ( isset( $item['itemtitle'] ) ) {
$attrs['title'] = $item['itemtitle'];
}
+ // @phan-suppress-next-line PhanTypeInvalidDimOffset
return Html::rawElement( $options['tag'] ?? 'li', $attrs, $html );
}
* Generates a HTMLForm descriptor array from a set of authentication requests.
* @param AuthenticationRequest[] $requests
* @param string $action AuthManager action name (one of the AuthManager::ACTION_* constants)
- * @return array
+ * @return array[]
*/
protected function getAuthFormDescriptor( $requests, $action ) {
$fieldInfo = AuthenticationRequest::mergeFieldInfo( $requests );
/**
* Adds a sequential tabindex starting from 1 to all form elements. This way the user can
* use the tab key to traverse the form without having to step through all links and such.
- * @param array &$formDescriptor
+ * @param array[] &$formDescriptor
*/
protected function addTabIndex( &$formDescriptor ) {
$i = 1;
$logId = $logEntry->insert();
if ( !empty( $data['Tags'] ) ) {
- $logEntry->setTags( $data['Tags'] );
+ $logEntry->addTags( $data['Tags'] );
}
$logEntry->publish( $logId );
$linkRenderer = $sp->getLinkRenderer();
+ $tools = [];
# No talk pages for IP ranges.
if ( !$isRange ) {
$tools['user-talk'] = $linkRenderer->makeLink(
$linkRenderer = $this->getLinkRenderer();
$link = $linkRenderer->makeLink( $title );
+ $tools = [];
$tools['talk'] = $linkRenderer->makeLink(
$title->getTalkPage(),
$this->msg( 'talkpagelinktext' )->text()
# Is the title semi-protected?
if ( $this->oldTitle->isSemiProtected( 'move' ) ) {
$noticeMsg = 'semiprotectedpagemovewarning';
- $classes[] = 'mw-textarea-sprotected';
} else {
# Then it must be protected based on static groups (regular)
$noticeMsg = 'protectedpagemovewarning';
- $classes[] = 'mw-textarea-protected';
}
$out->addHTML( "<div class='mw-warning-with-logexcerpt'>\n" );
$out->addWikiMsg( $noticeMsg );
$entry->setTarget( $title );
$entry->setParameters( $logParams );
$entry->setComment( $reason );
- $entry->setTags( $tags );
+ $entry->addTags( $tags );
$logid = $entry->insert();
$entry->publish( $logid );
$logEntry->setComment( $data['Reason'] );
$logEntry->setPerformer( $performer );
if ( isset( $data['Tags'] ) ) {
- $logEntry->setTags( $data['Tags'] );
+ $logEntry->addTags( $data['Tags'] );
}
$logEntry->setRelations( [ 'ipb_id' => $block->getId() ] );
$logId = $logEntry->insert();
$out->enableOOUI();
+ $fields = [];
$fields[] = new OOUI\ActionFieldLayout(
new OOUI\TextInputWidget( [
'name' => 'prefix',
}
if ( $this->mAllowed && ( $haveRevisions || $haveFiles ) ) {
+ $fields = [];
$fields[] = new OOUI\Layout( [
'content' => new OOUI\HtmlSnippet( $this->msg( 'undeleteextrahelp' )->parseAsBlock() )
] );
] );
$logid = $logEntry->insert();
if ( count( $tags ) ) {
- $logEntry->setTags( $tags );
+ $logEntry->addTags( $tags );
}
$logEntry->publish( $logid );
}
$fetchlinks = ( !$hidelinks || !$hideredirs );
// Build query conds in concert for all three tables...
+ $conds = [];
$conds['pagelinks'] = [
'pl_namespace' => $target->getNamespace(),
'pl_title' => $target->getDBkey(),
// Read the rows into an array and remove duplicates
// templatelinks comes second so that the templatelinks row overwrites the
// pagelinks row, so we get (inclusion) rather than nothing
+ $rows = [];
if ( $fetchlinks ) {
foreach ( $plRes as $row ) {
$row->is_template = 0;
protected $mMaxFileSize = [];
+ /** @var array */
protected $mMaxUploadSize = [];
public function __construct( array $options = [], IContextSource $context = null,
// Make sure the null revision will be tagged as well
$logEntry->setAssociatedRevId( $nullRevId );
if ( count( $this->logTags ) ) {
- $logEntry->setTags( $this->logTags );
+ $logEntry->addTags( $this->logTags );
}
$logid = $logEntry->insert();
$logEntry->publish( $logid );
* @param string $name
* @param string $value
* @return string
- * @suppress PhanTypeArraySuspiciousNullable
+ * @suppress PhanTypeArraySuspiciousNullable,PhanTypeArraySuspicious
*/
function formatValue( $name, $value ) {
static $msg = null;
/* User preference timezone */true
) );
if ( $this->getUser()->isAllowed( 'block' ) ) {
+ $links = [];
if ( $row->ipb_auto ) {
$links[] = $linkRenderer->makeKnownLink(
SpecialPage::getTitleFor( 'Unblock' ),
* - array $config['namespace'] Configuration for the NamespaceInputWidget dropdown
* with list of namespaces
* - array $config['title'] Configuration for the TitleInputWidget text field
+ * @phan-param array{namespace?:array,title?:array} $config
*/
public function __construct( array $config = [] ) {
// Configuration initialization
* TODO: For now, we do full update even though some data hasn't changed,
* e.g. parents for parent cat and counts for child cat.
*/
+ $childPages = [];
+ $parentCats = [];
foreach ( $batch as $row ) {
$childPages[$row->rc_cur_id] = true;
$parentCats[$row->rc_title] = true;
$pages = [];
$deleteUrls = [];
- if ( !empty( $childPages ) ) {
+ if ( $childPages ) {
// Load child rows by ID
$childRows = $dbr->select(
[ 'page', 'page_props', 'category' ],
}
}
- if ( !empty( $parentCats ) ) {
+ if ( $parentCats ) {
// Load parent rows by title
$joinConditions = [
'page' => [
$this->json[$realName] = $value;
}
+ /**
+ * @param string $realName
+ * @param array[] $value
+ * @suppress PhanTypeInvalidDimOffset
+ */
protected function handleResourceModules( $realName, $value ) {
$defaults = [];
$remote = $this->hasOption( 'skin' ) ? 'remoteSkinPath' : 'remoteExtPath';
protected $firstPageWritten = false;
protected $lastPageWritten = false;
protected $checkpointJustWritten = false;
+ /** @var string[] */
protected $checkpointFiles = [];
/**
$param = $split[1];
}
$fileURIs = explode( ';', $param );
+ $newFileURIs = [];
foreach ( $fileURIs as $URI ) {
switch ( $val ) {
case "file":
$dbw->doAtomicSection( __METHOD__, function ( IDatabase $dbw, $fname ) {
$dbw->insert( 'revision', self::$dummyRev, $fname );
$id = $dbw->insertId();
- $toDelete[] = $id;
+ $toDelete = [ $id ];
$maxId = max(
(int)$dbw->selectField( 'archive', 'MAX(ar_rev_id)', [], $fname ),
return $this->sqlPrintResult( $res, $db );
} catch ( DBQueryError $e ) {
if ( $dieOnError ) {
- $this->fatalError( $e );
+ $this->fatalError( (string)$e );
} else {
- $this->error( $e );
+ $this->error( (string)$e );
}
}
return null;
* @param string $extdb
* @param bool|int $maxPageId
* @return bool
+ * @suppress PhanTypeInvalidDimOffset
*/
private function compressWithConcat( $startId, $maxChunkSize, $beginDate,
$endDate, $extdb = "", $maxPageId = false
'STRAIGHT_JOIN' // per T58041
];
- if ( $force ) {
- $collationConds = [];
- } else {
+ $collationConds = [];
+ if ( !$force ) {
if ( $this->hasOption( 'previous-collation' ) ) {
$collationConds['cl_collation'] = $this->getOption( 'previous-collation' );
} else {
}
$json = FormatJson::decode( file_get_contents( $filename ), true );
- if ( $json === null ) {
+ if ( !is_array( $json ) ) {
$this->fatalError( "Error: Invalid JSON" );
}
wfRequireOnceInGlobalScope( "$IP/includes/DefaultSettings.php" );
wfRequireOnceInGlobalScope( "$IP/includes/GlobalFunctions.php" );
-// Load extensions/skins present in filesystem so that classes can be discovered.
+// Populate classes and namespaces from extensions and skins present in filesystem.
$directoryToJsonMap = [
- 'extensions' => [ 'extension.json', 'extension-wip.json' ],
- 'skins' => [ 'skin.json', 'skin-wip.json' ]
+ $GLOBALS['wgExtensionDirectory'] => [ 'extension.json', 'extension-wip.json' ],
+ $GLOBALS['wgStyleDirectory'] => [ 'skin.json', 'skin-wip.json' ]
];
foreach ( $directoryToJsonMap as $directory => $jsonFile ) {
- foreach ( new DirectoryIterator( __DIR__ . '/../../' . $directory ) as $iterator ) {
+ foreach ( new DirectoryIterator( $directory ) as $iterator ) {
foreach ( $jsonFile as $file ) {
+
$jsonPath = $iterator->getPathname() . '/' . $file;
if ( file_exists( $jsonPath ) ) {
+ // ExtensionRegistry->readFromQueue is not used as it checks extension/skin
+ // dependencies, which we don't need or want for unit tests.
$json = file_get_contents( $jsonPath );
$info = json_decode( $json, true );
$dir = dirname( $jsonPath );