Topic on Project:Support desk

1.39 Special Page extensions

8
2003:C2:3F21:FD00:D130:B302:B1E3:AC4 (talkcontribs)

Some of our own extensions are based on existing special pages. According to Mediawiki programming gudelines, the code looks like this:

class WantedPagesNeo extends WantedPagesPage {

        function __construct( $name = 'WantedPages_(PR_Neo)' ) {
                parent::__construct( $name );
        }

...

Prior to 1.39, this used to work but now it crashes:

[739319141d78cf5548ef724f] /wiki/Spezial:Spezialseiten TypeError: Argument 1 passed to WantedPagesPage::__construct() must implement interface Wikimedia\Rdbms\ILoadBalancer, string given, called in /var/www/test/mediawiki/extensions/PPaddons/src/SpecialPages/WantedPagesNeo.php on line 26

Backtrace:

from /var/www/test/mediawiki/includes/specials/SpecialWantedpages.php(39)
#0 /var/www/test/mediawiki/extensions/PPaddons/src/SpecialPages/WantedPagesNeo.php(26): WantedPagesPage->__construct()
#1 /var/www/test/mediawiki/vendor/wikimedia/object-factory/src/ObjectFactory.php(247): PP\SpecialPages\WantedPagesNeo->__construct()
#2 /var/www/test/mediawiki/vendor/wikimedia/object-factory/src/ObjectFactory.php(152): Wikimedia\ObjectFactory\ObjectFactory::getObjectFromSpec()
#3 /var/www/test/mediawiki/includes/specialpage/SpecialPageFactory.php(1279): Wikimedia\ObjectFactory\ObjectFactory->createObject()
#4 /var/www/test/mediawiki/includes/specialpage/SpecialPageFactory.php(1314): MediaWiki\SpecialPage\SpecialPageFactory->getPage()
#5 /var/www/test/mediawiki/includes/specials/SpecialSpecialpages.php(53): MediaWiki\SpecialPage\SpecialPageFactory->getUsablePages()
#6 /var/www/test/mediawiki/includes/specials/SpecialSpecialpages.php(42): SpecialSpecialpages->getPageGroups()
#7 /var/www/test/mediawiki/includes/specialpage/SpecialPage.php(701): SpecialSpecialpages->execute()
#8 /var/www/test/mediawiki/includes/specialpage/SpecialPageFactory.php(1428): SpecialPage->run()
#9 /var/www/test/mediawiki/includes/MediaWiki.php(316): MediaWiki\SpecialPage\SpecialPageFactory->executePath()
#10 /var/www/test/mediawiki/includes/MediaWiki.php(904): MediaWiki->performRequest()
#11 /var/www/test/mediawiki/includes/MediaWiki.php(562): MediaWiki->main()
#12 /var/www/test/mediawiki/index.php(50): MediaWiki->run()
#13 /var/www/test/mediawiki/index.php(46): wfIndexMain()
#14 {main}

What does it mean and where can I find documentation on how to correct it?

Jack Phoenix (talkcontribs)

This is a Dependency Injection (DI) related issue.

If you look at the source code of SpecialWantedpages.php as of REL1_39, you'll notice that the constructor's function signature has changed. These kind of changes are all over the place and will be in the future as well. Honestly, it's a mess.

Anyway, try changing your code to something like this, maybe:

	function __construct() {
		WantedQueryPage::__construct( 'WantedPages_(PR_Neo)' );
	}

This may or may not work (also, I should note that the use of parentheses in a special page name like that is quite unusual). Having access to the actual code would aid in debugging.

Someone who understand's MW's DI and its current state can probably chime in and explain it more and better.

Bawolff (talkcontribs)

You need to pass the correct arguments to parent::__construct(). This can change between versions.

You can adjust your special page registration in extension.json to match what WantedQuery does so it gets the same arguments, which you could just pass on. Alternatively you can use the methods of MediaWiki\MediaWikiServices::getInstance() to get the relavent objects.

> According to Mediawiki programming gudelines, the code looks like this

When you say guidelines, is there a specific page you are referencing? I'm asking because if there is incorrect information somewhere i would like to correct it.

TheDJ (talkcontribs)

Seems the signature was accidentally/deliberately changed/broken in REL1_36 https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/core/+/ab4d80512c2d5c6d028e885fd66c85f2e6096d14%5E%21/#F1. Probably because people assumed it was unused.

The documentation talks about a special page implementation subclassing the generic SpecialPage class, it is not a general example on how to do that for every single imaginable special page class. The best way to implement this tight now, is to, after calling the parent constructor, set $this->mName directly. This is how core classes like SpecialPrefixIndex.php do it.

2003:C2:3F21:FD00:682B:745E:5DA7:60C2 (talkcontribs)

@Jack Phoenix Thank you for the basic information. Unfortunately your suggested modification won't work. It results in "InvalidArgumentException: Provided specification is not an array"

Documentation in Manual:Special pages states "it generally means that MediaWiki could not find the class specified in extension.json that implements your special page" but in this case it is not true. If I replace the class's sourcecode by some other code, it works.

I'll try following the other hints supplied by Bawolff and TheDJ.

2003:C2:3F21:FD00:682B:745E:5DA7:60C2 (talkcontribs)

@Bawolff I'll try MediaWikiServices::getInstance() next and I'll report.

The referred extension was written by one of our former members, no longer available. The subclassing and constructor calls match the guidelines documented at Manual:Special pages:

Constructor signature is described underneath the topic SpecialPage.php.

I admit, this is the constructor used when subclassing from SpecialPage class. But, some paragraphs above "The special page" I read:

The body file (MyExtension/src/Special.php) should contain a subclass of SpecialPage or one of its subclasses. This file loads automatically when someone requests the special page. The example below implements the subclass SpecialMyExtension.

You need the __construct() constructor because its first parameter names your special page. 

I think the last sentence is outdated.

2003:C2:3F21:FD00:682B:745E:5DA7:60C2 (talkcontribs)

@TheDJ I think it was the proposition "subclass of SpecialPage or one of its subclasses" that lead to the subclassing of WantedPagesPage. This practice allows to make a simple query modification instead of copying all the code.

I'll try following your hints and then report again.

2003:C2:3F21:FD00:682B:745E:5DA7:60C2 (talkcontribs)
        function __construct( $name = 'PPwantedPagesNeo' ) {
                $services = MediaWikiServices::getInstance();
                parent::__construct( $services->getDBLoadBalancer(), $services->getLinkBatchFactory() );
                $this->mName = $name;
        }

The constructor works but now the db query hangs forever. Probably there are further modifications within WantedPagesPage that knock out this extension.

I'll give up here. I already spent 7-8 hours examining and that's not worth it.

Lesson learned ... never write own extensions, they'll be broken one day. Anyway, thanks for your patience.

Reply to "1.39 Special Page extensions"