代碼指南

出自MoodleDocs
跳到:導覽、​搜尋

此頁面尚未開始翻譯,或者正在翻譯過程中,正需要您來做貢獻。 請點擊左面導航條最末尾的「English」查看原文,開始翻譯。 如果您對翻譯流程和Wiki語法不了解,請閱讀貢獻者指引

政策的延續和穩定是所有合作項目成功的基石。

編寫編碼指南是為了給所有的 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、$SITE、$COURSE和$USER之外的全局變量。
  10. 所有變量都應當被初始化或者至少在使用前用isset()或empty()等函數進行檢測。
  11. 所有字符串都應當可翻譯――在「lang/en_utf8」目錄中創建新的文本文件,字符串應當使用簡潔的英文小寫名稱,並通過函數get_string()或print_string()來取得。為了保證語言包的向後兼容,絕對不要刪除字符串。
  12. 所有幫助文件應當可翻譯――在「lang/en_utf8/help」目錄中創建文本文件並且通過helpbutton()函數來調用它們。如果要更新幫助文件:
    • 對於小的修改,舊的翻譯文件仍然可以繼續使用時,可以直接進行修改,但您應當通知 translation AT moodle DOT org。
    • 對於大的修改,應當創建新的文件,新文件的文件名是在原文件名的末尾添加一個數字,並在以後逐漸增加(如filename2.html),這樣翻譯人員就可以方便地知道文件有了一個新的版本。很顯然,新的代碼和幫助索引文件都必須修改以指向新版本的文件。
  13. 從瀏覽器發來的信息(以GET或POST形式發送)都應用了magic_quotes(無論PHP的設置如何),因此直接將它們插入到數據庫中是安全的。所有其它的原始數據(來自文件或數據庫的)都必須在插入數據庫前使用addslashes()進行預處理。由於在這個問題上人們經常出錯,我們編寫了一個獨立的頁面來介紹如何添加和刪除「\」符號。
  14. 非常重要:Moodle中所有的文本,特別是用戶提供的文本,都必須使用format_text()函數來輸出。這樣就可以確保文本已經正確地過濾。在輸出函數頁面上有關於這個問題的更多信息。.
  15. 用戶的動作應當用add_to_log()函數記錄下來,這些信息會被用於活動報告日誌

編碼風格

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

