PHP程序編碼規(guī)范標(biāo)準(zhǔn)

2010-08-28 10:50:35來源:西部e網(wǎng)作者:

PHP 編程標(biāo)準(zhǔn)

(English version)

最后修改日期: 2000-11-16


PHP編程標(biāo)準(zhǔn)是經(jīng)由Todd Hoff許可,基于《C++ 編程標(biāo)準(zhǔn)》為PHP而重寫的,
作者為Fredrik Kristiansen,

使用本標(biāo)準(zhǔn),如果您想拷貝一份留做自用的話,那是完全免費(fèi)的,這也是我們制作它的原因。假如您發(fā)現(xiàn)了任何的錯誤又或者是有任何的改進(jìn),請您給筆者發(fā)一個email,以便筆者將它們合并到最新更新中去。

目錄


介紹

標(biāo)準(zhǔn)化的重要性

標(biāo)準(zhǔn)化問題在某些方面上讓每個人頭痛,讓人人都覺得大家處于同樣的境地。這有助于讓這些建
議在許多的項目中不斷演進(jìn),許多公司花費(fèi)了許多星期逐子字逐句的進(jìn)行爭論。標(biāo)準(zhǔn)化不是特殊
的個人風(fēng)格,它對本地改良是完全開放的。

優(yōu)點(diǎn)

當(dāng)一個項目嘗試著遵守公用的標(biāo)準(zhǔn)時,會有以下好處:
  • 程序員可以了解任何代碼,弄清程序的狀況
  • 新人可以很快的適應(yīng)環(huán)境
  • 防止新接觸php的人出于節(jié)省時間的需要,自創(chuàng)一套風(fēng)格并養(yǎng)成終生的習(xí)慣
  • 防止新接觸php的人一次次的犯同樣的錯誤
  • 在一致的環(huán)境下,人們可以減少犯錯的機(jī)會
  • 程序員們有了一致的敵人 :-)

缺點(diǎn)

現(xiàn)在輪到壞處了:
  • 因為標(biāo)準(zhǔn)由一些不懂得php的人所制定,所以標(biāo)準(zhǔn)通?瓷先ズ苌
  • 因為標(biāo)準(zhǔn)跟我做的不一樣,所以標(biāo)準(zhǔn)通常看上去很傻
  • 標(biāo)準(zhǔn)降低了創(chuàng)造力
  • 標(biāo)準(zhǔn)在長期互相合作的人群中是沒有必要的
  • 標(biāo)準(zhǔn)強(qiáng)迫太多的格式
  • 總之人們忽視標(biāo)準(zhǔn)

討論

許多項目的經(jīng)驗?zāi)艿贸鲞@樣的結(jié)論:采用編程標(biāo)準(zhǔn)可以使項目更加順利地完成。標(biāo)準(zhǔn)是成功的關(guān)
鍵么?當(dāng)然不。但它們可以幫助我們,而且我們需要我們能得到的所有的幫助!老實說,對一個
細(xì)節(jié)標(biāo)準(zhǔn)的大部分爭論主要是源自自負(fù)思想。對一個合理的標(biāo)準(zhǔn)的很少決定能被說為是缺乏技術(shù)
性的話,那只是口味的原因罷了。所以,要靈活的控制自負(fù)思想,記住,任何項目都取決于團(tuán)隊
合作的努力。

解釋

慣例

在本文檔中使用“要”字所指的是使用本規(guī)范的所有項目需要遵守規(guī)定的標(biāo)準(zhǔn)。

使用“應(yīng)該”一詞的作用是指導(dǎo)項目定制項目細(xì)節(jié)規(guī)范。因為項目必須適當(dāng)?shù)陌?(include),
排除(exclude)或定制(tailor)需求。

使用“可以”一詞的作用與“應(yīng)該”類似,因為它指明了可選的需求。

標(biāo)準(zhǔn)實施

首先應(yīng)該在開發(fā)小組的內(nèi)部找出所有的最重要的元素,也許標(biāo)準(zhǔn)對你的狀況還不夠恰當(dāng)。它可能已經(jīng)概
括了 重要的問題,也可能還有人對其中的某些問題表示強(qiáng)烈的反對。

無論在什么情況下,只要最后順利的話,人們將成熟的明白到這個標(biāo)準(zhǔn)是合理的,然后其他的程序員們
也會發(fā)現(xiàn)它的合理性,并覺得帶著一些保留去遵循這一標(biāo)準(zhǔn)是值得的。

如果沒有自愿的合作,可以制定需求:標(biāo)準(zhǔn)一定要經(jīng)過代碼的檢驗。

如果沒有檢驗的話,這個解決方案僅僅是一個建立在不精確的基礎(chǔ)上的一大群可笑的人。

認(rèn)同觀點(diǎn)

  1. 這行不通;
  2. 也許可行吧,但是它既不實用又無聊;
  3. 這是真的,而且我也告訴過你;
  4. 這個是我先想到的;
  5. 本來就應(yīng)該這樣。
如果您帶著否定的成見而來看待事物的話,請您保持開放的思想。你仍可以做出它是廢話的結(jié)論,但是做
出結(jié)論的方法就是你必須要能夠接受不同的思想。請您給自己一點(diǎn)時間去做到它。

項目的四個階段

  1. 數(shù)據(jù)庫結(jié)構(gòu)
  2. 設(shè)計
  3. 數(shù)據(jù)層
  4. HTML層

命名規(guī)則

合適的命名

命名是程序規(guī)劃的核心。古人相信只要知道一個人真正的名字就會獲得凌駕于那個人之上的不可思議的力
量。只要你給事物想到正確的名字,就會給你以及后來的人帶來比代碼更強(qiáng)的力量。別笑!

名字就是事物在它所處的生態(tài)環(huán)境中一個長久而深遠(yuǎn)的結(jié)果?偟膩碚f,只有了解系統(tǒng)的程序員才能為系
統(tǒng)取出最合適的名字。如果所有的命名都與其自然相適合,則關(guān)系清晰,含義可以推導(dǎo)得出,一般人的推
想也能在意料之中。

