CakePHP 开发人员将使用 PSR-12 编码风格指南 以及以下规则作为编码规范。
建议其他开发 CakeIngredients 的人员遵循相同的标准。
您可以使用 CakePHP 代码嗅探器 来检查您的代码是否符合所需的标准。
不应添加任何新功能,除非它们有自己的测试,并且在将它们提交到存储库之前应通过这些测试。
请确保您的 IDE 设置为在空格上“修剪右侧”。每行不应有尾随空格。
大多数现代 IDE 也支持 .editorconfig
文件。CakePHP 应用程序骨架默认情况下会附带它。它已经包含最佳实践默认值。
如果您想最大程度地提高 IDE 兼容性,我们建议您使用 IdeHelper 插件。它将帮助您保持注释的最新状态,这将使 IDE 能够完全理解所有类之间的工作方式,并提供更好的类型提示和自动完成功能。
将使用四个空格进行缩进。
因此,缩进应该像这样
// base level
// level 1
// level 2
// level 1
// base level
或者
$booleanVariable = true;
$stringVariable = 'moose';
if ($booleanVariable) {
echo 'Boolean value is true';
if ($stringVariable === 'moose') {
echo 'We have encountered a moose';
}
}
在您使用多行函数调用时,请使用以下准则
多行函数调用的左括号必须是该行上的最后一个内容。
多行函数调用中每行只允许一个参数。
多行函数调用的右括号必须独占一行。
例如,不使用以下格式
$matches = array_intersect_key($this->_listeners,
array_flip(preg_grep($matchPattern,
array_keys($this->_listeners), 0)));
而是使用此格式
$matches = array_intersect_key(
$this->_listeners,
array_flip(
preg_grep($matchPattern, array_keys($this->_listeners), 0)
)
);
建议将行保持在约 100 个字符长,以提高代码可读性。80 或 120 个字符的限制要求通过函数来分配复杂的逻辑或表达式,并为函数和对象提供更短、更具表达力的名称。行不能超过 120 个字符。
简而言之
100 个字符是软限制。
120 个字符是硬限制。
控制结构例如“if
”,“for
”,“foreach
”,“while
”,“switch
” 等。下面是“if
” 的示例
if ((expr_1) || (expr_2)) {
// action_1;
} elseif (!(expr_3) && (expr_4)) {
// action_2;
} else {
// default_action;
}
在控制结构中,第一个括号之前应该有 1(一个)个空格,最后一个括号和左大括号之间应该有 1(一个)个空格。
在控制结构中始终使用大括号,即使它们不是必需的。它们提高了代码的可读性,并且可以减少逻辑错误。
左大括号应放在与控制结构相同的行上。右大括号应放在新行上,并且应与控制结构具有相同的缩进级别。包含在大括号内的语句应从新行开始,并且其中包含的代码应获得新的缩进级别。
内联赋值不应在控制结构内使用。
// wrong = no brackets, badly placed statement
if (expr) statement;
// wrong = no brackets
if (expr)
statement;
// good
if (expr) {
statement;
}
// wrong = inline assignment
if ($variable = Class::function()) {
statement;
}
// good
$variable = Class::function();
if ($variable) {
statement;
}
当整个三元运算符适合一行时,允许使用三元运算符。更长的三元运算符应拆分为 if else
语句。三元运算符永远不应该嵌套。可以选择在三元条件检查周围使用括号以提高清晰度
// Good, simple and readable
$variable = isset($options['variable']) ? $options['variable'] : true;
// Nested ternaries are bad
$variable = isset($options['variable']) ? isset($options['othervar']) ? true : false : false;
在模板文件中,开发人员应使用关键字控制结构。关键字控制结构在复杂的模板文件中更容易阅读。控制结构可以包含在一个更大的 PHP 块中,也可以包含在单独的 PHP 标签中
<?php
if ($isAdmin):
echo '<p>You are the admin user.</p>';
endif;
?>
<p>The following is also acceptable:</p>
<?php if ($isAdmin): ?>
<p>You are the admin user.</p>
<?php endif; ?>
始终尝试尽可能严格。如果非严格测试是故意的,最好对其进行注释,以免将其误认为错误。
为了测试变量是否为空,建议使用严格检查
if ($value === null) {
// ...
}
要检查的值应放在右侧
// not recommended
if (null === $this->foo()) {
// ...
}
// recommended
if ($this->foo() === null) {
// ...
}
函数应在函数名称和起始括号之间没有空格的情况下调用。函数调用的每个参数之间应该有一个空格
$var = foo($bar, $bar2, $bar3);
如上所示,等号 (=) 两侧应该有一个空格。
方法定义的示例
public function someFunction($arg1, $arg2 = '')
{
if (expr) {
statement;
}
return $var;
}
具有默认值的参数应放在函数定义的最后。尝试让您的函数返回某些内容,至少是 true
或 false
,以便可以确定函数调用是否成功
public function connection($dns, $persistent = false)
{
if (is_array($dns)) {
$dnsInfo = $dns;
} else {
$dnsInfo = BD::parseDNS($dns);
}
if (!($dnsInfo) || !($dnsInfo['phpType'])) {
return $this->addError();
}
return true;
}
等号两侧都有空格。
尝试通过尽早退出来避免不必要的嵌套
public function run(array $data)
{
...
if (!$success) {
return false;
}
...
}
public function check(array $data)
{
...
if (!$success) {
throw new RuntimeException(/* ... */);
}
...
}
这有助于保持逻辑顺序,从而提高可读性。
期望对象、数组或回调(可调用)的参数可以进行类型提示。但是,我们只对公共方法进行类型提示,因为类型提示并非没有成本
/**
* Some method description.
*
* @param \Cake\ORM\Table $table The table class to use.
* @param array $array Some array value.
* @param callable $callback Some callback.
* @param bool $boolean Some boolean value.
*/
public function foo(Table $table, array $array, callable $callback, $boolean)
{
}
这里 $table
必须是 \Cake\ORM\Table
的实例,$array
必须是 array
,$callback
必须是 callable
类型(有效的回调)。
请注意,如果您想允许 $array
也是 \ArrayObject
的实例,则不应进行类型提示,因为 array
仅接受基本类型
/**
* Some method description.
*
* @param array|\ArrayObject $array Some array value.
*/
public function foo($array)
{
}
定义匿名函数遵循 PSR-12 编码风格指南,其中它们在 function 关键字后使用空格,并在 use 关键字前后使用空格进行声明
$closure = function ($arg1, $arg2) use ($var1, $var2) {
// code
};
方法链应将多个方法分布在不同的行上,并缩进四个空格
$email->from('[email protected]')
->to('[email protected]')
->subject('A great message')
->send();
所有注释都应使用英文编写,并应以清晰的方式描述所注释的代码块。
注释可以包含以下 phpDocumentor 标签
@deprecated 使用 @version <vector> <description>
格式,其中 version
和 description
是必须的。版本指的是它被弃用的版本。
PhpDoc 标签非常类似于 Java 中的 JavaDoc 标签。如果标签是 DocBlock 行中的第一件事,则只会处理这些标签,例如
/**
* Tag example.
*
* @author this tag is parsed, but this @version is ignored
* @version 1.0 this tag is also parsed
*/
/**
* Example of inline phpDoc tags.
*
* This function works hard with foo() to rule the world.
*
* @return void
*/
function bar()
{
}
/**
* Foo function.
*
* @return void
*/
function foo()
{
}
除了文件中的第一个块外,注释块始终应该以换行符开头。
在 DocBlocks 中使用的变量类型
描述
类型未定义(或多种)的变量。
整数类型变量(整数)。
浮点数类型(小数)。
逻辑类型(真或假)。
字符串类型(“ ” 或 ‘ ‘ 中的任何值)。
空类型。通常与其他类型一起使用。
数组类型。
对象类型。如果可能,应使用特定类名。
资源类型(例如由 mysql_connect() 返回)。请记住,当您将类型指定为 mixed 时,您应该指示它是未知的,还是可能存在的类型。
可调用函数。
您也可以使用管道字符组合类型
int|bool
对于超过两种类型的情况,通常最好只使用 mixed
。
当返回对象本身时(例如,为了链式操作),应该使用 $this
代替
/**
* Foo function.
*
* @return $this
*/
public function foo()
{
return $this;
}
include
、require
、include_once
和 require_once
不需要括号
// wrong = parentheses
require_once('ClassFileName.php');
require_once ($class);
// good = no parentheses
require_once 'ClassFileName.php';
require_once $class;
当包含包含类或库的文件时,仅且始终使用 require_once 函数。
将所有函数编写为驼峰式
function longFunctionName()
{
}
类名应该以驼峰式编写,例如
class ExampleClass
{
}
变量名应该尽可能地描述性,但也尽可能地简短。所有变量都应该以小写字母开头,如果包含多个单词,应该以驼峰式编写。引用对象的变量应该在某种程度上与该变量所属的类相关联。例如
$user = 'John';
$users = ['John', 'Hans', 'Arne'];
$dispatcher = new Dispatcher();
对方法和变量使用 PHP 的 public
、protected
和 private
关键字。
对于所有示例 URL 和邮件地址,使用 “example.com”、“example.org” 和 “example.net”,例如
电子邮件: someone@example.com
“example.com” 域名已为此保留(参见 RFC 2606),建议在文档或示例中使用。
不包含类的文件名应该小写并用下划线分隔,例如
long_file_name.php
对于强制类型转换,我们使用
描述
强制转换为布尔值。
强制转换为整数。
强制转换为浮点数。
强制转换为字符串。
强制转换为数组。
强制转换为对象。
请使用 (int)$var
代替 intval($var)
,使用 (float)$var
代替 floatval($var)
(如果适用)。
常量应该用大写字母定义
define('CONSTANT', 1);
如果一个常量名包含多个单词,它们应该用下划线字符分隔,例如
define('LONG_NAMED_CONSTANT', 2);
枚举情况用 CamelCase
样式定义
enum ArticleStatus: string
{
case Published = 'Y';
case NotPublishedYet = 'N';
}
虽然 empty()
似乎经常是正确的用法,但它会掩盖错误,并在给出 '0'
和 0
时导致意外影响。当变量或属性已经被定义时,不建议使用 empty()
。在处理变量时,最好依赖于类型强制转换为布尔值而不是 empty()
function manipulate($var)
{
// Not recommended, $var is already defined in the scope
if (empty($var)) {
// ...
}
// Use boolean type coercion
if (!$var) {
// ...
}
if ($var) {
// ...
}
}
在处理定义的属性时,您应该优先考虑 null
检查而不是 empty()
/isset()
检查
class Thing
{
private $property; // Defined
public function readProperty()
{
// Not recommended as the property is defined in the class
if (!isset($this->property)) {
// ...
}
// Recommended
if ($this->property === null) {
}
}
}
在处理数组时,最好合并默认值而不是使用 empty()
检查。通过合并默认值,您可以确保定义了必需的键
function doWork(array $array)
{
// Merge defaults to remove need for empty checks.
$array += [
'key' => null,
];
// Not recommended, the key is already set
if (isset($array['key'])) {
// ...
}
// Recommended
if ($array['key'] !== null) {
// ...
}
}