php代码风格规范

php代码风格规范本规范融合了 PSR 1 中文 和 PSR 2 中文 根据现有项目的状态制定 本规范希望通过制定一系列规范化 PHP 代码的规则 以减少在浏览不同作者的代码时 因代码风格的不同而造成不便

大家好,欢迎来到IT知识分享网。

本规范融合了 PSR-1(中文) 和 PSR-2(中文),根据现有项目的状态制定。

本规范希望通过制定一系列规范化 PHP 代码的规则,以减少在浏览不同作者的代码时,因代码风格的不同而造成不便。

当多名工程师在多个项目中合作时,就需要一个共同的编码规范, 而本文中的风格规范源自于多个不同项目代码风格的共同特性, 因此,本规范的价值在于我们都遵循这个编码风格,而不是在于它本身。

关键词 “必须”(“MUST”)、“一定不可/一定不能”(“MUST NOT”)、“需要”(“REQUIRED”)、 “将会”(“SHALL”)、“不会”(“SHALL NOT”)、“应该”(“SHOULD”)、“不该”(“SHOULD NOT”)、 “推荐”(“RECOMMENDED”)、“可以”(“MAY”)和”可选“(“OPTIONAL”)的详细描述可参见 RFC 2119。

目录

  1. 概览
  2. 文件
  3. 空格
  4. 类的常量、属性和方法
  5. 控制结构
  6. 闭包
  7. 代码太长时的折行方法
  8. 代码按列对齐
  9. 代码注释和文档
  10. 最佳实践
  11. 总结

1. 概览^

  • PHP代码文件必须以 <?php<?= 标签开始。
  • PHP代码文件必须以 不带 BOM 的 UTF-8 编码。
  • PHP代码文件必须以 LF(\n) 作为换行符。
  • 代码必须使用 4 个空格而不是 Tab 键进行缩进。
  • 每行的字符数应该保持在 80 个之内,理论上一定不可多于 100 个,超过限制的折成多行。
  • PHP代码中应该只定义类、函数、常量等声明,或其他会产生 从属效应 的操作(如:生成文件输出以及修改.ini配置文件等),二者只能选其一。
  • 类的命名必须遵循 StudlyCaps 大写开头的驼峰或用下划线 _ 连接小写单词的命名规范。
  • 方法名称必须符合 camelCase 式的小写开头驼峰或用下划线 _ 连接小写单词的命名规范。
  • 类中的常量必须用下划线 _ 连接大写单词的命名规范。
  • 类中的属性必须用下划线 _ 连接小写单词的命名规范。
  • 函数名称必须用下划线 _ 连接小写单词的命名规范。
  • 全局常量必须用下划线 _ 连接大写单词的命名规范。
  • 变量名称必须用下划线 _ 连接小写单词的命名规范。
  • 每个 namespace 命名空间声明语句和 use 声明语句块后面,必须插入一个空白行。
  • 类的开始花括号({)必须写在函数声明后自成一行,结束花括号(})也必须写在函数主体后自成一行。
  • 方法的开始花括号({)必须写在函数声明后自成一行,结束花括号(})也必须写在函数主体后自成一行。
  • 类的属性和方法必须添加访问修饰符(privateprotected 以及 public), abstract 以及 final 必须声明在访问修饰符之前,而 static 必须声明在访问修饰符之后。
  • 类的属性和方法名不该以下划线(_)作为前缀,如 _getTime
  • 控制结构的关键字后必须要有一个空格,而调用方法或函数时则一定不能有。
  • 控制结构的开始花括号({)必须写在声明后自成一行,不能跟在声明后面,而结束花括号(})必须写在主体后自成一行。
  • 控制结构的开始左括号后和结束右括号前,都一定不能有空格。
  • 关键字 以及 true false null 必须全部小写。

1.1. 例子

以下例子程序简单地展示了以上大部分规范:

