🐘

PSR-12 拡張コーディングスタイル(日本語訳)

2023/12/23に公開

PSR-12 拡張コーディングスタイル

"MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY"および"OPTIONAL"というキーワードは、このドキュメントでは、RFC 2119で説明されているように解釈されます。

概要

この仕様は、PSR-2(コーディングスタイルガイド)を延長、拡張および置き換え、PSR-1(基本的なコーディングスタンダード)への遵守を必要とします。

PSR-2のように、この仕様の関心は、異なる作者のコードを読むときの認知摩擦を減らすことです。これは、PHPコードのフォーマット方法に関する共有されたルールと期待を列挙することによって行われます。このPSRは、コーディングスタイルツールが実装できる一連の方法、プロジェクトが遵守を宣言できる一連の方法、および開発者が異なるプロジェクト間で簡単に関連付けられる一連の方法を提供しようとします。さまざまな作者が複数のプロジェクトで協力するとき、それはそれら全てのプロジェクトの間で使われる一連のガイドラインを持つことを助けます。したがって、このガイドの利点は、ルール自体ではなく、それらのルールを共有することにあります。

PSR-2は2012年に承認され、その後、コーディングスタイルガイドに影響を与えるPHPの変更がいくつか行われています。一方PSR-2は、執筆時点で存在していたPHP機能に非常に包括的ですが、新しい機能は解釈の余地が非常に大きいです。したがって、このPSRは、PSR-2の内容を新しい機能が利用可能になったより現代的なコンテキストで明確にし、PSR-2の拘束に訂正を生じさせることを目的としています。

以前の言語バージョン

このドキュメントでは、あなたのプロジェクトでサポートされているPHPのバージョンに存在しない場合、いかなる指示を無視してもかまいません。

この例は、短い概要として以下のルールの一部を網羅しています。

<?php

declare(strict_types=1);

namespace Vendor\Package;

use Vendor\Package\{ClassA as A, ClassB, ClassC as C};
use Vendor\Package\SomeNamespace\ClassD as D;

use function Vendor\Package\{functionA, functionB, functionC};

use const Vendor\Package\{ConstantA, ConstantB, ConstantC};

class Foo extends Bar implements FooInterface
{
    public function sampleFunction(int $a, int $b = null): array
    {
        if ($a === $b) {
            bar();
        } elseif ($a > $b) {
            $foo->bar($arg1);
        } else {
            BazClass::bar($arg2, $arg3);
        }
    }

    final public static function bar()
    {
        // method body
    }
}

2. 一般

2.1 基本的なコーディングスタンダード

コードは、PSR-1で概説されているすべてのルールに従わねばなりません。
StudlyCapsという用語は、PSR-1では、各単語の最初の文字が大文字である、最初の文字を含むPascalCaseとして解釈されなければなりません。

2.2 ファイル


すべてのPHPファイルは、Unix LF(ラインフィード)の行末を使用しなければなりません。

全てのPHPファイルは、単一のLFで終了する、空行でない行で終了しなければなりません。

PHPのみを含むファイルからは、?>タグを省略しなければなりません。

2.3 行


行の長さには厳格な制限を設けてはなりません。

行の長さの緩い制限は120文字でなければなりません。

行は80文字を超えないべきで、それ以上の行は80文字を超えない複数の行に分割されるべきです。

行の末尾の空白はあってはなりません。

空白の行は、明示的に禁止されていない限り、可読性を向上させ、関連するコードブロックを示すために追加することができます。

一行に一つより多くの文を含んではなりません。

2.4 インデント


1つのインデントレベルにつき4つのスペースを使用しなければならず、インデントにタブを使用してはなりません。

2.5 キーワードと型


全てのPHPの予約されたキーワードと型は小文字でなければなりません。

将来のPHPバージョンに追加れる、あらゆる新しい型とキーワードは、ローワーケースでなければなりません。

型キーワードの短い形式を使用しなければなりません。例えば、booleanの代わりにboolintegerの代わりにintなどを使用しなければなりません。

3. 宣言文、名前空間、およびインポート文


