【PHP】PSR-12 Extended Coding Style(拡張コーディングスタイル)

公開
更新

PSR(PHP標準勧告)


概要

PSR-12 は、コーディングスタイルガイドである PSR-2 の拡張および置き換えと、基本的なコーディング標準である PSR-1 への準拠が必要になります。

PSR-2 と同様に、PSR-12 の目的は、異なる作成者のコードを理解する(読む)際の認識の摩擦を減らすことです。これは、共有された一連のルールと、PHP コードのフォーマット方法に関する期待を列挙することによって行われます。

PSR-12 は、コーディングスタイルツールが実装できる一連の方法を提供することを目指しており、プロジェクトはコーディングスタイルの遵守を宣言でき、開発者は異なるプロジェクト間で簡単にこれを関連付けることができます。

※ PSR-12 の後継として、PHP-FIG から PER Coding Style が公開されています。PER Coding Style は PSR-12 を拡張・置き換えるもので、enum(列挙型)や attribute(属性)など、PSR-12 以降に追加された PHP の構文の書き方も定めています。

さまざまな作成者が複数のプロジェクトにわたって共同作業を行う場合、それらすべてのプロジェクト間で1つのガイドラインセットを使用すると役立ちます。

したがって、このガイドの利点はルール自体ではなく、それらのルールの共有にあります。

PSR-2 は 2012 年に承認され、それ以降、コーディングスタイルのガイドラインに影響する PHP コードに多くの変更が加えられました。

PSR-2 は、執筆時点で存在していた PHP 機能の非常に包括的なものですが、新しい機能は非常に解釈の自由があります。

したがって、PSR-12 は、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
    }
}

名前空間の使用法: エイリアス/インポート

General

Basic Coding Standard

コードは、PSR-1 の規則全てに従う必要があります。

PSR-1の「StudlyCaps」という用語は、各単語の最初の文字が最初の文字を含めて大文字になっている PascalCase として解釈する必要があります。

Files

  • 全ての PHP ファイルは、Unix LF(ラインフィード)の行末のみを使用する必要があります。
  • 全ての PHP ファイルは、空白以外の行で終わり、単一の LF で終了する必要があります。
  • PHP のみを含むファイルでは、終了タグを省略する必要があります。

Lines

  • 行の長さに厳しい制限があってはなりません。
  • 行の長さのソフト制限は 120 文字でなければなりません。
  • 行は 80 文字以下にする必要があります。それより長い行はそれぞれが 80 文字以下の複数の後続行に分割されます。
  • 行末に空白を入れてはいけません。
  • 明示的に禁止されている場合を除き、読みやすさを向上させ、コードの関連ブロックを示すために、空白行を追加できます。
  • 1行に複数のステートメントがあってはなりません。

Indenting

コードは、インデントレベルごとに4つのスペースのインデントを使用する必要があります。インデントにタブを使用しないでください。

Keywords and Types

  • 全ての PHP 予約キーワードとタイプは小文字でなければなりません。
  • 将来のバージョンの PHP に追加される新しいタイプとキーワードは全て小文字でなければなりません。
  • タイプキーワードの短い形式を使用する必要があります。「boolean」の代わりに「bool」、「integer」の代わりに「int」などを使用する必要があります。

Declare Statements, Namespace, and Import Statements

PHPファイルのヘッダーは、いくつかの異なるブロックで構成されている場合があります。

存在する場合、以下の各ブロックは単一の空白行で区切る必要があり、空白行を含めることはできません。

各ブロックは、以下にリストされている順序である必要がありますが、関連のないブロックは省略できます。

  • 開始タグ
  • ファイルレベルの DocBlock
  • 1つ以上の宣言ステートメント
  • ファイルの名前空間宣言
  • 1つ以上のクラスベースの use インポート文
  • 1つ以上の関数ベースの use インポート文
  • 1つ以上の定数ベースの use インポート文
  • ファイル内の残りのコード

ファイルに HTML と PHP が混在している場合でも、上記のセクションのいずれかを使用できます。

その場合、コードの残りの部分が PHP の終了タグで構成され、その後に HTML と 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 の開始タグと終了タグの外側にマークアップを含むファイルで厳密な型を宣言する場合、宣言はファイルの最初の行にあり、「開始タグ・厳密な型の宣言・終了タグ」を含める必要があります。

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

Declareステートメントにはスペースを含めず、正確に declare(strict_types=1) にする必要があります(オプションでセミコロンターミネータを使用)。

ブロック宣言ステートメントは許可され、以下のようにフォーマットする必要があります。 ブレースの位置と間隔に注意してください。

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

Classes, Properties, and Methods

「クラス」という用語は、全てのクラス、インターフェース、およびトレイトを指します。

閉じ括弧の後に、同じ行にコメントやステートメントを続けてはなりません。

新しいクラスをインスタンス化するとき、コンストラクターに渡される引数がない場合でも、括弧は常に存在している必要があります。

new Foo();

Extends and Implements

  • extends および implements キーワードは、クラス名と同じ行で宣言する必要があります。
  • クラスの開始波括弧は、独自の行に配置する必要があります。
  • クラスの終了波括弧は、本文の次の行に配置する必要があります。
  • 開始波括弧はそれぞれの行になければならず、前後に空白行があってはなりません。
  • 終了波括弧は独自の行に置く必要があり、前に空白行を入れてはなりません。
<?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つのインターフェースのみが存在する必要があります。

<?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
}

Using traits

トレイトを実装するためにクラス内で使用される 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;
}

insteadof および as 演算子を使用するときは、インデント・間隔・改行に注意して、次のように使用する必要があります。

<?php

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

