remy-theroux / php-convention
现代PHP应用的PHP约定集合
Requires
- php: ^7.3
- dealerdirect/phpcodesniffer-composer-installer: ^0.5.0
- escapestudios/symfony2-coding-standard: ^3.9
This package is auto-updated.
Last update: 2024-08-28 16:43:29 UTC
README
用法
vendor/bin/phpcs --standard=ModernPhp your_path
故障排除
标准必须通过composer插件在package dealerdirect/phpcodesniffer-composer-installer中自动安装
如果您遇到此错误 'PHP_CodeSniffer_Exception' with message 'Referenced sniff "ModernPhp" does not exist'
手动将标准添加到您的安装路径: ./vendor/bin/phpcs --config-set installed_paths $PWD/vendor/remy-theroux/php-convention
目录
文件
-
仅使用
UTF-8 without BOM
。 -
仅使用Unix LF(换行符)行结束。
-
所有PHP文件都必须以一个空行结束。
-
非视图脚本使用长
<?php ?>
标签。
<?php echo 'test';
- 视图脚本使用短-echo
<?= ?>
标签。
<title><?=∙$title;∙?></title>
-
仅含PHP的文件中必须省略关闭的
?>
标签。 -
行长度限制必须为
200个字符
。 -
文件必须只包含一个命名空间语句。
-
代码必须使用
4个空格
进行缩进,而不是制表符。
行
- 可以添加空行以提高可读性并指示相关的代码块。
// nah function foo() { $foo = 'test'; $foo = strtoupper($foo); return $foo; } // good function bar() { $foo = 'test'; $foo = strtoupper($foo); return $foo; }
- 每行不能有多个语句。
// bad $foo = substr(strtoupper('test'), 0, 1); // good $foo = 'test'; $foo = strtoupper($foo); $foo = substr($foo, 0, 1);
关键字
- 所有PHP关键字都必须小写(例如:
true
、false
、null
等)
命名空间和使用声明
- 命名空间名称必须声明为
UpperCamelCase
。
// bad namespace Vendor\fooBar; // bad namespace Vendor\foo_bar; // good namespace Vendor\FooBar;
-
命名空间声明永远不以反斜杠开始
Vendor\Space\Space
。 -
在
namepsace
声明前后必须有一个空行。 -
在
use
声明块后必须有一个空行。 -
use
声明不能由逗号分隔。 -
use
块声明必须按包分组
// bad use Foo\Bar, Qux\Quux, Foo\Baz; // bad use Foo\Bar; use Qux\Quux; use Foo\Baz; // good use Foo\Bar; use Foo\Baz; use Qux\Quux; use Qux\Corge\Grault;
use
别名声明应与子命名空间名称一起组成。
// bad use Foo\Bar as Baz; // bad use Baz\Qux\Quux as BQQ; // good use Foo\Bar as FooBar; // good use Baz\Qux\Quux as BazQuxQuux;
注释
行内代码注释
- 注释应位于单独的一行上,紧接在被注释的代码行或代码块之前。
// bad $foo = 'bar'; // Bar in foo // good // Foo assignment for example $foo = 'bar'; // good // Foo assignment // for example $foo = 'bar';
代码块注释
- 您必须为所有类、方法和函数添加PHPDoc块,但如果方法不返回任何内容,则可以省略
@return
标签。
/** * Foo * */ class Foo { /** * The description of bar * * @param string $baz The baz * * @return string The return of bar */ public function bar($baz) { // Returned value return 'Do something...'; } }
- 您必须为所有变量引用添加PHPDoc块。
/** @var Bar\Baz $foo Baz object */
$foo = $bar->baz();
/** * Foo * */ class Foo { /** @var string $bar It's bar! */ public $bar = ''; }
- 您不能在PHPDoc块中使用完全限定的类名。这意味着即使在PHP块的其他地方没有引用,您也必须使用use声明来声明类名。
// Bad namespace Vendor\Bar\Baz; /** * Foo * */ class Foo { /** @var \Other\MyClass $myClass */ protected $myClass; /** * @return \Other\MyClass */ public function getMyClass() { return $this->myClass; } }
// Good namespace Vendor\Bar\Baz; use Other\MyClass; /** * Foo * */ class Foo { /** @var MyClass $myClass */ protected $myClass; /** * @return MyClass */ public function getMyClass() { return $this->myClass; } }
@todo
和@fixme
必须在PHPDoc块中像注释一样使用。
/** @todo Think to check value */ $foo = 'bar'; /** @fixme Change qux to quux */ $baz = 'qux';
使用对象时请指明
- 当您从抽象方法获取对象时,应添加
@var
标签
// bad $logger = $this->getServiceLocator()->get('logger'); // bad $this->getServiceLocator()->get('AwesomeFactory')->createAwesomeness(); // good /** @var LoggerInterface $logger */ $logger = $this->getServiceLocator()->get('logger'); // good /** @var AwesomeFactory $awesomeFactory */ $awesomeFactory = $this->getServiceLocator()->get('AwesomeFactory'); $awesomeFactory->createAwesomeness()
- 从显式方法获取对象时,不应添加
@var
标签
// bad /** * Class AwesomeFactory */ class AwesomeFactory { /** * @return Awesome */ public function createAwesomeness() { return Awesome(); } } $awesomeFactory = new AwesomeFactory(); /** @var Awesome $awesome */ $awesome = $awesomeFactory->createAwesomeness(); // good /** * Class AwesomeFactory */ class AwesomeFactory { /** * @return Awesome */ public function createAwesomeness() { return Awesome(); } } $awesomeFactory = new AwesomeFactory(); $awesome = $awesomeFactory->createAwesomeness();
命名
- 在变量、方法和类名中,清晰度优于简洁性
// bad $o = new Object(); // bad class A { } // bad public function doIt() { } // good $object = new Object(); // good class Substracter { } // good public function associateChannelToOperator() { }
Boolean
变量名应该是形容词或过去分词形式。相关getter方法应以is
或has
开头。
// bad $enablePlugin = true; // bad public function getEnablePlugin() {} // bad public function getPluginEnabled() {} // good $pluginEnabled = true; // good $visible = true; // good public function isPluginEnabled() {} // good public function isVisible() {}
DateTime
变量名应该是以At
结尾的过去分词形式。
// bad $dateUpdate = new \DateTime; // bad $endDate = new \DateTime; // good $updatedAt = new \DateTime; // good $lastLoggedAt = new \DateTime;
变量
用户变量
- 变量应使用小驼峰命名法
lowerCamelCase
。
// bad $_foo=''; // bad $foo_bar = ''; // bad $fooBar=''; // good $fooBar∙=∙'';
- 必须用空格分隔操作符。
// bad $foo = (5+6)/5; // good $foo∙=∙(5∙+∙6)∙/∙5;
- 必须保持良好的对齐。
// bad $foo = 'Ba'; $foo .= 'r'; $quux = 'Qu'; $quux .= 'x'; // good $foo = 'Ba'; $foo .= 'r'; $quux = 'Qu'; $quux .= 'x';
// bad $fooBarBazQux->bar()-> baz()->qux(); // good $fooBarBazQux ∙∙∙∙->bar() ∙∙∙∙->baz() ∙∙∙∙->qux();
全局变量
- 必须使用
$_POST
、$_GET
和$_COOKIE
而不是$_REQUEST
。如果使用框架,请使用Request
组件。
字符串
-
字符串必须用单引号括起来
'hello'
。 -
连接字符串必须使用单引号
'foo' . $bar
-
连接字符串必须在使用点符号时周围有空格
'foo' . $bar
-
多行字符串声明必须对齐
$foo = 'Bar' .'Baz' .'Qux';
- 字符串不得与函数或方法连接
// bad $foo = ucfisrt('bar') . ' baz'; // good $foo = ucfirst('bar'); $foo = $foo . ' baz'; // very good $foo = ucfirst('bar'); $foo .= ' baz';
常量
类常量
- 常量应使用大写蛇形命名法
UPPER_SNAKE_CASE
。
// bad const MAXSIZE=5; // bad const max_size∙=∙4; // good const MAX_SIZE∙=∙5;
- 必须用空格设置赋值运算符。
// bad const MAX_SIZE=5; // good const MAX_SIZE∙=∙5;
- 必须保持良好的对齐。
// bad const FOO∙∙=∙'Ba'; const FOO∙∙=∙'r'; const QUUX∙=∙'Qu'; const QUUX∙=∙'x'; // good const FOO∙∙=∙'Ba'; const FOO∙∙=∙'r'; const QUUX∙=∙'Qu'; const QUUX∙=∙'x';
类型转换
-
必须使用
(int) $foo
而不是intval($foo)
。 -
必须使用
(bool) $foo
而不是boolval($foo)
。 -
必须使用
(float) $foo
而不是floatval($foo)
。 -
必须使用
(string) $foo
而不是strval($foo)
。
// bad $foo∙=∙(string)$bar; // good $foo∙=∙(string)∙$bar;
数组
-
必须使用
[]
符号而不是array()
。 -
少量数据的数组必须这样声明
$foo∙=∙['Bar',∙'Baz',∙'Qux'];
- 大量数据的数组必须这样声明
$foo = [ ∙∙∙∙'bar'∙∙=>∙'abc', ∙∙∙∙'baz'∙∙=>∙123, ∙∙∙∙'qux'∙∙=>∙true, ∙∙∙∙'quux'∙=>∙[ ∙∙∙∙∙∙∙∙'corge'∙∙=>∙[], ∙∙∙∙∙∙∙∙'grault'∙=>∙123.456, ∙∙∙∙], ];
- 对于大量数据的数组,行必须以逗号结束。(易于复制粘贴)
类、属性和方法
类
-
extends
和implements
关键字应与类名在同一行声明。 -
类的开头大括号必须单独一行;类的结尾大括号必须在主体后下一行。
<?php namespace Vendor\Foo; class Foo extends Bar implements Baz, Qux, Quux { // Do something... }
- 实现列表可以跨多行,其中每一行后续都缩进一次。这样做时,列表中的第一项必须在下一行,并且每行只能有一个接口。
<?php namespace Vendor\Foo; class Foo extends Bar implements ∙∙∙∙Baz, ∙∙∙∙Qux, ∙∙∙∙Quux { // Do something... }
属性
- 必须声明所有属性的可访问性。
// bad /** @var string Property description */ $foo = ''; // good /** @var string Property description */ public $foo = '';
- 每个语句中声明的属性不得超过一个。
// bad public $foo = '', $bar = ''; // good /** @var string Property description */ public $foo = ''; /** @var string Property description */ protected $bar = '';
- 属性名不得以单下划线开头以表示
protected
或private
可见性。
// bad /** @var string Property description */ protected $_bar = ''; /** @var string Property description */ private $_baz = ''; // good /** @var string Property description */ protected $bar = ''; /** @var string Property description */ private $baz = '';
- 如果有,则
static
声明必须放在可见性声明之后。
// bad /** @var string $foo Property description */ static public $foo = ''; // good /** @var string $foo Property description */ public static $foo = '';
方法
-
必须声明所有方法的可访问性。(例如:
public|protected|private foo()
) -
方法名不应以单下划线开头以表示受保护的或私有的可见性。
// bad protected function _foo() { // Do something... } // good protected function foo() { // Do something... }
- 方法名后不得有空格。
// bad public function foo∙() { // Do something... } // good public function foo() { // Do something... }
- 在括号打开后和关闭前不得有空格。
// bad public function foo()∙{∙ // Do something... ∙} // good public function foo() { // Do something... }
- 开头大括号必须单独一行,结尾大括号必须跟在主体后下一行。
// bad public function foo()∙{ // Do something...} // good public function foo() { // Do something... }
- 在参数列表中,每个逗号前不得有空格,每个逗号后必须有一个空格。
// bad public function foo($bar∙,∙&$baz∙,∙$qux = []) { // Do something... } // bad public function foo(∙$bar, &$baz, $qux = []∙) { // Do something... } // good public function foo($bar,∙&$baz,∙$qux∙=∙[]) { // Do something... }
-
参数列表可以跨多行,其中每一行后续都缩进一次。
-
这样做时,列表中的第一项必须在下一行,并且每行只能有一个参数。
-
当参数列表跨多行时,括号和花括号必须放在它们自己的行上,并且它们之间有一个空格。
// good public function foo( ∙∙∙∙$bar, ∙∙∙∙&$baz, ∙∙∙∙$qux = [] ) { // Do something... }
-
如果有,则
abstract
和final
声明必须放在可见性声明之前。 -
如果有,则
static
声明必须放在可见性声明之后。
// bad protected abstract function foo(); static public final function bar() { // Do something... } // good abstract protected function foo(); final public static function bar() { // Do something... }
接口、特质
接口
- 接口名称必须以
Interface
结尾。
<?php namespace Vendor\Foo; /** * Interface Foo * */ interface FooInterface { /** * Set Foo * * @param string $foo */ public function setFoo($foo); }
特质
- 特质名称必须以
Trait
结尾。
<?php namespace Vendor\Foo; /** * Trait Foo * */ trait FooTrait { /** @var \Vendor\Bar */ protected $bar; /** * Set Bar * * @param string $bar */ public function setBar($bar) { $this->bar = $bar; } }
函数和方法调用
- 方法或函数名与打开括号之间不得有空格。
// bad foo∙(); $bar->baz∙(); // good foo(); $bar->baz();
- 在参数列表中,打开括号后和关闭括号前不得有空格。
// bad foo(∙$qux∙); $bar->baz(∙$qux∙); // good foo($qux); $bar->baz($qux);
- 每个逗号前不得有空格,每个逗号后必须有一个空格。
// bad foo($bar∙,∙$baz∙,∙$qux); // good foo($bar,∙$baz,∙$qux);
- 参数列表可能跨越多行,每行后续的缩进一次。这样做时,列表中的第一个项目必须在下一行,并且每行只能有一个参数。
// bad foo($longFoo, $longBar, $longBaz ); // bad foo($longFoo, $longBar, $longBaz); // good foo( ∙∙∙∙$longFoo, ∙∙∙∙$longBar, ∙∙∙∙$longBaz );
- 链式方法调用必须在第一个调用之前包裹,并缩进一次。
// bad $fooBar->baz()->qux($param); // good $fooBar ->baz() ->qux($param);
- 当您仅作为参数传递数组时,数组括号应与方法括号在同一行。
// bad foo( [ 'foo' => 'bar', ] ); // good foo([ 'foo' => 'bar', ]);
控制结构
一般
-
控制结构关键字之后必须有一个空格
-
开括号之后不允许有空格
-
闭括号之前不允许有空格
-
闭括号与开大括号之间必须有一个空格
-
结构体必须缩进一次
-
闭大括号必须在体之后的一行
-
每个结构体的体必须用大括号括起来。这标准化了结构体的外观,并减少了在体中添加新行时引入错误的可能性。
if
, elseif
, else
- 应使用关键字
elseif
而不是else if
,以便所有控制关键字看起来像单个单词。
示例
// bad if(EXPRESSION){ // Do something... } // bad if (EXPRESSION) { // Do something... } // bad if (EXPRESSION) { // Do something... } // bad if (EXPRESSION) { // Do something... } else { // Do something... } // good if∙(EXPRESSION)∙{ ∙∙∙∙// Do something... }∙elseif∙(OTHER_EXPRESSION)∙{ ∙∙∙∙// Do something... }∙else∙{ ∙∙∙∙// Do something... }
三元运算符 (?:
)
- 不应使用嵌套的三元运算符。
示例
// bad $foo = EXPRESSION ? 'bar' : OTHER_EXPRESSION ? 'baz' : 'qux'; // good $foo∙=∙EXPRESSION∙?∙'bar'∙:∙'baz'; // good $foo∙=∙EXPRESSION ∙∙∙∙?∙'bar' ∙∙∙∙:∙'baz';
switch
和 case
-
case
语句必须从switch
缩进一次,并且break
关键字(或其他终止关键字)必须与case
体处于同一缩进级别。 -
当非空
case
体中的贯穿是故意的时,必须有一个如// no break
的注释。
示例
// bad switch(EXPRESSION) { case 0: // Do something... break; } // good switch∙(EXPRESSION)∙{ ∙∙∙∙case∙0: ∙∙∙∙∙∙∙∙// Do something... ∙∙∙∙∙∙∙∙break; ∙∙∙∙case∙1: ∙∙∙∙∙∙∙∙// Do something with no break... ∙∙∙∙∙∙∙∙// no break ∙∙∙∙case∙2: ∙∙∙∙case∙3: ∙∙∙∙case∙4: ∙∙∙∙∙∙∙∙// Do something with return instead of break... ∙∙∙∙∙∙∙∙return; ∙∙∙∙default: ∙∙∙∙∙∙∙∙// Do something in default case... ∙∙∙∙∙∙∙∙break; }
while
和 do while
示例
// bad while(EXPRESSION) { // Do something... } // bad do { // Do something... } while(EXPRESSION); // good while∙(EXPRESSION)∙{ ∙∙∙∙// Do something... } // good do∙{ ∙∙∙∙// Do something... }∙while∙(EXPRESSION);
for
示例
// bad for( $i=0;$i<10;$i++ ) { // Do something... } // good for∙($i∙=∙0;∙$i∙<∙10;∙$i++)∙{ ∙∙∙∙// Do something... }
foreach
示例
// bad foreach( $foo as $key=>$value ) { // Do something... } // good foreach∙($foo∙as∙$key∙=>∙$value)∙{ ∙∙∙∙// Do something... }
try
和 catch
示例
// bad try { // Do something... } catch(FooException $e) { // Do something... } // good try∙{ ∙∙∙∙// Do something... }∙catch∙(FooException∙$exception)∙{ ∙∙∙∙// Do something... }∙catch∙(BarException∙$exception)∙{ ∙∙∙∙// Do something... }∙finally∙{ ∙∙∙∙// Do something... }
闭包
-
闭包必须使用函数关键字之后有空格,并在使用关键字前后都有空格来声明。
-
开大括号必须在同一行,闭大括号必须紧跟在体之后的一行。
-
参数列表或变量列表的开括号之后不允许有空格,并且参数列表或变量列表的闭括号之前不允许有空格。
-
在参数列表和变量列表中,每个逗号之前不允许有空格,并且每个逗号之后必须有一个空格。
-
具有默认值的闭包参数必须放在参数列表的末尾。
-
参数列表和变量列表可能跨越多行,每行后续的缩进一次。
-
这样做时,列表中的第一个项目必须在下一行,并且每行只能有一个参数或变量。
-
当结束列表(无论参数还是变量)跨越多行时,闭括号和开大括号必须放在它们自己的行上,并彼此之间有一个空格。
示例(声明)
// good $closureWithArguments∙=∙function∙($foo,∙$bar)∙{ ∙∙∙∙// Do something... }; // good $closureWithArgumentsAndVariables∙=∙function∙($foo,∙$bar)∙use∙($baz,∙$qux)∙{ ∙∙∙∙// Do something... }; // good $longArgumentsNoVariables∙=∙function∙( ∙∙∙∙$longArgumentFoo, ∙∙∙∙$longArgumentBar, ∙∙∙∙$longArgumentBaz )∙{ ∙∙∙∙// Do something... }; // good $noArgumentsLongVariables∙=∙function∙()∙use∙( ∙∙∙∙$longVariableFoo, ∙∙∙∙$longVariablBar, ∙∙∙∙$longVariableBaz )∙{ ∙∙∙∙// Do something... }; // good $longArgumentsLongVariables∙=∙function∙( ∙∙∙∙$longArgumentFoo, ∙∙∙∙$longArgumentBar, ∙∙∙∙$longArgumentBaz )∙use∙( ∙∙∙∙$longVariableFoo, ∙∙∙∙$longVariableBar, ∙∙∙∙$longVariableBaz )∙{ ∙∙∙∙// Do something... }; // good $longArgumentsShortVariables∙=∙function∙( ∙∙∙∙$longArgumentFoo, ∙∙∙∙$longArgumentBar, ∙∙∙∙$longArgumentBaz )∙use∙($variableFoo)∙{ ∙∙∙∙// Do something... }; // good $shortArgumentsLongVariables∙=∙function∙($argumentFoo)∙use∙( ∙∙∙∙$longVariableFoo, ∙∙∙∙$longVariableBar, ∙∙∙∙$longVariableBaz )∙{ ∙∙∙∙// Do something... };
示例(使用)
$foo->bar( ∙∙∙∙$argumentFoo, ∙∙∙∙function∙($argumentBar)∙use∙($variableFoo)∙{ ∙∙∙∙∙∙∙∙// Do something... ∙∙∙∙}, ∙∙∙∙$argumentBaz );
最佳实践
日期
-
必须使用
new \DateTime('2014-01-01 00:00:00')
而不是date('2014-01-01 00:00:00')
。 -
必须使用
new \DateTime('Sunday')
而不是strtotime('Sunday')
。
可读性
- 在可能的情况下,避免超过2级的嵌套,并优先使用“尽早返回”结构。
// bad $response = []; if ($foo) { foreach ($foo->getBars() as $bar) { if ($bar->hasBaz()) { // 3 nested levels } } } return $response; // good $response = []; if (!$foo) { return $response; } foreach ($foo->getBars() as $bar) { if ($bar->hasBaz()) { // only 2 nested levels } } return $response;