Each extension or library is a package. And for Composer to be able to work with your package, it needs some information, such as the name of your package. This information goes into a file named composer.json, which you should put at the root of your package. You can read more on this in the Composer documentation.

Important to understand here is that packages are installed into MediaWiki, rather than MediaWiki and extensions being installed into another package. This means one should not list MediaWiki itself as a dependency of your package. Most existing extensions do not have any dependencies, so there is a good chance you just want to specify the minimum PHP version.

When using Composer to install your extension, users no longer have to manually include its entry point from their LocalSettings.php file. Composer takes care of all the autoloading. It does so based on the package definitions, which means you have to specify how to load your package in your composer.json file. This is done in the autoload section.

The simplest way to get this done for your typical MediaWiki extension is to use the “file” loading support. This will look as follows:

1

2

3

4

5

{

"autoload":{

"files":["MyExtension.php"]

}

}

In this example, MyExtension.php is the entry point of your extension. The path is relative to your package root.

Global scope assumptions

Unfortunately just specifying the entry point of your extension is unlikely to make the loading work already. The reason for this being that virtually all MediaWiki extensions assume their entry point gets included from global scope. They do so by accessing global variables (ie wgExtensionCredits or wgAutoloadClasses). This works in the typical MediaWiki use case because these entry points get included from LocalSettings.php, which itself is included from global scope. When this assumption is broken, as is the case when Composer loads your entry point, your code ends up breaking.

The fix for this is to make all references to globals explicit. You can change

I personally prefer the later approach, as it is much less error prone. There is some religion against using this style in the MediaWiki community though, so luckily going with the former approach works just as fine.

You can make these changes before making the other steps to support Composer. I highly recommend you to avoid global scope assumptions in any new code you write, if it is going to support Composer or not, as it is simply bad practice to not do so. Composer is not the only standard tool that will not work for you when this is done wrong, PHPUnit is another good example, though that is another story altogether.

It is clearly important to verify that you got rid of all global scope assumptions and did not miss one. And to notice if new ones get introduced, even while you might typically not be running your extension via a Composer install. This can be both achieved easily by making your code not execute in global scope, no matter how it is included. You can do this by means of self executing anonymous function. You can use this to wrap the code in your entry point file, or to wrap the inclusion statement for it in your LocalSettings file. The below example demonstrates the second approach, which is simpler, though clearly does not address the issue for other developers:

1

2

3

call_user_func(function(){

require_once(__DIR__."/extensions/MyExtension/MyExtension.php");

});

Specifying the package type

You might have been wondering where your extension gets put when it is installed.

By default Composer puts packages in a directory called “vendor”. This directory is created in the root of the package into which the Composer install is done. So when using MediaWiki 1.22 or later, this will be in the MediaWiki root. When using the ExtensionInstaller for older versions of MediaWiki, it will be in the root of the extension installer, at extensions/ExtensionInstaller/vendor.

The packages themselves are by default then put into vendor-name/package-name. So Semantic MediaWiki would, if it used the default behavior, end up in vendor/mediawiki/semantic-mediawiki.

For your typical PHP library this is the desired behavior. However for MediaWiki extensions we rather have them end up in in the extensions directory. For some extensions this might be a requirement, if they assume something about this path (which of course they ideally do not). Furthermore one wants Semantic MediaWiki to send up in extensions/SemanticMediaWiki, not in extensions/mediawiki/semantic-mediawiki. Both these behaviors can be achieved by specifying your package to be of type “mediawiki-extension”.

1

2

3

4

5

{

"name":"mediawiki/semantic-mediawiki",

"type":"mediawiki-extension",

// ...

}

Publish on Packagist

After you followed the above steps, your extension should be ready to be installed via Composer. Composer needs to be able to find your package so it can install it.

You can have your users specify the location of your source repository in their composer.json file. Read the repositories documentation for more info.

That approach is far from ideal though, as it requires more work from everyone installing your package, and exposes them to the location of your source code. This approach is mainly useful if you have no alternatives, or for getting started yourself with Composer without publishing your package.

The standard approach is to publish your package on the Packagist package repository. This is a public collection of packages, which Composer consults whenever one does an install. So once your package is on there, users are able to install it into MediaWiki with a single “composer require vendor-name/package-name” command. Have a look at Packagist and its documentation to get started with this.

Releasing versions

Unless users specify that they want to get a development version, Composer will only install stable releases for them. Creating a stable release is done by creating a git tag in semantic versioning format. For instance “1.4.2″. Once this is done, make sure the package on Packagist gets updated. If you have your code on GitHub, this can be done automatically.