Manual:コーディング規約

This page is a translated version of the page Manual:Coding conventions and the translation is 91% complete.

このページでは、MediaWiki コードベース内や、ウィキメディアのウェブサイト群で使用されることを意図している拡張機能内で使用される、コーディング規約を説明します。 コードレビューワーは、これら規約に従わない変更をまとめて-1処理することがあります。その場合は、スタイルの問題を修正してパッチを更新するように推奨されたと解釈するようにお願いします。

このページでは、コードがどの言語で書かれているかにかかわらずすべての MediaWiki コードに適用される全般的な規約を列挙します。 MediaWiki の特定のコンポーネントやファイルの種類に適用されるガイドラインについては、以下を参照してください:

wiki技術部 (少なくとも運営/パペットに適用):

コードの構造

ファイルの整形

タブの幅

各行は深さ1段ごとにタブ記号1個でインデントを施します。タブ1個が何文字相当か自分で決めることはできません。MediaWikiの開発者の多くは可読性の面からもタブ1個を半角アキ4個相当が妥当だとしていますが、多くのシステムでは半角アキ8個相当の設定で、なかには半角アキ2個のつもりで使用する開発者もいます。

テキストエディタのvim利用者には $HOME/.vimrc に以下の記述を追加して設定する方法があります。

autocmd Filetype php setlocal ts=4 sw=4

CSSやHTML、JavaScriptにも似た記述を使います。

しかしながらPythonでは、コードのスタイルガイドPEP 8の空白スペース (アキ) ガイドラインに従い、タブではなくアキを使用するように推奨しています。

改行

どのファイルも改行はUnix式にします (LF記号1個であって、CR+LFではない)。

  • Windows版のgitでは (既定として) コミットの段階で自動的にCR+LF改行をLFに置換します。

すべてのファイルについて、末尾に改行文字を付けるべきです。

  • その他の行はすべて、行末に改行記号があるため整合性があります。
  • これにより非バイナリ形式 (差分など) でのデータ取り回しが楽になります。
  • catwcなどコマンドラインツールで欠如するとファイル処理が円滑に進みません (あるいは少なくとも予期したりこうあるべきと思う挙動になりません)。

エンコーディング

テキストファイルはすべてBOMのない、UTF-8で符号化。

ファイル編集には、必ず BOM を挿入する Microsoft メモ帳は使用できません。 ファイル先頭に特殊文字 BOM が入ることによって、ウェブ ブラウザーがそれをクライアントに出力するため、PHP ファイルの動作が阻害されます。

まとめるなら、必ずUTF-8対応でBOMを挿入しないエディタをご利用ください。

末尾の空白類

IDEを使用すると「Home」と「End」のキー (その他のキーボードショートカットも含む) を押すと、想定どおり、通常は行末の空白スペースを無視してコード末尾にジャンプします。ところが非IDEテキストエディタの場合は「End」キーを押すと行末の空白スペースの末尾に飛んでしまい、開発者は入力しようとして想定した位置まで、バックスペースで戻る必要があります。

操作としては、行末の空白スペースの削除はほとんどのテキストエディタで簡単です。開発者は、他の可視コードを含む行では特に、行の末尾に空白を入れないでください。

処理を簡略化するツールがあります。

  • nano: GNU nano 3.2;
  • Komodo Edit : メニューの「Edit > Preferences」を開きSave Optionsで「Clean trailing whitespace and EOL markers」ならびに「Only clean changed lines」を有効にする;
  • Kate: オプション「Highlight trailing spaces」を有効にすると、行末の空白スペースを表示する。「Settings > Configure Kate > Appearance」から設定。また「Settings > Configure Kate > Open/Save」の設定で、保存時に行末の空白スペース削除を自動処理できる。
  • vim: さまざまな自動クリーンナッププラグインを備える。
  • Sublime Text: TrailingSpace処理用プラグイン (行末の空白スペースを強調表示、削除)

キーワード

キーワード (例 require_oncerequire) に不要な括弧を付けない。

インデントと配置

全般的なスタイル

MediaWikiのインデントの形式は、いわゆる字下げスタイルの1TBS あるいは OTBSと同様です。波括弧は関数、条件文やループなどの開始と同じ行に置きます。else/elseifは直前の「閉じる」波括弧と同じ行に置きます。

function wfTimestampOrNull( $outputtype = TS_UNIX, $ts = null ) {
	if ( $ts === null ) {
		return null;
	} else {
		return wfTimestamp( $outputtype, $ts );
	}
}

