ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 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 }; ~~~