一、說明
程式的開發及維護時,對程式掌握度的高低對開發維護的效率與質量影響很大,但我們又不可能將做過的程式永遠記得牢牢的,總會有其他人的改動,總會接觸其他人編寫的程式,所以個人對程式的閱讀追蹤能力很大一部份代表個人實力,在此介紹一些php web程式的閱讀追蹤方式。
二、程式結構與追蹤
2.1 web使用模型
網頁使用流程可解析成(MVC):
- User輸入網址
- Browser對Controller要求View頁面
- View對Controller發出ajax請求
- Controller透過Model存取Database
- Controller將資料ajax回應給View
- View渲染畫面給User
從流程中可看出,View是web系統的起點跟終點
2.2 字串搜尋方式追蹤
適用:想要找到畫面內容所在程式碼
以 Yii 2 basic 預設頁面為例,假設這個頁面我從未接觸過
我想將「Congratulations!」改成「Congratulations-trace」時,可直接從頁面原始碼中的目標附近找一個不太可能重複的字串,如「class=”jumbotron”」
將字串「class=”jumbotron”」貼到IDE的全專案搜尋中尋找,如字串選得好,找到的可能目標會非常少,很容易確認目標。
修改後查看狀況,完成!
使用本方法,需注意不要選可能是組合字、變數等字串,大略可用tag, id, class, style名稱/值 為搜尋字串
2.3 依流程追蹤
適用:想要找到畫面內容所在程式碼
使用本方法,需熟知framework運作流程及相關函式。
2.4 插行號/檔名/字串方式追蹤
適用:
- 狀況一:當程式錯誤後會沒有任何輸出時
- 狀況二:當想知道有哪些判斷式被執行時
狀況一:當程式錯誤後會沒有任何輸出時
如果要確定在執行哪段程式時發生錯誤,可在可能的錯誤點前插行號。
下例假設資料處理掛了且沒有錯誤輸出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| $opt = [ 'code' => 200, 'message' => 'OK', ];
try { $post = $input->post(); echo __LINE__."\n"; $users = \app\service\Users::getInstance(); $rule = \app\service\Rule::getInstance(); $timeoff = \app\service\Timeoff::getInstance(); $punch = \app\service\Punch::getInstance(); $attend = \app\service\Attendance::getInstance();
echo __LINE__."\n"; $userData = $users->prepare($post);
echo __LINE__."\n"; if (! $rule->validate($userData)) { echo __LINE__."\n"; throw new Exceptioh('Input error', 400); }
$timeoffData = $timeoff->get($userData['id'], $startDate, $endDate); $punchData = $punch->get($userData['id'], $startDate, $endDate);
echo __LINE__."\n"; $attendData = $attend->workStatus($timeoff, $punch, $startDate, $endDate);
echo __LINE__."\n"; $attend->write($attendData); } catch (Exception $e) { $opt = [ 'code' => $e->getCode(), 'message' => $e->getMessage(), ]; }
return $output->json($opt);
|
上例可看出:
- 最後一個輸出行號在資料處理之前
- $rule->validate($userData)驗証有通過,因為沒有看到丟出例外前的那個行號
- 當然如果不嫌麻煩,也可以不用__LINE__行號(魔術常量),而是自己定義輸出內容
狀況二:當想知道有哪些判斷式被執行時
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| $attendances = $attend->get($userID, $startDate, $endDate);
echo __LINE__."\n";
foreach ($attendances as $key => $att) {
echo '$key = '.$key."\n";
echo __LINE__."\n";
try { if ($att <= $workStart) { echo __LINE__."\n"; } elseif ($workStart < $att && $att < $restStart) { echo __LINE__."\n"; } elseif ($restStart <= $att && $att < $restEnd) { echo __LINE__."\n"; } elseif ($restEnd < $att && $att < $workEnd) { echo __LINE__."\n"; } elseif ($workEnd <= $att) { echo __LINE__."\n"; } } catch (Exception $e) { echo __LINE__."\n"; }
echo __LINE__."\n"; }
echo __LINE__."\n";
|
- 從輸出的 行號、定位loop迴圈數、印出迴圈內容 可協助判斷程式碼執行狀況。
- 當然如果不嫌麻煩,也可以不用__LINE__(行號魔術常量),而是自己定義輸出內容
2.5 追蹤呼叫歷程
適用: 想知道本函被呼叫的過程時,可使用函式debug_backtrace()取得呼叫歷程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| class Home extends CI_Controller { public function index() { $debug = debug_backtrace($options = 2, $limit = 0); print_r($debug); exit; } }
|
2.6 查看輸入輸出資料變化
適用:輸入輸出值錯誤 (沒有語法錯誤)
1 2 3 4 5 6 7 8 9
| // 查看參數值 echo '$id = '. $id ."\n"; echo '$date = '. $date ."\n";
// 資料取得函式 $data = $obj->getData($id, $date);
// 查看資料內容 echo '$data = ' . var_export($data, 1) . "\n";
|