複数行にわたる文の場合、2行目以降の行を1段深くインデントして記述します。

インデントと改行を使い、コードの論理的構造を明示します。インデントの深さを変え入れ子にした複数の波括弧あるいは同様の構造にすると、入れ子構造ごとに新しいインデントを作る場合があります。

$wgAutopromote = [
	'autoconfirmed' => [ '&',
		[ APCOND_EDITCOUNT, &$wgAutoConfirmCount ],
		[ APCOND_AGE, &$wgAutoConfirmAge ],
	],
];

垂直方向の位置合わせ

垂直方向の配置の使用は避けます。新しい項目を追加するたび、左カラムに適用する幅を増やす必要があり、差分表示が読み取りにくくなります。

差分表示ツールの多くには空白スペースの変更を無視するオプションがあります。
Git: git diff -w

必要に応じて、行中心で垂直位置合わせをするには、タブではなく空白スペースを使います。例はこうなります。

$namespaceNames = [
	NS_MEDIA            => 'Media',
	NS_SPECIAL          => 'Special',
	NS_MAIN             => '',
];

これは空白スペースが半角中グロ「・」に変換され、下記の表示になります。

$namespaceNames·=·[
 →  NS_MEDIA············=>·'Media',
 →  NS_SPECIAL··········=>·'Special',
 →  NS_MAIN·············=>·'',
];

(もしvimでアドオンtabular vim add-onを使用した場合、:Tabularize /=と入力すると「=」符号の場所で位置合わせします。)

行の幅

1 行は 80 桁から 100 桁の範囲で改行文字で改行します。これにはいくつかのまれな例外があります。多数のパラメーターを取る関数も例外なく改行が必要です。 折り返しを OFF にしたときにコードが画面外にはみ出さないようにするための工夫です。

2 つの行を区切る演算子は、一貫して配置する必要があります (常に行の末尾または先頭に)。個々の言語には、より具体的な規則がある場合があります。

return strtolower( $val ) === 'on'
	|| strtolower( $val ) === 'true'
	|| strtolower( $val ) === 'yes'
	|| preg_match( '/^\s*[+-]?0*[1-9]/', $val );
$foo->dobar(
	Xml::fieldset( wfMessage( 'importinterwiki' )->text() ) .
		Xml::openElement( 'form', [ 'method' => 'post', 'action' => $action, 'id' => 'mw-import-interwiki-form' ] ) .
		wfMessage( 'import-interwiki-text' )->parse() .
		Xml::hidden( 'action', 'submit' ) .
		Xml::hidden( 'source', 'interwiki' ) .
		Xml::hidden( 'editToken', $wgUser->editToken() ),
	'secondArgument'
);

メソッド演算子は、常に次の行の先頭に配置する必要があります。

$this->getMockBuilder( Message::class )->setMethods( [ 'fetchMessage' ] )
	->disableOriginalConstructor()
	->getMock();

「if」文を続ける場合は、オールマンのスタイルの括弧に変更することで、条件と本文の区切りを明確にできます。

if ( $.inArray( mw.config.get( 'wgNamespaceNumber' ), whitelistedNamespaces ) !== -1 &&
	mw.config.get( 'wgArticleId' ) > 0 &&
	( mw.config.get( 'wgAction' ) == 'view' || mw.config.get( 'wgAction' ) == 'purge' ) &&
	mw.util.getParamValue( 'redirect' ) !== 'no' &&
	mw.util.getParamValue( 'printable' ) !== 'yes'
) {
	
}

条件部分のインデントの深さについては意見が分かれます。本文と異なるインデントの深さにすることで、条件部分が本文ではないことがより明確になりますが、これは普遍的なことではありません。

条件文の継続や非常に長い式は、どのようにしても醜くなりがちです。そこで、一時的な変数を使って分割したほうがいい場合があります。

波括弧で囲まない制御構造

「ブロック」は1行に記述しない。左マージンから読者が探すはずの重要な文を、遠い位置に動かしてしまう結果になるためです。コードを短くしても簡略化したことになりません。コーディングの形式はヒトとのコミュニケーションを効率化することにあり、狭い空間にコンピュータ可読の文を押し込むことではありません。

// No:
if ( $done ) return;

// No:
if ( $done ) { return; }

// Yes:
if ( $done ) {
	return;
}