如果你發(fā)覺你的命名只有少量能和其對應(yīng)事物相匹配的話, 最好還是重新好好再看看你的設(shè)計吧。

類命名

  • 在為類(class )命名前首先要知道它是什么。如果通過類名的提供的線索,你還是想不起這個類是
    什么 的話,那么你的設(shè)計就還做的不夠好。

  • 超過三個詞組成的混合名是容易造成系統(tǒng)各個實體間的混淆,再看看你的設(shè)計,嘗試使用(CRC Se-
    ssion card)看看該命名所對應(yīng)的實體是否有著那么多的功用。

  • 對于派生類的命名應(yīng)該避免帶其父類名的誘惑,一個類的名字只與它自身有關(guān),和它的父類叫什么無
    關(guān)。

  • 有時后綴名是有用的,例如:如果你的系統(tǒng)使用了代理(agent ),那么就把某個部件命名為“下
    載代理”(DownloadAgent)用以真正的傳送信息。

方法和函數(shù)命名

  • 通常每個方法函數(shù)都是執(zhí)行一個動作的,所以對它們的命名應(yīng)該清楚的說明它們是做什么的:用
    CheckForErrors()代替ErrorCheck(),用DumpDataToFile()代替DataFile()。這么做也可以使功能和
    數(shù)據(jù)成為更可區(qū)分的物體。

  • 有時后綴名是有用的:
    • Max - 含義為某實體所能賦予的最大值。
    • Cnt - 一個運(yùn)行中的計數(shù)變量的當(dāng)前值。
    • Key - 鍵值。

    例如:RetryMax 表示最多重試次數(shù),RetryCnt 表示當(dāng)前重試次數(shù)。

  • 有時前綴名是有用的:
    • Is - 含義為問一個關(guān)于某樣事物的問題。無論何時,當(dāng)人們看到Is就會知道這是一個問題。
    • Get - 含義為取得一個數(shù)值。
    • Set - 含義為設(shè)定一個數(shù)值

    例如:IsHitRetryLimit。

縮寫詞不要全部使用大寫字母

  • 無論如何,當(dāng)遇到以下情況,你可以用首字母大寫其余字母小寫來代替全部使用大寫字母的方法來表
    示縮寫詞。

    使用: GetHtmlStatistic.
    不使用: GetHTMLStatistic.

理由

  • 當(dāng)命名含有縮略詞時,人們似乎有著非常不同的直覺。統(tǒng)一規(guī)定是最好,這樣一來,命名的含義就完
    全可以預(yù)知了。

    舉個NetworkABCKey的例子,注意C是應(yīng)該是ABC里面的C還是key里面的C,這個是很令人費(fèi)解的。有些
    人不在意這些,其他人卻很討厭這樣。所以你會在不同的代碼里看到不同的規(guī)則,使得你不知道怎么
    去叫它。

例如

   class FluidOz             // 不要寫成 FluidOZ
   class GetHtmlStatistic       // 不要寫成 GetHTMLStatistic


類命名

  • 使用大寫字母作為詞的分隔,其他的字母均使用小寫
  • 名字的首字母使用大寫
  • 不要使用下劃線('_')

理由

  • 根據(jù)很多的命名方式,大部分人認(rèn)為這樣是最好的方式。

例如

   class NameOneTwo

   class Name


類庫命名

  • 目前命名空間正在越來越廣泛的被采用,以避免不同廠商和團(tuán)體類庫間的類名沖突。

  • 當(dāng)尚未采用命名空間的時候,為了避免類名沖突,一般的做法是在類名前加上獨(dú)特的前綴,兩個字符就
    可以了,當(dāng)然多用一些會更好。

例如

John Johnson的數(shù)據(jù)結(jié)構(gòu)類庫可以用Jj做為前綴,如下:
   class JjLinkList
   {
   }


方法命名

  • 采用與類命名一致的規(guī)則

理由

  • 使用所有不同規(guī)則的大部分人發(fā)現(xiàn)這是最好的折衷辦法。

例如

   class NameOneTwo
   {
      function DoIt() {};
      function HandleError() {};
   }


類屬性命名

  • 屬性命名應(yīng)該以字符‘m’為前綴。
  • 前綴‘m’后采用于類命名一致的規(guī)則。
  • ‘m’總是在名字的開頭起修飾作用,就像以‘r’開頭表示引用一樣。

理由

  • 前綴'm'防止類屬性和方法名發(fā)生任何沖突。你的方法名和屬性名經(jīng)常會很類似,特別是存取元素。

例如

   class NameOneTwo
   {
      function VarAbc() {};
      function ErrorNumber() {};

var mVarAbc; var mErrorNumber; var mrName; }


方法中參數(shù)命名

  • 第一個字符使用小寫字母。
  • 在首字符后的所有字都按照類命名規(guī)則首字符大寫。

理由

  • 你可以隨時知道那個變量對應(yīng)那個變量。
  • 你可以使用與類名相似的名稱而不至于產(chǎn)生重名沖突。

例如

   class NameOneTwo
   {
      function StartYourEngines(
                &$rSomeEngine,
                &$rAnotherEngine);
   }


變量命名

  • 所有字母都使用小寫
  • 使用'_'作為每個詞的分界。

理由

  • 通過這一途徑,代碼中變量的作用域是清晰的。
  • 所有的變量在代碼中都看起來不同,容易辨認(rèn)。

例如

function HandleError($errorNumber)
{
      $error = OsErr();
      $time_of_error = OsErr->getTimeOfError;
      $error_processor = OsErr->getErrorProcessor;
}


引用變量和函數(shù)返回引用

  • 引用必須帶‘r’前綴

理由

  • 使得類型不同的變量容易辨認(rèn)
  • 它可以確定哪個方法返回可更改對象,哪個方法返回不可更改對象。

