iadvize/php-convention

Iadvize PHP 规范

1.2.0 2024-05-23 14:42 UTC

README

目录

  1. IDE 集成
  2. 文件
  3. 行数
  4. 关键词
  5. 注释
  6. 命名
  7. 变量
  8. 常量
  9. 类型转换
  10. 命名空间和使用声明
  11. 字符串
  12. 数组
  13. 类、属性和方法
  14. 接口、特质
  15. 函数和方法调用
  16. 控制结构
  17. 闭包
  18. 最佳实践

IDE 集成

Configure your PHPStorm

故障排除

如果你有这个错误 phpcs: PHP 致命错误: 未捕获的异常 'PHP_CodeSniffer_Exception',信息 '引用的 sniff "Symfony2" 不存在'

运行以下命令: ./vendor/bin/phpcs --config-set installed_paths $PWD/vendor/escapestudios/symfony2-coding-standard,$PWD/vendor/iadvize/php-convention/phpcs

文件

  • 只使用 UTF-8 无 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 关键字必须小写(例如:truefalsenull 等)

命名空间和使用声明

  • 命名空间名称必须使用 UpperCamelCase 声明。
// bad
namespace Vendor\fooBar;

// bad
namespace Vendor\foo_bar;

// good
namespace Vendor\FooBar;
  • 命名空间声明从不以反斜杠开始 Vendor\Space\Space

  • 命名空间声明前后必须有一个空白行。

  • 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 块中使用完全限定的类名。这意味着即使 PHPBlock 中没有其他地方引用该类,你也必须使用 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 变量名应该是形容词或过去分词形式。相关的获取方法应以 ishas 开头。
// 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,
∙∙∙∙],
];
  • 对于大量数据的数组,行必须以逗号结尾。(易于复制/粘贴)

类、属性和方法

  • extendsimplements 关键字应与类名在同一行上声明。

  • 类的开头大括号必须单独占一行;类的结尾大括号必须在体之后的下一行上。

<?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 = '';
  • 属性名不得以单个下划线为前缀以表示 protectedprivate 可见性。
// 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...
}
  • 如果存在,则 abstractfinal 声明必须放在可见性声明之前。

  • 如果存在,则 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',
]);

控制结构

一般

  • 控制结构关键字后必须有一个空格

  • 在开括号后不允许有空格

  • 在闭括号前不允许有空格

  • 在闭括号和开大括号之间必须有一个空格

  • 结构体必须缩进一次

  • 闭大括号必须在体之后的下一行

  • 每个结构体的体必须被括号包围。这标准化了结构体的外观,并减少了在体中添加新行时引入错误的可能性。

ifelseifelse

  • 应使用关键词 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';

switchcase

  • case 语句必须从 switch 缩进一次,并且 break 关键字(或其他终止关键字)必须与 case 体缩进相同的级别。

  • 当非空 case 体有意进行穿透时,必须有一个如 // no break 的注释。

示例

// bad
switch(EXPRESSION)
{
    case 0:
        // Do something...
    break;
}

// good
switch∙(EXPRESSION)∙{
∙∙∙∙case0:
∙∙∙∙∙∙∙∙// Do something...
∙∙∙∙∙∙∙∙break;
∙∙∙∙case1:
∙∙∙∙∙∙∙∙// Do something with no break...
∙∙∙∙∙∙∙∙// no break
∙∙∙∙case2:
∙∙∙∙case3:
∙∙∙∙case4:
∙∙∙∙∙∙∙∙// Do something with return instead of break...
∙∙∙∙∙∙∙∙return;
∙∙∙∙default:
∙∙∙∙∙∙∙∙// Do something in default case...
∙∙∙∙∙∙∙∙break;
}

whiledo 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∙($fooas$key∙=>∙$value)∙{
∙∙∙∙// Do something...
}

trycatch

示例

// 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;