├── .gitattributes ├── .gitignore ├── .travis-build.php ├── .travis.yml ├── LICENSE └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | *.md linguist-documentation=false 2 | *.md linguist-language=PHP 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea -------------------------------------------------------------------------------- /.travis-build.php: -------------------------------------------------------------------------------- 1 | setFlags(SplFileObject::DROP_NEW_LINE); 6 | 7 | $cliRedBackground = "\033[37;41m"; 8 | $cliReset = "\033[0m"; 9 | $exitStatus = 0; 10 | 11 | $indentationSteps = 3; 12 | $manIndex = 0; 13 | $linesWithSpaces = []; 14 | $tableOfContentsStarted = null; 15 | $currentTableOfContentsChapters = []; 16 | $chaptersFound = []; 17 | foreach ($readMeFile as $lineNumber => $line) { 18 | if (preg_match('/\s$/', $line)) { 19 | $linesWithSpaces[] = sprintf('%5s: %s', 1 + $lineNumber, $line); 20 | } 21 | if (preg_match('/^(?##+)\s(?.+)/', $line, $matches)) { 22 | if (null === $tableOfContentsStarted) { 23 | $tableOfContentsStarted = true; 24 | continue; 25 | } 26 | $tableOfContentsStarted = false; 27 | 28 | $chaptersFound[] = sprintf('%s [%s](#%s)', 29 | strlen($matches['depth']) === 2 30 | ? sprintf(' %s.', ++$manIndex) 31 | : ' *' 32 | , 33 | $matches['title'], 34 | preg_replace(['/ /', '/[^-\w]+/'], ['-', ''], strtolower($matches['title'])) 35 | ); 36 | } 37 | if ($tableOfContentsStarted === true && isset($line[0])) { 38 | $currentTableOfContentsChapters[] = $line; 39 | } 40 | } 41 | 42 | if (count($linesWithSpaces)) { 43 | fwrite(STDERR, sprintf("${cliRedBackground}The following lines end with a space character:${cliReset}\n%s\n\n", 44 | implode(PHP_EOL, $linesWithSpaces) 45 | )); 46 | $exitStatus = 1; 47 | } 48 | 49 | $currentTableOfContentsChaptersFilename = __DIR__ . '/current-chapters'; 50 | $chaptersFoundFilename = __DIR__ . '/chapters-found'; 51 | 52 | file_put_contents($currentTableOfContentsChaptersFilename, implode(PHP_EOL, $currentTableOfContentsChapters)); 53 | file_put_contents($chaptersFoundFilename, implode(PHP_EOL, $chaptersFound)); 54 | 55 | $tableOfContentsDiff = shell_exec(sprintf('diff --unified %s %s', 56 | escapeshellarg($currentTableOfContentsChaptersFilename), 57 | escapeshellarg($chaptersFoundFilename) 58 | )); 59 | 60 | @ unlink($currentTableOfContentsChaptersFilename); 61 | @ unlink($chaptersFoundFilename); 62 | 63 | if (!empty($tableOfContentsDiff)) { 64 | fwrite(STDERR, sprintf("${cliRedBackground}The table of contents is not aligned:${cliReset}\n%s\n\n", 65 | $tableOfContentsDiff 66 | )); 67 | $exitStatus = 1; 68 | } 69 | 70 | exit($exitStatus); 71 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | sudo: false 4 | 5 | php: 6 | - nightly 7 | 8 | script: php .travis-build.php 9 | 10 | notifications: 11 | email: false 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Ryan McDermott 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Clean Code PHP 2 | 3 | ## မာတိကာ 4 | 5 | 1. [Introduction](#introduction) 6 | 2. [Variables](#variables) 7 | * [Use meaningful and pronounceable variable names](#use-meaningful-and-pronounceable-variable-names) 8 | * [Use the same vocabulary for the same type of variable](#use-the-same-vocabulary-for-the-same-type-of-variable) 9 | * [Use searchable names (part 1)](#use-searchable-names-part-1) 10 | * [Use searchable names (part 2)](#use-searchable-names-part-2) 11 | * [Use explanatory variables](#use-explanatory-variables) 12 | * [Avoid nesting too deeply and return early (part 1)](#avoid-nesting-too-deeply-and-return-early-part-1) 13 | * [Avoid nesting too deeply and return early (part 2)](#avoid-nesting-too-deeply-and-return-early-part-2) 14 | * [Avoid Mental Mapping](#avoid-mental-mapping) 15 | * [Don't add unneeded context](#dont-add-unneeded-context) 16 | * [Use default arguments instead of short circuiting or conditionals](#use-default-arguments-instead-of-short-circuiting-or-conditionals) 17 | 3. [Comparison](#comparison) 18 | * [Use identical comparison](#use-identical-comparison) 19 | 4. [Functions](#functions) 20 | * [Function arguments (2 or fewer ideally)](#function-arguments-2-or-fewer-ideally) 21 | * [Functions should do one thing](#functions-should-do-one-thing) 22 | * [Function names should say what they do](#function-names-should-say-what-they-do) 23 | * [Functions should only be one level of abstraction](#functions-should-only-be-one-level-of-abstraction) 24 | * [Don't use flags as function parameters](#dont-use-flags-as-function-parameters) 25 | * [Avoid Side Effects](#avoid-side-effects) 26 | * [Don't write to global functions](#dont-write-to-global-functions) 27 | * [Don't use a Singleton pattern](#dont-use-a-singleton-pattern) 28 | * [Encapsulate conditionals](#encapsulate-conditionals) 29 | * [Avoid negative conditionals](#avoid-negative-conditionals) 30 | * [Avoid conditionals](#avoid-conditionals) 31 | * [Avoid type-checking (part 1)](#avoid-type-checking-part-1) 32 | * [Avoid type-checking (part 2)](#avoid-type-checking-part-2) 33 | * [Remove dead code](#remove-dead-code) 34 | 5. [Objects and Data Structures](#objects-and-data-structures) 35 | * [Use object encapsulation](#use-object-encapsulation) 36 | * [Make objects have private/protected members](#make-objects-have-privateprotected-members) 37 | 6. [Classes](#classes) 38 | * [Prefer composition over inheritance](#prefer-composition-over-inheritance) 39 | * [Avoid fluent interfaces](#avoid-fluent-interfaces) 40 | * [Prefer `final` classes](#prefer-final-classes) 41 | 7. [SOLID](#solid) 42 | * [Single Responsibility Principle (SRP)](#single-responsibility-principle-srp) 43 | * [Open/Closed Principle (OCP)](#openclosed-principle-ocp) 44 | * [Liskov Substitution Principle (LSP)](#liskov-substitution-principle-lsp) 45 | * [Interface Segregation Principle (ISP)](#interface-segregation-principle-isp) 46 | * [Dependency Inversion Principle (DIP)](#dependency-inversion-principle-dip) 47 | 8. [Don’t repeat yourself (DRY)](#dont-repeat-yourself-dry) 48 | 9. [Translations](#translations) 49 | 50 | ## Introduction 51 | 52 | ဒီစာအုပ်လေးက Robert C. Martin ရဲ့ လက်ရာမြှောက် [*Clean Code*](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882) စာအုပ်ထဲက Software engineering principles တွေကို PHP Language အတွက် သီးသန့် (ဆီလျှော်အောင်) ပြန်လည်မွမ်းမံ စုစည်းပေးထားတာ ဖြစ်ပါတယ်။ ဒါက Coding Style Guideline တစ်ခုတော့ မဟုတ်ပါဘူး။ PHP program တွေရေးတဲ့အခါ 53 | 54 | - Code တွေ ရှင်းရှင်းလင်းလင်းဖြစ်အောင် (ဝါ) readable ဖြစ်အောင် ၊ 55 | - Code အစိတ်အပိုင်းများကို တခါရေးပြီးနောက် အခြားနေရာများတွင်လည်း ပြန်လည်အသုံးချနိုင်အောင် (ဝါ) reusable ဖြစ်အောင်၊ 56 | - ရှုပ်ထွေးနေတဲ့ Code တွေကို လိုအပ်ရင် အလွယ်တကူ သပ်သပ်ရပ်ရပ် Structure ကျကျ ပြန်လည်ပြင်ဆင်နိုင်တဲ့ (ဝါ) refactorable ဖြစ်အောင် 57 | 58 | ရေးသားနိုင်ဖို့ အကူအညီပေးမယ့် လမ်းညွှန်ချက်တစ်ခုဆိုရင် ပိုမှန်ပါလိမ့်မယ်။ 59 | 60 | ဒီမှာဖော်ပြထားတဲ့ Software ရေးသားနည်း ဥပဒေသတွေကို တသွေမတိမ်း လိုက်နာဖို့တော့ မလိုအပ်ပါဘူး။ တချို့သော ဥပဒေသတွေကိုသာ နေရာတိုင်းမှာ မှန်တယ်လို့ လက်ခံထားကြတာပါ။ အများစုက သူ့နေရာနဲ့သူ မှန်ကန်အောင် အသုံးချတတ်မှသာ အသုံးဝင်မှာပါ။ 61 | ဒီ ဥပဒေသတွေက လမ်းညွှန်တစ်ခုထက် မပိုပါဘူး။ ဒါပေမယ့် ဒီလမ်းညွှန်ချက်တွေက နှစ်ပေါင်းများစွာ code တွေရေးရင်း ရလာတဲ့ အတွေအကြုံတွေကို Clean Code ရဲ့ မူရင်းရေးသားသူက ကြိုးစားပြီး စုစည်းပေးထားတာပါ။ 62 | 63 | Inspired from [clean-code-javascript](https://github.com/ryanmcdermott/clean-code-javascript). 64 | 65 | အခုလက်ရှိ PHP developers တော်တော်များများက PHP 5 ကို သုံးနေဆဲဖြစ်ပေမယ့်၊ Code example တော်တော်များများကိုတော့ PHP 7.1+ ကိုသာ အသုံးပြုရေးသားဖြစ်သွားပါတယ်။ 66 | 67 | ## Variables 68 | 69 | ### Use meaningful and pronounceable variable names 70 | 71 | Variable တွေနာမည်ပေးတဲ့အခါ ထင်ရှားတဲ့ အဓိပ္ပာယ်ရှိပြီး အသံထွက်ဖတ်လို့ရတဲ့ စကားလုံးမျိုးတွေကိုသာ ရွေးချယ်သင့်ပါတယ်။ 72 | 73 | **မဖြစ်သင့်:** 74 | 75 | ```php 76 | $ymdstr = $moment->format('y-m-d'); 77 | ``` 78 | 79 | **ဖြစ်သင့်:** 80 | 81 | ```php 82 | $currentDate = $moment->format('y-m-d'); 83 | ``` 84 | 85 | **[⬆ back to top](#introduction)** 86 | 87 | ### Use the same vocabulary for the same type of variable 88 | 89 | အမျိုးအစားတူတဲ့ variable တွေအတွက် နာမည်ပေးတဲ့အခါ တူညီတဲ့ ဝေါဟာရတခုတည်းကိုသာ တသတ်မတ်တည်း အသုံးပြုသင့်ပါတယ်။ 90 | 91 | **မဖြစ်သင့်:** 92 | 93 | ```php 94 | getUserInfo(); 95 | getUserData(); 96 | getUserRecord(); 97 | getUserProfile(); 98 | ``` 99 | 100 | **ဖြစ်သင့်:** 101 | 102 | ```php 103 | getUser(); 104 | ``` 105 | 106 | **[⬆ back to top](#မာတိကာ)** 107 | 108 | ### Use searchable names (part 1) 109 | 110 | ကျွန်တော်တို့ Programmer တွေဟာ Code ရေးလို့ အချိန်ကုန်တာထက်စာရင် ရေးပြီးသား Code ကို နားလည်အောင်ပြန်ဖတ်ရတာအတွက် အချိန်ပိုကုန်လေ့ ရှိပါတယ်။ ဒါကြောင့် ကျွန်တော်တို့ ရေးတဲ့ Code တွေဟာ ပြန်ဖတ်တဲ့အခါ ဖတ်ရလွယ်ကူဖို့နဲ့ လွယ်လင့်တကူပြန်ရှာလို့ရနိုင်ဖို့ အရေးကြီးလှပါတယ်။ 111 | 112 | ပြန်ရှာတယ်ဆိုတာ - ဥပမာ: Code Line ပေါင်း ၁၀၀၀၀ လောက် ရှိတဲ့ Project ကြီးမှာ User Log In စဝင်တဲ့ အချိန်ကို ဘယ် Variable နဲ့ မှတ်ထားမိလဲဆိုတာ ပြန်ရှာသလိုမျိုးပေါ့။ userLogInTime လို့ variable ကို နာမည်မပေးဘဲ uLInTime လို့ အတိုကောက် ပေးလိုက်ရင် နောက် Maintain လုပ်မယ့် Developer အနေနဲ့ နားလည်ဖို့ အခက်အခဲဖြစ်သွားနိုင်တာကို ဆိုလိုတာပါ။ 113 | 114 | ဒါကြောင့် variable နာမည်ပေးတဲ့အခါ ပြီးပြီးရောမပေးသင့်ပါဘူး။ ကိုယ်ရေးနေတဲ့ program ကို ပြန်ဖတ်တဲ့အခါ နားလည်လွယ်စေဖို့ ရည်ရွယ်ပြီး သေချာရွေးချယ်ပြီးမှ ပေးသင့်ပါတယ်။ အဲလိုမှ မဟုတ်ရင် ကိုယ့် program ကို ဖတ်မယ့်သူတွေကို အခက်အခဲ များစွာ ဖြစ်ပေါ်စေမှာပါ။ လိုရင်းကတော့ variable နာမည်ကို ပြန်ဖတ်ရင် အဓိပ္ပာယ်ရှိအောင်၊ ပြန်ရှာလို့လွယ်အောင် ပေးပါ။ 115 | 116 | **မဖြစ်သင့်:** 117 | 118 | ```php 119 | // 448 ဆိုတာ ဘာကြီးလဲ၊ ဘာကိုဆိုလိုတာလဲ 120 | $result = $serializer->serialize($data, 448); 121 | ``` 122 | 123 | **ဖြစ်သင့်:** 124 | 125 | ```php 126 | $json = $serializer->serialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); 127 | ``` 128 | 129 | ### Use searchable names (part 2) 130 | 131 | **မဖြစ်သင့်::** 132 | 133 | ```php 134 | // 4 ဆိုတာ ဘာကြီးလဲ၊ ဘာကိုရည်ရွယ်ခြင်တာလဲ 135 | if ($user->access & 4) { 136 | // ... 137 | } 138 | ``` 139 | 140 | **ဖြစ်သင့်:** 141 | 142 | ```php 143 | class User 144 | { 145 | const ACCESS_READ = 1; 146 | const ACCESS_CREATE = 2; 147 | const ACCESS_UPDATE = 4; 148 | const ACCESS_DELETE = 8; 149 | } 150 | 151 | if ($user->access & User::ACCESS_UPDATE) { 152 | // do edit ... 153 | } 154 | ``` 155 | 156 | **[⬆ back to top](#မာတိကာ)** 157 | 158 | ### Use explanatory variables 159 | 160 | Variable တွေကို အဓိပ္ပာယ်ပေါ်အောင် နာမည်ကောင်းကောင်း ပေးတတ်ရုံနဲ့ မလုံလောက်သေးပါဘူး။ Variable ကို အသုံးပြုမယ့် scope နဲ့ code statement ပေါ်မူတည်ပြီး မှန်ကန်အောင် ရေးတတ်ရပါသေးတယ်။ Technically အရ Scope in mind လို့ ခေါ်ပါတယ်။ 161 | 162 | Variable ကို အသုံးပြုထားတဲ့ code statement ကို ကြည့်ရုံနဲ့ variable ထဲမှာ ဘာ data သိမ်းပြီး process လုပ်သွားတာလဲ၊ ဘာ ရည်ရွယ်ချက်နဲ့ code statement မှာ သုံးသွားလဲဆိုတဲ့ အဓိပ္ပာယ်ပေါ်အောင်လည်း ရေးတတ်ရပါတယ်။ 163 | 164 | **မဖြစ်သင့်:** 165 | 166 | အောက်ပါ ဥပမာမှာဆို regular expression ကို process လုပ်သွားတဲ့ preg_match function ထဲမှာ ပေးထားတဲ့ $matches ဆိုတဲ့ variable ဟာ သူ အဓိပ္ပာယ်နဲ့သူ မှန်နေပါတယ်။ 167 | 168 | ဒါပေမယ့် `saveCityZipCode` function မှာ `parameters` အနေနဲ့ပေးတဲ့အခါကျတော့ `$matches[1]`, `$matches[2]` လို့ ပေးလိုက်တာဟာ code statement ကို ဖတ်ရတာ ဝေဝါးသွားစေပါတယ်။ `$matches[1]` ဆိုတာ `$matches` ဆိုတဲ့ `array` ရဲ့ ပထမအခန်းကို ရည်ညွှန်းမှန်းသိပေမယ့် အဲ့ဒီ ပထမအခန်းထဲမှာ ဘာ data သိမ်းထားမှန်းဆိုတာကတော့ မသိရတော့ပါဘူး။ အဲဒီတော့ code ကို maintain လုပ်မယ့်သူက အပေါ်က **regular expression** ကို အသေးစိတ်နားလည်အောင် သွားဖတ်ရပါတော့မယ်။ 169 | 170 | ```php 171 | $address = 'One Infinite Loop, Cupertino 95014'; 172 | $cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/'; 173 | preg_match($cityZipCodeRegex, $address, $matches); 174 | 175 | saveCityZipCode($matches[1], $matches[2]); 176 | ``` 177 | 178 | **အသင့်အတင့်:** 179 | 180 | အောက်က ပိုမိုကောင်းမွန်အောင် ပြန်လည်ပြင်ဆင်ထားတဲ့ ဥပမာကို ကြည့်ပါဦး။ 181 | 182 | ဒီဥပမာမှာတော့ `saveCityZipCode` function ရေးသားပုံက ကောင်းမွန်သွားပါပြီ။ ဒါပေသိ `[, $city, $zipCode] = $matches` ဆိုတဲ့ code line ဟာ code နားလည်လွယ်အောင် အပိုရေးလိုက်ရတဲ့ code တစ်ကြောင်းပုံစံ ဖြစ်သွားပါတယ်။ တကယ့် ရှုပ်ထွေးမှုတွေကို ဖြစ်ပေါ်စေတဲ့ **regular expression** ကို မရှင်းဘဲ code အပိုတစ်ကြောင်း ရေးလိုက်သလို ဖြစ်သွားတာပါ။ 183 | 184 | စာခြွင်း။ ။ တကယ့်လက်တွေ့မှာတော့ ဒီဥပမာက code ရေးသားမှုပုံစံဟာ လက်သင့်ခံလို့ ရပါတယ်။ အခုလောက် ရှင်းလင်းသပ်ရပ်မှု ရှိပြီဆိုရင် မဆိုးဘူးလို့ ပြောလို့ရပါပြီ။ ဘာဖြစ်လို့လဲဆိုရင် တကယ်လက်တွေ့ Project တွေမှာက အချိန်ဒီလောက် မပေးပါဘူး။ refactoring လုပ်ရင်း feature အသစ်ရေးရမယ့် အချိန်တွေပါ ကုန်သွားမှာကိုလည်း ဆင်ခြင်သင့်ပါတယ်။ 185 | 186 | ```php 187 | $address = 'One Infinite Loop, Cupertino 95014'; 188 | $cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/'; 189 | preg_match($cityZipCodeRegex, $address, $matches); 190 | 191 | [, $city, $zipCode] = $matches; 192 | saveCityZipCode($city, $zipCode); 193 | ``` 194 | 195 | **ဖြစ်သင့်:** 196 | 197 | အခု ဥပမာမှာတော့ code ရေးသားမှုဟာ အကောင်းဆုံးပုံစံကို ရောက်ရှိသွားပြီလို့ ယူဆလို့ ရပါတယ်။ တကယ့် ရှုပ်ထွေးမှုကို ဖြစ်စေတဲ့ **regular expression** ကို ရှင်းလင်းအောင် `subpattern` တွေကို (naming) နာမည်ပေးပြီး ဖြေရှင်းလိုက်နိုင်ပါတယ်။ code ရေးသားမှုမှာလည်း အပိုအလို မရှိ ကျစ်လစ်သွားပါတယ်။ 198 | 199 | ```php 200 | $address = 'One Infinite Loop, Cupertino 95014'; 201 | $cityZipCodeRegex = '/^[^,]+,\s*(?<city>.+?)\s*(?<zipCode>\d{5})$/'; 202 | preg_match($cityZipCodeRegex, $address, $matches); 203 | 204 | saveCityZipCode($matches['city'], $matches['zipCode']); 205 | ``` 206 | 207 | **[⬆ back to top](#မာတိကာ)** 208 | 209 | ### Avoid nesting too deeply and return early (part 1) 210 | 211 | Nested if-else statement တွေကို တတ်နိုင်သလောက် ရှောင်ပါ။ return statement တွေ အများကြီး ပြန်တာမျိုးတွေကိုလည်း ရှောင်ပါ။ (အပိုင်း ၁) 212 | 213 | if-else statement တွေ သိပ်များသွားရင် code ကို trace လိုက်ရတာ ခက်ခဲသွားစေပါတယ်။ နောက်တစ်ခု ယူဆလို့ ရတာက ကိုယ့် ရေးတဲ့ function ထဲမှာ if-else statement တွေ ၂ ခု ၃ ခုထက် ပိုနေပြီဆိုရင် ကိုယ့် function က အလုပ်တစ်ခုထက် မက လုပ်နေပြီလို့ ယူဆလို့ ရပါတယ်။ function တစ်ခုက လုပ်ဆောင်ချက် တစ်ခုဘဲ ရှိသင့်ပါတယ်။ ဒီလို အခါမျိုးမှာ Polymorphism (သို့) State/Strategy pattern နဲ့ ပြန်ပြီး code ကို refactor လုပ်လို့ ရပါတယ်။ 214 | 215 | function တစ်ခုကနေ return statement တွေ အများကြီး ပြန်တာမျိုးတွေကိုလည်း ရှောင်သင့်ပါတယ်။ ဒီအချက်ကလည်း function တစ်ခုကို trace လိုက်ရတာ အတော်လေး ခက်ခဲသွားစေနိုင်တဲ့ အချက်ပါ။ return statement အများဆုံး ဘယ်လောက်ဘဲ ရှိသင့်တယ်လို့တော့ အတိအကျ သတ်မှတ်ထားတာ မရှိပါဘူး။ နည်းနိုင်သမျှ နည်းတာတော့ ကောင်းပါတယ်။ 216 | 217 | Returning early ဆိုတဲ့ အယူအဆ မျိုးတော့ ရှိပါတယ်။ ဥပမာ - file download လုပ်တဲ့ function တစ်ခုမှာ Internet connection မရှိရင် exception တက်တာဘဲဖြစ်ဖြစ်၊ return false ပြန်တာဘဲဖြစ်ဖြစ်ကို function ရဲ့ အပေါ်ဆုံး code line တွေမှာ ရေးလို့ ရပါတယ်။ ဒါပေမယ့် function ကို ဖတ်ရတာ ပိုကောင်းသွားစေတယ်လို့ သေချာမှသာ ဒီလိုမျိုး ရေးသင့်ပါတယ်။ 218 | 219 | စာခြွင်း ရှင်းလင်းချက်: 220 | trace လိုက်တယ်ဆိုတာ code တကြောင်းခြင်းစီက ဘာအလုပ်လုပ်သွားလဲ၊ ဘာ output ထွက်သွားလဲဆိုတာ လိုက်ကြည့်တယ်လို့ ယေဘုယျနားလည်လို့ ရပါတယ်။ PHP မှာဆိုရင် xdebug နဲ့ debug လိုက်ကြည့်သလိုမျိုး၊ var_dump နဲ့ print ထုတ်ကြည့်သလိုမျိုးပေါ့။ ဒီလို tools တွေနဲ့ မဟုတ်ဘဲ စိတ်ထဲမှာဘဲဖြစ်စေ၊ စာအုပ်ပေါ်မှာ ချရေးပြီးတော့ ဖြစ်စေ step by step စိတ်မှန်းနဲ့လည်း trace လိုက်သွားတာမျိုးလည်း ရှိပါတယ်။ အဲဒါကို term အရ Dry Run လို့ ခေါ်ပါတယ်။ 221 | 222 | ပုံမှန် trace ကို code ရဲ့ ဘယ်နားက error တက်သွားလဲဆိုတာသိဖို့ လိုက်ကြတာ များပါတယ်။ သို့မဟုတ် သူများရေးထားတဲ့ (ကိုယ်အရင်က ရေးထားပေမယ့် မေ့သွားတဲ့) code တွေကို နားလည်အောင် ပြန် ဖတ်တဲ့ အခါမျိုးမှာလည်း သုံးပါတယ်။ 223 | 224 | **မဖြစ်သင့်:** 225 | 226 | ```php 227 | function isShopOpen($day): bool 228 | { 229 | if ($day) { 230 | if (is_string($day)) { 231 | $day = strtolower($day); 232 | if ($day === 'friday') { 233 | return true; 234 | } elseif ($day === 'saturday') { 235 | return true; 236 | } elseif ($day === 'sunday') { 237 | return true; 238 | } else { 239 | return false; 240 | } 241 | } else { 242 | return false; 243 | } 244 | } else { 245 | return false; 246 | } 247 | } 248 | ``` 249 | 250 | **ဖြစ်သင့်:** 251 | 252 | ```php 253 | function isShopOpen(string $day): bool 254 | { 255 | if (empty($day)) { 256 | return false; 257 | } 258 | 259 | $openingDays = [ 260 | 'friday', 'saturday', 'sunday' 261 | ]; 262 | 263 | return in_array(strtolower($day), $openingDays, true); 264 | } 265 | ``` 266 | 267 | **[⬆ back to top](#မာတိကာ)** 268 | 269 | ### Avoid nesting too deeply and return early (part 2) 270 | 271 | **မဖြစ်သင့်:** 272 | 273 | ```php 274 | function fibonacci(int $n) 275 | { 276 | if ($n < 50) { 277 | if ($n !== 0) { 278 | if ($n !== 1) { 279 | return fibonacci($n - 1) + fibonacci($n - 2); 280 | } else { 281 | return 1; 282 | } 283 | } else { 284 | return 0; 285 | } 286 | } else { 287 | return 'Not supported'; 288 | } 289 | } 290 | ``` 291 | 292 | **ဖြစ်သင့်:** 293 | 294 | ```php 295 | function fibonacci(int $n): int 296 | { 297 | if ($n === 0 || $n === 1) { 298 | return $n; 299 | } 300 | 301 | if ($n > 50) { 302 | throw new \Exception('Not supported'); 303 | } 304 | 305 | return fibonacci($n - 1) + fibonacci($n - 2); 306 | } 307 | ``` 308 | 309 | **[⬆ back to top](#မာတိကာ)** 310 | 311 | ### Avoid Mental Mapping 312 | 313 | ဒီအချက်ကတော့ အပေါ်ကအချက်တွေကိုဘဲ မူကွဲပုံစံ တစ်မျိုးနဲ့ ပြောထားတာပါ။ 314 | Variable တစ်ခုကို နားလည်ဖို့ သူ့ထဲမှာ သိမ်းထားတဲ့ data ကို သွားဖတ်ပြီးမှ သိရတာမျိုးက မကောင်းပါဘူး။ ရှင်းလင်းပြတ်သားခြင်းက သွယ်ဝိုက်နေတာထက် ပိုကောင်းပါတယ်။ 315 | 316 | **မဖြစ်သင့်:** 317 | 318 | ```php 319 | $l = ['Austin', 'New York', 'San Francisco']; 320 | 321 | for ($i = 0; $i < count($l); $i++) { 322 | $li = $l[$i]; 323 | doStuff(); 324 | doSomeOtherStuff(); 325 | // ... 326 | // ... 327 | // ... 328 | // Wait, what is `$li` for again? 329 | dispatch($li); 330 | } 331 | ``` 332 | 333 | **ဖြစ်သင့်:** 334 | 335 | ```php 336 | $locations = ['Austin', 'New York', 'San Francisco']; 337 | 338 | foreach ($locations as $location) { 339 | doStuff(); 340 | doSomeOtherStuff(); 341 | // ... 342 | // ... 343 | // ... 344 | dispatch($location); 345 | } 346 | ``` 347 | 348 | **[⬆ back to top](#မာတိကာ)** 349 | 350 | ### Don't add unneeded context 351 | 352 | ဖော်ပြခဲ့သမျှ အချက်တွေကတော့ Variable နာမည် ပေးတဲ့အခါ တတ်နိုင်သမျှ ပြည့်စုံရှင်းလင်းအောင် ရေးဖို့ ပြောထားတာပါ။ 353 | ဒါပေမယ့် မလိုအပ်ဘဲ စကားဆက် (context) တွေ မထည့်မိဖို့လည်း အရေးကြီးပါတယ်။ 354 | 355 | ဥပမာပေးရမယ်ဆိုရင် class/object ထဲက member variable တိုင်းမှာ class နာမည်ကြီးကို ရှေ့က လိုက်ထည့်နေတာမျိုးပါ။ "User" class ထဲမှာ user ရဲ့ အသက်ကိုသိမ်းဖို့ variable ကို နာမည်ပေးတဲ့အခါ age ဆိုရင် လုံလောက်ပါပြီ။ userAge ဆိုပြီး စကားဆက် (context) ကို ရှေ့ကခံပေးစရာ မလိုပါဘူး။ ဘာဖြစ်လို့လဲဆိုရင်အဲ့ဒီ age ဆိုတဲ့ member variable ကို ခေါ်သုံးတဲ့အခါ `user->age` ဆိုပြီး ခေါ်သုံးရမှာ ဖြစ်လို့ပါ။ `user->age` ဆိုတဲ့ code expression ကို ကြည့်မယ်ဆိုရင် user ရဲ့ age ကို ရည်ညွှန်းမှန်း သိသာပါတယ်။ 356 | 357 | **မဖြစ်သင့်:** 358 | 359 | ```php 360 | class Car 361 | { 362 | public $carMake; 363 | public $carModel; 364 | public $carColor; 365 | 366 | //... 367 | } 368 | ``` 369 | 370 | **ဖြစ်သင့်:** 371 | 372 | ```php 373 | class Car 374 | { 375 | public $make; 376 | public $model; 377 | public $color; 378 | 379 | //... 380 | } 381 | ``` 382 | 383 | **[⬆ back to top](#မာတိကာ)** 384 | 385 | ### Use default arguments instead of short circuiting or conditionals 386 | 387 | Programmer တွေအနေနဲ့ Function တွေကို ရေးတဲ့အခါတိုင်း parameter declaration ဆိုတာကို တနည်းမဟုတ်တနည်းနဲ့တော့ ကြုံကြရတာပါဘဲ။ 388 | 389 | Parameter ကြေငြာတဲ့အခါ အဲဒီ parameter အတွက် default value သတ်မှတ်ပေးဖို့ လိုအပ်တဲ့အခါတိုင်း default argument ကြေငြာတဲ့ပုံစံမျိုးနဲ့ဘဲ ရေးသင့်ပါတယ်။ **Ternary conditional** ပုံစံတွေ၊ **short circuiting** ပုံစံတွေနဲ့ မရေးသင့်ပါဘူး။ 390 | 391 | စာခြွင်း။ ။ **short circuiting** ဆိုတဲ့ term က တချို့သော developer တွေအတွက် အနည်းငယ် စိမ်းနေနိုင်ပါတယ်။ ဒီ [wiki link](https://en.wikipedia.org/wiki/Short-circuit_evaluation) နဲ့ [stackoverflow link](https://stackoverflow.com/questions/9344305/what-is-short-circuiting-and-how-is-it-used-when-programming-in-java) မှာတော့ ရှင်းထားတာ အတော်လေး ကောင်းပါတယ်။ 392 | 393 | **မဖြစ်သင့်:** 394 | 395 | ဒီရေးသားမှုပုံစံဟာ လိုအပ်ချက်ရှိနေသေးတယ်လို့ ယူဆလို့ရပါတယ်။ အောက်ပါ function ကိုသာ `$this->createMicrobrewery(null)` နဲ့ call လိုက်ရင် `$breweryName` ဟာ ဖြစ်စေခြင်တဲ့ default value “Hipster Brew Co.” အစား `null` value ဝင်သွားမှာပါ။ 396 | 397 | စာခြွင်း။ ။ ကိုယ်ပိုင်အမြင်အရဆိုရင် ဒီရေးသားမှုပုံစံဟာ function ရဲ့ argument control ကို လျှော့ကျသွားတယ် ဆိုရုံလောက်ဘဲ ယူဆလို့ ရတာပါ။ **PHP** လို *dynamic typing language* တစ်ခုမှာ function parameter တစ်ခုကို `null` value ဝင်တာနဲ့ မကောင်းဘူးပြောဖို့ဆိုတာ အနည်းငယ် တဖက်သက်ဆန်တယ်လို့ ခံစားမိပါတယ်။ 398 | 399 | ```php 400 | function createMicrobrewery($breweryName = 'Hipster Brew Co.'): void 401 | { 402 |    // ... 403 | } 404 | ``` 405 | 406 | **အသင့်အတင့်:** 407 | 408 | ဒီရေးသားမှုပုံစံကတော့ မဆိုးဘူးလို့ ပြောလို့ရပါပြီ။ default value ကို `null` လို့ ထားလိုက်တယ်။ ပြီးမှ function body ထဲမှာ **ternary operator** နဲ့ `null` ဖြစ်ခဲ့ရင် actual default value “Hipster Brew Co.” ထည့်မယ်ဆိုပြီး ရေးလိုက်ပါတယ်။ 409 | 410 | ဒါကြောင့် argument မသတ်မှတ်ဘဲ function call လိုက်သည်ဖြစ်စေ၊ `null` or `empty value` ပေးလိုက်သည်ဖြစ်စေ `$breweryName` parameter ဟာ actual default value “Hipster Brew Co.” ရှိနေတော့မှာပါ။ 411 | 412 | စာခြွင်း။ ။ ဒီ ရေးသားမှုမှာလည်း မိမိကိုယ်ပိုင်အမြင်အရဆိုရင် အရမ်းကြီး သဘောမတူခြင်ပါဘူး။ default argument ဆိုတာ `null` value ကို ရှောင်ခြင်လို့ ရေးရတာမျိုး မဟုတ်ပါဘူး။ function call တဲ့ အချိန်မှာ လိုအပ်တဲ့ parameter အတွက် argument မပေးခဲ့ဘူးဆိုရင် logic အရ default value တစ်ခု သတ်မှတ်ပေးလိုက်တဲ့ သဘောပါ။ 413 | 414 | ```php 415 | function createMicrobrewery($name = null): void 416 | { 417 |    $breweryName = $name ?: 'Hipster Brew Co.'; 418 | // ... 419 | } 420 | ``` 421 | 422 | **ဖြစ်သင့်:** 423 | 424 | ဒီ ရေးသားမှုမှာတော့ Php7 ရဲ့ [type hinting](http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration) feature ကို အသုံးပြုပြီး argument ကို **string datatype** နဲ့ restrict လုပ်လိုက်ပါတယ်။ ဒါကြောင့် `null` value ဝင်လာခဲ့ရင် `exception` တက်မှာပါ။ 425 | 426 | `null` value ဖြစ်မှာလည်း စိုးရိမ်စရာ မလိုတော့သလို default argument လည်း ကြေငြာပြီးသား ဖြစ်သွားစေတဲ့အတွက် ကောင်းမွန်တဲ့ ရေးသားမှုတစ်ခုလို့ ဆိုလို့ရပါတယ်။ 427 | 428 | ```php 429 | function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void 430 | { 431 |    // ... 432 | } 433 | ``` 434 | 435 | **[⬆ back to top](#မာတိကာ)** 436 | 437 | ## Comparison 438 | 439 | ### Use [identical comparison](http://php.net/manual/en/language.operators.comparison.php) 440 | Php မှာ == (equality) နဲ့ === (identical) ဆိုပြီး comparison operator နှစ်မျိုးရှိတာ developer တိုင်း သိကြမှာပါ။ Php မှာ == (equality) နဲ့ === (identical) ဆိုပြီး comparison operator နှစ်မျိုးရှိတာ developer တိုင်း သိကြမှာပါ။ == (equality) comparison နဲ့ condition စစ်ရင် operands တွေကို type juggling လုပ်လိုက်ပါတယ်။ === (identical) comparison မှာတော့ type juggling မလုပ်ပါဘူး။ 441 | 442 | Type juggling rules အသေးစိတ်ကိုတော့ [php official documentation](http://php.net/manual/en/language.operators.comparison.php) မှာ သွားဖတ်လို့ ရပါတယ်။ 443 | 444 | Type juggling လုပ်တဲ့ rules တွေက အနည်းငယ် ရှုပ်ထွေးပြီး အလွတ်မှတ်ထား နိုင်ဖို့လည်း မလွယ်လှပါဘူး။ ဒါကြောင့် == (equality) operator နဲ့ရေးတဲ့ comparison expression တွေမှာ ကိုယ်မမျှော်လင့်ထားတဲ့ result တွေ ထွက်လာနိုင်ပါတယ်။ 445 | 446 | ဖြစ်နိုင်ရင်တော့ condition စစ်တိုင်း (===) identical comparison operator ကိုသာ သုံးသင့်ပါတယ်။ 447 | 448 | **မဖြစ်သင့်:** 449 | 450 | အောက်ပါ code block မှာဆိုရင် (==) equality comparison operator က string ကို integer ပြောင်းလိုက်ပါတယ်။ 451 | 452 | ```php 453 | $a = '42'; 454 | $b = 42; 455 | 456 | if ($a != $b) { 457 | // The expression will always pass 458 | } 459 | ``` 460 | 461 | *string* `42` နဲ့ *integer* `42` ဟာ computational logic အရ ဆိုရင် မတူပါဘူး။ ဒါပေမယ့် `$a != $b` လို့ comparison လုပ်လိုက်ရင်တော့ `FALSE` return ပြန်လာတာကို တွေ့ရမှာပါ။ 462 | 463 | ဘာဖြစ်လို့လဲဆိုရင် သူတို့နှစ်ခုကို (==) equality operator က type juggling လုပ်လိုက်တဲ့အတွက် *string* `42` က *integer* ဖြစ်သွားပါတယ်။ ဒါကြောင့် သူတို့နှစ်ခုကို comparison လုပ်တဲ့အခါ တူတယ်ဆိုတဲ့အဖြေဆိုတဲ့ ထွက်လာစေတာပါ။ 464 | 465 | **ဖြစ်သင့်:** 466 | 467 | (===) identical operator ကတော့ operand တွေရဲ့ datatype မတူရင် value comparison ကို မလုပ်ဆောင်တော့ပါဘူး။ 468 | 469 | ```php 470 | $a = '42'; 471 | $b = 42; 472 | 473 | if ($a !== $b) { 474 | // The expression is verified 475 | } 476 | ``` 477 | 478 | အထက်ပါ code block မှာတော့ မျှော်လင့်ထားတဲ့အတိုင်း `TRUE` ကို ရရှိမှာဘဲ ဖြစ်ပါတယ်။ 479 | 480 | **[⬆ back to top](#မာတိကာ)** 481 | 482 | 483 | ## Functions 484 | 485 | ### Function arguments (2 or fewer ideally) 486 | 487 | Function parameters အရေအတွက်ကို ကန့်သတ်ထားဖို့က အရမ်း အရေးကြီးပါတယ်။ Function parameters နည်းတဲ့အခါ အဲဒီ function အတွက် testing ရေးရတာ ပိုလွယ်ကူတာကို တွေ့ရမှာပါ။ 488 | Parameters ၃ ခုထက် ကျော်သွားရင် parameter တစ်ခုခြင်းစီအတွက် မတူတဲ့ value တစ်ခုခြင်းစီနဲ့ Test case တွေ လိုက်ရေးပေးနေရတဲ့အတွက် Test case တွေ အများကြီး ဖောင်းပွသွားနိုင်တာကို သတိချပ်သင့်ပါတယ်။ 489 | 490 | Zero argument (argument တစ်ခုမှ မလိုတဲ့ function) က အကောင်းဆုံးပါ။ Argument ၁ ခု (သို့) ၂ ခု လောက်ထိ အဆင်ပြေပါတယ်။ ၃ ခု ဆိုရင်တော့ စတင် သတိထားပြီး ရှောင်ရှားသင့်ပါပြီ။ အဲဒါထက် ပိုများသွားပြီဆိုရင်တော့ function ကို ပြန်ပြင်ရေးသင့်နေပါပြီ။ Argument ၂ ခုထက် ပိုနေတာဟာ အဲဒီ function က အလုပ်တစ်ခုထက်မက လုပ်နေပြီလို့ ယူဆလို့ ရပါတယ်။ အကယ်၍ ၃ ခုထက် ပိုတဲ့ argument တွေက context တစ်ခုတည်းအောက်မှာ ရှိနေတဲ့အခါမျိုးမှာတော့ object တစ်ခု အစားတည်ဆောက်ပြီး pass ပေးသင့်ပါတယ်။ 491 | 492 | အောက်ပါ ဥပမာကို လေ့လာကြည့်လို့ ရပါတယ်။ Menu တစ်ခု တည်ဆောက်ဖို့ parameter ၄ ခု pass ပေးမယ့်အစား MenuConfig ဆိုတဲ့ object တစ်ခု တည်ဆောက်ပြီး createMenu function ကို pass ပေးလိုက်တာ တွေ့ရမှာပါ။ 493 | 494 | **မဖြစ်သင့်:** 495 | 496 | ```php 497 | function createMenu(string $title, string $body, string $buttonText, bool $cancellable): void 498 | { 499 | // ... 500 | } 501 | ``` 502 | 503 | **ဖြစ်သင့်:** 504 | 505 | ```php 506 | class MenuConfig 507 | { 508 | public $title; 509 | public $body; 510 | public $buttonText; 511 | public $cancellable = false; 512 | } 513 | 514 | $config = new MenuConfig(); 515 | $config->title = 'Foo'; 516 | $config->body = 'Bar'; 517 | $config->buttonText = 'Baz'; 518 | $config->cancellable = true; 519 | 520 | function createMenu(MenuConfig $config): void 521 | { 522 | // ... 523 | } 524 | ``` 525 | 526 | **[⬆ back to top](#မာတိကာ)** 527 | 528 | ### Functions should do one thing 529 | 530 | ဒါကတော့ software engineering မှာ လိုက်နာသင့်တဲ့ အရေးကြီးဆုံး စည်းကမ်း တစ်ခုပါဘဲ။ အလုပ်တစ်ခုထက်ပိုတဲ့ Functions တွေ ရေးမိတဲ့အခါ program ကို အစဉ်တကျဆက်ရေးဖို့ ခက်သွားစေပါတယ်။ ဒုတိယအချက်အနေနဲ့ test case တွေ ရေးရတာလည်း မလွယ်တော့ပါဘူး။ Function က အလုပ်တစ်ခုထက် ပိုလုပ်တဲ့အတွက် အဲဒါကို cover ဖြစ်ဖို့အတွက် test cases တွေ အများကြီး လိုက်ရေးရတော့တာပါဘဲ။ တတိယအချက်ကတော့ reason to change တစ်ခုထက် ပိုသွားတာပါဘဲ။ Function တစ်ခုဟာ program ထဲမှာ သူဘာကြောင့်တည်ရှိနေသင့်လဲဆိုတဲ့ ရည်ရွယ်ချက် တစ်ခုဘဲ ရှိသင့်ပါတယ်။ အဲ့ဒါကို reason to change လို့ ခေါ်ကြတာပါ။ 531 | 532 | Function တွေကို လုပ်ဆောင်ချက် တစ်ခုတည်းသာလုပ်ဖို့ ကန့်သတ်ရေးသားနိုင်မယ်ဆိုရင် refactor လုပ်ရလွယ်သလို code တွေကလည်း အတော်လေး ရှင်းလင်းသွားပါလိမ့်မယ်။ အကယ်လို့ ဒီ guide ထဲက တခြားလမ်းညွှန်ချက်တွေကို မေ့ခြင်မေ့သွားပါစေ၊ ဒီတစ်ချက်ကိုတော့ သေချာပေါက် မှတ်ထားဖို့ လိုပါတယ်။ ဒီ တစ်ချက်ကို လိုက်နာနိုင်ယုံနဲ့တင် ပုံမှန် developers တွေရေးတဲ့ code ထက် အများကြီးသာတဲ့ clean code ကို ရေးနိုင်သွားမှာပါ။ 533 | 534 | စကားချပ်။ ။ ဒီမှာ တစ်ခုရှိနိုင်တာက လုပ်ဆောင်ချက်တစ်ခုတည်းလုပ်တယ်ဆိုတာ ဘာကို ပြောတာလဲဆိုပြီး မေးခွန်းထုတ်နိုင်ပါတယ်။ Function body ထဲမှာ ရှိတဲ့ code တွေ အားလုံးဟာ abstraction level တစ်ခုတည်းအောက်မှာ ရှိနေပြီး final output result တစ်ခုတည်းရှိနေခြင်းကို ဆိုလိုပါတယ်။ 535 | 536 | ဥပမာ - hard disk ပေါ်မှာ file သိမ်းတဲ့ function တစ်ခုကိုရေးမယ်ဆိုရင် code line တွေ အများကြီး ပါဝင်နိုင်ပေမယ့် သူတို့အားလုံးရဲ့ ရည်ရွယ်ချက်ကတော့ နောက်ဆုံးမှာ file သိမ်းသွားဖို့ပါဘဲ။ cloud ပေါ်မှာ file သိမ်းတဲ့ လုပ်ဆောင်ချက်ကိုပါ ထည့်ရေးလိုက်ရင် ဒါလုပ်ဆောင်ချက်တစ်ခုထက်ပိုတဲ့ function တစ်ခု ဖြစ်သွားပါလိမ့်မယ်။ 537 | 538 | **မဖြစ်သင့်:** 539 | ```php 540 | function emailClients(array $clients): void 541 | { 542 | foreach ($clients as $client) { 543 | $clientRecord = $db->find($client); 544 | if ($clientRecord->isActive()) { 545 | email($client); 546 | } 547 | } 548 | } 549 | ``` 550 | 551 | **ဖြစ်သင့်:** 552 | 553 | ```php 554 | function emailClients(array $clients): void 555 | { 556 | $activeClients = activeClients($clients); 557 | array_walk($activeClients, 'email'); 558 | } 559 | 560 | function activeClients(array $clients): array 561 | { 562 | return array_filter($clients, 'isClientActive'); 563 | } 564 | 565 | function isClientActive(int $client): bool 566 | { 567 | $clientRecord = $db->find($client); 568 | 569 | return $clientRecord->isActive(); 570 | } 571 | ``` 572 | 573 | **[⬆ back to top](#မာတိကာ)** 574 | 575 | ### Function names should say what they do 576 | 577 | Programmer တွေဟာ ဗေဒင်ဆရာမဟုတ်ပေမယ့် နာမည်ပေးတဲ့အလုပ်ကိုတော့ နိစ္စဓူဝ ပျော်ပျော်ကြီး လုပ်နေကြရပါတယ်။ ဒီနေရာမှာလည်း Function တွေကို နာမည်ပေးတဲ့အခါ သူရဲ့လုပ်ဆောင်ချက်ကို ပေါ်လွင်စေတဲ့ အမည်မျိုးကို ရွေးချယ်ပေးသင့်ပါတယ်။ နာမည်ကောင်းတစ်ခု ရွေးချယ်သတ်မှတ်ရတာဟာ လွယ်တဲ့အလုပ်တစ်ခုတော့ မဟုတ်ပါဘူး။ 578 | အချိန်ပေးပြီး စဉ်းစားရပါတယ်။ ဒါပေမယ့် ရေရည်မှာ ကိုယ့် program ရဲ့ maintainability ကို အများကြီး အထောက်အကူကောင်း ဖြစ်စေမှာပါ။ 579 | 580 | **မဖြစ်သင့်:** 581 | 582 | ```php 583 | class Email 584 | { 585 | //... 586 | 587 | public function handle(): void 588 | { 589 | mail($this->to, $this->subject, $this->body); 590 | } 591 | } 592 | 593 | $message = new Email(...); 594 | // What is this? A handle for the message? Are we writing to a file now? 595 | $message->handle(); 596 | ``` 597 | 598 | **ဖြစ်သင့်:** 599 | 600 | ```php 601 | class Email 602 | { 603 | //... 604 | 605 | public function send(): void 606 | { 607 | mail($this->to, $this->subject, $this->body); 608 | } 609 | } 610 | 611 | $message = new Email(...); 612 | // Clear and obvious 613 | $message->send(); 614 | ``` 615 | 616 | **[⬆ back to top](#မာတိကာ)** 617 | 618 | ### Functions should only be one level of abstraction 619 | 620 | When you have more than one level of abstraction your function is usually 621 | doing too much. Splitting up functions leads to reusability and easier 622 | testing. 623 | 624 | **Bad:** 625 | 626 | ```php 627 | function parseBetterJSAlternative(string $code): void 628 | { 629 | $regexes = [ 630 | // ... 631 | ]; 632 | 633 | $statements = explode(' ', $code); 634 | $tokens = []; 635 | foreach ($regexes as $regex) { 636 | foreach ($statements as $statement) { 637 | // ... 638 | } 639 | } 640 | 641 | $ast = []; 642 | foreach ($tokens as $token) { 643 | // lex... 644 | } 645 | 646 | foreach ($ast as $node) { 647 | // parse... 648 | } 649 | } 650 | ``` 651 | 652 | **Bad too:** 653 | 654 | We have carried out some of the functionality, but the `parseBetterJSAlternative()` function is still very complex and not testable. 655 | 656 | ```php 657 | function tokenize(string $code): array 658 | { 659 | $regexes = [ 660 | // ... 661 | ]; 662 | 663 | $statements = explode(' ', $code); 664 | $tokens = []; 665 | foreach ($regexes as $regex) { 666 | foreach ($statements as $statement) { 667 | $tokens[] = /* ... */; 668 | } 669 | } 670 | 671 | return $tokens; 672 | } 673 | 674 | function lexer(array $tokens): array 675 | { 676 | $ast = []; 677 | foreach ($tokens as $token) { 678 | $ast[] = /* ... */; 679 | } 680 | 681 | return $ast; 682 | } 683 | 684 | function parseBetterJSAlternative(string $code): void 685 | { 686 | $tokens = tokenize($code); 687 | $ast = lexer($tokens); 688 | foreach ($ast as $node) { 689 | // parse... 690 | } 691 | } 692 | ``` 693 | 694 | **Good:** 695 | 696 | The best solution is move out the dependencies of `parseBetterJSAlternative()` function. 697 | 698 | ```php 699 | class Tokenizer 700 | { 701 | public function tokenize(string $code): array 702 | { 703 | $regexes = [ 704 | // ... 705 | ]; 706 | 707 | $statements = explode(' ', $code); 708 | $tokens = []; 709 | foreach ($regexes as $regex) { 710 | foreach ($statements as $statement) { 711 | $tokens[] = /* ... */; 712 | } 713 | } 714 | 715 | return $tokens; 716 | } 717 | } 718 | 719 | class Lexer 720 | { 721 | public function lexify(array $tokens): array 722 | { 723 | $ast = []; 724 | foreach ($tokens as $token) { 725 | $ast[] = /* ... */; 726 | } 727 | 728 | return $ast; 729 | } 730 | } 731 | 732 | class BetterJSAlternative 733 | { 734 | private $tokenizer; 735 | private $lexer; 736 | 737 | public function __construct(Tokenizer $tokenizer, Lexer $lexer) 738 | { 739 | $this->tokenizer = $tokenizer; 740 | $this->lexer = $lexer; 741 | } 742 | 743 | public function parse(string $code): void 744 | { 745 | $tokens = $this->tokenizer->tokenize($code); 746 | $ast = $this->lexer->lexify($tokens); 747 | foreach ($ast as $node) { 748 | // parse... 749 | } 750 | } 751 | } 752 | ``` 753 | 754 | **[⬆ back to top](#မာတိကာ)** 755 | 756 | ### Don't use flags as function parameters 757 | 758 | Flag Argument တွေကို function ရေးတဲ့နေရာမှာ သုံးတာကလည်း ဆိုးရွားတဲ့ ရေးသားပုံတစ်ခုပါဘဲ။ 759 | Flag Argument ဆိုတာ function ခေါ်တဲ့အခါ boolean value တစ်ခုကို parameter အနေနဲ့ လက်ခံတယ်။ အဲဒီ boolean value ပေါ်မှာ မူတည်ပြီး function ထဲမှာ လုပ်ဆောင်ချက်တွေပြောင်းလဲ လုပ်ဆောင်ဖို့ ရေးသားထားတဲ့ ပုံစံကို ပြောတာပါ။ 760 | 761 | အောက်က code example ကို ကြည့်လို့ ရပါတယ်။ Concert booking လုပ်တဲ့ function မှာ isPremium ဆိုတဲ့ boolean argument တစ်ခုကို လက်ခံတယ်။ isPremium က true ဖြစ်ခဲ့ရင် premium booking ကိုလုပ်ပြီး false ဆိုရင် regular booking ကို လုပ်ဆောင်ဖို့ ရေးထားပါတယ်။ မကောင်းတဲ့အချက်တွေက - 762 | ၁) function က premium booking နဲ့ regular booking ဆိုပြီး အလုပ်တစ်ခုထက် ပိုလုပ်ဖို့ ရေးထားတယ်၊ 763 | ၂) function ကို ခေါ်တဲ့နေရာမှာလည်း order->book(customer, true) ဆိုတဲ့ statement ဟာဆိုရင် function declaration ကိုသွားမကြည့်ဘဲ boolean parameter က ဘာကိုကိုယ်စားပြုလဲဆိုတာ သိဖို့မလွယ်ကူဘူး။ 764 | 765 | ဒါကြောင့် ဒီလို flag နဲ့ ရေးမယ့်အစား function နှစ်ခုခွဲပြီး ရေးသားသင့်ပါတယ်။ 766 | 767 | **Bad:** 768 | 769 | ```php 770 | function concertBooking(Customer $customer, bool $isPremium = false): void 771 | { 772 | if ($isPremium) { 773 | // premium booking logic 774 | } else { 775 | // regular booking logic 776 | } 777 | } 778 | ``` 779 | 780 | **Good:** 781 | 782 | ```php 783 | function regularBooking(Customer $customer): void 784 | { 785 | // regular booking logic 786 | } 787 | 788 | function premiumBooking(Customer $customer): void 789 | { 790 | // premium booking logic 791 | } 792 | ``` 793 | 794 | စာခြွင်း။ ။ 795 | 796 | တခါတလေ function နှစ်ခုခွဲရေးလိုက်လို့ ဘုံတူနေတဲ့ code တွေ duplicate ဖြစ်ကုန်တဲ့ အခါမျိုးတွေလည်း ကြုံကောင်းကြုံရနိုင်ပါတယ်။ ဥပမာ - အထက်က booking case မျိုးမှာဆိုရင် booking အတွက် ထိုင်ခုံလွတ်ကျန်သေးလားစစ်တဲ့ code ကို premiumBooking မှာရော၊ regularBooking မှာပါ နှစ်ခါလိုက်ရေးရတဲ့ အခြေအနေမျိုး ဖြစ်လာနိုင်ပါတယ်။ 797 | 798 | အကယ်လို့ duplication ပမာဏက အရမ်းများသွားတဲ့ အခြေအနေမျိုးမှာဆိုရင် method ကို နှစ်ခုမခွဲဘဲ ဒီအတိုင်း ထားလိုက်လို့ရပါတယ်။ ဒါပေမယ့် private scope နဲ့ hidden လုပ်လိုက်ပါ။ 799 | ပြီးမှ သူကိုယူသုံးလို့ရအောင် public method နှစ်ခုပြန်ခွဲရေးပေးလိုက်လို့ ရပါတယ်။ မြင်သာအောင် အောက်က ဥပမာလေးနဲ့ တွဲကြည့်ပေးပါ။ 800 | 801 | ```php 802 | function regularBooking(Customer $customer): void 803 | { 804 | return $this->hiddenBookingImpl($customer); 805 | } 806 | 807 | function premiumBooking(Customer $customer): void 808 | { 809 | return $this->hiddenBookingImpl($customer, true); 810 | } 811 | 812 | private function hiddenBookingImpl(Customer $customer, bool $isPremium = false) : void 813 | { 814 | ... 815 | } 816 | ``` 817 | ဒီအတိုင်းဆိုရင် code လည်း duplicate မဖြစ်တော့ဘူး။ client class တွေထဲမှာလည်း readable code တွေ ရေးလို့ရသွားပါပြီ။ 818 | 819 | **[⬆ back to top](#မာတိကာ)** 820 | 821 | ### Avoid Side Effects 822 | 823 | Side Effect ဆိုတာ function ရဲ့ scope အပြင်ဘက်က state တစ်ခုခုကို (မမျှော်လင့်ထားတဲ့) ပြောင်းလဲမှုတွေ လုပ်လိုက်တာကို ပြောတာပါ။ ဥပမာ - file တစ်ခုကို data သွား write တာ၊ global variable, member variable တွေထဲမှာ သိမ်းထားတဲ့ data တွေကို ပြောင်းပစ်လိုက်တာ၊ database ထဲမှာ data သွားထည့်တာ၊ ပြင်တာ၊ဖျက်တာ အစရှိတဲ့ လုပ်ဆောင်ချက်တွေက side effect တွေပေါ့။ 824 | 825 | စာခြွင်း။ ။ 826 | ဒီနေရာမှာ တစ်ခုသတိထားရမှာက Functional Programming နဲ့ Object Oriented Programming ပေါ်မှာ မူတည်ပြီး function နဲ့ side effect အပေါ်ယူဆချက်တွေက ကွဲပြားပါသေးတယ်။ ကိုယ်ပြောနေတဲ့ အကြောင်းအရာသည် ဘယ် Programming Paradigm ပေါ်မူတည်ပြီး ပြောနေလဲဆိုတာသိဖို့ အလွန်အရေးကြီးပါတယ်။ 827 | 828 | မူရင်း Clean code စာအုပ်ရဲ့ coding အယူအဆအားလုံးနီးပါးဟာ Object Oriented Programming (OOP) ပေါ်မှာ အခြေခံထားတာပါ။ ဒါကြောင့် ဒီနေရာမှာ side effect အကြောင်းကို OOP Paradigm နဲ့ဘဲ ရှင်းလင်းသွားပါ့မယ်။ 829 | 830 | Function Programming (FP) မှာလည်း pure function ဆိုတဲ့ code ရေးသားနည်းပုံစံ ရှိပါတယ်။ သူ့မှာလည်း မဖြစ်မနေလိုက်နာရမယ့် အခြေခံအချက်လေးတွေ ရှိပါတယ်။ ဥပမာ - idempotent တို့၊ no side effect တို့ပါ။ အသေးစိတ်တော့ မရေးတော့ပါဘူး။ အဓိက OOP နဲ့ မရောသွားစေဖို့ သတိချပ်စေခြင်တာပါဘဲ။ 831 | 832 | 833 | 834 | **Bad:** 835 | 836 | ```php 837 | // Global variable referenced by following function. 838 | // If we had another function that used this name, now it'd be an array and it could break it. 839 | $name = 'Ryan McDermott'; 840 | 841 | function splitIntoFirstAndLastName(): void 842 | { 843 | global $name; 844 | 845 | $name = explode(' ', $name); 846 | } 847 | 848 | splitIntoFirstAndLastName(); 849 | 850 | var_dump($name); // ['Ryan', 'McDermott']; 851 | ``` 852 | 853 | **Good:** 854 | 855 | ```php 856 | function splitIntoFirstAndLastName(string $name): array 857 | { 858 | return explode(' ', $name); 859 | } 860 | 861 | $name = 'Ryan McDermott'; 862 | $newName = splitIntoFirstAndLastName($name); 863 | 864 | var_dump($name); // 'Ryan McDermott'; 865 | var_dump($newName); // ['Ryan', 'McDermott']; 866 | ``` 867 | 868 | **[⬆ back to top](#မာတိကာ)** 869 | 870 | ### Don't write to global functions 871 | 872 | Polluting globals is a bad practice in many languages because you could clash with another 873 | library and the user of your API would be none-the-wiser until they get an exception in 874 | production. Let's think about an example: what if you wanted to have configuration array? 875 | You could write global function like `config()`, but it could clash with another library 876 | that tried to do the same thing. 877 | 878 | **Bad:** 879 | 880 | ```php 881 | function config(): array 882 | { 883 | return [ 884 | 'foo' => 'bar', 885 | ] 886 | } 887 | ``` 888 | 889 | **Good:** 890 | 891 | ```php 892 | class Configuration 893 | { 894 | private $configuration = []; 895 | 896 | public function __construct(array $configuration) 897 | { 898 | $this->configuration = $configuration; 899 | } 900 | 901 | public function get(string $key): ?string 902 | { 903 | return isset($this->configuration[$key]) ? $this->configuration[$key] : null; 904 | } 905 | } 906 | ``` 907 | 908 | Load configuration and create instance of `Configuration` class 909 | 910 | ```php 911 | $configuration = new Configuration([ 912 | 'foo' => 'bar', 913 | ]); 914 | ``` 915 | 916 | And now you must use instance of `Configuration` in your application. 917 | 918 | **[⬆ back to top](#မာတိကာ)** 919 | 920 | ### Don't use a Singleton pattern 921 | 922 | Singleton is an [anti-pattern](https://en.wikipedia.org/wiki/Singleton_pattern). Paraphrased from Brian Button: 923 | 1. They are generally used as a **global instance**, why is that so bad? Because **you hide the dependencies** of your application in your code, instead of exposing them through the interfaces. Making something global to avoid passing it around is a [code smell](https://en.wikipedia.org/wiki/Code_smell). 924 | 2. They violate the [single responsibility principle](#single-responsibility-principle-srp): by virtue of the fact that **they control their own creation and lifecycle**. 925 | 3. They inherently cause code to be tightly [coupled](https://en.wikipedia.org/wiki/Coupling_%28computer_programming%29). This makes faking them out under **test rather difficult** in many cases. 926 | 4. They carry state around for the lifetime of the application. Another hit to testing since **you can end up with a situation where tests need to be ordered** which is a big no for unit tests. Why? Because each unit test should be independent from the other. 927 | 928 | There is also very good thoughts by [Misko Hevery](http://misko.hevery.com/about/) about the [root of problem](http://misko.hevery.com/2008/08/25/root-cause-of-singletons/). 929 | 930 | **Bad:** 931 | 932 | ```php 933 | class DBConnection 934 | { 935 | private static $instance; 936 | 937 | private function __construct(string $dsn) 938 | { 939 | // ... 940 | } 941 | 942 | public static function getInstance(): DBConnection 943 | { 944 | if (self::$instance === null) { 945 | self::$instance = new self(); 946 | } 947 | 948 | return self::$instance; 949 | } 950 | 951 | // ... 952 | } 953 | 954 | $singleton = DBConnection::getInstance(); 955 | ``` 956 | 957 | **Good:** 958 | 959 | ```php 960 | class DBConnection 961 | { 962 | public function __construct(string $dsn) 963 | { 964 | // ... 965 | } 966 | 967 | // ... 968 | } 969 | ``` 970 | 971 | Create instance of `DBConnection` class and configure it with [DSN](http://php.net/manual/en/pdo.construct.php#refsect1-pdo.construct-parameters). 972 | 973 | ```php 974 | $connection = new DBConnection($dsn); 975 | ``` 976 | 977 | And now you must use instance of `DBConnection` in your application. 978 | 979 | **[⬆ back to top](#မာတိကာ)** 980 | 981 | ### Encapsulate conditionals 982 | 983 | **Bad:** 984 | 985 | ```php 986 | if ($article->state === 'published') { 987 | // ... 988 | } 989 | ``` 990 | 991 | **Good:** 992 | 993 | ```php 994 | if ($article->isPublished()) { 995 | // ... 996 | } 997 | ``` 998 | 999 | **[⬆ back to top](#မာတိကာ)** 1000 | 1001 | ### Avoid negative conditionals 1002 | 1003 | **Bad:** 1004 | 1005 | ```php 1006 | function isDOMNodeNotPresent(\DOMNode $node): bool 1007 | { 1008 | // ... 1009 | } 1010 | 1011 | if (!isDOMNodeNotPresent($node)) 1012 | { 1013 | // ... 1014 | } 1015 | ``` 1016 | 1017 | **Good:** 1018 | 1019 | ```php 1020 | function isDOMNodePresent(\DOMNode $node): bool 1021 | { 1022 | // ... 1023 | } 1024 | 1025 | if (isDOMNodePresent($node)) { 1026 | // ... 1027 | } 1028 | ``` 1029 | 1030 | **[⬆ back to top](#မာတိကာ)** 1031 | 1032 | ### Avoid conditionals 1033 | 1034 | This seems like an impossible task. Upon first hearing this, most people say, 1035 | "how am I supposed to do anything without an `if` statement?" The answer is that 1036 | you can use polymorphism to achieve the same task in many cases. The second 1037 | question is usually, "well that's great but why would I want to do that?" The 1038 | answer is a previous clean code concept we learned: a function should only do 1039 | one thing. When you have classes and functions that have `if` statements, you 1040 | are telling your user that your function does more than one thing. Remember, 1041 | just do one thing. 1042 | 1043 | **Bad:** 1044 | 1045 | ```php 1046 | class Airplane 1047 | { 1048 | // ... 1049 | 1050 | public function getCruisingAltitude(): int 1051 | { 1052 | switch ($this->type) { 1053 | case '777': 1054 | return $this->getMaxAltitude() - $this->getPassengerCount(); 1055 | case 'Air Force One': 1056 | return $this->getMaxAltitude(); 1057 | case 'Cessna': 1058 | return $this->getMaxAltitude() - $this->getFuelExpenditure(); 1059 | } 1060 | } 1061 | } 1062 | ``` 1063 | 1064 | **Good:** 1065 | 1066 | ```php 1067 | interface Airplane 1068 | { 1069 | // ... 1070 | 1071 | public function getCruisingAltitude(): int; 1072 | } 1073 | 1074 | class Boeing777 implements Airplane 1075 | { 1076 | // ... 1077 | 1078 | public function getCruisingAltitude(): int 1079 | { 1080 | return $this->getMaxAltitude() - $this->getPassengerCount(); 1081 | } 1082 | } 1083 | 1084 | class AirForceOne implements Airplane 1085 | { 1086 | // ... 1087 | 1088 | public function getCruisingAltitude(): int 1089 | { 1090 | return $this->getMaxAltitude(); 1091 | } 1092 | } 1093 | 1094 | class Cessna implements Airplane 1095 | { 1096 | // ... 1097 | 1098 | public function getCruisingAltitude(): int 1099 | { 1100 | return $this->getMaxAltitude() - $this->getFuelExpenditure(); 1101 | } 1102 | } 1103 | ``` 1104 | 1105 | **[⬆ back to top](#မာတိကာ)** 1106 | 1107 | ### Avoid type-checking (part 1) 1108 | 1109 | PHP is untyped, which means your functions can take any type of argument. 1110 | Sometimes you are bitten by this freedom and it becomes tempting to do 1111 | type-checking in your functions. There are many ways to avoid having to do this. 1112 | The first thing to consider is consistent APIs. 1113 | 1114 | **Bad:** 1115 | 1116 | ```php 1117 | function travelToTexas($vehicle): void 1118 | { 1119 | if ($vehicle instanceof Bicycle) { 1120 | $vehicle->pedalTo(new Location('texas')); 1121 | } elseif ($vehicle instanceof Car) { 1122 | $vehicle->driveTo(new Location('texas')); 1123 | } 1124 | } 1125 | ``` 1126 | 1127 | **Good:** 1128 | 1129 | ```php 1130 | function travelToTexas(Traveler $vehicle): void 1131 | { 1132 | $vehicle->travelTo(new Location('texas')); 1133 | } 1134 | ``` 1135 | 1136 | **[⬆ back to top](#မာတိကာ)** 1137 | 1138 | ### Avoid type-checking (part 2) 1139 | 1140 | If you are working with basic primitive values like strings, integers, and arrays, 1141 | and you use PHP 7+ and you can't use polymorphism but you still feel the need to 1142 | type-check, you should consider 1143 | [type declaration](http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration) 1144 | or strict mode. It provides you with static typing on top of standard PHP syntax. 1145 | The problem with manually type-checking is that doing it will require so much 1146 | extra verbiage that the faux "type-safety" you get doesn't make up for the lost 1147 | readability. Keep your PHP clean, write good tests, and have good code reviews. 1148 | Otherwise, do all of that but with PHP strict type declaration or strict mode. 1149 | 1150 | **Bad:** 1151 | 1152 | ```php 1153 | function combine($val1, $val2): int 1154 | { 1155 | if (!is_numeric($val1) || !is_numeric($val2)) { 1156 | throw new \Exception('Must be of type Number'); 1157 | } 1158 | 1159 | return $val1 + $val2; 1160 | } 1161 | ``` 1162 | 1163 | **Good:** 1164 | 1165 | ```php 1166 | function combine(int $val1, int $val2): int 1167 | { 1168 | return $val1 + $val2; 1169 | } 1170 | ``` 1171 | 1172 | **[⬆ back to top](#မာတိကာ)** 1173 | 1174 | ### Remove dead code 1175 | 1176 | Dead code is just as bad as duplicate code. There's no reason to keep it in 1177 | your codebase. If it's not being called, get rid of it! It will still be safe 1178 | in your version history if you still need it. 1179 | 1180 | **Bad:** 1181 | 1182 | ```php 1183 | function oldRequestModule(string $url): void 1184 | { 1185 | // ... 1186 | } 1187 | 1188 | function newRequestModule(string $url): void 1189 | { 1190 | // ... 1191 | } 1192 | 1193 | $request = newRequestModule($requestUrl); 1194 | inventoryTracker('apples', $request, 'www.inventory-awesome.io'); 1195 | ``` 1196 | 1197 | **Good:** 1198 | 1199 | ```php 1200 | function requestModule(string $url): void 1201 | { 1202 | // ... 1203 | } 1204 | 1205 | $request = requestModule($requestUrl); 1206 | inventoryTracker('apples', $request, 'www.inventory-awesome.io'); 1207 | ``` 1208 | 1209 | **[⬆ back to top](#မာတိကာ)** 1210 | 1211 | 1212 | ## Objects and Data Structures 1213 | 1214 | ### Use object encapsulation 1215 | 1216 | In PHP you can set `public`, `protected` and `private` keywords for methods. 1217 | Using it, you can control properties modification on an object. 1218 | 1219 | * When you want to do more beyond getting an object property, you don't have 1220 | to look up and change every accessor in your codebase. 1221 | * Makes adding validation simple when doing a `set`. 1222 | * Encapsulates the internal representation. 1223 | * Easy to add logging and error handling when getting and setting. 1224 | * Inheriting this class, you can override default functionality. 1225 | * You can lazy load your object's properties, let's say getting it from a 1226 | server. 1227 | 1228 | Additionally, this is part of [Open/Closed](#openclosed-principle-ocp) principle. 1229 | 1230 | **Bad:** 1231 | 1232 | ```php 1233 | class BankAccount 1234 | { 1235 | public $balance = 1000; 1236 | } 1237 | 1238 | $bankAccount = new BankAccount(); 1239 | 1240 | // Buy shoes... 1241 | $bankAccount->balance -= 100; 1242 | ``` 1243 | 1244 | **Good:** 1245 | 1246 | ```php 1247 | class BankAccount 1248 | { 1249 | private $balance; 1250 | 1251 | public function __construct(int $balance = 1000) 1252 | { 1253 | $this->balance = $balance; 1254 | } 1255 | 1256 | public function withdraw(int $amount): void 1257 | { 1258 | if ($amount > $this->balance) { 1259 | throw new \Exception('Amount greater than available balance.'); 1260 | } 1261 | 1262 | $this->balance -= $amount; 1263 | } 1264 | 1265 | public function deposit(int $amount): void 1266 | { 1267 | $this->balance += $amount; 1268 | } 1269 | 1270 |    public function getBalance(): int 1271 | { 1272 | return $this->balance; 1273 | } 1274 | } 1275 | 1276 | $bankAccount = new BankAccount(); 1277 | 1278 | // Buy shoes... 1279 | $bankAccount->withdraw($shoesPrice); 1280 | 1281 | // Get balance 1282 | $balance = $bankAccount->getBalance(); 1283 | ``` 1284 | 1285 | **[⬆ back to top](#မာတိကာ)** 1286 | 1287 | ### Make objects have private/protected members 1288 | 1289 | * `public` methods and properties are most dangerous for changes, because some outside code may easily rely on them and you can't control what code relies on them. **Modifications in class are dangerous for all users of class.** 1290 | * `protected` modifier are as dangerous as public, because they are available in scope of any child class. This effectively means that difference between public and protected is only in access mechanism, but encapsulation guarantee remains the same. **Modifications in class are dangerous for all descendant classes.** 1291 | * `private` modifier guarantees that code is **dangerous to modify only in boundaries of single class** (you are safe for modifications and you won't have [Jenga effect](http://www.urbandictionary.com/define.php?term=Jengaphobia&defid=2494196)). 1292 | 1293 | Therefore, use `private` by default and `public/protected` when you need to provide access for external classes. 1294 | 1295 | For more informations you can read the [blog post](http://fabien.potencier.org/pragmatism-over-theory-protected-vs-private.html) on this topic written by [Fabien Potencier](https://github.com/fabpot). 1296 | 1297 | **Bad:** 1298 | 1299 | ```php 1300 | class Employee 1301 | { 1302 | public $name; 1303 | 1304 | public function __construct(string $name) 1305 | { 1306 | $this->name = $name; 1307 | } 1308 | } 1309 | 1310 | $employee = new Employee('John Doe'); 1311 | echo 'Employee name: '.$employee->name; // Employee name: John Doe 1312 | ``` 1313 | 1314 | **Good:** 1315 | 1316 | ```php 1317 | class Employee 1318 | { 1319 | private $name; 1320 | 1321 | public function __construct(string $name) 1322 | { 1323 | $this->name = $name; 1324 | } 1325 | 1326 | public function getName(): string 1327 | { 1328 | return $this->name; 1329 | } 1330 | } 1331 | 1332 | $employee = new Employee('John Doe'); 1333 | echo 'Employee name: '.$employee->getName(); // Employee name: John Doe 1334 | ``` 1335 | 1336 | **[⬆ back to top](#မာတိကာ)** 1337 | 1338 | ## Classes 1339 | 1340 | ### Prefer composition over inheritance 1341 | 1342 | As stated famously in [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) by the Gang of Four, 1343 | you should prefer composition over inheritance where you can. There are lots of 1344 | good reasons to use inheritance and lots of good reasons to use composition. 1345 | The main point for this maxim is that if your mind instinctively goes for 1346 | inheritance, try to think if composition could model your problem better. In some 1347 | cases it can. 1348 | 1349 | You might be wondering then, "when should I use inheritance?" It 1350 | depends on your problem at hand, but this is a decent list of when inheritance 1351 | makes more sense than composition: 1352 | 1353 | 1. Your inheritance represents an "is-a" relationship and not a "has-a" 1354 | relationship (Human->Animal vs. User->UserDetails). 1355 | 2. You can reuse code from the base classes (Humans can move like all animals). 1356 | 3. You want to make global changes to derived classes by changing a base class. 1357 | (Change the caloric expenditure of all animals when they move). 1358 | 1359 | **Bad:** 1360 | 1361 | ```php 1362 | class Employee 1363 | { 1364 | private $name; 1365 | private $email; 1366 | 1367 | public function __construct(string $name, string $email) 1368 | { 1369 | $this->name = $name; 1370 | $this->email = $email; 1371 | } 1372 | 1373 | // ... 1374 | } 1375 | 1376 | // Bad because Employees "have" tax data. 1377 | // EmployeeTaxData is not a type of Employee 1378 | 1379 | class EmployeeTaxData extends Employee 1380 | { 1381 | private $ssn; 1382 | private $salary; 1383 | 1384 | public function __construct(string $name, string $email, string $ssn, string $salary) 1385 | { 1386 | parent::__construct($name, $email); 1387 | 1388 | $this->ssn = $ssn; 1389 | $this->salary = $salary; 1390 | } 1391 | 1392 | // ... 1393 | } 1394 | ``` 1395 | 1396 | **Good:** 1397 | 1398 | ```php 1399 | class EmployeeTaxData 1400 | { 1401 | private $ssn; 1402 | private $salary; 1403 | 1404 | public function __construct(string $ssn, string $salary) 1405 | { 1406 | $this->ssn = $ssn; 1407 | $this->salary = $salary; 1408 | } 1409 | 1410 | // ... 1411 | } 1412 | 1413 | class Employee 1414 | { 1415 | private $name; 1416 | private $email; 1417 | private $taxData; 1418 | 1419 | public function __construct(string $name, string $email) 1420 | { 1421 | $this->name = $name; 1422 | $this->email = $email; 1423 | } 1424 | 1425 | public function setTaxData(string $ssn, string $salary) 1426 | { 1427 | $this->taxData = new EmployeeTaxData($ssn, $salary); 1428 | } 1429 | 1430 | // ... 1431 | } 1432 | ``` 1433 | 1434 | **[⬆ back to top](#မာတိကာ)** 1435 | 1436 | ### Avoid fluent interfaces 1437 | 1438 | A [Fluent interface](https://en.wikipedia.org/wiki/Fluent_interface) is an object 1439 | oriented API that aims to improve the readability of the source code by using 1440 | [Method chaining](https://en.wikipedia.org/wiki/Method_chaining). 1441 | 1442 | While there can be some contexts, frequently builder objects, where this 1443 | pattern reduces the verbosity of the code (for example the [PHPUnit Mock Builder](https://phpunit.de/manual/current/en/test-doubles.html) 1444 | or the [Doctrine Query Builder](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/query-builder.html)), 1445 | more often it comes at some costs: 1446 | 1447 | 1. Breaks [Encapsulation](https://en.wikipedia.org/wiki/Encapsulation_%28object-oriented_programming%29). 1448 | 2. Breaks [Decorators](https://en.wikipedia.org/wiki/Decorator_pattern). 1449 | 3. Is harder to [mock](https://en.wikipedia.org/wiki/Mock_object) in a test suite. 1450 | 4. Makes diffs of commits harder to read. 1451 | 1452 | For more informations you can read the full [blog post](https://ocramius.github.io/blog/fluent-interfaces-are-evil/) 1453 | on this topic written by [Marco Pivetta](https://github.com/Ocramius). 1454 | 1455 | **Bad:** 1456 | 1457 | ```php 1458 | class Car 1459 | { 1460 | private $make = 'Honda'; 1461 | private $model = 'Accord'; 1462 | private $color = 'white'; 1463 | 1464 | public function setMake(string $make): self 1465 | { 1466 | $this->make = $make; 1467 | 1468 | // NOTE: Returning this for chaining 1469 | return $this; 1470 | } 1471 | 1472 | public function setModel(string $model): self 1473 | { 1474 | $this->model = $model; 1475 | 1476 | // NOTE: Returning this for chaining 1477 | return $this; 1478 | } 1479 | 1480 | public function setColor(string $color): self 1481 | { 1482 | $this->color = $color; 1483 | 1484 | // NOTE: Returning this for chaining 1485 | return $this; 1486 | } 1487 | 1488 | public function dump(): void 1489 | { 1490 | var_dump($this->make, $this->model, $this->color); 1491 | } 1492 | } 1493 | 1494 | $car = (new Car()) 1495 | ->setColor('pink') 1496 | ->setMake('Ford') 1497 | ->setModel('F-150') 1498 | ->dump(); 1499 | ``` 1500 | 1501 | **Good:** 1502 | 1503 | ```php 1504 | class Car 1505 | { 1506 | private $make = 'Honda'; 1507 | private $model = 'Accord'; 1508 | private $color = 'white'; 1509 | 1510 | public function setMake(string $make): void 1511 | { 1512 | $this->make = $make; 1513 | } 1514 | 1515 | public function setModel(string $model): void 1516 | { 1517 | $this->model = $model; 1518 | } 1519 | 1520 | public function setColor(string $color): void 1521 | { 1522 | $this->color = $color; 1523 | } 1524 | 1525 | public function dump(): void 1526 | { 1527 | var_dump($this->make, $this->model, $this->color); 1528 | } 1529 | } 1530 | 1531 | $car = new Car(); 1532 | $car->setColor('pink'); 1533 | $car->setMake('Ford'); 1534 | $car->setModel('F-150'); 1535 | $car->dump(); 1536 | ``` 1537 | 1538 | **[⬆ back to top](#မာတိကာ)** 1539 | 1540 | ### Prefer final classes 1541 | 1542 | The `final` should be used whenever possible: 1543 | 1544 | 1. It prevents uncontrolled inheritance chain. 1545 | 2. It encourages [composition](#prefer-composition-over-inheritance). 1546 | 3. It encourages the [Single Responsibility Pattern](#single-responsibility-principle-srp). 1547 | 4. It encourages developers to use your public methods instead of extending the class to get access on protected ones. 1548 | 5. It allows you to change your code without any break of applications that use your class. 1549 | 1550 | The only condition is that your class should implement an interface and no other public methods are defined. 1551 | 1552 | For more informations you can read [the blog post](https://ocramius.github.io/blog/when-to-declare-classes-final/) on this topic written by [Marco Pivetta (Ocramius)](https://ocramius.github.io/). 1553 | 1554 | **Bad:** 1555 | 1556 | ```php 1557 | final class Car 1558 | { 1559 | private $color; 1560 | 1561 | public function __construct($color) 1562 | { 1563 | $this->color = $color; 1564 | } 1565 | 1566 | /** 1567 | * @return string The color of the vehicle 1568 | */ 1569 | public function getColor() 1570 | { 1571 | return $this->color; 1572 | } 1573 | } 1574 | ``` 1575 | 1576 | **Good:** 1577 | 1578 | ```php 1579 | interface Vehicle 1580 | { 1581 | /** 1582 | * @return string The color of the vehicle 1583 | */ 1584 | public function getColor(); 1585 | } 1586 | 1587 | final class Car implements Vehicle 1588 | { 1589 | private $color; 1590 | 1591 | public function __construct($color) 1592 | { 1593 | $this->color = $color; 1594 | } 1595 | 1596 | /** 1597 | * {@inheritdoc} 1598 | */ 1599 | public function getColor() 1600 | { 1601 | return $this->color; 1602 | } 1603 | } 1604 | ``` 1605 | 1606 | **[⬆ back to top](#မာတိကာ)** 1607 | 1608 | ## SOLID 1609 | 1610 | **SOLID** is the mnemonic acronym introduced by Michael Feathers for the first five principles named by Robert Martin, which meant five basic principles of object-oriented programming and design. 1611 | 1612 | * [S: Single Responsibility Principle (SRP)](#single-responsibility-principle-srp) 1613 | * [O: Open/Closed Principle (OCP)](#openclosed-principle-ocp) 1614 | * [L: Liskov Substitution Principle (LSP)](#liskov-substitution-principle-lsp) 1615 | * [I: Interface Segregation Principle (ISP)](#interface-segregation-principle-isp) 1616 | * [D: Dependency Inversion Principle (DIP)](#dependency-inversion-principle-dip) 1617 | 1618 | ### Single Responsibility Principle (SRP) 1619 | 1620 | As stated in Clean Code, "There should never be more than one reason for a class 1621 | to change". It's tempting to jam-pack a class with a lot of functionality, like 1622 | when you can only take one suitcase on your flight. The issue with this is 1623 | that your class won't be conceptually cohesive and it will give it many reasons 1624 | to change. Minimizing the amount of times you need to change a class is important. 1625 | It's important because if too much functionality is in one class and you modify a piece of it, 1626 | it can be difficult to understand how that will affect other dependent modules in 1627 | your codebase. 1628 | 1629 | **Bad:** 1630 | 1631 | ```php 1632 | class UserSettings 1633 | { 1634 | private $user; 1635 | 1636 | public function __construct(User $user) 1637 | { 1638 | $this->user = $user; 1639 | } 1640 | 1641 | public function changeSettings(array $settings): void 1642 | { 1643 | if ($this->verifyCredentials()) { 1644 | // ... 1645 | } 1646 | } 1647 | 1648 | private function verifyCredentials(): bool 1649 | { 1650 | // ... 1651 | } 1652 | } 1653 | ``` 1654 | 1655 | **Good:** 1656 | 1657 | ```php 1658 | class UserAuth 1659 | { 1660 | private $user; 1661 | 1662 | public function __construct(User $user) 1663 | { 1664 | $this->user = $user; 1665 | } 1666 | 1667 | public function verifyCredentials(): bool 1668 | { 1669 | // ... 1670 | } 1671 | } 1672 | 1673 | class UserSettings 1674 | { 1675 | private $user; 1676 | private $auth; 1677 | 1678 | public function __construct(User $user) 1679 | { 1680 | $this->user = $user; 1681 | $this->auth = new UserAuth($user); 1682 | } 1683 | 1684 | public function changeSettings(array $settings): void 1685 | { 1686 | if ($this->auth->verifyCredentials()) { 1687 | // ... 1688 | } 1689 | } 1690 | } 1691 | ``` 1692 | 1693 | **[⬆ back to top](#မာတိကာ)** 1694 | 1695 | ### Open/Closed Principle (OCP) 1696 | 1697 | As stated by Bertrand Meyer, "software entities (classes, modules, functions, 1698 | etc.) should be open for extension, but closed for modification." What does that 1699 | mean though? This principle basically states that you should allow users to 1700 | add new functionalities without changing existing code. 1701 | 1702 | **Bad:** 1703 | 1704 | ```php 1705 | abstract class Adapter 1706 | { 1707 | protected $name; 1708 | 1709 | public function getName(): string 1710 | { 1711 | return $this->name; 1712 | } 1713 | } 1714 | 1715 | class AjaxAdapter extends Adapter 1716 | { 1717 | public function __construct() 1718 | { 1719 | parent::__construct(); 1720 | 1721 | $this->name = 'ajaxAdapter'; 1722 | } 1723 | } 1724 | 1725 | class NodeAdapter extends Adapter 1726 | { 1727 | public function __construct() 1728 | { 1729 | parent::__construct(); 1730 | 1731 | $this->name = 'nodeAdapter'; 1732 | } 1733 | } 1734 | 1735 | class HttpRequester 1736 | { 1737 | private $adapter; 1738 | 1739 | public function __construct(Adapter $adapter) 1740 | { 1741 | $this->adapter = $adapter; 1742 | } 1743 | 1744 | public function fetch(string $url): Promise 1745 | { 1746 | $adapterName = $this->adapter->getName(); 1747 | 1748 | if ($adapterName === 'ajaxAdapter') { 1749 | return $this->makeAjaxCall($url); 1750 | } elseif ($adapterName === 'httpNodeAdapter') { 1751 | return $this->makeHttpCall($url); 1752 | } 1753 | } 1754 | 1755 | private function makeAjaxCall(string $url): Promise 1756 | { 1757 | // request and return promise 1758 | } 1759 | 1760 | private function makeHttpCall(string $url): Promise 1761 | { 1762 | // request and return promise 1763 | } 1764 | } 1765 | ``` 1766 | 1767 | **Good:** 1768 | 1769 | ```php 1770 | interface Adapter 1771 | { 1772 | public function request(string $url): Promise; 1773 | } 1774 | 1775 | class AjaxAdapter implements Adapter 1776 | { 1777 | public function request(string $url): Promise 1778 | { 1779 | // request and return promise 1780 | } 1781 | } 1782 | 1783 | class NodeAdapter implements Adapter 1784 | { 1785 | public function request(string $url): Promise 1786 | { 1787 | // request and return promise 1788 | } 1789 | } 1790 | 1791 | class HttpRequester 1792 | { 1793 | private $adapter; 1794 | 1795 | public function __construct(Adapter $adapter) 1796 | { 1797 | $this->adapter = $adapter; 1798 | } 1799 | 1800 | public function fetch(string $url): Promise 1801 | { 1802 | return $this->adapter->request($url); 1803 | } 1804 | } 1805 | ``` 1806 | 1807 | **[⬆ back to top](#မာတိကာ)** 1808 | 1809 | ### Liskov Substitution Principle (LSP) 1810 | 1811 | This is a scary term for a very simple concept. It's formally defined as "If S 1812 | is a subtype of T, then objects of type T may be replaced with objects of type S 1813 | (i.e., objects of type S may substitute objects of type T) without altering any 1814 | of the desirable properties of that program (correctness, task performed, 1815 | etc.)." That's an even scarier definition. 1816 | 1817 | The best explanation for this is if you have a parent class and a child class, 1818 | then the base class and child class can be used interchangeably without getting 1819 | incorrect results. This might still be confusing, so let's take a look at the 1820 | classic Square-Rectangle example. Mathematically, a square is a rectangle, but 1821 | if you model it using the "is-a" relationship via inheritance, you quickly 1822 | get into trouble. 1823 | 1824 | **Bad:** 1825 | 1826 | ```php 1827 | class Rectangle 1828 | { 1829 | protected $width = 0; 1830 | protected $height = 0; 1831 | 1832 | public function setWidth(int $width): void 1833 | { 1834 | $this->width = $width; 1835 | } 1836 | 1837 | public function setHeight(int $height): void 1838 | { 1839 | $this->height = $height; 1840 | } 1841 | 1842 | public function getArea(): int 1843 | { 1844 | return $this->width * $this->height; 1845 | } 1846 | } 1847 | 1848 | class Square extends Rectangle 1849 | { 1850 | public function setWidth(int $width): void 1851 | { 1852 | $this->width = $this->height = $width; 1853 | } 1854 | 1855 | public function setHeight(int $height): void 1856 | { 1857 | $this->width = $this->height = $height; 1858 | } 1859 | } 1860 | 1861 | function printArea(Rectangle $rectangle): void 1862 | { 1863 | $rectangle->setWidth(4); 1864 | $rectangle->setHeight(5); 1865 | 1866 | // BAD: Will return 25 for Square. Should be 20. 1867 | echo sprintf('%s has area %d.', get_class($rectangle), $rectangle->getArea()).PHP_EOL; 1868 | } 1869 | 1870 | $rectangles = [new Rectangle(), new Square()]; 1871 | 1872 | foreach ($rectangles as $rectangle) { 1873 | printArea($rectangle); 1874 | } 1875 | ``` 1876 | 1877 | **Good:** 1878 | 1879 | The best way is separate the quadrangles and allocation of a more general subtype for both shapes. 1880 | 1881 | Despite the apparent similarity of the square and the rectangle, they are different. 1882 | A square has much in common with a rhombus, and a rectangle with a parallelogram, but they are not subtype. 1883 | A square, a rectangle, a rhombus and a parallelogram are separate shapes with their own properties, albeit similar. 1884 | 1885 | ```php 1886 | interface Shape 1887 | { 1888 | public function getArea(): int; 1889 | } 1890 | 1891 | class Rectangle implements Shape 1892 | { 1893 | private $width = 0; 1894 | private $height = 0; 1895 | 1896 | public function __construct(int $width, int $height) 1897 | { 1898 | $this->width = $width; 1899 | $this->height = $height; 1900 | } 1901 | 1902 | public function getArea(): int 1903 | { 1904 | return $this->width * $this->height; 1905 | } 1906 | } 1907 | 1908 | class Square implements Shape 1909 | { 1910 | private $length = 0; 1911 | 1912 | public function __construct(int $length) 1913 | { 1914 | $this->length = $length; 1915 | } 1916 | 1917 | public function getArea(): int 1918 | { 1919 |        return $this->length ** 2; 1920 |    } 1921 | } 1922 | 1923 | function printArea(Shape $shape): void 1924 | { 1925 | echo sprintf('%s has area %d.', get_class($shape), $shape->getArea()).PHP_EOL; 1926 | } 1927 | 1928 | $shapes = [new Rectangle(4, 5), new Square(5)]; 1929 | 1930 | foreach ($shapes as $shape) { 1931 | printArea($shape); 1932 | } 1933 | ``` 1934 | 1935 | **[⬆ back to top](#မာတိကာ)** 1936 | 1937 | ### Interface Segregation Principle (ISP) 1938 | 1939 | ISP states that "Clients should not be forced to depend upon interfaces that 1940 | they do not use." 1941 | 1942 | A good example to look at that demonstrates this principle is for 1943 | classes that require large settings objects. Not requiring clients to set up 1944 | huge amounts of options is beneficial, because most of the time they won't need 1945 | all of the settings. Making them optional helps prevent having a "fat interface". 1946 | 1947 | **Bad:** 1948 | 1949 | ```php 1950 | interface Employee 1951 | { 1952 | public function work(): void; 1953 | 1954 | public function eat(): void; 1955 | } 1956 | 1957 | class Human implements Employee 1958 | { 1959 | public function work(): void 1960 | { 1961 | // ....working 1962 | } 1963 | 1964 | public function eat(): void 1965 | { 1966 | // ...... eating in lunch break 1967 | } 1968 | } 1969 | 1970 | class Robot implements Employee 1971 | { 1972 | public function work(): void 1973 | { 1974 | //.... working much more 1975 | } 1976 | 1977 | public function eat(): void 1978 | { 1979 | //.... robot can't eat, but it must implement this method 1980 | } 1981 | } 1982 | ``` 1983 | 1984 | **Good:** 1985 | 1986 | Not every worker is an employee, but every employee is a worker. 1987 | 1988 | ```php 1989 | interface Workable 1990 | { 1991 | public function work(): void; 1992 | } 1993 | 1994 | interface Feedable 1995 | { 1996 | public function eat(): void; 1997 | } 1998 | 1999 | interface Employee extends Feedable, Workable 2000 | { 2001 | } 2002 | 2003 | class Human implements Employee 2004 | { 2005 | public function work(): void 2006 | { 2007 | // ....working 2008 | } 2009 | 2010 | public function eat(): void 2011 | { 2012 | //.... eating in lunch break 2013 | } 2014 | } 2015 | 2016 | // robot can only work 2017 | class Robot implements Workable 2018 | { 2019 | public function work(): void 2020 | { 2021 | // ....working 2022 | } 2023 | } 2024 | ``` 2025 | 2026 | **[⬆ back to top](#မာတိကာ)** 2027 | 2028 | ### Dependency Inversion Principle (DIP) 2029 | 2030 | This principle states two essential things: 2031 | 1. High-level modules should not depend on low-level modules. Both should 2032 | depend on abstractions. 2033 | 2. Abstractions should not depend upon details. Details should depend on 2034 | abstractions. 2035 | 2036 | This can be hard to understand at first, but if you've worked with PHP frameworks (like Symfony), you've seen an implementation of this principle in the form of Dependency 2037 | Injection (DI). While they are not identical concepts, DIP keeps high-level 2038 | modules from knowing the details of its low-level modules and setting them up. 2039 | It can accomplish this through DI. A huge benefit of this is that it reduces 2040 | the coupling between modules. Coupling is a very bad development pattern because 2041 | it makes your code hard to refactor. 2042 | 2043 | **Bad:** 2044 | 2045 | ```php 2046 | class Employee 2047 | { 2048 | public function work(): void 2049 | { 2050 | // ....working 2051 | } 2052 | } 2053 | 2054 | class Robot extends Employee 2055 | { 2056 | public function work(): void 2057 | { 2058 | //.... working much more 2059 | } 2060 | } 2061 | 2062 | class Manager 2063 | { 2064 | private $employee; 2065 | 2066 | public function __construct(Employee $employee) 2067 | { 2068 | $this->employee = $employee; 2069 | } 2070 | 2071 | public function manage(): void 2072 | { 2073 | $this->employee->work(); 2074 | } 2075 | } 2076 | ``` 2077 | 2078 | **Good:** 2079 | 2080 | ```php 2081 | interface Employee 2082 | { 2083 | public function work(): void; 2084 | } 2085 | 2086 | class Human implements Employee 2087 | { 2088 | public function work(): void 2089 | { 2090 | // ....working 2091 | } 2092 | } 2093 | 2094 | class Robot implements Employee 2095 | { 2096 | public function work(): void 2097 | { 2098 | //.... working much more 2099 | } 2100 | } 2101 | 2102 | class Manager 2103 | { 2104 | private $employee; 2105 | 2106 | public function __construct(Employee $employee) 2107 | { 2108 | $this->employee = $employee; 2109 | } 2110 | 2111 | public function manage(): void 2112 | { 2113 | $this->employee->work(); 2114 | } 2115 | } 2116 | ``` 2117 | 2118 | **[⬆ back to top](#မာတိကာ)** 2119 | 2120 | ## Don’t repeat yourself (DRY) 2121 | 2122 | Try to observe the [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) principle. 2123 | 2124 | Do your absolute best to avoid duplicate code. Duplicate code is bad because 2125 | it means that there's more than one place to alter something if you need to 2126 | change some logic. 2127 | 2128 | Imagine if you run a restaurant and you keep track of your inventory: all your 2129 | tomatoes, onions, garlic, spices, etc. If you have multiple lists that 2130 | you keep this on, then all have to be updated when you serve a dish with 2131 | tomatoes in them. If you only have one list, there's only one place to update! 2132 | 2133 | Often you have duplicate code because you have two or more slightly 2134 | different things, that share a lot in common, but their differences force you 2135 | to have two or more separate functions that do much of the same things. Removing 2136 | duplicate code means creating an abstraction that can handle this set of different 2137 | things with just one function/module/class. 2138 | 2139 | Getting the abstraction right is critical, that's why you should follow the 2140 | SOLID principles laid out in the [Classes](#classes) section. Bad abstractions can be 2141 | worse than duplicate code, so be careful! Having said this, if you can make 2142 | a good abstraction, do it! Don't repeat yourself, otherwise you'll find yourself 2143 | updating multiple places any time you want to change one thing. 2144 | 2145 | **Bad:** 2146 | 2147 | ```php 2148 | function showDeveloperList(array $developers): void 2149 | { 2150 | foreach ($developers as $developer) { 2151 | $expectedSalary = $developer->calculateExpectedSalary(); 2152 | $experience = $developer->getExperience(); 2153 | $githubLink = $developer->getGithubLink(); 2154 | $data = [ 2155 | $expectedSalary, 2156 | $experience, 2157 | $githubLink 2158 | ]; 2159 | 2160 | render($data); 2161 | } 2162 | } 2163 | 2164 | function showManagerList(array $managers): void 2165 | { 2166 | foreach ($managers as $manager) { 2167 | $expectedSalary = $manager->calculateExpectedSalary(); 2168 | $experience = $manager->getExperience(); 2169 | $githubLink = $manager->getGithubLink(); 2170 | $data = [ 2171 | $expectedSalary, 2172 | $experience, 2173 | $githubLink 2174 | ]; 2175 | 2176 | render($data); 2177 | } 2178 | } 2179 | ``` 2180 | 2181 | **Good:** 2182 | 2183 | ```php 2184 | function showList(array $employees): void 2185 | { 2186 | foreach ($employees as $employee) { 2187 | $expectedSalary = $employee->calculateExpectedSalary(); 2188 | $experience = $employee->getExperience(); 2189 | $githubLink = $employee->getGithubLink(); 2190 | $data = [ 2191 | $expectedSalary, 2192 | $experience, 2193 | $githubLink 2194 | ]; 2195 | 2196 | render($data); 2197 | } 2198 | } 2199 | ``` 2200 | 2201 | **Very good:** 2202 | 2203 | It is better to use a compact version of the code. 2204 | 2205 | ```php 2206 | function showList(array $employees): void 2207 | { 2208 | foreach ($employees as $employee) { 2209 | render([ 2210 | $employee->calculateExpectedSalary(), 2211 | $employee->getExperience(), 2212 | $employee->getGithubLink() 2213 | ]); 2214 | } 2215 | } 2216 | ``` 2217 | 2218 | **[⬆ back to top](#မာတိကာ)** 2219 | 2220 | ## Translations 2221 | 2222 | This is also available in other languages: 2223 | 2224 | * :cn: **Chinese:** 2225 | * [php-cpm/clean-code-php](https://github.com/php-cpm/clean-code-php) 2226 | * :ru: **Russian:** 2227 | * [peter-gribanov/clean-code-php](https://github.com/peter-gribanov/clean-code-php) 2228 | * :es: **Spanish:** 2229 | * [fikoborquez/clean-code-php](https://github.com/fikoborquez/clean-code-php) 2230 | * :brazil: **Portuguese:** 2231 | * [fabioars/clean-code-php](https://github.com/fabioars/clean-code-php) 2232 | * [jeanjar/clean-code-php](https://github.com/jeanjar/clean-code-php/tree/pt-br) 2233 | * :thailand: **Thai:** 2234 | * [panuwizzle/clean-code-php](https://github.com/panuwizzle/clean-code-php) 2235 | * :fr: **French:** 2236 | * [errorname/clean-code-php](https://github.com/errorname/clean-code-php) 2237 | * :vietnam: **Vietnamese** 2238 | * [viethuongdev/clean-code-php](https://github.com/viethuongdev/clean-code-php) 2239 | * :kr: **Korean:** 2240 | * [yujineeee/clean-code-php](https://github.com/yujineeee/clean-code-php) 2241 | 2242 | **[⬆ back to top](#မာတိကာ)** 2243 | --------------------------------------------------------------------------------