原始英文版本: http://www.dagbladet.no/development/phpcodingstandard/

PHP 程式設計標準

最後更新: 2003-02-17

PHP 程式設計標準是經由Todd Hoff授權,基於他的C++ 程式設計標準 改寫成PHP的版本
本文件作者為 Fredrik Kristiansen / DB Medialab, Oslo 2000-2003.

使用這份文件時. 您可以任意的在自己的電腦上保留本文件的備份,這也是我們製作這份文件的目的!如果您發現任何錯誤或是需要調整的地方,請 e-mail 給我 您建議的內容,這樣一來我才能夠將它加入本文件的最新版本

目錄


介紹

標準化的重要性

如果在某些層面的標準造成了每個人的困擾,標準化可以讓他們有個共同的遊戲規則。
這份文件歷經了許多專案、公司的驗證,正確的說法應該是經過了數週的爭執。
這份文件避免了特殊的個人風格而且可以隨時視情況調整。

正確的觀點

當一個專案嘗試著遵守公用的標準時,會有以下好處:

錯誤的觀點

現在輪到錯誤的觀點了:

討論

從許多專案的經驗中得知,採用程式設計標準可以讓專案進行的順利些。
不過標準是成功的必要條件嗎?當然不是!
但他們是有幫助的,我們需要任何可以取得的幫助!
老實說,大部分對於特定標準的爭執多來自於自負不已的人。
在合理的標準中很少有所謂的技術缺陷,只是習慣的問題罷了。
所以腦子活一點,控制那樣的自負思想,並且記得任何專案都是基於一個團隊的努力。

解釋

慣例

在本文檔中使用“要”字所指的是使用本規範的所有專案需要遵守規定的標準。

使用“應該”一詞的作用是引導個別專案標準的組成,請依照詞義適當的包含、排除或組合需求。

“也許”一詞相近於“應該”,通常指的是次要的需求。

標準的執行

首先應該在開發小組的內部找出所有的最重要的元素,也許標準對您的狀況還不夠恰當。它可能已經概括了重要的問題,也可能還有人對其中的某些問題表示強烈的反對。 :-)

無論如何,只要最後順利的話,人們將成熟的明白這個標準是合理的,然後其他的程式設計人員也會發現它的合理性,並覺得帶著一些疑問去遵循這個標準是值得的。

如果沒有配合的意願,可以把這個標準改為要求,所有的程式碼都必須基於這個編準做檢驗。

如果沒有經過檢驗,唯一的解決方案會困擾不斷。

認同觀點

  1. 不可能
  2. 也許可能,但是相當的薄弱與無趣
  3. 這是事實,我也已經告訴過你了
  4. 這是我先想到的
  5. 本來就應該這樣
如果您帶著否定的成見而來看待事物的話,請您保持開放的思想。
你仍可以做出它是廢話的結論,但是做 出結論的方法就是你必須要能夠接受不同的思想。請您給自己一點時間去做到它。

命名規則

合適的命名

命名是程式規劃的核心。古人相信只要知道一個人真正的名字就會獲得淩駕於那個人之上不可思議的力量。
只要你為事物想到正確的名字,將會為你自己以及後來的人帶來比程式碼更強的力量。別笑!

一個名稱是事物在它所處的生態環境中一個長久而深遠的結果,只有了解系統的程式設計人員知道如何建立"適合"這個系統的名稱。如果名稱恰當,許多事情就自然而然結合在一起,關係因此明確,意義因此容易傳達,可以預期一般人心中所推論的結果。

如果您發現自己所有的命名都是東西與命令,您也許需要重新檢視自己的設計。

物件名稱

方法和函數命名

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

解釋

範例

   class FluidOz             // 不要用 FluidOZ
class GetHtmlStatistic // 不要用 GetHTMLStatistic


物件(Class)命名

解釋

範例

   class NameOneTwo
class Name


物件庫(Class Library)命名

範例

John Johnson的完整資料結構庫可以使用前置字元JJ ,所以物件變成:
   class JjLinkList
   {
   }


方法(Method)命名

解釋

範例

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


物件屬性(Class Attribute)命名

解釋

範例

   class NameOneTwo
   {
      function VarAbc() {};
      function ErrorNumber() {};
      var $mVarAbc;
      var $mErrorNumber;
      var $mrName;
   }


方法參數(Method Argument)命名

解釋

