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.
SemanticMetaTags
SemanticExtraSpecialProperties
SemanticBreadcrumbLinks
SemanticScribunto
SemanticGlossary
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 thetests/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(); } }