O slideshow foi denunciado.
Utilizamos seu perfil e dados de atividades no LinkedIn para personalizar e exibir anúncios mais relevantes. Altere suas preferências de anúncios quando desejar.

PHP 良好實踐 (Best Practice)

5.271 visualizações

Publicada em

https://kylinyu.win/php_best_practice
For PHP Beginner.
在不過度依賴 Framework 或者 Library 工具的情況下,撰寫良好的 PHP 程式, PHP 良好實踐搭配 Clean Code 實戰範例分享
#clean_code #best_practice #PHP #軟體設計

Publicada em: Software
  • Seja o primeiro a comentar

PHP 良好實踐 (Best Practice)

  1. 1. PHP PIXNET 踢克⼤⼩事分享 PIXNET @ Win 17/11/10 kylinyu.win 良好實踐
  2. 2. 前傳
  3. 3. 實戰
  4. 4. 服⽤警告 ✤ 我們不談 Framework 的⼯具 ✤ 程式碼皆為範例⽰意,切勿直接複製套⽤ ✤ Coding Style 為 PIXNET Platform 的風格
  5. 5. Naming 名詞單複數 // 複數名詞 ResultSet / Collection $users = User::search(1); // 單數名詞 Row $article = BlogArticle::find(1);
  6. 6. 變數提煉 foreach ($employees as $employee) { $expectedSalary = $employee->calculateExpectedSalary(); $experience = $employee->getExperience(); $githubLink = $employee->getGithubLink(); $data = [ $expectedSalary, $experience, $githubLink ]; render($data); } ✤ 當有進⼀步運⽤需求時
  7. 7. 變數提煉 foreach ($employees as $employee) { $expectedSalary = $employee->calculateExpectedSalary(); $experience = $employee->getExperience(); $githubLink = $employee->getGithubLink(); $data = [ $expectedSalary, $experience, $githubLink ]; render($data); } foreach ($employees as $employee) { render([ $employee->calculateExpectedSalary() $employee->getExperience(), $employee->getGithubLink(), ]); } ✤ 當有進⼀步運⽤需求時 ✤ 但,只有賦值就不建議提煉
  8. 8. 變數提煉 foreach ($employees as $employee) { render([ $employee->calculateExpectedSalary() $employee->getExperience(), $employee->getGithubLink(), ]); } $bouns = 1.25; foreach ($employees as $employee) { $expectedSalary = $employee->calculateExpectedSalary() * $bouns; render([ $expectedSalary, $employee->getExperience(), $employee->getGithubLink(), ]); } ✤ 當有進⼀步運⽤需求時 ✤ 假設要後處理(額外運算)
  9. 9. 變數提煉 ✤ 特殊語意不易理解的時候 $user = User::find(1); if (!$user->friends->friends_feeds['12345']->friends_comments->count()) { echo 'No comments.' }
  10. 10. 變數提煉 ✤ 特殊語意不易理解的時候 $user = User::find(1); $friends_comments_count = $user->friends->friends_feeds['12345']->friends_comments->count(); if (!$friends_comments_count) { echo 'No comments.' } $user = User::find(1); if (!$user->friends->friends_feeds['12345']->friends_comments->count()) { echo 'No comments.' }
  11. 11. 常數提煉 class CardAuthor { // .... public function checkAdmin() { return (2 === $this->role); } } ✤ 物件的限制特性
  12. 12. 常數提煉 class CardAuthor { // .... public function checkAdmin() { return (2 === $this->role); } } class CardAuthor { // .... const ROLE_GENERAL = 0; // ⼀一般作者 const ROLE_EDITOR = 1; // 內容管理理員 const ROLE_ADMIN = 2; // 者總管理理員 public function checkAdmin() { return (self::ROLE_EDITOR === $this->role); } } ✤ 物件的限制特性 ✤ 跟著類別⾛,語易化更佳
  13. 13. 變/常數提煉 $member = Member::find(1); if (5 < $member->day_logs) { throw new Exception('該名員⼯工超時⼯工作!!'); } ✤ 特殊⽤意的參數都建議提煉 ➡ 能被改就⽤變數 ($variable) ➡ 不能被改就⽤常數 (constant)
  14. 14. $member = Member::find(1); if (5 < $member->day_logs) { throw new Exception('該名員⼯工超時⼯工作!!'); } $workdays_per_week_limit = 5; if ($workdays_per_week_limit < $member->day_logs) { throw new Exception('該名員⼯工超時⼯工作!!'); } 變/常數提煉 ✤ 特殊⽤意的參數都建議提煉 ➡ 能被改就⽤變數 ($variable) ➡ 不能被改就⽤常數 (constant)
  15. 15. 函式 - ⾸則要務 − 簡短 public function checkAlbumStatus() { if ('ok' === $this->album->status) { return true; } return false; }
  16. 16. 函式 - ⾸則要務 − 簡短 public function checkAlbumStatus() { if ('ok' === $this->album->status) { return true; } return false; } public function checkAlbumStatus() { $album_status = $this->album->status; return ('ok' === $album_status) ? true : false; } …
  17. 17. 函式 - ⾸則要務 − 簡短 public function checkAlbumStatus() { $is_ok = ('ok' === $this->album->status); return $is_ok; } …
  18. 18. 函式 - ⾸則要務 − 簡短 public function checkAlbumStatus() { $is_ok = ('ok' === $this->album->status); return $is_ok; } … public function checkAlbumStatus() { return ('ok' === $this->album->status); }
  19. 19. ✤ set 設定某個 property ✤ get 設定某個 property ✤ is/has 通常判斷式 boolean ✤ check 做某種條件檢查 boolean, void ✤ filter 過濾, input/output 不⾒見見得⼀一樣 mixed ✤ validate 驗證器資料是否符合預期 boolean, void, throw exception 函式 - 動詞起⼿式 ✤ can ✤ should
  20. 20. 函式 - 動詞起⼿式 /** * getUser 取得使⽤用者 * * @param int $id * @return UserRow */ function getUser($id) { // implement } /** * setAge 設定使⽤用者年年齡 * * @param UserRow * @return boolean */ function setAge($user) { // implement } /** * isAdmin 判斷是否為管理理者 * * @param UserRow * @return boolean */ function isAdmin($user) { // implement } /** * checkLogin 檢查免費的時效 * * @return void */ function checkFreeExpired() { // implement } set get is check
  21. 21. 函式 - 動詞起⼿式 /** * validateCSRFToken 檢查 CSTF Token * * @thorw Exception * @return void */ function validateCSRFToken() { if (...) { throw new Exception("Don't Hack Me."); } // return true; //(optional) } <?php class UserPreference { const SERVICE_WHITE_LIST = [ 'PIXNET', 'STYLEME', 'PIXWALLET' ]; /** * filterRegisterService 過濾註冊服務類別 * * @param string $service * @return string */ public function filterRegisterService($service) { if (in_array($service, self::SERVICE_WHITE_LIST)) { return $service; } return "kylinyu.win"; } } filter validate
  22. 22. 函式 - 只做⼀件事 - 難以組成 - 難以測試 - 難以理解 當⼀個函式 做超過⼀件事的時候...
  23. 23. 函式 - 只做⼀件事 - 難以組成 - 難以測試 - 難以理解 - 容易測試 - 容易重構 - 結構清晰 當⼀個函式 做超過⼀件事的時候... 當⼀個函式 拆分到只剩⼀個功能時...
  24. 24. 函式 - 只做⼀件事 function createArticle($title, $is_draft = true) { if ($is_draft) { touch('./temp/' . $title); } else { touch('./article/' . $title); } } ✤ 不⽤ Flag 做參數
  25. 25. 函式 - 只做⼀件事 function createArticle($title, $is_draft = true) { if ($is_draft) { touch('./temp/' . $title); } else { touch('./article/' . $title); } } function createArticle($title) { touch('./temp/' . $title); } function createDraftArticle($title) { touch('./article/' . $title); } ✤ 不⽤ Flag 做參數
  26. 26. 能⾒度Visibility class User { public $name; public function __construct($name) { $this->name = $name; } } $user = new User('Win Yu'); echo 'User name: ' . $user->name; // User name: Win Yu ✤ 沒必要對外開放的就不要開 public ✤ 使⽤類別時只要看 public 成員就好
  27. 27. 能⾒度Visibility class User { public $name; public function __construct($name) { $this->name = $name; } } $user = new User('Win Yu'); echo 'User name: ' . $user->name; // User name: Win Yu class User { private $name; public function __construct($name) { $this->name = $name; } public function getName() { return $this->name; } } $user = new User('Win Yu'); echo 'User name: ' . $user->getName(); // User name: John Doe ✤ 沒必要對外開放的就不要開 public ✤ 使⽤類別時只要看 public 成員就好
  28. 28. 封裝條件句 if ('active' === $user->status) { // do something } ✤ 更清楚的語意
  29. 29. 封裝條件句 if ('active' === $user->status) { // do something } if ($user->isActive()) { // do something } class User { // ... public function isActive() { return ('active' === $this->status); } } ✤ 更清楚的語意
  30. 30. Guard Clause 原則 //define('DEBUG_ENV', true); /** * markByENV 根據 env 做標記 * * @return string */ function markByENV() { $user = User::find($user_id); if (DEBUG_ENV) { $user->mark('staging'); $message = "標記 {$user->name} staging"; } else { $user->mark('production'); $message = "標記 {$user->name} production"; } return $message; } ✤ Early return ✤ 減少巢狀 loop
  31. 31. Guard Clause 原則 //define('DEBUG_ENV', true); /** * markByENV 根據 env 做標記 * * @return string */ function markByENV() { $user = User::find($user_id); if (DEBUG_ENV) { $user->mark('staging'); $message = "標記 {$user->name} staging"; } else { $user->mark('production'); $message = "標記 {$user->name} production"; } return $message; } //define('DEBUG_ENV', true); /** * markByENV 根據 env 做標記 * * @return sting */ function markByENV() { $user = User::find($user_id); if (DEBUG_ENV) { $user->mark('staging'); return "標記 {$user->name} staging"; } $user->mark('production'); return "標記 {$user->name} production"; } ✤ Early return ✤ 減少巢狀 loop
  32. 32. Type Hint function enableEpaper($user) { if ($user instanceof UserRow) { // } } ✤ PHP7 可⽤強型別檢查
  33. 33. Type Hint function enableEpaper($user) { if ($user instanceof UserRow) { // } } function enableEpaper(UserRow $user) { // } ✤ PHP7 可⽤強型別檢查
  34. 34. 查表法 /** * getFruitByColor ⽤用顏⾊色取得⽔水果名稱 * * @param string $color * @return string */ function getFruitByColor($color) { if ('紅⾊色' === $color) { return '蘋果'; } elseif ('綠⾊色' === $color) { return '芭樂'; } elseif ('紫⾊色' === $color) { return '葡萄'; } return $color; } ✤ 簡化 if/else 可讀性更⾼
  35. 35. 查表法 /** * getFruitByColor ⽤用顏⾊色取得⽔水果名稱 * * @param string $color * @return string */ function getFruitByColor($color) { if ('紅⾊色' === $color) { return '蘋果'; } elseif ('綠⾊色' === $color) { return '芭樂'; } elseif ('紫⾊色' === $color) { return '葡萄'; } return $color; } ✤ 簡化 if/else 可讀性更⾼ /** * getFruitByColor ⽤用顏⾊色取得⽔水果名稱 * * @param string $color * @return string */ function getFruitByColor($color) { $fruit_map = [ '紅⾊色' => '蘋果', '綠⾊色' => '芭樂', '紫⾊色' => '葡萄', ]; return isset($fruit_map[$color]) ? $fruit_map[$color] : color; }
  36. 36. 查表法 /** * getFruitByColor ⽤用顏⾊色取得⽔水果名稱 * * @param string $color * @return string */ function getFruitByColor($color) { if ('紅⾊色' === $color) { return '蘋果'; } elseif ('綠⾊色' === $color) { return '芭樂'; } elseif ('紫⾊色' === $color) { return '葡萄'; } return $color; } ✤ 簡化 if/else 可讀性更⾼ /** * getFruitByColor ⽤用顏⾊色取得⽔水果名稱 * * @param string $color * @return string */ function getFruitByColor($color) { $fruit_map = [ '紅⾊色' => '蘋果', '綠⾊色' => '芭樂', '紫⾊色' => '葡萄', ]; return isset($fruit_map[$color]) ? $fruit_map[$color] : color; } return $fruit_map[$color] ?? $color; PHP 7
  37. 37. 避免⽤ switch/case ⽽是⽤多型 class Airplane { // ... public function getCruisingAltitude() { switch ($this->type) { case '777': return $this->getMaxAltitude() - $this->getPassengerCount(); case 'Air Force One': return $this->getMaxAltitude(); case 'Cessna': return $this->getMaxAltitude() - $this->getFuelExpenditure(); } } } ✤ ⼀個函式做多件事 ✤ 等同任由後者隨意增加邏輯於 case 中
  38. 38. interface Airplane { // ... public function getCruisingAltitude(); } class Boeing777 implements Airplane { // ... public function getCruisingAltitude() { return $this->getMaxAltitude() - $this->getPassengerCount(); } } class AirForceOne implements Airplane { // ... public function getCruisingAltitude() { return $this->getMaxAltitude(); } } class Cessna implements Airplane { // ... public function getCruisingAltitude() { return $this->getMaxAltitude() - $this->getFuelExpenditure(); } } 避免⽤ switch/case ⽽是⽤多型
  39. 39. Practice and Practice ✤ 反覆練習 ✤ 藉由 Code Review 互相提醒 ✤ 不斷思考,何以更為精練 ✤ 勿矯枉過正,良好的可讀性優先
  40. 40. See more ✤ 無瑕程式碼的彩虹七式 ✤ https://www.slideshare.net/kylinfish/clean-code-72688451 ✤ Clean Code PHP ✤ https://github.com/php-cpm/clean-code-php ✤ PHP The Right Way ✤ https://laravel-china.github.io/php-the-right-way
  41. 41. kylinyu.win Thanks you.

×