PSR-12:扩展编码风格

扩展编码风格指南

本文件中的关键词“必须”,“不得”,“必须”,“应该”,“不应该”,“应该”,“不应该”,“推荐”,“可以”和“可选”按照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
    }
}

2.一般

2.1基本编码标准

代码必须遵循PSR-1中列出的所有规则

PSR-1中的“StudlyCaps”一词必须解释为PascalCase,其中每个单词的首字母大写,包括第一个字母。

2.2文件

所有PHP文件必须仅使用Unix LF(换行)行结尾。

所有PHP文件必须以非空白行结束,以单个LF结束。

?>必须从仅包含PHP的文件中省略结束标记。

2.3行

行长度不得有硬性限制。

行长度的软限制必须是120个字符。

行不应超过80个字符; 超过的行应该被分成多个后续行,每行不超过80个字符。

在行的末尾不得有尾随空格。

可以添加空行以提高可读性并指示相关的代码块,除非明确禁止。

每行不得超过一个语句。

2.4缩进

代码必须为每个缩进级别使用4个空格的缩进,并且不得使用制表符进行缩进。

2.5关键字和类型

所有PHP保留的关键字和类型[1] [2]必须是小写的。

添加到未来PHP版本的任何新类型和关键字必须是小写的。

类型的关键字的简短形式必须是即使用bool代替booleanint而不是integer

3.声明语句,命名空间和导入语句

PHP文件的标头可能包含许多不同的块。如果存在,下面的每个块必须用一个空行分隔,并且不得包含空行。尽管可以省略不相关的块,但每个块必须按下面列出的顺序排列。

  • 打开<?php标签。
  • 文件级docblock。
  • 一个或多个声明语句。
  • 文件的名称空间声明。
  • 一个或多个基于类的useimport语句。
  • 一个或多个基于函数的useimport语句。
  • 一个或多个基于常量的useimport语句。
  • 文件中的其余代码。

当文件包含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.类,属性和方法

术语“类”指的是所有类,接口和traits。

任何结束括号不得在同一行上跟随任何注释或声明。

在实例化一个新类时,即使没有传递给构造函数的参数,也必须始终存在括号。

new Foo();

4.1继承和实现

extendsimplements关键字必须在同一行类名来声明。

类的左括号必须转到自己的行; 类的右括号必须在正文之后的下一行上。。

左大括号必须位于自己的行上,并且不得在空白行的前面或后面加上空白行。 。

右括号必须位于自己的行上,并且不能前面有一张空白行。 。

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

use实现traits的类中使用关键字必须在左括号后的下一行声明。

<?php

namespace Vendor\Package;

use Vendor\Package\FirstTrait;

class ClassName
{
    use FirstTrait;
}

导入到类中的每个单独的Trait必须包含在每行一个,每个包含必须有自己的useimport语句。

<?php

namespace Vendor\Package;

use Vendor\Package\FirstTrait;
use Vendor\Package\SecondTrait;
use Vendor\Package\ThirdTrait;

class ClassName
{
    use FirstTrait;
    use SecondTrait;
    use ThirdTrait;
}

当类在useimport语句之后没有任何内容时,类右括号必须在useimport语句之后的下一行

<?php

namespace Vendor\Package;

use Vendor\Package\FirstTrait;

class ClassName
{
    use FirstTrait;
}

否则,它必须在useimport语句后面有一个空行

<?php

namespace Vendor\Package;

use Vendor\Package\FirstTrait;

class ClassName
{
    use FirstTrait;

    private $property;
}

使用insteadofas运算符时,必须按如下方式使用它们,记下缩进,间距和新行。

<?php

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

4.3属性和常量

必须在所有属性上声明可见性。

如果您的项目PHP最低版本支持持续可见性(PHP 7.1或更高版本),则必须在所有常量上声明可见性。

var关键字不能被用于声明属性。

每个声明不得超过一个属性。

属性名称不得以单个下划线为前缀,以指示受保护或私有可见性。也就是说,下划线前缀明确没有任何意义。

类型声明和属性名称之间必须有一个空格。

属性声明如下所示:

<?php

namespace Vendor\Package;

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

4.4方法和函数

必须在所有方法上声明可见性。

方法名称不得以单个下划线为前缀,以指示受保护或私有可见性。也就是说,下划线前缀明确没有任何意义。

不得在方法名称后用空格声明方法和函数名称。左大括号必须位于自己的行上,而右大括号必须位于正文之后的下一行上。。在左括号后面不能有空格,并且在右括号之前不能有空格。

方法声明如下所示。请注意括号,逗号,空格和大括号的位置:

<?php

namespace Vendor\Package;

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

函数声明如下所示。请注意括号,逗号,空格和大括号的位置:

<?php

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

4.5方法和函数参数

在参数列表中,每个逗号之前不得有空格,每个逗号后必须有一个空格。

具有默认值的方法和函数参数必须位于参数列表的末尾。

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

如果存在,abstractfinal声明必须先于可见性声明。

如果存在,static声明必须在可见性声明之后。

<?php

namespace Vendor\Package;

abstract class ClassName
{
    protected static $foo;

    abstract protected function zim();

    final public static function bar()
    {
        // method body
    }
}

4.7方法和函数调用

在进行方法或函数调用时,方法或函数名称与左括号之间不能有空格,在左括号后面不能有空格,并且在右括号之前不能有空格。在参数列表中,每个逗号之前不得有空格,每个逗号后必须有一个空格。

<?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.控制结构

控制结构的一般样式规则如下:

  • 控制结构关键字后面必须有一个空格
  • 在左括号后面不能有空格
  • 在右括号之前不能有空格
  • 在右括号和左括号之间必须有一个空格
  • 结构体必须缩进一次
  • 主体必须在左大括号后的下一行
  • 右括号必须位于正文之后的下一行

每个结构的主体必须用大括号括起来。这标准化了结构的外观,并降低了新行添加到正文中时引入错误的可能性。

5.1 if,elseif,else

一个if结构如下所示。注意括号,空格和大括号的位置; 并且elseelseif与上一正文中的右括号位于同一行。。

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

一个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

一个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

一个for声明如下所示。请注意括号,空格和大括号的位置。

<?php

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

括号中的表达式可以分为多行,其中每个后续行至少缩进一次。这样做时,第一个表达式必须在下一行。右括号和左括号必须放在一起,它们之间有一个空格。

<?php

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

5.5 foreach

一个foreach声明如下所示。请注意括号,空格和大括号的位置。

<?php

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

5.6try, catch, finally

一个try-catch-finally块如下所示。请注意括号,空格和大括号的位置。

<?php

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

6.运算符

运算符的样式规则按arity(它们采用的操作数的数量)进行分组。

当运算符周围允许空间时,可以使用多个空格用于可读性目的。

此处未描述的所有运算符都未定义。

6.1。单一运算符

递增/递减运算符在运算符和操作数之间不能有任何空格。

类型转换运算符在括号内不能有任何空格:

$intValue = (int) $input;

6.2。二元运算符

所有二元算术比较赋值按位逻辑字符串类型运算符必须至少前后一个空格:

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

6.3。三元运算符

条件运算符,也简称为三元运算符,必​​须在?:字符周围至少有一个空格

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

当省略条件运算符的中间操作数时,运算符必须遵循与其他二元比较运算符相同的样式规则

$variable = $foo ?: 'bar';

7.闭包

闭包必须在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.匿名类

匿名类必须遵循与上一节中的闭包相同的准则和原则。

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