# PSR-12:擴展編碼風格
本文件中的關鍵詞“必須”,“不得”,“必須”,“應該”,“不應該”,“應該”,“不應該”,“推薦”,“可以”和“可選”按照[RFC 2119中的](http://tools.ietf.org/html/rfc2119)描述進行解釋。
## [概觀](http://phpfig.p2hp.com/psr/psr-12/#overview)
該規范擴展,擴充和替換[PSR-2](http://phpfig.p2hp.com/psr/psr-2/)編碼風格指南,并要求遵守基本編碼標準[PSR-1](http://phpfig.p2hp.com/psr/psr-1/)。
### [以前的語言版本](http://phpfig.p2hp.com/psr/psr-12/#previous-language-versions)
在本文檔中,如果項目支持的PHP版本中不存在任何指令,則可以忽略這些指令。
### [例子](http://phpfig.p2hp.com/psr/psr-12/#example)
此示例包含以下一些規則作為快速概述:
~~~
<?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.一般](http://phpfig.p2hp.com/psr/psr-12/#2-general)
### [2.1基本編碼標準](http://phpfig.p2hp.com/psr/psr-12/#21-basic-coding-standard)
代碼必須遵循[PSR-1中](http://phpfig.p2hp.com/psr/psr-1/)列出的所有規則。
PSR-1中的“StudlyCaps”一詞必須解釋為PascalCase,其中每個單詞的首字母大寫,包括第一個字母。
### [2.2文件](http://phpfig.p2hp.com/psr/psr-12/#22-files)
所有PHP文件必須僅使用Unix LF(換行)行結尾。
所有PHP文件必須以非空白行結束,以單個LF結束。
`?>`必須從僅包含PHP的文件中省略結束標記。
### [2.3行](http://phpfig.p2hp.com/psr/psr-12/#23-lines)
行長度不得有硬性限制。
行長度的軟限制必須是120個字符。
行不應超過80個字符;超過的行應該被分成多個后續行,每行不超過80個字符。
在行的末尾不得有尾隨空格。
可以添加空行以提高可讀性并指示相關的代碼塊,除非明確禁止。
每行不得超過一個語句。
### [2.4縮進](http://phpfig.p2hp.com/psr/psr-12/#24-indenting)
代碼必須為每個縮進級別使用4個空格的縮進,并且不得使用制表符進行縮進。
### [2.5關鍵字和類型](http://phpfig.p2hp.com/psr/psr-12/#25-keywords-and-types)
所有PHP保留的關鍵字和類型[\[1\]](http://php.net/manual/en/reserved.keywords.php)[\[2\]](http://php.net/manual/en/reserved.other-reserved-words.php)必須是小寫的。
添加到未來PHP版本的任何新類型和關鍵字必須是小寫的。
類型的關鍵字的簡短形式必須是即使用`bool`代替`boolean`,`int`而不是`integer`等
## [3.聲明語句,命名空間和導入語句](http://phpfig.p2hp.com/psr/psr-12/#3-declare-statements-namespace-and-import-statements)
PHP文件的標頭可能包含許多不同的塊。如果存在,下面的每個塊必須用一個空行分隔,并且不得包含空行。盡管可以省略不相關的塊,但每個塊必須按下面列出的順序排列。
* 打開`<?php`標簽。
* 文件級docblock。
* 一個或多個聲明語句。
* 文件的名稱空間聲明。
* 一個或多個基于類的`use`import語句。
* 一個或多個基于函數的`use`import語句。
* 一個或多個基于常量的`use`import語句。
* 文件中的其余代碼。
當文件包含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開始和結束標記之外的標記的文件中聲明嚴格類型時,聲明必須位于文件的第一行,并包含一個開始的PHP標記,嚴格類型聲明和結束標記。
例如:
~~~
<?php declare(strict_types=1) ?>
<html>
<body>
<?php
// ... additional PHP code ...
?>
</body>
</html>
~~~
聲明語句必須不包含空格,并且必須完全`declare(strict_types=1)`(使用可選的分號終止符)。
允許塊聲明語句,并且必須格式化如下。注意括號和間距的位置:
~~~
declare(ticks=1) {
// some code
}
~~~
## [4.類,屬性和方法](http://phpfig.p2hp.com/psr/psr-12/#4-classes-properties-and-methods)
術語“類”指的是所有類,接口和traits。
任何結束括號不得在同一行上跟隨任何注釋或聲明。
在實例化一個新類時,即使沒有傳遞給構造函數的參數,也必須始終存在括號。
~~~
new Foo();
~~~
### [4.1繼承和實現](http://phpfig.p2hp.com/psr/psr-12/#41-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`可以分為多行,每行后續行縮進一次。這樣做時,列表中的第一項必須在下一行,并且每行必須只有一個接口。
~~~
<?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使用traits](http://phpfig.p2hp.com/psr/psr-12/#42-using-traits)
在`use`實現traits的類中使用的關鍵字必須在左括號后的下一行聲明。
~~~
<?php
namespace Vendor\Package;
use Vendor\Package\FirstTrait;
class ClassName
{
use FirstTrait;
}
~~~
導入到類中的每個單獨的Trait必須包含在每行一個,每個包含必須有自己的`use`import語句。
~~~
<?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`import語句之后沒有任何內容時,類右括號必須在`use`import語句之后的下一行。
~~~
<?php
namespace Vendor\Package;
use Vendor\Package\FirstTrait;
class ClassName
{
use FirstTrait;
}
~~~
否則,它必須在`use`import語句后面有一個空行。
~~~
<?php
namespace Vendor\Package;
use Vendor\Package\FirstTrait;
class ClassName
{
use FirstTrait;
private $property;
}
~~~
使用`insteadof`和`as`運算符時,必須按如下方式使用它們,記下縮進,間距和新行。
~~~
<?php
class Talker
{
use A, B, C {
B::smallTalk insteadof A;
A::bigTalk insteadof C;
C::mediumTalk as FooBar;
}
}
~~~
### [4.3屬性和常量](http://phpfig.p2hp.com/psr/psr-12/#43-properties-and-constants)
必須在所有屬性上聲明可見性。
如果您的項目PHP最低版本支持持續可見性(PHP 7.1或更高版本),則必須在所有常量上聲明可見性。
該`var`關鍵字不能被用于聲明屬性。
每個聲明不得超過一個屬性。
屬性名稱不得以單個下劃線為前綴,以指示受保護或私有可見性。也就是說,下劃線前綴明確沒有任何意義。
類型聲明和屬性名稱之間必須有一個空格。
屬性聲明如下所示:
~~~
<?php
namespace Vendor\Package;
class ClassName
{
public $foo = null;
public static int $bar = 0;
}
~~~
### [4.4方法和函數](http://phpfig.p2hp.com/psr/psr-12/#44-methods-and-functions)
必須在所有方法上聲明可見性。
方法名稱不得以單個下劃線為前綴,以指示受保護或私有可見性。也就是說,下劃線前綴明確沒有任何意義。
不得在方法名稱后用空格聲明方法和函數名稱。左大括號必須位于自己的行上,而右大括號必須位于正文之后的下一行上。。在左括號后面不能有空格,并且在右括號之前不能有空格。
方法聲明如下所示。請注意括號,逗號,空格和大括號的位置:
~~~
<?php
namespace Vendor\Package;
class ClassName
{
public function fooBarBaz($arg1, &$arg2, $arg3 = [])
{
// method body
}
}
~~~
函數聲明如下所示。請注意括號,逗號,空格和大括號的位置:
~~~
<?php
function fooBarBaz($arg1, &$arg2, $arg3 = [])
{
// function body
}
~~~
### [4.5方法和函數參數](http://phpfig.p2hp.com/psr/psr-12/#45-method-and-function-arguments)
在參數列表中,每個逗號之前不得有空格,每個逗號后必須有一個空格。
具有默認值的方法和函數參數必須位于參數列表的末尾。
~~~
<?php
namespace Vendor\Package;
class ClassName
{
public function foo(int $arg1, &$arg2, $arg3 = [])
{
// method body
}
}
~~~
參數列表可以分為多行,每行后續行縮進一次。這樣做時,列表中的第一項必須在下一行,并且每行必須只有一個參數。
當參數列表分成多行時,右括號和左括號必須放在一起,它們各自之間有一個空格。
~~~
<?php
namespace Vendor\Package;
class ClassName
{
public function aVeryLongMethodName(
ClassTypeHint $arg1,
&$arg2,
array $arg3 = []
) {
// method body
}
}
~~~
如果存在返回類型聲明,則冒號后面必須有一個空格,后跟類型聲明。冒號和聲明必須與參數列表右括號位于同一行,兩個字符之間沒有空格。
~~~
<?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';
}
}
~~~
在可空類型聲明中,問號和類型之間不能有空格。
~~~
<?php
declare(strict_types=1);
namespace Vendor\Package;
class ReturnTypeVariations
{
public function functionName(?string $arg1, ?int &$arg2): ?string
{
return 'foo';
}
}
~~~
在`&`參數之前使用引用運算符時,其后面不能有空格,就像前面的例子一樣。
在可變三點運算符和參數名稱之間不能有空格:
~~~
public function process(string $algorithm, ...$parts)
{
// processing
}
~~~
當組合引用運算符和可變三點運算符時,它們之間不能有任何空格:
~~~
public function process(string $algorithm, &...$parts)
{
// processing
}
~~~
### [4.6abstract,final和static](http://phpfig.p2hp.com/psr/psr-12/#46-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
}
}
~~~
### [4.7方法和函數調用](http://phpfig.p2hp.com/psr/psr-12/#47-method-and-function-calls)
在進行方法或函數調用時,方法或函數名稱與左括號之間不能有空格,在左括號后面不能有空格,并且在右括號之前不能有空格。在參數列表中,每個逗號之前不得有空格,每個逗號后必須有一個空格。
~~~
<?php
bar();
$foo->bar($arg1);
Foo::bar($arg2, $arg3);
~~~
參數列表可以分為多行,每行后續行縮進一次。這樣做時,列表中的第一項必須在下一行,并且每行必須只有一個參數。跨多行分割的單個參數(可能是匿名函數或數組的情況)不構成拆分參數列表本身。
~~~
<?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.控制結構](http://phpfig.p2hp.com/psr/psr-12/#5-control-structures)
控制結構的一般樣式規則如下:
* 控制結構關鍵字后面必須有一個空格
* 在左括號后面不能有空格
* 在右括號之前不能有空格
* 在右括號和左括號之間必須有一個空格
* 結構體必須縮進一次
* 主體必須在左大括號后的下一行
* 右括號必須位于正文之后的下一行
每個結構的主體必須用大括號括起來。這標準化了結構的外觀,并降低了新行添加到正文中時引入錯誤的可能性。
### [5.1 if,elseif,else](http://phpfig.p2hp.com/psr/psr-12/#51-if-elseif-else)
一個`if`結構如下所示。注意括號,空格和大括號的位置;并且`else`與`elseif`與上一正文中的右括號位于同一行。。
~~~
<?php
if ($expr1) {
// if body
} elseif ($expr2) {
// elseif body
} else {
// else body;
}
~~~
應該使用`elseif`關鍵字而不是`else if`使所有控制關鍵字看起來像單個單詞。
括號中的表達式可以分為多行,其中每個后續行至少縮進一次。這樣做時,第一個條件必須在下一行。右括號和左括號必須放在一起,它們之間有一個空格。條件之間的布爾運算符必須始終位于行的開頭或結尾,而不是兩者的混合。
~~~
<?php
if (
$expr1
&& $expr2
) {
// if body
} elseif (
$expr3
&& $expr4
) {
// elseif body
}
~~~
### [5.2 switch, case](http://phpfig.p2hp.com/psr/psr-12/#52-switch-case)
一個`switch`結構如下所示。請注意括號,空格和大括號的位置。`case`語句必須從`switch`縮進一次,并且`break`關鍵字(或其他終止關鍵字)必須縮進與`case`正文相同的級別。必須有一個注釋,例如`// no break`在非空`case`體中有意識地跳過時。
~~~
<?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;
}
~~~
括號中的表達式可以分為多行,其中每個后續行至少縮進一次。這樣做時,第一個條件必須在下一行。右括號和左括號必須放在一起,它們之間有一個空格。條件之間的布爾運算符必須始終位于行的開頭或結尾,而不是兩者的混合。
~~~
<?php
switch (
$expr1
&& $expr2
) {
// structure body
}
~~~
### [5.3while, do while](http://phpfig.p2hp.com/psr/psr-12/#53-while-do-while)
一個`while`聲明如下所示。請注意括號,空格和大括號的位置。
~~~
<?php
while ($expr) {
// structure body
}
~~~
括號中的表達式可以分為多行,其中每個后續行至少縮進一次。這樣做時,第一個條件必須在下一行。右括號和左括號必須放在一起,它們之間有一個空格。條件之間的布爾運算符必須始終位于行的開頭或結尾,而不是兩者的混合。
~~~
<?php
while (
$expr1
&& $expr2
) {
// structure body
}
~~~
同樣,`do while`語句如下所示。請注意括號,空格和大括號的位置。
~~~
<?php
do {
// structure body;
} while ($expr);
~~~
括號中的表達式可以分為多行,其中每個后續行至少縮進一次。這樣做時,第一個條件必須在下一行。條件之間的布爾運算符必須始終位于行的開頭或結尾,而不是兩者的混合。
~~~
<?php
do {
// structure body;
} while (
$expr1
&& $expr2
);
~~~
### [5.4 for](http://phpfig.p2hp.com/psr/psr-12/#54-for)
一個`for`聲明如下所示。請注意括號,空格和大括號的位置。
~~~
<?php
for ($i = 0; $i < 10; $i++) {
// for body
}
~~~
括號中的表達式可以分為多行,其中每個后續行至少縮進一次。這樣做時,第一個表達式必須在下一行。右括號和左括號必須放在一起,它們之間有一個空格。
~~~
<?php
for (
$i = 0;
$i < 10;
$i++
) {
// for body
}
~~~
### [5.5 foreach](http://phpfig.p2hp.com/psr/psr-12/#55-foreach)
一個`foreach`聲明如下所示。請注意括號,空格和大括號的位置。
~~~
<?php
foreach ($iterable as $key => $value) {
// foreach body
}
~~~
### [5.6try, catch, finally](http://phpfig.p2hp.com/psr/psr-12/#56-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.運算符](http://phpfig.p2hp.com/psr/psr-12/#6-operators)
運算符的樣式規則按arity(它們采用的操作數的數量)進行分組。
當運算符周圍允許空間時,可以使用多個空格用于可讀性目的。
此處未描述的所有運算符都未定義。
### [6.1。單一運算符](http://phpfig.p2hp.com/psr/psr-12/#61-unary-operators)
遞增/遞減運算符在運算符和操作數之間不能有任何空格。
類型轉換運算符在括號內不能有任何空格:
~~~
$intValue = (int) $input;
~~~
### [6.2。二元運算符](http://phpfig.p2hp.com/psr/psr-12/#62-binary-operators)
所有二元[算術](http://php.net/manual/en/language.operators.arithmetic.php),[比較](http://php.net/manual/en/language.operators.comparison.php),[賦值](http://php.net/manual/en/language.operators.assignment.php),[按位](http://php.net/manual/en/language.operators.bitwise.php),[邏輯](http://php.net/manual/en/language.operators.logical.php),[字符串](http://php.net/manual/en/language.operators.string.php)和[類型](http://php.net/manual/en/language.operators.type.php)運算符必須至少前后一個空格:
~~~
if ($a === $b) {
$foo = $bar ?? $a ?? $b;
} elseif ($a > $b) {
$foo = $a + $b * $c;
}
~~~
### [6.3。三元運算符](http://phpfig.p2hp.com/psr/psr-12/#63-ternary-operators)
條件運算符,也簡稱為三元運算符,必??須在`?`和`:`字符周圍至少有一個空格:
~~~
$variable = $foo ? 'foo' : 'bar';
~~~
當省略條件運算符的中間操作數時,運算符必須遵循與其他二元[比較運算符](http://php.net/manual/en/language.operators.comparison.php)相同的樣式規則:
~~~
$variable = $foo ?: 'bar';
~~~
## [7.閉包](http://phpfig.p2hp.com/psr/psr-12/#7-closures)
閉包必須在`function`關鍵字之后用空格聲明,并在關鍵字之前和之后用空格聲明`use`。
左大括號必須位于同一行上,而右大括號必須位于正文之后的下一行上。
在參數列表或變量列表的左括號之后不能有空格,并且在參數列表或變量列表的右括號之前不能有空格。
在參數列表和變量列表中,每個逗號前不能有空格,每個逗號后必須有一個空格。
具有默認值的閉包參數必須位于參數列表的末尾。
如果存在返回類型,則必須遵循與正常函數和方法相同的規則;如果`use`關鍵字存在,冒號必須遵循`use`列表右括號,兩個字符之間沒有空格。
閉包聲明如下所示。請注意括號,逗號,空格和大括號的位置:
~~~
<?php
$closureWithArgs = function ($arg1, $arg2) {
// body
};
$closureWithArgsAndVars = function ($arg1, $arg2) use ($var1, $var2) {
// body
};
$closureWithArgsVarsAndReturn = function ($arg1, $arg2) use ($var1, $var2): bool {
// body
};
~~~
參數列表和變量列表可以分為多行,每行后續行縮進一次。這樣做時,列表中的第一項必須在下一行,并且每行必須只有一個參數或變量。
當結束列表(無論是參數還是變量)被分割成多行時,右括號和左括號必須放在一起,在它們自己的行上,它們之間有一個空格。
以下是包含和不包含參數列表的閉包的示例,以及跨多行分割的變量列表。
~~~
<?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.匿名類](http://phpfig.p2hp.com/psr/psr-12/#8-anonymous-classes)
匿名類必須遵循與上一節中的閉包相同的準則和原則。
~~~
<?php
$instance = new class {};
~~~
`class`只要`implements`接口列表不換行,左大括號就可以與關鍵字位于同一行。如果接口列表換行,則必須將括號放在緊接最后一個接口后面的行上。
~~~
<?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
};
~~~