Moodle程式編寫準則

任何合作的專案都需要一致性和穩定性才能保持它的強壯。

編寫這些準則的目的是為所有的Moodle代碼提供一個努力的目標。有一些比較老的已經存在的代碼在極少的方面尚未達標,但它們最終將會被修正。所有新的代碼都必須盡可能地遵守這些準則。

一般規定

1.      所有代碼檔應當使用 .php 作為副檔名。

2.      所有範本檔應當使用 .html 作為副檔名。

3.      所有文字檔案應當使用Unix風格的文本格式(多數的文本編輯器都有這個選項)。

4.      所有php標記都必須是“完整標記,譬如<?php ?> ... 而不是如<? ?>“短”標記。

5.      所有已經存在的版權聲明應當被保留。如果有必要,您可以加入自己的。

6.      每個檔都應當包含主config.php文件。

7.      每個檔都應當檢查用戶的身份是否正確,可以使用函數require_login()和isadmin()isteacher()、iscreator()或者isstudent()

8.      所有訪問資料庫的操作都應當使用lib/datalib.php中的函數――這樣可以相容更多的資料庫伺服器。所有的事情都應當是可以用這些函數來完成的,如果遇到必須要寫SQL代碼的情況,請記住這些代碼應當是跨平臺的,且僅僅對您代碼中的特定函數(通常在一個lib.php檔中)有效,且代碼中用注釋進行明確說明。

9.      不要建立和使用標準總體變數$CFG、$SESSION$THEME和$USER之外的總體變數。

10.  所有變數都應當被初始化或者至少在使用前用isset()或empty()等函數進行檢測。

11.  所有字串都應當可翻譯――在"lang/en"目錄中創建新的文字檔案,字串應當使用簡潔的英文小寫名稱,並通過函數get_string()或print_string()來取得。

12.  所有幫助檔應當可翻譯――在"en/help"目錄中創建文字檔案並且通過helpbutton()函數來調用它們。

如果需要上傳一個幫助檔:

o 對於小的修改,舊的翻譯檔仍然可以繼續使用時,可以直接進行修改,但您應當通知translation@moodle.org

o 對於大的修改,應當創建新的檔,新檔的檔案名是在原檔案名的末尾添加一個數字,並在以後逐漸增加(如filename2.html),這樣翻譯人員就可以方便地知道檔有了一個新的版本。很顯然,新的代碼和幫助索引檔都必須修改以指向新版本的檔。

13.  從流覽器發來的資訊(以GETPOST形式發送)都應用了magic_quotes(無論PHP的設置如何),因此直接將它們插入到資料庫中是安全的。所有其他的原始資料(來自檔或資料庫的)都必須在插入資料庫前使用addslashes()進行預處理。

14.  重要的:Moodle中所有的文本,特別是用戶提供的文本,都必須使用format_text()函數來輸出。這樣就可以確保文本已經正確地過濾。

編碼風格

我知道,如果您已經習慣了一種編碼風格而我卻讓您改變它是有一點討厭的,但比較而言,這比日後所有人都需要去搞清混合風格的Moodle代碼要好一些。對於人們使用的任何編碼風格都有很多支援和反對的意見,但現在正在使用的風格已經存在了,因此請堅持下去。

1.      縮進應當是4個連續的空格。絕對不要使用跳位字元。

2.      變數名應當是容易理解、有含義的小寫英文單詞。如果確實需要兩個或以上的單詞,請把它們連在一起,但要保持名稱盡可能短。對於陣列物件,請使用複數名稱。

好的: $quiz
好的: $errorstring
好的: $assignments (用於陣列)
好的: $i (僅用於小型迴圈)

壞的: $Quiz
壞的: $aReallyLongVariableNameWithoutAGoodReason
壞的: $error_string

3.      常量應當總是大寫的,並總是以模組的名稱作為首碼。單詞之間應當用下劃線分隔。

define("FORUM_MODE_FLATOLDEST", 1);

4.      函數名稱應當是簡單的英文小寫單詞,且總是以模組名作為首碼以防止模組之間的衝突。單詞之間以下劃線分隔。變數如果可能應當總有合理的缺省值。注意在函數名和其後的括弧之間沒有空格。

function forum_set_display_mode($mode=0) {
    global
$USER, $CFG;

    if (
$mode) {
        
$USER->mode = $mode;
    } else if (empty(
$USER->mode)) {
        
$USER->mode = $CFG->forum_displaymode;
    }
}

5.      語句塊必須總是使用大括弧(即便是只有一行)。Moodle使用如下風格:

if ($quiz->attempts) {
    if (
$numattempts > $quiz->attempts) {
        
error($strtoomanyattempts, "view.php?id=$cm->id");
    }
}

6.      字串應當盡可能用單引號定義以提高速度。

$var = 'some text without any variables';
$var = "with special characters like a new line n";
$var = 'a very, very long string with a '.$single.' variable in it';
$var = "some $text with $many variables $within it";

7.      實用的注釋應當盡可能填寫,用以解釋代碼流程和函數與變數的功能。