例如

   class Test
   {
      var mrStatus;

function DoSomething(&$rStatus) {}; function &rStatus() {}; }


全局變量

  • 全局變量應(yīng)該帶前綴‘g’。

理由

  • 知道一個變量的作用域是非常重要的。

例如

    global $gLog;
    global &$grLog;


定義命名 / 全局常量

  • 全局常量用'_'分隔每個單詞。

理由

這是命名全局常量的傳統(tǒng)。你要注意不要與其它的定義相沖突。

例如


define("A_GLOBAL_CONSTANT", "Hello world!");

靜態(tài)變量

  • 靜態(tài)變量應(yīng)該帶前綴‘s’。

理由

  • 知道一個變量的作用域是非常重要的。

例如

function test()
{
static $msStatus = 0; }


函數(shù)命名

  • 函數(shù)名字采用C GNU的慣例,所有的字母使用小寫字母,使用'_'分割單詞。

理由

  • 這樣可以更易于區(qū)分相關(guān)聯(lián)的類名。

例如

function some_bloody_function()
{
}


錯誤返回檢測規(guī)則

  • 檢查所有的系統(tǒng)調(diào)用的錯誤信息,除非你要忽略錯誤。
  • 為每條系統(tǒng)錯誤消息定義好系統(tǒng)錯誤文本以便include。


