展開文件目錄

Coding Style Guide

這份指南繼承於 PSR-1( basic coding standard )之上並且擴展。

這份指南試圖在檢視不同作者撰寫的程式碼時減少認知分歧。本文藉由列舉共同的規則,以及撰寫 PHP 程式碼的共識來達成目的。

這裡的指南風格歸納了各種多人專案的共通之處。當很多作者共同合作多個專案,有一份專案間共通的指南是相當有幫助的。因此,這份指南的好處不在於制定的規則本身,而是在共同遵守這些規則。

本文件中以 RFC 2119 的關鍵字 “必須( MUST )”、”不可( MUST NOT )”、”必要( REQUIRED )”、”將要( SHALL )”、”將不( SHALL NOT )” 、”應該( SHOULD )”、“不應該( SHOULD NOT )”、”建議( RECOMMENDED )”、”可以/可能( MAY )” 和 “選用( OPTIONAL )” 來做解釋性的描述。

  1. 概觀

  • 程式碼 “必須” 遵守 "coding style guide" PSR [PSR-1].

  • 程式碼 “必須” 以 4 個空格,而不是用 tabs 當作縮排。

  • “不可” 硬性規定單行長度;單行字元數 “必須” 限制在最多 120 字元左右;每行字元數 "應該" 在 80 字元或更少。

  • namespace 宣告後 “必須” 空一行,在全部 use 宣告完後 “必須” 空一行。

  • 類別宣告的左大括號 “必須” 放在類別名稱的下一行,右大括號 “必須” 放在類別內容最後單獨一行。

  • 方法宣告的左大括號 “必須” 放在方法名稱的下一行,右大括號 “必須” 放在方法內容最後單獨一行。

  • 所有屬性和方法宣告 “必須” 加上可見性( Visibility ); abstractfinal “必須” 寫在可見性之前; static “必須” 寫在可見性之後。

  • 控制結構的關鍵字之後 “必須” 空一格;但方法和函數呼叫後 “不可” 空格。

  • 控制結構的左大括號 “必須” 放在同一行,右大括號 “必須” 放在內容最後單獨一行。

  • 控制結構的左小括號之後 “不可” 空格,右小括號之前也 “不可” 空格。

1.1. 範例

下面的範例含括了一些規則,可作為概覽:

<?php
namespace Vendor\Package;

use FooInterface;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

class Foo extends Bar implements FooInterface
{
    public function sampleFunction($a, $b = null)
    {
        if ($a === $b) {
            bar();
        } elseif ($a > $b) {
            $foo->bar($arg1);
        } else {
            BazClass::bar($arg2, $arg3);
        }
    }

    final public static function bar()
    {
        // method body
    }
}
  1. 一般準則

2.1 基本程式碼標準

程式碼 “必須” 遵守 PSR-1 裡的規則。

2.2 PHP 文件擋案

所有的 PHP 文件擋案 “必須” 使用 Unix LF (linefeed) 做每行結尾。

所有的 PHP 文件擋案 “必須” 在檔案結尾空一行。

在文件內容只有 PHP 的檔案 “必須” 省略 PHP 結尾標簽 ?>

2.3. 行

“不可” 硬性規定每行長度。

單行字元數 “必須” 最多 120 字元左右;自動格式檢查 “必須” 提出警告,但是 “不可” 發出錯誤。

單行字元數 “不應該” 超過 80 字元;超過的時候 "應該" 拆分成不超過 80 字元的數行。

非空白的單行 “不可” 在最後留有空白。

空行 “可以” 用來增加可讀性,以及區分出相關程式碼邏輯。

“不可” 在一行內寫超過一個陳述式。

2.4. 縮排

程式碼 “必須” 使用 4 個空格做縮排, “不可” 使用 tabs 做縮排。

提醒:只使用空格,而不和 tabs 混用,可以避免在作版本比較( diff )、補丁( patched )、 歷史版本( history )和注釋( annotations )時產生問題。只使用空格也可以讓行間對齊時, 插入子縮排的表現更好。

2.5. 關鍵字和 True / False / Null

PHP 關鍵字 “必須” 使用小寫。

PHP 常數 truefalse ,和 null “必須” 小寫。

  1. 命名空間( Namespace )和 Use 宣告