PHPファイルのヘッダーは、複数の異なるブロックで構成される場合があります。その場合、以下の各ブロックは、単一の空行で区切られなければならず、空行を含んではなりません。各ブロックは、以下にリストされている順序でなければなりませんが、関連しないブロックは省略することができます。

  • <?phpタグ
  • ファイルレベルのdocblock
  • 1つ以上のdeclare文
  • ファイルの名前空間宣言
  • 1つ以上のクラスベースのuseインポート文
  • 1つ以上の関数ベースのuseインポート文
  • 1つ以上の定数ベースのuseインポート文
  • ファイル内の残りのコード

    ファイルがHTMLとPHPの混合を含む場合、上記のいずれのセクションをも使用することができます。その場合、たとえコードの残りがPHPの終了タグとHTMLとPHPの混合であっても、それらはファイルの先頭に存在しなければなりません。


<?phpタグがファイルの最初の行にある場合、それがPHPの開始と終了タグの外にマークアップを含むファイルでない限り、それは他の文がない行にある必要があります。

インポート文は、常に完全修飾名である必要があるため、バックスラッシュで始まってはなりません。

下記の例は、すべてのブロックの完全なリストを示しています。

<?php

/**
 * This file contains an example of coding styles.
 */

declare(strict_types=1);

namespace Vendor\Package;

use Vendor\Package\{ClassA as A, ClassB, ClassC as C};
use Vendor\Package\SomeNamespace\ClassD as D;
use Vendor\Package\AnotherNamespace\ClassE as E;

use function Vendor\Package\{functionA, functionB, functionC};
use function Another\Vendor\functionD;

use const Vendor\Package\{CONSTANT_A, CONSTANT_B, CONSTANT_C};
use const Another\Vendor\CONSTANT_D;

/**
 * FooBar is an example class.
 */
class FooBar
{
    // ... additional PHP code ...
}


深さが2を超える合成名前空間は使用してはなりません。したがって、以下が許可される最大の合成深度です。

<?php

use Vendor\Package\SomeNamespace\{
    SubnamespaceOne\ClassA,
    SubnamespaceOne\ClassB,
    SubnamespaceTwo\ClassY,
    ClassZ,
};


そして、以下は許可されません。


<?php

use Vendor\Package\SomeNamespace\{
    SubnamespaceOne\AnotherNamespace\ClassA,
    SubnamespaceOne\ClassB,
    ClassZ,
};


PHPの開始タグと終了タグの外にマークアップを含むファイルでstrict typesを宣言したい場合、宣言はファイルの最初の行にあり、開始PHPタグ、strict types宣言、終了タグが含まれていなければなりません。
例:


<?php declare(strict_types=1) ?>
<html>
<body>
    <?php
        // ... additional PHP code ...
    ?>
</body>
</html>


宣言文はスペースを含まず、正確に declare(strict_types=1) でなければなりません。(オプションのセミコロン終端子を含む)

ブロック宣言文は許可され、以下のようにフォーマットしなければなりません。括弧とスペースの位置に注意してください。


declare(ticks=1) {
    // some code
}

4. クラス、プロパティ、およびメソッド


"class" という用語は、すべてのクラス、インターフェース、トレイトを指します。

あらゆる閉じ括弧の後には、同じ行にコメントや文が続いてはなりません。

新しいクラスをインスタンス化する時、引数がコンストラクタに渡されない場合でも、常に括弧を使用しなければなりません。

new Foo();

4.1 拡張と実装


extendsimplements キーワードは、クラス名と同じ行に宣言しなければなりません。

クラスの開始括弧は、独自の行に配置しなければなりません。クラスの終了括弧は、本体の次の行に配置しなければなりません。

開始括弧は、独自の行に配置しなければなりません。また、前後に空行を配置してはなりません。

終了括弧は、独自の行に配置しなければなりません。また、前に空行を配置してはなりません。

<?php

namespace Vendor\Package;

use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

class ClassName extends ParentClass implements \ArrayAccess, \Countable
{
    // constants, properties, methods
}


implements のリストと、インターフェースの場合は extends のリストは、各行が1回インデントされた複数の行に分割することができます。その場合、リスト内の最初の項目は次の行に配置しなければならず、1行に1つのインターフェースしかないようにしなければなりません。

<?php

