Conversation
malberts
added a commit
that referenced
this pull request
Aug 13, 2026
Three ways an errored query failed to reach the reader as an error: A query that failed still wrote its empty result into the cache, so for the rest of the timeout every reader got 'No results.' with nothing to say the query had failed at all. Only successful queries are cached now. Under the default five minute timeout this made a transient Bugzilla outage look like an empty bug list for five minutes after Bugzilla recovered. display="number" ignored the error flag entirely and counted $query->data['bugs'] unconditionally. On any failed query that key is absent, so PHP 8 raised 'count(): Argument #1 ($value) must be of type Countable|array, null given' and the page could not be saved or rendered. It now renders the same error the other display modes do. The error template ran its message through print_r without the return argument, which echoes the value and then echoes print_r's own return value, appending a stray '1' to every error box. Messages are escaped now, and the print_r fallback only applies to values that are not strings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
malberts
marked this pull request as ready for review
August 13, 2026 20:16
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The extension does not run on MediaWiki 1.43. A page containing a
<bugzilla>tag fatals before any request ismade, and the ResourceLoader module is unloadable on every page of the wiki. This branch makes it work, and fixes
the error paths that were hiding failures rather than reporting them.
Blocking breakage
MWHttpRequest::factory()was removed from core. Called at three sites, so every page with a tag failed withCall to undefined method MWHttpRequest::factory()and could not be saved or rendered. Replaced withMediaWikiServices::getHttpRequestFactory()->create(), which takes the same arguments and returns the same object;the option arrays are unchanged.
There was no
extension.json, sowfLoadExtension( 'Bugzilla' )took the whole wiki down with an HTTP 500.Registration now lives in
extension.json, andBugzilla.phpis removed rather than kept as a shim. See thebreaking change below.
This also fixes an ordering trap.
Bugzilla.phpassigned every$wgBugzilla*global at the bottom of the file,so any value a wiki set before the
require_oncewas silently discarded,$wgBugzillaRESTURLincluded. Registryconfiguration is applied after
LocalSettings.phpis read and skips globals the wiki has already set, so bothorders now work.
The
ext.Bugzillamodule could not load. It depended onjquery.ui.core, which core renamed tojquery.ui(keeping
jquery.ui.coreonly as a file inside it), so ResourceLoader refused the whole module withUnknown module: jquery.ui.core. DataTables only needs jQuery, which is always present, so the dependency isdropped rather than pointed at the now-deprecated
jquery.ui. Also removed:'position', no longer aResourceLoader option, and two declared messages that were never defined in the i18n file.
The module was queued from
BeforePageDisplayon every page, which is what turned an unloadable module into awiki-wide console warning. It is queued from the tag renderer instead.
PHP 8.2 deprecations that have present-day consequences
Only the two that cost something today; nothing here is future-proofing against an unreleased PHP.
"${var}"interpolation in two templates. Those templates run inside the output buffer that becomes the tag'sreturn value, so the notice is captured into the buffer rather than the log and is rendered into the article
body above every table and every list.
errors, so without the declarations most of the tests below fail before asserting anything. They are declared
public because the templates and
BugzillaOutputread them from outside the declaring class.Deprecated core APIs
ObjectCache::getInstance()becomesObjectCacheFactory.StatusValue::getErrors()becomes the combined messagethe
Statusalready builds.catch ( MWException )becomescatch ( Exception ):MWExceptionis deprecatedsince 1.40 and
HttpRequestFactorydoes not throw it, so both catch blocks were unreachable and a DNS failureescaped as an uncaught
Errorinstead of rendering the error box the code intended.Errors that were not reaching the reader
No results.with nothing to indicate the query had failed. A transient Bugzilla outage looked like an empty buglist for five minutes after Bugzilla recovered.
nulland was passedon unchanged, also reading as zero bugs.
display="number"ignored the error flag and counteddata['bugs']unconditionally. On any failed querythat key is absent, so PHP 8 raised
count(): Argument #1 ($value) must be of type Countable|array, null givenand the page could not be saved.error.tplranprint_rwithout the return argument, echoing the value and then echoingprint_r's ownreturn value, appending a stray
1to every error box. Combined withgetErrors()[0]returning a raw array, anHTTP 404 reached the reader as a literal
Array ( [type] => error [message] => http-bad-status ... )1. Messagesare escaped now and read as
There was a problem during the HTTP request: 404 Not Found.Escaping and tag input
include_fieldscomes from the tag body, so any user who canedit a page picks them, and a name containing a quote closed the
classattribute and turned the rest intomarkup. Tag output reaches the reader through the strip state, which MediaWiki does not sanitise. The
<th>textand every bug value were already escaped, so the omission looks accidental rather than deliberate. This was
unreachable while the fetch still fataled, which is why it surfaces with the port rather than before it.
list.tplread$bug[$field]and$bug['status']without checking they are there, which is a warning and anhtmlspecialchars(null)deprecation per row wheneverinclude_fieldsdoes not cover them.table.tplalreadyguards both.
<bugzilla>123</bugzilla>) reached theinclude_fieldsassignment with a scalar and threw an uncaught
Error, so the page could not be saved or rendered. Only a bodythat
json_decodecould not read at all was rejected before.Display modes with no implementation
display="inline"mapped to aBugzillaInlineclass that does not exist anywhere in the repository, so the tagfataled with
Class "BugzillaInline" not found. The mapping is dropped, so it takes the same path as any otherunrecognised display value: a table.
type="count"has had no reachable template since graphing support was removed.displayis normalised to one oflist,numberortablebefore the template path is built, sotemplates/count/bar.tplandtemplates/count/pie.tplcould never be selected, and both still referenced the$response->imagethe graphingcode used to set. They are removed;
type="count"reports the invalid combination instead of pointing at deadfiles.
$wgBugzillaJqueryTableSetting this to
trueproduced onlyReferenceError: $ is not definedand a plain table: the DataTables bootstrapwas a bare
$(document).ready(...)emitted into the head, before jQuery exists. It moves into the module asweb/js/bugzilla.init.js, where ResourceLoader supplies$, and attaches through thewikipage.contenthook. Thedead
$wgVersion < 1.17branch that loaded the bundled jQuery 1.6.2 and jQuery UI 1.8.14 goes with it.Tests
The suite could not run: its one class extended
PHPUnit_Framework_TestCase(removed in PHPUnit 6) andphpunit.xmldeclared the PHPUnit 4.5 schema. Classes now extendMediaWikiIntegrationTestCase,phpunit.xml.distmoves to the extension root, and core's entry point picks the suite up:The existing
prepare_optionsandrebase_fieldscases are kept. New coverage targets what a rendered page cannotshow: that a failed query leaves nothing in the cache, that
display="number"reports errors rather than countingan absent result, that errors are escaped, and that the HTTP round trip returns what Bugzilla sent, reports a 200
carrying something other than JSON, and reports an HTTP error on one line. Dev dependencies were an abandoned
package name and a 2016 codesniffer; both now track the versions core uses. No runtime dependency is added.
Behaviour changes worth a maintainer's attention
Bugzilla.phpis gone, sorequire_onceno longer loads the extension. Every existing install needswfLoadExtension( 'Bugzilla' );instead. This is deliberate rather than shimmed:extension.jsonrequiresMediaWiki 1.43, so a shim would not help anyone on an older version (
ExtensionRegistryrefuses the loadhowever it was reached), and on 1.43 the extension fataled on any page containing a tag, so there is no working
install whose entry point needs preserving. A wiki that upgrades without changing its config gets
Failed opening required '.../Bugzilla.php'on every page. Worth noting for anyone monitoring by status code:that fatal is served as HTTP 200, not 500.
$wgBugzillaRESTURLchanges fromhttps://bugzilla.mozilla.org/bzapito.../rest. Thebzapiproxy is gone and returns 404, so the shipped default cannot work against any current Bugzilla.$wgBugzillaExtVersionis removed. The version has a single home inextension.jsonand the RESTUser-Agentreads it from the registry.$wgBugzillaTable['lengthMenu']reaches DataTables as an array, accepted either as the JSON array literalthe default ships or as a PHP array. The shipped default is unchanged.
$wgBugzillaTableuses thearray_plusmerge strategy, so a wiki setting one key keeps the shipped default for the other;
$wgBugzillaDefaultFieldsuses
provide_default, since replacing a flat list outright is what setting it means.that combined set, so queries for subsets of it share one entry. Building the request from the raw options
instead meant pages could share a key while asking Bugzilla for different things, and whichever rendered first
decided what the others showed. The request carries up to four extra fields as a result.
Considered, omitted
Left alone deliberately, each still present on the branch: the vendored jQuery 1.6.2 / jQuery UI 1.8.14 /
DataTables 1.8.1 under
web/;bJQueryUI: truewithout the jQuery UI stylesheet that would theme it;Utils.php,whose
gChartExtendedEncode()has no callers and is never included;BugzillaBaseQuery::_update_cache(), whichcalls an undefined
$this->_getCache()and would fatal if anything reached it; the custom.css hook, which passes aURL path to
file_exists()and is therefore always false;'ssl_verify_peer'and'follow_redirects', which arenot MWHttpRequest option names and are silently ignored; and the cache key, which hashes query options without the
query type, so
type=bugandtype=countwith identical options collide.Verification
Exercised on a real MediaWiki 1.43.9 stack:
displayin {table, list, number} against$wgBugzillaJqueryTablein {false, true}, in a custom namespace pair and in main, plusdisplay="inline"andtype="count". Bug data came frombugzilla.mozilla.org/restover anonymous GETs. The deliberate error paths(unreachable host, HTTP error, non-JSON body, empty result set, API error payload, invalid tag JSON) and the
cache-masking behaviour were exercised against a local fixture server. Console checks were Playwright-driven
rather than hand-verified: clean on a page with no tag, and on tag pages with the jQuery table both off and on,
where DataTables initialises and paginates.