namespace Vendor\Package; use FooInterface; use BarClass as Bar; use OtherVendor\OtherPackage\BazClass; class Foo extends Bar implements FooInterface { const VERSION = '1.0'; const DATE_APPROVED = '2012-06-01'; public function sampleFunction($user_id, $b = null) { if ($user_id === $b) { bar(); } else if ($user_id > $b) { $foo->bar($arg1); } else { BazClass::bar($arg2, $arg3); } } final public static function bar() { // method body } }

class mod_group extends model implements db_interface { const VERSION = '1.0'; const DATE_APPROVED = '2012-06-01'; public function sample_function($user_id, $b = null) { if ($user_id === $b) { bar(); } else if ($user_id > $b) { $foo->bar($arg1); } else { baz_class::bar($arg2, $arg3); } } final public static function bar() { // method body } }

2. 文件^

2.1. 通则

  • 每个类都独立为一个文件。
  • 多个类方法或函数定义用一个空行分隔。
  • 行尾一定不能有多余的空格。
  • 空行可以使得阅读代码更加方便以及有助于代码的分块。
  • 每行一定不能存在多于一条语句。
  • 所有PHP文件必须使用 Unix LF (linefeed) 作为行的结束符。
  • 所有PHP文件必须以一个空白行作为结束。
  • 纯PHP代码文件必须省略最后的 ?> 结束标签。

2.2. PHP标签

PHP 代码必须使用 <?php ?> 长标签 或 <?= ?> 短输出标签,一定不可使用其它自定义标签。

2.3. 字符编码

PHP 代码必须且只可使用不带 BOM 的 UTF-8 编码。

2.4. 从属效应(副作用)

一份 PHP 文件中应该要不就只定义新的声明,如类、函数或常量等不产生从属效应的操作, 要不就只有会产生从属效应的逻辑操作,但不该同时具有两者。

“从属效应”(side effects)一词的意思是,仅仅通过包含文件,不直接声明类、函数和常量等,而执行的逻辑操作。

“从属效应”包含却不仅限于:生成输出、直接的 requireinclude、 连接外部服务、修改 ini 配置、抛出错误或异常、修改全局或静态变量、读或写文件等。

以下是一个反例,一份包含声明以及产生从属效应的代码:

// 从属效应:修改 ini 配置
ini_set('error_reporting', E_ALL);

// 从属效应:引入文件
include "file.php";

// 从属效应:生成输出
echo "<html>\n";

// 声明函数
function foo()
{
    // 函数主体部分
}

下面是一个范例,一份只包含声明不产生从属效应的代码:

// 声明函数 function foo() { // 函数主体部分 } // 条件声明不属于从属效应 if (! function_exists('bar')) { function bar() { // 函数主体部分 } }

3. 空格^