この方法は開発者が「スマートなインデント」機能のないテキストエディタを使用している場合に特に発生しがちな、一般的な論理エラーを回避します。1行だったブロックを後に2行に拡張した場合、このエラーが発生します。

if ( $done )
	return;

上記に後で次の変更を加えた場合。

if ( $done )
	$this->cleanup();
	return;

これにより微妙なバグが発生する可能性があります。

Emacs方式

Emacs では、nXHTML モードに代わって php-mode.el を使用し、.emacs ファイルに MediaWiki マイナーモードを設定できます:

(defconst mw-style
  '((indent-tabs-mode . t)
    (tab-width . 4)
    (c-basic-offset . 4)
    (c-offsets-alist . ((case-label . +)
                        (arglist-cont-nonempty . +)
                        (arglist-close . 0)
                        (cpp-macro . (lambda(x) (cdr x)))
                        (comment-intro . 0)))
    (c-hanging-braces-alist
        (defun-open after)
        (block-open after)
        (defun-close))))

(c-add-style "MediaWiki" mw-style)

(define-minor-mode mah/mw-mode
  "tweak style for mediawiki"
  nil " MW" nil
  (delete-trailing-whitespace)
  (tabify (point-min) (point-max))
  (subword-mode 1)) ;; If this gives an error, try (c-subword-mode 1)), which is the earlier name for it

;; Add other sniffers as needed
(defun mah/sniff-php-style (filename)
  "Given a filename, provide a cons cell of
   (style-name . function)
where style-name is the style to use and function
sets the minor-mode"
  (cond ((string-match "/\\(mw[^/]*\\|mediawiki\\)/"
                       filename)
         (cons "MediaWiki" 'mah/mw-mode))
        (t
         (cons "cc-mode" (lambda (n) t)))))

(add-hook 'php-mode-hook (lambda () (let ((ans (when (buffer-file-name)
                                                 (mah/sniff-php-style (buffer-file-name)))))
                                      (c-set-style (car ans))
                                      (funcall (cdr ans) 1))))

上記の mah/sniff-php-style 関数はパスを確認し、php-mode を呼び出したときに「mw」または「mediawiki」を含んでいるかどうか見極めると、バッファを設定してMediaWikiのソースの編集に mw-mode マイナーモードを使用させます。 バッファが mw-mode を使用したことは、モードラインに「PHP MW」や「PHP/lw MW」のようなものが表示されると判断できます。

データ操作

URL の構築

文字列の連結などの方法で手動でURLを構築しないでください。リクエストをコードが行う場合 ( 特にPOSTとバックグラウンドリクエスト) には必ず完全なURL形式を使用します。

PHPで使用できる方式は適切なLinker またはTitle 方式、ウィキテキストではfullurl というマジックワード、JavaScriptを使うならmw.util.getUrl()メソッド、さらに他の言語の同様の方式です。 予期せぬ短いURL構成などの問題を避けることができます。

ファイルの命名

サーバー側のコードを含むファイルには要素語の最初を大文字で書き表すアッパーキャメルケース (UpperCamelCase) で命名します。 拡張機能の命名規約もこの方式です。[1] ファイルの名前は、それが含んでいる最も重要なクラスに基づいて付けます。ほとんどのファイルは、1 つのクラスだけを含むか、基本クラスとその派生クラスのいくつかを含むでしょう。 例えば、Title.phpTitle クラスのみを含みます。WebRequest.php には WebRequest クラスが含まれており、さらにその派生クラスである FauxRequestDerivativeRequest も含まれています。

アクセスポイントのファイル

SQLなど「access point」ファイルに命名し、PHPエントリポイントの名前には index.phpfoobar.sqlなどのように小文字表記に限定しています。メンテナンスのスクリプトは一般的に小文字のキャメルケースで記述しますが、必ずしもこの限りではありません。サイト管理者が使うファイルでreadme、licensesやchangelogsなどは大文字表記です。

ファイルやディレクトリの名称に空白スペースあるいは非ASCII文字を使わないでください。小文字表記の題名はアンダースコア (_) ではなくハイフン (-) を使うようにします。

JS、CSSとメディアの命名

For JavaScript, CSS and other frontend files (usually registered via ResourceLoader) should be placed in directory named after the module bundle in which they are registered. 例えば、モジュール mediawiki.foo のファイルには mediawiki.foo/Bar.jsmediawiki.foo/baz.css があります

