外部に開発依頼する現場で納品後の品質にバラつきが発生することはよくあることですよね。
一定品質を確保する上でコーディング規約はやっぱり大切だと実感している今日この頃。
今回はコーディング規約を決める際に必要なことと、phpのコーディング規約を改めて考えてみました。
コーディング規約を決めるのに必要なことは?
プロジェクトの規模やメンバーのスキル、使用言語によって変わってきますが、
あらかじめ以下の定義をしておくことが重要になります。
スコープ(適用する範囲)
内部で使うだけか、外部依頼時にも使う規約かのスコープを決めておく。
コードレビュアー選任と周知
コーディング規約を設定しただけでは品質や生産性は上がりません。例えばプロジェクトチームに、不慣れなメンバーや新人が居た場合に、経験者が周知、レビューする必要性があります。全て一人でコードレビューはできないので、ある程度の経験者を選任し、同意を得ましょう。
品質保持の手段
CIツールで自動で規約チェックしてくれるような仕掛けも重要になってきます。
例: PHPの場合
PHP コードのコーディング規約をチェックすることにした
メンテナンスルールの設定
プロジェクト、メンバーに応じて規約のメンテナンスを行う必要があります。不具合などの振り返り共にコーディング規約の見直しを実施したり、予め知識を深める上で品質管理の担当などを組織内で予め立てておくと良いでしょう。
PHPでのコーディング規約
今回はPHPでオーソドックスな規約であるPSRのうちPSR1、PSR2をベースにサンプルを作ってみました。
外部に仕事をお願いする際に明示することで、納品後のトラブルを予防することができます。
インデントについて
スペース4つ、タブは禁止
スペースのみにすることで、差分表示やパッチ、履歴や注釈がずれる問題を回避できます。
改行コードについて
LF
開始タグについて
<?php
<?=
終了タグについて
?>は省略
文字コードについて
UTF-8(BOM無し)
コメントについて
// コメントは開始後スペースを1文字開けます
プログラム全体、クラスのコメントについて
/**
* ××を実現するクラス // 説明
* // return値、型
* @return string JSON
*/
行の長さ
ハードリミットはなし
ソフトリミットは120文字上限
行末
行末の空白文字列は禁止
ファイル末
ファイルの最後に空行を入れる
予約語、true、false、nullについて
大文字禁止、小文字のみ
クラス名について
StudlyCaps(単語の先頭文字を大文字で表記する記法)記法
メソッド名について
camelCase記法
require、include、ini、エラーや例外発行、グローバル変数や静的変数修正、ファイルからの読み込み・書き込み処理について
<?php
// 同じファイル内でfunctionを定義すると振る舞いが変わってしまう可能性があるのでファイル単位で分ける。
ini_set('error_reporting', E_ALL);
include "file.php";
echo "<html>\n";
// NG
function foo()
{
}
namaspaceについて
<?php
// namespaceの後は一行改行する
namespace Vendor\Model;
class Foo
{
}
定数について
<?php
namespace Vendor\Model;
class Foo
{
// アンダースコア文字を区切り文字として大文字で定義する
const VERSION = '1.0';
const DATE_APPROVED = '2012-06-01';
}
名前空間とuse演算子による定義について
<?php
// use定義は名前空間宣言の後でなければなりません。
// 名前空間の定義の後に空行が必要です。
namespace Vendor\Package;
use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;
// use定義のブロックの後には空行が必要です。
extendsとimplementsについて
<?php
namespace Vendor\Package;
use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;
// クラス名と同じ行で定義する。
class ClassName extends ParentClass implements \ArrayAccess, \Countable
{
}
implementsについて
<?php
namespace Vendor\Package;
use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;
// インデントにより揃えることで、複数行に分割しても構いません。
class ClassName extends ParentClass implements
\ArrayAccess,
\Countable,
\Serializable
{
}
アクセス修飾子について
<?php
namespace Vendor\Package;
class ClassName
{
// プロパティ定義にvarは禁止
// ステートメントあたりプロパティ定義は一つ
// プロパティ名にprotectedまたはprivateを示すためのアンダースコアは禁止
public $foo = null;
}
メソッドについて
<?php
namespace Vendor\Package;
class ClassName
{
// アクセス修飾子は、全てのメソッドに定義しなければなりません。
// protectedまたはprivateを示すためのアンダースコアは禁止
public function fooBarBaz($arg1, &$arg2, $arg3 = [])
{
}
}
メソッドの引数について
<?php
namespace Vendor\Package;
class ClassName
{
// 各カンマの後ろには1スペース
// デフォルト値を持つ引数は、引数リストの最後に
public function foo($arg1, &$arg2, $arg3 = [])
{
}
}
複数の引数はについて
<?php
namespace Vendor\Package;
class ClassName
{
// インデントにより揃え、複数行に分割
public function aVeryLongMethodName(
ClassTypeHint $arg1,
&$arg2,
array $arg3 = []
) {
}
}
abstract、 final、 and staticについて
<?php
namespace Vendor\Package;
abstract class ClassName
{
protected static $foo;
// abstractとfinalはアクセス修飾子の前に
abstract protected function zim();
// staticはアクセス修飾子の後に定義
final public static function bar()
{
}
}
メソッド、関数の呼び出しについて
<?php
bar();
$foo->bar($arg1);
Foo::bar($arg2, $arg3); // 各カンマの後に1スペースを入れる
メソッド、関数の複数引数について
<?php
// インデントにより揃え、複数行に分割する。
$foo->bar(
$longArgument,
$longerArgument,
$muchLongerArgument
);
if, elseif, else文について
<?php
if ($expr1) {
// eleseifは間にスペースを空けない
} elseif ($expr2) {
} else {
}
switch, case文について
<?php
switch ($expr) {
case 0:
echo 'First case, with a break';
break;
// no breakを明示的にコメントする
case 1:
echo 'Second case, which falls through';
// no break
case 2:
case 3:
case 4:
echo 'Third case, return instead of break';
return;
default:
echo 'Default case';
break;
}
while、 do while文
<?php
while ($expr) {
}
do while文
<?php
do {
} while ($expr);
for文
<?php
for ($i = 0; $i < 10; $i++) {
}
foreach文
<?php
foreach ($iterable as $key => $value) {
}
try〜catch文
<?php
// 更新用メソッド
public function update($data) {
$results = array();
DB:beginTransaction();
// 例外が発生するかもしれない処理
try {
$user = User::find($data['id']);
if($user->updated_at > $data['updated_at']) {
throw new Exception('exception');
}
$user->name = $data['name'];
$user->email = $data['email'];
$user->save();
DB::commit();
$results[] = true;
// 例外が発生したときの対処を書き
} catch (Exception $e) {
DB::rollback();
$results[] = false;
$results['message'] = $e->getMessage();
// 例外が起きても起きなくても実行される処理
} finally {
return $results;
}
}
まとめ
PSR1、PSR2をベースに基本的なコーディング規約をまとめてみました。
前に触れたように規約を決めただけでは品質は上がりません。
開発者に周知することと、自動チェックできるインフラを整えることが重要です。
内部、外部関わらずコーディング規約を定義することからプログラムの品質をあげましょう。