  • 控制结构的关键字后必须要有一个空格,而调用方法或函数时方法和函数名后一定不能有。
  • 代码注释符 // /* */ 与注释内容之间必须有一个空格。如果在行尾注释,注释符与代码之间必须有一个空格。
  • 函数和方法参数列表中,每个逗号后面必须要有一个空格,而逗号前面一定不能有空格。
  • 数组成员列表中,每个逗号后面必须要有一个空格,而逗号前面一定不能有空格。
  • 方法和函数调用的开始左括号后和结束右括号前,一定不能有空格。
  • 控制结构的开始左括号后和结束右括号前,一定不能有空格。
  • 赋值操作符 = => .= += 等两边必须有一个空格。为了保持列对齐,左边的空格数可以超过一个,右边必须只有一个空格。
  • 字符串连接操作符 . 双边必须有空格。
  • 类型转换操作右括号后面不能有空格, 例如:(int)$uid
  • 类方法调用操作符 -> 两边必须没有空格,除非位于行首。
  • 数组成员操作符 [ ] 两边必须没有空格。
  • 语句终止符 ; 两边必须没有空格。
  • 如下操作符 !(逻辑非)&(引用 ) ++ @(屏蔽错误) ~(位操作) 右边必须不能有空格。
  • 其他操作符 + * / % == == != !== > >= < <= && || as << & instanceof ?: 等两边必须有且只有一个空格。

4. 类的常量、属性和方法^

此处的“类”指代所有的类、接口以及可复用代码块(traits)

4.1. 属性

类的属性命名必须是下划线分隔式 (_)。

每个属性都必须添加访问修饰符。

一定不可使用关键字 var 声明一个属性。

每条语句一定不可定义超过一个属性。

不要使用下划线作为前缀来区分属性是 public,protected 或 private。

以下是属性声明的一个范例:

namespace Vendor\Package; / * Class Description... */ class ClassName { / * @var int */ public $foo = 0; }

4.2. 方法

方法名称必须符合 camelCase() 式的小写开头驼峰或用下划线 _ 连接小写单词的命名规范, 不管选择哪一种,同一个项目中必须保持一致。

所有方法都必须添加访问修饰符。

不要使用下划线作为前缀,来区分方法是 public,protected 或 private。

方法名称后一定不能有空格,其开始花括号必须独占一行,结束花括号也必须在方法主体后单独成一行。

参数左括号后和右括号前一定不能有空格。

一个标准的方法声明可参照以下范例,留意其括号、逗号、空格以及花括号的位置。

namespace Vendor\Package; / * Class Description... */ class ClassName { / * summary... * * @param callable $arg1 回调函数 * @param string $arg2 * @param User[] $arg3 * * @throw Exception 远程调用失败 * * @return false|int[] */ public function fooBarBaz($arg1, &$arg2, array $arg3 = []) { // method body } }

4.3. 方法的参数

参数列表中,每个逗号后面必须要有一个空格,而逗号前面一定不能有空格。

有默认值的参数,必须放到参数列表的末尾。

数组类型和自定义类型的参数必须写明。

namespace Vendor\Package; / * Class Description... */ class ClassName { / * summary... * * @param callable $arg1 回调函数 * @param string $arg2 * @param User[] $arg3 * * @throw Exception 远程调用失败 * * @return false|int[] */ public function foo($arg1, User &$arg2, array $arg3 = []) { // method body } }

参数列表可以分列成多行,这样,包括第一个参数在内的每个参数都必须单独成行。

拆分成多行的参数列表后,结束括号以及方法开始花括号必须写在独立的一行。

namespace Vendor\Package; / * Class Description... */ class ClassName { / * summary... * * @param ClassTypeHint $arg1 * @param string $arg2 * @param User[] $arg3 * * @throw Exception 远程调用失败 * * @return bool */ public function aVeryLongMethodName( ClassTypeHint $arg1, &$arg2, array $arg3 = [] ) { // method body } }

4.4. 方法及函数调用

方法及函数调用时,方法名或函数名与参数左括号之间一定不能有空格, 参数右括号前也 一定不能有空格。每个参数前一定不能有空格,但其后必须有一个空格。

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

参数可以分列成多行,此时包括第一个参数在内的每个参数都必须单独成行。

$foo->bar( $long_argument, $longer_argument, $much_longer_argument );

4.5. namespace 以及 use 声明

namespace 声明后必须插入一个空白行。

所有 use 必须namespace 后声明。

每条 use 声明语句必须只有一个 use 关键词。

use 声明语句块后必须要有一个空白行。

例如:

namespace Vendor\Package; use FooClass; use BarClass as Bar; use OtherVendor\OtherPackage\BazClass; // ... additional PHP code ...

4.6. 扩展与继承

关键词 extendsimplements必须写在类名称的同一行。

类的开始花括号必须独占一行,结束花括号也必须在类主体后独占一行。

namespace Vendor\Package; use FooClass; use BarClass as Bar; use OtherVendor\OtherPackage\BazClass; / * Class Description.... */ class ClassName extends ParentClass implements \ArrayAccess, \Countable { // constants, properties, methods }

implements 的继承列表也可以分成多行,这样的话,每个继承接口名称都必须分开独立成行,包括第一个。

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.7.abstract、final、 以及static

需要添加 abstractfinal 声明时, 必须写在访问修饰符前,而 static必须写在其后。

namespace Vendor\Package; / * Abstract Class */ abstract class ClassName { / * @var string */ protected static $foo; / * Method zim */ abstract protected function zim(); / * Method bar * * @return void */ final public static function bar() { // method body } }

5. 控制结构^