大括號 {} 規(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 (...)
         {
    	...
         }
    
    從一個程序塊移動到另一個程序塊只需要用光標(biāo)和你的括號匹配鍵就可以了,不需要來回的移動到行末去
    找匹配的括號。


縮進(jìn)/制表符/空格 規(guī)則

  • 使用制表符縮進(jìn)。
  • 使用三到四個空格為每層次縮進(jìn)。
  • 不再使用只要一有需要就縮排的方法。對與最大縮進(jìn)層數(shù),并沒有一個固定的規(guī)矩,假如縮進(jìn)層數(shù)大于四或
    者五層的時候,你可以考慮著將代碼因數(shù)分解(factoring out code)。

理由

  • 許多編程者支持制表符。
  • Tabs was invented for a rason
  • 當(dāng)人們使用差異太大的制表符標(biāo)準(zhǔn)的話,會使閱讀代碼變得很費(fèi)力。
  • 如此多的人愿意限定最大的縮進(jìn)層數(shù),它通常從未被看作是一件工作。我們相信程序員們會明智的選擇嵌套
    的深度。

例如

   function func()
   {
      if (something bad)
      {
         if (another thing bad)
         {
            while (more input)
            {
            }
         }
      }
   }


小括號、關(guān)鍵詞和函數(shù) 規(guī)則

  • 不要把小括號和關(guān)鍵詞緊貼在一起,要用空格隔開它們。
  • 不要把小括號和函數(shù)名緊貼在一起。
  • 除非必要,不要在Return返回語句中使用小括號。

理由

  • 關(guān)鍵字不是函數(shù)。如果小括號緊貼著函數(shù)名和關(guān)鍵字,二者很容易被看成是一體的。

例如

    if (condition)
    {
    }

    while (condition)
    {
    }

    strcmp($s, $s1);

    return 1;


RCS關(guān)鍵詞、更改記錄和歷史記錄規(guī)則

直接使用RCS關(guān)鍵詞的規(guī)則必須改變,其中包括使用CVS等類似的支持RCS風(fēng)格關(guān)鍵詞的源代碼控制系統(tǒng):
  • 別在文件以內(nèi)使用 RCS 關(guān)鍵詞。
  • 別在文件中保存歷史修改記錄。
  • 別在文件中保存作者信息記錄。

理由

  • The reasoning is your source control system already keeps all this information. There is no reason to clutter up source files with duplicate information that:
    • makes the files larger
    • makes doing diffs difficult as non source code lines change
    • makes the entry into the file dozens of lines lower in the file which makes a search or jump necessary for each file
    • is easily available from the source code control system and does not need embedding in the file
  • When files must be sent to other organizations the comments may contain internal details that should not be exposed to outsiders.


別在對象架構(gòu)期做實際的工作

別在對象架構(gòu)期做真實的工作,在架構(gòu)期初始化變量和/或做任何不會有失誤的事情。

當(dāng)完成對象架構(gòu)時,為該對象建立一個Open()方法,Open()方法應(yīng)該以對象實體命名。

理由

  • 構(gòu)造不能返回錯誤 。

例如

   class Device
   {
      function Device()    { /* initialize and other stuff */ }
      function Open()  { return FAIL; }
   };

   $dev = new Device;
   if (FAIL == $dev->Open()) exit(1);


If Then Else 格式

布局

這由程序員決定。不同的花括號樣式會產(chǎn)生些微不同的樣觀。一個通用方式是:

   if (條件1)                 // 注釋
   {
   }
   else if (條件2)            // 注釋
   {
   }
   else                           // 注釋
   {
   }
如果你有用到else if 語句的話,通常最好有一個else塊以用于處理未處理到的其他情況?梢缘脑
放一個記錄信息注釋在else處,即使在else沒有任何的動作。

條件格式

總是將恒量放在等號/不等號的左邊,例如:

if ( 6 == $errorNum ) ...

一個原因是假如你在等式中漏了一個等號,語法檢查器會為你報錯。第二個原因是你能立刻找到數(shù)值
而不是在你的表達(dá)式的末端找到它。需要一點(diǎn)時間來習(xí)慣這個格式,但是它確實很有用。


switch 格式

  • Falling through a case statement into the next case statement shall be permitted as long as a comment is included.
  • default case總應(yīng)該存在,它應(yīng)該不被到達(dá),然而如果到達(dá)了就會觸發(fā)一個錯誤。
  • 如果你要創(chuàng)立一個變量,那就把所有的代碼放在塊中。

例如

   switch (...)
   {
      case 1:
         ...
      // FALL THROUGH

      case 2:
      {
         $v = get_week_number();
         ...
      }
      break;

      default:
   }


continue,break? 的使用:

Continue 和 Break

Continue 和 break 其實是變相的隱蔽的 goto方法。

Continue 和 break 像 goto 一樣,它們在代碼中是有魔力的,所以要節(jié)儉(盡可能少)的使用它們。
使用了這一簡單的魔法,由于一些未公開的原因,讀者將會被定向到只有上帝才知道的地方去。

Continue有兩個主要的問題:

  • 它可以繞過測試條件。
  • 它可以繞過等/不等表達(dá)式。

看看下面的例子,考慮一下問題都在哪兒發(fā)生:

while (TRUE)
{
   ...
   // A lot of code
   ...
   if (/* some condition */) {
      continue;
   }
   ...
   // A lot of code
   ...
   if ( $i++ > STOP_VALUE) break;
}
注意:"A lot of code"是必須的,這是為了讓程序員們不能那么容易的找出錯誤。

通過以上的例子,我們可以得出更進(jìn)一步的規(guī)則:continue 和 break 混合使用是引起災(zāi)難的正確方法。

?:

麻煩在于人民往往試著在 ? 和 : 之間塞滿了許多的代碼。以下的是一些清晰的連接規(guī)則:
  • 把條件放在括號內(nèi)以使它和其他的代碼相分離。
  • 如果可能的話,動作可以用簡單的函數(shù)。
  • 把所做的動作,“?”,“:”放在不同的行,除非他們可以清楚的放在同一行。

例如

   (condition) ? funct1() : func2();

   or

   (condition)
      ? long statement
      : another long statement;


聲明塊的定位

  • 聲明代碼塊需要對齊。

理由Justification

  • 清晰。
  • 變量初始化的類似代碼塊應(yīng)該列表。
  • The ??token should be adjacent to the type, not the name.

例如

   var       $mDate
   var&      $mrDate
   var&      $mrName
   var       $mName

   $mDate    = 0;
   $mrDate   = NULL;
   $mrName   = 0;
   $mName    = NULL;


每行一個語句

除非這些語句有很密切的聯(lián)系,否則每行只寫一個語句。


短方法

  • 方法代碼要限制在一頁內(nèi)。

理由

  • 這個思想是,每一個方法代表著一個完成單獨(dú)目的的技術(shù)。
  • 從長遠(yuǎn)來說,過多的無效參數(shù)是錯誤的。
  • 調(diào)用函數(shù)比不調(diào)用要慢,但是這需要詳細(xì)考慮做出決定(見premature optimization 未完善的優(yōu)化)。


記錄所有的空語句

總是記錄下for或者是while的空塊語句,以便清楚的知道該段代碼是漏掉了,還是故意不寫的。

   while ($dest++ = $src++)
      ;         // VOID


不要采用缺省方法測試非零值

不要采用缺省值測試非零值,也就是使用:

   if (FAIL != f())
比下面的方法好:

   if (f())
即使 FAIL 可以含有 0 值 ,也就是PHP認(rèn)為false的表示。在某人決定用-1代替0作為失敗返回值的時候,
一個顯式的測試就可以幫助你了。就算是比較值不會變化也應(yīng)該使用顯式的比較;例如:if (!($bufsize % strlen($str)))
應(yīng)該寫成:if (($bufsize % strlen($str)) == 0)以表示測試的數(shù)值(不是布爾)型。一個經(jīng)常出
問題的地方就是使用strcmp來測試一個字符等式,結(jié)果永遠(yuǎn)也不會等于缺省值。

非零測試采用基于缺省值的做法,那么其他函數(shù)或表達(dá)式就會受到以下的限制:

  • 只能返回0表示失敗,不能為/有其他的值。
  • 命名以便讓一個真(true)的返回值是絕對顯然的,調(diào)用函數(shù)IsValid()而不是Checkvalid()。


布爾邏輯類型

大部分函數(shù)在FALSE的時候返回0,但是發(fā)揮非0值就代表TRUE,因而不要用1(TRUE,YES,諸如此類)等式檢測一個布爾值,應(yīng)該用0(FALSE,NO,諸如此類)的不等式來代替:


   if (TRUE == func()) { ...
應(yīng)該寫成:

   if (FALSE != func()) { ...


通常避免嵌入式的賦值

有時候在某些地方我們可以看到嵌入式賦值的語句,那些結(jié)構(gòu)不是一個比較好的少冗余,可讀性強(qiáng)的方法。


   while ($a != ($c = getchar()))
   {
      process the character
   }

++和--操作符類似于賦值語句。因此,出于許多的目的,在使用函數(shù)的時候會產(chǎn)生副作用。使用嵌入式賦值
提高運(yùn)行時性能是可能的。無論怎樣,程序員在使用嵌入式賦值語句時需要考慮在增長的速度和減少的可維
護(hù)性兩者間加以權(quán)衡。例如:


   a = b + c;
   d = a + r;
不要寫成:

   d = (a = b + c) + r;
雖然后者可以節(jié)省一個周期。但在長遠(yuǎn)來看,隨著程序的維護(hù)費(fèi)用漸漸增長,程序的編寫者對代碼漸漸遺忘,
就會減少在成熟期的最優(yōu)化所得。


重用您和其他人的艱苦工作

跨工程的重用在沒有一個通用結(jié)構(gòu)的情況下幾乎是不可能的。對象符合他們現(xiàn)有的服務(wù)需求,不同的過程有著
不同的服務(wù)需求環(huán)境,這使對象重用變得很困難。

開發(fā)一個通用結(jié)構(gòu)需要預(yù)先花費(fèi)許多的努力來設(shè)計。當(dāng)努力不成功的時候,無論出于什么原因,有幾種辦法推
薦使用:

請教!給群組發(fā)Email求助

這個簡單的方法很少被使用。因為有些程序員們覺得如果他向其他人求助,會顯得自己水平低,這多傻啊!做新
的有趣的工作,不要一遍又一遍的做別人已經(jīng)做過的東西。

如果你需要某些事項的源代碼,如果已經(jīng)有某人做過的話,就向群組發(fā)email求助。結(jié)果會很驚喜哦!

在許多大的群組中,個人往往不知道其他人在干什么。你甚至可以發(fā)現(xiàn)某人在找一些東西做,并且自愿為你寫代
碼,如果人們在一起工作,外面就總有一個金礦。

告訴!當(dāng)你在做事的時候,把它告訴所有人

如果你做了什么可重用的東西的話,讓其他人知道。別害羞,也不要為了保護(hù)自豪感而把你的工作成果藏起來。
一旦養(yǎng)成共享工作成果的習(xí)慣,每個人都會獲得更多。

Don't be Afraid of Small Libraries

對于代碼重用,一個常見的問題就是人們不從他們做過的代碼中做庫。一個可重用的類可能正隱蔽在一個程序目
錄并且決不會有被分享的激動,因為程序員不會把類分拆出來加入庫中。

這樣的其中一個原因就是人們不喜歡做一個小庫,對小庫有一些不正確感覺。把這樣的感覺克服掉吧,電腦才不
關(guān)心你有多少個庫呢。

如果你有一些代碼可以重用,而且不能放入一個已經(jīng)存在的庫中,那么就做一個新的庫吧。如果人們真的考慮重
用的話,庫不會在很長的一段時間里保持那么小的。

If you are afraid of having to update makefiles when libraries are recomposed or added then don't include libraries in your makefiles, include the idea of services. Base level makefiles define services that are each composed of a set of libraries. Higher level makefiles specify the services they want. When the libraries for a service change only the lower level makefiles will have to change.

Keep a Repository

Most companies have no idea what code they have. And most programmers still don't communicate what they have done or ask for what currently exists. The solution is to keep a repository of what's available.

In an ideal world a programmer could go to a web page, browse or search a list of packaged libraries, taking what they need. If you can set up such a system where programmers voluntarily maintain such a system, great. If you have a librarian in charge of detecting reusability, even better.

Another approach is to automatically generate a repository from the source code. This is done by using common class, method, library, and subsystem headers that can double as man pages and repository entries.


評價注釋

注釋應(yīng)該是講述一個故事

Consider your comments a story describing the system. Expect your comments to be extracted by a robot and formed into a man page. Class comments are one part of the story, method signature comments are another part of the story, method arguments another part, and method implementation yet another part. All these parts should weave together and inform someone else at another point of time just exactly what you did and why.

Document Decisions

Comments should document decisions. At every point where you had a choice of what to do place a comment describing which choice you made and why. Archeologists will find this the most useful information.

使用標(biāo)頭說明

利用類似ccdoc的文檔抽取系統(tǒng)。在這一文檔的其他部分描述的是怎么利用ccdoc記錄一個類和方法。
這些標(biāo)頭說明可以以這樣的一個方式來提取并分析和加以組織,它們不像一般的標(biāo)頭一樣是無用的。
因此花時間去填上他吧。

注釋布局

工程的每部分都有特定的注釋布局。

Make Gotchas Explicit

Explicitly comment variables changed out of the normal control flow or other code likely to break during maintenance. Embedded keywords are used to point out issues and potential problems. Consider a robot will parse your comments looking for keywords, stripping them out, and making a report so people can make a special effort where needed.

Gotcha Keywords

  • :TODO: topic
    Means there's more to do here, don't forget.

  • :BUG: [bugid] topic
    means there's a Known bug here, explain it and optionally give a bug ID.

  • :KLUDGE:
    When you've done something ugly say so and explain how you would do it differently next time if you had more time.

  • :TRICKY:
    Tells somebody that the following code is very tricky so don't go changing it without thinking.

  • :WARNING:
    Beware of something.

  • :PHARSER:
    Sometimes you need to work around a pharser problem. Document it. The problem may go away eventually.

  • :ATTRIBUTE: value
    The general form of an attribute embedded in a comment. You can make up your own attributes and they'll be extracted.

Gotcha Formatting

  • Make the gotcha keyword the first symbol in the comment.
  • Comments may consist of multiple lines, but the first line should be a self-containing, meaningful summary.
  • The writer's name and the date of the remark should be part of the comment. This information is in the source repository, but it can take a quite a while to find out when and by whom it was added. Often gotchas stick around longer than they should. Embedding date information allows other programmer to make this decision. Embedding who information lets us know who to ask.

Example

   // :TODO: tmh 960810: possible performance problem
   // We should really use a hash table here but for now we'll
   // use a linear search.

   // :KLUDGE: tmh 960810: possible unsafe type cast
   // We need a cast here to recover the derived type. It should
   // probably use a virtual method or template.

See Also

See Interface and Implementation Documentation for more details on how documentation should be laid out.


Interface and Implementation Documentation

There are two main audiences for documentation:
  • Class Users
  • Class Implementors
With a little forethought we can extract both types of documentation directly from source code.

Class Users

Class users need class interface information which when structured correctly can be extracted directly from a header file. When filling out the header comment blocks for a class, only include information needed by programmers who use the class. Don't delve into algorithm implementation details unless the details are needed by a user of the class. Consider comments in a header file a man page in waiting.

Class Implementors

Class implementors require in-depth knowledge of how a class is implemented. This comment type is found in the source file(s) implementing a class. Don't worry about interface issues. Header comment blocks in a source file should cover algorithm issues and other design decisions. Comment blocks within a method's implementation should explain even more.


目錄文檔

所有的目錄下都需要具有README文檔,其中包括:
  • 該目錄的功能及其包含內(nèi)容
  • 一個對每一文件的在線說明(帶有l(wèi)ink),每一個說明通常還應(yīng)該提取文件標(biāo)頭的一些屬性名字。
  • 包括設(shè)置、使用說明
  • 指導(dǎo)人民如何連接相關(guān)資源:
    • 源文件索引
    • 在線文檔
    • 紙文檔
    • 設(shè)計文檔
  • 其他對讀者有幫助的東西
考慮一下,當(dāng)每個原有的工程人員走了,在6個月之內(nèi)來的一個新人,那個孤獨(dú)受驚嚇的探險者通過整個
工程的源代碼目錄樹,閱讀說明文件,源文件的標(biāo)頭說明等等做為地圖,他應(yīng)該有能力穿越整個工程。


Use a Design Notation and Process

Programmers need to have a common language for talking about coding, designs, and the software process in general. This is critical to project success.

Any project brings together people of widely varying skills, knowledge, and experience. Even if everyone on a project is a genius you will still fail because people will endlessly talk past each other because there is no common language and processes binding the project together. All you'll get is massive fights, burnout, and little progress. If you send your group to training they may not come back seasoned experts but at least your group will all be on the same page; a team.

There are many popular methodologies out there. The point is to do some research, pick a method, train your people on it, and use it. Take a look at the top of this page for links to various methodologies.

You may find the CRC (class responsibility cards) approach to teasing out a design useful. Many others have. It is an informal approach encouraging team cooperation and focusing on objects doing things rather than objects having attributes. There's even a whole book on it: Using CRC Cards by Nancy M. Wilkinson.


Using Use Cases

A use case is a generic description of an entire transaction involving several objects. A use case can also describe the behaviour of a set of objects, such as an organization. A use case model thus presents a collection of use cases and is typically used to specify the behavior of a whole application system together with one or more external actors that interact with the system.

An individual use case may have a name (although it is typically not a simple name). Its meaning is often written as an informal text description of the external actors and the sequences of events between objects that make up the transaction. Use cases can include other use cases as part of their behaviour.

Requirements Capture

Use cases attempt to capture the requirements for a system in an understandable form. The idea is by running through a set of use case we can verify that the system is doing what it should be doing.

Have as many use cases as needed to describe what a system needs to accomplish.

The Process

  • Start by understanding the system you are trying to build.
  • Create a set of use cases describing how the system is to be used by all its different audiences.
  • Create a class and object model for the system.
  • Run through all the use cases to make sure your model can handle all the cases. Update your model and create new use cases as necessary.


Open/Closed Principle

The Open/Closed principle states a class must be open and closed where:
  • open means a class has the ability to be extended.
  • closed means a class is closed for modifications other than extension. The idea is once a class has been approved for use having gone through code reviews, unit tests, and other qualifying procedures, you don't want to change the class very much, just extend it.
The Open/Closed principle is a pitch for stability. A system is extended by adding new code not by changing already working code. Programmers often don't feel comfortable changing old code because it works! This principle just gives you an academic sounding justification for your fears :-)

In practice the Open/Closed principle simply means making good use of our old friends abstraction and polymorphism. Abstraction to factor out common processes and ideas. Inheritance to create an interface that must be adhered to by derived classes.


Design by Contract

The idea of design by contract is strongly related to LSP . A contract is a formal statement of what to expect from another party. In this case the contract is between pieces of code. An object and/or method states that it does X and you are supposed to believe it. For example, when you ask an object for its volume that's what you should get. And because volume is a verifiable attribute of a thing you could run a series of checks to verify volume is correct, that is, it satisfies its contract.

The contract is enforced in languages like Eiffel by pre and post condition statements that are actually part of the language. In other languages a bit of faith is needed.

Design by contract when coupled with language based verification mechanisms is a very powerful idea. It makes programming more like assembling spec'd parts.


其他雜項

這一部分包含著各種各樣的該做的和不該做的。

  • 在需要用到離散的數(shù)值使,不要使用浮點(diǎn)數(shù)變量。采用浮點(diǎn)數(shù)來做循環(huán)計數(shù)器無異于向自己的腳
    開槍。測試浮點(diǎn)數(shù)時總要使用 <= 或 => ,永遠(yuǎn)不要用 = 或 => 。

  • 不要使用程序自動美化器,得益于好的程序樣式的主要的人就是程序員自己,特別是剛開著手代
    碼、算法設(shè)計的程序員,使用程序自動美化器僅僅能根據(jù)語法來更正程序,因此當(dāng)對空白和縮進(jìn)
    的注意有很大需要時,它是不可能做到的。正常的細(xì)心注意細(xì)節(jié)的程序員們能很好的用清晰直觀
    的樣式來完成一個函數(shù)或文件(換句話來說,一些直觀的樣式是意向的規(guī)定而不是程序自動美化
    器能讀懂的智慧)。馬虎的程序員應(yīng)該學(xué)習(xí)細(xì)致的程序員,不要依賴程序自動美化器來增加程序
    的可讀性。最初的美化器是必須分析源代碼的程序,復(fù)雜的美化器不值得通過這樣獲得好處,美
    化器最好用于生成總的機(jī)器建立(machine-generated)格式代碼。

  • 對邏輯表達(dá)式第二個 = 不小心的忽略是一個問題,以下顯得混亂而且更像是錯誤:
            if ($abool= $bbool) { ... }
         
    程序員在這里真的是要賦值么?一般常常是,但通常又不是這樣。這樣避免引起這樣的混亂呢?解
    決方案就是不要這樣做,利用顯式和隱式的判斷測試,推薦的方法是在做測試前先做賦值:
    
           $abool= $bbool;
           if ($abool) { ... }
        

使用if (0)來注釋外部代碼塊

有時需要注釋大段的測試代碼,最簡單的方法就是使用if (0)塊:
   function example()
   {
      great looking code

      if (0) {
      lots of code
      }

      more code
    }

你不能使用/**/,因為注釋內(nèi)部不能包含注釋,而大段的程序中可以包含注釋,不是么?


Different Accessor Styles

Why Accessors?

Access methods provide access to the physical or logical attributes of an object. We disallow direct access to attributes to break dependencies, the reason we do most things. Directly accessing an attribute exposes implementation details about the object.

To see why ask yourself:

  • What if the object decided to provide the attribute in a way other than physical containment?
  • What if it had to do a database lookup for the attribute?
  • What if a different object now contained the attribute?
If any of the above changed code would break. An object makes a contract with the user to provide access to a particular attribute; it should not promise how it gets those attributes. Accessing a physical attribute makes such a promise.

Implementing Accessors

There are three major idioms for creating accessors.

Get/Set

   class X
   {
      function GetAge()        { return $this->mAge; }
      function SetAge($age)    { $mAge= $age; }
      var $mAge;
   }

One Method Name

   class X
   {
      function Age()           { return $mAge; }
      function Age($age)       { $mAge= $age; }
      var $mAge;
   }
Similar to Get/Set but cleaner. Use this approach when not using the Attributes as Objects approach.

Attributes as Objects

   class X
   {
      function Age()           { return $mAge; }
      function rAge()          { return &$mAge; }

      function Name()          { return mName; }
      function rName()         { return &$mName; }

      var $mAge;
      var $mName;
   }
X $x; $x->rName()= "test";
The above two attribute examples shows the strength and weakness of the Attributes as Objects approach.

When using rAge(), which is not a real object, the variable is set directly because rAge() returns a reference. The object can do no checking of the value or do any representation reformatting. For many simple attributes, however, these are not horrible restrictions.


Layering

Layering is the primary technique for reducing complexity in a system. A system should be divided into layers. Layers should communicate between adjacent layers using well defined interfaces. When a layer uses a non-adjacent layer then a layering violation has occurred.

A layering violation simply means we have dependency between layers that is not controlled by a well defined interface. When one of the layers changes code could break. We don't want code to break so we want layers to work only with other adjacent layers.

Sometimes we need to jump layers for performance reasons. This is fine, but we should know we are doing it and document appropriately.


Code Reviews

If you can make a formal code review work then my hat is off to you. Code reviews can be very useful. Unfortunately they often degrade into nit picking sessions and endless arguments about silly things. They also tend to take a lot of people's time for a questionable payback.

My god he's questioning code reviews, he's not an engineer!

Not really, it's the form of code reviews and how they fit into normally late chaotic projects is what is being questioned.

First, code reviews are way too late to do much of anything useful. What needs reviewing are requirements and design. This is where you will get more bang for the buck.

Get all relevant people in a room. Lock them in. Go over the class design and requirements until the former is good and the latter is being met. Having all the relevant people in the room makes this process a deep fruitful one as questions can be immediately answered and issues immediately explored. Usually only a couple of such meetings are necessary.

If the above process is done well coding will take care of itself. If you find problems in the code review the best you can usually do is a rewrite after someone has sunk a ton of time and effort into making the code "work."

You will still want to do a code review, just do it offline. Have a couple people you trust read the code in question and simply make comments to the programmer. Then the programmer and reviewers can discuss issues and work them out. Email and quick pointed discussions work well. This approach meets the goals and doesn't take the time of 6 people to do it.


Create a Source Code Control System Early and Not Often

A common build system and source code control system should be put in place as early as possible in a project's lifecycle, preferably before anyone starts coding. Source code control is the structural glue binding a project together. If programmers can't easily use each other's products then you'll never be able to make a good reproducible build and people will piss away a lot of time. It's also hell converting rogue build environments to a standard system. But it seems the right of passage for every project to build their own custom environment that never quite works right.

Some issues to keep in mind:

  • Shared source environments like CVS usually work best in largish projects.
  • If you use CVS use a reference tree approach. With this approach a master build tree is kept of various builds. Programmers checkout source against the build they are working on. They only checkout what they need because the make system uses the build for anything not found locally. Using the -I and -L flags makes this system easy to setup. Search locally for any files and libraries then search in the reference build. This approach saves on disk space and build time.
  • Get a lot of disk space. With disk space as cheap it is there is no reason not to keep plenty of builds around.
  • Make simple things simple. It should be dead simple and well documented on how to:
    • check out modules to build
    • how to change files
    • how to add new modules into the system
    • how to delete modules and files
    • how to check in changes
    • what are the available libraries and include files
    • how to get the build environment including all compilers and other tools

    Make a web page or document or whatever. New programmers shouldn't have to go around begging for build secrets from the old timers.

  • On checkins log comments should be useful. These comments should be collected every night and sent to interested parties.

Sources

If you have the money many projects have found Clear Case a good system. Perfectly workable systems have been build on top of GNU make and CVS. CVS is a freeware build environment built on top of RCS. Its main difference from RCS is that is supports a shared file model to building software.


Create a Bug Tracking System Early and Not Often

The earlier people get used to using a bug tracking system the better. If you are 3/4 through a project and then install a bug tracking system it won't be used. You need to install a bug tracking system early so people will use it.

Programmers generally resist bug tracking, yet when used correctly it can really help a project:

  • Problems aren't dropped on the floor.
  • Problems are automatically routed to responsible individuals.
  • The lifecycle of a problem is tracked so people can argue back and forth with good information.
  • Managers can make the big schedule and staffing decisions based on the number of and types of bugs in the system.
  • Configuration management has a hope of matching patches back to the problems they fix.
  • QA and technical support have a communication medium with developers.
Not sexy things, just good solid project improvements.

FYI, it's not a good idea to reward people by the number of bugs they fix :-)

Source code control should be linked to the bug tracking system. During the part of a project where source is frozen before a release only checkins accompanied by a valid bug ID should be accepted. And when code is changed to fix a bug the bug ID should be included in the checkin comments.

Sources

Several projects have found DDTS a workable system (I 've not verified this link for this PHP release, DDTS may not work for PHP). There is also a GNU bug tracking system available. Roll your own is a popular option but using an existing system seems more cost efficient.


Honor Responsibilities

Responsibility for software modules is scoped. Modules are either the responsibility of a particular person or are common. Honor this division of responsibility. Don't go changing things that aren't your responsibility to change. Only mistakes and hard feelings will result.

Face it, if you don't own a piece of code you can't possibly be in a position to change it. There's too much context. Assumptions seemingly reasonable to you may be totally wrong. If you need a change simply ask the responsible person to change it. Or ask them if it is OK to make such-n-such a change. If they say OK then go ahead, otherwise holster your editor.

Every rule has exceptions. If it's 3 in the morning and you need to make a change to make a deliverable then you have to do it. If someone is on vacation and no one has been assigned their module then you have to do it. If you make changes in other people's code try and use the same style they have adopted.

Programmers need to mark with comments code that is particularly sensitive to change. If code in one area requires changes to code in an another area then say so. If changing data formats will cause conflicts with persistent stores or remote message sending then say so. If you are trying to minimize memory usage or achieve some other end then say so. Not everyone is as brilliant as you.

The worst sin is to flit through the system changing bits of code to match your coding style. If someone isn't coding to the standards then ask them or ask your manager to ask them to code to the standards. Use common courtesy.

Code with common responsibility should be treated with care. Resist making radical changes as the conflicts will be hard to resolve. Put comments in the file on how the file should be extended so everyone will follow the same rules. Try and use a common structure in all common files so people don't have to guess on where to find things and how to make changes. Checkin changes as soon as possible so conflicts don't build up.

As an aside, module responsibilities must also be assigned for bug tracking purposes.


PHP文件擴(kuò)展名

我見過許多種PHP文件的擴(kuò)展名(.html, .php, .php3, .php4, .phtml, .inc, .class...)
  • 所有瀏覽者可見頁面使用.html
  • 所有類、函數(shù)庫文件使用.php

理由

  • 擴(kuò)展名描述的是那種數(shù)據(jù)是用戶將會收到的。PHP是解釋為HTML的。


不要不可思議的數(shù)字

一個在源代碼中使用了的赤裸裸的數(shù)字是不可思議的數(shù)字,因為包括作者,在三個月內(nèi),沒人它的含義。例如:

if      (22 == $foo) { start_thermo_nuclear_war(); }
else if (19 == $foo) { refund_lotso_money(); }
else if (16 == $foo) { infinite_loop(); }
else                { cry_cause_im_lost(); }
在上例中22和19的含義是什么呢?如果一個數(shù)字改變了,或者這些數(shù)字只是簡單的錯誤,你會怎么想?

使用不可思議的數(shù)字是該程序員是業(yè)余運(yùn)動員的重要標(biāo)志,這樣的程序員從來沒有在團(tuán)隊環(huán)境中工作過,
又或者是為了維持代碼而不得不做的,否則他們永遠(yuǎn)不會做這樣的事。

你應(yīng)該用define()來給你想表示某樣?xùn)|西的數(shù)值一個真正的名字,而不是采用赤裸裸的數(shù)字,例如:

define("PRESIDENT_WENT_CRAZY", "22");
define("WE_GOOFED", "19");
define("THEY_DIDNT_PAY", "16");

if      (PRESIDENT_WENT_CRAZY == $foo) { start_thermo_nuclear_war(); }
else if (WE_GOOFED            == $foo) { refund_lotso_money(); }
else if (THEY_DIDNT_PAY       == $foo) { infinite_loop(); }
else                                   { happy_days_i_know_why_im_here(); }
現(xiàn)在不是變得更好了么?


Promise of OO

OO has been hyped to the extent you'd figure it would solve world hunger and usher in a new era of world peace. Not! OO is an approach, a philosophy, it's not a recipe which blindly followed yields quality.

Robert Martin put OO in perspective:

  • OO, when properly employed, does enhance the reusability of software. But it does so at the cost of complexity and design time. Reusable code is more complex and takes longer to design and implement. Furthermore, it often takes two or more tries to create something that is even marginally reusable.
  • OO, when properly employed, does enhance the software's resilience to change. But it does so at the cost of complexity and design time. This trade off is almost always a win, but it is hard to swallow sometimes.
  • OO does not necessarily make anything easier to understand. There is no magical mapping between the software concepts and every human's map of the real world. Every person is different. What one person percieves to be a simple and elegant design, another will perceive as convoluted and opaque.
  • If a team has been able, by applying point 1 above, to create a repository of reusable items, then development times can begin to shrink significantly due to reuse.
  • If a team has been able, by applying point 2 above, to create software that is resilient to change, then maintenance of that software will be much simpler and much less error prone.


Thin vs. Fat Class Interfaces

How many methods should an object have? The right answer of course is just the right amount, we'll call this the Goldilocks level. But what is the Goldilocks level? It doesn't exist. You need to make the right judgment for your situation, which is really what programmers are for :-)

The two extremes are thin classes versus thick classes. Thin classes are minimalist classes. Thin classes have as few methods as possible. The expectation is users will derive their own class from the thin class adding any needed methods.

While thin classes may seem "clean" they really aren't. You can't do much with a thin class. Its main purpose is setting up a type. Since thin classes have so little functionality many programmers in a project will create derived classes with everyone adding basically the same methods. This leads to code duplication and maintenance problems which is part of the reason we use objects in the first place. The obvious solution is to push methods up to the base class. Push enough methods up to the base class and you get thick classes.

Thick classes have a lot of methods. If you can think of it a thick class will have it. Why is this a problem? It may not be. If the methods are directly related to the class then there's no real problem with the class containing them. The problem is people get lazy and start adding methods to a class that are related to the class in some willow wispy way, but would be better factored out into another class. Judgment comes into play again.

Thick classes have other problems. As classes get larger they may become harder to understand. They also become harder to debug as interactions become less predictable. And when a method is changed that you don't use or care about your code will still have to be retested, and rereleased.


Recent Changes

  1. 2000-11-16. Release
關(guān)鍵詞:PHP