namespace Vendor\Package;

use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

class ClassName extends ParentClass implements
    \ArrayAccess,
    \Countable,
    \Serializable
{
    // constants, properties, methods
}

4.2 トレイトの使用


クラス内で使用されるトレイトを実装するためのuseキーワードは、開始括弧の次の行に宣言しなければなりません。

<?php

namespace Vendor\Package;

use Vendor\Package\FirstTrait;

class ClassName
{
    use FirstTrait;
}


クラスにインポートされる各個別のトレイトは、1行に1つずつ含まれていなければならず、各インクルードには独自のuseインポート文が必要です。

<?php

namespace Vendor\Package;

use Vendor\Package\FirstTrait;
use Vendor\Package\SecondTrait;
use Vendor\Package\ThirdTrait;

class ClassName
{
    use FirstTrait;
    use SecondTrait;
    use ThirdTrait;
}


クラスがuseインポート文の後に何も持たない場合、クラスの終了括弧はuseインポート文の次の行に配置しなければなりません。

<?php

namespace Vendor\Package;

use Vendor\Package\FirstTrait;

class ClassName
{
    use FirstTrait;
}


そうでない場合は、useインポート文の後に空行を置かなければなりません。

<?php

namespace Vendor\Package;

use Vendor\Package\FirstTrait;

class ClassName
{
    use FirstTrait;

    private $property;
}


insteadofas 演算子を使用する場合は、インデント、スペース、および改行に注意して、以下のように使用しなければなりません。

<?php

class Talker
{
    use A;
    use B {
        A::smallTalk insteadof B;
    }
    use C {
        B::bigTalk insteadof C;
        C::mediumTalk as FooBar;
    }
}

4.3 プロパティと定数


可視性はすべてのプロパティについて宣言しなければなりません。

可視性は、プロジェクトのPHPの最小バージョンが定数の可視性をサポートしている場合(PHP 7.1以降)、すべての定数について宣言しなければなりません。

var キーワードを使用してプロパティを宣言してはなりません。

1つの文に複数のプロパティを宣言してはなりません。

protectedまたはprivateの可視性を示すために、プロパティ名をアンダースコアで始めてはなりません。つまり、アンダースコアの接頭辞には明示的には意味がありません。

型宣言とプロパティ名の間にはスペースを置かなければなりません。

プロパティ宣言は以下のようになります。

<?php

namespace Vendor\Package;

class ClassName
{
    public $foo = null;
    public static int $bar = 0;
}

4.4 メソッドと関数


可視性はすべてのメソッドについて宣言しなければなりません。

protectedまたはprivateの可視性を示すために、メソッド名をアンダースコアで始めてはなりません。つまり、アンダースコアの接頭辞には明示的には意味がありません。

メソッド名や関数名は、その後にスペースを置いて宣言してはなりません。開始波括弧は独自の行に配置しなければならず、終了波括弧は本体の次の行に配置しなければなりません。開始丸括弧の後にスペースを置いてはならず、終了丸括弧の前にスペースを置いてはなりません。

メソッド宣言は以下のようになります。括弧、カンマ、スペース、波括弧の配置に注意してください。

<?php

namespace Vendor\Package;

class ClassName
{
    public function fooBarBaz($arg1, &$arg2, $arg3 = [])
    {
        // method body
    }
}


関数宣言は以下のようになります。括弧、カンマ、スペース、波括弧の配置に注意してください。

<?php

function fooBarBaz($arg1, &$arg2, $arg3 = [])
{
    // function body
}

4.5 メソッドと関数の引数


引数リストでは、カンマの前にスペースを置いてはならず、カンマの後にスペースを置かなければなりません。

メソッドと関数のデフォルト値のある引数は、引数リストの最後に配置しなければなりません。

<?php

namespace Vendor\Package;

class ClassName
{
    public function foo(int $arg1, &$arg2, $arg3 = [])
    {
        // method body
    }
}


引数リストは、各行が1回インデントされた複数の行に分割することができます。その場合、リスト内の最初の項目は次の行に配置しなければならず、1行に1つの引数しかないようにしなければなりません。

引数リストが複数の行に分割される場合、終了丸括弧と開始波括弧は、それらの間に1つのスペースを置いて、独自の行に一緒に配置しなければなりません。