1. 縮進應當是4個連續的空格。絕對不要使用制表符。

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

     好的: $quiz
     好的: $errorstring
     好的: $assignments (for an array of objects)
     好的: $i (but only in little loops)
     坏的: $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. 實用的注釋應當儘可能填寫,用以解釋代碼流程和函數與變量的功能。

  • 每個函數和類都應該使用流行的phpDoc格式編寫,以便自動生成代碼文檔。
  • 內嵌注釋應使用 // 風格,並且整齊佈局,使其能融入代碼中並和代碼對齊。
     /**
     * 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;
     }

9. 當複製一個對象時,要使用php5的clone()函數(否則得到的就僅僅是指向第一個對象的引用)。Moodle會保證這種方法在php4上也能正常工作。

     坏的:   $b = $a;
     好的:  $b = clone($a);

如果你要複製的並非對象,但包含了對象(如對象數組),那麼要使用fullclone()函數。

數據庫結構

  1. 每個表格都必須有一個自增的id字段(INT10)作為主鍵。(參考IdColumnReasons
  2. 包含着模塊中數據實例的主表格必須和模塊同名(譬如widget),並且至少包含如下字段:
    • id - 如上一條所述
    • course - 每個實例所屬的課程id
    • name - 每個實例的完整名稱
  3. 與模塊相關的其它表格的命名規則是:如果它包含的信息是關於「things」的,則它的名字應當是widget_things(注意採用複數形式)。
  4. 表名和字段名都應當避免使用各種數據庫的保留字。因此在創建前要仔細檢查。
  5. 字段名稱應當簡短並使用小寫,與變量名的規則相同。
  6. 在可能的情況下,包含着對其它表格(如widget)引用的字段應當命名為widgetid。(注意這是個新約定,有一些老的表格並未遵守。)
  7. 布爾字段應當使用小整數類型(如INT4)並存儲為0或1,這樣就可以在需要時擴展它。
  8. 多數的表格應當有一個timemodified字段(INT10),並用PHP的time()函數取得的當前時間戳來更新它。
  9. 給每個字段定義一個缺省值(並保證它是有意義的)。
  10. 每個表名都必須有前綴($CFG->prefix)。在大多數情況下,您必須自己注意此問題。並且在Postgres中,所有的索引名也必須有前綴。
  11. In order to guarantee cross-db compatibility follow these simple rules about the use of the AS keyword (only if you need table/colum aliases, of course):
    • 不要將關鍵字AS用於表格別名
    • 要將關鍵字AS用於字段別名
  12. 不要創建UNIQUE KEY(限制),用UNIQUE INDEX來代替它。 In the future, if we decide to add referential integrity to Moodle and we need UNIQUE KEYs they will be used, but not now. Please note that the XMLDB editor allows you to specify both XMLDB-only UNIQUE and FOREIGN constraints (and that's good, in order to have the XML well defined) but only underlying INDEXes will be generated.
  13. Those XMLDB-only UNIQUE KEYs (read previous point) only must be defined if such field/fields are going to be the target for some (XMLDB-only too) FOREIGN KEY. Else, create them as simple UNIQUE INDEXes.
  14. 某個版塊相關的表格命名時需要遵守以下規則:$CFG->prefix + "block_" + name_of_the_block + anything_else。例如,$CFG->prefix是「mdl_」,則與「rss_client」版塊相關的表格,其名稱前綴應當是「mdl_block_rss_client」(在末尾添加多一些的詞不會帶來任何問題,如「mdl_block_rss_client_anothertable」……)。在Moodle 2.0中,這個規則會100%地強制執行,開發者需在此之前做好準備。要了解詳情,請參考Task 6786
  15. 不要在穩定的部分對數據庫進行修改。如果修改的話,使用者從一個穩定版本升級到另一個會帶來重複的修改。這將會帶來嚴重的錯誤。
  16. 在SQL查詢中查詢整數變量時,不要給變量加引號。例如,get_records_select('question', "category=$catid")是對的。get_records_select('question', "category='$catid'") 是錯的。這會隱藏變量$catid沒有定義的地方的錯誤。

安全問題(處理表單和URL數據)

  1. 不要依靠「register_globals」。每個變量必須在每個源文件里正確初始化。變量的來源必須顯而易見。
  2. 初始化所有的數組和對象,即使它是空的。 $a = array() 或 $obj = new stdClass(); 。
  3. 不要使用optional_variable()函數,使用optional_param()函數來替代它。根據數據類型選擇正確的PARAM_XXXX值。
  4. 不要使用require_variable()函數(現在不推薦使用該函數),使用require_param()函數來替代它。根據數據類型選擇正確的PARAM_XXXX值。
  5. 早使用data_submitted()時要小心,在使用數據前仍要對其進行清理。
  6. 使用$_GET、$_POST或$_REQUEST。根據您的需要使用更合適的required_param()或optional_param()。
  7. 檢查一個動作時,不要使用像if (isset($_GET['something'])) 這樣的代碼。可以使用諸如:$something = optional_param('something',-1,PARAM_ALPHA),並且根據它取值的範圍來判斷,如 if ($something>=0) {....
  8. 在所有可能的情況下,把您所有的required_param()、optional_param()和其它的變量初始化一起放在每個文件的開頭,這樣它們會更容易被找到。
  9. 使用「sesskey」機制保護表單處理函數不被攻擊。簡單示例:當生成表單的時候,要包含 <input type="hidden" name="sesskey" value="><?php echo sesskey(); ?>" />。在處理表單時,使用 if (!confirm_sesskey()) {error('Bad Session Key');} 檢查。
  10. 所有文件名必須用clean_filename()函數「過濾」,但如果已經適當地使用required_param()或optional_param()做了此項工作,則不必如此。
  11. 再把任何從數據庫讀出的數據寫回之前,必須先用addslashes()處理。一個完整數據對象可以用addslashes_object()一次性完全處理完畢。
  12. 在任何可能的情況下,存入數據庫的數據必須來自POST據(也就是來自表單的數據),而不能來自GET數據(也就是來自URL的數據)。
  13. 如果能避免的話,不要使用來自$_SERVER的數據。它會給移植性帶來問題。
  14. 在其它地方沒有做的情況下,確保所有寫入數據庫的數據已經用clean_param函數處理過,並且針對數據類型,使用了恰當的PARAM_XXXX。
  15. 如果您編寫了自定義的SQL代碼,請確保它是正確的。尤其小心在數值周圍不要丟失引號,這可能會帶來SQL「注入」漏洞。
  16. 每個文件內都要檢查它使用的所有數據(尤其是寫到數據庫的數據)的合法性。不要期盼或依賴其它地方已經檢查了。
  17. 被其它文件包含的代碼塊應當有確切定義的PHP結構(例如類的聲明、函數定義等)——直接執行的代碼塊建議使用變量時不要初始化。