Continuous integration/Tutorials/Add phan to a MediaWiki extension

From mediawiki.org

This is a tutorial to set up phan for a MediaWiki extension. We'll look at the ParserMigration extension specifically.

Setup[edit]

You'll need to install PHP 7+, and the ast extension. On Debian/Ubuntu sudo apt-get install php-cli php-ast should do it. phan has some other advice for this part too.

Preparing your extension[edit]

Your extension must follow the best practices file structure, since the default phan configuration will only look in those directories for PHP code.

Adding phan configuration[edit]

I'm going to assume that you installed MediaWiki core to ~/projects/mediawiki and ParserMigration to ~/projects/mediawiki/extensions/ParserMigration. Make sure you've fully set up MediaWiki core and installed all the composer dependencies.

First, let's set up the default configuration:

$ cd ~/projects/mediawiki/extensions/ParserMigration
$ composer require --dev mediawiki/mediawiki-phan-config:0.10.6
$ mkdir -p .phan
$ echo "<?php" >> .phan/config.php
$ echo "return require __DIR__ . '/../vendor/mediawiki/mediawiki-phan-config/src/config.php';" >> .phan/config.php

We're now ready to try to run phan:

$ cd ~/projects/mediawiki/extensions/ParserMigration
$ vendor/bin/phan -d . --long-progress-bar

If MediaWiki core isn't two levels above your current directory, you can explicitly declare the install path:

$ MW_INSTALL_PATH=/path/to/mw/core vendor/bin/phan -d . --long-progress-bar

You should see a progress bar, and it might take a minute to finish running. It looks like we have some errors:

includes/Hooks.php:11 PhanUndeclaredTypeParameter Parameter of undeclared type \MediaWiki\ParserMigration\User
includes/MigrationEditPage.php:31 PhanDeprecatedProperty Reference to deprecated property \MediaWiki\ParserMigration\MigrationEditPage::mTitle defined at ./../../includes/EditPage.php:230
includes/MigrationEditPage.php:34 PhanDeprecatedProperty Reference to deprecated property \MediaWiki\ParserMigration\MigrationEditPage::mTitle defined at ./../../includes/EditPage.php:230

Looking at the first one, the namespace for User on the Hooks::onGetPreferences() documentation comment is wrong. Let's fix it (example).

For the second one, EditPage::$mTitle is marked as deprecated for public use, to it's OK that we're using it in MigrationEditPage. You can suppress that warning on each particular line (phan docs) or you can suppress it throughout the extension. Let's suppress that warning for now by adding it to suppress_issue_types in config.php.

We can do something like:

<?php
$cfg = require __DIR__ . '/../vendor/mediawiki/mediawiki-phan-config/src/config.php';

$cfg['suppress_issue_types'] = array_merge( $cfg['suppress_issue_types'], [
	// MigrationEditPage::$mTitle is a false-positive
	'PhanDeprecatedProperty',
] );

return $cfg;

Note that you'll usually want to suppress issues on particular lines, as suppressing them for the whole extension may hide bugs.

For whatever extension you're working on, there will likely be other errors. You'll have to use some judgement on whether the issue needs fixing or can be suppressed.

Let's run phan again with the same command, and there should be no errors left! You can commit your work and submit it to Gerrit.

On Gerrit, CI will automatically run phan as part of the CI system to make sure it will pass. Once your patch lands, it will be enabled for all subsequent patches.

Advanced[edit]

If your extension depends upon other extensions, you can add those extensions to directory_list and exclude_analysis_directory_list. Here's an example:

$cfg['directory_list'] = array_merge(
	$cfg['directory_list'],
	[
		'../../extensions/CheckUser',
	]
);

$cfg['exclude_analysis_directory_list'] = array_merge(
	$cfg['exclude_analysis_directory_list'],
	[
		'../../extensions/CheckUser',
	]
);

If the extension runs a CI (Continuos Integration) test, make sure you also add the extension to parameter_functions.py in the phan_dependencies list.

Stubs[edit]

If you don't want to require having the dependency checked out while running phan, you can also use stubs. Stubs are a copy of the class/function structure, with any necessary documentation blocks, but none of the implementation code. You can see an example of this in the MassMessage extension. The downside to using stubs is that if the dependency changes, you also need to update the stub for phan to know of the change.

Stub files should be placed in .phan/stubs/.

The upstream documentation about stubs may also be useful.