範例

   class NameOneTwo
   {
      function StartYourEngines(&$someEngine, &$anotherEngine) {
        $this->mSomeEngine = $someEngine;
        $this->mAnotherEngine = $anotherEngine;
      }

      var $mSomeEngine;
      var $mAnotherEngine;
   }


變數(Variable)命名

解釋

範例

function HandleError($errorNumber)
{
    $error = new OsError;
    $time_of_error = $error->GetTimeOfError();
    $error_processor = $error->GetErrorProcessor();
}


陣列元件(Array Element)

陣列元件名稱依循變數的規則

解釋

範例

$myarr['foo_bar'] = 'Hello';
print "$myarr[foo_bar] world"; // 將會輸出: Hello world
$myarr['foo-bar'] = 'Hello';
print "$myarr[foo-bar] world"; // 警告訊息

單引號或雙引號

解釋

範例

$myarr['foo_bar'] = 'Hello';
$element_name = 'foo_bar';
print "$myarr[foo_bar] world"; // 將會輸出: Hello world
print "$myarr[$element_name] world"; // 將會輸出: Hello world
print "$myarr['$element_name'] world"; // 語法錯誤
print "$myarr["$element_name"] world"; // 語法錯誤


參照變數(Reference Variables)與函數傳回參照( Functions Returning References)

解釋

範例

class Test
{
    var $mrStatus;
    function DoSomething(&$rStatus) {};
    function &rStatus() {};
}


全域變數(Global Variables)

解釋

範例

    global $gLog;
global &$grLog;


定義(Define)命名與全域常數( Global Constants)

解釋

全域常數一直以來都是用這個方式命名,您必須要小心避免與系統預設常數衝突

範例


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

靜態變數

解釋

範例

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


函數(Function)命名

解釋

範例

function some_bloody_function()
{
}


錯誤返回檢測規則


大括弧 {} 規則

在三種主要的大括弧配置策略中,有兩種是比較被接受的,而下面的第一種通常是比較多人採用的:

解釋


縮排/跳位/空格 規則

解釋

範例

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


小括弧()的關鍵字與函數規則

解釋

範例

    if (condition)
    {
    }

    while (condition)
    {
    }

    strcmp($s, $s1);

    return 1;


別在物件架構期做實際的工作

別在物件的建構子中執行實際的工作,建構子應該只有拿來組態變數且/或執行不能夠發生錯誤的工作

例如在完成組態的物件中建立一個方法 Open() 時,Open() 應該在物件完成組態後才去呼叫它

解釋

範例

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

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


讓函數重複執行

函數不應該保留靜態變數,以避免函數被重覆執行


If Then Else 格式

配置

這是任由程式設計人員決定的,不同的括弧配置看起來只會有些許的差異;通常會像這樣:
   if (condition)                 // Comment
   {
   }
   else if (condition)            // Comment
   {
   }
   else                           // Comment
   {
   }
如果您的程式碼有else if ,加個 else 的區塊來找到沒有處理過的情況,或許可以在此放個記錄,即使它不需要任何矯正的動作

條件格式

盡可能將常數放在運算子左邊,例如:

if ( 6 == $errorNum ) ...

其中一個理由是當您少了一個等號時系統會產生錯誤訊息;第二個理由是當您希望尋找變數中放了什麼數值時,您可以在開始的地方找到,而不是在結束的地方。這需要一點時間適應,但是這真的很有幫助!


switch 格式

範例

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

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

      default:
   }


continue,break 與?:的使用時機

Continue 與 Break

Continue 與 break 就像是 gotos 一樣,所以放在這邊

Continue 與 break 跟 goto 一樣盡可能不要去用它

使用 continue 有兩個比較主要的問題:

下面這個例子中兩個問題都會發生:

while (TRUE)
{
   ...
   // A lot of code
   ...
   if (/* some condition */) {
      continue;
   }
   ...
   // A lot of code
   ...
   if ( $i++ > STOP_VALUE) break;
}
注意:要讓程式設計人員無法輕易的找出問題 ,"很多的程式碼"是必要的

在上面的例子中,我們可以說:在同一個迴圈中同時使用 continue 與 break 必然會是個災難

?:

這會發生問題的原因在於,人們總是喜歡在?: 之間塞進大量程式碼;下面有幾個清楚的規則可以遵循:

範例

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

   or

   (condition)
      ? long statement
      : another long statement;


定義區塊的對齊

解釋

範例

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

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


