# Run the extension hook
$bad = false;
if ( !Hooks::run( 'BadImage', [ $name, &$bad ] ) ) {
- return $bad;
+ return (bool)$bad;
}
$cache = ObjectCache::getLocalServerInstance( 'hash' );
*
* @since 1.17
*
- * @param mixed ... Parameters as strings, or a single argument that is
- * an array of strings.
+ * @param mixed ... Parameters as strings or arrays from
+ * Message::numParam() and the like, or a single array of parameters.
*
* @return Message $this
*/
public function params( /*...*/ ) {
$args = func_get_args();
- if ( isset( $args[0] ) && is_array( $args[0] ) ) {
- $args = $args[0];
+
+ // If $args has only one entry and it's an array, then it's either a
+ // non-varargs call or it happens to be a call with just a single
+ // "special" parameter. Since the "special" parameters don't have any
+ // numeric keys, we'll test that to differentiate the cases.
+ if ( count( $args ) === 1 && isset( $args[0] ) && is_array( $args[0] ) ) {
+ if ( $args[0] === [] ) {
+ $args = [];
+ } else {
+ foreach ( $args[0] as $key => $value ) {
+ if ( is_int( $key ) ) {
+ $args = $args[0];
+ break;
+ }
+ }
+ }
}
- $args_values = array_values( $args );
- $this->parameters = array_merge( $this->parameters, $args_values );
+
+ $this->parameters = array_merge( $this->parameters, array_values( $args ) );
return $this;
}
$pixels = $context->msg( 'unit-pixel' )->text();
foreach ( $context->getConfig()->get( 'ImageLimits' ) as $index => $limits ) {
- $display = "{$limits[0]}×{$limits[1]}" . $pixels;
+ // Note: A left-to-right marker (\u200e) is inserted, see T144386
+ $display = "{$limits[0]}" . json_decode( '"\u200e"' ) . "×{$limits[1]}" . $pixels;
$ret[$display] = $index;
}
'revdelUser' => $this->getUser(),
];
+ if ( isset( $params['badfilecontexttitle'] ) ) {
+ $badFileContextTitle = Title::newFromText( $params['badfilecontexttitle'] );
+ if ( !$badFileContextTitle ) {
+ $this->dieUsage( 'Invalid title in badfilecontexttitle parameter', 'invalid-title' );
+ }
+ } else {
+ $badFileContextTitle = false;
+ }
+
$pageIds = $this->getPageSet()->getGoodAndMissingTitlesByNamespace();
if ( !empty( $pageIds[NS_FILE] ) ) {
$titles = array_keys( $pageIds[NS_FILE] );
$result = $this->getResult();
foreach ( $titles as $title ) {
+ $info = [];
$pageId = $pageIds[NS_FILE][$title];
$start = $title === $fromTitle ? $fromTimestamp : $params['start'];
if ( !isset( $images[$title] ) ) {
- if ( isset( $prop['uploadwarning'] ) ) {
- // Uploadwarning needs info about non-existing files
+ if ( isset( $prop['uploadwarning'] ) || isset( $prop['badfile'] ) ) {
+ // uploadwarning and badfile need info about non-existing files
$images[$title] = wfLocalFile( $title );
+ // Doesn't exist, so set an empty image repository
+ $info['imagerepository'] = '';
} else {
$result->addValue(
[ 'query', 'pages', intval( $pageId ) ],
break;
}
- $fit = $result->addValue(
- [ 'query', 'pages', intval( $pageId ) ],
- 'imagerepository', $img->getRepoName()
- );
+ if ( !isset( $info['imagerepository'] ) ) {
+ $info['imagerepository'] = $img->getRepoName();
+ }
+ if ( isset( $prop['badfile'] ) ) {
+ $info['badfile'] = (bool)wfIsBadImage( $title, $badFileContextTitle );
+ }
+
+ $fit = $result->addValue( [ 'query', 'pages' ], intval( $pageId ), $info );
if ( !$fit ) {
if ( count( $pageIds[NS_FILE] ) == 1 ) {
// The user is screwed. imageinfo can't be solely
ApiBase::PARAM_DFLT => '',
ApiBase::PARAM_TYPE => 'string',
],
+ 'badfilecontexttitle' => [
+ ApiBase::PARAM_TYPE => 'string',
+ ],
'continue' => [
ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
],
'archivename' => 'apihelp-query+imageinfo-paramvalue-prop-archivename',
'bitdepth' => 'apihelp-query+imageinfo-paramvalue-prop-bitdepth',
'uploadwarning' => 'apihelp-query+imageinfo-paramvalue-prop-uploadwarning',
+ 'badfile' => 'apihelp-query+imageinfo-paramvalue-prop-badfile',
],
array_flip( $filter )
);
"apihelp-query+imageinfo-paramvalue-prop-archivename": "Adds the filename of the archive version for non-latest versions.",
"apihelp-query+imageinfo-paramvalue-prop-bitdepth": "Adds the bit depth of the version.",
"apihelp-query+imageinfo-paramvalue-prop-uploadwarning": "Used by the Special:Upload page to get information about an existing file. Not intended for use outside MediaWiki core.",
+ "apihelp-query+imageinfo-paramvalue-prop-badfile": "Adds whether the file is on the [[MediaWiki:Bad image list]]",
"apihelp-query+imageinfo-param-limit": "How many file revisions to return per file.",
"apihelp-query+imageinfo-param-start": "Timestamp to start listing from.",
"apihelp-query+imageinfo-param-end": "Timestamp to stop listing at.",
"apihelp-query+imageinfo-param-extmetadatamultilang": "If translations for extmetadata property are available, fetch all of them.",
"apihelp-query+imageinfo-param-extmetadatafilter": "If specified and non-empty, only these keys will be returned for $1prop=extmetadata.",
"apihelp-query+imageinfo-param-urlparam": "A handler specific parameter string. For example, PDFs might use <kbd>page15-100px</kbd>. <var>$1urlwidth</var> must be used and be consistent with <var>$1urlparam</var>.",
+ "apihelp-query+imageinfo-param-badfilecontexttitle": "If <kbd>$2prop=badfile</kbd> is set, this is the page title used when evaluting the [[MediaWiki:Bad image list]]",
"apihelp-query+imageinfo-param-localonly": "Look only for files in the local repository.",
"apihelp-query+imageinfo-example-simple": "Fetch information about the current version of [[:File:Albert Einstein Head.jpg]].",
"apihelp-query+imageinfo-example-dated": "Fetch information about versions of [[:File:Test.jpg]] from 2008 and later.",
"apihelp-query+imageinfo-paramvalue-prop-archivename": "{{doc-apihelp-paramvalue|query+imageinfo|prop|archivename}}",
"apihelp-query+imageinfo-paramvalue-prop-bitdepth": "{{doc-apihelp-paramvalue|query+imageinfo|prop|bitdepth}}",
"apihelp-query+imageinfo-paramvalue-prop-uploadwarning": "{{doc-apihelp-paramvalue|query+imageinfo|prop|uploadwarning}}",
+ "apihelp-query+imageinfo-paramvalue-prop-badfile": "{{doc-apihelp-paramvalue|query+imageinfo|prop|badfile}}",
"apihelp-query+imageinfo-param-limit": "{{doc-apihelp-param|query+imageinfo|limit}}",
"apihelp-query+imageinfo-param-start": "{{doc-apihelp-param|query+imageinfo|start}}",
"apihelp-query+imageinfo-param-end": "{{doc-apihelp-param|query+imageinfo|end}}",
"apihelp-query+imageinfo-param-extmetadatamultilang": "{{doc-apihelp-param|query+imageinfo|extmetadatamultilang}}",
"apihelp-query+imageinfo-param-extmetadatafilter": "{{doc-apihelp-param|query+imageinfo|extmetadatafilter}}",
"apihelp-query+imageinfo-param-urlparam": "{{doc-apihelp-param|query+imageinfo|urlparam}}",
+ "apihelp-query+imageinfo-param-badfilecontexttitle": "{{doc-apihelp-param|query+imageinfo|badfilecontexttitle}}",
"apihelp-query+imageinfo-param-localonly": "{{doc-apihelp-param|query+imageinfo|localonly}}",
"apihelp-query+imageinfo-example-simple": "{{doc-apihelp-example|query+imageinfo}}",
"apihelp-query+imageinfo-example-dated": "{{doc-apihelp-example|query+imageinfo}}",
* @return string
*/
function openStream() {
- global $wgLanguageCode;
+ global $wgContLang;
$ver = WikiExporter::schemaVersion();
return Xml::element( 'mediawiki', [
'xmlns' => "http://www.mediawiki.org/xml/export-$ver/",
'xsi:schemaLocation' => "http://www.mediawiki.org/xml/export-$ver/ " .
"http://www.mediawiki.org/xml/export-$ver.xsd",
'version' => $ver,
- 'xml:lang' => $wgLanguageCode ],
+ 'xml:lang' => $wgContLang->getHtmlCode() ],
null ) .
"\n" .
$this->siteInfo();
'patch-add-rc_name_type_patrolled_timestamp_index.sql' ],
[ 'addField', 'change_tag', 'ct_id', 'patch-change_tag-ct_id.sql' ],
[ 'addField', 'tag_summary', 'ts_id', 'patch-tag_summary-ts_id.sql' ],
+
+ // 1.29
+ [ 'addField', 'externallinks', 'el_index_60', 'patch-externallinks-el_index_60.sql' ],
];
}
[ 'addField', 'change_tag', 'ct_id', 'patch-change_tag-ct_id.sql' ],
[ 'addField', 'tag_summary', 'ts_id', 'patch-tag_summary-ts_id.sql' ],
[ 'modifyField', 'recentchanges', 'rc_ip', 'patch-rc_ip_modify.sql' ],
+
+ // 1.29
+ [ 'addField', 'externallinks', 'el_index_60', 'patch-externallinks-el_index_60.sql' ],
];
}
[ 'addField', 'change_tag', 'ct_id', 'patch-change_tag-ct_id.sql' ],
[ 'addField', 'tag_summary', 'ts_id', 'patch-tag_summary-ts_id.sql' ],
+ // 1.29
+ [ 'addField', 'externallinks', 'el_index_60', 'patch-externallinks-el_index_60.sql' ],
+
// KEEP THIS AT THE BOTTOM!!
[ 'doRebuildDuplicateFunction' ],
"INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('change_tag_ct_id_seq')" ],
[ 'addPgField', 'tag_summary', 'ts_id',
"INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('tag_summary_ts_id_seq')" ],
+
+ // 1.29
+ [ 'addPgField', 'externallinks', 'el_index_60', "BYTEA NOT NULL DEFAULT ''" ],
+ [ 'addPgIndex', 'externallinks', 'el_index_60', '( el_index_60, el_id )' ],
+ [ 'addPgIndex', 'externallinks', 'el_from_index_60', '( el_from, el_index_60, el_id )' ],
];
}
'patch-add-rc_name_type_patrolled_timestamp_index.sql' ],
[ 'addField', 'change_tag', 'ct_id', 'patch-change_tag-ct_id.sql' ],
[ 'addField', 'tag_summary', 'ts_id', 'patch-tag_summary-ts_id.sql' ],
+
+ // 1.29
+ [ 'addField', 'externallinks', 'el_index_60', 'patch-externallinks-el_index_60.sql' ],
];
}
# Constants needed for external link processing
# Everything except bracket, space, or control characters
# \p{Zs} is unicode 'separator, space' category. It covers the space 0x20
- # as well as U+3000 is IDEOGRAPHIC SPACE for bug 19052
+ # as well as U+3000 is IDEOGRAPHIC SPACE for T21052
const EXT_LINK_URL_CLASS = '[^][<>"\\x00-\\x20\\x7F\p{Zs}]';
# Simplified expression to match an IPv4 or IPv6 address, or
# at least one character of a host name (embeds EXT_LINK_URL_CLASS)
# Preprocessor_Hash is much faster than Preprocessor_DOM under HipHop
$this->mPreprocessorClass = 'Preprocessor_Hash';
} elseif ( extension_loaded( 'domxml' ) ) {
- # PECL extension that conflicts with the core DOM extension (bug 13770)
+ # PECL extension that conflicts with the core DOM extension (T15770)
wfDebug( "Warning: you have the obsolete domxml extension for PHP. Please remove it!\n" );
$this->mPreprocessorClass = 'Preprocessor_Hash';
} elseif ( extension_loaded( 'dom' ) ) {
public function __clone() {
$this->mInParse = false;
- // Bug 56226: When you create a reference "to" an object field, that
+ // T58226: When you create a reference "to" an object field, that
// makes the object field itself be a reference too (until the other
// reference goes out of scope). When cloning, any field that's a
// reference is copied as a reference in the new object. Both of these
$this->mStripState = new StripState;
- # Clear these on every parse, bug 4549
+ # Clear these on every parse, T6549
$this->mTplRedirCache = $this->mTplDomCache = [];
$this->mShowToc = true;
}
/**
- * Process the wikitext for the "?preload=" feature. (bug 5210)
+ * Process the wikitext for the "?preload=" feature. (T7210)
*
* "<noinclude>", "<includeonly>" etc. are parsed as for template
* transclusion, comments, templates, arguments, tags hooks and parser
# A cell could contain both parameters and data
$cell_data = explode( '|', $cell, 2 );
- # Bug 553: Note that a '|' inside an invalid link should not
+ # T2553: Note that a '|' inside an invalid link should not
# be mistaken as delimiting cell parameters
if ( strpos( $cell_data[0], '[[' ) !== false ) {
$cell = "{$previous}<{$last_tag}>{$cell}";
# Clean up special characters, only run once, next-to-last before doBlockLevels
$fixtags = [
- # french spaces, last one Guillemet-left
+ # French spaces, last one Guillemet-left
# only if there is something before the space
'/(.) (?=\\?|:|;|!|%|\\302\\273)/' => '\\1 ',
# french spaces, Guillemet-right
'/(\\302\\253) /' => '\\1 ',
- '/ (!\s*important)/' => ' \\1', # Beware of CSS magic word !important, bug #11874.
+ '/ (!\s*important)/' => ' \\1', # Beware of CSS magic word !important, T13874.
];
$text = preg_replace( array_keys( $fixtags ), array_values( $fixtags ), $text );
}
} else {
# attempt to sanitize at least some nesting problems
- # (bug #2702 and quite a few others)
+ # (T4702 and quite a few others)
$tidyregs = [
# ''Something [http://www.cool.com cool''] -->
# <i>Something</i><a href="http://www.cool.com"..><i>cool></i></a>
'/(<([bi])>)(<([bi])>)?([^<]*)(<\/?a[^<]*>)([^<]*)(<\/\\4>)?(<\/\\2>)/' =>
'\\1\\3\\5\\8\\9\\6\\1\\3\\7\\8\\9',
# fix up an anchor inside another anchor, only
- # at least for a single single nested link (bug 3695)
+ # at least for a single single nested link (T5695)
'/(<a[^>]+>)([^<]*)(<a[^>]+>[^<]*)<\/a>(.*)<\/a>/' =>
'\\1\\2</a>\\3</a>\\1\\4</a>',
# fix div inside inline elements- doBlockLevels won't wrap a line which
$thislen = strlen( $arr[$i] );
// If there are ever four apostrophes, assume the first is supposed to
// be text, and the remaining three constitute mark-up for bold text.
- // (bug 13227: ''''foo'''' turns into ' ''' foo ' ''')
+ // (T15227: ''''foo'''' turns into ' ''' foo ' ''')
if ( $thislen == 4 ) {
$arr[$i - 1] .= "'";
$arr[$i] = "'''";
} elseif ( $thislen > 5 ) {
// If there are more than 5 apostrophes in a row, assume they're all
// text except for the last 5.
- // (bug 13227: ''''''foo'''''' turns into ' ''''' foo ' ''''')
+ // (T15227: ''''''foo'''''' turns into ' ''''' foo ' ''''')
$arr[$i - 1] .= str_repeat( "'", $thislen - 5 );
$arr[$i] = "'''''";
$thislen = 5;
# If we get a ] at the beginning of $m[3] that means we have a link that's something like:
# [[Image:Foo.jpg|[http://example.com desc]]] <- having three ] in a row fucks up,
# the real problem is with the $e1 regex
- # See bug 1300.
+ # See T1500.
# Still some problems for cases where the ] is meant to be outside punctuation,
- # and no image is in sight. See bug 2095.
+ # and no image is in sight. See T4095.
if ( $text !== ''
&& substr( $m[3], 0, 1 ) === ']'
&& strpos( $text, '[' ) !== false
if ( $wasblank ) {
$text = $link;
} else {
- # Bug 4598 madness. Handle the quotes only if they come from the alternate part
+ # T6598 madness. Handle the quotes only if they come from the alternate part
# [[Lista d''e paise d''o munno]] -> <a href="...">Lista d''e paise d''o munno</a>
# [[Criticism of Harry Potter|Criticism of ''Harry Potter'']]
# -> <a href="Criticism of Harry Potter">Criticism of <i>Harry Potter</i></a>
in_array( $iw, $wgExtraInterlanguageLinkPrefixes )
)
) {
- # Bug 24502: filter duplicates
+ # T26502: filter duplicates
if ( !isset( $this->mLangLinkLanguages[$iw] ) ) {
$this->mLangLinkLanguages[$iw] = true;
$this->mOutput->addLanguageLink( $nt->getFullText() );
continue;
}
} elseif ( $ns == NS_CATEGORY ) {
- $s = rtrim( $s . "\n" ); # bug 87
+ $s = rtrim( $s . "\n" ); # T2087
if ( $wasblank ) {
$sortkey = $this->getDefaultSort();
$this->mOutput->addCategory( $nt->getDBkey(), $sortkey );
/**
- * Strip the whitespace Category links produce, see bug 87
+ * Strip the whitespace Category links produce, see T2087
*/
$s .= trim( $prefix . $trail, "\n" ) == '' ? '' : $prefix . $trail;
$subjPage = $this->mTitle->getSubjectPage();
$value = wfEscapeWikiText( $subjPage->getPrefixedURL() );
break;
- case 'pageid': // requested in bug 23427
+ case 'pageid': // requested in T25427
$pageid = $this->getTitle()->getArticleID();
if ( $pageid == 0 ) {
# 0 means the page doesn't exist in the database,
$value = $pageLang->formatNum( MWTimestamp::getInstance( $ts )->format( 'H' ), true );
break;
case 'currentweek':
- # @bug 4594 PHP5 has it zero padded, PHP4 does not, cast to
+ # @bug T6594 PHP5 has it zero padded, PHP4 does not, cast to
# int to remove the padding
$value = $pageLang->formatNum( (int)MWTimestamp::getInstance( $ts )->format( 'W' ) );
break;
$value = $pageLang->formatNum( MWTimestamp::getLocalInstance( $ts )->format( 'H' ), true );
break;
case 'localweek':
- # @bug 4594 PHP5 has it zero padded, PHP4 does not, cast to
+ # @bug T6594 PHP5 has it zero padded, PHP4 does not, cast to
# int to remove the padding
$value = $pageLang->formatNum( (int)MWTimestamp::getLocalInstance( $ts )->format( 'W' ) );
break;
* included. Default is to assume a direct page view.
*
* The generated DOM tree must depend only on the input text and the flags.
- * The DOM tree must be the same in OT_HTML and OT_WIKI mode, to avoid a regression of bug 4899.
+ * The DOM tree must be the same in OT_HTML and OT_WIKI mode, to avoid a regression of T6899.
*
* Any flag added to the $flags parameter here, or any other parameter liable to cause a
* change in the DOM tree for a given text, must be passed through the section identifier
&& !$piece['lineStart']
&& preg_match( '/^(?:{\\||:|;|#|\*)/', $text )
) {
- # Bug 529: if the template begins with a table or block-level
+ # T2529: if the template begins with a table or block-level
# element, it should be treated as beginning a new line.
# This behavior is somewhat controversial.
$text = "\n" . $text;
if ( is_string( $text ) && !$this->incrementIncludeSize( 'post-expand', strlen( $text ) ) ) {
# Error, oversize inclusion
if ( $titleText !== false ) {
- # Make a working, properly escaped link if possible (bug 23588)
+ # Make a working, properly escaped link if possible (T25588)
$text = "[[:$titleText]]";
} else {
# This will probably not be a working link, but at least it may
) {
$this->addTrackingCategory( 'hidden-category-category' );
}
- # (bug 8068) Allow control over whether robots index a page.
- # @todo FIXME: Bug 14899: __INDEX__ always overrides __NOINDEX__ here! This
- # is not desirable, the last one on the page should win.
+ # (T10068) Allow control over whether robots index a page.
+ # __INDEX__ always overrides __NOINDEX__, see T16899
if ( isset( $this->mDoubleUnderscores['noindex'] ) && $this->mTitle->canUseNoindex() ) {
$this->mOutput->setIndexPolicy( 'noindex' );
$this->addTrackingCategory( 'noindex-category' );
# Strip out HTML (first regex removes any tag not allowed)
# Allowed tags are:
- # * <sup> and <sub> (bug 8393)
- # * <i> (bug 26375)
+ # * <sup> and <sub> (T10393)
+ # * <i> (T28375)
# * <b> (r105284)
- # * <bdi> (bug 72884)
- # * <span dir="rtl"> and <span dir="ltr"> (bug 35167)
+ # * <bdi> (T74884)
+ # * <span dir="rtl"> and <span dir="ltr"> (T37167)
# * <s> and <strike> (T35715)
# We strip any parameter from accepted tags (second regex), except dir="rtl|ltr" from <span>,
# to allow setting directionality in toc items.
'noninitial' );
}
- # HTML names must be case-insensitively unique (bug 10721).
+ # HTML names must be case-insensitively unique (T12721).
# This does not apply to Unicode characters per
# https://www.w3.org/TR/html5/infrastructure.html#case-sensitivity-and-string-comparison
# @todo FIXME: We may be changing them depending on the current locale.
# the database, we use $wgContLang here in order to give
# everyone the same signature and use the default one rather
# than the one selected in each user's preferences.
- # (see also bug 12815)
+ # (see also T14815)
$ts = $this->mOptions->getTimestamp();
$timestamp = MWTimestamp::getLocalInstance( $ts );
$ts = $timestamp->format( 'YmdHis' );
$validated = true;
}
}
- # else no validation -- bug 13436
+ # else no validation -- T15436
} else {
if ( $type === 'handler' ) {
# Validate handler parameter
* was derived during a template inclusion parse, in other words this is a template
* section edit link. If no flags are given, it was an ordinary section edit link.
* This flag is required to avoid a section numbering mismatch when a section is
- * enclosed by "<includeonly>" (bug 6563).
+ * enclosed by "<includeonly>" (T8563).
*
* The section number 0 pulls the text before the first heading; other numbers will
* pull the given section along with its lower-level subsections. If the section is
return $parsedWidthParam;
}
$m = [];
- # (bug 13500) In both cases (width/height and width only),
+ # (T15500) In both cases (width/height and width only),
# permit trailing "px" for backward compatibility.
if ( preg_match( '/^([0-9]*)x([0-9]*)\s*(?:px)?\s*$/', $value, $m ) ) {
$width = intval( $m[1] );
],
];
- $form = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
+ HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
->setWrapperLegendMsg( 'sp-contributions-search' )
->setSubmitTextMsg( 'sp-contributions-submit' )
// prevent setting subpage and 'target' parameter at the same time
],
];
- $form = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
+ HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
->setWrapperLegendMsg( 'mimesearch' )
->setSubmitTextMsg( 'ilsubmit' )
->setAction( $this->getPageTitle()->getLocalURL() )
unset( $formDescriptor['hidepatrolled'] );
}
- $form = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
+ HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
->setWrapperLegendMsg( 'newimages-legend' )
->setSubmitTextMsg( 'ilsubmit' )
->setMethod( 'get' )
$this->namespaceNames = self::$dataCache->getItem( $this->mCode, 'namespaceNames' );
$validNamespaces = MWNamespace::getCanonicalNamespaces();
+ /** @suppress PhanTypeInvalidLeftOperand */
$this->namespaceNames = $wgExtraNamespaces + $this->namespaceNames + $validNamespaces;
$this->namespaceNames[NS_PROJECT] = $wgMetaNamespace;
--- /dev/null
+-- @since 1.29
+ALTER TABLE /*$wgDBprefix*/externallinks ADD COLUMN el_index_60 varbinary(60) NOT NULL DEFAULT '';
+CREATE INDEX /*i*/el_index_60 ON /*_*/externallinks (el_index_60, el_id);
+CREATE INDEX /*i*/el_from_index_60 ON /*_*/externallinks (el_from, el_index_60, el_id);
-- which allows for fast searching for all pages under example.com with the
-- clause:
-- WHERE el_index LIKE 'http://com.example.%'
- el_index nvarchar(450) NOT NULL
+ el_index nvarchar(450) NOT NULL,
+
+ -- This is el_index truncated to 60 bytes to allow for sortable queries that
+ -- aren't supported by a partial index.
+ -- @todo Drop the default once this is deployed everywhere and code is populating it.
+ el_index_60 varbinary(60) NOT NULL default ''
);
CREATE INDEX /*i*/el_from ON /*_*/externallinks (el_from);
CREATE INDEX /*i*/el_index ON /*_*/externallinks (el_index);
+CREATE INDEX /*i*/el_index_60 ON /*_*/externallinks (el_index_60, el_id);
+CREATE INDEX /*i*/el_from_index_60 ON /*_*/externallinks (el_from, el_index_60, el_id);
-- el_to index intentionally not added; we cannot index nvarchar(max) columns,
-- but we also cannot restrict el_to to a smaller column size as the external
-- link may be larger.
--- /dev/null
+define mw_prefix='{$wgDBprefix}';
+
+ALTER TABLE &mw_prefix.externallinks ADD el_index_60 VARBINARY(60) NOT NULL DEFAULT '';
+CREATE INDEX &mw_prefix.externallinks_i04 ON &mw_prefix.externallinks (el_index_60, el_id);
+CREATE INDEX &mw_prefix.externallinks_i05 ON &mw_prefix.externallinks (el_from, el_index_60, el_id);
el_id NUMBER NOT NULL,
el_from NUMBER NOT NULL,
el_to VARCHAR2(2048) NOT NULL,
- el_index VARCHAR2(2048) NOT NULL
+ el_index VARCHAR2(2048) NOT NULL,
+ el_index_60 VARBINARY(60) NOT NULL DEFAULT ''
);
ALTER TABLE &mw_prefix.externallinks ADD CONSTRAINT &mw_prefix.externallinks_pk PRIMARY KEY (el_id);
ALTER TABLE &mw_prefix.externallinks ADD CONSTRAINT &mw_prefix.externallinks_fk1 FOREIGN KEY (el_from) REFERENCES &mw_prefix.page(page_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX &mw_prefix.externallinks_i01 ON &mw_prefix.externallinks (el_from, el_to);
CREATE INDEX &mw_prefix.externallinks_i02 ON &mw_prefix.externallinks (el_to, el_from);
CREATE INDEX &mw_prefix.externallinks_i03 ON &mw_prefix.externallinks (el_index);
+CREATE INDEX &mw_prefix.externallinks_i04 ON &mw_prefix.externallinks (el_index_60, el_id);
+CREATE INDEX &mw_prefix.externallinks_i05 ON &mw_prefix.externallinks (el_from, el_index_60, el_id);
CREATE TABLE &mw_prefix.langlinks (
ll_from NUMBER NOT NULL,
CREATE SEQUENCE externallinks_el_id_seq;
CREATE TABLE externallinks (
- el_id INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('externallinks_el_id_seq'),
- el_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
- el_to TEXT NOT NULL,
- el_index TEXT NOT NULL
+ el_id INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('externallinks_el_id_seq'),
+ el_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+ el_to TEXT NOT NULL,
+ el_index TEXT NOT NULL,
+ el_index_60 BYTEA NOT NULL DEFAULT ''
);
CREATE INDEX externallinks_from_to ON externallinks (el_from,el_to);
CREATE INDEX externallinks_index ON externallinks (el_index);
+CREATE INDEX el_index_60 ON externallinks (el_index_60, el_id);
+CREATE INDEX el_from_index_60 ON externallinks (el_from, el_index_60, el_id);
CREATE TABLE langlinks (
ll_from INTEGER NOT NULL REFERENCES page (page_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
-- which allows for fast searching for all pages under example.com with the
-- clause:
-- WHERE el_index LIKE 'http://com.example.%'
- el_index blob NOT NULL
+ el_index blob NOT NULL,
+
+ -- This is el_index truncated to 60 bytes to allow for sortable queries that
+ -- aren't supported by a partial index.
+ -- @todo Drop the default once this is deployed everywhere and code is populating it.
+ el_index_60 varbinary(60) NOT NULL default ''
) /*$wgDBTableOptions*/;
CREATE INDEX /*i*/el_from ON /*_*/externallinks (el_from, el_to(40));
CREATE INDEX /*i*/el_to ON /*_*/externallinks (el_to(60), el_from);
CREATE INDEX /*i*/el_index ON /*_*/externallinks (el_index(60));
+CREATE INDEX /*i*/el_index_60 ON /*_*/externallinks (el_index_60, el_id);
+CREATE INDEX /*i*/el_from_index_60 ON /*_*/externallinks (el_from, el_index_60, el_id);
--
-- Track interlanguage links
.done( function ( watchResponse ) {
var mwTitle, message, otherAction = action === 'watch' ? 'unwatch' : 'watch';
- message = action === 'watch' ? 'addedwatchtext' : 'removedwatchtext';
mwTitle = mw.Title.newFromText( title );
- if ( mwTitle && mwTitle.getNamespaceId() > 0 &&
- /* eslint-disable no-bitwise */
- ( mwTitle.getNamespaceId() & 1 ) === 1
- /* eslint-enable no-bitwise */
- ) {
- message += '-talk';
+ if ( mwTitle && mwTitle.getNamespaceId() > 0 && mwTitle.getNamespaceId() % 2 === 1 ) {
+ message = action === 'watch' ? 'addedwatchtext-talk' : 'removedwatchtext-talk';
+ } else {
+ message = action === 'watch' ? 'addedwatchtext' : 'removedwatchtext';
}
mw.notify( mw.message( message, title ).parseDom(), {
php7.0 $PHAN \
--project-root-directory "$ROOT" \
--config-file "$ROOT/tests/phan/config.php" \
- --output "$ROOT/tests/phan/issues/issues-${REV}" \
+ --output "$RUN" \
"${@}"
+
+cat "${RUN}" | php "$ROOT/tests/phan/bin/postprocess-phan.php" "${@}" > /tmp/phan.$$
EXIT_CODE="$?"
+mv /tmp/phan.$$ "${RUN}"
# Re-link the latest file
rm -f "${ISSUES}/latest"
--- /dev/null
+<?php
+
+abstract class Suppressor {
+ /**
+ * @param string $input
+ * @return bool do errors remain
+ */
+ abstract public function suppress( $input );
+
+ /**
+ * @param string[] $source
+ * @param string $type
+ * @param int $lineno
+ * @return bool
+ */
+ protected function isSuppressed( array $source, $type, $lineno ) {
+ return $lineno > 0 && preg_match(
+ "|/\*\* @suppress {$type} |",
+ $source[$lineno - 1]
+ );
+ }
+}
+
+class TextSuppressor extends Suppressor {
+ /**
+ * @param string $input
+ * @return bool do errors remain
+ */
+ public function suppress( $input ) {
+ $hasErrors = false;
+ $errors = [];
+ foreach ( explode( "\n", $input ) as $error ) {
+ if ( empty( $error ) ) {
+ continue;
+ }
+ if ( !preg_match( '/^(.*):(\d+) (Phan\w+) (.*)$/', $error, $matches ) ) {
+ echo "Failed to parse line: $error\n";
+ continue;
+ }
+ list( $source, $file, $lineno, $type, $message ) = $matches;
+ $errors[$file][] = [
+ 'orig' => $error,
+ // convert from 1 indexed to 0 indexed
+ 'lineno' => $lineno - 1,
+ 'type' => $type,
+ ];
+ }
+ foreach ( $errors as $file => $fileErrors ) {
+ $source = file( $file );
+ foreach ( $fileErrors as $error ) {
+ if ( !$this->isSuppressed( $source, $error['type'], $error['lineno'] ) ) {
+ echo $error['orig'], "\n";
+ $hasErrors = true;
+ }
+ }
+ }
+
+ return $hasErrors;
+ }
+}
+
+class CheckStyleSuppressor extends Suppressor {
+ /**
+ * @param string $input
+ * @return bool True do errors remain
+ */
+ public function suppress( $input ) {
+ $dom = new DOMDocument();
+ $dom->loadXML( $input );
+ $hasErrors = false;
+ // DOMNodeList's are "live", convert to an array so it works as expected
+ $files = [];
+ foreach ( $dom->getElementsByTagName( 'file' ) as $file ) {
+ $files[] = $file;
+ }
+ foreach ( $files as $file ) {
+ $errors = [];
+ foreach ( $file->getElementsByTagName( 'error' ) as $error ) {
+ $errors[] = $error;
+ }
+ $source = file( $file->getAttribute( 'name' ) );
+ $fileHasErrors = false;
+ foreach ( $errors as $error ) {
+ $lineno = $error->getAttribute( 'line' ) - 1;
+ $type = $error->getAttribute( 'source' );
+ if ( $this->isSuppressed( $source, $type, $lineno ) ) {
+ $error->parentNode->removeChild( $error );
+ } else {
+ $fileHasErrors = true;
+ $hasErrors = true;
+ }
+ }
+ if ( !$fileHasErrors ) {
+ $file->parentNode->removeChild( $file );
+ }
+ }
+ echo $dom->saveXML();
+
+ return $hasErrors;
+ }
+}
+
+class NoopSuppressor extends Suppressor {
+ private $mode;
+
+ public function __construct( $mode ) {
+ $this->mode = $mode;
+ }
+ public function suppress( $input ) {
+ echo "Unsupported output mode: {$this->mode}\n$input";
+ return true;
+ }
+}
+
+$opt = getopt( "m:", [ "output-mode:" ] );
+// if provided multiple times getopt returns an array
+if ( isset( $opt['m'] ) ) {
+ $mode = $opt['m'];
+} elseif ( isset( $mode['output-mode'] ) ) {
+ $mode = $opt['output-mode'];
+} else {
+ $mode = 'text';
+}
+if ( is_array( $mode ) ) {
+ // If an option is passed multiple times getopt returns an
+ // array. Just take the last one.
+ $mode = end( $mode );
+}
+
+switch ( $mode ) {
+case 'text':
+ $suppressor = new TextSuppressor();
+ break;
+case 'checkstyle':
+ $suppressor = new CheckStyleSuppressor();
+ break;
+default:
+ $suppressor = new NoopSuppressor( $mode );
+}
+
+$input = file_get_contents( 'php://stdin' );
+$hasErrors = $suppressor->suppress( $input );
+
+if ( $hasErrors ) {
+ exit( 1 );
+}
"PhanTypeArraySuspicious",
// approximate error count: 4
"PhanTypeComparisonFromArray",
- // approximate error count: 1
- "PhanTypeInvalidLeftOperand",
// approximate error count: 3
"PhanTypeInvalidRightOperand",
// approximate error count: 563
"PhanTypeMissingReturn",
// approximate error count: 5
"PhanTypeNonVarPassByRef",
- // approximate error count: 3
- "PhanTypeVoidAssignment",
// approximate error count: 27
"PhanUndeclaredConstant",
// approximate error count: 185
/**
* Make sure MediaWikiTestCase extending classes have called their
* parent setUp method
+ *
+ * With strict coverage activated in PHP_CodeCoverage, this test would be
+ * marked as risky without the following annotation (T152923).
+ * @coversNothing
*/
final public function testMediaWikiTestCaseParentSetupCalled() {
$this->assertArrayHasKey( 'setUp', $this->called,
[],
[],
],
+ [
+ [],
+ [ [] ],
+ ],
[
[ 'foo' ],
[ 'foo' ],
[ [ 'baz', 'foo' ] ],
],
[
- [ 'baz', 'foo' ],
+ [ Message::rawParam( 'baz' ) ],
+ [ Message::rawParam( 'baz' ) ],
+ ],
+ [
+ [ Message::rawParam( 'baz' ), 'foo' ],
+ [ Message::rawParam( 'baz' ), 'foo' ],
+ ],
+ [
+ [ Message::rawParam( 'baz' ) ],
+ [ [ Message::rawParam( 'baz' ) ] ],
+ ],
+ [
+ [ Message::rawParam( 'baz' ), 'foo' ],
+ [ [ Message::rawParam( 'baz' ), 'foo' ] ],
+ ],
+
+ // Test handling of erroneous input, to detect if it changes
+ [
+ [ [ 'baz', 'foo' ], 'hhh' ],
[ [ 'baz', 'foo' ], 'hhh' ],
],
[
- [ 'baz', 'foo' ],
+ [ [ 'baz', 'foo' ], 'hhh', [ 'ahahahahha' ] ],
[ [ 'baz', 'foo' ], 'hhh', [ 'ahahahahha' ] ],
],
[
- [ 'baz', 'foo' ],
+ [ [ 'baz', 'foo' ], [ 'ahahahahha' ] ],
[ [ 'baz', 'foo' ], [ 'ahahahahha' ] ],
],
[
- [ 'baz' ],
+ [ [ 'baz' ], [ 'ahahahahha' ] ],
[ [ 'baz' ], [ 'ahahahahha' ] ],
],
];