如果有命名空間,在 namespace 宣告下面 “必須” 空一行。

如果有使用 Use,所有的 use 宣告 “必須” 在 namespace 在宣告之後。

每個 Use 宣告 “必須” 用一個 use 關鍵字。

在所有的 use 宣告完後,下面 “必須” 空一行。

範例:

<?php
namespace Vendor\Package;

use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

// ... 其餘的 PHP 程式碼 ...
  1. "Class" ,屬性和方法

這裡的 "class" 是指所有的類別( classes ),介面( interfaces ),和特性( traits )。

4.1. 繼承和實作

extendsimplements 關鍵字 “必須” 跟 class 名稱宣告在同一行。

class 的 左大括號 “必須” 單獨成行; class 的右大括號 “必須” 在宣告內容之後單獨成行。

<?php
namespace Vendor\Package;

use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

class ClassName extends ParentClass implements \ArrayAccess, \Countable
{
    // 常數,屬性,方法
}

implements 後的介面名稱列表 “可以” 分成很多行,每行縮排一次。當使用分行的時候,第一個介面名稱 “必須” 寫在下一行,每一行 “必須” 只有一個介面名稱。

<?php
namespace Vendor\Package;

use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

class ClassName extends ParentClass implements
    \ArrayAccess,
    \Countable,
    \Serializable
{
    // 常數,屬性,方法
}

4.2. 屬性

所有屬性宣告 “必須” 加上可見性( Visibility )。

“不可” 使用 var 關鍵字宣告屬性。

每個宣告 “不可” 多於一個屬性。

屬性名稱前 “不應該” 使用單一底線前綴來表示 protected 或 private 可見性。

一個屬性宣告看起來如下:

<?php
namespace Vendor\Package;

class ClassName
{
    public $foo = null;
}

4.3. 方法

所有方法都 “必須” 宣告可視性。

方法名稱前 “不應該” 使用單一底線前綴來表示 protected 或 private 可見性。

在方法名稱之後 “不可” 空格。左大括號 “必須” 單獨成行,右大括號 “必須” 在方法內容之後單獨成行。在左小括號之後 “不可” 空格,在右小括號之前也 “不可” 空格。

一個方法宣告看起來如下,注意小括號,逗號,空格和大括號的用法:

<?php
namespace Vendor\Package;

class ClassName
{
    public function fooBarBaz($arg1, &$arg2, $arg3 = [])
    {
        // 方法內容
    }
}

4.4. 方法參數

每個方法參數的逗號前 “不可” 空格,並且 “必須” 在逗號後面空一格。

有預設值的參數 “必須” 排在參數列表的後面。

<?php
namespace Vendor\Package;

class ClassName
{
    public function foo($arg1, &$arg2, $arg3 = [])
    {
        // 方法內容
    }
}

參數列表 “可以” 分成多行,每一行前面要縮排一次。如果要分行的話,第一個參數 “必須” 寫在方法名稱的下一行,並且每一行 “必須” 只能有一個參數。

當參數列表分成多行,右小括號和左大括號 “必須” 一同放在單獨一行,並且用一個空格分開。

<?php
namespace Vendor\Package;

class ClassName
{
    public function aVeryLongMethodName(
        ClassTypeHint $arg1,
        &$arg2,
        array $arg3 = []
    ) {
        // 方法內容
    }
}

4.5. abstractfinal , 和 static

如果有的話, abstractfinal 宣告 “必須” 寫在可視性前面。

如果有的話,static 宣告 “必須” 寫在可視性後面。

<?php
namespace Vendor\Package;

abstract class ClassName
{
    protected static $foo;

    abstract protected function zim();

    final public static function bar()
    {
        // 方法內容
    }
}

4.6. 方法和函式呼叫

呼叫方法或函示時,方法或函示名稱和左小括號之間 “不可” 空一格,左小括號之後 “不可” 空格,右小括號之前 “不可” 空格。參數列表的每個逗號前 “不可” 空格,但逗號後 “必須” 空一格。

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

參數列表 “可能” 分列成數行,每一行前面要縮排一次。要如此表示的時候,第一個參數 “必須” 放到下一行,並且每一行 “必須” 只能有一個參數。