一行一個描述

即使描述之間是相當的接近,還是應該將它分成一行一個描述


短方法

解釋


放一個空值( Null )的描述

即使迴圈不需要其他描述,在 for 或 while 的描述中記得放上一個空值( Null )的內容,這樣才能夠清楚的知道程式是刻意這樣子做而不是忘記輸入
   while ($dest++ = $src++)
      ;         // VOID


如果測試值不為零則不要做為 if 的預設值

不要將不為零的測試值當預設值,例如:
   if (FAIL != f())
會比下面這樣好:
   if (f())
即使 FAIL 也等於數值零,會讓PHP判斷為錯誤( false )。如果其他人決定要將錯誤的回傳值定義為 -1 而不是 0 時,一個明確的測試值會很有幫助。又即使比較的數值一定不會改變,明確的比較運算還是必要的!例如 if (!($bufsize % strlen($str))) 應該改寫為 if (0 == ($bufsize % strlen($str))) 來反應測試值的自然數(不是布林數)。經常發生一個錯誤是使用 strcmp 來測試字串是否相等,結果就永遠不會是預設值。

不為零的測試值經常被當做 if 的預設值,而這樣子其他函數或程式就會受到下面這樣的限制:


自相矛盾的布林類型

不要用1 (TRUE, YES, etc.)來檢查布林數值,將它改為不等於0 (FALSE, NO, etc.)。因為大部分的函數保證會在 false 的情況時傳回 0 ,但是true 的狀況會傳回任何不為零的數值,所以...

   if (TRUE == func()) { ...
應該要改寫為
   if (FALSE != func()) { ...


避免嵌入式工作

嵌入式工作可以節省時間與空間,但是某些人認為這樣子除了讓程式碼變的龐大且難以閱讀外沒有更好的優點。

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

++ 與 -- 兩個運算子是使用指派描述來完成運算,所以在很多情況下執行函式會有些副作用;使用嵌入式指派描述也許會增加執行效能,但是你必須同時考慮加速與不容易維護兩種結果,例如:

   $a = $b + $c;
   $d = $a + $r;
不應該寫成
   $d = ($a = $b + $c) + $r;
即使後者可以節省一個週期,在長時間的執行中,加速器的成熟會讓兩者的差異會越來越小;不過後者在維護上會較困難


重複使用您以及其他人努力的成果

如果沒有適當的基礎架構,在不同的專案之間重複使用程式碼幾乎是不可能的;而物件通常也只符合所需的服務,不同的專案有著不同的服務環境,使得重複使用物件相當困難。

發展一個基礎架構可以讓設計的工作輕鬆許多,不管怎麼樣,在還沒開始之前,下面有幾個技巧可以用來鼓勵重複使用程式碼:

別擔心函式庫太小

重複使用程式碼最大的敵人就是人們不習慣將函式庫脫離他的程式碼,一個可以重複使用的物件也許藏在一個程式的資料夾中而且不易與人分享,因為程式設計人員不會將物件分解或是將物件放入函式庫中

其中一個理由是人們不喜歡製作小的函式庫,總覺得這樣怪怪的;別鬧了,電腦不會在乎你使用了多少函式庫

如果你有可以重複使用的程式碼而且無法放在既有的函式庫,麻煩就建立一個新的吧!如果人們真的去思考重複使用的好處,這個函式庫不會一直這麼小的!

如果你擔心在函式庫異動或新增的時候必須去更新 makefiles ,那就不要在 makefiles 中引入(include)函式庫,試著去引入想法或是服務 。 基礎層級的 makefiles 定義了服務是由一堆函式庫所組成,更高層級的 makefiles 指定它們需要的服務,當一個服務的函式庫有了異動,你只需要去修改較低層級的 makefiles 。

維持一個資料庫

大部分的公司並不知道他們到底有哪些程式碼,而且大部分的程式設計人員仍然不會告訴其他人他完成了什麼或是詢問其他人什麼已經做好了。而解決的方式就是去維護一個程式資源的資料庫。

在理想的狀況下,程式設計人員可以從網頁瀏覽或搜尋包裝好的函式庫清單,並且取得他們需要的部份。如果你能建立這樣一個讓程式設計人員自願維護的系統,那就太棒了;甚至如果能夠記錄重複使用的狀況更好。

另外一個方法就是自動從程式碼中產生一個資料庫,這是基於使用通用的物件、函數、函式庫與子系統標頭,這樣子同時可以完成說明頁面與資料庫。


註解中的註解

註解應該像是說個故事

讓你的註解像是描述這個系統的一個故事,預期你的註解會被自動程式從程式碼中取出然後格式化成說明頁面;而物件的註解就是這個故事的一部分,函式註解、函式參數與函式的配置則是故事的另外一個部份。所有的內容應該編織在一起,並且在另一個時間點告訴某人你做了些什麼以及為什麼這麼做

寫下為什麼這麼做

註解應該說明你的決定,在任何一個你必須做出決定的地方留下註解說明你做了什麼決定以及為什麼,考古學家會從裡面找到最有用的資訊

使用標頭( Headers)

使用像是 ccdoc 的文件抽離系統,文件中的其他部分說明如何使用 ccdoc 來完成一個物件與函式的文件。

這些標頭使用同樣的規則結構化,這樣一來它們可以被分析與分離。他們不像是一般的標頭,所以花點時間去完成填寫。如果你一次作對,你就不需要另外製作文件了!

註解配置

專案的每個部份都該有個特別的註解配置方式

明確的抓住重點

明確的對以下事情加以註解:在正常的控制流程之外所發生的變數變化; 在維護時很可能會被弄壞的程式碼。使用內嵌關鍵字來指出程式要點與潛在問題。 想像有一個自動程式,它會剖析您的註解以尋找關鍵字,然後取出關鍵字所指的 內容,再製作出一份報告,好讓大家在需要解決的地方予以特別處理。

搜尋關鍵字﹝Gotcha Keywords﹞

搜尋格式﹝Gotcha Formatting﹞

範例

   // :TODO: 年月日 960810: 執行效率問題
   // 其實我們應該在這裡使用雜湊表,但是現在先使用線性搜尋。

   // :KLUDGE: 年月日 960810: 不安全的強制轉型
   // 這裡我們需要作一個強制轉型,以回復原本的衍生型別。或許我們應該
   // 使用一個 virtual method、或是使用 template。
   

相關資訊

請參考介面文件與實作文件來了解文件格式的細節。


介面文件與實作文件

文件主要是為兩種人製作的: 只要在撰寫程式時事先考慮到文件的製作,我們就可以從原始程式碼中直接 取出上述的兩種文件。

Class 使用者

Class 使用者需要類別介面的資訊,如果標頭檔的結構正確,這些介面資訊可以直接 從標頭檔中取得。當您在檔案表頭的註解區塊中,為某個 class 撰寫註解時,要記得 使用該 class 的程式設計師只需要知道如何去使用該 class。不要在註解中深入討論 實作上的演算法細節,除非這是該 class 的使用者應該要知道的。把標頭檔中的註解 當作是以後要加入說明書的一部份。

Class 實作者

Class 實作者需要知道該 class 如何實作的深入細節。這些資訊可以在在原始碼檔案中 的注解找到。不要擔心介面的問題。原始碼檔案的表頭註解區塊中,應該涵蓋演算法議題, 與其他設計上的決定。在 method 實作的註解區塊裡,應該有更詳細的討論。


目錄文件

每個目錄裡面都應該一個 README 檔案,其內容包括: 假設有某個專案,每位開發成員都離開了,六個月後,有新人進來。這位孤獨害怕的 探索者,就可以藉著遍歷原始碼的目錄樹,閱讀每個 README 檔案、Makefiles、與 原始碼檔案的表頭,來拼湊出該專案的面貌。


開放/封閉原則 ﹝Open/Closed Principle﹞

Software entities should be open for extension, but closed for modification. (Bertrand Meyer)

開放/封閉原則的意思是,一個 class 在擴充方面應該是開放的,而在修改方面是封閉的: 開放/封閉原則所高唱的是穩定性。擴充一個系統的方法,應該是加入新的程式碼, 而不是對已經可以使用的程式碼加以修改。程式設計師通常不是很喜歡去修改舊的 程式碼,因為它們沒有問題!這個原則只是給您一個學術上的依據,讓您可以安心 :-)

在實際上,開放/封閉原則就是要我們好好利用抽象化﹝abstraction﹞與多型 ﹝polymorphism﹞這兩個老朋友。使用抽象化來釐出通用的過程與想法。 使用繼承來建立出衍生類別必須遵守的程式介面。


伺服器設定

本節包含某些 PHP/Apache 組態的指導原則。


HTTP_*_VARS

HTTP_*_VARS可能是開啟或關閉的。當開啟時,所有變數都必須以$HTTP_*_VARS[key] 的方式來存取。當關閉時,所有變數則可以直接使用 key 的名稱來存取。

解釋


PHP檔案副檔名

PHP檔案有許多不同的副檔名﹝.html,.php,.php3,.php4,.phtml, .inc,.class...﹞。

解釋


其他

本節要說明哪些是可以做的、哪些是不能做的。


使用 if (0) 來註解程式碼區塊

有時候,您需要將很大的程式碼區塊註解起來,以便測試。最好用的方法 是使用 if(0) 區塊:
   function example()
   {
      一堆程式碼

      if (0) {
      許多程式碼
      }

      更多程式碼
    }

您不能使用 /**/ 的方式來註解一大塊程式碼,因為註解裡面不能再包含 註解,而且很肯定的,一大塊程式碼裡面一定會包含有註解,不是嗎?


不同的存取函式風格

實作存取函式

存取函式的建立有兩種主要風格。

Get/Set

   class X
   {
      function GetAge()        { return $this->mAge; }
      function SetAge($age)    { $this->mAge = $age; }
      var $mAge;
   };
個人並不欣賞Get/Set風格。它會讓程式碼裡面充斥了一堆 Get 與 Set。

但是它有一個好處,在處理網路訊息時,我們可以在 Set 函式裡面將本機的 位元組順序﹝byte order﹞轉換成網路的位元組順序。

屬性物件﹝Attributes as Objects﹞

   class X
   {
      function         Age()          { return $this->mAge; }
      function         Name()         { return $this->mName; }

      var              $mAge;
      var              $mName;
   }

   $x = new X;

   // 範例 1
   $age = $x->Age();
   $r_age = &$x->Age(); // 屬性的參考﹝Reference﹞

   // 範例 2
   $name = $x->Name();
   $r_name = &$x->Name(); // 屬性的參考﹝Reference﹞

從函式名稱來看,屬性物件的函式名稱很簡潔。請儘可能使用這種方式來存取屬性。

階層

階層式設計是減低系統複雜度的主要技術。一個系統應該被分割成數個階層。 某個階層可以使用定義良好的介面來與鄰近階層進行溝通。如果一個階層可以 接觸到非鄰近階層時,就違背了階層設計的原則,稱之為「階層違背」。

「階層違背」的發生,就表示有兩個階層之間的互依關係,不是經由良好定義的介面來產生的。 當其中一個階層有所改變時,程式碼可能會無法運作。我們不希望程式碼無法正常運作,所以 要求每個階層只能與鄰近階層進行溝通。

有時候,為了改善執行效率,我們需要跳過幾個階層。這沒有問題,但是我們 應該要知道自己正在違背階層原則,並應該在文件中加以說明。


程式碼檢討﹝Code Review﹞

如果您能夠對程式碼進行一次正式的檢討會議,我向您脫帽致敬。程式碼檢討是非常有幫助的。 不幸的是,它們總是會轉變成吹毛求疵的會議,無止境的在愚蠢的事情上互相爭論。 它們也總是會佔據許多人的時間,而無法產生預期的效果。

天阿,這個人在質疑程式碼檢討的必要性,他一定不是工程師!

也不盡然,我所要質疑的,只是程式碼檢討的會議形式,以及在延期又混亂 的專案中進行程式碼檢討的效果。

首先,程式碼檢討只能在專案後期舉行,它來得太遲了,無法產生任何幫助。 真正需要檢討的,是需求規格與設計規格。這才是您可以獲得更大成效的地方。

將相關人員集合在一個房間。把他們鎖在裡面。完整的審視程式介面設計,以及需求 規格,直到前者的設計令人滿意,後者的需求也都達成。將所有相關人員集中在一個 房間裡面,可以讓這個過程產生良好的效益,因為問題可以被立即回答,觀點可以被 立即探討。通常,這種會議只要舉行兩三次就很夠了。

如果上述的過程圓滿達成,程式碼撰寫工作就不會有什麼問題。如果您在程式碼檢討 中發現問題,而這個問題是某人花了許多時間與精神不停測試所發現的,您最好回去將 程式碼重新寫過一遍。

您仍然會想要做程式碼檢討,那只要在一旁進行就好了。找幾位您信得過的人, 閱讀可能有問題的程式碼,並將建議直接告訴程式設計師。如此,程式設計師與審查者 就可以討論各種觀點,並將它們實行出來。電子郵件與快速犀利的討論是很好的方式。 這種方式符合檢討的目的,也不會一次用掉六個人的時間。


提早且不是經常建立原始碼控制系統

在專案生命週期中,應該安裝一個共用的建置系統與原始碼控制系統,愈早安裝愈好, 最好是在開始撰寫程式碼之前就安裝完畢。原始碼控制系統是讓專案結構保持完整的 黏著劑。如果程式設計師不能輕易的使用他人的成果,您就永遠無法製造出可以重覆 生產的建置版本,也會浪費大家許多時間。將一個失控的建置環境改建成標準系統, 也是很糟糕的事情。每個專案都有權利去建立它自己的客製環境,這個道理似乎是對的, 但是該客製環境卻總是無法運作得那麼對。

某些要點必須牢記在心:

相關資訊

如果您的錢夠多,許多專案發現 Clear Case 是個好系統。也有許多完全可以運作的系統是建立在 GNU 的 make 與 CVS 上面。CVS 是一個建立在 RCS 上面的 freeware 建置系統。它與 RCS 之間的 主要差異是,它為建置中的軟體提供了一個共享檔案模式。


提早且不是經常建立錯誤追蹤系統

愈早習慣使用臭蟲追蹤系統愈好。如果您在專案進度3/4的時候才安裝臭蟲追蹤系統, 沒有人會去使用它。您必須早一點安裝臭蟲追蹤系統,讓大家去使用。

程式設計師通常很不喜歡去追蹤臭蟲,然而一旦使用正確,它會對專案有很大的幫助:

以上各點,雖然不是什麼迷人的事情,但是它們讓專案有良好紮實的進步。

原始程式碼的版本控制應該連結到臭蟲追蹤系統上面來。當專案進行到釋出軟體前的 程式碼凍結階段時,版本控制系統應該只接受帶有合法臭蟲ID的存入動作﹝checkin﹞。 當程式碼的臭蟲被修正完畢後,該臭蟲的ID也應該包含在checkin的注解中。


遵守責任區分

軟體模組的責任是有所區分的。模組的維護可能是某人的責任,或是大家共同的責任。 請遵守這樣的責任區分。不要去修改不屬於您負責的模組。否則,只會引起 錯誤與不愉快的經驗。

請面對這樣的事實,如果某段程式碼不屬於您維護,就請放棄自己去修改它的念頭。 程式碼的來龍去脈是很複雜的,您認為相當合理的事情,可能到頭來完全是錯誤的。 如果您需要修改某個地方,只要請負責該程式碼的人去修改就好了。或是在您進行 修改之前,先詢問他們是否可以做這樣那樣的改變。如果他們說好,那就可以去改, 否則就請不要動您的編輯器。

每個規則都有例外的情形。如果在清晨三點鐘,您需要做一個修改,以釋出一個 可以使用的版本,那麼您就必須改下去。如果負責人正在休假,而且該模組沒有 指定代理人,您也必須改下去。當您修改別人所撰寫的程式碼時,請試著使用他們 所採用的程式碼風格。

某些特別敏感的程式碼在修改之後會對其他地方造成影響,負責的程式設計師必須在這些程式碼 中加入註解。如果修改某個地方的程式碼,也必須對另一地方的程式碼進行修改,那就 要在註解中加以說明。如果資料格式的改變會與儲存的資料產生衝突、或是與送至遠端電腦的訊息 不一致,也要在註解中加以說明。如果您試著減少記憶體的使用量,或是想達成某種目標, 也請在註解中說明。不會有人聰明到可以猜出您心中的想法。

最不可原諒的是,把整個系統的程式碼全部改過一遍,以符合您個人的程式風格。 請保持禮貌。如果某人沒有按照標準來撰寫程式碼,就請他們修正,或是 請您的經理來要求他們。

如果模組是由大家一起維護的,就要特別小心。堅持不做劇烈的修改,以免產生難以解決的 版本衝突。在程式檔中加入註解,說明這個程式檔的擴增方法,好讓每個人都遵循這個規則。 試著在共用的程式檔中使用相同的程式結構,免得其他人必須摸索著去尋找所要的 程式碼,找到以後也不知道如何去修改它。每次修改完程式碼,立即將新版程式碼 儲存進版本控制軟體中,以避免版本衝突的發生。

另外一提,追蹤臭蟲也是一種必須指定的模組責任。


PHP程式碼標籤

PHP 標籤﹝Tags﹞是用來標示出夾雜在 html 中的 PHP 程式碼。有許多標示方法可以使用。 , <% %>,以及。某些方法可能在您的 PHP 設定 中被關掉了。

解釋

範例

 // 會列印出 "Hello world"

 // 會列印出 "Hello world"

 // 會列印出 "Hello world"

<% print "Hello world"; %> // 會列印出 "Hello world"

 // 會列印出 $street 這個變數的值

避免使用不可思議的數字

不可思議的數字﹝magic number﹞,就是光溜溜的出現在原始程式碼中的數字。 它們之所以不可思議,是因為沒有人知道這些數字的意思是什麼,甚至連原始作者 也會在三個月後完全忘記它們的意思。例如:

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 的意思是什麼?您又怎麼能夠知道,這些數字曾經被修改過, 或是這些數字根本就是錯誤的?

頻繁的使用不可思議的數字,表示該位程式設計師只不過是業餘的。這樣的程式設計師 可能從未在開發團隊的環境中工作過,否則,如果他們需要去維護程式碼,那他們鐵定不會 這樣做。

請絕對不要在程式碼中寫下不可思議的數字,改用有意義的名稱來代表它們。您應該使用 define()。例如:

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(); }

