Changes between Initial Version and Version 1 of EC-CUBE標準規約/リファクタリングガイドライン


Ignore:
Timestamp:
2011/03/23 16:33:28 (13 years ago)
Author:
nanasess
Comment:

wiki:リファクタリングガイドライン から移動

Legend:

Unmodified
Added
Removed
Modified
  • EC-CUBE標準規約/リファクタリングガイドライン

    v1 v1  
     1= リファクタリングガイドライン = 
     2 
     3== init 関数 == 
     4init 関数は, クラスの初期化を目的する. 
     5ビジネスロジックの記述はしないこと 
     6 
     7{{{ 
     8#!php 
     9<?php 
     10function init() { 
     11 
     12    /** 
     13     * NG ビジネスロジックの記述はしない 
     14     */ 
     15    if (count($this->arrPayment) > 0) { 
     16        $i = 0; 
     17        foreach ($this->arrPayment as $val) { 
     18            $this->payment[$i] = $val; 
     19            $i++; 
     20        } 
     21    } 
     22 
     23    /** 
     24     * OK クラスの初期化のみ行う 
     25     */ 
     26    $this->tpl_mainpage = 'index.tpl'; 
     27    $this->arrDISP = $masterData->getMasterData('mtb_disp'); 
     28} 
     29?> 
     30}}} 
     31 
     32== action 関数 == 
     33 
     34action 関数は MVC のコントローラにあたり以下の処理を記述する. 
     35 
     36 * 条件分岐 
     37 * VIEW からの入力 
     38 * VIEW に渡すメンバ変数($this->variable_name)への代入 
     39 * 宣言 
     40 * ビジネスロジックの呼び出し 
     41 
     42action 関数に, ビジネスロジックを直接記述したり, SQL を実行する処理を記述しないこと. 
     43 
     44{{{ 
     45#!php 
     46<?php 
     47/** 
     48 * action 関数のサンプル 
     49 */ 
     50function action() { 
     51 
     52    /** OK VIEW からの入力を処理する */ 
     53    $this->arrForm = $this->lfConvertParam($_POST); 
     54 
     55    /** OK 入力に応じて条件分岐する */ 
     56    switch ($this->getMode()) { 
     57    case 'edit': 
     58 
     59        /** OK エラーの内容に応じて条件分岐する */ 
     60        $this->arrErr = $this->lfProductClassError($this->arrForm); 
     61        if (empty($this->arrErr)){ 
     62            $this->tpl_mainpage = 'products/product_class_confirm.tpl'; 
     63            $this->lfProductConfirmPage($this->arrForm); 
     64        } else { 
     65            $this->doPreEdit($this->arrForm, false ,true); 
     66        } 
     67        break; 
     68 
     69    case 'delete': 
     70        $this->doDelete($this->arrForm); 
     71        break; 
     72 
     73    case 'disp': 
     74        /** NG ビジネスロジックの記述をしてはならない */ 
     75        foreach ($this->arrForm as $key => $val) { 
     76            $foo[$key] = $val; 
     77        } 
     78 
     79        /** NG SQLを実行する処理を記述してはならない */ 
     80        $objQuery =& SC_Query::getSingletonInstance(); 
     81        $arrResults = $objQuery->select('*', 'table_name'); 
     82 
     83        $this->doDisp($this->arrForm); 
     84        break; 
     85 
     86    case 'complete': 
     87        $this->registerProductClass($this->arrForm, $this->arrForm['product_id']); 
     88        SC_Response_Ex::sendRedirect('complete.php'); 
     89        break; 
     90 
     91    default: 
     92    } 
     93} 
     94?> 
     95}}} 
     96 
     97== MODE パラメータ == 
     98 
     99`$_POST['mode']` や `$_GET['mode']` に対する条件分岐は, LC_Page::getMode() を使用し, switch 文で記述する. 
     100 
     101{{{ 
     102#!php 
     103<?php 
     104/** OK switch 文で mode の分岐を行う */ 
     105switch ($this->getMode()) { 
     106case 'disp': 
     107    $this->doDisp($this->arrForm); 
     108    break; 
     109 
     110case 'complete': 
     111    SC_Response_Ex::sendRedirect('complete.php'); 
     112    break; 
     113default: 
     114} 
     115 
     116/** NG mode の条件分岐に if 文を使用してはならない */ 
     117if ($_POST['mode'] == 'disp') { 
     118    $this->doDisp($this->arrForm); 
     119} elseif ($_POST['mode'] == 'complete') { 
     120    SC_Response_Ex::sendRedirect('complete.php'); 
     121} 
     122?> 
     123}}} 
     124 
     125== ビジネスロジック == 
     126 
     127  * ページ間で重複する処理が存在する場合は, Helper などの共通クラスへ記述する 
     128  * ページ固有の処理は, ローカル関数で記述する 
     129  * 可能であれば, PHPUnit を使用してテストケースを残すこと 
     130  * 宣言を除き, 引数や返り値が無く, すべて内部のメンバ変数で処理するような関数は極力作成しない 
     131    * ステートレスな処理を心掛けること 
     132 
     133{{{ 
     134#!php 
     135<?php 
     136 
     137/** 
     138 * NG 引数ではなく, クラスのメンバ変数を使用して振舞いを変更する 
     139 */ 
     140function lfSendMail() { 
     141     $objPurchase = new SC_Purchase_Ex(); 
     142     $objPurchase->sendOrderMail($this->order_id) 
     143} 
     144 
     145/** 
     146 * OK 引数で振舞いを変更できる 
     147 */ 
     148function lfSendMail($order_id) { 
     149     $objPurchase = new SC_Purchase_Ex(); 
     150     $objPurchase->sendOrderMail($order_id) 
     151} 
     152 
     153/** 
     154 * NG 引数や返り値が無く, メンバ変数の値を直接変更している 
     155 */ 
     156function lfConvertLoginPass(){ 
     157    if(strlen($this->arrForm['login_pass']) < 1 ) { 
     158        return; 
     159    } 
     160    $this->arrForm['login_pass'] = trim($this->arrForm['login_pass']); 
     161    $this->arrForm['login_pass1'] = $this->arrForm['login_pass']; 
     162    $this->arrForm['login_pass2'] = $this->arrForm['login_pass']; 
     163} 
     164 
     165/** 
     166 * OK ローカル関数内では, ローカル変数を使用することで, 拡張性, 保守性が向上し, ユニットテストも書きやすい 
     167 */ 
     168 
     169$this->arrForm = $this->lfConvertLoginPass($this->arrForm); 
     170 
     171function lfConvertLoginPass(&$arrForm){ 
     172    if(strlen($arrForm['login_pass']) < 1 ) { 
     173        return; 
     174    } 
     175    $arrForm['login_pass'] = trim($arrForm['login_pass']); 
     176    $arrForm['login_pass1'] = $arrForm['login_pass']; 
     177    $arrForm['login_pass2'] = $arrForm['login_pass']; 
     178    return $arrForm; 
     179} 
     180 
     181/** 
     182 * ユニットテストの例. 
     183 */ 
     184function testLfConvertLoginPass() { 
     185    $login_pass = 'login_pass_value'; 
     186    $arrForm = array('login_pass' => $login_pass); 
     187 
     188    $expected = array('login_pass' => $login_pass, 
     189                      'login_pass1' => $login_pass, 
     190                      'login_pass2' => $login_pass); 
     191 
     192    $actual = lfConvertLoginPass($arrForm); 
     193 
     194    $this->assertEquals($expected, $actual); 
     195} 
     196?> 
     197}}} 
     198 
     199 * ループ処理などで, メンバ変数や, スーパーグローバル変数は直接使わない 
     200 
     201{{{ 
     202#!php 
     203<?php 
     204 
     205/** 
     206 * NG ループ処理で, メンバ変数やスーパーグローバル変数を直接扱っている 
     207 */ 
     208foreach ($_POST['params'] as $key => $val) { 
     209     $this->arrForm[$key] = $val; 
     210} 
     211 
     212/** 
     213 * OK メンバ変数や, スーパーグローバル変数に作用するループ処理は, 別途関数を作成する 
     214 */ 
     215$this->arrParams = $this->getParams($_POST['params']); 
     216 
     217function getParams($arrParams) { 
     218     $arrResults = array(); 
     219     foreach ($arrParams as $key => $val) { 
     220         $arrResults[$key] = $val; 
     221     } 
     222     return $arrResults; 
     223} 
     224?> 
     225}}} 
     226 
     227== データベースアクセス == 
     228 
     229特に理由の無い場合は `SC_Query::getSingletonInstance()` を使用してインスタンスを取得すること 
     230 
     231{{{ 
     232#!php 
     233<?php 
     234 
     235/** 
     236 * NG PHP4 環境では, 新たなインスタンスが生成されてしまう 
     237 */ 
     238$objQuery = new SC_Query(); 
     239 
     240/** 
     241 * OK シングルトンが保証され, トランザクションの扱いが容易になる 
     242 */ 
     243$objQuery =& SC_Query::getSingletonInstance(); 
     244 
     245?> 
     246}}} 
     247 
     248SQL 文を散乱させない. 
     249SELECT id, name, foo, bar FROM table_name と SELECT id, name, foo, bar, too FROM table_name が存在する場合は, 後者を共通関数にリファクタリングすること 
     250 
     251{{{ 
     252#!php 
     253<?php 
     254 
     255/** 
     256 * NG 類似した SQL を散乱させてはいけない 
     257 */ 
     258$arrResults = $objQuery->select('id, name, foo, bar', 'table_name'); 
     259$arrResult2 = $objQuery->select('id, name, foo, bar, too', 'table_name', 'too = ?', $arrParams['too']); 
     260 
     261/** 
     262 * OK カラム名や, WHERE の違いなどは共通関数で吸収する 
     263 */ 
     264function getResults($arrParams = array()) { 
     265    $where = ''; 
     266    $arrValues = array(); 
     267    if (isset($arrParams['too'])) { 
     268        $where .= 'too = ?'; 
     269        $arrValues[] = $arrParams['too']; 
     270 
     271    } 
     272    $objQuery =& SC_Query::getSingletonInstance(); 
     273    $arrResults = $objQuery->select('*', 'table_name', $where, $arrValues); 
     274    return $arrResults; 
     275} 
     276$arrResults = getResults(); 
     277$arrResults2 = getResults($arrParams); 
     278 
     279?> 
     280}}} 
     281 
     282INSERT/UPDATE/DELETE は, SC_Query::insert(), SC_Query::update(),  SC_Query::delete() を使用し, SC_Query::query() は使用しないこと 
     283 
     284{{{ 
     285#!php 
     286<?php 
     287 
     288/** NG SC_Query::query() は使用しない */ 
     289$objQuery =& SC_Query::getSingletonInstance(); 
     290$objQuery->query('INSERT INTO table_name (col1, col2) VALUES (?, ?)', arrary($col1, $col2)); 
     291 
     292/** OK SC_Query::insert(), SC_Query::update(),  SC_Query::delete() を使用する */ 
     293$objQuery =& SC_Query::getSingletonInstance(); 
     294$objQuery->insert('table_name', array('col1' => $col1, 
     295                                      'col2' => $col2)); 
     296 
     297?> 
     298}}} 
     299== 入力チェック == 
     300 
     301入力チェック用のクラスには, SC_FromParam と SC_CheckError が存在するが, 極力 SC_FormParam を使用すること 
     302 
     303== 端末種別の判別 == 
     304 
     305`Net_UserAgent_Mobile::isMobile()` や `MOBILE_SITE` 定数など存在するが, `SC_Display::detectDevice()` に統一する 
     306 
     307== スーパーグローバル変数 == 
     308 
     309`$_POST` や `$_GET` の値は, 必ず入力チェック後に使用する. `SC_FormParam` を使用していれば, `SC_FormParam::getHashArray()` で取得できる 
     310 
     311{{{ 
     312#!php 
     313<?php 
     314 
     315/** NG $_POST の値を入力チェックせずに使用する */ 
     316$this->register($_POST); 
     317 
     318/** OK 必ず入力チェックを行う */ 
     319$objFormParam = new SC_FormParam(); 
     320$objFormParam->addParam('商品規格ID', 'product_class_id', INT_LEN, 'n', array('EXIST_CHECK', 'MAX_LENGTH_CHECK', 'NUM_CHECK')); 
     321$objFormParam->setParam($_POST); 
     322$objFormParam->convParam(); 
     323$arrErr = $objFormParam->checkError() 
     324if (SC_Utils_Ex::isBlank($arrErr)) { 
     325     // 入力チェック後の値が取得可能 
     326     $arrForm = $objFormParam->getHashArray(); 
     327} 
     328$this->register($arrForm); 
     329 
     330?> 
     331}}} 
     332 
     333`$_SESSION` は, `SC_CartSession クラス`等, セッションを扱うビジネスロジックを通じて使用するのが望ましい. 
     334 
     335どうしても, グローバル変数を使用したい場合は, global キーワードは使用せず, `$GLOBALS` を使用すること. 
     336 
     337{{{ 
     338#!php 
     339<?php 
     340/** 
     341 * NG global キーワードの使用すると, 変数名衝突の原因となる 
     342 */ 
     343function globalSample() { 
     344    global $conn; 
     345 
     346    // 外部からは $conn という変数を使用しているのがわからない 
     347    if (is_null($conn)) { 
     348        $conn =& new ClassName(); 
     349    } 
     350} 
     351 
     352/** 
     353 * OK $GLOBALS を使用すると, ローカル変数との衝突の心配は無い 
     354 */ 
     355function globalSample() { 
     356    if (is_null($GLOBALS['_conn'])) { 
     357        $GLOBALS['_conn'] = new ClassName(); 
     358    } 
     359} 
     360?> 
     361}}} 
     362 
     363== 変数名 == 
     364 
     365変数名で, どんなデータか識別できるよう考慮すること 
     366 
     367{{{ 
     368#!php 
     369<?php 
     370 
     371/** 
     372 * NG 一見して, どんなデータが格納されているかわからない 
     373 */ 
     374$arrRet = getProducts(); 
     375 
     376/** 
     377 * OK 商品データの配列だと, 変数名で判別可能 
     378 */ 
     379$arrProducts = getProducts(); 
     380 
     381?> 
     382}}} 
     383 
     384リテラルを格納する (つまり配列・オブジェクト以外の) 変数名は, すべて小文字を使用し, アンダーバーで区切ること 
     385 
     386{{{ 
     387#!php 
     388<?php 
     389 
     390/** 
     391 * リテラルを格納する変数名は, 連想配列の添字で使用されることも考慮し 
     392 * アンダーバー区切りの方が扱いやすい 
     393 */ 
     394 
     395/** 
     396 * NG 
     397 */ 
     398$productId = getProductId(); 
     399 
     400/** 
     401 * OK 
     402 */ 
     403$product_id = getProductId(); 
     404 
     405?> 
     406}}} 
     407 
     408== 1行の文字数/行数 == 
     409 * 1行の文字数は80文字までにすることを目指すこと. すなわち, コードの長さを現実的な範囲で80文字までにおさめるよう努力すべきです. しかしながら, 場合によっては少々長くなってしまってもかまいません. 
     410 * 1関数の行数は多くても200行程度に留めるのが望ましい. これ以上行数が増えてしまう場合は, 関数を分割する等, コーディングの見直しを行ってください 
     411 
     412== PHPDoc コメント == 
     413 
     414関数の PHPDoc コメントは必ず記述する. 
     415  * 関数の説明 
     416  * 引数 
     417    * 引数の無い関数は省略可能 
     418  * 返り値 
     419    * 返り値が無くても void を明記すること 
     420 
     421== 非推奨機能 == 
     422 
     423error_reporting(E_ALL) で, エラー及び警告が出力されないようコーディングすること. 
     424 
     425PHP5.3 で非推奨となっている関数は使用しないこと. 
     426 
     427  * call_user_method() (かわりに call_user_func() を使用します) 
     428  * call_user_method_array() (かわりに call_user_func_array() を使用します) 
     429  * define_syslog_variables() 
     430  * dl() 
     431  * ereg() (かわりに preg_match() を使用します) 
     432  * ereg_replace() (かわりに preg_replace() を使用します) 
     433  * eregi() (かわりに preg_match() で 'i' 修正子を使用します) 
     434  * eregi_replace() (かわりに preg_replace() で 'i' 修正子を使用します) 
     435  * set_magic_quotes_runtime() およびそのエイリアスである magic_quotes_runtime() 
     436  * session_register() (かわりにスーパーグローバル `$_SESSION` を使用します) 
     437  * session_unregister() (かわりにスーパーグローバル `$_SESSION` を使用します) 
     438  * session_is_registered() (かわりにスーパーグローバル `$_SESSION` を使用します) 
     439  * set_socket_blocking() (かわりに stream_set_blocking() を使用します) 
     440  * split() (かわりに preg_split() を使用します) 
     441  * spliti() (かわりに preg_split() で 'i' 修正子を使用します) 
     442  * sql_regcase() 
     443  * is_dst を mktime() に渡すこと。 かわりにタイムゾーン処理用の新しい関数を使用します。 
     444 
     445以下の機能も, PHP5.3.x で非推奨となっているが, PHP4互換のために使用しても良い 
     446 
     447  * new の返り値を参照で代入すること 
     448  * 呼び出し時の参照渡し 
     449 
     450参考 
     451http://www.php.net/manual/ja/migration53.deprecated.php 
     452 
     453== PHP4 での動作 == 
     454 
     455PHP4.3.11 以降で動作可能な構文にすること 
     456 
     457参考 
     458http://svn.ec-cube.net/open_trac/ticket/854 
     459http://svn.ec-cube.net/open_trac/changeset/20024 
     460