PSR-2:编码样式指南

翻译:薛粲
授权许可:CC BY-NC 4.0

这份文档是《PSR-2: Coding Style Guide》的非官方译文。

《PSR-2:编码样式指南》扩展和扩充了《PSR-1:基础编码规范》。

这份指南的初衷是减少当我们阅读不同作者编写的代码时遇到的认知差异。它期望通过列举了一组可供共同遵循的规则用于格式化 PHP 源代码来实现这一目的。

英文原文使用的关键词 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", 以及 "OPTIONAL" 遵循 RFC 2119 的描述。译文中根据上下文可能会使用不同的词汇来对应这些关键词,并加粗显示。

1. 概述

  • 代码必须遵循一份 PSR 编码样式指南 PSR-1

  • 代码必须使用 4 个空格而不是制表符作为缩进。

  • 不得对行宽进行硬性限制;软性限制必须是 120 个字符;每行应该包含 80 个或者更少的字符。

  • namespace 声明之后必须有一个空行,在 use 声明之后也必须有一个空行。

  • 类的左花括号必须在下一行,右花括号必须在内容后的下一行。

  • 方法的左花括号必须在下一行,右花括号必须在内容后的下一行。

  • 必须为所有属性和方法声明访问控制;abstractfinal 必须在访问控制之前;static 必须在访问控制之后。

  • 在控制结构的保留字后必须有一个空格;在方法和函数调用之后不得有空格。

  • 控制结构的左花括号必须在同一行,右花括号必须在内容后的下一行。

  • 控制结构的左括号之后不得有空格,右括号之前不得有空格。

1.1 示例

这个例子作为一个简明的示例,涵盖了一些后面提及的规则:

<?php
namespace Vendor\Package;

use FooInterface;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

class Foo extends Bar implements FooInterface
{
    public function sampleFunction($a, $b = null)
    {
        if ($a === $b) {
            bar();
        } elseif ($a > $b) {
            $foo->bar($arg1);
        } else {
            BazClass::bar($arg2, $arg3);
        }
    }

    final public static function bar()
    {
        // 方法内容
    }
}

2. 通用规则

2.1 基础编码标准

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

2.2 文件

所有 PHP 源文件必须使用 Unix LF (linefeed) 作为换行符。

所有 PHP 源文件必须以一个空行结束。

对于只包含 PHP 的文件,必须省略用于指示 PHP 段结束的 ?> 标记。

2.3. 行

不得对行宽设置硬性限制。

对行宽的软性限制必须是 120 个字符;超出时自动样式检查必须发出警告但不得产生错误。

不应该超过 80 个字符;超过的行应该分割为多个不超过 80 个字符的行。

非空的行不得以空白字符结束。

可以添加空行用于指出代码块的关联性以提升代码的可读性。

一行不得包含超过一个语句。

2.4. 缩进

代码必须使用 4 个空格用于缩进,不得使用制表符用于缩进。

只使用空格,不混用空格和制表符,能够帮助避免查看差异、打补丁、查看历史以及批注时潜在的问题。使用空格还让跨行对齐时插入细粒度的子缩进更加容易。

2.5. 保留字和 True/False/Null

PHP 保留字必须使用小写。

PHP 常量 truefalse 以及 null 必须使用小写。

3. Namespace 和 Use 声明

如果存在,必须namespace 声明后加一个空行。

如果存在,所有 use 声明必须namespace 声明之后。

每个声明必须要有一个 use 保留字。

use 块之后必须有一个空行。

例如:

<?php
namespace Vendor\Package;

use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

// ... additional PHP code ...

4. 类、属性和方法

术语“类”指代所有的类、接口和 trait。

4.1. Extends 和 Implements

保留字 extendsimplements 必须与类名位于同一行。

类的起始花括号必须独自一行;结束花括号必须在内容结束后的下一行。

<?php
namespace Vendor\Package;

use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

class ClassName extends ParentClass implements \ArrayAccess, \Countable
{
    // 常量、属性、方法等
}

implements 列表可以分割为多行,这些行缩进一次。当这样做时,第一个条目必须在下一行,且每行必须只包含一个接口。

<?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. 属性

必须为所有属性声明访问控制。

不得使用保留字 var 声明属性。

一条语句不得声明超过一个属性。

不应该使用单个下划线作为 protectedprivate 属性的访问控制的前缀。

属性声明看类似下面的例子。

<?php
namespace Vendor\Package;

class ClassName
{
    public $foo = null;
}

4.3. 方法

必须为所有方法声明访问控制。

不应该使用单个下划线作为 protectedprivate 方法的访问控制的前缀。