這樣不是好多了嗎?


輕巧相對於龐大的物件介面

一個物件應該有多少method?正確的答案當然是剛剛好,我們稱之為「歌蒂拉克程度」 ﹝Goldilocks level,程度適中的意思﹞。但是「歌蒂拉克程度」是什麼樣子?答案是, 它不存在。您必須依據實際情形來拿捏method的數量,這就是為什麼我們需要程式設計師 :-)

兩種極端的設計是,輕巧類別﹝thin classes﹞與龐大類別﹝thick classes﹞。輕巧類別 是最低限度的類別,它所擁有的method愈少愈好。一般來說,使用者會繼承輕巧類別來衍生 出他們自己的類別,並在衍生類別中加入所需要的method。

輕巧類別或許看起來很「乾淨」,但是實際上並非如此。輕巧類別沒有多大的用途,它的 主要目的只是為了建立起一個型別。正因為輕巧類別沒什麼實際功用,所以在同一個專案 中的每位程式設計師,都會衍生出他們自己的類別,而且他們在衍生類別中所加入的method, 基本上都是一樣的。這就造成了程式碼重複開發的問題,而違背了當初採用物件的方法來 開發程式的目的─讓同一份程式碼可以重覆使用。解決這個問題的方法,就是把新增的method 往上放到基底類別中。如果往上放的method很多,基底類別就會變成龐大類別。

