Developing extensions

From semantic-mediawiki.org

Extensions maintained by the project try follow some simple guidelines, also to make maintenance easier with a common infrastructure and setup:

  • Use Composer as deployment tool
  • Loaded via MediaWiki's extension.json
  • Use Make and Docker to support both, local testing and GitHub Actions continuous integration

Guidelines and conventions

Some general guidelines are:

  • An extension specifies its dependency to ensure it is tested and usable for the Semantic MediaWiki release it was intended for by maintaining a composer.json
  • Use PSR-4 and a PHP namespace to distinguish a codebase from other repositories that may use similar (or even the same) class/interface names.
  • Code quality should be considered when writing an extension in order for fellow developers to be able to understand, read, and review the code.
  • See also the coding conventions defined by Semantic MediaWiki
  • Deploy and write tests as part of the extension

Getting started ...

The following extensions can be used as inspiration on "How to write an extension" in connection with Semantic MediaWiki.

Technical notes

Files and folders

It has been good practice to code around the following files and folders structure:

docs/
i18n/
src/
tests/

Continuous integration


This section is outdated. Most SMW extensions use Make, Docker and Docker-Compose to run tests both, locally and on GitHub. Coverage is repored on Codecov.


Using Travis-CI is fairly easy to set up and integrable with Semantic MediaWiki, the best approach is to select an existing repository and copy files such as:

  • .travis.yml
  • tests/travis folder and adapt the necessary references
  • When you host your extension with GitHub remember to register your Travis-CI either as App or via the webhook (see the documentation from the provider)
  • phpunit.xml.dist and the tests/bootstrap.php and make necessary changes

composer.json

    "require": {
        "php": ">=5.6.0",
        "composer/installers": "1.*,>=1.0.1",
        "mediawiki/semantic-media-wiki": "~3.0"
    },
    "extra": {
        "branch-alias": {
            "dev-master": "0.1.x-dev"
        }
    },
    "autoload": {
        "files" : [
            "Foo.php"
        ],
        "psr-4": {
            "Foo\\": "src/"
        }
    }

extension.json

{
    "name": "Foo",
    "version": "0.1-alpha",
    "author": [
        "Foo",
        "..."
    ],
    "descriptionmsg": "foo-desc",
    "namemsg": "foo-name",
    "license-name": "GPL-2.0-or-later",
    "type": "foo",
    "requires": {
        "MediaWiki": ">= 1.30"
    },
    "MessagesDirs": {
        "Foo": [
            "i18n"
        ]
    },
    "callback": "Foo::initExtension",
    "ExtensionFunctions": [
        "Foo::onExtensionFunction"
    ],
    "load_composer_autoloader":true,
    "manifest_version": 1
}

Foo.php


/**
 * Extension ...
 *
 * @defgroup Foo Foo
 */
Foo::load();

/**
 * @codeCoverageIgnore
 */
class Foo {

    /**
     * @note It is expected that this function is loaded before LocalSettings.php
     * to ensure that settings and global functions are available by the time
     * the extension is activated.
     */
    public static function load() {
        if ( is_readable( __DIR__ . '/vendor/autoload.php' ) ) {
            include_once __DIR__ . '/vendor/autoload.php';
        }
    }

    /**
     * @see https://www.mediawiki.org/wiki/Manual:Extension.json/Schema#callback
     */
    public static function initExtension( $credits = [] ) {
        define( 'FOO_VERSION', isset( $credits['version'] ) ? $credits['version'] : 'UNKNOWN' );
    }

    /**
     * @since 1.0
     */
    public static function onExtensionFunction() {

        if ( !defined( 'SMW_VERSION' ) ) {
            if ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' ) {
                die( "\nThe 'Foo' extension requires the 'Semantic MediaWiki' extension to be installed and enabled.\n" );
            } else {
                die( 'Error: The Foo extension requires the Semantic MediaWiki extension to be installed and enabled.' );
            }
        }

        // Do call the setup code
        // $hooks = new Hooks();
        // $hooks->register();
    }

}


See also[edit]