PHP編碼規(guī)范 1. 介紹 1.1. 標準化的重要** 標準化問題在某些方面上讓每個人頭痛,讓人人都覺得大家處于同樣的境地。這有助于讓這些建議在許多的項目中不斷演進,許多公司花費了許多星期逐子字逐句的進行爭論。標準化不是特殊的個人風(fēng)格,它對本地改良是完全開放的。 1.2. 優(yōu)點 當(dāng)一個項目嘗試著遵守公用的標準時,會有以下好處: · 程序員可以了解任何代碼,弄清程序的狀況 · 新人可以很快的適應(yīng)環(huán)境 · 防止新接觸php的人出于節(jié)省時間的需要,自創(chuàng)一套風(fēng)格并養(yǎng)成終生的習(xí)慣 · 防止新接觸php的人一次次的犯同樣的錯誤 · 在一致的環(huán)境下,人們可以減少犯錯的機會 · 程序員們有了一致的敵人 1.3. 缺點 · 因為標準由一些不懂得php的人所制定,所以標準通常看上去很傻 · 因為標準跟我做的不一樣,所以標準通常看上去很傻 · 標準降低了創(chuàng)造力 · 標準在長期互相合作的人群中是沒有必要的 · 標準強迫太多的格式 1.4. 討論 許多項目的經(jīng)驗?zāi)艿贸鲞@樣的結(jié)論:采用編程標準可以使項目更加順利地完成。標準是成功的關(guān)鍵么?當(dāng)然不。但它們可以幫助我們,而且我們需要我們能得到的所有的幫助!老實說,對一個細節(jié)標準的大部分爭論主要是源自自負思想。對一個合理的標準的很少決定能被說為是缺乏技術(shù)**的話,那只是口味的原因罷了。所以,要靈活的控制自負思想,記住,任何項目都取決于團隊合作的努力。 1.5. 解釋 1.5.1. 標準實施 首先應(yīng)該在開發(fā)小組的內(nèi)部找出所有的最重要的元素,也許標準對你的狀況還不夠恰當(dāng)。它可能已經(jīng)概括了 重要的問題,也可能還有人對其中的某些問題表示強烈的反對。無論在什么情況下,只要最后順利的話,人們將成熟的明白到這個標準是合理的,然后其他的程序員們也會發(fā)現(xiàn)它的合理**,并覺得帶著一些保留去遵循這一標準是值得的。如果沒有自愿的合作,可以制定需求:標準一定要經(jīng)過代碼的檢驗。如果沒有檢驗的話,這個解決方案僅僅是一個建立在不精確的基礎(chǔ)上的一大群可笑的人。 1.5.2. 認同觀點 1. 這行不通; 2. 也許可行吧,但是它既不實用又無聊; 3. 這是真的,而且我也告訴過你啊; 4. 這個是我先想到的; 5. 本來就應(yīng)該這樣。 如果您帶著否定的成見而來看待事物的話,請您保持開放的思想。你仍可以做出它是廢話的結(jié)論,但是做出結(jié)論的方法就是你必須要能夠接受不同的思想。請您給自己一點時間去做到它。 1.5.3. 項目的四個階段 1. 數(shù)據(jù)庫結(jié)構(gòu) 2. 設(shè)計 3. 數(shù)據(jù)層 4. HTML層
2. 命名規(guī)則
2.1. 合適的命名
命名是程序規(guī)劃的核心。古人相信只要知道一個人真正的名字就會獲得凌駕于那個人之上的不可思議的力量。只要你給事物想到正確的名字,就會給你以及后來的人帶來比代碼更強的力量。別笑! 名字就是事物在它所處的生態(tài)環(huán)境中一個長久而深遠的結(jié)果。總的來說,只有了解系統(tǒng)的程序員才能為系統(tǒng)取出最合適的名字。如果所有的命名都與其自然相適合,則關(guān)系清晰,含義可以推導(dǎo)得出,一般人的推想也能在意料之中。 如果你發(fā)覺你的命名只有少量能和其對應(yīng)事物相匹配的話, 最好還是重新好好再看看你的設(shè)計吧。
2.2. 類命名
· 在為類(class )命名前首先要知道它是什么。如果通過類名的提供的線索,你還是想不起這個類是什么的話,那么你的設(shè)計就還做的不夠好。 · 超過三個詞組成的混合名是容易造成系統(tǒng)各個實體間的混淆,再看看你的設(shè)計,嘗試使用(CRC Session card)看看該命名所對應(yīng)的實體是否有著那么多的功用。 · 對于派生類的命名應(yīng)該避免帶其父類名的誘惑,一個類的名字只與它自身有關(guān),和它的父類叫什么無關(guān)。 · 有時后綴名是有用的,例如:如果你的系統(tǒng)使用了代理(agent ),那么就把某個部件命名為“下載代理”(DownloadAgent)用以真正的傳送信息。
2.3. 方法和函數(shù)命名
· 通常每個方法和函數(shù)都是執(zhí)行一個動作的,所以對它們的命名應(yīng)該清楚的說明它們是做什么的:用CheckForErrors()代替ErrorCheck(),用DumpDataToFile()代替DataFile()。這么做也可以使功能和數(shù)據(jù)成為更可區(qū)分的物體。 · 有時后綴名是有用的: o Max - 含義為某實體所能賦予的最大值。 o Cnt - 一個運行中的計數(shù)變量的當(dāng)前值。 o Key - 鍵值。 例如:RetryMax 表示最多重試次數(shù),RetryCnt 表示當(dāng)前重試次數(shù)。 · 有時前綴名是有用的: o Is - 含義為問一個關(guān)于某樣事物的問題。無論何時,當(dāng)人們看到Is就會知道這是一個問題。 o Get - 含義為取得一個數(shù)值。 o Set - 含義為設(shè)定一個數(shù)值 例如:IsHitRetryLimit。
2.4. 縮寫詞不要全部使用大寫字母
· 無論如何,當(dāng)遇到以下情況,你可以用首字母大寫其余字母小寫來代替全部使用大寫字母的方法來表示縮寫詞。 使用: GetHtmlStatistic. 不使用: GetHTMLStatistic. 理由 · 當(dāng)命名含有縮略詞時,人們似乎有著非常不同的直覺。統(tǒng)一規(guī)定是最好,這樣一來,命名的含義就完全可以預(yù)知了。 舉個NetworkABCKey的例子,注意C是應(yīng)該是ABC里面的C還是key里面的C,這個是很令人費解的。有些人不在意這些,其他人卻很討厭這樣。所以你會在不同的代碼里看到不同的規(guī)則,使得你不知道怎么去叫它。 例如 class FluidOz // 不要寫成 FluidOZ class GetHtmlStatistic // 不要寫成 GetHTMLStatistic
2.5. 類命名
· 使用大寫字母作為詞的分隔,其他的字母均使用小寫 · 名字的首字母使用大寫 · 不要使用下劃線('_') 理由 · 根據(jù)很多的命名方式,大部分人認為這樣是最好的方式。 例如 class NameOneTwo class Name
2.6. 類庫命名
· 目前命名空間正在越來越廣泛的被采用,以避免不同廠商和團體類庫間的類名沖突。 · 當(dāng)尚未采用命名空間的時候,為了避免類名沖突,一般的做法是在類名前加上獨特的前綴,兩個字符就可以了,當(dāng)然多用一些會更好。 例如 John Johnson的數(shù)據(jù)結(jié)構(gòu)類庫可以用Jj做為前綴,如下: class JjLinkList { } 另一種折中方式是建立包含類庫目錄(事實上Java也是這么做的),以不通的目錄代表不同的命名空間。 例如 Microsoft的數(shù)據(jù)庫相關(guān)類庫可以在: /classes/com/Microsoft/ Database/DbConn.php Apache的數(shù)據(jù)庫相關(guān)類庫可在: /classes/org/apache/Database/DbConn.php
2.7. 方法命名
· 采用與類命名一致的規(guī)則 理由 · 使用所有不同規(guī)則的大部分人發(fā)現(xiàn)這是最好的折衷辦法。 例如 class NameOneTwo { function DoIt() {}; function HandleError() {}; }
2.8. 類屬**命名
· 屬**命名應(yīng)該以字符‘m’為前綴。 · 前綴‘m’后采用于類命名一致的規(guī)則。 · ‘m’總是在名字的開頭起修飾作用,就像以‘r’開頭表示引用一樣。 理由 · 前綴'm'防止類屬**和方法名發(fā)生任何沖突。你的方法名和屬**名經(jīng)常會很類似,特別是存取元素。 例如 class NameOneTwo { function VarAbc() {}; function ErrorNumber() {}; var $mVarAbc; var $mErrorNumber; var $mrName; }
2.9. 方法中參數(shù)命名
· 第一個字符使用小寫字母。 · 在首字符后的所有字都按照類命名規(guī)則首字符大寫。 理由 · 可以區(qū)分方法中的一般變量。 · 你可以使用與類名相似的名稱而不至于產(chǎn)生重名沖突。 例如 class NameOneTwo { function StartYourEngines( &$rSomeEngine, &$rAnotherEngine); }
2.10. 變量命名
· 所有字母都使用小寫 · 使用'_'作為每個詞的分界。 理由 · 通過這一途徑,代碼中變量的作用域是清晰的。 · 所有的變量在代碼中都看起來不同,容易辨認。 例如 function HandleError($errorNumber) { $error = OsErr($errorNumber); $time_of_error = OsErr->GetTimeOfError(); $error_processor = OsErr->GetErrorProcessor(); }
2.11. 引用變量和函數(shù)返回引用
· 引用必須帶‘r’前綴 理由 · 使得類型不同的變量容易辨認 · 它可以確定哪個方法返回可更改對象,哪個方法返回不可更改對象。 例如 class Test { var mrStatus; function DoSomething(&$rStatus) {}; function &rStatus() {}; }
2.12. 全局變量
· 全局變量應(yīng)該帶前綴‘g’。 理由 · 知道一個變量的作用域是非常重要的。 例如 global $gLog; global &$grLog;
2.13. 定義命名 / 全局常量
· 全局常量用'_'分隔每個單詞。 理由 這是命名全局常量的傳統(tǒng)。你要注意不要與其它的定義相沖突。 例如 define("A_GLOBAL_CONSTANT", "Hello world!");
2.14. 靜態(tài)變量
· 靜態(tài)變量應(yīng)該帶前綴‘s’。 理由 · 知道一個變量的作用域是非常重要的。 例如 function test() { static $msStatus = 0; }
2.15. 函數(shù)命名
· 函數(shù)名字采用C GNU的慣例,所有的字母使用小寫字母,使用'_'分割單詞。 理由 · 這樣可以更易于區(qū)分相關(guān)聯(lián)的類名。 例如 function some_bloody_function() { }
2.16. 錯誤返回檢測規(guī)則
· 檢查所有的系統(tǒng)調(diào)用的錯誤信息,除非你要忽略錯誤。 · 為每條系統(tǒng)錯誤消息定義好系統(tǒng)錯誤文本以便include。
3. 書寫規(guī)則
3.1. 大括號 {} 規(guī)則
在三種主要的大括號放置規(guī)則中,有兩種是可以接受的,如下的第一種是最好的: · 將大括號放置在關(guān)鍵詞下方的同列處: if ($condition) while ($condition) { { ... ... } } · 傳統(tǒng)的UNIX的括號規(guī)則是,首括號與關(guān)鍵詞同行,尾括號與關(guān)鍵字同列: if ($condition) { while ($condition) { ... ... } } 理由 · 引起劇烈爭論的非原則的問題可通過折衷的辦法解決,兩種方法任意一種都是可以接受的,然而對于大多數(shù)人來說更喜歡第一種。原因就是心理研究學(xué)習(xí)范疇的東西了。 對于更喜歡第一種還有著更多的原因。如果您使用的字符編輯器支持括號匹配功能的話(例如vi),最重要的就是有一個好的樣式。為什么?我們說當(dāng)你有一大塊的程序而且想知道這一大塊程序是在哪兒結(jié)束的話。你先移到開始的括號,按下按鈕編輯器就會找到與之對應(yīng)的結(jié)束括號,例如: if ($very_long_condition && $second_very_long_condition) { ... } else if (...) { ... } 從一個程序塊移動到另一個程序塊只需要用光標和你的括號匹配鍵就可以了,不需找匹配的括號。
3.2. 縮進/制表符/空格 規(guī)則
· 使用制表符縮進。 · 使用三到四個空格為每層次縮進。 · 不再使用只要一有需要就縮排的方法。對于最大縮進層數(shù),并沒有一個固定的規(guī)矩,假如縮進層數(shù)大于四或者五層的時候,你可以考慮著將代碼因數(shù)分解(factoring out code)。 理由 · 許多編程者支持制表符。 · 當(dāng)人們使用差異太大的制表符標準的話,會使閱讀代碼變得很費力。 · 如此多的人愿意限定最大的縮進層數(shù),它通常從未被看作是一件工作。我們相信程序員們會明智的選擇嵌套的深度。 例如 function func() { if (something bad) { if (another thing bad) { while (more input) { } } } }
3.3. 小括號、關(guān)鍵詞和函數(shù) 規(guī)則
· 不要把小括號和關(guān)鍵詞緊貼在一起,要用空格隔開它們。 · 不要把小括號和函數(shù)名緊貼在一起。 · 除非必要,不要在Return返回語句中使用小括號。 理由 · 關(guān)鍵字不是函數(shù)。如果小括號緊貼著函數(shù)名和關(guān)鍵字,二者很容易被看成是一體的。 例如 if (condition) { }
while (condition) { }
strcmp($s, $s1);
return 1;
3.4. 別在對象架構(gòu)函數(shù)中做實際的工作
別在對象架構(gòu)構(gòu)造函數(shù)中做實際的工作, 構(gòu)造函數(shù)應(yīng)該包含變量的初始化和(或)不會發(fā)生失敗的操作。 理由 · 構(gòu)造不能返回錯誤 。 例如 class Device { function Device() { /* initialize and other stuff */ } function Open() { return FAIL; } };
$dev = new Device; if (FAIL == $dev->Open()) exit(1);
3.5. If Then Else 格式
布局 這由程序員決定。不同的花括號樣式會產(chǎn)生些微不同的樣觀。一個通用方式是: if (條件1) // 注釋 { } else if (條件2) // 注釋 { } else // 注釋 { } 如果你有用到else if 語句的話,通常最好有一個else塊以用于處理未處理到的其他情況。可以的話放一個記錄信息注釋在else處,即使在else沒有任何的動作。 條件格式 總是將恒量放在等號/不等號的左邊,例如: if ( 6 == $errorNum ) ... 一個原因是假如你在等式中漏了一個等號,語法檢查器會為你報錯。第二個原因是你能立刻找到數(shù)值而不是在你的表達式的末端找到它。需要一點時間來習(xí)慣這個格式,但是它確實很有用。
3.6. switch 格式
· 當(dāng)一個case塊處理后,直接轉(zhuǎn)到下一個case塊處理,在這個case塊的最后應(yīng)該加上注釋。 · default case總應(yīng)該存在,它應(yīng)該不被到達,然而如果到達了就會觸發(fā)一個錯誤。 · 如果你要創(chuàng)立一個變量,那就把所有的代碼放在塊中。 例如 switch (...) { case 1: ... // FALL THROUGH case 2: { $v = get_week_number(); ... } break;
default: }
3.7. continue,break 和 ? 的使用
3.7.1. Continue 和 Break Continue 和 break 其實是變相的隱蔽的 goto方法。 Continue 和 break 像 goto 一樣,它們在代碼中是有魔力的,所以要節(jié)儉(盡可能少)的使用它們。使用了這一簡單的魔法,由于一些未公開的原因,讀者將會被定向到只有上帝才知道的地方去。 Continue有兩個主要的問題: · 它可以繞過測試條件。 · 它可以繞過等/不等表達式。 看看下面的例子,考慮一下問題都在哪兒發(fā)生: while (TRUE) { ... // A lot of code ... if (/* some condition */) { continue; } ... // A lot of code ... if ( $i++ > STOP_VALUE) break; } 注意:"A lot of code"是必須的,這是為了讓程序員們不能那么容易的找出錯誤。 通過以上的例子,我們可以得出更進一步的規(guī)則:continue 和 break 混合使用是引起災(zāi)難的正確方法。 3.7.2. ?: 麻煩在于人們往往試著在 ? 和 : 之間塞滿了許多的代碼。以下的是一些清晰的連接規(guī)則: · 把條件放在括號內(nèi)以使它和其他的代碼相分離。 · 如果可能的話,動作可以用簡單的函數(shù)。 · 把所做的動作,“?”,“:”放在不同的行,除非他們可以清楚的放在同一行。 例如 (condition) ? funct1() : func2();
or
(condition) ? long statement : another long statement;
3.8. 聲明塊的定位
· 聲明代碼塊需要對齊。 理由 · 清晰。 · 變量初始化的類似代碼塊應(yīng)該列表。 · &應(yīng)靠近類型,而不是變量名。 例如 var $mDate var& $mrDate var& $mrName var $mName
$mDate = 0; $mrDate = NULL; $mrName = 0; $mName = NULL;
3.9. 每行一個語句
除非這些語句有很密切的聯(lián)系,否則每行只寫一個語句。
3.10. 短方法
方法代碼要限制在一頁內(nèi)。
|