本文件中的关键词“必须”,“不得”,“必须”,“应该”,“不应该”,“应该”,“不应该”,“推荐”,“可以”和“可选”按照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
}
}
代码必须遵循PSR-1中列出的所有规则。
PSR-1中的“StudlyCaps”一词必须解释为PascalCase,其中每个单词的首字母大写,包括第一个字母。
所有PHP文件必须仅使用Unix LF(换行)行结尾。
所有PHP文件必须以非空白行结束,以单个LF结束。
?>
必须从仅包含PHP的文件中省略结束标记。
行长度不得有硬性限制。
行长度的软限制必须是120个字符。
行不应超过80个字符; 超过的行应该被分成多个后续行,每行不超过80个字符。
在行的末尾不得有尾随空格。
可以添加空行以提高可读性并指示相关的代码块,除非明确禁止。
每行不得超过一个语句。
代码必须为每个缩进级别使用4个空格的缩进,并且不得使用制表符进行缩进。
添加到未来PHP版本的任何新类型和关键字必须是小写的。
类型的关键字的简短形式必须是即使用bool
代替boolean
,
int
而不是integer
等
PHP文件的标头可能包含许多不同的块。如果存在,下面的每个块必须用一个空行分隔,并且不得包含空行。尽管可以省略不相关的块,但每个块必须按下面列出的顺序排列。
<?php
标签。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
}
术语“类”指的是所有类,接口和traits。
任何结束括号不得在同一行上跟随任何注释或声明。
在实例化一个新类时,即使没有传递给构造函数的参数,也必须始终存在括号。
new Foo();
在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
}
在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;
}
}
必须在所有属性上声明可见性。
如果您的项目PHP最低版本支持持续可见性(PHP 7.1或更高版本),则必须在所有常量上声明可见性。
该var
关键字不能被用于声明属性。
每个声明不得超过一个属性。
属性名称不得以单个下划线为前缀,以指示受保护或私有可见性。也就是说,下划线前缀明确没有任何意义。
类型声明和属性名称之间必须有一个空格。
属性声明如下所示:
<?php
namespace Vendor\Package;
class ClassName
{
public $foo = null;
public static int $bar = 0;
}
必须在所有方法上声明可见性。
方法名称不得以单个下划线为前缀,以指示受保护或私有可见性。也就是说,下划线前缀明确没有任何意义。
不得在方法名称后用空格声明方法和函数名称。左大括号必须位于自己的行上,而右大括号必须位于正文之后的下一行上。。在左括号后面不能有空格,并且在右括号之前不能有空格。
方法声明如下所示。请注意括号,逗号,空格和大括号的位置:
<?php
namespace Vendor\Package;
class ClassName
{
public function fooBarBaz($arg1, &$arg2, $arg3 = [])
{
// method body
}
}
函数声明如下所示。请注意括号,逗号,空格和大括号的位置:
<?php
function fooBarBaz($arg1, &$arg2, $arg3 = [])
{
// function body
}
在参数列表中,每个逗号之前不得有空格,每个逗号后必须有一个空格。
具有默认值的方法和函数参数必须位于参数列表的末尾。
<?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
}
如果存在,abstract
并final
声明必须先于可见性声明。
如果存在,static
声明必须在可见性声明之后。
<?php
namespace Vendor\Package;
abstract class ClassName
{
protected static $foo;
abstract protected function zim();
final public static function bar()
{
// method body
}
}
在进行方法或函数调用时,方法或函数名称与左括号之间不能有空格,在左括号后面不能有空格,并且在右括号之前不能有空格。在参数列表中,每个逗号之前不得有空格,每个逗号后必须有一个空格。
<?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);
});
控制结构的一般样式规则如下:
每个结构的主体必须用大括号括起来。这标准化了结构的外观,并降低了新行添加到正文中时引入错误的可能性。
一个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
}
一个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
}
一个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
);
一个for
声明如下所示。请注意括号,空格和大括号的位置。
<?php
for ($i = 0; $i < 10; $i++) {
// for body
}
括号中的表达式可以分为多行,其中每个后续行至少缩进一次。这样做时,第一个表达式必须在下一行。右括号和左括号必须放在一起,它们之间有一个空格。
<?php
for (
$i = 0;
$i < 10;
$i++
) {
// for body
}
一个foreach
声明如下所示。请注意括号,空格和大括号的位置。
<?php
foreach ($iterable as $key => $value) {
// foreach body
}
一个try-catch-finally
块如下所示。请注意括号,空格和大括号的位置。
<?php
try {
// try body
} catch (FirstThrowableType $e) {
// catch body
} catch (OtherThrowableType | AnotherThrowableType $e) {
// catch body
} finally {
// finally body
}
运算符的样式规则按arity(它们采用的操作数的数量)进行分组。
当运算符周围允许空间时,可以使用多个空格用于可读性目的。
此处未描述的所有运算符都未定义。
递增/递减运算符在运算符和操作数之间不能有任何空格。
类型转换运算符在括号内不能有任何空格:
$intValue = (int) $input;
所有二元算术,比较,赋值,按位, 逻辑,字符串和类型运算符必须至少前后一个空格:
if ($a === $b) {
$foo = $bar ?? $a ?? $b;
} elseif ($a > $b) {
$foo = $a + $b * $c;
}
条件运算符,也简称为三元运算符,必须在?
和:
字符周围至少有一个空格:
$variable = $foo ? 'foo' : 'bar';
当省略条件运算符的中间操作数时,运算符必须遵循与其他二元比较运算符相同的样式规则:
$variable = $foo ?: 'bar';
闭包必须在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
);
匿名类必须遵循与上一节中的闭包相同的准则和原则。
<?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
};