<?php

namespace Vendor\Package;

class ClassName
{
    public function aVeryLongMethodName(
        ClassTypeHint $arg1,
        &$arg2,
        array $arg3 = []
    ) {
        // method body
    }
}


戻り値の型宣言がある場合、コロンの後には、型宣言が続くスペースが1つ必要です。コロンと宣言は、引数リストの終了丸括弧と同じ行に配置しなければなりません。2つの文字の間にスペースを置いてはなりません。

<?php

declare(strict_types=1);

namespace Vendor\Package;

class ReturnTypeVariations
{
    public function functionName(int $arg1, $arg2): string
    {
        return 'foo';
    }

    public function anotherFunction(
        string $foo,
        string $bar,
        int $baz
    ): string {
        return 'foo';
    }
}


null許容型宣言では、疑問符と型の間にスペースを置いてはなりません。

<?php

declare(strict_types=1);

namespace Vendor\Package;

class ReturnTypeVariations
{
    public function functionName(?string $arg1, ?int &$arg2): ?string
    {
        return 'foo';
    }
}


引数の前に参照演算子&を使用する場合、前の例のように、その後にスペースを置いてはなりません。

可変長引数の3点演算子と引数名の間にスペースを置いてはなりません。

public function process(string $algorithm, ...$parts)
{
    // processing
}


参照演算子と可変長引数の3点演算子を組み合わせる場合、2つの間にスペースを置いてはなりません。

public function process(string $algorithm, &...$parts)
{
    // processing
}

4.6 abstractfinal、および static


存在する場合、abstractとfinalの宣言は、可視性の宣言の前に配置しなければなりません。

存在する場合、staticの宣言は、可視性の宣言の後に配置しなければなりません。

<?php

namespace Vendor\Package;

abstract class ClassName
{
    protected static $foo;

    abstract protected function zim();

    final public static function bar()
    {
        // method body
    }
}

4.7 メソッドと関数の呼び出し


メソッドや関数を呼び出す場合、メソッドや関数名と開始丸括弧の間にスペースを置いてはなりません。開始丸括弧の後にスペースを置いてはならず、終了丸括弧の前にスペースを置いてはなりません。引数リストでは、カンマの前にスペースを置いてはならず、カンマの後にスペースを置かなければなりません。

<?php

bar();
$foo->bar($arg1);
Foo::bar($arg2, $arg3);


引数リストは、各行が1回インデントされた複数の行に分割しても構いません。その場合、リスト内の最初の項目は次の行に配置しなければならず、1行に1つの引数しかないようにしなければなりません。複数行に分割されている単一の引数(無名関数や配列の場合)は、引数リスト自体を分割するものではありません。

<?php

$foo->bar(
    $longArgument,
    $longerArgument,
    $muchLongerArgument
);
<?php

somefunction($foo, $bar, [
  // ...
], $baz);

$app->get('/hello/{name}', function ($name) use ($app) {
    return 'Hello ' . $app->escape($name);
});

5. 制御構造


制御構造の一般的なスタイルルールは以下の通りです。

  • 制御構造キーワードの後には1つのスペースを置かなければなりません。
  • 開始丸括弧の後にスペースを置いてはなりません。
  • 終了丸括弧の前にスペースを置いてはなりません。
  • 終了丸括弧と開始波括弧の間には1つのスペースを置かなければなりません。
  • 構造体の本体は1回インデントされなければなりません。
  • 本体は開始波括弧の次の行に配置しなければなりません。
  • 終了波括弧は本体の次の行に配置しなければなりません。

    制御構造の本体は波括弧で囲まれていなければなりません。これにより、構造の見た目が標準化され、本体に新しい行が追加されたときにエラーが発生する可能性が低くなります。

5.1 if, elseif, else


if 構造は以下のようになります。括弧、スペース、波括弧の配置に注意してください。また、elseelseif は、前の本体の終了波括弧と同じ行にあります。

<?php

if ($expr1) {
    // if body
} elseif ($expr2) {
    // elseif body
} else {
    // else body;
}


すべての制御キーワードが単語のように見えるようにするために、else if の代わりにelseif キーワードが使用されるべきです。

