目次
- PSR-2 Coding Style Guide
- CakePHPコーディング規約
- Symfonyコーディング規約
- WordPressコーディング規約
- FuelPHPコーディング規約
- 参考記事集
チームで開発するにあたり、コーディング規約を決めておくことは重要です。コーディング規約を統一しておくことにより、綺麗で読みやすいコードとなりますし、コードレビューの差分も見やすいものとなります。
しかし、PHPのコーディング規約はフレームワークやPHPのバージョンなどによって異なります。
例えば、メソッド名がキャメルケース(camelCase
のように、小文字始まりで単語の区切りで大文字になるような文字列)だったり、スネークケース(snake_case
のようなアンダースコアつながりの文字列)だったり様々です。
名前空間やクラス名の規則、インデントやスペースの規則などもそれぞれ異なります。
本記事ではPHPで有名なコーディング規約をいくつか紹介します。
PSR-2 Coding Style Guide
PSR とは
PSRとは、PHP-FIG(PHP Framework Interop Group)が定めた規約です。
PHPには、Zend FarmeworkやSymfonyなど様々なフレームワークやライブラリがありますが、それぞれコーディング規約が微妙に異なっています。
そこで、各フレームワークの関係者が集まったグループPHP-FIGが、PHP共通でえるようなコーディング規約を決めたのがPSRです。
PSRにはPSR-0,PSR-1,PSR-2,PSR-4などの規約があります。
PSR-0とPSR-4はPHPのオートローダーのためのコーディング規約、PSR-1とPSR-2は標準コーディング規約になっています。
PSR-2はPHPの最もスタンダードなコーディング規約となりつつあります。
PSR-0とPSR-4
PSR-0とPSR-4はともに名前空間・クラス名とファイルパスの関係に関する規約です。
PSR-0は2014年10月以降非推奨となっており、新しいプロジェクトではPSR-4の規約を使うことが推奨されています。
PSR-0: Autoloading Standard – PHP-FIG
PHPにはオートローダーという仕組みがあるため、名前空間つきのクラス名1つに対して1つのファイルパスが対応している必要があります。
PSR-0
PSR-0では、名前空間つきクラス名とファイルパスの対応について以下のように決められています。
- 名前空間の
\
をディレクトリセパレーター/
に置き換える - クラス名の
_
をディレクトリセパレータ/
に置き換える - 最後に
.php
を付ける
例えば、クラス名とファイルパスの対応は以下のようになります
名前空間付きクラス名 | 対応するファイルパス | 備考 |
---|---|---|
\vendorname \namespace \ClassName | /project /path /namespace /ClassName.php | \ が/ に置き変わる |
\vendorname \name_space \ClassName | /project /path /name_space /ClassName.php | 名前空間の_ は特別な意味を持たない |
\vendorname \name_space \Class_Name | /project /path /name_space /Class /Name.php | クラス名の_ は/ に置き換わる |
PSR-4
PSR-4はPSR-0に変わる新しい規約です。主な変更点は以下の点です。
- クラス名の
_
は特別な意味を持たない
つまり \vendorname\name_space\Class_Name
に対応するファイルパスは
- PSR-0:
/project/path/name_space/Class/Name.php
- PSR-4:
/project/path/name_space/Class_Name.php
となります。
PSR-0とPSR-4がある理由
PHPの名前空間はPHP5から実装されました。それ以前のPHPには名前空間というものがありませんでした。
名前空間がない場合、クラス名が衝突しやすいという問題点がありました。
例えば、Mysql
というクラスを定義したくても、導入しているライブラリなどですでにMysql
というクラスが定義されていた場合、そのクラス名は使えませんでした。
そこで、Zend_Mysql
のようにクラス名にはプロジェクトのプレフィックスを付けるのが慣習となっていました。
このようなプレフィックス付きのクラス名を名前空間のように扱えるようにしたのがPSR-0です。
PSR-0はこのような少し昔のコードのために用意されている規約であり、古いPHPでは、PSR-0を使うことも考える必要があるかもしれません。しかし現在は非推奨となっており、新しいプロジェクトではPSR-4の規約を使うことが推奨されています。
PSR-1 Basic Coding Standard
PSR-1はPHPのコーディング規約の中でも基本的な部分が定めてあります。例えば以下のようなことが定められています。
- PHPタグは
<?php
または<?=
のみ使えます。 - ファイルはBOM(Byte Order Mark)無しUTF-8でなければなりません。
- 名前空間とクラス名はPSR-0またはPSR-4に従わなければなりません。
- クラス名 は
UpperCamelCase
のような アッパーキャメルケース で定義しなければなりません。 - クラス定数 は
UPPER_SNAKE_CASE
のような アッパースネークケース で定義しなければなりません。 - メソッド名 は
camelCase
のような キャメルケース で定義しなければなりません。
PHPの標準関数はスネークケースで定義されていますが、PSR-1においてメソッド名はキャメルケースで定義しなければなりません。
変数名やプロパティ名に関しては得に決まりは無いため、キャメルケース、アッパーキャメルケース、スネークケースのどれを利用しても良いですが、ある程度一貫性があった方が良いということが書かれています。
例えば、通常のプロパティはキャメルケース、staticプロパティはアッパーキャメルケースにするなどが考えられます。
class Something { public $normalPropterty; public static $StaticProperty; }
PSR-2 Coding Style Guide
PSR-2は、PSR-1を拡張したコーディング規約です。一例として以下のようなことが決められています。
- PSR-1のコーディング規約には従わなければなりません。
- インデントには4つのスペースを利用しなければなりません。タブは使用してはいけません。
- 1行の長さに制限はありませんが、120文字以下が望ましいです。できれば80文字以下にして下さい。
- クラス・メソッドの波括弧の前には改行を入れなければなりません。
- メソッドやプロパティの定義は最初に
abstract
/final
、次にpublic
/protected
/private
、最後にstatic
を書かなければなりません。 - 制御構文開始の波括弧の前には改行を入れません。
- 制御構文の
(
の後、)
の前にはスペースを入れません。
後に紹介するCakePHPやSymfonyもこのPSR-2に準拠しています。
クラスの定義
クラス定義の{
の前には改行を挟まなければいけません。また、extends
と implements
はクラス名と同じ行に書きます。
class ClassName extends ParentClassName implements Interface1, Interface2 { // クラスの定義 }
インターフェースが多くて行が長くなる場合は、implements
の後で改行し、1行に1つのインタフェースを書きます。
class ClassName extends ParentClassName implements Interface1, Interface2, Interface3, Interface4 { // クラスの定義 }
class ClassName {
のように、同じ行に{
を書く規約も少なくないため、こういった書き方を初めて見る方もいらっしゃるかもしれません。
プロパティの定義
PSR-2ではpublic
/protected
/private
を省略することは出来ません。
PHPではこれらを省略するとpublic
になりますが、意図して省略したのか書くのを忘れたのか区別できないため、public
でも必ず書くべきです。
static
キーワードはこの後に書きます。プロパティの定義に var
を使ってはいけません。var
にはpublic
などの修飾子を付けることができないためです。
class ClassName { public $property1; private $property2; public static $staticProperty; }
また、1つのステートメントで2つ以上のプロパティを定義しては行けません。
以下のようなプロパティ定義は可能ですが、PSR-2では禁止されています。
class ClassName { private $property1, $property2; }
メソッド
プロパティ同様、public
/protected
/private
は必ず付けなければならず、abstract
/final
を付ける場合はその前に付けます。
static
は修飾子の中では一番最後です。丸括弧の前後にはスペースを入れず、波括弧の前には改行を入れます。
また、引数のカンマの前にはスペースを入れず、後ろには1つのスペースを入れます。
class ClassName { abstract protected function abstractDoSomething(); final public static function doSomething($arg1, $arg2, $arg3) { // ... } }
メソッドの引数が多い場合は、(
の後で改行し、1行に1つの引数を書くことが出来ます。
この場合1行に複数の引数を書くことはできません。
また、)
と{
は1つのスペースを挟んで同じ行に書きます。
class ClassName { public function doSomething( TypeHint $arg1, $arg2, $arg3, $arg4 ) { // ... } }
注意点として、クロージャーの場合は{
の前に改行をいれてはいけません。
$closure = function ($a, $b) use ($c) { // 本体 };
制御構文
制御構文は、
(
の前に1スペースを入れます。(
の後にスペースは入れません。)
の前にスペースは入れません。)
の後には1スペースを入れます。
また、 else if
ではなく elseif
を使うべきです。
if ($condition1) { // ... } elseif ($condition2) { // ... } else { // ... }
else if
とelseif
は全く同じものではないことに注意してください。elseif
はこれで1つの構文であるのに対し、else if
は最初のif
のelse
節の中にif
を入れている事になります。
if ($condition1) { // ... } else if ($condition2) { // ... } else { // ... }
この構文は、以下のように解釈されます。
if ($condition1) { // ... } else { if ($condition2) { // ... } else { // ... } }
switch
文は、case
のインデントがswitch
よりも1段深く、case
の中身はさらに1段深くなります。
case
の中で何か処理をした後、break
しない時はコメントを書かなければなりません。
switch ($condition) { case 0: echo 'First case, with a break'; 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; }
CakePHPコーディング規約
CakePHPとは
CakePHPはオープンソースのWebフレームワークです。
昔からあるWebサービスにはCakePHPを採用しているものも多いかと思います。
CakePHPは開発者がPHP-FIGに参加しており通り、基本的にはPSR-2に従って書くことになっていますが、いくつか追加のコーディング規約があります。
1行の長さ
PSR-2では1行の長さを120文字以下にするよう推奨していますが、これは 必須 ではありませんでした。
対してCakePHPのコーディング規約では、100文字以内を 推奨 し、120文字以内であることが 必須 となっています。
三項演算子
三項演算子のネストは禁止されています。
$variable = isset($options['variable']) ? isset($options['othervar']) ? true : false : false;
三項演算子をネストすると非常に読みづらくなります。どこが先に評価されて、どの値が返ってくるのかが非常に分かりづらくなります。
if
文などを利用して見やすく書き直すべきです。
$variable = false; if(isset($options['variable']) && isset($options['othervar'])) { $variable = true; }
比較
比較は可能な限り厳密な比較(===
, !==
)を利用するべきとしています。
if ($value === null) { // ... }
PHPには厳密な比較だけでなくゆるい比較(==
, !=
)もあります。
ゆるい比較ででプリミティブ型を比較すると、型がキャストされて比較されます。
便利な面もあるのですが、意図しない値がtrueになることもあるため注意が必要です。
$value = 0; if ($value == null) { // nullをintにキャストすると0になるので // この結果はtrueになり、この処理が行われる }
また、比較においてチェック対象の値は右側に置くことを推奨しています。
// 非推奨(ヨーダ記法と呼ばれる記法) if (null === $value) { // ... } // 推奨 if ($value === null) { // ... }
チェック対象の値を左側に書く記法はヨーダ記法と言われます。もし、===
を=
と書き間違えた場合、$value = null
であるとエラーが起きませんが、 null = $value
はエラーになります。このため、ヨーダ記法はバグを生みづらい記法とされています。ただし、CakePHPコーディング規約では読みやすさの観点からか、ヨーダ記法は非推奨とされています。
Symfonyコーディング規約
Symfonyとは
SynfonyはCakePHP同様歴史の古いオープンソースのWebフレームワークです。PHP-FIGに開発者が参加しており、基本的なコーディング規約はPSR-2に従っていますが、いくつか追加の規約を定めています。この追加の部分がCakePHPとは異なった方向性になっているのでいくつか紹介します。
比較の際にはヨーダ記法を利用する
==
, ===
, !=
, !==
などの比較の際は、チェック対象の値を左側に置くヨーダ記法を推奨しています。この点においてはCakePHPと全く逆のルールを採用しています。
// 推奨(ヨーダ記法と呼ばれる記法) if (null === $value) { // ... } // 非推奨(CakePHPではこちらの記法が推奨されていた) if ($value === null) { // ... }
CakePHPの章でも述べた通り、$value === null
といった書き方は、===
を=
に書き間違えた際にもエラーにならず、バグに気づきづらいです。そのため、Symfonyコーディング規約ではヨーダ記法を推奨しています。
複数行の配列は最後の要素の末尾にもカンマを書く
以下の2つの配列の書き方は同じ値になりますが、前者の書き方が推奨されています。
// 推奨されている書き方 $array = [ 'value1', 'value2', 'value3', // 最後の行にもカンマを書く ]; // 非推奨 $array = [ 'value1', 'value2', 'value3' // 最後の行にカンマがない ];
この理由として、要素を追加した時の差分が見やすいことが上げられます
// 推奨 $array = [ 'value1', 'value2', 'value3', // 最後の行にもカンマを書く + 'value4', // この行だけが差分になる ]; // 非推奨 $array = [ 'value1', 'value2', - 'value3' // 最後の行にカンマがない + 'value3', // 最後の行にカンマがない + 'value4' // この行と一つ上の行も差分になる ];
非推奨の書き方では、カンマを追加しないといけないため、差分が1行増えてしまいます。コードレビューなどで差分を見やすくするために、配列が複数行に渡る場合は最後の要素の後にもカンマを書いたほうが良いでしょう。
メソッドや関数の引数は関数名と同じ行に書く
メソッドや関数の引数は、メソッド名や関数名と同じ行に書きます。
public function doSomething($arg1, $arg2, $longNameArgument3, $longNameArgument4, $longNameArgument5) { // ... }
これには、引数が多すぎるメソッドや関数の定義を抑制する意図や、関数の引数と本体を区別しやすくして見通しを良くする意図があると考えられます。ただし、一行が長くなってしまうというデメリットもあります。1行の長さを制限しているCakePHPとは少し方向性が異なり規約でしょうか。
クラス内のプロパティ・メソッドの定義の順序
クラス内ではプロパティを定義してからメソッドを定義します。
プロパティやメソッドは、publicメソッドを一番最初に、次にprotected、最後にprivateを定義します。ただし、メソッドの中でもコンストラクタ(__construct()
)とPHPUnitとsetUp()
, tearDown()
は最初に定義します。
class Something
{
public $property1;
protected $property2;
private $property3;
public function __construct()
{
// ...
}
public function doSomething()
{
// ...
}
private function doSomethingPrivate()
{
// ...
}
}
クラスのプレフィックスとサフィックス
abstractクラスにはAbstract
サフィックスを付けます。
abstract class AbstractDatabase { // ... }
インターフェースにはInterface
サフィックスを付けます。
interface LoggerInterface { // ... }
トレイトにはTrait
サフィックスを付けます。
trait SomethingTrait
{
// ...
}
例外にはException
サフィックスを付けます。
class NotFoundException extends \RuntimeException
{
// ...
}
WordPressコーディング規約
WordPressとは
WordPressとは、PHPで書かれたオープンソースのソフトウェアで、主にブログを作成するのに利用されています。こちらも古いフレームワークですが、現在でもWordPressで作成されたブログやサイトは多くあります。WordPressは開発者がPHP-FIGのメンバーとして参加していないためか、PSRとは違う方向性のコーディング規約となっています。
インデントはタブを利用する
WordPressのコーディング規約では、インデントにスペースではなくタブを使うように定められています。ただし、コードを揃えるためにスペースを使うことは許されています。
[tab]$foo = 'somevalue'; [tab]$foo2 = 'somevalue2'; [tab]$foo34 = 'somevalue3'; [tab]$foo5 = 'somevalue4';
インデントにタブを利用するメリットの一つは、エディタでタブ幅の設定を変えることで、コードに変更を与えずインデント幅を変えられる点です。人によって見やすいインデント幅は違うかもしれませんが、それにも対応できます。また、スペース4つよりタブ1つの方がファイルサイズを節約できるというメリットがあります。
ただし、環境によっては正しくタブを表示方できないこともあります。このような環境依存の要因を減らすため、PSRでは、4スペースのインデントを採用しています。比較的古いコードはインデントがタブで書かれていたりします。
制御構文や関数呼び出し時のスペースについて
if
やfor
などの制御構文の丸括弧の前後にはスペースを入れます。
if ( $condition ) { // ... }
関数の定義、呼び出しのときは以下の形式でスペースを入れます。
function my_function( $arg1, $arg2 ) { // ... }
関数の引数には意味のあるフラグ値を利用する
以下のような関数定義は避けなければいけません。
function eat( $what, $slowly = true ) { //... }
この関数を呼ぶ時には以下のように呼び出すことになります。
eat( 'mushrooms', true );
しかしこれでは、true
の意味が分かりません。true
の意味を知るためには関数の定義を見る必要があります。そこで、WordPressコーディング規約では以下の書き方を推奨しています。
function eat( $what, $speed = 'slowly' ) { // ... }
これにより、呼び出す側が以下のようになります。
eat( 'mushrooms', 'slowly' );
「ゆっくり」食べるということがこれを見るだけで分かるようになりました。更によりよい書き方として以下の書き方も推奨されています。
function eat( $what, array $args ) { // ... }
呼び出す側は以下のようになります。
eat ( 'mushrooms', array( 'speed' => 'slowly' ) );
slowly
がspeed
であることがより分かりやすくなりました。
この書き方、一見読みやすくはなりますが、speed
やslowly
といった文字列をタイプミスしてもエラーにならないため、バグを生み出しやすくなってしまうというデメリットもあります。
FuelPHPコーディング規約
FuelPHPとは
FuelPHPとは、PHPで書かれたオープンソースのWebフレームワークです。これまで紹介したSymfony等他のフレームワークよりも少し新しいですが、PSRとは少し異なった方向性の規約となっています。
メソッド名・変数名の命名規則など
FuelPHPでは、メソッド名や変数名はスネークケース(snake_case
のような文字列)を使用します。また、PSR-2と同様、クラスやメソッドの{
の前は改行をはさみます。
private
またはprotected
なメソッドやプロパティはアンダースコア始まりの名前にします。
class Session { protected $_data; public static function get_flash($name, $data) { // ... } private function _private_metod($argument_name) { // ... } }
PHPは標準関数がスネークケースで定義されているため、それに合わせて変数名やメソッド名にスネークケースを採用するケースもあります。
private
/protected
メソッドやプロパティをアンダースコア始まりの名前で定義するのは、昔の慣習の名残りです。PHP4以前はプライベートメソッドやプライベートプロパティを定義することができませんでした。そこで、プライベートにしたいメソッドやプロパティは、アンダースコア始まりの名前を付けるという慣習がありました。
制御構文の波括弧の前では改行をする
PSR-2において、クラス定義やメソッド定義の波括弧の前では改行をすることになっていましたが、制御構文の波括弧については特に決められていませんでした。FuelPHPでは、制御構造に関しても、波括弧の前に改行を入れる必要があります。
if ($condition) { // ... }
&&
や||
のかわりにand
やor
を使う
FuelPHPでは読みやすさの観点から、&&
や||
のかわりにand
やor
を使うことを推奨しています。
// こうではなく
if ($var == false && $other_var != 'some_value')
// こっちで書く
if ($var == false and $other_var != 'some_value')
ただし、&&
とand
では演算子の優先順位が微妙に異なるので注意が必要です。
<?php $var = 0; if($var = 2 && 1) { echo $var, PHP_EOL; // => 1 } $var = 0; if($var = 2 and 1) { echo $var, PHP_EOL; // => 2 }
and
より=
の方が優先度が高く、=
より&&
の方が優先度が高いため、前者は $var = (2 && 1)
となり、$var
の値は1
になります。一方で、後者は($var = 2) and 1
となるので、$varの値は2
になります。かなり特殊な場合でしか影響しませんが、注意が必要です。
最後に
今回はいくつか有名なフレームワークのコーディング規約や、興味深いコーディング規約を取り上げました。他にも、Zend FrameworkやPEARなどもコーディング規約を定めています。Zend Framework 等の昔からあるフレームワークは、古いバージョンのPHPに対応したコーディング規約があったりします。しかし、PSR-2が策定されて以降は、PSR-2に従うフレームワークが増えてきました。古いPHPでフレームワークを採用していないものについては、PEARに準拠しているコードもあるかもしれません。
新しいプロジェクトのコーディング規約を決める際には、PSR-1またはPSR-2に準拠しつつ追加の規約を決めていくか、利用しているフレームワークの規約に準拠するのが良いかと思います。
コーディング規約を定めた際に、コードが規約に則っているかどうかをコードレビュー等でチェックすることがあると思います。しかし、人の目で見ていると手間がかかったり、見落としがある場合もあります。そこで、コーディング規約を自動でチェックしてくれるPHP_CodeSnifferというツールがあります。PHP_CodeSnifferを利用することで、PSR-2やCakePHPのコーディング規約に違反しているコードを自動で指摘してくれます。また、SideCIを導入することで、プルリクエストのコードがコーディング規約に違反していないかどうかを調べることができます。SideCIとGitHubを連携させ、sideci.yml
をリポジトリに追加することで、プルリクエストに対してPHP_CodeSnifferを使ったコーディング規約のチェックをすることができます( https://docs.sideci.com/tools/codesniffer.html )。デフォルトはPSR-2ですが、もちろんCakePHPやSymfonyのコーディング規約もチェックできます。
参考
- PHP-FIG
- php-fig/fig-standards – GitHub(PSRのリポジトリ)
- cakephp/cakephp – GitHub
- CakePHP 3.0 コーディング規約(日本語)
- simfony/simfony – GitHub
- Symfony Coding Standards
- WordPress – GitHub
- WordPress Coding Standards
- fuel/fuel – GitHub(FuelPHP)
- FuelPHP Coding Standards
- squizlabs/PHP_CodeSniffer – GitHub
- SideCI
- PHP_CodeSniffer – SideCI
参考にさせていただきましたが、クラスのプレフィックスとサフィックスのabstract文の説明は誤りではないでしょうか。
例を見るにサフィックスではなく、プレフィックスが正しいのでは?
ご確認をお願いします。