o 每個函數和類都應該使用流行的phpDoc格式編寫,以便自動生成代碼文檔。

o 內嵌注釋應使用 // 風格,並且整齊佈局,使其能融入代碼中並和代碼對齊。

/**
* The description should be first, with asterisks laid out exactly
* like this example. If you want to refer to a another function,
* do it like this: {@link clean_param()}. Then, add descriptions
* for each parameter as follows.
*
* @param int $postid The PHP type is followed by the variable name
* @param array $scale The PHP type is followed by the variable name
* @param array $ratings The PHP type is followed by the variable name
* @return mixed
*/

function forum_get_ratings_mean($postid, $scale, $ratings=NULL) {
    if (!$ratings) {
        
$ratings = array();     // Initialize the empty array
        if (
$rates = get_records("forum_ratings", "post", $postid)) {
   
// Process each rating in turn
   foreach (
$rates as $rate) {
....etc

8.      換行可以被大方地使用——把東西分散開看起來會比較清楚。一般情況下,在花括弧和普通命令之間應當有一個換行符,但在花括弧和變數或函數之間可以沒有換行符:

foreach ($objects as $key => $thing) {
    process($thing);
}

if ($x == $y) {
    $a = $b;
} else if (
$x == $z) {
    $a = $c;
} else {
    $a = $d;
}

資料庫結構

1.      每個表格都必須有一個自增的id欄位(INT10)作為主鍵。

2.      包含著模組中資料實例的主表格必須和模組同名(譬如widget),並且至少包含如下欄位:

o id - 如上一條所述

o course - 每個實例所屬的課程id

o name - 每個實例的完整名稱

3.      與模組相關的其他表格的命名規則是:如果它包含的資訊是關於“things”的,則它的名字應當是widget_things(注意採用複數形式)

4.      欄位名稱應當簡短,與變數名的規則相同。

5.      在可能的情況下,包含著對其他表格(如widget)引用的欄位應當命名為widgetid。(注意這是個新約定,有一些老的表格並未遵守)

6.      布林欄位應當使用小整數類型(如INT4)並存儲為0或1,這樣就可以在需要時擴展它。

7.      多數的表格應當有一個timemodified欄位(INT10),並用PHPtime()函數取得的當前時間戳來更新它。


 

安全問題(處理表單和URL資料)

1.      不要依靠“register_globals”。每個變數必須在每個原始檔案裏正確初始化。變數的來源必須顯而易見

2.      初始化所有的陣列和物件,即使它是空的。$a = array()$obj = new stdClass();

3.      不要使用optional_variable()函數。使用optional_param()函數來替代。根據資料類型選擇正確的PARAM_XXXX值。使用set_default()函數檢查和設置變數的可選值。

4.      不要使用require_variable()函數。使用required_param()來替代。根據資料類型選擇正確的PARAM_XXXX值。

5.      不要使用$_GET$_POST$_REQUEST。根據您的需要使用更合適的required_param()optional_param()

6.      檢查一個動作時,不要使用像if (isset($_GET['something']))這樣的代碼。可以使用諸如:$something = optional_param( 'something','',PARAM_ALPHA ),並且用empty()函數或(如果'empty'也是一個合法值)isset_param()函數測試

7.      在任何可能的情況下,把您所有的required_param()optional_param()和其他的變數初始化一起放在每個文件的開頭,這樣它們會更容易被找到。

8.      使用“sesskey”機制保護表單處理函數不被攻擊。簡單示例:當生成表單的時候,要包含<input type="hidden" name="sesskey" value="<?php echo sesskey(); ?>" />。使用表單時用if (!confirm_sesskey()) {error('Bad Session Key');}檢查。

9.      所有檔案名必須用clean_filename()函數“過濾”,但如果已經適當地使用required_param()optional_param()做了此項工作,則不必如此

10.  再把任何從資料庫讀出的資料寫回之前,必須先用addslashes()處理。一個完整資料物件可以用addslashes_object()一次性完全處理完畢。

11.  在任何可能的情況下,存入資料庫的資料必須來自POST資料(也就是來自表單的資料),而不能來自GET資料(也就是來自URL的資料)。

12.  如果能避免的話,不要使用來自$_SERVER的資料。它會給移植性帶來問題

13.  在其他地方沒有做的情況下,確保所有寫入資料庫的資料已經被clean_param 函數處理,並且針對資料類型,使用了恰當的PARAM_XXXX。

14.  如果您編寫了自定義的SQL代碼,請確保它是正確的。尤其小心在數值周圍不要丟失引號,這可能會帶來SQL“注入”漏洞。

15.  每個檔內都要檢查它使用的所有資料(尤其是寫到資料庫的資料)的合法性。不要期盼或依賴其他地方已經檢查了。

16.  被其他檔包含的代碼塊應當有確切定義的PHP結構(例如類的聲明、函數定義等)——直接執行的代碼塊建議使用變數時不要初始化。

Moodle文檔

Version: $Id: coding.html,v 1.3 2008/07/27 16:28:41 kiang Exp $