PHP社区百花齐放,拥有大量的函数库、框架和组件。PHP开发者通常会在自己的项目中使用若干个外部库,因而PHP代码遵循或尽量接近同一个代码风格就非常重要,可以让开发者方便地把多个代码库集成在自己的项目中。
框架互操作组(即PHP标准组)发布了一系列代码风格推荐标准,即PSR-0,PSR-1,PSR-2和PSR-3。 不要让这些名称所混淆,这些推荐仅是一些被其它项目所遵循的规则,如Drupal, Zend, Symfony, CakePHP, phpBB, AWS SDK, FuelPHP, Lithium等,你可以把这些规则用在自己的项目中,或者继续使用你自己的风格。
通常情况下,你的PHP代码应该遵循其中一项或多项标准,从而其他开发者可以方便地阅读和使用你的代码。这些标准都是在前一个标准 上附加新的规则,所以使用PSR-1就同时要求遵循PSR-0,但可以不遵循PSR-2。
可以使用PHP_CodeSniffer来检查代码是否符合这些标准,文本编辑器插件Sublime Text 2还能提供实时检查。如果不符合规范,可以使用Fabien Potencier提供的工具PHP Coding Standards Fixer自动修复,不用自己手工修复。
PSR完整地址为:https://github.com/php-fig/fig-standards
PSR-0:类命名与autoloader之间的规范
PSR-0主要是对namespace和class命名规范进行约束,这个规范主要作用是使得autoloader函数能够根据完整的类名从硬盘中自动加载指定的类文件,其中涉及到的“强制”要求有以下几点:
- 完整的namespace和class命名必须符合结构:\<Vendor Name>\(<Namespace>\)*<Class Name>.php
- Vendor Name(开发者信息)必须是最顶层的namespace,这样可以防止自己类库与其他开发者类库出现命名冲突
- (<Namespace>\)*后面带有一个正则表达式中的星号,代表Namespace可以有0个或者多个
- 每个Namespace之间的分隔符在autoloader函数中,将会被转化为目录分隔符DIRECTORY_SEPARATOR
- Class Name即类名称中允许出现_(下划线),但是在autoloader函数中,该下划线也会转化为DIRECTORY_SEPARATOR
- 在autoloader函数中通过namespace和class命名来加载类文件时,类文件的后缀名必须是.php
- 对其中的英文字母大小写没有特殊要求
如果符合了上述要求,那么autoload代码就如下:
- <?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;
- }
- \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\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
PSR-1:简单编码规范
原文中的 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" 等关键字含义请参与RFC 2119文档。并且本文中不会对这些关键字进行翻译。
- 代码文件中MUST使用<?php ?>和<?= ?>两个代码标签;文件编码MUST使用UTF-8必须不包含BOM。
- 单个代码文件SHOULD要么用于声明类,函数或者常量等符号,要么用于进行一些会产生副作用(Side effects)的计算操作,比如输出和改变全局的设置。SHOULD NOT(不建议)同时在一个单文件中完成这两项工作。
- 命名空间和类名称MUST符合PSR_0规范
- 类名称声明MUST符合StudlyCaps(大写骆驼拼写法)
- 类中常量MUST使用全部大写,并使用_(下划线)作为分割符
- 函数名称声明MUST符合camelCase(小写骆驼拼写法)
PSR-2:编码风格
针对PHP文件
- 所有的PHP文件MUST使用Unix的LF作为行结尾
- 所有的PHP文件MUST是以单一的空白行结尾
- 在纯PHP文件中MUST省略文件结尾的?>标记
针对文件行
- MUST NOT对每一行的长度进行硬限制(Hard Limit)
- 每一个行长度软限制(Soft Limit)MUST为120个字符。如果样式检查的软件发现文件不符合该软限制时MUST给予warn但是MUST NOT给予error
- 每一行SHOULD NOT 超过80个字符。如果实在是超过80个字符,SHOULD分割为多行
- 每一个非空白行后面MUST NOT有多余的尾随空格
- 空白行 may(可以)被添加来提高代码的可读性,以及用于分割不相关代码
- 每一行MUST NOT超过一条语句
- 在命名空间声明行后,MUST有一个空白行
文本缩进
- 代码MUST 使用4个空格来进行缩进,MUST NOT使用tabs
PHP关键字大小写
- PHP关键字包括true, false,null在内,MUST 使用小写
命名空间和Use声明
- 在命名空间声明行后,MUST有一个空白行
- Use语句MUST在命名空间声明行后面
- 每一行MUST只能有一个Use声明
- 所有的Use语句后面,必须有一个空白行
- <?php
- namespace Vendor\Package;
- use FooClass;
- use BarClass as Bar;
- use OtherVendor\OtherPackage\BazClass;
- // ... additional PHP code ...
类声明:Extents和implements
- 类声明的{}大括号必须在两个独立行中
- 如果类声明中有extends和implements,那么它们MUST与类声明在同一行
- 如果同时有extends与implements,那么必须先写extends,再写implements
- 如果implements列表有多个,may把他们分割到多行中,每一行单独缩进。如果这样做,那么第一个implements就MUST在新行,并且每个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
- }
类声明/函数声明:abstract,final,static
- 如果一个类或者函数在声明时,其中包含abstract,final,static三个关键字,那么与可见性关键字(public private protected)之间顺序为:abstarct 和final Must在可见性关键字之前;而static MUST在可见性关键字之后
类属性
- 每一个类属性MUST指定它们的可见性
- MUST NOT使用var关键字来声明一个类属性
- 每一条语句MUST声明单个属性
- SHOULD NOT使用_(下划线)作为属性名称的开头来表示属性为protected或private
方法声明与方法参数
- 每一个方法MUST显示指定它们的可见性
- SHOULD NOT使用_(下划线)作为方法名称的开头来表示方法为protected或private
- 方法名称后面MUST NOT带有空格。并且方法的大括号{}MUST分别在单独的行
- 方法的括号()内部的前后MUST NOT有空格,
- 方法的每一个参数后面MUST NOT 有空格,多个参数之间的逗号后面MUST 有空格。
- 含有默认值的参数MUST 放到参数类别后面
- 参数列表may被分割为多行,每行缩进一次;(括号紧跟在行数名后面,)在单独一行,并且与函数声明缩进对齐,后面紧跟一个空格和函数的大括号{。每一个参数MUST在单独行。
- <?php
- namespace Vendor\Package;
- class ClassName
- {
- public function foo($arg1, &$arg2, $arg3 = [])
- {
- // method body
- }
- public function aVeryLongMethodName(
- ClassTypeHint $arg1,
- &$arg2,
- array $arg3 = []
- ) {
- // method body
- }
- }
函数调用
- 在调用函数时,函数名称与(之间MUST NOT有空格,(和)与参数之间MUST NOT 有空格。
- 方法的每一个参数后面MUST NOT 有空格,多个参数之间的逗号后面MUST 有空格。
- 调用参数过长时,May把参数分为多行,每一行一个参数,并且拥有一个缩进。函数的(紧跟在函数名后,)最后一个参数后面的一行,并且不能有缩进。
- <?php
- bar();
- $foo->bar($arg1);
- Foo::bar($arg2, $arg3);
- $foo->bar(
- $longArgument,
- $longerArgument,
- $muchLongerArgument
- );
控制结构
PHP中包含if/else/elseif,switch/case,while/dowhile,for,foreach,try/catch六种控制结构,它们的编码规范有一部分共性:
- 控制关键字后面MUST有一个空格
- 控制结构开括号(后面MUST NOT有空格
- 控制结构闭括号)前面MUST NOT有空格
- 控制结构开括号(与大括号}之间MUST有一个空格
- 控制结构体MUST缩进一次
- 控制结构闭大括号}必须在控制结构体后单独的一行
它们之间除了上述共性以外,还有各自的规范。
控制结构:if/elseif/else
具体的if编码规范见下述实例
- <?php
- if ($expr1) {
- // if body
- } elseif ($expr2) {
- // elseif body
- } else {
- // else body;
- }
请注意上述的空格,括号开闭的位置。
控制结构:switch/case
具体的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;
- }
- 每一个case MUST相对于switch有一个缩进。
- 如果一个case没有break或者return之类中断语句,那么必须在它相应位置注视// no break
控制结构:while/dowhile
具体的while编码规范见下述实例,请注意它们的空格,括号开闭的位置。
- <?php
- while ($expr) {
- // structure body
- }
- do {
- // structure body;
- } while ($expr);
控制结构:for
具体的for编码规范见下述实例,请注意它们的空格,括号开闭的位置。
- <?php
- for ($i = 0; $i < 10; $i++) {
- // for body
- }
控制结构:foreach
具体的foreach编码规范见下述实例,请注意它们的空格,括号开闭的位置。
- foreach ($iterable as $key => $value) {
- // foreach body
- }
控制结构:try/catch
具体的try编码规范见下述实例,请注意它们的空格,括号开闭的位置。
- <?php
- try {
- // try body
- } catch (FirstExceptionType $e) {
- // catch body
- } catch (OtherExceptionType $e) {
- // catch body
- }
闭包
- function和use关键字与括号(之间MUST有一个空格
- 闭包的大开括号MUST与括号)在空一行,并且之间有一个空格
- 闭包的大闭括号MUST与闭包实体部分分割开,在单独一行;并且与闭包语句‘;’之间没有空格
- 闭包的function 和use的参数与括号()之间MUST NOT有空格,并且多个参数之间的‘,’前面没有空格,后面有一个空格
- 如果闭包的参数有默认值,那么它MUST放在参数列表的后面
- 闭包的参数列表可以分多行,具体的请参考实例
- <?php
- $closureWithArgs = function ($arg1, $arg2) {
- // body
- };
- $closureWithArgsAndVars = function ($arg1, $arg2) use ($var1, $var2) {
- // body
- };
- $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
- };
PSR-3:日志接口
PSR-3是关于系统日志的接口的开发规范,先不翻译,后面复习php日志处理再回来翻译
链接:https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md