丸括弧の中の式は、各行が1回インデントされた複数の行に分割することができます。その場合、最初の条件は次の行に配置しなければなりません。終了丸括弧と開始波括弧は、それらの間に1つのスペースを置いて、独自の行に一緒に配置しなければなりません。条件間のブール演算子は、常に行の先頭または行の末尾に配置しなければならず、両方の混合であってはなりません。

<?php

if (
    $expr1
    && $expr2
) {
    // if body
} elseif (
    $expr3
    && $expr4
) {
    // elseif body
}

5.2 switch, case


switch 構造は以下のようになります。括弧、スペース、波括弧の配置に注意してください。case 文は switch から1回インデントされ、break キーワード(またはその他の終端キーワード)は case 本体と同じレベルにインデントされなければなりません。case 本体が空でないとき、意図的にフォールスルーする場合は、// no break のようなコメントを付けなければなりません。

<?php

switch ($expr) {
    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;
}


丸括弧の中の式は、各行が少なくとも1回インデントされた複数の行に分割することができます。その場合、最初の条件は次の行に配置しなければなりません。終了丸括弧と開始波括弧は、それらの間に1つのスペースを置いて、独自の行に一緒に配置しなければなりません。条件間のブール演算子は、常に行の先頭または行の末尾に配置しなければならず、両方の混合であってはなりません。

<?php

switch (
    $expr1
    && $expr2
) {
    // structure body
}

5.3 while, do while


while 文は以下のようになります。括弧、スペース、波括弧の配置に注意してください。

<?php

while ($expr) {
    // structure body
}


丸括弧の中の式は、各行が少なくとも1回インデントされた複数の行に分割することができます。その場合、最初の条件は次の行に配置しなければなりません。終了丸括弧と開始波括弧は、それらの間に1つのスペースを置いて、独自の行に一緒に配置しなければなりません。条件間のブール演算子は、常に行の先頭または行の末尾に配置しなければならず、両方の混合であってはなりません。

<?php

while (
    $expr1
    && $expr2
) {
    // structure body
}


似たように、do while 文は以下のようになります。括弧、スペース、波括弧の配置に注意してください。

<?php

do {
    // structure body;
} while ($expr);


丸括弧の中の式は、各行が少なくとも1回インデントされた複数の行に分割することができます。その場合、最初の条件は次の行に配置しなければなりません。条件間のブール演算子は、常に行の先頭または行の末尾に配置しなければならず、両方の混合であってはなりません。

<?php

do {
    // structure body;
} while (
    $expr1
    && $expr2
);

5.4 for


for 文は以下のようになります。括弧、スペース、波括弧の配置に注意してください。

<?php

for ($i = 0; $i < 10; $i++) {
    // for body
}


丸括弧の中の式は、各行が少なくとも1回インデントされた複数の行に分割することができます。その場合、最初の式は次の行に配置しなければなりません。終了丸括弧と開始波括弧は、それらの間に1つのスペースを置いて、独自の行に一緒に配置しなければなりません。

<?php

for (
    $i = 0;
    $i < 10;
    $i++
) {
    // for body
}

5.5 foreach


foreach 文は以下のようになります。括弧、スペース、波括弧の配置に注意してください。

<?php

foreach ($iterable as $key => $value) {
    // foreach body
}

5.6 try, catch, finally


try-catch-finally ブロックは以下のようになります。括弧、スペース、波括弧の配置に注意してください。

<?php

try {
    // try body
} catch (FirstThrowableType $e) {
    // catch body
} catch (OtherThrowableType | AnotherThrowableType $e) {
    // catch body
} finally {
    // finally body
}

6. 演算子


演算子のスタイルルールは、arity(そられがとるオペランドの数)によってグループ化されます。

演算子の周りにスペースを許可する場合、可読性を高めるために複数のスペースを使用することができます。

ここで説明されていないすべての演算子は未定義のままです。

6.1 単項演算子


インクリメントまたはデクリメント演算子は、演算子とオペランドの間にスペースを置いてはなりません。

$i++;
++$j;


型キャスト演算子は、丸括弧の中にスペースを置いてはなりません。