<?php
$foo->bar(
    $longArgument,
    $longerArgument,
    $muchLongerArgument
);
  1. 控制結構

下面是通用的控制結構寫作風格:

  • 在控制結構關鍵字後 “必須” 空一格
  • 在左小括號後 “不可” 空格
  • 在右小括號前 “不可” 空格
  • 右小括號 “必須” 和左大括號之間空一格
  • 控制結構內容 “必須” 縮排一次
  • 右大括號 “必須” 放在結構內容後下一行

每個控制結構內容 “必須” 以大括號包圍住。這會讓結構外觀標準化,以及減少在新增一行時發生錯誤的機率。

5.1. ifelseifelse

一個 if 陳述語法看起來結構如下。注意小括號,空格和大括號的位置; elseelseif 要和上一個的右大括號放在同一行。

<?php
if ($expr1) {
    // if body
} elseif ($expr2) {
    // elseif body
} else {
    // else body;
}

"應該" 使用關鍵字 elseif 而不是 else if ,讓所有控制結構關鍵字看起來是一個單字。

5.2. switchcase

一個 switch 陳述語法看起來結構如下。注意小括號,空格和大括號的位置; case 陳述句 和 switch 之間 “必須” 縮排一次, break 關鍵字(或其他結束陳述語法的關鍵字) “必須” 和 case 的內容縮排一致。當刻意想要非空白的 case 內容陳述語法執行後繼續向下時, “必須” 加上像是 // no break 的註解。

<?php
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. whiledo while

一個 while 陳述語法看起來結構如下。注意小括號,空格和大括號的位置。

<?php
while ($expr) {
    // structure body
}

很相似的, do while 陳述語法看起來結構如下。注意小括號,空格和大括號的位置。

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

5.4. for

for 陳述語法看起來結構如下。注意小括號,空格和大括號的位置。

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

5.5. foreach

foreach 陳述語法看起來結構如下。注意小括號,空格和大括號的位置。

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

5.6. trycatch

try catch 陳述語法看起來結構如下。注意小括號,空格和大括號的位置。

<?php
try {
    // try body
} catch (FirstExceptionType $e) {
    // catch body
} catch (OtherExceptionType $e) {
    // catch body
}
  1. 閉包

閉包宣告在 function 關鍵字後 “必須” 空一格,並在 use 關鍵字前後各空一格。

左大括號 “必須” 放在同一行,右大括號 “必須” 放在結構內容後下一行。

在參數列表或變數列表的左小括號後 “不可” 空格,並且在參數列表或變數列表的右小括號之前也 “不可” 空格。

參數列表和變數列表中,每個逗號前 “不可” 空格,但每個逗號後 “必須” 空一格。

有預設值的參數 “必須” 作為參數列表的最後一個。

閉合宣告會如下所示。注意小括號,逗號,空格和大括號的位置:

<?php
$closureWithArgs = function ($arg1, $arg2) {
    // body
};

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

參數列表和變數列表 “可以” 分成很多行,每行縮排一次。當使用分行的時候,列表第一項 “必須” 放在方法名稱的下一行,每行 “必須” 只有一個參數或變數。

分行參數結構的最後一行,右小括號和左大括號 “必須” 一起單獨放在一行,兩者中間空一行。

下面是函式結構的範例,有參數和無參樹以及分行參數結構。

<?php
$longArgs_noVars = function (
    $longArgument,
    $longerArgument,
    $muchLongerArgument
) {
   // body
};

$noArgs_longVars = function () use (
    $longVar1,
    $longerVar2,
    $muchLongerVar3
) {
   // body
};

$longArgs_longVars = function (
    $longArgument,
    $longerArgument,
    $muchLongerArgument
) use (
    $longVar1,
    $longerVar2,
    $muchLongerVar3
) {
   // body
};

$longArgs_shortVars = function (
    $longArgument,
    $longerArgument,
    $muchLongerArgument
) use ($var1) {
   // body
};

$shortArgs_longVars = function ($arg) use (
    $longVar1,
    $longerVar2,
    $muchLongerVar3
) {
   // body
};

注意格式規則也適用在函數或方法呼叫時,作為參數傳入的閉合函數。

<?php
$foo->bar(
    $arg1,
    function ($arg2) use ($var1) {
        // body
    },
    $arg3
);
  1. 結論