Properties and Constants

  • 全てのプロパティで可視性を宣言する必要があります。
  • プロジェクトの PHP 最小バージョンが定数の可視性(PHP 7.1以降)をサポートしている場合、全ての定数で可視性を宣言する必要があります。
  • プロパティの宣言に var キーワードを使用しないでください。
  • 1つのステートメントで宣言できるプロパティは1つだけです。
  • protected または private を示すために、プロパティ名の前に単一の下線を付けることはできません。 つまり、アンダースコアのプレフィックスは明示的に意味を持ちません。
  • 型宣言とプロパティ名の間にはスペースが必要です。

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

<?php

namespace Vendor\Package;

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

Methods and Functions

  • 全てのメソッドで可視性を宣言する必要があります。
  • protected または private を示すために、メソッド名の前に単一の下線を付けることはできません。つまり、アンダースコアのプレフィックスは明示的に意味を持ちません。
  • メソッド名と関数名は、メソッド名の後にスペースを入れて宣言しないでください。
  • 開始ブレースは独自の行に配置し、終了ブレースは本文に続く次の行に配置する必要があります。
  • 左括弧の後にスペースがあってはならず、右括弧の前にスペースがあってはなりません。

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

<?php

namespace Vendor\Package;

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

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

<?php

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

Method and Function Arguments

引数リストでは、各コンマの前にスペースがあってはならず、各コンマの後にスペースが1つなければなりません。

デフォルト値を持つメソッドと関数の引数は、引数リストの最後に置く必要があります。

<?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つのスペースが必要です。

コロンと宣言は、引数リストの閉じ括弧と同じ行になければなりません。(閉じ括弧とコロンの間にスペースは入れない)

<?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
}

abstract, final, and 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
    }
}

Method and Function Calls

メソッドまたは関数の呼び出しを行う場合

  • メソッドまたは関数の名前と開き括弧の間にスペースがあってはなりません。
  • 開き括弧の後にスペースがあってはなりません。
  • 閉じ括弧の前にスペースがあってはなりません。
  • 引数リスト
    • 各コンマの前にスペースがあってはなりません。
    • 各コンマの後にスペースが1つなければなりません。
<?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);
});

Control Structures

制御構造の一般的なスタイル規則は次のとおりです。

  • 制御構造キーワードの後にスペースが1つ必要です。
  • 左括弧の後にスペースがあってはなりません。
  • 右括弧の前にスペースがあってはなりません。
  • 右括弧と左波括弧の間にスペースが1つ必要です。
  • 構造体は一度インデントする必要があります。
  • ボディは左波括弧の後の次の行にある必要があります。
  • 右波括弧は、本文の後の次の行にある必要があります。

各構造の本体は波括弧で囲む必要があります。これにより構造の外観が標準化され、新しい行が本文に追加されるときにエラーが発生する可能性が低くなります。

if, elseif, else

if 構造は次のようになります。括弧、スペース、および波括弧の配置に注意してください。 また、 else と elseif は、前の本文の閉じ括弧と同じ行にあります。

<?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
}

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
}

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
);

for

for ステートメントは次のようになります。括弧、スペース、波括弧の配置に注意してください。

<?php

for ($i = 0; $i < 10; $i++) {
    // for body
}
  • 括弧内の式は複数の行に分割できます。その場合、後続の各行は少なくとも1回インデントされます。そして、最初の式は次の行になければなりません。
  • 閉じ括弧と開き括弧は、それらの間に1つのスペースを入れて、それら自身の行に一緒に配置する必要があります。
<?php

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

foreach

foreach ステートメントは次のようになります。 括弧、スペース、波括弧の配置に注意してください。

<?php

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

try, catch, finally

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

<?php

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

Operators

演算子のスタイルルールは、アリティ(演算子が取るオペランドの数)ごとにグループ化されています。

演算子の周囲にスペースが許可されている場合、読みやすさの目的で複数のスペースを使用できます。

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

単項演算子

増分/減分演算子では、演算子とオペランドの間にスペースを入れないでください。

$i++;

++$j;

型キャスト演算子は、括弧内にスペースがあってはなりません。

$intValue = (int) $input;

二項演算子

全ての二項演算子・比較演算子・代入演算子・ビット演算子・論理演算子・文字列演算子・型演算子の前後には、少なくとも1つのスペースが必要です。

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

条件演算子

条件演算子は、単に三項演算子とも呼ばれ、「?」文字と「:」文字の両方の前後に少なくとも1つのスペースが必要です。

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

条件演算子の中央のオペランドが省略されている場合、演算子は他の比較演算子と同じスタイル規則に従う必要があります。

$variable = $foo ?: 'bar';

Closures

クロージャーは、function キーワードの後にスペースを使用し、use キーワードの前後にスペースを使用して宣言する必要があります。

  • 左波括弧は同じ行に配置する必要があり、右波括弧は本文に続く次の行に配置する必要があります。
  • 引数リストまたは変数リストの左括弧の後にスペースがあってはならず、引数リストまたは変数リストの右括弧の前にスペースがあってはなりません。
  • 引数リストと変数リストでは、各コンマの前にスペースがあってはならず、各コンマの後にスペースが1つなければなりません。
  • デフォルト値を持つクロージャー引数は、引数リストの最後に置く必要があります。
  • 戻り値の型が存在する場合は、通常の関数とメソッドと同じ規則に従う必要があります。use キーワードが存在する場合、コロンは、2つの文字の間にスペースを入れずに、use リストの閉じ括弧の後に続く必要があります。

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

<?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
);

無名クラス

無名(匿名)クラスは、上記のセクションのクロージャーと同じガイドラインと原則に従う必要があります。

$instance = new class {};

実装インターフェースのリストが折り返されない限り、左波括弧は 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
};
Ritolabo