$intValue = (int) $input;

6.2 二項演算子


全ての算術、比較、代入、ビット演算、論理、文字列、型の二項演算子は、前後に少なくとも1つのスペースを置かなければなりません。

if ($a === $b) {
    $foo = $bar ?? $a ?? $b;
} elseif ($a > $b) {
    $foo = $a + $b * $c;
}

6.3 三項演算子


条件演算子(三項演算子とも呼ばれる)は、? と : の両方の文字の前後に少なくとも1つのスペースを置かなければなりません。

$variable = $foo ? 'foo' : 'bar';


条件演算子の中間オペランドが省略されている場合、演算子は他の二項比較演算子と同じスタイルルールに従わなければなりません。

$variable = $foo ?: 'bar';

7. クロージャー


クロージャーは、function キーワードの後にスペースを置いて宣言し、use キーワードの前後にスペースを置かなければなりません。

開始波括弧は同じ行に配置しなければならず、終了波括弧は本体の次の行に配置しなければなりません。

開始丸括弧の後にスペースを置いてはならず、終了丸括弧の前にスペースを置いてはなりません。

引数リストと変数リストでは、カンマの前にスペースを置いてはならず、カンマの後にスペースを置かなければなりません。

クロージャーのデフォルト値を持つ引数は、引数リストの最後に配置しなければなりません。

戻り値の方が存在する場合、通常の関数やメソッドと同じルールに従わなければなりません。use キーワードが存在する場合、コロンは use リストの終了丸括弧の後に、2つの文字の間にスペースを置かずに配置しなければなりません。

クロージャーの宣言は以下のようになります。括弧、カンマ、スペース、波括弧の配置に注意してください。

<?php

$closureWithArgs = function ($arg1, $arg2) {
    // body
};

$closureWithArgsAndVars = function ($arg1, $arg2) use ($var1, $var2) {
    // body
};

$closureWithArgsVarsAndReturn = function ($arg1, $arg2) use ($var1, $var2): bool {
    // body
};


引数リストと変数リストは、各行が1回インデントされた複数の行に分割することができます。その場合、リスト内の最初の項目は次の行に配置しなければならず、1行に1つの引数または変数しかないようにしなければなりません。

終了リスト(引数または変数)が複数の行に分割される場合、終了丸括弧と開始波括弧は、それらの間に1つのスペースを置いて、独自の行に一緒に配置しなければなりません。

以下は、引数リストと変数リストが複数の行に分割されたクロージャーの例です。

<?php

$longArgs_noVars = function (
    $longArgument,
    $longerArgument,
    $muchLongerArgument
) {
   // body
};

$noArgs_longVars = function () use (
    $longVar1,
    $longerVar2,
    $muchLongerVar3
) {
   // body
};

$longArgs_longVars = function (
    $longArgument,
    $longerArgument,
    $muchLongerArgument
) use (
    $longVar1,
    $longerVar2,
    $muchLongerVar3
) {
   // body
};

$longArgs_shortVars = function (
    $longArgument,
    $longerArgument,
    $muchLongerArgument
) use ($var1) {
   // body
};

$shortArgs_longVars = function ($arg) use (
    $longVar1,
    $longerVar2,
    $muchLongerVar3
) {
   // body
};


クロージャーが引数として関数やメソッドの呼び出しに直接使用される場合でも、フォーマットルールが適用されることに注意してください。

<?php

$foo->bar(
    $arg1,
    function ($arg2) use ($var1) {
        // body
    },
    $arg3
);

8. 無名クラス


無名クラスは、上記のセクションにあるクロージャーと同じガイドラインと原則に従わなければなりません。

<?php

$instance = new class {};


開始波括弧は、implementsインターフェースのリストが折り返されない限り、classキーワードと同じ行にあっても構いません。インターフェースのリストが折り返される場合は、波括弧は最後のインターフェースの直後の行に配置しなければなりません。

<?php

// Brace on the same line
$instance = new class extends \Foo implements \HandleableInterface {
    // Class content
};

// Brace on the next line
$instance = new class extends \Foo implements
    \ArrayAccess,
    \Countable,
    \Serializable
{
    // Class content
};
GitHubで編集を提案

Discussion