這份指南有意的略去了很多寫作風格元素和實務。包含但不僅限於以下:

  • 全域變數和全域常數宣告

  • 函數宣告

  • 運算子和設定值

  • 行間對齊

  • 註解和文件區塊

  • Class 名稱前綴和後綴

  • 最佳實務

未來 “可以” 推薦之後修改並且擴展這份指南,加入這些或是其他的寫作風格元素和實務。

Appendix A. Survey

In writing this style guide, the group took a survey of member projects to determine common practices. The survey is retained herein for posterity.

A.1. Survey Data

url,http://www.horde.org/apps/horde/docs/CODING_STANDARDS,http://pear.php.net/manual/en/standards.php,http://solarphp.com/manual/appendix-standards.style,http://framework.zend.com/manual/en/coding-standard.html,http://symfony.com/doc/2.0/contributing/code/standards.html,http://www.ppi.io/docs/coding-standards.html,https://github.com/ezsystems/ezp-next/wiki/codingstandards,http://book.cakephp.org/2.0/en/contributing/cakephp-coding-conventions.html,https://github.com/UnionOfRAD/lithium/wiki/Spec%3A-Coding,http://drupal.org/coding-standards,http://code.google.com/p/sabredav/,http://area51.phpbb.com/docs/31x/coding-guidelines.html,https://docs.google.com/a/zikula.org/document/edit?authkey=CPCU0Us&hgd=1&id=1fcqb93Sn-hR9c0mkN6m_tyWnmEvoswKBtSc0tKkZmJA,http://www.chisimba.com,n/a,https://github.com/Respect/project-info/blob/master/coding-standards-sample.php,n/a,Object Calisthenics for PHP,http://doc.nette.org/en/coding-standard,http://flow3.typo3.org,https://github.com/propelorm/Propel2/wiki/Coding-Standards,http://developer.joomla.org/coding-standards.html
voting,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,no,no,no,?,yes,no,yes
indent_type,4,4,4,4,4,tab,4,tab,tab,2,4,tab,4,4,4,4,4,4,tab,tab,4,tab
line_length_limit_soft,75,75,75,75,no,85,120,120,80,80,80,no,100,80,80,?,?,120,80,120,no,150
line_length_limit_hard,85,85,85,85,no,no,no,no,100,?,no,no,no,100,100,?,120,120,no,no,no,no
class_names,studly,studly,studly,studly,studly,studly,studly,studly,studly,studly,studly,lower_under,studly,lower,studly,studly,studly,studly,?,studly,studly,studly
class_brace_line,next,next,next,next,next,same,next,same,same,same,same,next,next,next,next,next,next,next,next,same,next,next
constant_names,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper
true_false_null,lower,lower,lower,lower,lower,lower,lower,lower,lower,upper,lower,lower,lower,upper,lower,lower,lower,lower,lower,upper,lower,lower
method_names,camel,camel,camel,camel,camel,camel,camel,camel,camel,camel,camel,lower_under,camel,camel,camel,camel,camel,camel,camel,camel,camel,camel
method_brace_line,next,next,next,next,next,same,next,same,same,same,same,next,next,same,next,next,next,next,next,same,next,next
control_brace_line,same,same,same,same,same,same,next,same,same,same,same,next,same,same,next,same,same,same,same,same,same,next
control_space_after,yes,yes,yes,yes,yes,no,yes,yes,yes,yes,no,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes
always_use_control_braces,yes,yes,yes,yes,yes,yes,no,yes,yes,yes,no,yes,yes,yes,yes,no,yes,yes,yes,yes,yes,yes
else_elseif_line,same,same,same,same,same,same,next,same,same,next,same,next,same,next,next,same,same,same,same,same,same,next
case_break_indent_from_switch,0/1,0/1,0/1,1/2,1/2,1/2,1/2,1/1,1/1,1/2,1/2,1/1,1/2,1/2,1/2,1/2,1/2,1/2,0/1,1/1,1/2,1/2
function_space_after,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no
closing_php_tag_required,no,no,no,no,no,no,no,no,yes,no,no,no,no,yes,no,no,no,no,no,yes,no,no
line_endings,LF,LF,LF,LF,LF,LF,LF,LF,?,LF,?,LF,LF,LF,LF,?,,LF,?,LF,LF,LF
static_or_visibility_first,static,?,static,either,either,either,visibility,visibility,visibility,either,static,either,?,visibility,?,?,either,either,visibility,visibility,static,?
control_space_parens,no,no,no,no,no,no,yes,no,no,no,no,no,no,yes,?,no,no,no,no,no,no,no
blank_line_after_php,no,no,no,no,yes,no,no,no,no,yes,yes,no,no,yes,?,yes,yes,no,yes,no,yes,no
class_method_control_brace,next/next/same,next/next/same,next/next/same,next/next/same,next/next/same,same/same/same,next/next/next,same/same/same,same/same/same,same/same/same,same/same/same,next/next/next,next/next/same,next/same/same,next/next/next,next/next/same,next/next/same,next/next/same,next/next/same,same/same/same,next/next/same,next/next/next

