Page MenuHomePhabricator

Uninitialised Parser::mOutput triggers an error
Open, Needs TriagePublicBUG REPORT

Description

Steps to replicate the issue (include links if applicable):

  • On MediaWiki 1.41, activate the extensions VisualEditor and Lingo with branches REL1_41, and configure some logging.
  • Navigate on some page and try to edit with the VisualEditor

What happens?:

  • It starts to load, but fails before the end of the loading with the user message [252518f0e04244d3bc266935] Caught exception of type Error
  • In the logs, there is the error Typed property Parser::$mOutput must not be accessed before initialization and the stacktrace:
{
  "class": "Error",
  "message": "Typed property Parser::$mOutput must not be accessed before initialization",
  "code": 0,
  "file": "/var/www/html/mediawiki-1.41/includes/parser/Parser.php:1052",
  "trace": [
    "/var/www/html/mediawiki-1.41/extensions/Lingo/src/LingoParser.php:391",
    "/var/www/html/mediawiki-1.41/extensions/Lingo/src/LingoParser.php:77",
    "/var/www/html/mediawiki-1.41/extensions/Lingo/src/Lingo.php:72",
    "/var/www/html/mediawiki-1.41/includes/HookContainer/HookContainer.php:161",
    "/var/www/html/mediawiki-1.41/includes/HookContainer/HookRunner.php:1181",
    "/var/www/html/mediawiki-1.41/includes/content/ContentHandler.php:1774",
    "/var/www/html/mediawiki-1.41/includes/content/Renderer/ContentRenderer.php:47",
    "/var/www/html/mediawiki-1.41/includes/Revision/RenderedRevision.php:260",
    "/var/www/html/mediawiki-1.41/includes/Revision/RenderedRevision.php:232",
    "/var/www/html/mediawiki-1.41/includes/Revision/RevisionRenderer.php:223",
    "/var/www/html/mediawiki-1.41/includes/Revision/RevisionRenderer.php:164",
    "/var/www/html/mediawiki-1.41/includes/Revision/RenderedRevision.php:199",
    "/var/www/html/mediawiki-1.41/includes/poolcounter/PoolWorkArticleView.php:84",
    "/var/www/html/mediawiki-1.41/includes/poolcounter/PoolWorkArticleViewCurrent.php:104",
    "/var/www/html/mediawiki-1.41/includes/poolcounter/PoolCounterWork.php:167",
    "/var/www/html/mediawiki-1.41/includes/page/ParserOutputAccess.php:304",
    "/var/www/html/mediawiki-1.41/includes/parser/Parsoid/ParsoidOutputAccess.php:227",
    "/var/www/html/mediawiki-1.41/includes/Rest/Handler/Helper/HtmlOutputRendererHelper.php:769",
    "/var/www/html/mediawiki-1.41/includes/Rest/Handler/Helper/HtmlOutputRendererHelper.php:577",
    "/var/www/html/mediawiki-1.41/includes/Rest/Handler/Helper/HtmlOutputRendererHelper.php:438",
    "/var/www/html/mediawiki-1.41/extensions/VisualEditor/includes/DirectParsoidClient.php:157",
    "/var/www/html/mediawiki-1.41/extensions/VisualEditor/includes/ApiParsoidTrait.php:110",
    "/var/www/html/mediawiki-1.41/extensions/VisualEditor/includes/ApiVisualEditor.php:231",
    "/var/www/html/mediawiki-1.41/includes/api/ApiMain.php:1931",
    "/var/www/html/mediawiki-1.41/includes/api/ApiMain.php:908",
    "/var/www/html/mediawiki-1.41/includes/api/ApiMain.php:879",
    "/var/www/html/mediawiki-1.41/api.php:95",
    "/var/www/html/mediawiki-1.41/api.php:48"
  ]
}

What should have happened instead?:

Obviously the VisualEditor should be entirely loaded.

Why did this exception occur?

  • In the extension Lingo, a Parser is created with MediaWikiServices::getInstance()->getParser() (code here)
  • It is checked on the Parser object if its output is empty with $parser->getOutput() === null (code here)
  • But since rMW6cf91bbb4c, the property Parser::$mOutput has the type ParserOutput (and not ?ParserOutput and some initial value), so before the property is initialised, its value is *uninitialized*. Previously, before the type hint, its value before initialisation with null. See also this StackOverflow.

How to solve it?

It may be changed Lingo to call Parser->resetParser(), but I find it would have more sense to define public ?ParserOutput mOutput = null; since it is legit and backward-compatible that there is no ParserOutput in some state of the Parser.

@Tacsipacsi: I add you as author of the MW commit.

Software version (skip for WMF-hosted wikis like Wikipedia):

Event Timeline

See also this small experiment with PHP 7.4.33:

$ php -a
Interactive mode enabled

php > class A { public $id; }
php > var_dump( new A );
php shell code:1:
class A#1 (1) {
  public $id =>
  NULL
}
php > var_dump( (new A)->id );
php shell code:1:
NULL

php > class B { public int $id; }
php > var_dump( new B );
php shell code:1:
class B#1 (1) {
  public int $id =>
  *uninitialized*
}
php > var_dump( (new B)->id );
PHP Warning:  Uncaught Error: Typed property B::$id must not be accessed before initialization in php shell code:1
Stack trace:
#0 {main}
  thrown in php shell code on line 1

As of now, $parser->getOutput() cannot be used with a fresh Parser because Parser::$mOutput is uninitialised.

A way to temporarily fix it may be:

  • just after a new parser is created with MediaWikiServices::getInstance()->getParser()
  • set the ParserOptions with $parser->setOptions( ParserOptions::newFromAnon() ) (or something else)
  • call $parser->resetParser()

But it is not very convenient in the general case when you have a Parser object and you don’t know if it was initialised with that, and you cannot call getOutput() to check because it would trigger an exception (or perhaps wrap it in a try-catch).

There’s already a patch to fix (but at the same time deprecate) this usage at https://gerrit.wikimedia.org/r/c/mediawiki/core/+/985035. Maybe you could weigh in about whether to deprecate on Gerrit.