  • 控制结构关键词后必须有一个空格。
  • 左括号 ( 后一定不能有空格。
  • 右括号 ) 前也一定不能有空格。
  • 结构体主体一定要有一次缩进。
  • 开始花括号 { 一定在结构声明后单独成行。
  • 结束花括号 } 一定在结构体主体后单独成行。
  • 每个结构体的主体都必须被包含在成对的花括号之中。

5.1.if、else if和else

标准的 if 结构如下代码所示,留意括号、空格以及花括号的位置,注意 elseelse if 都与前面的结束花括号不在同一行。

else if 也可以写成 elseif

if ($expr1) { // if body } else if ($expr2) { // else if body } else { // else body; }

一定不能写成下面这样:

if ($expr1) unset($list['count']); else unset($list); while ($expr2) $i++;

5.2.switch和case

标准的 switch 结构如下代码所示,留意括号、空格以及花括号的位置。 case 语句必须相对 switch 进行一次缩进,而 break 语句以及 case 内的其它语句都必须相对 case 进行一次缩进。 如果存在非空的 case 直穿语句,主体里必须有类似 // no break 的注释。

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 语句应该如下所示,注意其括号、空格以及花括号的位置。

while ($expr) { // structure body }

标准的 do while 语句如下所示,同样的,注意其括号、空格以及花括号的位置。

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

5.4.for

标准的 for 语句如下所示,注意其括号、空格以及花括号的位置。

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

5.5.foreach

标准的 foreach 语句如下所示,注意其括号、空格以及花括号的位置。

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

5.6.try,catch

标准的 try catch 语句如下所示,注意其括号、空格以及花括号的位置。

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

6. 闭包^

闭包声明时,关键词 function 后以及关键词 use 的前后都必须要有一个空格。

开始花括号必须写在声明的下一行,结束花括号必须紧跟主体结束的下一行。

参数列表和变量列表的左括号后以及右括号前,必须不能有空格。

参数和变量列表中,逗号前必须不能有空格,而逗号后必须要有空格。

闭包中有默认值的参数必须放到列表的后面。

标准的闭包声明语句如下所示,注意其括号、逗号、空格以及花括号的位置。

$closure_with_args = function ($arg1, $arg2) { // body }; $closure_with_args_and_vars = function ($arg1, $arg2) use ($var1, $var2) { // body };

参数列表以及变量列表可以分成多行,这样,包括第一个在内的每个参数或变量都必须单独成行, 而列表的右括号与闭包的开始花括号必须放在独立的一行。

以下几个例子,包含了参数和变量列表被分成多行的多情况。

$long_args_no_vars = function( $long_argument, $longer_argument, $much_longer_argument ) { // body }; $no_args_long_vars = function () use ( $long_var1, $longer_var2, $much_longer_var3 ) { // body }; $long_args_long_vars = function ( $long_argument, $longer_argument, $much_longer_argument ) use ( $long_var1, $longer_var2, $much_longer_var3 ) { // body }; $long_args_short_vars = function ( $long_argument, $longer_argument, $much_longer_argument ) use ($var1) { // body }; $short_args_long_vars = function ($arg) use ( $long_var1, $longer_var2, $much_longer_var3 ) { // body };

注意,闭包被直接用作函数或方法调用的参数时,以上规则仍然适用。

$foo->bar( $arg1, function ($arg2) use ($var1) { // body }, $arg3 );

7. 代码太长时的折行方法^

每行的字符数应该保持在 80 个之内,理论上一定不可多于 100 个,超过限制的折成多行。

注意括号、空格以及花括号的位置。

7.1.ifelse ifwhile条件太长

条件太长时按判断逻辑分行,每行一个判断逻辑,逻辑操作符放在行尾,每个判断逻辑与第一个列对齐。

范例:

if ($update_time > 0 && is_string($user_name) && ($is_admin || $level < 1000)) { // if body } else if ($update_time > (time() - 7 * 86400) || $gender == 1) { // else if body } else { $time_limit = time() + 3 * 86400; while ($count < 100 && $update_time < $time_limit && !$is_admin) { // while body } }

反例:

if ($update_time > 0 && is_string($user_name) && && ($is_admin || $level < 1000)) { // if body } else if ($update_time > (time() - 7 * 86400) || $gender == 1) { // else if body } else { $time_limit = time() + 3 * 86400; while ($count < 100 && $update_time < $time_limit && !$is_admin) { // while body } }

7.2 函数或方法定义的参数太多时

参数列表可以分列成多行,这样,包括第一个参数在内的每个参数都必须单独成行,比访问修饰符或 function 多一次缩进。

范例:

/ * User Model */ class User { / * 获取多个用户的信息 * * @param int $gid * @param int[] $uids * @param string[] $fields 需要查询的字段 * @param bool $use_cache 是否使用缓存 * * @return mixed[] */ public function getMulti( $gid, array $uids, array $fields = [], $use_cache = true ) { // method body } }

反例:

/ * User Model */ class User { / * 获取多个用户的信息 * * @param int $gid * @param int[] $uids * @param string[] $fields 需要查询的字段 * @param bool $use_cache 是否使用缓存 * * @return mixed[] */ public function getMulti($gid, array $uids, array $fields = [], $use_cache = true) { // method body } }

7.3 调用函数或方法的参数太多时

参数列表可以分列成多行,这样,包括第一个参数在内的每个参数都必须单独成行,比函数名多一次缩进。

范例:

function foo() { $gid = 123; $user = new User(); $list = $user->getMultiFromDB( $gid, [11, 22, 33, 44, 55, 66, 77, 88] ['user_id', 'user_name', 'level', 'face'] ); }

反例:

function foo() { $gid = 123; $user = new User(); $list = $user->getMultiFromDB($gid, [11, 22, 33, 44, 55, 66, 77, 88] ['user_id', 'user_name', 'level', 'face']); }

7.4. 方法链式调用对齐

当方法链式调用需要折行时,-> 需要列对齐。

范例:

function foo() { $group = new Group(123); $list = $group->setName('banana', true) ->updateListCache() ->getAll() ->formatList(); }

反例:

function foo() { $group = new Group(123); $list = $group->setName('banana', true)->updateListCache() ->getAll() ->formatList(); } function bar() { $group = new Group(123); $list = $group->setName('banana', true) ->updateListCache() ->getAll() ->formatList(); }

7.5. 三元操作

范例:

function foo() { $title = isset($row['data']['title']) ? $row['data']['title'] : $row['data']['content']; }

反例:

function foo() { $title = isset($row['data']['title']) ? $row['data']['title'] : $row['data']['content']; }

7.6. 字符串太长

范例:

$sql = "SELECT `user_id`, `user_name`, `age` " . "FROM `users` " . "WHERE `user_id` !=  AND `age` >= 18 " . "ORDER BY `age`";

反例:

$sql = "SELECT `user_id`, `user_name`, `age` " . "FROM `users` " . "WHERE `user_id` !=  AND `age` >= 18 " . "ORDER BY `age`";
$sql = "SELECT `user_id`, `user_name`, `age` FROM `users` WHERE `user_id` !=  AND `age` >= 18 ORDER BY `age`";

8. 代码按列对齐^

8.1. 变量定义

多个连续的局部变量定义可以按 = 列对齐,但变量名长度差别太大时不该对齐。

范例:

$user_name = 'John'; $type = 34; $list[3] = [1, 2, 3];

反例:

$uid = 123; $this_is_a_long_variable = 32; $result['list'][0]['type'] = 3;

8.2. 数组定义

  • 推荐使用 [] 而不是 array() 定义数组。
  • 数组定义必须一次定义完整,不允许使用多条赋值语句定义一个数组。
  • 超过两个成员的键值数组定义,必须每行定义一对键值。
  • 键值数组的赋值操作符应该按列对齐。
  • 最后一个成员必须以逗号结束。
  • 如果多个成员写在同一行,分隔成员的逗号之后必须有且只有一个空格。
  • 只有值的数组,在成员很多时可以定义成多行,每行多个成员。

范例:

$count = 32; $result = [ 'count' => $count, 'list' => [ [ 'user_id' => 123, 'type' => 88, ], [ 'user_id' => 124, 'type' => 89, ], ], ]; $types = ['123', 4555, 'abc']; $uids = [ 123, 456, 789, 1123, 1456, 1789, 2123, 2456, 2789, ]; $map = ['a' => 3, 'b' => 4];

反例:

$count = 32; $result = []; $result['count'] = $count; $result['list'] = [ [ 'user_id' => 123, ] ]; $map = ['a' => 3, 'b' => 5, 'x' => 32, 'y' => 'blah...', 'z' => 'long, long ago'];

9. 代码注释和文档^

  • 只允许使用 // /* */ 作为注释符,注释符与注释内容之间必须有一个空格
  • 文件头可以注释。
  • 类、类方法、类常量、类成员变量,函数,参数必须有文档类型的注释。
  • 文档型注释遵从 phpDocumentor 的规范。
  • 类方法和函数的文档型注释分为 5 段,摘要 详情 参数注释 异常注释 返回值注释,每段之间必须有一个空行,详情 可以省略。
  • PHP 内置类型的注释分为 string int float bool resource null callable, 根据 phpDocumentor 规范,还支持 mixed void object false true self $this
  • 变量或参数类型如果是数组,必须写明成员类型,如 int[]User[],如果是混和类型则为 mixed[]
  • 建议设置 todo 提醒将来要完成的代码。

范例:

注意空格,空行

/ * 类说明 */ class ClassName { / * 摘要...... * * 详情............................ * 详情............................ * * @param User $user * @param int $argv1 变量说明 * @param string $arg2 * @param resource[] $arg3 optional * * @throws InvalidArgumentException 参数错误 * @throws DBException 数据库操作失败 * * @return false|array */ public function fooBarBaz(User $user, $arg1, &$arg2, array $arg3 = []) { if (!is_numeric($arg1)) { throw new InvalidArgumentException('arg1 invalid'); } $user_id = $user->getId(); $is_admin = $user->isAdmin(); // level 大于 3 的都是管理员 if ($is_admin) { $user->foo($argv1); $list = $user->baz(); // 只返回最新一天的记录 $start_time = time() - 86400; $list = array_filter( function ($i) use ($start_time) { return $i['update_time'] > $start_time; }, $list ); return $list; } else { // todo 如果不是管理员 return false; } } }

10. 最佳实践^

  • 一定不能使用执行命令的操作符,例如 ls -ls
  • 逻辑操作符不该使用 and or xor,应该使用 && ||
  • 局部变量应该定义在开始使用的地方。
  • 函数和方法不要太长,应该把复杂的逻辑分解成独立函数或方法,即使函数或方法只有一个地方调用。

11. 总结^

以上规范难免有疏忽,本规范之后的修订将弥补不足之处。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/96372.html

(0)
上一篇 2026-03-12 17:45
下一篇 2023-09-05 21:33

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信