PHP PSR 代码风格
FIG组织在制定跟PHP相关规范,简称PSR,PSR旨在通过讨论我们代码项目的共同点以找出一个协作编程的方法。
什么是psr0 强调自动加载的方式
下文描述了若要使用一个通用的自动加载器(autoloader),你所需要遵守的规范:
规范
一个完全标准的命名空间(namespace)和类(class)的结构是这样的:\\(\)*
每个命名空间(namespace)都必须有一个顶级的空间名(namespace)("组织名(Vendor Name)")。
每个命名空间(namespace)中可以根据需要使用任意数量的子命名空间(sub-namespace)。
从文件系统中加载源文件时,空间名(namespace)中的分隔符将被转换为 DIRECTORY_SEPARATOR。
类名(class name)中的每个下划线_都将被转换为一个DIRECTORY_SEPARATOR。下划线_在空间名(namespace)中没有什么特殊的意义。
完全标准的命名空间(namespace)和类(class)从文件系统加载源文件时将会加上.php后缀。
组织名(vendor name),空间名(namespace),类名(class name)都由大小写字母组合而成。
示例
\Doctrine\Common\IsolatedClassLoader => /path/to/project/lib/vendor/Doctrine/Common/IsolatedClassLoader.php
\Symfony\Core\Request => /path/to/project/lib/vendor/Symfony/Core/Request.php
\Zend\Acl => /path/to/project/lib/vendor/Zend/Acl.php
\Zend\Mail\Message => /path/to/project/lib/vendor/Zend/Mail/Message.php
空间名(namespace)和类名(class name)中的下划线
\namespace\package\Class_Name => /path/to/project/lib/vendor/namespace/package/Class/Name.php
\namespace\package_name\Class_Name => /path/to/project/lib/vendor/namespace/package_name/Class/Name.php
以上是我们为实现通用的自动加载而制定的最低标准。你可以利用能够自动加载PHP 5.3类的SplClassLoader来测试你的代码是否符合这些标准。
实例
下面是一个怎样利用上述标准来实现自动加载的示例函数。
<?php
function autoload($className)
{
$className = ltrim($className, '\\');
$fileName = '';
$namespace = '';
if ($lastNsPos = strrpos($className, '\\')) {
$namespace = substr($className, 0, $lastNsPos);
$className = substr($className, $lastNsPos + 1);
$fileName = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
}
$fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
require $fileName;
}
SplClassLoader实现
下面的gist是一个按照上面建议的标准来自动加载类的SplClassLoader实例。这是依据这些标准来加载PHP 5.3类的推荐方案。
什么是psr1 定义基本代码规范
本节我们将会讨论一些基本的代码规范问题,以此作为将来讨论更高级别的代码分享和技术互用的基础。
RFC 2119中的必须(MUST),不可(MUST NOT),建议(SHOULD),不建议(SHOULD NOT),可以/可能(MAY)等关键词将在本节用来做一些解释性的描述。
1. 概述
源文件必须只使用 和
这两种标签。
源文件中php代码的编码格式必须只使用不带字节顺序标记(BOM)的UTF-8。
一个源文件建议只用来做声明(类(class),函数(function),常量(constant)等)或者只用来做一些引起副作用的操作(例如:输出信息,修改.ini配置等),但不建议同时做这两件事。
命名空间(namespace)和类(class) 必须遵守PSR-0标准。
类名(class name) 必须使用骆驼式(StudlyCaps)写法 (译者注:驼峰式(cameCase)的一种变种,后文将直接用StudlyCaps表示)。
类(class)中的常量必须只由大写字母和下划线(_)组成。
方法名(method name) 必须使用驼峰式(cameCase)写法(译者注:后文将直接用camelCase表示)。
2. 文件
2.1. PHP标签
PHP代码必须只使用长标签()或者短输出式标签(< ?= ?>
);而不可使用其他标签。
2.2. 字符编码
PHP代码的编码格式必须只使用不带字节顺序标记(BOM)的UTF-8。
2.3. 副作用
一个源文件建议只用来做声明(类(class),函数(function),常量(constant)等)或者只用来做一些引起副作用的操作(例如:输出信息,修改.ini配置等),但不建议同时做这两件事。
短语副作用(side effects)的意思是 在包含文件时 所执行的逻辑与所声明的类(class),函数(function),常量(constant)等没有直接的关系。
副作用(side effects)包含但不局限于:产生输出,显式地使用require或include,连接外部服务,修改ini配置,触发错误或异常,修改全局或者静态变量,读取或修改文件等等
下面是一个既包含声明又有副作用的示例文件;即应避免的例子:
<?php
// 副作用:修改了ini配置
ini_set('error_reporting', E_ALL);
// 副作用:载入了文件
include "file.php";
// 副作用:产生了输出
echo "<html>\n";
// 声明
function foo()
{
// 函数体
}
下面是一个仅包含声明的示例文件;即应提倡的例子:
<?php
// 声明
function foo()
{
// 函数体
}
// 条件式声明不算做是副作用
if (! function_exists('bar')) {
function bar()
{
// 函数体
}
}
3. 空间名(namespace)和类名(class name)
命名空间(namespace)和类(class)必须遵守 PSR-0.
这意味着一个源文件中只能有一个类(class),并且每个类(class)至少要有一级空间名(namespace):即一个顶级的组织名(vendor name)。
类名(class name) 必须使用StudlyCaps写法。
PHP5.3之后的代码必须使用正式的命名空间(namespace) 例子:
<?php
// PHP 5.3 及之后:
namespace Vendor\Model;
class Foo
{
}
PHP5.2.x之前的代码建议用伪命名空间Vendor_作为类名(class name)的前缀
<?php
// PHP 5.2.x 及之前:
class Vendor_Model_Foo
{
}
4. 类的常量、属性和方法
术语类(class)指所有的类(class),接口(interface)和特性(trait)
4.1. 常量
类常量必须只由大写字母和下划线(_)组成。 例子:
<?php
namespace Vendor\Model;
class Foo
{
const VERSION = '1.0';
const DATE_APPROVED = '2012-06-01';
}
4.2. 属性
本指南中故意不对$StulyCaps,$camelCase或者$unser_score中的某一种风格作特别推荐,完全由读者依据个人喜好决定属性名的命名风格。
但是不管你如何定义属性名,建议在一个合理的范围内保持一致。这个范围可能是组织(vendor)级别的,包(package)级别的,类(class)级别的,或者方法(method)级别的。
4.3. 方法
方法名则必须使用camelCase()风格来声明。
什么是PSR2 定义代码风格
代码风格指南本手册是基础代码规范(PSR-1)的继承和扩展。
为了尽可能的提升阅读其他人代码时的效率,下面例举了一系列的通用规则,特别是有关于PHP代码风格的。
各个成员项目间的共性组成了这组代码规范。当开发者们在多个项目中合作时,本指南将会成为所有这些项目中共用的一组代码规范。 因此,本指南的益处不在于这些规则本身,而在于在所有项目中共用这些规则。
RFC 2119中的必须(MUST),不可(MUST NOT),建议(SHOULD),不建议(SHOULD NOT),可以/可能(MAY)等关键词将在本节用来做一些解释性的描述。
1. 概述
代码必须遵守 PSR-1。
代码必须使用4个空格来进行缩进,而不是用制表符。
一行代码的长度不建议有硬限制;软限制必须为120个字符,建议每行代码80个字符或者更少。
在命名空间(namespace)的声明下面必须有一行空行,并且在导入(use)的声明下面也必须有一行空行。
类(class)的左花括号必须放到其声明下面自成一行,右花括号则必须放到类主体下面自成一行。
方法(method)的左花括号必须放到其声明下面自成一行,右花括号则必须放到方法主体的下一行。
所有的属性(property)和方法(method) 必须有可见性声明;抽象(abstract)和终结(final)声明必须在可见性声明之前;而静态(static)声明必须在可见性声明之后。
在控制结构关键字的后面必须有一个空格;而方法(method)和函数(function)的关键字的后面不可有空格。
控制结构的左花括号必须跟其放在同一行,右花括号必须放在该控制结构代码主体的下一行。
控制结构的左括号之后不可有空格,右括号之前也不可有空格。
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(换行)作为行结束符。
所有PHP源文件必须以一个空行结束。
纯PHP代码源文件的关闭标签?> 必须省略。
2.3. 行
行长度不可有硬限制。
行长度的软限制必须是120个字符;对于软限制,代码风格检查器必须警告但不可报错。
一行代码的长度不建议超过80个字符;较长的行建议拆分成多个不超过80个字符的子行。
在非空行后面不可有空格。
空行可以用来增强可读性和区分相关代码块。
一行不可多于一个语句。
2.4. 缩进
代码必须使用4个空格,且不可使用制表符来作为缩进。
注意:代码中只使用空格,且不和制表符混合使用,将会对避免代码差异,补丁,历史和注解中的一些问题有帮助。空格的使用还可以使通过调整细微的缩进来改进行间对齐变得更加的简单。2.5. 关键字和 True/False/Null
PHP关键字(keywords)必须使用小写字母。
PHP常量true, false和null 必须使用小写字母。
3. 命名空间(Namespace)和导入(Use)声明
命名空间(namespace)的声明后面必须有一行空行。
所有的导入(use)声明必须放在命名空间(namespace)声明的下面。
一句声明中,必须只有一个导入(use)关键字。
在导入(use)声明代码块后面必须有一行空行。
示例:
<?php
namespace Vendor\Package;
use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;
// ... 其它PHP代码 ...
4. 类(class),属性(property)和方法(method)
术语“类”指所有的类(class),接口(interface)和特性(trait)。
4.1. 扩展(extend)和实现(implement)
一个类的扩展(extend)和实现(implement)关键词必须和类名(class name)在同一行。
类(class)的左花括号必须放在下面自成一行;右花括号必须放在类(class)主体的后面自成一行。
<?php
namespace Vendor\Package;
use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;
class ClassName extends ParentClass implements \ArrayAccess, \Countable
{
// 常量、属性、方法
}
实现(implement)列表可以被拆分为多个缩进了一次的子行。如果要拆成多个子行,列表的第一项必须要放在下一行,并且每行必须只有一个接口(interface)。
<?php
namespace Vendor\Package;
use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;
class ClassName extends ParentClass implements
\ArrayAccess,
\Countable,
\Serializable
{
// 常量、属性、方法
}
4.2. 属性(property)
所有的属性(property)都必须声明其可见性。
变量(var)关键字不可用来声明一个属性(property)。
一条语句不可声明多个属性(property)。
属性名(property name) 不推荐用单个下划线作为前缀来表明其保护(protected)或私有(private)的可见性。
一个属性(property)声明看起来应该像下面这样。
<?php
namespace Vendor\Package;
class ClassName
{
public $foo = null;
}
4.3. 方法(method)
所有的方法(method)都必须声明其可见性。
方法名(method name) 不推荐用单个下划线作为前缀来表明其保护(protected)或私有(private)的可见性。
方法名(method name)在其声明后面不可有空格跟随。其左花括号必须放在下面自成一行,且右花括号必须放在方法主体的下面自成一行。左括号后面不可有空格,且右括号前面也不可有空格。
一个方法(method)声明看来应该像下面这样。 注意括号,逗号,空格和花括号的位置:
<?php
namespace Vendor\Package;
class ClassName
{
public function fooBarBaz($arg1, &$arg2, $arg3 = [])
{
// 方法主体部分
}
}
4.4. 方法(method)的参数
在参数列表中,逗号之前不可有空格,而逗号之后则必须要有一个空格。
方法(method)中有默认值的参数必须放在参数列表的最后面。
<?php
namespace Vendor\Package;
class ClassName
{
public function foo($arg1, &$arg2, $arg3 = [])
{
// 方法主体部分
}
}
参数列表可以被拆分为多个缩进了一次的子行。如果要拆分成多个子行,参数列表的第一项必须放在下一行,并且每行必须只有一个参数。
当参数列表被拆分成多个子行,右括号和左花括号之间必须又一个空格并且自成一行。
<?php
namespace Vendor\Package;
class ClassName
{
public function aVeryLongMethodName(
ClassTypeHint $arg1,
&$arg2,
array $arg3 = []
) {
// 方法主体部分
}
}
4.5. 抽象(abstract),终结(final)和 静态(static)
当用到抽象(abstract)和终结(final)来做类声明时,它们必须放在可见性声明的前面。
而当用到静态(static)来做类声明时,则必须放在可见性声明的后面。
<?php
namespace Vendor\Package;
abstract class ClassName
{
protected static $foo;
abstract protected function zim();
final public static function bar()
{
// 方法主体部分
}
}
4.6. 调用方法和函数
调用一个方法或函数时,在方法名或者函数名和左括号之间不可有空格,左括号之后不可有空格,右括号之前也不可有空格。参数列表中,逗号之前不可有空格,逗号之后则必须有一个空格。
<?php bar(); $foo->bar($arg1);
Foo::bar($arg2, $arg3);
参数列表可以被拆分成多个缩进了一次的子行。如果拆分成子行,列表中的第一项必须放在下一行,并且每一行必须只能有一个参数。
<?php $foo->bar(
$longArgument,
$longerArgument,
$muchLongerArgument
);
5. 控制结构
下面是对于控制结构代码风格的概括:
控制结构的关键词之后必须有一个空格。
控制结构的左括号之后不可有空格。
控制结构的右括号之前不可有空格。
控制结构的右括号和左花括号之间必须有一个空格。
控制结构的代码主体必须进行一次缩进。
控制结构的右花括号必须主体的下一行。
每个控制结构的代码主体必须被括在花括号里。这样可是使代码看上去更加标准化,并且加入新代码的时候还可以因此而减少引入错误的可能性。
5.1. if,elseif,else
下面是一个if条件控制结构的示例,注意其中括号,空格和花括号的位置。同时注意else和elseif要和前一个条件控制结构的右花括号在同一行。
<?php
if ($expr1) {
// if body
} elseif ($expr2) {
// elseif body
} else {
// else body;
}
推荐用elseif来替代else if,以保持所有的条件控制关键字看起来像是一个单词。
5.2. switch,case
下面是一个switch条件控制结构的示例,注意其中括号,空格和花括号的位置。case语句必须要缩进一级,而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. 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. 闭包
声明闭包时所用的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. 结论
本指南有意的省略了许多元素的代码风格。主要包括:
全局变量和全局常量的声明
函数声明
操作符和赋值
行间对齐
注释和文档块
类名的前缀和后缀
最佳实践
以后的代码规范中可能会修正或扩展本指南中规定的代码风格。