编码规范

CakePHP 开发人员将使用 PSR-12 编码风格指南 以及以下规则作为编码规范。

建议其他开发 CakeIngredients 的人员遵循相同的标准。

您可以使用 CakePHP 代码嗅探器 来检查您的代码是否符合所需的标准。

添加新功能

不应添加任何新功能,除非它们有自己的测试,并且在将它们提交到存储库之前应通过这些测试。

IDE 设置

请确保您的 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;
}

具有默认值的参数应放在函数定义的最后。尝试让您的函数返回某些内容,至少是 truefalse,以便可以确定函数调用是否成功

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 标签

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 中使用的变量类型

类型

描述

mixed

类型未定义(或多种)的变量。

int

整数类型变量(整数)。

float

浮点数类型(小数)。

bool

逻辑类型(真或假)。

string

字符串类型(“ ” 或 ‘ ‘ 中的任何值)。

null

空类型。通常与其他类型一起使用。

array

数组类型。

object

对象类型。如果可能,应使用特定类名。

resource

资源类型(例如由 mysql_connect() 返回)。请记住,当您将类型指定为 mixed 时,您应该指示它是未知的,还是可能存在的类型。

callable

可调用函数。

您也可以使用管道字符组合类型

int|bool

对于超过两种类型的情况,通常最好只使用 mixed

当返回对象本身时(例如,为了链式操作),应该使用 $this 代替

/**
 * Foo function.
 *
 * @return $this
 */
public function foo()
{
    return $this;
}

包含文件

includerequireinclude_oncerequire_once 不需要括号

// wrong = parentheses
require_once('ClassFileName.php');
require_once ($class);

// good = no parentheses
require_once 'ClassFileName.php';
require_once $class;

当包含包含类或库的文件时,仅且始终使用 require_once 函数。

PHP 标签

始终使用长标签 (<?php ?>) 代替短标签 (<? ?>)。短 echo 应该在适当的情况下用于模板文件。

短 Echo

短 echo 应该在模板文件中代替 <?php echo 使用。它应该紧随其后一个空格,要 echo 的变量或函数值,一个空格,以及 php 结束标签

// wrong = semicolon, no spaces
<td><?=$name;?></td>

// good = spaces, no semicolon
<td><?= $name ?></td>

从 PHP 5.4 开始,短 echo 标签 (<?=) 不再被视为 “短标签”,它始终可用,无论 short_open_tag ini 指令如何。

命名规范

函数

将所有函数编写为驼峰式

function longFunctionName()
{
}

类名应该以驼峰式编写,例如

class ExampleClass
{
}

变量

变量名应该尽可能地描述性,但也尽可能地简短。所有变量都应该以小写字母开头,如果包含多个单词,应该以驼峰式编写。引用对象的变量应该在某种程度上与该变量所属的类相关联。例如

$user = 'John';
$users = ['John', 'Hans', 'Arne'];

$dispatcher = new Dispatcher();

成员可见性

对方法和变量使用 PHP 的 publicprotectedprivate 关键字。

示例地址

对于所有示例 URL 和邮件地址,使用 “example.com”、“example.org” 和 “example.net”,例如

“example.com” 域名已为此保留(参见 RFC 2606),建议在文档或示例中使用。

文件

不包含类的文件名应该小写并用下划线分隔,例如

long_file_name.php

强制类型转换

对于强制类型转换,我们使用

类型

描述

(bool)

强制转换为布尔值。

(int)

强制转换为整数。

(float)

强制转换为浮点数。

(string)

强制转换为字符串。

(array)

强制转换为数组。

(object)

强制转换为对象。

请使用 (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()/isset() 时要小心

虽然 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) {
        // ...
    }
}