PSR 2 编码风格指南
本篇规范是 PSR-1 基本代码规范的继承与补充
本规范希望通过制定一系列规范化 PHP 代码的规则,以减少在浏览不同作者的代码时,因代码风格的不同而造成不便
当多名程序员在多个项目中合作时,就需要一个共同的编码规范, 而本文中的风格规范源自于多个不同项目代码风格的共同特性, 因此,本规范的价值在于我们都遵循这个编码风格,而不是在于它本身
本篇规范中的 必须,不得,需要,应,不应,应该,不应该,推荐,可能 和 可选 等词按照 RFC 2119 中的描述进行解释
规范
-
代码 必须 遵循 PSR-1 中的编码规范
-
代码 必须 使用 4 个空格符进行缩进,而不是使用制表符 (
\t
) 或者 「 Tab 键 」还是可以使用 Tab 键,只要设置将 Tab 键转换成 4 个空格即可
-
每行的字符数 应该 软性保持在
80
个之内,理论上 一定不能 多于 120 个字符,且 一定不可 有硬性限制 -
每个
namespace
命名空间声明语句和use
声明语句块后面,必须 插入一个空白行 -
代码块开始的花括号(
{
) 必须 写在函数声明或类声明后自成一行,结束花括号(}
)也 必须 写在函数主体或类主体后自成一行 -
类的属性和方法 必须 添加访问修饰符 (
private
、protected
以及public
) ,abstract
以及final
必须 声明在访问修饰符之前,而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 sampleMethod($a, $b = null) { 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 中的所有规范
2.2. 文件
所有 PHP 文件 必须 使用 Unix LF (linefeed)
作为行的结束符
所有 PHP 文件 必须 以空白行结尾
纯 PHP 文件 必须 省略最后的 ?>
结束标签
2.3. 行 行的长度 一定不可 有硬性的约束。
2.3. 行
行的长度 一定不可 有硬性的限制
软性的长度约束 必须 要限制在 120 个字符以内,若超过此长度,带代码规范检查的编辑器 必须 要发出警告,不过 一定不可 发出错误提示
每行 不该 多于 80 个字符,大于 80 字符的行 应该 折成多行
非空白行后 一定不可 有多余的空白符
空白行 可以 有助于改进阅读和区分代码块
每行 一定不能 多于一条语句
2.4. 缩进
代码必须使用 4 个空格缩进,且 一定不能 使用制表符 ( \t
)
注: 仅使用空格,而不是混用空格和 tab 键,能够避免在查看代码差异,打补丁,查看提交历史,以及进行注解时产生问题
使用空格也使得代码对齐更轻松
2.5. 关键字 和 True / False / Null
PHP 的 关键字 必须 使用小写形式
PHP 中的常量 true
, false
, 还有 null
必须 使用小写形式
命名空间和使用声明
-
namespace
命名空间声明语句之后必须添加一个空白行 -
所有的
use
声明必须在namespace
声明之后 -
每一个
use
关键字 必须 且只能用于引用一个声明 -
use
语句块之后,必须添加一个空白行
范例
<?php namespace Vendor\Package; use FooClass; use BarClass as Bar; use OtherVendor\OtherPackage\BazClass; // ... 其它 PHP 代码 ...
4. 类 、属性、 和 方法
此处的 「类」泛指所有的 「 class 类 」、「 接口 」以及「 traits 可复用代码块 」
4.1. 继承 与 实现
-
关键词
extends
和implements
必须 写在类名称的同一行 -
类的开始花括号 必须 独占一行,结束花括号也 必须 在类的主体后独占一行
范例
<?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 { // 常量, 属性, 方法 }
4.2. 属性
-
所有的属性都 必须 添加访问修饰符
-
一定不可 使用
var
关键字来声明一个属性 -
每条语句 一定不可 多于一个属性的声明
-
属性名 不应该 使用下划线 (
_
) 作为前缀来区分是protected
orprivate
范例
以下是属性声明的一个范例
<?php namespace Vendor\Package; class ClassName { public $foo = null; }
4.3. 方法
-
所有的方法都 必须 添加访问修饰符
-
方法名 不应该 使用下划线 (
_
) 作为前缀来区分是protected
或private
-
声明方法时,方法名和左括号 (
(
) 之间一定 不能 有空格 -
开始花括号 (
{
) 必须 独占一行,且后面不能有空格 -
结束花括号 (
}
) 必须 独占一行,且前面不能有空格
以下是方法声明的一个范例
留意其括号、逗号、空格以及花括号的位置
<?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. abstract
, final
, 和 static
关键字
-
当需要添加
abstract
或final
声明时,abstract
和final
必须 写在访问修饰符 (public
、private
、protected
) 之前 -
当需要添加
static
声明时,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. if
、 elseif
、 else
关键字 elseif
必须 使用 else if
代替,这样所有的控制结构关键词都是一个单独的单词
下面的代码是一个标准的 if
控制结构
留意「括号」、「空格」以及「花括号」的位置
注意 else
和 else if
都与前面的结束花括号在同一行
<?php if ($expr1) { // if body } else if ($expr2) { // elseif body } else { // else body; }
5.2. switch
, case
case
语句 必须 相对 switch 进行一次缩进break
语句以及case
内的其它语句都 必须 相对 case 进行一次缩进- 如果存在非空的
case
直穿语句,主体里 必须 有类似// no break
的注释
下面的代码是一个标准的 switch
控制结构
留意「括号」、「空格」以及「花括号」的位置
<?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. while
, do 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. try
, catch
下面的代码是一个符合标准的 try catch
控制结构
留意「括号」、「空格」以及「花括号」的位置
<?php try { // try body } catch (FirstExceptionType $e) { // catch body } catch (OtherExceptionType $e) { // catch body }
6. 闭包 ( Closures )
-
声明闭包时,关键词
function
后以及关键词use
的前后都 必须 要有一个空格 -
开始花括号 必须 写在声明的同一行,结束花括号 必须 紧跟主体结束的下一行,独自成行
-
参数列表和变量列表的左括号后以及右括号前,一定不可 有空格
-
参数和变量列表中,逗号前 一定不可 有空格,而逗号后 必须 要有空格
-
闭包中有默认值的参数 必须 放到列表的后面
下面的代码是一个符合标准的闭包声明
注意留意「括号」、「空格」以及「花括号」的位置
<?php $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. 调研
撰写这篇风格指南的过程中,FIG 团队对众多的成员做了一份调研,用于寻找常见的风格
在此保留一份数据,以供后查
A.1. 调研数据
url,http://www.horde.org/apps/horde/docs/CODING_STANDARDS,http://pear.php.net/manual/en/standards.php,http://solarphp.com/manual/appendix-standards.style,http://framework.zend.com/manual/en/coding-standard.html,http://symfony.com/doc/2.0/contributing/code/standards.html,http://www.ppi.io/docs/coding-standards.html,https://github.com/ezsystems/ezp-next/wiki/codingstandards,http://book.cakephp.org/2.0/en/contributing/cakephp-coding-conventions.html,https://github.com/UnionOfRAD/lithium/wiki/Spec%3A-Coding,http://drupal.org/coding-standards,http://code.google.com/p/sabredav/,http://area51.phpbb.com/docs/31x/coding-guidelines.html,https://docs.google.com/a/zikula.org/document/edit?authkey=CPCU0Us&hgd=1&id=1fcqb93Sn-hR9c0mkN6m_tyWnmEvoswKBtSc0tKkZmJA,http://www.chisimba.com,n/a,https://github.com/Respect/project-info/blob/master/coding-standards-sample.php,n/a,Object Calisthenics for PHP,http://doc.nette.org/en/coding-standard,http://flow3.typo3.org,https://github.com/propelorm/Propel2/wiki/Coding-Standards,http://developer.joomla.org/coding-standards.html voting,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,no,no,no,?,yes,no,yes indent_type,4,4,4,4,4,tab,4,tab,tab,2,4,tab,4,4,4,4,4,4,tab,tab,4,tab line_length_limit_soft,75,75,75,75,no,85,120,120,80,80,80,no,100,80,80,?,?,120,80,120,no,150 line_length_limit_hard,85,85,85,85,no,no,no,no,100,?,no,no,no,100,100,?,120,120,no,no,no,no class_names,studly,studly,studly,studly,studly,studly,studly,studly,studly,studly,studly,lower_under,studly,lower,studly,studly,studly,studly,?,studly,studly,studly class_brace_line,next,next,next,next,next,same,next,same,same,same,same,next,next,next,next,next,next,next,next,same,next,next constant_names,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper true_false_null,lower,lower,lower,lower,lower,lower,lower,lower,lower,upper,lower,lower,lower,upper,lower,lower,lower,lower,lower,upper,lower,lower method_names,camel,camel,camel,camel,camel,camel,camel,camel,camel,camel,camel,lower_under,camel,camel,camel,camel,camel,camel,camel,camel,camel,camel method_brace_line,next,next,next,next,next,same,next,same,same,same,same,next,next,same,next,next,next,next,next,same,next,next control_brace_line,same,same,same,same,same,same,next,same,same,same,same,next,same,same,next,same,same,same,same,same,same,next control_space_after,yes,yes,yes,yes,yes,no,yes,yes,yes,yes,no,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes always_use_control_braces,yes,yes,yes,yes,yes,yes,no,yes,yes,yes,no,yes,yes,yes,yes,no,yes,yes,yes,yes,yes,yes else_elseif_line,same,same,same,same,same,same,next,same,same,next,same,next,same,next,next,same,same,same,same,same,same,next case_break_indent_from_switch,0/1,0/1,0/1,1/2,1/2,1/2,1/2,1/1,1/1,1/2,1/2,1/1,1/2,1/2,1/2,1/2,1/2,1/2,0/1,1/1,1/2,1/2 function_space_after,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no closing_php_tag_required,no,no,no,no,no,no,no,no,yes,no,no,no,no,yes,no,no,no,no,no,yes,no,no line_endings,LF,LF,LF,LF,LF,LF,LF,LF,?,LF,?,LF,LF,LF,LF,?,,LF,?,LF,LF,LF static_or_visibility_first,static,?,static,either,either,either,visibility,visibility,visibility,either,static,either,?,visibility,?,?,either,either,visibility,visibility,static,? control_space_parens,no,no,no,no,no,no,yes,no,no,no,no,no,no,yes,?,no,no,no,no,no,no,no blank_line_after_php,no,no,no,no,yes,no,no,no,no,yes,yes,no,no,yes,?,yes,yes,no,yes,no,yes,no class_method_control_brace,next/next/same,next/next/same,next/next/same,next/next/same,next/next/same,same/same/same,next/next/next,same/same/same,same/same/same,same/same/same,same/same/same,next/next/next,next/next/same,next/same/same,next/next/next,next/next/same,next/next/same,next/next/same,next/next/same,same/same/same,next/next/same,next/next/next
A.2. 调研说明
-
indent_type
缩进风格
tab
= "Use a tab",2
or4
= "number of spaces" -
line_length_limit_soft
「 soft 」软换行字符限制
?
= 不可辨比或没有回复,no
表示无限制 -
line_length_limit_hard
「 hand 」 硬换行字符限制
?
= 不可辨比或没有回复,no
表示无限制 -
class_names
类如何命名
lower
= 全部字母小写形式lower_under
= 小写字母形式,词之间使用下划线 (_
) 分隔studly
= 首字母大写的驼峰命名法 -
class_brace_line
声明类时 左花括号的位置
same
= 和类名放在同一行next
= 类名之后单独成行 -
constant_names
常量命名方式
upper
= 所有字母大写且使用下划线分隔每个单词 -
true_false_null
true
、false
和null
的书写形式lower
=true
,false
和null
几个关键字都小写upper
=true
,false
和null
几个关键字都大写 -
method_names
方法名命名方式
camel
=camelCase
驼峰命名法lower_under
= 所有字母下写且单词之间使用下划线分隔 -
method_brace_line
方法声明中开始大括号的位置
same
= 和方法名在同一行next
= 方法名之后单独成行 -
control_brace_line
控制语句大括号的位置
same
= 和控制语句关键词同一行next
= 控制语句关键词之后单独成一行 -
control_space_after
控制结构关键词之后是否有一个空格
-
always_use_control_braces
控制语句是否始终都添加花括号
如果控制结构只有一条语句,是可以不用添加大括号的
-
else_elseif_line
当使用
else
或elseif
时,是否单独一行same
= 和结束花括号 (}
) 同一行next
= 结束花括号之后单独成行 -
case_break_indent_from_switch
case
和break
语句相比于switch
的缩进次数 -
function_space_after
调用函数时,函数名和左小括号
(
之间是否有一个空格 -
closing_php_tag_required
纯 PHP 文件,是否添加 PHP
?>
标签 -
line_endings
使用哪种类型的换行符
-
static_or_visibility_first
声明方法时,是把
static
写在开头还是将访问控制符写在开头
18 . control_space_parens
使用控制结构表达式时,是否在小括号之间添加空格 `yes` = `if ( $expr )` `no` = `if ($expr)`
-
blank_line_after_php
<?php
标签之后是否有一个空白行 -
class_method_control_brace
类 、方法 和 控制接口中的开始花括号的位置
A.3. 调研结果
indent_type: tab: 7 2: 1 4: 14 line_length_limit_soft: ?: 2 no: 3 75: 4 80: 6 85: 1 100: 1 120: 4 150: 1 line_length_limit_hard: ?: 2 no: 11 85: 4 100: 3 120: 2 class_names: ?: 1 lower: 1 lower_under: 1 studly: 19 class_brace_line: next: 16 same: 6 constant_names: upper: 22 true_false_null: lower: 19 upper: 3 method_names: camel: 21 lower_under: 1 method_brace_line: next: 15 same: 7 control_brace_line: next: 4 same: 18 control_space_after: no: 2 yes: 20 always_use_control_braces: no: 3 yes: 19 else_elseif_line: next: 6 same: 16 case_break_indent_from_switch: 0/1: 4 1/1: 4 1/2: 14 function_space_after: no: 22 closing_php_tag_required: no: 19 yes: 3 line_endings: ?: 5 LF: 17 static_or_visibility_first: ?: 5 either: 7 static: 4 visibility: 6 control_space_parens: ?: 1 no: 19 yes: 2 blank_line_after_php: ?: 1 no: 13 yes: 8 class_method_control_brace: next/next/next: 4 next/next/same: 11 next/same/same: 1 same/same/same: 6