龐大類別有很多method。您想在它裡面加入多少method都可以。這會產生什麼問題嗎?也許不會。 如果要加入的 method 與該類別有直接的關連,那把它們放在該類別裡面就不會有什麼問題。 真正的問題是,有些人會開始懶惰,把有些應該放到別的類別的method,即使與該類別只有一點點關連, 也一併新增到該類別裡面。在這裡,我們需要再次做出正確的判斷。

龐大類別還有其他的問題。愈龐大的類別,愈不容易了解,程式碼之間的互動更不可預期, 因此也更難除錯。而且,如果有一個您從不使用、也從不關心的method被修改了,您還是得 整個類別重新測試,再重新釋出這個類別。


Recent Changes

  1. 2004-07-18. Traditional Chinese version done by simula and kiang
  2. 2003-02-17. Modified indent rule.
  3. 2002-03-04. Some changes in PHP File Extensions section. Only .php extensions is now recommended.
  4. 2002-01-23. I've added Array Element.
  5. 2001-08-13. The Variable Names example is now compatible with this PHP standard. Added word version of this document submitted by Chris Hubbard.
  6. 2001-01-23. Method Argument Names example code fix. Parts of Different Accessor Styles has been deprecated because there was no support in PHP for these.
  7. 2000-12-12. HTTP_*_VARS added
  8. 2000-12-11. Indentation/Tabs/Space Policy has been changed
    PHP Code Tags added
  9. 2000-12-05. Method Argument Names has been updated
*由於原始英文網站暫時無法連結,所以文件中有提到原始網站的部分均隱藏,如果有興趣可以透過檢視原始碼取得!