クラスを定義する JavaScript ファイルは、定義するクラス名と正確に一致させる必要があります。 クラス TitleWidget は、ファイル名 TitleWidget.js、または末尾が TitleWidget.js である必要があります。 This allows for rapid navigation in text editors by navigating to files named after a selected class name (such as "Goto Anything [P]" in Sublime, or "Find File [P]" in Atom).

大規模なプロジェクトでは、ファイルを整理する方法を追加しなければ、重複したり曖昧になったりするような名前のクラスが階層化されることがあります。 We generally approach this with subdirectories like ext.foo/bar/TitleWidget.js (for Package files), or longer class and file names like mw.foo.bar.TitleWidget in ext.foo/bar.TitleWidget.js.

Modules bundles registered by extensions should follow names like ext.myExtension, for example MyExtension/modules/ext.myExtension/index.js. This makes it easy to get started with working on a module in a text editor, by directly finding the source code files from only the public module name (T193826).

説明文書

言語に特化した下位ページには、ファイル内のコードコメントの構文そのものの情報が、例えばdoxygenに対してはcomments in PHPのコメントに含まれています。 正確な構文を使用すると、doc.wikimedia.org のソースコードから説明文書の生成ができます。

高レベルの概念、下位システム、データ フローは、/docs フォルダー内で文書化する必要があります。

ソースファイルのヘッダー

ほとんどのライセンスに準拠するには、各ソースファイルの先頭に次に類する記述(GPLv2 PHPアプリケーションに固有のもの)が必要です。

<?php
/**
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 * http://www.gnu.org/copyleft/gpl.html
 * 
 * @file
 */

ライセンス

一般的にライセンスはSPDX標準に従ってフルネームまたは頭字語を照合します。Manual:$wgExtensionCredits#ライセンスも参照してください。

動的識別子

It is generally recommended to avoid dynamically constructing identifiers such as interface message keys, CSS class names, or file names. When possible, write them out and select between them (e.g. using a conditional, ternary, or switch). This improves code stabilty and developer productivity through: easier code review, higher confidence during debugging, usage discovery, git-grep, Codesearch, etc.

If code is considered to be a better reflection of the logical structure, or if required to be fully variable, then you may concatenate the identifier with a variable instead. In that case, you must leave a comment nearby with the possible (or most common) values to demonstrate behaviour and to aid search and discovery.

関連項目:

// No: Avoid composing message keys
$context->msg( 'templatesused-' . ( $section ? 'section' : 'page' ) );
// Yes: Prefer full message keys
$context->msg( $section ? 'templatesused-section' : 'templatesused-page' );
// If needed, concatenate and write explicit references in a comment

// Messages:
// * myextension-connect-success
// * myextension-connect-warning
// * myextension-connect-error
var text = mw.msg( 'myextension-connect-' + status );
// The following classes are used here:
// * mw-editfont-monospace
// * mw-editfont-sans-serif
// * mw-editfont-serif
$texarea.addClass( 'mw-editfont-' + mw.user.options.get( 'editfont' ) );
// Load example/foo.json, or example/foo.php
$thing->load( "$path/foo.$ext" );

リリースノート

ウィキ利用者及びサーバー管理者や管理者または拡張機能の作者に影響を与える可能性があるコアソフトウェアへの重要な変更はすべて (すべてのバグ修正レポートを含めて) RELEASE-NOTES-N.NN ファイルに記録します。 RELEASE-NOTES-1.43 は開発中です。過去のリリース ノートはリリースごとに毎回、HISTORY ファイルに移動され、リリースノートは新たに作成されます。 一般に RELEASE-NOTES-N.NN は次の3つの部分から構成されています:

  • 設定変更とは対象を閲覧して「これは自分のウィキにふさわしいか?」と決定するサーバ管理者を必要とするような、許容された既定の動作への変更に加え、後方互換性のない変更またはその他を置いておく場所です。必要に応じて以前の機能を復元する方法の簡単な説明を含めるようにしてください。
  • バグ修正とは、問題のある、または望ましくないと認識される動作に対し、これらを修正する変更を書き留める場所です。これらの問題はしばしばPhabricator で報告されますが、必ずしもそうとは限りません。
  • 新機能とは字で書いたとおり、新しく追加された機能の記録です。

追加として、特定の構成要素 (例: 操作API) や、上記のカテゴリ以外の変更を含めるセクションが存在する場合があります。