A.2. Survey Legend

indent_type: The type of indenting. tab = "Use a tab", 2 or 4 = "number of spaces"

line_length_limit_soft: The "soft" line length limit, in characters. ? = not discernible or no response, no means no limit.

line_length_limit_hard: The "hard" line length limit, in characters. ? = not discernible or no response, no means no limit.

class_names: How classes are named. lower = lowercase only, lower_under = lowercase with underscore separators, studly = StudlyCase.

class_brace_line: Does the opening brace for a class go on the same line as the class keyword, or on the next line after it?

constant_names: How are class constants named? upper = Uppercase with underscore separators.

true_false_null: Are the true, false, and null keywords spelled as all lower case, or all upper case?

method_names: How are methods named? camel = camelCase, lower_under = lowercase with underscore separators.

method_brace_line: Does the opening brace for a method go on the same line as the method name, or on the next line?

control_brace_line: Does the opening brace for a control structure go on the same line, or on the next line?

control_space_after: Is there a space after the control structure keyword?

always_use_control_braces: Do control structures always use braces?

else_elseif_line: When using else or elseif, does it go on the same line as the previous closing brace, or does it go on the next line?

case_break_indent_from_switch: How many times are case and break indented from an opening switch statement?

function_space_after: Do function calls have a space after the function name and before the opening parenthesis?

closing_php_tag_required: In files containing only PHP, is the closing ?> tag required?

line_endings: What type of line ending is used?

static_or_visibility_first: When declaring a method, does static come first, or does the visibility come first?

control_space_parens: In a control structure expression, is there a space after the opening parenthesis and a space before the closing parenthesis? yes = if ( $expr ), no = if ($expr).

blank_line_after_php: Is there a blank line after the opening PHP tag?

class_method_control_brace: A summary of what line the opening braces go on for classes, methods, and control structures.

A.3. Survey Results

indent_type:
    tab: 7
    2: 1
    4: 14
line_length_limit_soft:
    ?: 2
    no: 3
    75: 4
    80: 6
    85: 1
    100: 1
    120: 4
    150: 1
line_length_limit_hard:
    ?: 2
    no: 11
    85: 4
    100: 3
    120: 2
class_names:
    ?: 1
    lower: 1
    lower_under: 1
    studly: 19
class_brace_line:
    next: 16
    same: 6
constant_names:
    upper: 22
true_false_null:
    lower: 19
    upper: 3
method_names:
    camel: 21
    lower_under: 1
method_brace_line:
    next: 15
    same: 7
control_brace_line:
    next: 4
    same: 18
control_space_after:
    no: 2
    yes: 20
always_use_control_braces:
    no: 3
    yes: 19
else_elseif_line:
    next: 6
    same: 16
case_break_indent_from_switch:
    0/1: 4
    1/1: 4
    1/2: 14
function_space_after:
    no: 22
closing_php_tag_required:
    no: 19
    yes: 3
line_endings:
    ?: 5
    LF: 17
static_or_visibility_first:
    ?: 5
    either: 7
    static: 4
    visibility: 6
control_space_parens:
    ?: 1
    no: 19
    yes: 2
blank_line_after_php:
    ?: 1
    no: 13
    yes: 8
class_method_control_brace:
    next/next/next: 4
    next/next/same: 11
    next/same/same: 1
    same/same/same: 6