方法名后不得使用空格。左花括号必须独占一行,右花括号必须在内容之后的行。左括号后不得紧接一个空格,右括号前不得放置一个空格。

方法的声明类似下面的示例。请留意其中括号、逗号、空格以及花括号的位置:

<?php
namespace Vendor\Package;

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

4.4. 方法参数

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

具有默认值的方法参数必须位于参数列表的最后面。

<?php
namespace Vendor\Package;

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

参数列表可以分割为多行,这些行只需缩进一次。当使用这种方式的时候,列表中的第一个参数必须位于新行,且每行必须只包含一个参数。

当参数列表被分割为多行时,右括号和左花括号必须放在同一行,中间用一个空格隔开。

<?php
namespace Vendor\Package;

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

4.5. abstractfinal 以及 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.6. 方法和函数调用

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

<?php
bar();
$foo->bar($arg1);
Foo::bar($arg2, $arg3);

参数列表可以分割为多行,这些行只需缩进一次。当使用这种方式的时候,列表中的第一个参数必须位于新行,且每行必须只包含一个参数。

<?php
$foo->bar(
    $longArgument,
    $longerArgument,
    $muchLongerArgument
);

5. 控制结构

与控制结构相关的基本样式原则如下:

  • 控制结构保留字之后必须有一个空格

  • 左括号之后不得有空格

  • 右括号之前不得有空格

  • 右括号和左花括号直接必须有一个空格

  • 结构体必须缩进一次

  • 右花括号必须在内容后的下一行

每个结构的内容必须使用花括号包裹。这能让控制结构看起来更加标准,并能减少向结构体中加入新语句导致错误的可能性。

5.1. ifelseifelse

一个 if 结构看起来形如下面的示例。请留意其中括号、空格以及花括号的位置;留意 elseelseif 与之前内容的右花括号位于同一行。

<?php
if ($expr1) {
    // if body
} elseif ($expr2) {
    // elseif body
} else {
    // else body;
}

应该使用保留字 elseif 代替 else if,这样,所有控制结构的保留字看起来都是单个单词。

5.2. switchcase

一个 switch 结构看起来形如下面的示例。请留意其中括号、空格以及花括号的位置。case 语句必须相对 switch 缩进一次,保留字 break (或其它用于终止的保留字)必须case 块的缩进层次相同。当 case 块中穿透行为是设计需要的时候,必须添加类似 // no break 的注释。

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

5.3. whiledo while

一个 while 语句看起来形如下面的示例。请留意其中括号、空格以及花括号的位置。

<?php
while ($expr) {
    // structure body
}

类似的,do while 语句看起来形如下面的示例。请留意其中括号、空格以及花括号的位置。

<?php
do {
    // structure body;
} while ($expr);

5.4. for

一个 for 语句看起来形如下面的示例。请留意其中括号、空格以及花括号的位置。

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

5.5. foreach

一个 foreach 语句看起来形如下面的示例。请留意其中括号、空格以及花括号的位置。

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

5.6. trycatch

一个 try catch 块看起来形如下面的示例。请留意其中括号、空格以及花括号的位置。

<?php
try {
    // try body
} catch (FirstExceptionType $e) {
    // catch body
} catch (OtherExceptionType $e) {
    // catch body
}

6. 闭包

闭包声明中保留字 function 之后必须有一个空格,保留字 use 前后必须各有一个空格。

左花括号必须与闭包声明同一行,右花括号必须在内容结束后另起一行。

参数表和变量表的左括号之后不得留空格,右括号之前不得留空格。

参数表和变量表中逗号之前不得留空格,逗号之后必须留有一个空格。

闭包中具有默认值的参数必须位于参数表的最后。

一个闭包声明看起来形如下面的示例。请留意其中括号、逗号、空格以及花括号的位置。

$closureWithArgs = function ($arg1, $arg2) {
    // body
};

$closureWithArgsAndVars = function ($arg1, $arg2) use ($var1, $var2) {
    // 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
);

7. 后记

这份规范有意的忽略了一些样式和实践准则,这些内容包括但不限于:

  • 全局变量和全局常量的声明

  • 函数的声明

  • 操作符和赋值

  • 跨行对齐

  • 注释和文档块

  • 类名的前缀和后缀

  • 最佳实践

后续的推荐标准可以修订和扩展这份指南来涵盖这部分或更多其它的样式和实践准则。

附录 A:问卷调查

在起草这份指南是,PHP FIG 小组对其有成员项目进行了问卷调查以决定哪些实践行为是最普遍的。原文在附录中包含了问卷调查的情况,译文略。


xuecan
1.9k 声望34 粉丝

(●—●)大约是全栈(●—●)