基本風格及語法
下列說明開發 CodeIgniter 時,所需遵守的程式碼規則。
目錄
- 檔案格式
- PHP 結束標籤
- 類別與方法命名
- 變數名稱
- 註解
- 常數
- TRUE、FALSE 與 NULL
- 邏輯運算子
- 比較返回值與型別轉換
- 偵錯程式碼
- 檔案中的空格
- 相容性
- 使用一般用字來命名類別與檔案
- 資料表名稱
- 每個類別一個檔案
- 空格
- 分行
- 程式碼縮排
- 中括號與小括號空格
- Localized Text
- 私有方法與變數
- PHP錯誤
- 簡短的開始標籤
- 每行一個敘述
- 字串
- SQL 查詢
- 函數參數預設值
File Format 檔案格式
檔案必須使用 Unicode (UTF-8) 編碼來存檔。不可 使用 BOM 。與 UTF-16 及 UTF-32 不同, 在 UTF-8 編碼的檔案中,並不需要指定位元組順序(byte order)。而且 BOM 在 PHP 輸出時可能引起一些 不好的副作用,使得應用程式無法設定自己的 headers 。Unix 換行字元必須使用 (LF) 。
以下是用來設定幾個常用的文字編輯器的方法。設定方法在你的文字編輯器中可能會有一些不同,請參考你的編輯器的說明文件。
TextMate
- 開啟 "Application Preferences"
- 點選 "Advanced" ,然後選擇 "Saving" 頁籤。
- 在"File Encoding" 中,選擇 "UTF-8 (recommended)"
- 在 "Line Endings" 中,選擇 "LF (recommended)"
- 可選: 如果你想要在開啟檔案時改變檔案的換行字元 ,勾選 "Use for existing files as well" 選項
BBEdit
- 開啟 "Application Preferences"
- 選擇左側的 "Text Encodings"
- 在 "Default text encoding for new documents" 中,選擇 "Unicode (UTF-8, no BOM)"
- 可選: 在 "If file's encoding can't be guessed, use" 中,選擇 "Unicode (UTF-8, no BOM)"
- 在左側選擇 "Text Files"
- 在 "Default line breaks" 中,選擇 "Mac OS X and Unix (LF)"
PHP 結束標籤
PHP 檔案中的 PHP 結束標籤 ?> 對 PHP 剖析器來說不是必要的。然而,一旦使用的話,在結束標籤後由開發者、使用者或是FTP軟體加上去的空格,都有可能導致意外的輸出、PHP 錯誤 或是空白頁(如果不顯示錯誤的話)。因此,所有的 PHP 檔案都應當 省略 PHP 結束標籤,且用一個區塊註解來標示檔案結束以及檔案與應用程式根目錄的相對位置。這仍然讓你可以辨識檔案是否完整或是被切斷。
不正確:
<?php
echo "Here's my code!";
?>
正確:
<?php
echo "Here's my code!";
/* End of file myfile.php */
/* Location: ./system/modules/mymodule/myfile.php */
類別與方法命名
類別名稱第一個字母必須為大寫。多個字必須使用底線來分隔,而不是 CamelCased 。其他所有類別方法必須完全使用小寫字母,且名稱可以表明它的功能,最好包含一個動詞。盡量避免冗長的名稱。
不正確:
class superclass
class SuperClass
正確:
class Super_class
class Super_class {
function __construct()
{
}
}
不適當與適當的方法命名範例:
不正確:
function fileproperties() // 非敘述性,且需要底線分隔
function fileProperties() // 非敘述性且使用了 CamelCase
function getfileproperties() // 好一點,但是少了底線分隔
function getFileProperties() // 用了 CamelCase
function get_the_file_properties_from_the_file() // 太累贅
正確:
function get_file_properties() // 敘述性、底線分隔且全部用小寫字母
變數名稱
變數命名指引與類別的方法的指引類似。換句話說,變數名稱必須使用小寫字母,使用底線來分隔不同的字,並且合理地指出他的用途與內容。極短與非用字的變數命名最好只在 for() 迴圈中作為迭代使用。
不正確:
$j = 'foo'; // 單個字母的變數名稱只能在 for() 迴圈中使用
$Str // 含有大寫字母
$bufferedText // 用了 CamelCasing,且無法無損語意地縮短
$groupid // 必須用底線分隔多個字
$name_of_last_city_used // 太長了
正確:
for ($j = 0; $j < 10; $j++)
$str
$buffer
$group_id
$last_city
註解
一般來說,程式碼應當適當地註解。這不只可以提供描述程式流程與目的來協助經驗較少的程式設計師,在你回頭看幾個月前開發的東西時會非常有用。註解並沒有特別的格式要求,但是建議如下:
在類別與方法宣告前使用 DocBlock 風格的註解,這樣可以被 IDE 取用:
/**
* Super Class
*
* @package Package Name
* @subpackage Subpackage
* @category Category
* @author Author Name
* @link http://example.com
*/
class Super_class {
/**
* Encodes string for use in XML
*
* @access public
* @param string
* @return string
*/
function xml_encode($str)
在程式碼間使用單行註解,在大型的區塊註解與程式碼之間空出一行。
// break up the string by newlines
$parts = explode("\n", $str);
// A longer comment that needs to give greater detail on what is
// occurring and why can use multiple single-line comments. Try to
// keep the width reasonable, around 70 characters is the easiest to
// read. Don't hesitate to link to permanent external resources
// that may provide greater detail:
//
// http://example.com/information_about_something/in_particular/
$parts = $this->foo($parts);
常數
常數遵循與變數類似的指引,只是常數必須全部使用大寫字母命名。 適當時一律使用 ExpressionEngine 常數,例如 SLASH, LD, RD, PATH_CACHE 等。
不正確:
myConstant // 少了底線分隔且不是全部大寫
N // 不可以用單一字母常數名稱
S_C_VER // 非敘述性
$str = str_replace('{foo}', 'bar', $str); // 應該使用 LD 與 RD 常數
正確:
MY_CONSTANT
NEWLINE
SUPER_CLASS_VERSION
$str = str_replace(LD.'foo'.RD, 'bar', $str);
TRUE 、 FALSE 與 NULL
TRUE 、 FALSE 與 NULL 關鍵字必須全部使用大寫字母。
不正確:
if ($foo == true)
$bar = false;
function foo($bar = null)
正確:
if ($foo == TRUE)
$bar = FALSE;
function foo($bar = NULL)
邏輯運算子
不建議使用 || ,因為它在一些輸出裝置上可能會看不清楚(例如看起來像數字 11 )。偏好使用 && 而不是 AND ,但是都可接受。必須在 ! 前後加上空白。
不正確:
if ($foo || $bar)
if ($foo AND $bar) // OK但是不建議在語法凸顯(highlight)的應用軟體中使用
if (!$foo)
if (! is_array($foo))
正確:
if ($foo OR $bar)
if ($foo && $bar) // 建議使用
if ( ! $foo)
if ( ! is_array($foo))
比較返回值與型別轉換
一些 PHP 函數在失敗時返回 FALSE ,但是在成功時還是可能返回空字串 "" 或是 0 , 這樣在較寬鬆的比較下結果也是 FALSE 。當使用返回值作為條件時明確地比較變數型別,來確保返回值符合預期,而不是使用在鬆散型別時可能會相等的值。
用同樣嚴格的方式返回與檢查你的變數。需要時就使用 === 與 !== 。
不正確:
// 如果 'foo' 在字串開頭, strpos 會返回 0 ,
// 這樣會使條件中求值的結果變成 TRUE
if (strpos($str, 'foo') == FALSE)
正確:
if (strpos($str, 'foo') === FALSE)
不正確:
function build_string($str = "")
{
if ($str == "") // 阿!如果把 TURE 或是整數 0 當作參數傳給它會怎樣?
{
}
}
正確:
function build_string($str = "")
{
if ($str === "")
{
}
}
同時參閱 型別轉換(typecasting) 的相關資訊應該會很有用。型別轉換可能有你可能想要的些許不同的效果。例如,當把一個變數轉型為字串時,含有 NULL 與布林值 FALSE 的變數會變成空字串, 0 (還有其他數字)會變成數字字串,而布林值 TRUE 會變成 "1":
$str = (string) $str; // 把 $str 轉型成字串
Debugging Code 偵錯程式碼
除非加上註解,否則不要在已提交的附加程式(add-ons)中留下偵錯程式碼,例如在新增附加程式(add-ons)時呼叫的 var_dump() 、 print_r() 、 die() 以及 exit() ,除非把它們註解掉。
// print_r($foo);
檔案中的空格
不要在 PHP 開始標籤之前及 PHP 結束標籤之後加上空格。ExpressionEngine 的輸出有緩衝(buffered),所以在檔案中的空格或使得在 ExpressionEngine 輸出內容前就開始輸出,這會造成錯誤並使 ExpressionEngine 無法送出適當的 headers 。在下面的例子裡,用滑鼠反白文字就可以看到不正確的空格。
不正確:
<?php
// ...there is whitespace and a linebreak above the opening PHP tag
// as well as whitespace after the closing PHP tag
?>
正確:
<?php
// this sample has no whitespace before or after the opening and closing PHP tags
?>
相容性
除非在你的附加程式文件中特別指出,所有的程式碼必須與 PHP 5.1+ 以上的版本相容。並且,不要使用非預設安裝的 PHP 程式庫中的函數,除非你的程式中有在無法使用時可以替代的函數,或是在文件中暗示你的附加程式需要某個 PHP 程式庫。
使用一般用字命名類別與檔案
當你的類別或檔案名稱是一般用字,或是可能與其他 PHP 程式有雷同的名稱,要提供一個獨一無二的前置字串來協助避免衝突。要知道你的用戶可能會執行其他的附加程式或是第三方的 PHP 程式。選擇一個獨特到可以辨認開發者或是公司的前置字串。
不正確:
class Email pi.email.php
class Xml ext.xml.php
class Import mod.import.php
正確:
class Pre_email pi.pre_email.php
class Pre_xml ext.pre_xml.php
class Pre_import mod.pre_import.php
Database Table Names 資料表名稱
任何你的附加程式可能使用的資料表,必須加上 'exp_' 前置字串,接著是可以辨識開發者或是公司的獨特前置字串,然後才是一個簡短的敘述性的資料表名稱。你不用考慮使用者安裝時會使用的資料庫前置字串,因為 ExpressionEngine 的資料庫類別會自動將 'exp_' 轉換為真正使用的前置字串。
不正確:
email_addresses // 少了兩種前置字串
pre_email_addresses // 少了 exp_ 前置字串
exp_email_addresses // 少了 獨特 前置字串
正確:
exp_pre_email_addresses
注意:要留意到 MySQL 限制資料表名稱只能有 64 個字元。這應該不成問題,因為資料表名稱超過限制那這個名稱應該也不常理。例如,以下的資料表名稱超過限制一個字元。看起來很蠢吧? exp_pre_email_addresses_of_registered_users_in_seattle_washington
每個類別一個檔案
你的附加程式使用的類別要每個類別存成一個檔案,除非這些類別 緊密相關 。一個例子是在 ExpressionEngine 中,Database類別檔案中包含了幾個類別,包括 DB 類別與 DB_Cache 類別。而在 Magpie plugin 中,它同時包含 Magpie 與 Snoopy 類別。
空格
在程式碼中使用 tab 空格而不是 space 空格。這看起來只是件小事,但是卻可以讓開發者看你的程式碼時,依照他們使用的軟體,調整他們喜歡的縮排格式。還有一個好的副作用,就是一個 tab空格跟四個 space空格比起來,檔案比較小。
分行
檔案必須使用 Unix 分行格式來存檔。這對在 Windows 環境下工作的開發者可能會有些問題,但是只要他們的文字編輯器可以把檔案存成 Unix 分行格式就沒問題了。
程式碼縮排
使用 Allman 風格的縮排方式。除了類別宣告之外,大括號總是自己一行,與擁有它們的同一層的控制敘述縮排一致。
不正確:
function foo($bar) {
// ...
}
foreach ($arr as $key => $val) {
// ...
}
if ($foo == $bar) {
// ...
} else {
// ...
}
for ($i = 0; $i < 10; $i++)
{
for ($j = 0; $j < 10; $j++)
{
// ...
}
}
正確:
function foo($bar)
{
// ...
}
foreach ($arr as $key => $val)
{
// ...
}
if ($foo == $bar)
{
// ...
}
else
{
// ...
}
for ($i = 0; $i < 10; $i++)
{
for ($j = 0; $j < 10; $j++)
{
// ...
}
}
中括號與小括號空格
一般來說,小括號與中括號不能有任何空格。此外,接受參數的 PHP 控制結構所使用的小括號必須有空格(宣告、do-while、elseif、for、foreach、if、switch、while 等),以協助分辨它們與函數,並增加可讀性。
不正確:
$arr[ $foo ] = 'foo';
正確:
$arr[$foo] = 'foo'; // 陣列鍵值周圍沒有空格
不正確:
function foo ( $bar )
{
}
正確:
function foo($bar) // 函數宣告的小括號周圍沒有空格
{
}
不正確:
foreach( $query->result() as $row )
正確:
foreach ($query->result() as $row) // PHP 控制結構之後有一個空格,但不是在括號中
Localized Text
Any text that is output in the control panel should use language variables in your lang file to allow localization.
不正確:
return "Invalid Selection";
正確:
return $this->lang->line('invalid_selection');
私有方法與變數
只能在你自己類別內取用的方法與變數,例如用來當作公用以及補助公有方法以進行抽象化的函數,必須使用底線作為前置字元。
convert_text() // 公有方法
_convert_text() // 私有方法
PHP 錯誤
程式執行必須完全沒有錯誤,且不倚賴隱藏警告或注意訊息來達成這個要求。例如,絕對不能在使用 isset 檢查之前存取一個不是自己賦值的變數(例如 $_POST 的陣列鍵值)。
確定在你開發你自己的附加程式時,PHP環境中的報告錯誤是對所有用戶啟動的,並且 display_errors 也是啟動的。你可以這樣檢查設定:
if (ini_get('display_errors') == 1)
{
exit "Enabled";
}
在一些 display_errors 設定關閉,而你也沒有辦法改變 php.ini 中設定的伺服器上,你可以這樣來開啟:
ini_set('display_errors',1);
注意:在執行時期用 ini_set() 來設定 display_errors 並不等於在 PHP 環境中啟動它。換句話說,如果你的程式出現嚴重錯誤,那可能就沒效了。
簡短的開始標籤
總是使用完整的 PHP 開始標籤,在一些伺服器上, short_open_tag 可能沒有開啟。
不正確:
<? echo $foo; ?>
<?=$foo?>
正確:
<?php echo $foo; ?>
每行一個敘述
不要在一行裡面使用多個敘述。
不正確:
$foo = 'this'; $bar = 'that'; $bat = str_replace($foo, $bar, $bag);
正確:
$foo = 'this';
$bar = 'that';
$bat = str_replace($foo, $bar, $bag);
字串
如果你不需要變數剖析,那就使用單引號字串。如果你真的需要變數剖析,那要使用大括號來防止被進行貪婪的(greedy) token 剖析。如果你的字串內容中有單引號,你也可以用雙引號字串,這樣你就不用跳脫(escape)掉這些字元。
不正確:
"My String" // 沒有變數剖析,就不需要用雙引號字串
"My string $foo" // 要用大括號
'SELECT foo FROM bar WHERE baz = \'bag\'' // 好醜
正確:
'My String'
"My string {$foo}"
"SELECT foo FROM bar WHERE baz = 'bag'"
SQL 查詢
MySQL的關鍵字一律大寫:SELECT、INSERT、UPDATE、WHERE、AS、JOIN、ON、IN 等。
把過長的查詢分成多行以增加閱讀性,並在每個子句好好的斷行。
不正確:
// keywords are lowercase and query is too long for
// a single line (... indicates continuation of line)
$query = $this->db->query("select foo, bar, baz, foofoo, foobar as raboof, foobaz from exp_pre_email_addresses
...where foo != 'oof' and baz != 'zab' order by foobaz limit 5, 100");
正確:
$query = $this->db->query("SELECT foo, bar, baz, foofoo, foobar AS raboof, foobaz
FROM exp_pre_email_addresses
WHERE foo != 'oof'
AND baz != 'zab'
ORDER BY foobaz
LIMIT 5, 100");
函數參數預設值
在適當時就給函數參數預設值,這可以幫助避免在不正確呼叫函數時產生 PHP 錯誤,並且提供了備用值以節省幾行程式碼。參考範例:
function foo($bar = '', $baz = FALSE)