此规范起到继承,扩展和替换 PSR-2 的作用, 同时编码风格遵守 PSR-1 这个基础编码标准。
与 PSR-2 一样, 此规范的目的是减少不同人在阅读代码时认知冲突。 它通过列举一套如何格式化 PHP
代码的公共的规则和期望来实现这个目标。 PSR
力图提供一套方法,编码风格工具可以利用,项目可以遵守,开发人员可以方便的在不同的项目中使用。当各个的开发人员在进行多项目合作的时候,它可以帮助在这些项目中提供一套通用的指导。所以,本指南的价值不是规则本身,而是这些规则的共享。 PSR-2 在 2012 年被接受,随后 PHP
经历了很多变化,影响了编码风格。同时 PSR-2 是 PHP
编码时候的基础功能,被广泛的采用。因此,PSR
力图通过一种更加现代的方式说明 PSR-2 的内容和新功能,并对 PSR-2 进行更正。
在整个文档中,任何说明都可以被忽略,如果它们不存在于你项目所支持的 PHP
版本中。
此示例包含以下一些规则作为快速概述:
1<?php
2
3declare(strict_types=1);
4
5namespace Vendor\Package;
6
7use Vendor\Package\{ClassA as A, ClassB, ClassC as C};
8use Vendor\Package\SomeNamespace\ClassD as D;
9
10use function Vendor\Package\{functionA, functionB, functionC};
11
12use const Vendor\Package\{ConstantA, ConstantB, ConstantC};
13
14class Foo extends Bar implements FooInterface
15{
16 public function sampleFunction(int $a, int $b = null): array
17 {
18 if ($a === $b) {
19 bar();
20 } elseif ($a > $b) {
21 $foo->bar($arg1);
22 } else {
23 BazClass::bar($arg2, $arg3);
24 }
25 }
26
27 final public static function bar()
28 {
29 // 方法内容
30 }
31}
代码必须遵循 PSR-1 中列出的所有规则。 PSR-1 中的术语 StudlyCaps
必须解释为 PascalCase
(帕斯卡命名法:大驼峰式命名法),其中每个单词的第一个字母大写,包括第一个字母。
PHP
文件只能使用 Unix LF (换行符)
结尾。PHP
文件都必须以非空行结尾,以一个 LF
结尾。PHP
代码的文件中,必须省略结尾的 ?>
标记。120
个字符。80
个字符;超过该长度的行应拆分为多个后续行,每个行的长度不应超过 80
个字符。代码必须为每个缩进级别使用 4
个空格的缩进,并且不能使用缩进标签。
PHP
的所有关键字和类型 12 都必须使用小写。PHP
未来版本中新加的所有关键字和类型也都必须使用小写。 类型关键字必须使用缩写。使用 bool
而不是 boolean
,使用 int
而不是 integer
等等。一个 PHP
文件的头部可能会包含多个块。如果包含多个块,则每个块都必须用空白行和其他块分隔,并且块内不能包含空白行。所有的块都必须按照下面的顺序排列,如果不存在该块则忽略。
当文件包含 HTML
和 PHP
的混合代码时,可以使用上面列出的任何部分。如果是这种情况的话,即时代码的其他部分包含有 PHP
结束符,然后再包含 HTML
和 PHP
代码,声明、命名空间和导入语句块也必须放在文件的顶部。
什么时候开始 <?php
标签位于文件的第一行,它必须位于自己的行,没有其他语句,除非它是一个包含 PHP
之外的标记的文件打开和关闭标记。
import
语句不能以前导反斜杠开头,因为它们必须始终完全合格。 以下示例演示了所有块的完整列表:
1<?php
2
3/**
4 * This file contains an example of coding styles.
5 */
6
7declare(strict_types=1);
8
9namespace Vendor\Package;
10
11use Vendor\Package\{ClassA as A, ClassB, ClassC as C};
12use Vendor\Package\SomeNamespace\ClassD as D;
13use Vendor\Package\AnotherNamespace\ClassE as E;
14
15use function Vendor\Package\{functionA, functionB, functionC};
16use function Another\Vendor\functionD;
17
18use const Vendor\Package\{CONSTANT_A, CONSTANT_B, CONSTANT_C};
19use const Another\Vendor\CONSTANT_D;
20
21/**
22 * FooBar is an example class.
23 */
24class FooBar
25{
26 // ... additional PHP code ...
27}
不得使用深度超过两层的复合名称空间,因此以下展示了允许的最大复合深度。
1<?php
2
3use Vendor\Package\SomeNamespace\{
4 SubnamespaceOne\ClassA,
5 SubnamespaceOne\ClassB,
6 SubnamespaceTwo\ClassY,
7 ClassZ,
8};
并且不允许以下内容:
1<?php
2
3use Vendor\Package\SomeNamespace\{
4 SubnamespaceOne\AnotherNamespace\ClassA,
5 SubnamespaceOne\ClassB,
6 ClassZ,
7};
当希望在 PHP
外部包含标记的文件中声明严格类型时打开和关闭标签,声明必须写在文件的第一行并且包含在一个开始的 PHP
标签,以及严格的类型声明和结束标签。 例如:
1<?php declare(strict_types=1) ?>
2<html>
3<body>
4 <?php
5 // ... additional PHP code ...
6 ?>
7</body>
8</html>
声明语句不能包含空格,并且必须完全是 declare(strict_types=1)
(带有可选的分号终止符)。 允许使用块声明语句,并且必须按照以下的格式设置。注意的位置括号和间距:
1declare(ticks=1) {
2 // 一些代码
3}
这里的 类
指的是所有类
,接口
,以及 trait
。
任何注释和语句 不得 跟在其右花括号后的同一行。
当实例化一个类时,后面的圆括号 必须 写出来,即使没有参数传进其构造函数。
1new Foo();
extends
和 implments
必须 在类名的同一行声明。 1<?php
2
3namespace Vendor\Package;
4
5use FooClass;
6use BarClass as Bar;
7use OtherVendor\OtherPackage\BazClass;
8
9class ClassName extends ParentClass implements \ArrayAccess, \Countable
10{
11 // 常量,属性,方法
12}
如果有接口, implements
接口和 extends
父类 可以 分为多行,前者每行需缩进一次。当这么做时,第一个接口 必须 写在下一行,且每行 必须 只能写一个接口。
1<?php
2
3namespace Vendor\Package;
4
5use FooClass;
6use BarClass as Bar;
7use OtherVendor\OtherPackage\BazClass;
8
9class ClassName extends ParentClass implements
10 \ArrayAccess,
11 \Countable,
12 \Serializable
13{
14 // 常量,属性,方法
15}
在类里面用于实现 trait
的关键字 use
必须 在左花括号的下一行声明。
1<?php
2
3namespace Vendor\Package;
4
5use Vendor\Package\FirstTrait;
6
7class ClassName
8{
9 use FirstTrait;
10}
每个导入类的 trait 必须 每行一个包含声明,且每个包含声明 必须 有其 use
导入语句。
1<?php
2
3namespace Vendor\Package;
4
5use Vendor\Package\FirstTrait;
6use Vendor\Package\SecondTrait;
7use Vendor\Package\ThirdTrait;
8
9class ClassName
10{
11 use FirstTrait;
12 use SecondTrait;
13 use ThirdTrait;
14}
在类文件中,如果在使用 use Trait
之后没有其他内容了 ,类名右大括号必须另起一行。
1<?php
2
3namespace Vendor\Package;
4
5use Vendor\Package\FirstTrait;
6
7class ClassName
8{
9 use FirstTrait;
10}
如有其他内容,两者之间需空一行。
1<?php
2
3namespace Vendor\Package;
4
5use Vendor\Package\FirstTrait;
6
7class ClassName
8{
9 use FirstTrait;
10
11 private $property;
12}
当使用 insteadof
和 as
运算符时,它们必须如图所示使用,注意缩进、间距和另起一行。
1<?php
2
3class Talker
4{
5 use A, B, C {
6 B::smallTalk insteadof A;
7 A::bigTalk insteadof C;
8 C::mediumTalk as FooBar;
9 }
10}
PHP
最小版本支持常量可见性( PHP 7.1 或以上),所有常量 必须 声明可见性。var
不得 用于声明属性。一个属性声明看上去如下所示:
1<?php
2
3namespace Vendor\Package;
4
5class ClassName
6{
7 public $foo = null;
8 public static int $bar = 0;
9}
protected
或 private
类型。也就是说,不要用一个没有意义的下划线开头。一个方法的声明应该如下所示。注意括号,逗号,空格和花括号的位置:
1<?php
2
3namespace Vendor\Package;
4
5class ClassName
6{
7 public function fooBarBaz($arg1, &$arg2, $arg3 = [])
8 {
9 // 方法主体
10 }
11}
一个函数的声明应该如下所示。注意括号,逗号,空格和花括号的位置:
1<?php
2
3function fooBarBaz($arg1, &$arg2, $arg3 = [])
4{
5 // 函数主体
6}
在参数列表中, 不得 在每个逗号前存在空格,且 必须 在每个逗号后有一个空格。 方法和函数中带有默认值的参数 必须 放在参数列表的最后。
1<?php
2
3namespace Vendor\Package;
4
5class ClassName
6{
7 public function foo(int $arg1, &$arg2, $arg3 = [])
8 {
9 // 方法主体
10 }
11}
参数列表 可以 分为多行,每行参数缩进一次。当这么做时,第一个参数 必须 放在下一行,且每行 必须 只能有一个参数。
当参数列表分成多行时,右圆括号和左花括号 必须 放在同一行且单独成行,两者之间存在一个空格。
1<?php
2
3namespace Vendor\Package;
4
5class ClassName
6{
7 public function aVeryLongMethodName(
8 ClassTypeHint $arg1,
9 &$arg2,
10 array $arg3 = []
11 ) {
12 // 方法主体
13 }
14}
当你定义一个返回值类型声明时,冒号后面的类型声明 必须 用空格符隔开。冒号和声明 必须 在同一行,且跟参数列表后的结束括号之间没有空格。
1<?php
2
3declare(strict_types=1);
4
5namespace Vendor\Package;
6
7class ReturnTypeVariations
8{
9 public function functionName(int $arg1, $arg2): string
10 {
11 return 'foo';
12 }
13
14 public function anotherFunction(
15 string $foo,
16 string $bar,
17 int $baz
18 ): string {
19 return 'foo';
20 }
21}
在可空类型声明中,问号和类型声明之间不能有空格。
1<?php
2
3declare(strict_types=1);
4
5namespace Vendor\Package;
6
7class ReturnTypeVariations
8{
9 public function functionName(?string $arg1, ?int &$arg2): ?string
10 {
11 return 'foo';
12 }
13}
当在参数之前使用引用运算符 &
时,引用运算符之后不能有空格,例如上面的示例。
可变参数声明的三个点和参数名称之间不能有空格:
1public function process(string $algorithm, ...$parts)
2{
3 // 函数体
4}
当同时使用引用运算符和可变参数运算符时,它们之间不能有任何空格:
1public function process(string $algorithm, &...$parts)
2{
3 // 函数体
4}
abstract
, final
, static
如果是 abstract
, final
,那么申明的时候必须是可见性声明。 如果是 static
,声明必须位于可见性声明之后。
1<?php
2
3namespace Vendor\Package;
4
5abstract class ClassName
6{
7 protected static $foo;
8
9 abstract protected function zim();
10
11 final public static function bar()
12 {
13 // 请求体
14 }
15}
当我们在进行方法或者函数调用的时候,方法名或函数名与左括号之间不能出现空格,在右括号之后也不能出现空格,并且在右括号之前也不能有空格。在参数列表中,每个逗号前面不能有空格,每个逗号后面必须有一个空格。
1<?php
2
3bar();
4$foo->bar($arg1);
5Foo::bar($arg2, $arg3);
参数列表可以分为多行,每行后面缩进一次。这样做时,列表中的第一项必须位于下一行,并且每一行必须只有一个参数。跨多个行拆分单个参数 (就像匿名函数或者数组那样) 并不构成拆分参数列表本身。
1<?php
2
3$foo->bar(
4 $longArgument,
5 $longerArgument,
6 $muchLongerArgument
7);
1<?php
2
3somefunction($foo, $bar, [
4 // ...
5], $baz);
6
7$app->get('/hello/{name}', function ($name) use ($app) {
8 return 'Hello ' . $app->escape($name);
9});
如下是主要的流程控制风格规则:
每个流程控制主体 必须 以封闭的括号结束。这将标准化流程结构,同时减少由于流程中添加新的内容而引入错误的可能性。
if
结构如下。注意括号,空格,和大括号的位置;else
和 elseif
都在同一行,和右大括号一样在主体的前面。
1<?php
2
3if ($expr1) {
4 // if body
5} elseif ($expr2) {
6 // elseif body
7} else {
8 // else body;
9}
应该 使用关键词 elseif
替换 else if
,这样所有的控制关键词看起来都像单个词。
括号中的表达式 可能 会被分开为多行,每一行至少缩进一次。如果这样做,第一个条件 必须 在新的一行。右括号和左大括号 必须 在同一行,而且中间有一个空格。条件中间的布尔控制符 必须 在每一行的开头或者结尾,而不是混在一起。
1<?php
2
3if (
4 $expr1
5 && $expr2
6) {
7 // if body
8} elseif (
9 $expr3
10 && $expr4
11) {
12 // elseif body
13}
switch
结构如下。注意括号,空格和大括号的位置。case 必须 缩进一次从 switch
开始, break
关键词 (或者其他终止关键词) 必须 缩进和 case
主体保持一致。必须 要有一个像 // no break
这样的注释在不为空且不需要中断的 case 主体之中。
1<?php
2
3switch ($expr) {
4 case 0:
5 echo 'First case, with a break';
6 break;
7 case 1:
8 echo 'Second case, which falls through';
9 // no break
10 case 2:
11 case 3:
12 case 4:
13 echo 'Third case, return instead of break';
14 return;
15 default:
16 echo 'Default case';
17 break;
18}
括号中的表达式 可能 会被分开多行,每一行至少要缩进一次。如果这样做,第一个条件 必须 在新的一行。右括号和左大括号 必须 在同一行,而且中间有一个空格。条件中间的布尔控制符 必须 在一行的开头或者结尾,而不是混在一起。
1<?php
2
3switch (
4 $expr1
5 && $expr2
6) {
7 // structure body
8}
while
结构如下。注意括号,空格和大括号的位置。
1<?php
2
3while ($expr) {
4 // structure body
5}
括号中的表达式 可能 会被分开多行,每一行至少要缩进一次。如果这样做,第一个条件 必须 在新的一行。右括号和左大括号 必须 在同一行,而且中间有一个空格。条件中间的布尔控制符 必须 在每一行的开头或者结尾,而不是混在一起。
1<?php
2
3while (
4 $expr1
5 && $expr2
6) {
7 // structure body
8}
同样的, do while
申明如下。注意括号,空格和大括号的位置。
1<?php
2
3do {
4 // structure body;
5} while ($expr);
括号中的表达式 可能 会被分开多行,每一行至少要缩进一次。如果这样做,第一个条件 必须 在新的一行。条件中间的布尔控制符 必须 在每一行的开头或者结尾,而不是混在一起。
1<?php
2
3do {
4 // structure body;
5} while (
6 $expr1
7 && $expr2
8);
for
申明如下。注意括号,空格和大括号的位置。
1<?php
2
3for ($i = 0; $i < 10; $i++) {
4 // for body
5}
括号中的表达式 可能 会被分开多行,每一行至少要缩进一次。如果这样做,第一个条件 必须 在新的一行。右括号和左大括号 必须 在同一行,而且中间有一个空格。
1<?php
2
3for (
4 $i = 0;
5 $i < 10;
6 $i++
7) {
8 // for body
9}
foreach
语句的写法如下所示。请注意它的圆括号、空格和花括号。
1<?php
2
3foreach ($iterable as $key => $value) {
4 // 迭代主体
5}
一个 try-catch-finally
模块包含下面这些内容。请注意它的圆括号、空格和花括号。
1<?php
2
3try {
4 // try 主体
5} catch (FirstThrowableType $e) {
6 // 捕获异常主体
7} catch (OtherThrowableType | AnotherThrowableType $e) {
8 // 捕获异常主体
9} finally {
10 // finally 主体
11}
递增
/ 递减
运算符和操作数之间 不得 有任何空格。
1$i++;
2++$j;
类型转换运算符的圆括号内部 不得 有任何空格:
1$intValue = (int) $input;
所有二进制 算术,比较,赋值,按位,逻辑、字符串和类型运算符必须在前后跟至少一个空格:
1if ($a === $b) {
2 $foo = $bar ?? $a ?? $b;
3} elseif ($a > $b) {
4 $foo = $a + $b * $c;
5}
条件运算符,也称为三元运算符,必须在 ?
和 :
这两个字符之间:
1$variable = $foo ? 'foo' : 'bar';
如果省略条件运算符的中间操作数,运算符必须遵循与其他二进制比较运算符相同的样式规则:
1$variable = $foo ?: 'bar';
function
关键字后留有 1
个空格,并且在 use
关键字前后各留有 1
个空格。use
关键字,冒号必须在 use
右括号后且冒号前后不能有空格。闭包的声明方式如下,留意括号,逗号,空格和花括号:
1<?php
2
3$closureWithArgs = function ($arg1, $arg2) {
4 // 函数体
5};
6
7$closureWithArgsAndVars = function ($arg1, $arg2) use ($var1, $var2) {
8 // 函数体
9};
10
11$closureWithArgsVarsAndReturn = function ($arg1, $arg2) use ($var1, $var2): bool {
12 // 函数体
13};
参数和变量可以分多行放置,每个后续行缩进一次。执行此操作时,列表中的第一项 必须 放在下一行,并且每行只能有一个参数或变量。
结束多行列表(或者参数,变量)的时候,右括号和左大括号 必须 要放在一行,而且中间有一个空格。
下面是有和没有多行参数列表与变量列表的闭包示例。
1<?php
2
3$longArgs_noVars = function (
4 $longArgument,
5 $longerArgument,
6 $muchLongerArgument
7) {
8 // body
9};
10
11$noArgs_longVars = function () use (
12 $longVar1,
13 $longerVar2,
14 $muchLongerVar3
15) {
16 // body
17};
18
19$longArgs_longVars = function (
20 $longArgument,
21 $longerArgument,
22 $muchLongerArgument
23) use (
24 $longVar1,
25 $longerVar2,
26 $muchLongerVar3
27) {
28 // body
29};
30
31$longArgs_shortVars = function (
32 $longArgument,
33 $longerArgument,
34 $muchLongerArgument
35) use ($var1) {
36 // body
37};
38
39$shortArgs_longVars = function ($arg) use (
40 $longVar1,
41 $longerVar2,
42 $muchLongerVar3
43) {
44 // body
45};
注意格式化规则也适用一个闭包在一个方法或者操作中作为参数被直接引用。
1<?php
2
3$foo->bar(
4 $arg1,
5 function ($arg2) use ($var1) {
6 // body
7 },
8 $arg3
9);
匿名类 必须 遵循上面章节中和闭包一样的方针和准则。
1<?php
2
3$instance = new class {};
只要 implements
接口列表不换行,左花括号 可以 和关键字 class 在同一行。如果接口列表换行,花括号 必须 放在最后一个接口的下一行。
1<?php
2
3// 花括号在同一行
4$instance = new class extends \Foo implements \HandleableInterface {
5 // 类内容
6};
7
8// 花括号在下一行
9$instance = new class extends \Foo implements
10 \ArrayAccess,
11 \Countable,
12 \Serializable
13{
14 // 类内容
15};