どの場合も、変更がPhabricatorに報告された1つ以上の問題に対応する場合は、エントリの先頭にタスクIDを含めます。新しいエントリはセクションの終わりに時系列順に追加します。

システムメッセージ

新しいシステムメッセージを作成するときは、ファイル名の大文字小文字表記はキャメルケースあるいはsnake_case (アンダースコア区切り) ではなく、可能な限りハイフン区切り (-) を使用してください。 So for example, some-new-message is a good name, while someNewMessage and some_new_message are not.

もし当該のメッセージを直後に半角コロン (:) を伴うラベルとして使用する場合は、コロンはハードコードしないで、それもメッセージ文内に記述します。コロンの扱いは言語に依存し (例えばフランス語ではコロンの前に半角アキが必要) 、そのため、コロンをハードコードしてしまうと地域化ができません。他のいくつかのタイプの約物 (interpunctuation) にも同じことが言えます

メッセージキーは即席で作るのではなく、コード内で「省略せず」使います。これによりコードベースで検索するのが容易になります。たとえば下記の例では templatesused-section の検索で省略したメッセージ・キーを使用した場合、このようなメッセージ・キーが検出されない点を示しています。

// No:
return wfMessage( 'templatesused-' . ( $section ? 'section' : 'page' ) );

// Yes:
$msgKey = $section ? 'templatesused-section' : 'templatesused-page';
return wfMessage( $msgKey );

メッセージを即席に作成しなければならないと思う場合は、コメントを付けて、付近に記述され当てはまりそうなメッセージをすべて記入しておきます。

// Messages that can be used here:
// * myextension-connection-success
// * myextension-connection-warning
// * myextension-connection-error
$text = wfMessage( 'myextension-connection-' . $status )->parse();

メッセージキーの作成、使用、文書化および保守に関する規則の詳細については地域化を参照してください。

好ましい綴り

UIに統一性をもたせるには、UIとコードベースで用語のスペルがぶれないようにすることも重要です。開発史上の経緯により、英語のメッセージやコメント、説明文書では「アメリカ英語」を優先します。

メッセージキーの短縮形

ph
プレースホルダー (placeholder: 入力欄内のテキスト)
tip
ツールチップ テキスト (tooltip text)
tog-xx
利用者の個人設定内のトグル (toggle) オプション (訳注: チェックボックス)

句点

タイトル以外のエラーメッセージは文として扱われ、句読点が必要です。

Improve the core

If you need some additional functionality from a MediaWiki core component (PHP class, JS module etc.), or you need a function that does something similar but slightly different, prefer to improve the core component. Avoid duplicating the code to an extension or elsewhere in core and modifying it there.

リファクタリング

修正を加えるたびにコードのリファクタを実行:修正のだびにコードの悪化を蓄積しないでください。

ただし、リファクタリングが大量に及ぶ場合は、コミットを分割して行います。構築ガイドArchitecture guidelines (草案) も参照してください。

HTML

MediaWiki HTTP は、2 つのソースのうちのいずれかによって生成される HTML 出力を返します。 MediaWiki の PHP コードはユーザー インターフェイスの信頼できるソースであり、任意の HTML を出力できます。 パーサーは利用者が生成したウィキテキストを HTML に変換しますが、これは信頼できないソースです。 利用者がウィキテキストを介して作成した複雑な HTML は、多くの場合、「テンプレート」名前空間にあります。 パーサーが生成した HTML は、出力前にサニタイズされます。

ほとんどの data-* 属性は、利用者がウィキテキストとテンプレートで使用できるようにしてあります。ただし、以下の接頭辞は制限されており、ウィキテキストでは使用できないため、出力 HTML から除去されます。 この方法でクライアント JavaScript コードから、DOM 要素が信頼できるソース由来かどうか判断できます:

  • data-ooui – この属性は OOUI ウィジェットが生成し、HTML 内に現れます。
  • data-parsoid – Parsoid の内部使用のために予約された属性。
  • data-mw および data-mw-...reserved attribute for internal use by MediaWiki core, skins and extensions. The data-mw attribute is used by Parsoid; other core code should use data-mw-*.

JavaScriptの要素を選択するとき属性のキー/値を特定すると、意図する信頼できるソース由来のDOM要素に限定して対象とするように設定できます。 サンプル: 正式な差分表示のために'wikipage.diff'フックに限定して起動します

注記

外部リンク