From 53e07b44f0a81c7298e0f55fc28c22e0f1f722e1 Mon Sep 17 00:00:00 2001 From: Cassio Santos Date: Tue, 30 May 2017 17:08:55 -0300 Subject: [PATCH 001/210] Remove Object.assign section --- README.md | 47 ----------------------------------------------- 1 file changed, 47 deletions(-) diff --git a/README.md b/README.md index ec2affc0..1a9691df 100644 --- a/README.md +++ b/README.md @@ -433,53 +433,6 @@ function showList($employees) { ``` **[⬆ back to top](#table-of-contents)** -### Set default objects with Object.assign - -**Bad:** -```php -$menuConfig = [ - 'title' => null, - 'body' => 'Bar', - 'buttonText' => null, - 'cancellable' => true, -]; - -function createMenu(&$config) { - $config['title'] = $config['title'] ?: 'Foo'; - $config['body'] = $config['body'] ?: 'Bar'; - $config['buttonText'] = $config['buttonText'] ?: 'Baz'; - $config['cancellable'] = $config['cancellable'] ?: true; -} - -createMenu($menuConfig); -``` - -**Good**: -```php -$menuConfig = [ - 'title' => 'Order', - // User did not include 'body' key - 'buttonText' => 'Send', - 'cancellable' => true, -]; - -function createMenu(&$config) { - $config = array_merge([ - 'title' => 'Foo', - 'body' => 'Bar', - 'buttonText' => 'Baz', - 'cancellable' => true, - ], $config); - - // config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} - // ... -} - -createMenu($menuConfig); -``` -**[⬆ back to top](#table-of-contents)** - - ### Don't use flags as function parameters Flags tell your user that this function does more than one thing. Functions should do one thing. Split out your functions if they are following different code paths From f7a26211c12b430b8f75579df69d28dad1465fa9 Mon Sep 17 00:00:00 2001 From: Piotr Plenik Date: Wed, 31 May 2017 10:21:48 +0200 Subject: [PATCH 002/210] add missing variable --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 30a19a77..d4b4e566 100644 --- a/README.md +++ b/README.md @@ -714,7 +714,7 @@ function newRequestModule($url) { // ... } -$req = new newRequestModule(); +$req = new newRequestModule($requestUrl); inventoryTracker('apples', $req, 'www.inventory-awesome.io'); ``` @@ -725,7 +725,7 @@ function newRequestModule($url) { // ... } -$req = new newRequestModule(); +$req = new newRequestModule($requestUrl); inventoryTracker('apples', $req, 'www.inventory-awesome.io'); ``` **[⬆ back to top](#table-of-contents)** From 58ba5d1b00a252329a319d118f043e2c81c43d83 Mon Sep 17 00:00:00 2001 From: Piotr Plenik Date: Wed, 31 May 2017 10:25:01 +0200 Subject: [PATCH 003/210] remove new in method name --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d4b4e566..eb072e63 100644 --- a/README.md +++ b/README.md @@ -721,11 +721,11 @@ inventoryTracker('apples', $req, 'www.inventory-awesome.io'); **Good**: ```php -function newRequestModule($url) { +function requestModule($url) { // ... } -$req = new newRequestModule($requestUrl); +$req = new requestModule($requestUrl); inventoryTracker('apples', $req, 'www.inventory-awesome.io'); ``` **[⬆ back to top](#table-of-contents)** From d89d27ad8caf905651392df48db97644a3bc2fcc Mon Sep 17 00:00:00 2001 From: vlassiuk Date: Mon, 28 Aug 2017 16:11:02 +0300 Subject: [PATCH 004/210] no need for minus before variable --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eb072e63..985d5cae 100644 --- a/README.md +++ b/README.md @@ -788,7 +788,7 @@ class BankAccount { $bankAccount = new BankAccount(); // Buy shoes... -$bankAccount->withdrawBalance(-$shoesPrice); +$bankAccount->withdrawBalance($shoesPrice); // Get balance $balance = $bankAccount->getBalance(); From 6987a2eb7358f59bbea047c321e75ce38cf598cd Mon Sep 17 00:00:00 2001 From: Galymzhan Abdugalimov Date: Mon, 28 Aug 2017 22:16:17 +0600 Subject: [PATCH 005/210] Fixed array key=>value assign --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eb072e63..1493cfbb 100644 --- a/README.md +++ b/README.md @@ -519,7 +519,7 @@ would be much better to use singleton design pattern and simple set configuratio ```php function config() { return [ - 'foo': 'bar', + 'foo' => 'bar', ] } ``` From bfe98f0d2672de96001c6087f570163a45ac7ecd Mon Sep 17 00:00:00 2001 From: Valerii Kuznetsov Date: Tue, 29 Aug 2017 08:10:09 +1200 Subject: [PATCH 006/210] Update README.md Looks like a bug in good practice. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eb072e63..985d5cae 100644 --- a/README.md +++ b/README.md @@ -788,7 +788,7 @@ class BankAccount { $bankAccount = new BankAccount(); // Buy shoes... -$bankAccount->withdrawBalance(-$shoesPrice); +$bankAccount->withdrawBalance($shoesPrice); // Get balance $balance = $bankAccount->getBalance(); From 8739c9f0a8d41a9d6ad87761523136aba6d01ddd Mon Sep 17 00:00:00 2001 From: vlassiuk Date: Tue, 29 Aug 2017 11:21:12 +0300 Subject: [PATCH 007/210] function, of course --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 985d5cae..ae5e4284 100644 --- a/README.md +++ b/README.md @@ -1168,7 +1168,7 @@ class Manager { /** @var WorkerInterface $worker **/ private $worker; - public void setWorker(WorkerInterface $worker) { + public function setWorker(WorkerInterface $worker) { $this->worker = $worker; } From b4c9b75b21adade1fe31b0e4e061b8dab738b3e9 Mon Sep 17 00:00:00 2001 From: vlassiuk Date: Tue, 29 Aug 2017 11:22:13 +0300 Subject: [PATCH 008/210] function, of course --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ae5e4284..8af5920b 100644 --- a/README.md +++ b/README.md @@ -1202,7 +1202,7 @@ class Worker implements WorkableInterface, FeedableInterface { } class Robot implements WorkableInterface { - public void work() { + public function work() { // ....working } } @@ -1296,11 +1296,11 @@ class Manager { /** @var Worker $worker **/ private $worker; - public void __construct(WorkerInterface $worker) { + public function __construct(WorkerInterface $worker) { $this->worker = $worker; } - public void manage() { + public function manage() { $this->worker->work(); } } From 78803ded9bce2f1d25f0b6ba213ee7fd2a4f48a3 Mon Sep 17 00:00:00 2001 From: vlassiuk Date: Tue, 29 Aug 2017 11:26:26 +0300 Subject: [PATCH 009/210] arrays syntax --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8af5920b..2693a734 100644 --- a/README.md +++ b/README.md @@ -519,7 +519,7 @@ would be much better to use singleton design pattern and simple set configuratio ```php function config() { return [ - 'foo': 'bar', + 'foo' => 'bar', ] } ``` From c806ccd3736e5088f6900aa837d7bff9103b0a32 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 29 Aug 2017 18:04:53 +0300 Subject: [PATCH 010/210] decrease dependence on regex --- README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index eb072e63..f51262e1 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,10 @@ preg_match($cityZipCodeRegex, $address, $matches); saveCityZipCode($matches[1], $matches[2]); ``` -**Good**: +**Not bad**: + +It's better, but we are still heavily dependent on regex. + ```php $address = 'One Infinite Loop, Cupertino 95014'; $cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/'; @@ -98,6 +101,17 @@ preg_match($cityZipCodeRegex, $address, $matches); list(, $city, $zipCode) = $matchers; saveCityZipCode($city, $zipCode); ``` + +**Good**: + +Decrease dependence on regex by naming subpatterns. +```php +$address = 'One Infinite Loop, Cupertino 95014'; +$cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(?.+?)\s*(?\d{5})?$/'; +preg_match($cityZipCodeRegex, $address, $matches); + +saveCityZipCode($matchers['city'], $matchers['zipCode']); +``` **[⬆ back to top](#table-of-contents)** ### Avoid Mental Mapping From 08d73d655efcbab0ae789326fc016ffacc2fdaa2 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 29 Aug 2017 18:18:18 +0300 Subject: [PATCH 011/210] correct use foreach --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index eb072e63..a852edf3 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ Explicit is better than implicit. ```php $l = ['Austin', 'New York', 'San Francisco']; -foreach($i=0; $i Date: Tue, 29 Aug 2017 18:25:37 +0300 Subject: [PATCH 012/210] Correct declaration MenuConfig class --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index eb072e63..92b02eec 100644 --- a/README.md +++ b/README.md @@ -208,7 +208,8 @@ function createMenu($title, $body, $buttonText, $cancellable) { **Good**: ```php -class menuConfig() { +class MenuConfig +{ public $title; public $body; public $buttonText; From ee26d57559b30ba8136fa99303607bf1514d1d48 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 29 Aug 2017 19:23:58 +0300 Subject: [PATCH 013/210] Optimize showList function --- README.md | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index eb072e63..99c20102 100644 --- a/README.md +++ b/README.md @@ -418,16 +418,11 @@ function showManagerList($managers) { ```php function showList($employees) { foreach($employees as $employe) { - $expectedSalary = $employe->calculateExpectedSalary(); - $experience = $employe->getExperience(); - $githubLink = $employe->getGithubLink(); - $data = [ - $expectedSalary, - $experience, - $githubLink - ]; - - render($data); + render([ + $employe->calculateExpectedSalary(), + $employe->getExperience(), + $employe->getGithubLink() + ]); } } ``` From f6e0fe38bcb61bfd3ff5d84133969479df89c79d Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 29 Aug 2017 19:41:38 +0300 Subject: [PATCH 014/210] Singleton is a anti-pattern --- README.md | 59 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index eb072e63..24da54d1 100644 --- a/README.md +++ b/README.md @@ -512,35 +512,64 @@ Polluting globals is a bad practice in very languages because you could clash wi library and the user of your API would be none-the-wiser until they get an exception in production. Let's think about an example: what if you wanted to have configuration array. You could write global function like `config()`, but it could clash with another library -that tried to do the same thing. This is why it -would be much better to use singleton design pattern and simple set configuration. +that tried to do the same thing. **Bad:** ```php -function config() { +function config() +{ return [ - 'foo': 'bar', + 'foo' => 'bar', ] } ``` **Good:** + +Create configuration file + ```php -class Configuration { - private static $instance; - private function __construct($configuration) {/* */} - public static function getInstance() { - if (self::$instance === null) { - self::$instance = new Configuration(); - } - return self::$instance; +// config.php +return [ + 'foo' => 'bar', +]; +``` + +Or a [YAML](https://en.wikipedia.org/wiki/YAML) configuration file + +```php +// config.yml +configuration: + foo: 'bar' +``` + +Or something else + +```php +class Configuration +{ + private $configuration = []; + + public function __construct(array $configuration) + { + $this->configuration = $configuration; + } + + public function get($key) + { + return isset($this->configuration[$key]) ? $this->configuration[$key] : null; } - public function get($key) {/* */} - public function getAll() {/* */} } +``` + +Load configuration from file and create instance of `Configuration` class -$singleton = Configuration::getInstance(); +```php +$configuration = new Configuration($configuration); ``` + +And now you must use instance of `Configuration` in your application. + **[⬆ back to top](#table-of-contents)** ### Encapsulate conditionals From 0fab26d206e44c1f446638608a815e8df73d7c13 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Wed, 30 Aug 2017 00:21:31 +0300 Subject: [PATCH 015/210] use default arguments in PHP 7+ --- README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index eb072e63..6307b222 100644 --- a/README.md +++ b/README.md @@ -171,19 +171,23 @@ function paintCar(&$car) { ### Use default arguments instead of short circuiting or conditionals -**Bad:** +**Not bad:** + ```php -function createMicrobrewery($name = null) { - $breweryName = $name ?: 'Hipster Brew Co.'; +function createMicrobrewery($name = null) +{ +    $breweryName = $name ?: 'Hipster Brew Co.'; // ... } ``` -**Good**: +**Good for PHP 7+**: + ```php -function createMicrobrewery($breweryName = 'Hipster Brew Co.') { - // ... +function createMicrobrewery(string $breweryName = 'Hipster Brew Co.') +{ +    // ... } ``` From 47d6f95f43fc0150b192a6179baf1858a953e6b0 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Wed, 30 Aug 2017 08:41:34 +0300 Subject: [PATCH 016/210] Add origin bad example --- README.md | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6307b222..7958a1b4 100644 --- a/README.md +++ b/README.md @@ -171,26 +171,40 @@ function paintCar(&$car) { ### Use default arguments instead of short circuiting or conditionals +**Not good:** + +This is not good because `$breweryName` can be `NULL`. + +```php +function createMicrobrewery($breweryName = 'Hipster Brew Co.') +{ +    // ... +} +``` + **Not bad:** +This opinion is understandable than the previous version, but it better controls the value of the variable. + ```php function createMicrobrewery($name = null) {    $breweryName = $name ?: 'Hipster Brew Co.'; // ... } - ``` -**Good for PHP 7+**: +**Good**: + +If you support only PHP 7+, then you can use [type hinting](http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration) and be sure that the `$breweryName` will not be `NULL`. ```php function createMicrobrewery(string $breweryName = 'Hipster Brew Co.') {    // ... } - ``` + **[⬆ back to top](#table-of-contents)** ## **Functions** ### Function arguments (2 or fewer ideally) From 3c0c05e032c3194ec87a5dce46e637d078a52d54 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Wed, 30 Aug 2017 10:45:54 +0300 Subject: [PATCH 017/210] add bad singleton example and remove YAML config --- README.md | 45 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 24da54d1..e0299533 100644 --- a/README.md +++ b/README.md @@ -515,6 +515,7 @@ You could write global function like `config()`, but it could clash with another that tried to do the same thing. **Bad:** + ```php function config() { @@ -524,9 +525,41 @@ function config() } ``` +**Bad too:** + +Singleton is a [anti-pattern](https://en.wikipedia.org/wiki/Singleton_pattern). + +```php +class Configuration +{ + private static $instance; + + private function __construct($configuration) + { + // ... + } + + public static function getInstance() + { + if (self::$instance === null) { + self::$instance = new Configuration(); + } + + return self::$instance; + } + + public function get($key) + { + // ... + } +} + +$singleton = Configuration::getInstance(); +``` + **Good:** -Create configuration file +Create PHP configuration file or something else ```php // config.php @@ -535,16 +568,6 @@ return [ ]; ``` -Or a [YAML](https://en.wikipedia.org/wiki/YAML) configuration file - -```php -// config.yml -configuration: - foo: 'bar' -``` - -Or something else - ```php class Configuration { From 413895b25cd8e0d3caf91591c9c857ec0e64d548 Mon Sep 17 00:00:00 2001 From: Sergiy Litvinchuk Date: Wed, 30 Aug 2017 11:08:51 +0300 Subject: [PATCH 018/210] make verifyCredentials public after SRP splitting --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5dc5faf3..d1c30f8f 100644 --- a/README.md +++ b/README.md @@ -886,7 +886,7 @@ class UserAuth { $this->user = user; } - protected function verifyCredentials() { + public function verifyCredentials() { // ... } } From 1ddff50a9b3bbf01ae02658499d9c2615265c22d Mon Sep 17 00:00:00 2001 From: Pavel Date: Wed, 30 Aug 2017 14:44:26 +0500 Subject: [PATCH 019/210] Correction of typos --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5dc5faf3..e2f0e9e1 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ $address = 'One Infinite Loop, Cupertino 95014'; $cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(?.+?)\s*(?\d{5})?$/'; preg_match($cityZipCodeRegex, $address, $matches); -saveCityZipCode($matchers['city'], $matchers['zipCode']); +saveCityZipCode($matches['city'], $matches['zipCode']); ``` **[⬆ back to top](#table-of-contents)** @@ -124,7 +124,7 @@ $l = ['Austin', 'New York', 'San Francisco']; for ($i = 0; $i < count($l); $i++) { $li = $l[$i]; - oStuff(); + doStuff(); doSomeOtherStuff(); // ... // ... @@ -925,7 +925,7 @@ abstract class Adapter { class AjaxAdapter extends Adapter { public function __construct() { - parent::__construct(); + parent::__construct(); $this->name = 'ajaxAdapter'; } } @@ -1119,7 +1119,7 @@ class Square extends Shape { } public function getArea() { - return $this->length * $this->length; + return pow($this->length, 2); } } From 19b95ac77d9a726b52fc04d4e48671d3f7195d62 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Wed, 30 Aug 2017 18:47:04 +0300 Subject: [PATCH 020/210] add section: Don't use a Singleton pattern --- README.md | 82 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index e0299533..95e22443 100644 --- a/README.md +++ b/README.md @@ -525,16 +525,55 @@ function config() } ``` -**Bad too:** +**Good:** -Singleton is a [anti-pattern](https://en.wikipedia.org/wiki/Singleton_pattern). +Create PHP configuration file or something else + +```php +// config.php +return [ + 'foo' => 'bar', +]; +``` ```php class Configuration +{ + private $configuration = []; + + public function __construct(array $configuration) + { + $this->configuration = $configuration; + } + + public function get($key) + { + return isset($this->configuration[$key]) ? $this->configuration[$key] : null; + } +} +``` + +Load configuration from file and create instance of `Configuration` class + +```php +$configuration = new Configuration($configuration); +``` + +And now you must use instance of `Configuration` in your application. + +**[⬆ back to top](#table-of-contents)** + +### Don't use a Singleton pattern +Singleton is a [anti-pattern](https://en.wikipedia.org/wiki/Singleton_pattern). + +**Bad:** + +```php +class DBConnection { private static $instance; - private function __construct($configuration) + private function __construct($dsn) { // ... } @@ -542,56 +581,39 @@ class Configuration public static function getInstance() { if (self::$instance === null) { - self::$instance = new Configuration(); + self::$instance = new self(); } return self::$instance; } - public function get($key) - { - // ... - } + // ... } -$singleton = Configuration::getInstance(); +$singleton = DBConnection::getInstance(); ``` **Good:** -Create PHP configuration file or something else - ```php -// config.php -return [ - 'foo' => 'bar', -]; -``` - -```php -class Configuration +class DBConnection { - private $configuration = []; - - public function __construct(array $configuration) + public function __construct(array $dsn) { - $this->configuration = $configuration; + // ... } - public function get($key) - { - return isset($this->configuration[$key]) ? $this->configuration[$key] : null; - } + // ... } ``` -Load configuration from file and create instance of `Configuration` class +Create instance of `DBConnection` class and configure it with [DSN](http://php.net/manual/en/pdo.construct.php#refsect1-pdo.construct-parameters). ```php -$configuration = new Configuration($configuration); +$connection = new DBConnection($dsn); ``` -And now you must use instance of `Configuration` in your application. +And now you must use instance of `DBConnection` in your application. **[⬆ back to top](#table-of-contents)** From dd259f3135dae77fde97982e6ff11f8a5995befa Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Wed, 30 Aug 2017 19:14:00 +0300 Subject: [PATCH 021/210] correct example of encapsulate conditionals --- README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4455b29c..6b348150 100644 --- a/README.md +++ b/README.md @@ -559,22 +559,21 @@ $singleton = Configuration::getInstance(); ### Encapsulate conditionals **Bad:** + ```php -if ($fsm->state === 'fetching' && is_empty($listNode)) { +if ($article->state === 'published') { // ... } ``` **Good**: -```php -function shouldShowSpinner($fsm, $listNode) { - return $fsm->state === 'fetching' && is_empty($listNode); -} -if (shouldShowSpinner($fsmInstance, $listNodeInstance)) { - // ... +```php +if ($article->isPublished()) { + // ... } ``` + **[⬆ back to top](#table-of-contents)** ### Avoid negative conditionals From 03a82e35c2a9bb0fb3389b82f22c5689517ef5a7 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Wed, 30 Aug 2017 19:18:03 +0300 Subject: [PATCH 022/210] correct examples of don't add unneeded context --- README.md | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 4455b29c..c0525bec 100644 --- a/README.md +++ b/README.md @@ -151,34 +151,36 @@ foreach ($locations as $location) { ### Don't add unneeded context + If your class/object name tells you something, don't repeat that in your variable name. **Bad:** + ```php -$car = [ - 'carMake' => 'Honda', - 'carModel' => 'Accord', - 'carColor' => 'Blue', -]; +class Car +{ + public $carMake; + public $carModel; + public $carColor; -function paintCar(&$car) { - $car['carColor'] = 'Red'; + //... } ``` **Good**: + ```php -$car = [ - 'make' => 'Honda', - 'model' => 'Accord', - 'color' => 'Blue', -]; +class Car +{ + public $make; + public $model; + public $color; -function paintCar(&$car) { - $car['color'] = 'Red'; + //... } ``` + **[⬆ back to top](#table-of-contents)** ### Use default arguments instead of short circuiting or conditionals From 825836b20197441467ec8300a409515844fe0273 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Wed, 30 Aug 2017 19:25:01 +0300 Subject: [PATCH 023/210] optimize example of avoid type-checking 1 --- README.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4455b29c..3830da5e 100644 --- a/README.md +++ b/README.md @@ -659,28 +659,34 @@ class Cessna extends Airplane { **[⬆ back to top](#table-of-contents)** ### Avoid type-checking (part 1) + PHP is untyped, which means your functions can take any type of argument. Sometimes you are bitten by this freedom and it becomes tempting to do type-checking in your functions. There are many ways to avoid having to do this. The first thing to consider is consistent APIs. **Bad:** + ```php -function travelToTexas($vehicle) { +function travelToTexas($vehicle) +{ if ($vehicle instanceof Bicycle) { - $vehicle->peddle($this->currentLocation, new Location('texas')); - } else if ($vehicle instanceof Car) { - $vehicle->drive($this->currentLocation, new Location('texas')); + $vehicle->peddleTo(new Location('texas')); + } elseif ($vehicle instanceof Car) { + $vehicle->driveTo(new Location('texas')); } } ``` **Good**: + ```php -function travelToTexas($vehicle) { - $vehicle->move($this->currentLocation, new Location('texas')); +function travelToTexas(Traveler $vehicle) +{ + $vehicle->travelTo(new Location('texas')); } ``` + **[⬆ back to top](#table-of-contents)** ### Avoid type-checking (part 2) From 783fe13451dcf77513dd4d45549251e791797761 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Wed, 30 Aug 2017 19:40:07 +0300 Subject: [PATCH 024/210] Avoid type-checking (part 2) require PHP 7+ --- README.md | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 4455b29c..38290b09 100644 --- a/README.md +++ b/README.md @@ -684,32 +684,39 @@ function travelToTexas($vehicle) { **[⬆ back to top](#table-of-contents)** ### Avoid type-checking (part 2) + If you are working with basic primitive values like strings, integers, and arrays, -and you can't use polymorphism but you still feel the need to type-check, -you should consider type declaration or strict mode. It provides you with static -typing on top of standard PHP syntax. The problem with manually type-checking is -that doing it well requires so much extra verbiage that the faux "type-safety" -you get doesn't make up for the lost readability. Keep your PHP clean, write good -tests, and have good code reviews. Otherwise, do all of that but with PHP strict -type declaration or strict mode. +and you use PHP 7+ and you can't use polymorphism but you still feel the need to +type-check, you should consider +[type declaration](http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration) +or strict mode. It provides you with static typing on top of standard PHP syntax. +The problem with manually type-checking is that doing it well requires so much +extra verbiage that the faux "type-safety" you get doesn't make up for the lost +readability. Keep your PHP clean, write good tests, and have good code reviews. +Otherwise, do all of that but with PHP strict type declaration or strict mode. **Bad:** + ```php -function combine($val1, $val2) { - if (is_numeric($val1) && is_numeric($val2)) { - return $val1 + $val2; +function combine($val1, $val2) +{ + if (!is_numeric($val1) || !is_numeric($val2)) { + throw new \Exception('Must be of type Number'); } - - throw new \Exception('Must be of type Number'); + + return $val1 + $val2; } ``` **Good**: + ```php -function combine(int $val1, int $val2) { +function combine(int $val1, int $val2) +{ return $val1 + $val2; } ``` + **[⬆ back to top](#table-of-contents)** ### Remove dead code From eccde56b6ba6aaefeeb639da21087b5445b7c880 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Wed, 30 Aug 2017 21:54:23 +0300 Subject: [PATCH 025/210] use compact version of code --- README.md | 45 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 99c20102..4527b7a6 100644 --- a/README.md +++ b/README.md @@ -322,7 +322,8 @@ function parseBetterJSAlternative($code) { } ``` -**Good**: +**Good:** + ```php function tokenize($code) { $regexes = [ @@ -382,9 +383,11 @@ a good abstraction, do it! Don't repeat yourself, otherwise you'll find yourself updating multiple places anytime you want to change one thing. **Bad:** + ```php -function showDeveloperList($developers) { - foreach($developers as $developer) { +function showDeveloperList($developers) +{ +    foreach ($developers as $developer) { $expectedSalary = $developer->calculateExpectedSalary(); $experience = $developer->getExperience(); $githubLink = $developer->getGithubLink(); @@ -398,8 +401,9 @@ function showDeveloperList($developers) { } } -function showManagerList($managers) { - foreach($managers as $manager) { +function showManagerList($managers) +{ +    foreach ($managers as $manager) { $expectedSalary = $manager->calculateExpectedSalary(); $experience = $manager->getExperience(); $githubLink = $manager->getGithubLink(); @@ -414,10 +418,34 @@ function showManagerList($managers) { } ``` -**Good**: +**Good:** + +```php +function showList($employees) +{ +    foreach ($employees as $employe) { +        $expectedSalary = $employe->calculateExpectedSalary(); +        $experience = $employe->getExperience(); +        $githubLink = $employe->getGithubLink(); + $data = [ + $expectedSalary, + $experience, + $githubLink + ]; + + render($data); + } +} +``` + +**Very good:** + +It is better to use a compact version of the code. + ```php -function showList($employees) { - foreach($employees as $employe) { +function showList($employees) +{ +    foreach ($employees as $employe) { render([ $employe->calculateExpectedSalary(), $employe->getExperience(), @@ -426,6 +454,7 @@ function showList($employees) { } } ``` + **[⬆ back to top](#table-of-contents)** ### Don't use flags as function parameters From 465ca94b43af0d8b7e10bb26606d45aeb8f8f057 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 31 Aug 2017 13:54:33 +0300 Subject: [PATCH 026/210] add DRY principle --- README.md | 198 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 101 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index c8e650cc..6372942d 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ 3. [L: Liskov Substitution Principle (LSP)](#liskov-substitution-principle-lsp) 4. [I: Interface Segregation Principle (ISP)](#interface-segregation-principle-isp) 5. [D: Dependency Inversion Principle (DIP)](#dependency-inversion-principle-dip) + 6. [Don’t repeat yourself (DRY)](#dont-repeat-yourself-dry) ## Introduction @@ -375,103 +376,6 @@ function parseBetterJSAlternative($code) { ``` **[⬆ back to top](#table-of-contents)** -### Remove duplicate code -Do your absolute best to avoid duplicate code. Duplicate code is bad because -it means that there's more than one place to alter something if you need to -change some logic. - -Imagine if you run a restaurant and you keep track of your inventory: all your -tomatoes, onions, garlic, spices, etc. If you have multiple lists that -you keep this on, then all have to be updated when you serve a dish with -tomatoes in them. If you only have one list, there's only one place to update! - -Oftentimes you have duplicate code because you have two or more slightly -different things, that share a lot in common, but their differences force you -to have two or more separate functions that do much of the same things. Removing -duplicate code means creating an abstraction that can handle this set of different -things with just one function/module/class. - -Getting the abstraction right is critical, that's why you should follow the -SOLID principles laid out in the *Classes* section. Bad abstractions can be -worse than duplicate code, so be careful! Having said this, if you can make -a good abstraction, do it! Don't repeat yourself, otherwise you'll find yourself -updating multiple places anytime you want to change one thing. - -**Bad:** - -```php -function showDeveloperList($developers) -{ -    foreach ($developers as $developer) { - $expectedSalary = $developer->calculateExpectedSalary(); - $experience = $developer->getExperience(); - $githubLink = $developer->getGithubLink(); - $data = [ - $expectedSalary, - $experience, - $githubLink - ]; - - render($data); - } -} - -function showManagerList($managers) -{ -    foreach ($managers as $manager) { - $expectedSalary = $manager->calculateExpectedSalary(); - $experience = $manager->getExperience(); - $githubLink = $manager->getGithubLink(); - $data = [ - $expectedSalary, - $experience, - $githubLink - ]; - - render($data); - } -} -``` - -**Good:** - -```php -function showList($employees) -{ -    foreach ($employees as $employe) { -        $expectedSalary = $employe->calculateExpectedSalary(); -        $experience = $employe->getExperience(); -        $githubLink = $employe->getGithubLink(); - $data = [ - $expectedSalary, - $experience, - $githubLink - ]; - - render($data); - } -} -``` - -**Very good:** - -It is better to use a compact version of the code. - -```php -function showList($employees) -{ -    foreach ($employees as $employe) { - render([ - $employe->calculateExpectedSalary(), - $employe->getExperience(), - $employe->getGithubLink() - ]); - } -} -``` - -**[⬆ back to top](#table-of-contents)** - ### Don't use flags as function parameters Flags tell your user that this function does more than one thing. Functions should do one thing. Split out your functions if they are following different code paths @@ -1516,3 +1420,103 @@ class Employee { } ``` **[⬆ back to top](#table-of-contents)** + +## Don’t repeat yourself (DRY) + +Try to observe the [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) principle. + +Do your absolute best to avoid duplicate code. Duplicate code is bad because +it means that there's more than one place to alter something if you need to +change some logic. + +Imagine if you run a restaurant and you keep track of your inventory: all your +tomatoes, onions, garlic, spices, etc. If you have multiple lists that +you keep this on, then all have to be updated when you serve a dish with +tomatoes in them. If you only have one list, there's only one place to update! + +Oftentimes you have duplicate code because you have two or more slightly +different things, that share a lot in common, but their differences force you +to have two or more separate functions that do much of the same things. Removing +duplicate code means creating an abstraction that can handle this set of different +things with just one function/module/class. + +Getting the abstraction right is critical, that's why you should follow the +SOLID principles laid out in the [Classes](#classes) section. Bad abstractions can be +worse than duplicate code, so be careful! Having said this, if you can make +a good abstraction, do it! Don't repeat yourself, otherwise you'll find yourself +updating multiple places anytime you want to change one thing. + +**Bad:** + +```php +function showDeveloperList($developers) +{ +    foreach ($developers as $developer) { + $expectedSalary = $developer->calculateExpectedSalary(); + $experience = $developer->getExperience(); + $githubLink = $developer->getGithubLink(); + $data = [ + $expectedSalary, + $experience, + $githubLink + ]; + + render($data); + } +} + +function showManagerList($managers) +{ +    foreach ($managers as $manager) { + $expectedSalary = $manager->calculateExpectedSalary(); + $experience = $manager->getExperience(); + $githubLink = $manager->getGithubLink(); + $data = [ + $expectedSalary, + $experience, + $githubLink + ]; + + render($data); + } +} +``` + +**Good:** + +```php +function showList($employees) +{ +    foreach ($employees as $employe) { +        $expectedSalary = $employe->calculateExpectedSalary(); +        $experience = $employe->getExperience(); +        $githubLink = $employe->getGithubLink(); + $data = [ + $expectedSalary, + $experience, + $githubLink + ]; + + render($data); + } +} +``` + +**Very good:** + +It is better to use a compact version of the code. + +```php +function showList($employees) +{ +    foreach ($employees as $employe) { + render([ + $employe->calculateExpectedSalary(), + $employe->getExperience(), + $employe->getGithubLink() + ]); + } +} +``` + +**[⬆ back to top](#table-of-contents)** From 3c9845324fad47c0a4b97377eceec31745b5e30b Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 31 Aug 2017 14:10:18 +0300 Subject: [PATCH 027/210] fix errors and correct CS --- README.md | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index c8e650cc..e24c845e 100644 --- a/README.md +++ b/README.md @@ -307,31 +307,34 @@ addMonthToDate(1, $date); **[⬆ back to top](#table-of-contents)** ### Functions should only be one level of abstraction + When you have more than one level of abstraction your function is usually doing too much. Splitting up functions leads to reusability and easier testing. **Bad:** + ```php -function parseBetterJSAlternative($code) { +function parseBetterJSAlternative($code) +{ $regexes = [ // ... ]; $statements = split(' ', $code); $tokens = []; - foreach($regexes as $regex) { - foreach($statements as $statement) { + foreach ($regexes as $regex) { + foreach ($statements as $statement) { // ... } } $ast = []; - foreach($tokens as $token) { + foreach ($tokens as $token) { // lex... } - foreach($ast as $node) { + foreach ($ast as $node) { // parse... } } @@ -340,39 +343,43 @@ function parseBetterJSAlternative($code) { **Good:** ```php -function tokenize($code) { +function tokenize($code) +{ $regexes = [ // ... ]; $statements = split(' ', $code); $tokens = []; - foreach($regexes as $regex) { - foreach($statements as $statement) { + foreach ($regexes as $regex) { + foreach ($statements as $statement) { $tokens[] = /* ... */; - }); - }); + } + } return $tokens; } -function lexer($tokens) { +function lexer($tokens) +{ $ast = []; - foreach($tokens as $token) { + foreach ($tokens as $token) { $ast[] = /* ... */; - }); + } return $ast; } -function parseBetterJSAlternative($code) { +function parseBetterJSAlternative($code) +{ $tokens = tokenize($code); $ast = lexer($tokens); - foreach($ast as $node) { + foreach ($ast as $node) { // parse... - }); + } } ``` + **[⬆ back to top](#table-of-contents)** ### Remove duplicate code From a0d6ea9770419b16e71d5d09dd213e839c80c118 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 31 Aug 2017 14:40:42 +0300 Subject: [PATCH 028/210] move out dependencies --- README.md | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e24c845e..de82494e 100644 --- a/README.md +++ b/README.md @@ -340,7 +340,9 @@ function parseBetterJSAlternative($code) } ``` -**Good:** +**Bad too:** + +We have carried out some of the functionality, but the `parseBetterJSAlternative()` function is still very complex and not testable. ```php function tokenize($code) @@ -380,6 +382,72 @@ function parseBetterJSAlternative($code) } ``` +**Good:** + +The best solution is move out the dependencies of `parseBetterJSAlternative()` function. + +```php +class Tokenizer +{ + public function tokenize($code) + { + $regexes = [ + // ... + ]; + + $statements = split(' ', $code); + $tokens = []; + foreach ($regexes as $regex) { + foreach ($statements as $statement) { + $tokens[] = /* ... */; + } + } + + return $tokens; + } +} +``` + +```php +class Lexer +{ + public function lexify($tokens) + { + $ast = []; + foreach ($tokens as $token) { + $ast[] = /* ... */; + } + + return $ast; + } +} +``` + +```php +class BetterJSAlternative +{ + private $tokenizer; + private $lexer; + + public function __construct(Tokenizer $tokenizer, Lexer $lexer) + { + $this->tokenizer = $tokenizer; + $this->lexer = $lexer; + } + + public function parse($code) + { + $tokens = $this->tokenizer->tokenize($code); + $ast = $this->lexer->lexify($tokens); + foreach ($ast as $node) { + // parse... + } + } +} +``` + +Now we can mock the dependencies and test only the work of method `BetterJSAlternative::parse()`. + **[⬆ back to top](#table-of-contents)** ### Remove duplicate code From c1e704ce69bf40de04ac9440fad61307f5872afc Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 31 Aug 2017 16:46:29 +0300 Subject: [PATCH 029/210] correct CS --- README.md | 74 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index c8e650cc..7e3b5799 100644 --- a/README.md +++ b/README.md @@ -942,98 +942,128 @@ class UserSettings { **[⬆ back to top](#table-of-contents)** ### Open/Closed Principle (OCP) + As stated by Bertrand Meyer, "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification." What does that mean though? This principle basically states that you should allow users to add new functionalities without changing existing code. **Bad:** + ```php -abstract class Adapter { +abstract class Adapter +{ protected $name; - public function getName() { + + public function getName() + { return $this->name; } } -class AjaxAdapter extends Adapter { - public function __construct() { +class AjaxAdapter extends Adapter +{ + public function __construct() + { parent::__construct(); $this->name = 'ajaxAdapter'; } } -class NodeAdapter extends Adapter { - public function __construct() { +class NodeAdapter extends Adapter +{ + public function __construct() + { parent::__construct(); $this->name = 'nodeAdapter'; } } -class HttpRequester { +class HttpRequester +{ private $adapter; - public function __construct($adapter) { + + public function __construct($adapter) + { $this->adapter = $adapter; } - public function fetch($url) { + public function fetch($url) + { $adapterName = $this->adapter->getName(); + if ($adapterName === 'ajaxAdapter') { return $this->makeAjaxCall($url); - } else if ($adapterName === 'httpNodeAdapter') { + } elseif ($adapterName === 'httpNodeAdapter') { return $this->makeHttpCall($url); } } - protected function makeAjaxCall($url) { + protected function makeAjaxCall($url) + { // request and return promise } - protected function makeHttpCall($url) { + protected function makeHttpCall($url) + { // request and return promise } } ``` **Good:** + ```php -abstract class Adapter { +abstract class Adapter +{ abstract protected function getName(); + abstract public function request($url); } -class AjaxAdapter extends Adapter { - protected function getName() { +class AjaxAdapter extends Adapter +{ + protected function getName() + { return 'ajaxAdapter'; } - public function request($url) { + public function request($url) + { // request and return promise } } -class NodeAdapter extends Adapter { - protected function getName() { +class NodeAdapter extends Adapter +{ + protected function getName() + { return 'nodeAdapter'; } - public function request($url) { + public function request($url) + { // request and return promise } } -class HttpRequester { +class HttpRequester +{ private $adapter; - public function __construct(Adapter $adapter) { + + public function __construct(Adapter $adapter) + { $this->adapter = $adapter; } - public function fetch($url) { + public function fetch($url) + { return $this->adapter->request($url); } } ``` + **[⬆ back to top](#table-of-contents)** From 14d4efba2064182c2c394dd40ac660629f2a4e84 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 31 Aug 2017 16:49:16 +0300 Subject: [PATCH 030/210] not use abstract class as interface --- README.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7e3b5799..fce33919 100644 --- a/README.md +++ b/README.md @@ -1014,14 +1014,12 @@ class HttpRequester **Good:** ```php -abstract class Adapter +interface Adapter { - abstract protected function getName(); - - abstract public function request($url); + public function request($url); } -class AjaxAdapter extends Adapter +class AjaxAdapter implements Adapter { protected function getName() { @@ -1034,7 +1032,7 @@ class AjaxAdapter extends Adapter } } -class NodeAdapter extends Adapter +class NodeAdapter implements Adapter { protected function getName() { @@ -1061,13 +1059,12 @@ class HttpRequester return $this->adapter->request($url); } } - ``` **[⬆ back to top](#table-of-contents)** - ### Liskov Substitution Principle (LSP) + This is a scary term for a very simple concept. It's formally defined as "If S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., objects of type S may substitute objects of type T) without altering any From b17ca6eecab2cf87f6cc9854f9a2849589fefcc9 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 31 Aug 2017 16:49:56 +0300 Subject: [PATCH 031/210] remove not used methods --- README.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/README.md b/README.md index fce33919..2a1955d8 100644 --- a/README.md +++ b/README.md @@ -1021,11 +1021,6 @@ interface Adapter class AjaxAdapter implements Adapter { - protected function getName() - { - return 'ajaxAdapter'; - } - public function request($url) { // request and return promise @@ -1034,11 +1029,6 @@ class AjaxAdapter implements Adapter class NodeAdapter implements Adapter { - protected function getName() - { - return 'nodeAdapter'; - } - public function request($url) { // request and return promise From e15b99fb59349c237a8c47a4f89caf3e1b8fbb14 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 31 Aug 2017 16:59:29 +0300 Subject: [PATCH 032/210] correct CS --- README.md | 83 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index c8e650cc..266f8176 100644 --- a/README.md +++ b/README.md @@ -1176,6 +1176,7 @@ renderLargeRectangles($shapes); **[⬆ back to top](#table-of-contents)** ### Interface Segregation Principle (ISP) + ISP states that "Clients should not be forced to depend upon interfaces that they do not use." @@ -1185,97 +1186,123 @@ huge amounts of options is beneficial, because most of the time they won't need all of the settings. Making them optional helps prevent having a "fat interface". **Bad:** + ```php -interface WorkerInterface { +interface WorkerInterface +{ public function work(); public function eat(); } -class Worker implements WorkerInterface { - public function work() { +class Worker implements WorkerInterface +{ + public function work() + { // ....working } - public function eat() { + + public function eat() + { // ...... eating in launch break } } -class SuperWorker implements WorkerInterface { - public function work() { +class SuperWorker implements WorkerInterface +{ + public function work() + { //.... working much more } - public function eat() { + public function eat() + { //.... eating in launch break } } -class Manager { - /** @var WorkerInterface $worker **/ - private $worker; - - public function setWorker(WorkerInterface $worker) { +class Manager +{ + private $worker; + + public function setWorker(WorkerInterface $worker) + { $this->worker = $worker; } - public function manage() { + public function manage() + { $this->worker->work(); } } ``` **Good:** + ```php -interface WorkerInterface extends FeedableInterface, WorkableInterface { +interface WorkerInterface extends FeedableInterface, WorkableInterface +{ } -interface WorkableInterface { +interface WorkableInterface +{ public function work(); } -interface FeedableInterface { +interface FeedableInterface +{ public function eat(); } -class Worker implements WorkableInterface, FeedableInterface { - public function work() { +class Worker implements WorkableInterface, FeedableInterface +{ + public function work() + { // ....working } - public function eat() { + public function eat() + { //.... eating in launch break } } -class Robot implements WorkableInterface { - public function work() { +class Robot implements WorkableInterface +{ + public function work() + { // ....working } } -class SuperWorker implements WorkerInterface { - public function work() { +class SuperWorker implements WorkerInterface +{ + public function work() + { //.... working much more } - public function eat() { + public function eat() + { //.... eating in launch break } } -class Manager { - /** @var $worker WorkableInterface **/ +class Manager +{ private $worker; - public function setWorker(WorkableInterface $w) { + public function setWorker(WorkableInterface $w) + { $this->worker = $w; } - public function manage() { + public function manage() + { $this->worker->work(); } } ``` + **[⬆ back to top](#table-of-contents)** ### Dependency Inversion Principle (DIP) From 83eef477e9d8d53ea4165e528734930e41fc03c8 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 31 Aug 2017 17:06:55 +0300 Subject: [PATCH 033/210] rename Worker to Employe --- README.md | 51 +++++++++++++++++++-------------------------------- 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 266f8176..1f2d2e99 100644 --- a/README.md +++ b/README.md @@ -1188,13 +1188,13 @@ all of the settings. Making them optional helps prevent having a "fat interface" **Bad:** ```php -interface WorkerInterface +interface Employe { public function work(); public function eat(); } -class Worker implements WorkerInterface +class Human implements Employe { public function work() { @@ -1207,7 +1207,7 @@ class Worker implements WorkerInterface } } -class SuperWorker implements WorkerInterface +class Robot implements Employe { public function work() { @@ -1216,22 +1216,22 @@ class SuperWorker implements WorkerInterface public function eat() { - //.... eating in launch break + //.... robot can't eating, but it must implement this method } } class Manager { - private $worker; + private $employe; - public function setWorker(WorkerInterface $worker) + public function setWorker(Employe $employe) { - $this->worker = $worker; + $this->employe = $employe; } public function manage() { - $this->worker->work(); + $this->employe->work(); } } ``` @@ -1239,21 +1239,21 @@ class Manager **Good:** ```php -interface WorkerInterface extends FeedableInterface, WorkableInterface +interface Workable { + public function work(); } -interface WorkableInterface +interface Feedable { - public function work(); + public function eat(); } -interface FeedableInterface +interface Employe extends Feedable, Workable { - public function eat(); } -class Worker implements WorkableInterface, FeedableInterface +class Human implements Employe { public function work() { @@ -1266,7 +1266,7 @@ class Worker implements WorkableInterface, FeedableInterface } } -class Robot implements WorkableInterface +class Robot implements Workable { public function work() { @@ -1274,31 +1274,18 @@ class Robot implements WorkableInterface } } -class SuperWorker implements WorkerInterface -{ - public function work() - { - //.... working much more - } - - public function eat() - { - //.... eating in launch break - } -} - class Manager { - private $worker; + private $employe; - public function setWorker(WorkableInterface $w) + public function setWorker(Workable $employe) { - $this->worker = $w; + $this->employe = $employe; } public function manage() { - $this->worker->work(); + $this->employe->work(); } } ``` From d2d59eae49929c4323869a6783d2939d75ef5543 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 31 Aug 2017 17:11:03 +0300 Subject: [PATCH 034/210] rename method Manager::setWorker() to Manager::subdue() --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1f2d2e99..c6805354 100644 --- a/README.md +++ b/README.md @@ -1224,7 +1224,7 @@ class Manager { private $employe; - public function setWorker(Employe $employe) + public function subdue(Employe $employe) { $this->employe = $employe; } @@ -1266,6 +1266,7 @@ class Human implements Employe } } +// robot can only work class Robot implements Workable { public function work() @@ -1278,7 +1279,7 @@ class Manager { private $employe; - public function setWorker(Workable $employe) + public function subdue(Workable $employe) { $this->employe = $employe; } From 84ca341bfa44121db2e62d3347d4ca3211c18e29 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 31 Aug 2017 17:13:13 +0300 Subject: [PATCH 035/210] manager can manage several employees --- README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c6805354..dfc68451 100644 --- a/README.md +++ b/README.md @@ -1222,16 +1222,18 @@ class Robot implements Employe class Manager { - private $employe; + private $employees; public function subdue(Employe $employe) { - $this->employe = $employe; + $this->employees[] = $employe; } public function manage() { - $this->employe->work(); + foreach ($this->employees as $employe) { + $employe->work(); + } } } ``` @@ -1277,16 +1279,18 @@ class Robot implements Workable class Manager { - private $employe; + private $employees; public function subdue(Workable $employe) { - $this->employe = $employe; + $this->employees[] = $employe; } public function manage() { - $this->employe->work(); + foreach ($this->employees as $employe) { + $employe->work(); + } } } ``` From e24c70fe7c04af7f0302347ac5ce615beff1016e Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 31 Aug 2017 17:16:51 +0300 Subject: [PATCH 036/210] manager is also an employe. his work is to manage others --- README.md | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index dfc68451..73937b2c 100644 --- a/README.md +++ b/README.md @@ -1191,6 +1191,7 @@ all of the settings. Making them optional helps prevent having a "fat interface" interface Employe { public function work(); + public function eat(); } @@ -1220,21 +1221,26 @@ class Robot implements Employe } } -class Manager +class Manager implements Employe { - private $employees; + private $employees = []; public function subdue(Employe $employe) { $this->employees[] = $employe; } - public function manage() + public function work() { foreach ($this->employees as $employe) { $employe->work(); } } + + public function eat() + { + // ...... eating in launch break + } } ``` @@ -1277,21 +1283,26 @@ class Robot implements Workable } } -class Manager +class Manager implements Employe { - private $employees; + private $employees = []; public function subdue(Workable $employe) { $this->employees[] = $employe; } - public function manage() + public function work() { foreach ($this->employees as $employe) { $employe->work(); } } + + public function eat() + { + // ...... eating in launch break + } } ``` From a64e611fa6a4fc384f438ed7afbfded5ce4ccaa8 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 31 Aug 2017 17:25:11 +0300 Subject: [PATCH 037/210] correct word "employee" --- README.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 73937b2c..294839be 100644 --- a/README.md +++ b/README.md @@ -1188,14 +1188,14 @@ all of the settings. Making them optional helps prevent having a "fat interface" **Bad:** ```php -interface Employe +interface Employee { public function work(); public function eat(); } -class Human implements Employe +class Human implements Employee { public function work() { @@ -1208,7 +1208,7 @@ class Human implements Employe } } -class Robot implements Employe +class Robot implements Employee { public function work() { @@ -1221,19 +1221,19 @@ class Robot implements Employe } } -class Manager implements Employe +class Manager implements Employee { private $employees = []; - public function subdue(Employe $employe) + public function subdue(Employee $employee) { - $this->employees[] = $employe; + $this->employees[] = $employee; } public function work() { - foreach ($this->employees as $employe) { - $employe->work(); + foreach ($this->employees as $employee) { + $employee->work(); } } @@ -1257,11 +1257,11 @@ interface Feedable public function eat(); } -interface Employe extends Feedable, Workable +interface Employee extends Feedable, Workable { } -class Human implements Employe +class Human implements Employee { public function work() { @@ -1283,19 +1283,19 @@ class Robot implements Workable } } -class Manager implements Employe +class Manager implements Employee { private $employees = []; - public function subdue(Workable $employe) + public function subdue(Workable $employee) { - $this->employees[] = $employe; + $this->employees[] = $employee; } public function work() { - foreach ($this->employees as $employe) { - $employe->work(); + foreach ($this->employees as $employee) { + $employee->work(); } } From 2a04e3d6ac254b13e3a9d3375a645db430c9043c Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 31 Aug 2017 17:28:47 +0300 Subject: [PATCH 038/210] correct CS --- README.md | 60 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index c8e650cc..49039170 100644 --- a/README.md +++ b/README.md @@ -1279,6 +1279,7 @@ class Manager { **[⬆ back to top](#table-of-contents)** ### Dependency Inversion Principle (DIP) + This principle states two essential things: 1. High-level modules should not depend on low-level modules. Both should depend on abstractions. @@ -1293,65 +1294,80 @@ the coupling between modules. Coupling is a very bad development pattern because it makes your code hard to refactor. **Bad:** + ```php -class Worker { - public function work() { +class Worker +{ + public function work() + { // ....working } } -class Manager { - /** @var Worker $worker **/ +class Manager +{ private $worker; - - public function __construct(Worker $worker) { + + public function __construct(Worker $worker) + { $this->worker = $worker; } - - public function manage() { + + public function manage() + { $this->worker->work(); } } -class SuperWorker extends Worker { - public function work() { +class SuperWorker extends Worker +{ + public function work() + { //.... working much more } } ``` **Good:** + ```php -interface WorkerInterface { +interface WorkerInterface +{ public function work(); } -class Worker implements WorkerInterface { - public function work() { +class Worker implements WorkerInterface +{ + public function work() + { // ....working } } -class SuperWorker implements WorkerInterface { - public function work() { +class SuperWorker implements WorkerInterface +{ + public function work() + { //.... working much more } } -class Manager { - /** @var Worker $worker **/ +class Manager +{ private $worker; - - public function __construct(WorkerInterface $worker) { + + public function __construct(WorkerInterface $worker) + { $this->worker = $worker; } - - public function manage() { + + public function manage() + { $this->worker->work(); } } - ``` + **[⬆ back to top](#table-of-contents)** ### Use method chaining From 9c1f626a0e29697f4696e9bba4acc92a40d56c7e Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 31 Aug 2017 17:31:41 +0300 Subject: [PATCH 039/210] =?UTF-8?q?=D1=81hange=20dependencies=20without=20?= =?UTF-8?q?changing=20manager=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 49039170..6990855b 100644 --- a/README.md +++ b/README.md @@ -1304,6 +1304,14 @@ class Worker } } +class SuperWorker extends Worker +{ + public function work() + { + //.... working much more + } +} + class Manager { private $worker; @@ -1318,25 +1326,17 @@ class Manager $this->worker->work(); } } - -class SuperWorker extends Worker -{ - public function work() - { - //.... working much more - } -} ``` **Good:** ```php -interface WorkerInterface +interface Worker { public function work(); } -class Worker implements WorkerInterface +class SumeWorker implements Worker { public function work() { @@ -1344,7 +1344,7 @@ class Worker implements WorkerInterface } } -class SuperWorker implements WorkerInterface +class SuperWorker implements Worker { public function work() { @@ -1356,7 +1356,7 @@ class Manager { private $worker; - public function __construct(WorkerInterface $worker) + public function __construct(Worker $worker) { $this->worker = $worker; } From 4c2133dbaf22b29d9b045ed957adb6ed43a22796 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 31 Aug 2017 17:39:13 +0300 Subject: [PATCH 040/210] change example for Function names should say what they do --- README.md | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index c8e650cc..6895d0f0 100644 --- a/README.md +++ b/README.md @@ -284,26 +284,41 @@ function isClientActive($client) { ### Function names should say what they do **Bad:** + ```php -function addToDate($date, $month) { - // ... -} +class Email +{ + //... -$date = new \DateTime(); + public function handle() + { + mail($this->to, $this->subject, $this->body); + } +} -// It's hard to to tell from the function name what is added -addToDate($date, 1); +$message = new Email(...); +// What is this? A handle for the message? Are we writing to a file now? +$message->handle(); ``` -**Good**: +**Good:** + ```php -function addMonthToDate($month, $date) { - // ... +class Email +{ + //... + + public function send() + { + mail($this->to, $this->subject, $this->body); + } } -$date = new \DateTime(); -addMonthToDate(1, $date); +$message = new Email(...); +// Clear and obvious +$message->send(); ``` + **[⬆ back to top](#table-of-contents)** ### Functions should only be one level of abstraction From 80bd47f9fa8ca52682841e43948dac5bb5196354 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 31 Aug 2017 18:27:33 +0300 Subject: [PATCH 041/210] Manager manage workers --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 294839be..16e339c8 100644 --- a/README.md +++ b/README.md @@ -1285,17 +1285,17 @@ class Robot implements Workable class Manager implements Employee { - private $employees = []; + private $workers = []; - public function subdue(Workable $employee) + public function subdue(Workable $worker) { - $this->employees[] = $employee; + $this->workers[] = $worker; } public function work() { - foreach ($this->employees as $employee) { - $employee->work(); + foreach ($this->workers as $worker) { + $worker->work(); } } From e31b5b86cee98d1ee40a8ba06d94c2e8c5aefa72 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 31 Aug 2017 18:33:00 +0300 Subject: [PATCH 042/210] add comment --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 16e339c8..ad4a175a 100644 --- a/README.md +++ b/README.md @@ -1246,6 +1246,8 @@ class Manager implements Employee **Good:** +Not every worker is an employee, but every employee is an worker. + ```php interface Workable { From 00358108d0e87e750b9064656fcfbdc0b7f0b574 Mon Sep 17 00:00:00 2001 From: "Connor S. Parks" Date: Fri, 1 Sep 2017 10:32:00 +0100 Subject: [PATCH 043/210] Fix Invalid Code The "Avoid Side Effects" section's "Bad" code block is actually invalid code, `splitIntoFirstAndLastName()` will throw a notice of `Undefined variable`. We need a `$global` here. I'm all for competing against code that people may have but there is no point comparing code that doesn't work in the first place, it's out of the bounds of this document. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a51aafbe..efd6cedf 100644 --- a/README.md +++ b/README.md @@ -612,6 +612,8 @@ than the vast majority of other programmers. $name = 'Ryan McDermott'; function splitIntoFirstAndLastName() { + global $name; + $name = preg_split('/ /', $name); } From a77a62d7ce12409b6115f8dd2ff4ab29119111d7 Mon Sep 17 00:00:00 2001 From: Sergiy Litvinchuk Date: Fri, 1 Sep 2017 12:32:09 +0300 Subject: [PATCH 044/210] Pass $name and $email to parent constructor in EmployeeTaxData --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a51aafbe..62f24f6e 100644 --- a/README.md +++ b/README.md @@ -1664,8 +1664,8 @@ class Employee { class EmployeeTaxData extends Employee { private $ssn, $salary; - public function __construct($ssn, $salary) { - parent::__construct(); + public function __construct($name, $email, $ssn, $salary) { + parent::__construct($name, $email); $this->ssn = $ssn; $this->salary = $salary; } From 2992a4f3fd1d9cd9d673391d363ade0e15b1daea Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Fri, 1 Sep 2017 17:34:44 +0300 Subject: [PATCH 045/210] remove Manager in ISP --- README.md | 44 -------------------------------------------- 1 file changed, 44 deletions(-) diff --git a/README.md b/README.md index ad4a175a..839d3b57 100644 --- a/README.md +++ b/README.md @@ -1220,28 +1220,6 @@ class Robot implements Employee //.... robot can't eating, but it must implement this method } } - -class Manager implements Employee -{ - private $employees = []; - - public function subdue(Employee $employee) - { - $this->employees[] = $employee; - } - - public function work() - { - foreach ($this->employees as $employee) { - $employee->work(); - } - } - - public function eat() - { - // ...... eating in launch break - } -} ``` **Good:** @@ -1284,28 +1262,6 @@ class Robot implements Workable // ....working } } - -class Manager implements Employee -{ - private $workers = []; - - public function subdue(Workable $worker) - { - $this->workers[] = $worker; - } - - public function work() - { - foreach ($this->workers as $worker) { - $worker->work(); - } - } - - public function eat() - { - // ...... eating in launch break - } -} ``` **[⬆ back to top](#table-of-contents)** From 08c8cc7b0791ed38fc11d8dca9d442a31cb85795 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Fri, 1 Sep 2017 18:02:30 +0300 Subject: [PATCH 046/210] missing a second "e" at the end of employee --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 6372942d..b7bce13a 100644 --- a/README.md +++ b/README.md @@ -1487,10 +1487,10 @@ function showManagerList($managers) ```php function showList($employees) { -    foreach ($employees as $employe) { -        $expectedSalary = $employe->calculateExpectedSalary(); -        $experience = $employe->getExperience(); -        $githubLink = $employe->getGithubLink(); +    foreach ($employees as $employee) { +        $expectedSalary = $employee->calculateExpectedSalary(); +        $experience = $employee->getExperience(); +        $githubLink = $employee->getGithubLink(); $data = [ $expectedSalary, $experience, @@ -1509,11 +1509,11 @@ It is better to use a compact version of the code. ```php function showList($employees) { -    foreach ($employees as $employe) { +    foreach ($employees as $employee) { render([ - $employe->calculateExpectedSalary(), - $employe->getExperience(), - $employe->getGithubLink() + $employee->calculateExpectedSalary(), + $employee->getExperience(), + $employee->getGithubLink() ]); } } From 1a0c0c0f560e376e84bfb7843476edf0aa1d4b73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20Str=C3=BCbing?= Date: Sat, 2 Sep 2017 11:31:49 +0200 Subject: [PATCH 047/210] Correct spelling --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 133914a1..85eb052b 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ $address = 'One Infinite Loop, Cupertino 95014'; $cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/'; preg_match($cityZipCodeRegex, $address, $matches); -list(, $city, $zipCode) = $matchers; +list(, $city, $zipCode) = $matches; saveCityZipCode($city, $zipCode); ``` From cc9896241ae48a8ce1fb4e6f259b17a04990e5e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emin=20=C5=9Een?= Date: Sat, 2 Sep 2017 15:57:43 +0300 Subject: [PATCH 048/210] Duplicated fixes. Duplicate variables removed. Variable names updated. --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 133914a1..2ee3ae1b 100644 --- a/README.md +++ b/README.md @@ -528,8 +528,6 @@ var_dump($name); // ['Ryan', 'McDermott']; **Good**: ```php -$name = 'Ryan McDermott'; - function splitIntoFirstAndLastName($name) { return preg_split('/ /', $name); } @@ -970,8 +968,9 @@ your codebase. ```php class UserSettings { private $user; + public function __construct($user) { - $this->user = user; + $this->user = $user; } public function changeSettings($settings) { @@ -990,8 +989,9 @@ class UserSettings { ```php class UserAuth { private $user; + public function __construct($user) { - $this->user = user; + $this->user = $user; } public function verifyCredentials() { @@ -1002,6 +1002,7 @@ class UserAuth { class UserSettings { private $user; + public function __construct($user) { $this->user = $user; $this->auth = new UserAuth($user); From c058329bff60ada383e5445ab3f30fbcc7ff50dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emin=20=C5=9Een?= Date: Sat, 2 Sep 2017 16:08:24 +0300 Subject: [PATCH 049/210] foreach loop syntax fixed. does not use "in" syntax, it uses "as" --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2ee3ae1b..ecab842b 100644 --- a/README.md +++ b/README.md @@ -1186,12 +1186,12 @@ class Square extends Rectangle { } function renderLargeRectangles($rectangles) { - foreach($rectangle in $rectangles) { + foreach($rectangles as $rectangle) { $rectangle->setWidth(4); $rectangle->setHeight(5); $area = $rectangle->getArea(); // BAD: Will return 25 for Square. Should be 20. $rectangle->render($area); - }); + } } $rectangles = [new Rectangle(), new Rectangle(), new Square()]; @@ -1250,7 +1250,7 @@ class Square extends Shape { } function renderLargeRectangles($rectangles) { - foreach($rectangle in $rectangles) { + foreach($rectangles as $rectangle) { if ($rectangle instanceof Square) { $rectangle->setLength(5); } else if ($rectangle instanceof Rectangle) { @@ -1260,7 +1260,7 @@ function renderLargeRectangles($rectangles) { $area = $rectangle->getArea(); $rectangle->render($area); - }); + } } $shapes = [new Rectangle(), new Rectangle(), new Square()]; From 0c89c48adb9ff6e4071aef6fe70f0c518039770d Mon Sep 17 00:00:00 2001 From: Guilherme Portela Date: Sun, 3 Sep 2017 10:20:14 -0300 Subject: [PATCH 050/210] Fix variable type in PHPDoc --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 133914a1..d4392b65 100644 --- a/README.md +++ b/README.md @@ -1431,7 +1431,7 @@ class SuperWorker implements WorkerInterface { } class Manager { - /** @var Worker $worker **/ + /** @var WorkerInterface $worker **/ private $worker; public function __construct(WorkerInterface $worker) { From 820a6a1d9ecfa9f3588f09b1289712985f01a57f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emin=20=C5=9Een?= Date: Mon, 4 Sep 2017 04:58:47 +0300 Subject: [PATCH 051/210] Fix variable spelling and syntax Functions don't start with "new". "$req" variable incorrect variable name, "$request" good. Class construct syntax. --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index ecab842b..8982c4b5 100644 --- a/README.md +++ b/README.md @@ -833,8 +833,8 @@ function newRequestModule($url) { // ... } -$req = new newRequestModule($requestUrl); -inventoryTracker('apples', $req, 'www.inventory-awesome.io'); +$request = newRequestModule($requestUrl); +inventoryTracker('apples', $request, 'www.inventory-awesome.io'); ``` @@ -844,8 +844,8 @@ function requestModule($url) { // ... } -$req = new requestModule($requestUrl); -inventoryTracker('apples', $req, 'www.inventory-awesome.io'); +$request = requestModule($requestUrl); +inventoryTracker('apples', $request, 'www.inventory-awesome.io'); ``` **[⬆ back to top](#table-of-contents)** @@ -1215,8 +1215,8 @@ abstract class Shape { } class Rectangle extends Shape { - public function __construct { - parent::__construct(); + public function __construct() { + parent::__construct(); $this->width = 0; $this->height = 0; } @@ -1235,7 +1235,7 @@ class Rectangle extends Shape { } class Square extends Shape { - public function __construct { + public function __construct() { parent::__construct(); $this->length = 0; } From 656e7ff6e6e2606a569e07a51eb9648efffcd4c7 Mon Sep 17 00:00:00 2001 From: eddymens Date: Mon, 4 Sep 2017 08:21:53 +0000 Subject: [PATCH 052/210] fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fee49f20..7f06ac15 100644 --- a/README.md +++ b/README.md @@ -541,7 +541,7 @@ var_dump($newName); // ['Ryan', 'McDermott']; **[⬆ back to top](#table-of-contents)** ### Don't write to global functions -Polluting globals is a bad practice in very languages because you could clash with another +Polluting globals is a bad practice in many languages because you could clash with another library and the user of your API would be none-the-wiser until they get an exception in production. Let's think about an example: what if you wanted to have configuration array. You could write global function like `config()`, but it could clash with another library From 6e01046113f5419ffb2c2ba48bb866639c44e63b Mon Sep 17 00:00:00 2001 From: Pablo Godinez Date: Mon, 4 Sep 2017 10:57:27 +0200 Subject: [PATCH 053/210] LSP good example correction See #55 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fee49f20..4825cd76 100644 --- a/README.md +++ b/README.md @@ -1201,7 +1201,7 @@ renderLargeRectangles($rectangles); **Good:** ```php abstract class Shape { - private $width, $height; + protected $width, $height; abstract public function getArea(); From 32344ead21b0be088c1ee352c926191695f904a9 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Mon, 4 Sep 2017 13:03:17 +0300 Subject: [PATCH 054/210] correct access to $width and $height --- README.md | 115 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 67 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 48977655..9d41d650 100644 --- a/README.md +++ b/README.md @@ -1145,48 +1145,56 @@ if you model it using the "is-a" relationship via inheritance, you quickly get into trouble. **Bad:** + ```php -class Rectangle { - private $width, $height; - - public function __construct() { +class Rectangle +{ + protected $width + protected $height; + + public function __construct() + { $this->width = 0; $this->height = 0; } - - public function setColor($color) { - // ... - } - - public function render($area) { + + public function render($area) + { // ... } - - public function setWidth($width) { + + public function setWidth($width) + { $this->width = $width; } - - public function setHeight($height) { + + public function setHeight($height) + { $this->height = $height; } - - public function getArea() { + + public function getArea() + { return $this->width * $this->height; } } -class Square extends Rectangle { - public function setWidth($width) { +class Square extends Rectangle +{ + public function setWidth($width) + { $this->width = $this->height = $width; } - - public function setHeight(height) { + + public function setHeight(height) + { $this->width = $this->height = $height; } } -function renderLargeRectangles($rectangles) { - foreach($rectangles as $rectangle) { +function renderLargeRectangles($rectangles) +{ + foreach ($rectangles as $rectangle) { $rectangle->setWidth(4); $rectangle->setHeight(5); $area = $rectangle->getArea(); // BAD: Will return 25 for Square. Should be 20. @@ -1199,61 +1207,71 @@ renderLargeRectangles($rectangles); ``` **Good:** + ```php -abstract class Shape { - protected $width, $height; - +abstract class Shape +{ + protected $width; + protected $height; + abstract public function getArea(); - - public function setColor($color) { - // ... - } - - public function render($area) { + + public function render($area) + { // ... } } -class Rectangle extends Shape { - public function __construct() { +class Rectangle extends Shape +{ + public function __construct() + { parent::__construct(); $this->width = 0; $this->height = 0; } - - public function setWidth($width) { + + public function setWidth($width) + { $this->width = $width; } - - public function setHeight($height) { + + public function setHeight($height) + { $this->height = $height; } - - public function getArea() { + + public function getArea() + { return $this->width * $this->height; } } -class Square extends Shape { - public function __construct() { +class Square extends Shape +{ + public function __construct() + { parent::__construct(); $this->length = 0; } - - public function setLength($length) { + + public function setLength($length) + { $this->length = $length; } - - public function getArea() { + + public function getArea() + { return pow($this->length, 2); } } -function renderLargeRectangles($rectangles) { - foreach($rectangles as $rectangle) { +function renderLargeRectangles($rectangles) +{ + foreach ($rectangles as $rectangle) { if ($rectangle instanceof Square) { $rectangle->setLength(5); - } else if ($rectangle instanceof Rectangle) { + } elseif ($rectangle instanceof Rectangle) { $rectangle->setWidth(4); $rectangle->setHeight(5); } @@ -1266,6 +1284,7 @@ function renderLargeRectangles($rectangles) { $shapes = [new Rectangle(), new Rectangle(), new Square()]; renderLargeRectangles($shapes); ``` + **[⬆ back to top](#table-of-contents)** ### Interface Segregation Principle (ISP) From a71ad82a792399f7a6c9ed6c0808d9e07e0d8342 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Mon, 4 Sep 2017 13:06:19 +0300 Subject: [PATCH 055/210] forgot ; --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9d41d650..b5ae5f08 100644 --- a/README.md +++ b/README.md @@ -1149,7 +1149,7 @@ get into trouble. ```php class Rectangle { - protected $width + protected $width; protected $height; public function __construct() From eff33c5dfd6b5bc14e7be3438fb6a81666102586 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Mon, 4 Sep 2017 19:40:48 +0300 Subject: [PATCH 056/210] correct LSP example --- README.md | 114 +++++++++++++++++++++++++----------------------------- 1 file changed, 52 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index b5ae5f08..f0136a88 100644 --- a/README.md +++ b/README.md @@ -1149,19 +1149,8 @@ get into trouble. ```php class Rectangle { - protected $width; - protected $height; - - public function __construct() - { - $this->width = 0; - $this->height = 0; - } - - public function render($area) - { - // ... - } + protected $width = 0; + protected $height = 0; public function setWidth($width) { @@ -1173,7 +1162,7 @@ class Rectangle $this->height = $height; } - public function getArea() + public function area() { return $this->width * $this->height; } @@ -1186,50 +1175,42 @@ class Square extends Rectangle $this->width = $this->height = $width; } - public function setHeight(height) + public function setHeight($height) { $this->width = $this->height = $height; } } -function renderLargeRectangles($rectangles) +function rectangleAreaVerifier(Rectangle $rectangle) { - foreach ($rectangles as $rectangle) { - $rectangle->setWidth(4); - $rectangle->setHeight(5); - $area = $rectangle->getArea(); // BAD: Will return 25 for Square. Should be 20. - $rectangle->render($area); - } + $rectangle->setWidth(4); + $rectangle->setHeight(5); + + // BAD: Will return 25 for Square. Should be 20. + return $rectangle->area() == 20; } -$rectangles = [new Rectangle(), new Rectangle(), new Square()]; -renderLargeRectangles($rectangles); +$rectangles = [new Rectangle(), new Square()]; + +foreach ($rectangles as $rectangle) { + if (!rectangleAreaVerifier($rectangle)) { + throw new Exception('Bad area!'); + } +} ``` **Good:** ```php -abstract class Shape +interface Shape { - protected $width; - protected $height; - - abstract public function getArea(); - - public function render($area) - { - // ... - } + public function area(); } -class Rectangle extends Shape +class Rectangle implements Shape { - public function __construct() - { - parent::__construct(); - $this->width = 0; - $this->height = 0; - } + private $width = 0; + private $height = 0; public function setWidth($width) { @@ -1241,48 +1222,57 @@ class Rectangle extends Shape $this->height = $height; } - public function getArea() + public function area() { return $this->width * $this->height; } } -class Square extends Shape +class Square implements Shape { - public function __construct() - { - parent::__construct(); - $this->length = 0; - } + private $length = 0; public function setLength($length) { $this->length = $length; } - public function getArea() + public function area() { return pow($this->length, 2); } } -function renderLargeRectangles($rectangles) +function rectangleAreaVerifier(Rectangle $rectangle) { - foreach ($rectangles as $rectangle) { - if ($rectangle instanceof Square) { - $rectangle->setLength(5); - } elseif ($rectangle instanceof Rectangle) { - $rectangle->setWidth(4); - $rectangle->setHeight(5); - } - - $area = $rectangle->getArea(); - $rectangle->render($area); + $rectangle->setWidth(4); + $rectangle->setHeight(5); + + return $rectangle->area() == 20; +} + +function squareAreaVerifier(Square $square) +{ + $square->setLength(5); + + return $square->area() == 25; +} + +$rectangles = [new Rectangle()]; + +foreach ($rectangles as $rectangle) { + if (!rectangleAreaVerifier($rectangle)) { + throw new Exception('Bad area!'); } } -$shapes = [new Rectangle(), new Rectangle(), new Square()]; -renderLargeRectangles($shapes); +$squares = [new Square()]; + +foreach ($squares as $square) { + if (!squareAreaVerifier($square)) { + throw new Exception('Bad area!'); + } +} ``` **[⬆ back to top](#table-of-contents)** From cb933d10a514ac5ee407b7b40b53bb1c5a36645f Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Mon, 4 Sep 2017 20:32:02 +0300 Subject: [PATCH 057/210] rename rectangleAreaVerifier to areaVerifier --- README.md | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index f0136a88..159c123f 100644 --- a/README.md +++ b/README.md @@ -1181,7 +1181,7 @@ class Square extends Rectangle } } -function rectangleAreaVerifier(Rectangle $rectangle) +function areaVerifier(Rectangle $rectangle) { $rectangle->setWidth(4); $rectangle->setHeight(5); @@ -1193,7 +1193,7 @@ function rectangleAreaVerifier(Rectangle $rectangle) $rectangles = [new Rectangle(), new Square()]; foreach ($rectangles as $rectangle) { - if (!rectangleAreaVerifier($rectangle)) { +    if (!areaVerifier($rectangle)) { throw new Exception('Bad area!'); } } @@ -1258,20 +1258,16 @@ function squareAreaVerifier(Square $square) return $square->area() == 25; } -$rectangles = [new Rectangle()]; +$rectangle = new Rectangle(); -foreach ($rectangles as $rectangle) { - if (!rectangleAreaVerifier($rectangle)) { - throw new Exception('Bad area!'); - } +if (!rectangleAreaVerifier($rectangle)) { + throw new Exception('Bad area!'); } -$squares = [new Square()]; +$square = new Square(); -foreach ($squares as $square) { - if (!squareAreaVerifier($square)) { - throw new Exception('Bad area!'); - } +if (!squareAreaVerifier($square)) { + throw new Exception('Bad area!'); } ``` From cb272de8297ea09c4b2102e44cf32b54dc105bec Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Mon, 4 Sep 2017 20:42:33 +0300 Subject: [PATCH 058/210] separate client is not good way --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 159c123f..df832b19 100644 --- a/README.md +++ b/README.md @@ -1199,7 +1199,10 @@ foreach ($rectangles as $rectangle) { } ``` -**Good:** +**Not good:** + +You can separate client for differen shapes. +But this is not a best solution becous the square is still must be a subtype of the rectangle. ```php interface Shape From 25704346570d11e6422269a9641abb3975cabfcc Mon Sep 17 00:00:00 2001 From: webaholik Date: Mon, 4 Sep 2017 22:27:15 -0500 Subject: [PATCH 059/210] Update README.md Corrected a few words. --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b5ae5f08..97cb7b9f 100644 --- a/README.md +++ b/README.md @@ -1308,7 +1308,7 @@ class Worker implements WorkerInterface { // ....working } public function eat() { - // ...... eating in launch break + // ...... eating in lunch break } } @@ -1318,7 +1318,7 @@ class SuperWorker implements WorkerInterface { } public function eat() { - //.... eating in launch break + //.... eating in lunch break } } @@ -1355,7 +1355,7 @@ class Worker implements WorkableInterface, FeedableInterface { } public function eat() { - //.... eating in launch break + //.... eating in lunch break } } @@ -1371,7 +1371,7 @@ class SuperWorker implements WorkerInterface { } public function eat() { - //.... eating in launch break + //.... eating in lunch break } } @@ -1467,7 +1467,7 @@ class Manager { **[⬆ back to top](#table-of-contents)** ### Use method chaining -This pattern is very useful and commonly used it many libraries such +This pattern is very useful and commonly used in many libraries such as PHPUnit and Doctrine. It allows your code to be expressive, and less verbose. For that reason, I say, use method chaining and take a look at how clean your code will be. In your class functions, simply return `this` at the end of every function, From dc63a3e0d15aff0b9eafba36e4abd3e0227da580 Mon Sep 17 00:00:00 2001 From: zouyi Date: Tue, 5 Sep 2017 16:00:31 +0800 Subject: [PATCH 060/210] add Translation section add Translation section --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index b5ae5f08..bf68a53e 100644 --- a/README.md +++ b/README.md @@ -1728,3 +1728,14 @@ function showList($employees) ``` **[⬆ back to top](#table-of-contents)** + + + +## Translation + +This is also available in other languages: + - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese**: + - [yangweijie/clean-code-php](https://github.com/yangweijie/clean-code-php) + - [php-cpm/clean-code-php](https://github.com/php-cpm/clean-code-php) + +**[⬆ back to top](#table-of-contents)** From 0368f8004aeccceb20079ba0d3916a12c7fc097e Mon Sep 17 00:00:00 2001 From: Alexis Lefebvre Date: Tue, 5 Sep 2017 12:24:02 +0200 Subject: [PATCH 061/210] Restrict Method chaining to setters Remove "I say" that adds no value --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b5ae5f08..6b182061 100644 --- a/README.md +++ b/README.md @@ -1469,8 +1469,8 @@ class Manager { ### Use method chaining This pattern is very useful and commonly used it many libraries such as PHPUnit and Doctrine. It allows your code to be expressive, and less verbose. -For that reason, I say, use method chaining and take a look at how clean your code -will be. In your class functions, simply return `this` at the end of every function, +For that reason, use method chaining and take a look at how clean your code +will be. In your class functions, simply use `return $this` at the end of every `set` function, and you can chain further class methods onto it. **Bad:** From aa24debbac9160c31e805ad8edafb07a27e3925f Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 5 Sep 2017 14:08:13 +0300 Subject: [PATCH 062/210] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7958a1b4..0530f2f3 100644 --- a/README.md +++ b/README.md @@ -184,7 +184,7 @@ function createMicrobrewery($breweryName = 'Hipster Brew Co.') **Not bad:** -This opinion is understandable than the previous version, but it better controls the value of the variable. +This opinion is more understandable than the previous version, but it better controls the value of the variable. ```php function createMicrobrewery($name = null) From 263b32be164da73408100d754333074aaac16f5d Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 5 Sep 2017 14:14:42 +0300 Subject: [PATCH 063/210] correct Good solutuin --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index df832b19..1d0f44dc 100644 --- a/README.md +++ b/README.md @@ -1199,10 +1199,11 @@ foreach ($rectangles as $rectangle) { } ``` -**Not good:** +**Good:** -You can separate client for differen shapes. -But this is not a best solution becous the square is still must be a subtype of the rectangle. +You must separate different shapes. +Despite the apparent similarity of the square and the rectangle, this is not so. +A square also has much in common with a rhombus, but it is not a subtype of a rhombus. ```php interface Shape From 040fd20da89b698aa18d7c5ef04b1fa0a5fbfe15 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 5 Sep 2017 14:22:20 +0300 Subject: [PATCH 064/210] add immutable example for LSP --- README.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/README.md b/README.md index 1d0f44dc..79dc0a83 100644 --- a/README.md +++ b/README.md @@ -1199,6 +1199,60 @@ foreach ($rectangles as $rectangle) { } ``` +**Not bad:** + +You can solve the problem by making objects immutable, but this is not the best solution. + +```php +class Rectangle +{ + private $width = 0; + private $height = 0; + + public function __construct($width, $height) + { + $this->width = $width; + $this->height = $height; + } + + public function width() + { + return $this->width; + } + + public function height() + { + return $this->height; + } + + public function area() + { + return $this->width * $this->height; + } +} + +class Square extends Rectangle +{ + public function __construct($length) + { + parent::__construct($length, $length); + } +} + +function areaVerifier(Rectangle $rectangle) +{ + return $rectangle->area() == $rectangle->width() * $rectangle->height(); +} + +$rectangles = [new Rectangle(4, 5), new Square(5)]; + +foreach ($rectangles as $rectangle) { + if (!areaVerifier($rectangle)) { + throw new Exception('Bad area!'); + } +} +``` + **Good:** You must separate different shapes. From 9daf53d5455192a6bf127f66d0c16aabd794e139 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 5 Sep 2017 17:27:09 +0300 Subject: [PATCH 065/210] remove introduction of mocking --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 48977655..33d894e5 100644 --- a/README.md +++ b/README.md @@ -462,8 +462,6 @@ class BetterJSAlternative } ``` -Now we can mock the dependencies and test only the work of method `BetterJSAlternative::parse()`. - **[⬆ back to top](#table-of-contents)** ### Don't use flags as function parameters From d868624218da020f2ef7589c6418376fca6c74de Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 5 Sep 2017 17:45:05 +0300 Subject: [PATCH 066/210] remove nonsensical interface Shape --- README.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 79dc0a83..f5f36996 100644 --- a/README.md +++ b/README.md @@ -1260,12 +1260,7 @@ Despite the apparent similarity of the square and the rectangle, this is not so. A square also has much in common with a rhombus, but it is not a subtype of a rhombus. ```php -interface Shape -{ - public function area(); -} - -class Rectangle implements Shape +class Rectangle { private $width = 0; private $height = 0; @@ -1286,7 +1281,7 @@ class Rectangle implements Shape } } -class Square implements Shape +class Square { private $length = 0; From 5c7064a69ca9a4c8d3aa11b1a3a0dcd7c744b59d Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 5 Sep 2017 18:06:42 +0300 Subject: [PATCH 067/210] change example descriptions --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f5f36996..af58d8bb 100644 --- a/README.md +++ b/README.md @@ -1201,7 +1201,8 @@ foreach ($rectangles as $rectangle) { **Not bad:** -You can solve the problem by making objects immutable, but this is not the best solution. +You can solve the problem by making objects immutable. +But this is not the best solution, because the square specifies the invariants of the rectangle. ```php class Rectangle @@ -1255,9 +1256,10 @@ foreach ($rectangles as $rectangle) { **Good:** -You must separate different shapes. -Despite the apparent similarity of the square and the rectangle, this is not so. -A square also has much in common with a rhombus, but it is not a subtype of a rhombus. +The best way is separate the quadrangles. +Despite the apparent similarity of the square and the rectangle, they are different. +A square has much in common with a rhombus, and a rectangle with a parallelogram, but they are not subtype. +A square, a rectangle, a rhombus and a parallelogram are separate figures with their own properties, albeit similar. ```php class Rectangle From 0a5cb98bb0aad18e129a943ae4b32c3d1ad3a755 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 5 Sep 2017 18:36:34 +0300 Subject: [PATCH 068/210] use PSR CS --- README.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 48977655..f5d1f79e 100644 --- a/README.md +++ b/README.md @@ -467,13 +467,16 @@ Now we can mock the dependencies and test only the work of method `BetterJSAlter **[⬆ back to top](#table-of-contents)** ### Don't use flags as function parameters + Flags tell your user that this function does more than one thing. Functions should do one thing. Split out your functions if they are following different code paths based on a boolean. **Bad:** + ```php -function createFile($name, $temp = false) { +function createFile($name, $temp = false) +{ if ($temp) { touch('./temp/'.$name); } else { @@ -483,15 +486,19 @@ function createFile($name, $temp = false) { ``` **Good**: + ```php -function createFile($name) { +function createFile($name) +{ touch($name); } -function createTempFile($name) { +function createTempFile($name) +{ touch('./temp/'.$name); } ``` + **[⬆ back to top](#table-of-contents)** ### Avoid Side Effects From 068bf035f9ee9814ed5206ec6de2c9f0c208dfcd Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 5 Sep 2017 18:40:18 +0300 Subject: [PATCH 069/210] use PSR CS --- README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 48977655..5d51c2b4 100644 --- a/README.md +++ b/README.md @@ -495,6 +495,7 @@ function createTempFile($name) { **[⬆ back to top](#table-of-contents)** ### Avoid Side Effects + A function produces a side effect if it does anything other than take a value in and return another value or values. A side effect could be writing to a file, modifying some global variable, or accidentally wiring all your money to a stranger. @@ -510,12 +511,14 @@ centralizing where your side effects occur. If you can do this, you will be happ than the vast majority of other programmers. **Bad:** + ```php // Global variable referenced by following function. // If we had another function that used this name, now it'd be an array and it could break it. $name = 'Ryan McDermott'; -function splitIntoFirstAndLastName() { +function splitIntoFirstAndLastName() +{ global $name; $name = preg_split('/ /', $name); @@ -526,9 +529,11 @@ splitIntoFirstAndLastName(); var_dump($name); // ['Ryan', 'McDermott']; ``` -**Good**: +**Good:** + ```php -function splitIntoFirstAndLastName($name) { +function splitIntoFirstAndLastName($name) +{ return preg_split('/ /', $name); } @@ -538,6 +543,7 @@ $newName = splitIntoFirstAndLastName($name); var_dump($name); // 'Ryan McDermott'; var_dump($newName); // ['Ryan', 'McDermott']; ``` + **[⬆ back to top](#table-of-contents)** ### Don't write to global functions From fcdb1bb68ae51413c78a86789798d612048a980f Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 5 Sep 2017 18:49:07 +0300 Subject: [PATCH 070/210] use PSR CS --- README.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 48977655..9bd6d41d 100644 --- a/README.md +++ b/README.md @@ -673,19 +673,24 @@ if ($article->isPublished()) { ### Avoid negative conditionals **Bad:** + ```php -function isDOMNodeNotPresent($node) { +function isDOMNodeNotPresent($node) +{ // ... } -if (!isDOMNodeNotPresent($node)) { +if (!isDOMNodeNotPresent($node)) +{ // ... } ``` -**Good**: +**Good:** + ```php -function isDOMNodePresent($node) { +function isDOMNodePresent($node) +{ // ... } @@ -693,6 +698,7 @@ if (isDOMNodePresent($node)) { // ... } ``` + **[⬆ back to top](#table-of-contents)** ### Avoid conditionals From 46a7f3710457b16ff767749456f3433ad1f82fdf Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 5 Sep 2017 18:51:33 +0300 Subject: [PATCH 071/210] use PSR CS --- README.md | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 48977655..b9273ee6 100644 --- a/README.md +++ b/README.md @@ -696,6 +696,7 @@ if (isDOMNodePresent($node)) { **[⬆ back to top](#table-of-contents)** ### Avoid conditionals + This seems like an impossible task. Upon first hearing this, most people say, "how am I supposed to do anything without an `if` statement?" The answer is that you can use polymorphism to achieve the same task in many cases. The second @@ -706,10 +707,14 @@ are telling your user that your function does more than one thing. Remember, just do one thing. **Bad:** + ```php -class Airplane { +class Airplane +{ // ... - public function getCruisingAltitude() { + + public function getCruisingAltitude() + { switch ($this->type) { case '777': return $this->getMaxAltitude() - $this->getPassengerCount(); @@ -722,33 +727,45 @@ class Airplane { } ``` -**Good**: +**Good:** + ```php -class Airplane { +class Airplane +{ // ... } -class Boeing777 extends Airplane { +class Boeing777 extends Airplane +{ // ... - public function getCruisingAltitude() { + + public function getCruisingAltitude() + { return $this->getMaxAltitude() - $this->getPassengerCount(); } } -class AirForceOne extends Airplane { +class AirForceOne extends Airplane +{ // ... - public function getCruisingAltitude() { + + public function getCruisingAltitude() + { return $this->getMaxAltitude(); } } -class Cessna extends Airplane { +class Cessna extends Airplane +{ // ... - public function getCruisingAltitude() { + + public function getCruisingAltitude() + { return $this->getMaxAltitude() - $this->getFuelExpenditure(); } } ``` + **[⬆ back to top](#table-of-contents)** ### Avoid type-checking (part 1) From b218f288c8a49da53f6eb7934d9f96eb5a3fa2b5 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 5 Sep 2017 18:54:01 +0300 Subject: [PATCH 072/210] use PSR CS --- README.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 48977655..17763c05 100644 --- a/README.md +++ b/README.md @@ -819,34 +819,40 @@ function combine(int $val1, int $val2) **[⬆ back to top](#table-of-contents)** ### Remove dead code + Dead code is just as bad as duplicate code. There's no reason to keep it in your codebase. If it's not being called, get rid of it! It will still be safe in your version history if you still need it. **Bad:** + ```php -function oldRequestModule($url) { +function oldRequestModule($url) +{ // ... } -function newRequestModule($url) { +function newRequestModule($url) +{ // ... } $request = newRequestModule($requestUrl); inventoryTracker('apples', $request, 'www.inventory-awesome.io'); - ``` -**Good**: +**Good:** + ```php -function requestModule($url) { +function requestModule($url) +{ // ... } $request = requestModule($requestUrl); inventoryTracker('apples', $request, 'www.inventory-awesome.io'); ``` + **[⬆ back to top](#table-of-contents)** From 03af7a08961c1b011e6297ddf3f361700ad308b2 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 5 Sep 2017 18:56:35 +0300 Subject: [PATCH 073/210] use PSR CS --- README.md | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 48977655..707853e9 100644 --- a/README.md +++ b/README.md @@ -852,6 +852,7 @@ inventoryTracker('apples', $request, 'www.inventory-awesome.io'); ## **Objects and Data Structures** ### Use getters and setters + In PHP you can set `public`, `protected` and `private` keywords for methods. Using it, you can control properties modification on an object. @@ -868,8 +869,10 @@ Additionally, this is part of Open/Closed principle, from object-oriented design principles. **Bad:** + ```php -class BankAccount { +class BankAccount +{ public $balance = 1000; } @@ -879,27 +882,34 @@ $bankAccount = new BankAccount(); $bankAccount->balance -= 100; ``` -**Good**: +**Good:** + ```php -class BankAccount { +class BankAccount +{ private $balance; - - public function __construct($balance = 1000) { + + public function __construct($balance = 1000) + { $this->balance = $balance; } - - public function withdrawBalance($amount) { + + public function withdrawBalance($amount) + { if ($amount > $this->balance) { throw new \Exception('Amount greater than available balance.'); } + $this->balance -= $amount; } - - public function depositBalance($amount) { + + public function depositBalance($amount) + { $this->balance += $amount; } - - public function getBalance() { + + public function getBalance() + { return $this->balance; } } @@ -911,10 +921,9 @@ $bankAccount->withdrawBalance($shoesPrice); // Get balance $balance = $bankAccount->getBalance(); - ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#table-of-contents)** ### Make objects have private/protected members From bf037a3661603c9ba007765fce38b5aa276b0959 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 5 Sep 2017 19:00:45 +0300 Subject: [PATCH 074/210] use PSR CS --- README.md | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 48977655..be8776c8 100644 --- a/README.md +++ b/README.md @@ -919,11 +919,14 @@ $balance = $bankAccount->getBalance(); ### Make objects have private/protected members **Bad:** + ```php -class Employee { +class Employee +{ public $name; - - public function __construct($name) { + + public function __construct($name) + { $this->name = $name; } } @@ -932,12 +935,15 @@ $employee = new Employee('John Doe'); echo 'Employee name: '.$employee->name; // Employee name: John Doe ``` -**Good**: +**Good:** + ```php -class Employee { - protected $name; +class Employee +{ + private $name; - public function __construct($name) { + public function __construct($name) + { $this->name = $name; } @@ -949,6 +955,7 @@ class Employee { $employee = new Employee('John Doe'); echo 'Employee name: '.$employee->getName(); // Employee name: John Doe ``` + **[⬆ back to top](#table-of-contents)** From 2a9cef35718463b1d3a0f3b668c70adc89af7e37 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 5 Sep 2017 19:43:35 +0300 Subject: [PATCH 075/210] remove invalid comment --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 49fb8814..d89bf872 100644 --- a/README.md +++ b/README.md @@ -1447,8 +1447,6 @@ class SuperWorker implements Worker class Manager { - /** @var WorkerInterface $worker **/ - private $worker; public function __construct(Worker $worker) From 6f59430697b13bb6f40cb9bdf633cdcf3378df1b Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 5 Sep 2017 21:43:43 +0300 Subject: [PATCH 076/210] rename classes and interface --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index d89bf872..7b3b4717 100644 --- a/README.md +++ b/README.md @@ -1389,7 +1389,7 @@ it makes your code hard to refactor. **Bad:** ```php -class Worker +class Employee { public function work() { @@ -1397,7 +1397,7 @@ class Worker } } -class SuperWorker extends Worker +class Robot extends Employee { public function work() { @@ -1407,16 +1407,16 @@ class SuperWorker extends Worker class Manager { - private $worker; + private $employee; - public function __construct(Worker $worker) + public function __construct(Employee $employee) { - $this->worker = $worker; + $this->employee = $employee; } public function manage() { - $this->worker->work(); + $this->employee->work(); } } ``` @@ -1424,12 +1424,12 @@ class Manager **Good:** ```php -interface Worker +interface Employee { public function work(); } -class SumeWorker implements Worker +class Human implements Employee { public function work() { @@ -1437,7 +1437,7 @@ class SumeWorker implements Worker } } -class SuperWorker implements Worker +class Robot implements Employee { public function work() { @@ -1447,16 +1447,16 @@ class SuperWorker implements Worker class Manager { - private $worker; + private $employee; - public function __construct(Worker $worker) + public function __construct(Employee $employee) { - $this->worker = $worker; + $this->employee = $employee; } public function manage() { - $this->worker->work(); + $this->employee->work(); } } ``` From e1d22a8ed772df967ac6e58bfaaf59fe868093ab Mon Sep 17 00:00:00 2001 From: zouyi Date: Wed, 6 Sep 2017 09:42:10 +0800 Subject: [PATCH 077/210] Update README.md add #table-of-contents --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bf68a53e..90291507 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ 4. [I: Interface Segregation Principle (ISP)](#interface-segregation-principle-isp) 5. [D: Dependency Inversion Principle (DIP)](#dependency-inversion-principle-dip) 6. [Don’t repeat yourself (DRY)](#dont-repeat-yourself-dry) + 7. [Tanslations](#Translations) ## Introduction @@ -1731,7 +1732,7 @@ function showList($employees) -## Translation +## Translations This is also available in other languages: - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese**: From b9d4c3025134ffeaadd4f64252323be29792a8f5 Mon Sep 17 00:00:00 2001 From: zouyi Date: Wed, 6 Sep 2017 09:44:07 +0800 Subject: [PATCH 078/210] Update README.md use lower case --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 90291507..7bca6fbf 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ 4. [I: Interface Segregation Principle (ISP)](#interface-segregation-principle-isp) 5. [D: Dependency Inversion Principle (DIP)](#dependency-inversion-principle-dip) 6. [Don’t repeat yourself (DRY)](#dont-repeat-yourself-dry) - 7. [Tanslations](#Translations) + 7. [Tanslations](#translations) ## Introduction From b82c80d371c306b2608de20181cec235e16cfe8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emin=20=C5=9Een?= Date: Wed, 6 Sep 2017 09:20:12 +0300 Subject: [PATCH 079/210] Correct CS in objects --- README.md | 186 +++++++++++++++++++++++++++++------------------------- 1 file changed, 99 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index bf69a484..32af8d56 100644 --- a/README.md +++ b/README.md @@ -1041,20 +1041,24 @@ your codebase. **Bad:** ```php -class UserSettings { +class UserSettings +{ private $user; - public function __construct($user) { + public function __construct($user) + { $this->user = $user; } - public function changeSettings($settings) { + public function changeSettings($settings) + { if ($this->verifyCredentials()) { // ... } } - private function verifyCredentials() { + private function verifyCredentials() + { // ... } } @@ -1062,28 +1066,35 @@ class UserSettings { **Good:** ```php -class UserAuth { +class UserAuth +{ private $user; - public function __construct($user) { + public function __construct($user) + { $this->user = $user; } - public function verifyCredentials() { + public function verifyCredentials() + { // ... } } -class UserSettings { +class UserSettings +{ private $user; + private $auth; - public function __construct($user) { + public function __construct($user) + { $this->user = $user; $this->auth = new UserAuth($user); } - - public function changeSettings($settings) { + + public function changeSettings($settings) + { if ($this->auth->verifyCredentials()) { // ... } @@ -1117,6 +1128,7 @@ class AjaxAdapter extends Adapter public function __construct() { parent::__construct(); + $this->name = 'ajaxAdapter'; } } @@ -1126,6 +1138,7 @@ class NodeAdapter extends Adapter public function __construct() { parent::__construct(); + $this->name = 'nodeAdapter'; } } @@ -1138,7 +1151,7 @@ class HttpRequester { $this->adapter = $adapter; } - + public function fetch($url) { $adapterName = $this->adapter->getName(); @@ -1149,12 +1162,12 @@ class HttpRequester return $this->makeHttpCall($url); } } - + protected function makeAjaxCall($url) { // request and return promise } - + protected function makeHttpCall($url) { // request and return promise @@ -1224,14 +1237,8 @@ get into trouble. ```php class Rectangle { - protected $width; - protected $height; - - public function __construct() - { - $this->width = 0; - $this->height = 0; - } + protected $width = 0; + protected $height = 0; public function render($area) { @@ -1286,8 +1293,8 @@ renderLargeRectangles($rectangles); ```php abstract class Shape { - protected $width; - protected $height; + protected $width = 0; + protected $height = 0; abstract public function getArea(); @@ -1299,13 +1306,6 @@ abstract class Shape class Rectangle extends Shape { - public function __construct() - { - parent::__construct(); - $this->width = 0; - $this->height = 0; - } - public function setWidth($width) { $this->width = $width; @@ -1324,11 +1324,7 @@ class Rectangle extends Shape class Square extends Shape { - public function __construct() - { - parent::__construct(); - $this->length = 0; - } + protected $length = 0; public function setLength($length) { @@ -1554,28 +1550,29 @@ and you can chain further class methods onto it. **Bad:** ```php -class Car { - private $make, $model, $color; - - public function __construct() { - $this->make = 'Honda'; - $this->model = 'Accord'; - $this->color = 'white'; - } - - public function setMake($make) { +class Car +{ + private $make = 'Honda'; + private $model = 'Accord'; + private $color = 'white'; + + public function setMake($make) + { $this->make = $make; } - - public function setModel($model) { + + public function setModel($model) + { $this->model = $model; } - - public function setColor($color) { + + public function setColor($color) + { $this->color = $color; } - - public function dump() { + + public function dump() + { var_dump($this->make, $this->model, $this->color); } } @@ -1589,37 +1586,38 @@ $car->dump(); **Good:** ```php -class Car { - private $make, $model, $color; - - public function __construct() { - $this->make = 'Honda'; - $this->model = 'Accord'; - $this->color = 'white'; - } - - public function setMake($make) { +class Car +{ + private $make = 'Honda'; + private $model = 'Accord'; + private $color = 'white'; + + public function setMake($make) + { $this->make = $make; // NOTE: Returning this for chaining return $this; } - - public function setModel($model) { + + public function setModel($model) + { $this->model = $model; - + // NOTE: Returning this for chaining return $this; } - - public function setColor($color) { + + public function setColor($color) + { $this->color = $color; - + // NOTE: Returning this for chaining return $this; } - - public function dump() { + + public function dump() + { var_dump($this->make, $this->model, $this->color); } } @@ -1652,54 +1650,68 @@ relationship (Human->Animal vs. User->UserDetails). **Bad:** ```php -class Employee { - private $name, $email; +class Employee +{ + private $name; + private $email; - public function __construct($name, $email) { + public function __construct($name, $email) + { $this->name = $name; $this->email = $email; } - + // ... } // Bad because Employees "have" tax data. // EmployeeTaxData is not a type of Employee -class EmployeeTaxData extends Employee { - private $ssn, $salary; +class EmployeeTaxData extends Employee +{ + private $ssn; + private $salary; - public function __construct($name, $email, $ssn, $salary) { + public function __construct($name, $email, $ssn, $salary) + { parent::__construct($name, $email); + $this->ssn = $ssn; $this->salary = $salary; } - + // ... } ``` **Good:** ```php -class EmployeeTaxData { - private $ssn, $salary; +class EmployeeTaxData +{ + private $ssn; + private $salary; - public function __construct($ssn, $salary) { + public function __construct($ssn, $salary) + { $this->ssn = $ssn; $this->salary = $salary; } - + // ... } -class Employee { - private $name, $email, $taxData; - - public function __construct($name, $email) { +class Employee +{ + private $name; + private $email; + private $taxData; + + public function __construct($name, $email) + { $this->name = $name; $this->email = $email; } - + public function setTaxData($ssn, $salary) { $this->taxData = new EmployeeTaxData($ssn, $salary); } From 9f1be532f766c8aaeb9675dff4960a1bea4d149a Mon Sep 17 00:00:00 2001 From: Amit Merchant Date: Wed, 6 Sep 2017 12:15:49 +0530 Subject: [PATCH 080/210] Different function name used in the example. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bf69a484..b8692864 100644 --- a/README.md +++ b/README.md @@ -287,7 +287,7 @@ function emailClients($clients) { ```php function emailClients($clients) { $activeClients = activeClients($clients); - array_walk($activeClients, 'email'); + array_walk($activeClients, 'activeClients'); } function activeClients($clients) { From 4da91b55098eb5b5c312f523dcccc4593b04acd7 Mon Sep 17 00:00:00 2001 From: Amit Merchant Date: Wed, 6 Sep 2017 12:24:09 +0530 Subject: [PATCH 081/210] Fixed a typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bf69a484..3c28274b 100644 --- a/README.md +++ b/README.md @@ -627,7 +627,7 @@ And now you must use instance of `Configuration` in your application. **[⬆ back to top](#table-of-contents)** ### Don't use a Singleton pattern -Singleton is a [anti-pattern](https://en.wikipedia.org/wiki/Singleton_pattern). +Singleton is an [anti-pattern](https://en.wikipedia.org/wiki/Singleton_pattern). **Bad:** From 97381f5048534de86eff00c2f17b714f06e471da Mon Sep 17 00:00:00 2001 From: Amit Merchant Date: Wed, 6 Sep 2017 12:32:19 +0530 Subject: [PATCH 082/210] Fixed more typos. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b8692864..19214e44 100644 --- a/README.md +++ b/README.md @@ -842,7 +842,7 @@ and you use PHP 7+ and you can't use polymorphism but you still feel the need to type-check, you should consider [type declaration](http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration) or strict mode. It provides you with static typing on top of standard PHP syntax. -The problem with manually type-checking is that doing it well requires so much +The problem with manually type-checking is that doing it will require so much extra verbiage that the faux "type-safety" you get doesn't make up for the lost readability. Keep your PHP clean, write good tests, and have good code reviews. Otherwise, do all of that but with PHP strict type declaration or strict mode. From cbfad2167466ffa298ad686bbc93fe8cdeeb5385 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Wed, 6 Sep 2017 11:27:40 +0300 Subject: [PATCH 083/210] use json const --- README.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 48977655..3601b68b 100644 --- a/README.md +++ b/README.md @@ -56,27 +56,25 @@ getUser(); **[⬆ back to top](#table-of-contents)** ### Use searchable names + We will read more code than we will ever write. It's important that the code we do write is readable and searchable. By *not* naming variables that end up being meaningful for understanding our program, we hurt our readers. Make your names searchable. **Bad:** -```php -// What the heck is 86400 for? -addExpireAt(86400); +```php +// What the heck is 448 for? +$result = serialize($data, 448); ``` -**Good**: -```php -// Declare them as capitalized `const` globals. -interface DateGlobal { - const SECONDS_IN_A_DAY = 86400; -} +**Good:** -addExpireAt(DateGlobal::SECONDS_IN_A_DAY); +```php +$json = serialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); ``` + **[⬆ back to top](#table-of-contents)** From e507f54213a6f288741536c8dd8a4ccdf132310b Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Wed, 6 Sep 2017 12:00:39 +0300 Subject: [PATCH 084/210] add user access example --- README.md | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3601b68b..1a32ae19 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ getUser(); ``` **[⬆ back to top](#table-of-contents)** -### Use searchable names +### Use searchable names (part 1) We will read more code than we will ever write. It's important that the code we do write is readable and searchable. By *not* naming variables that end up being meaningful for @@ -75,6 +75,34 @@ $result = serialize($data, 448); $json = serialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); ``` +### Use searchable names (part 2) + +**Bad:** + +```php +// What the heck is 4 for? +if ($user->access & 4) { +} +``` + +**Good:** + +```php +class User +{ + const ACCESS_NONE = 0; // 0000 + const ACCESS_READ = 1 << 0; // 0001 + const ACCESS_CREATE = 1 << 1; // 0010 + const ACCESS_EDIT = 1 << 2; // 0100 + const ACCESS_DELETE = 1 << 3; // 1000 + const ACCESS_ALL = self::ACCESS_READ | self::ACCESS_CREATE | self::ACCESS_EDIT | self::ACCESS_DELETE; // 1111 +} + +if ($user->access & User::ACCESS_EDIT) { + // do edit ... +} +``` + **[⬆ back to top](#table-of-contents)** From 51e023425c6c5575d7829d266679691dfaca1438 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Wed, 6 Sep 2017 12:24:29 +0300 Subject: [PATCH 085/210] rename const ACCESS_ALL to ACCESS_FULL --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1a32ae19..29ca17b1 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ $json = serialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESC ```php // What the heck is 4 for? if ($user->access & 4) { + // ... } ``` @@ -95,7 +96,7 @@ class User const ACCESS_CREATE = 1 << 1; // 0010 const ACCESS_EDIT = 1 << 2; // 0100 const ACCESS_DELETE = 1 << 3; // 1000 - const ACCESS_ALL = self::ACCESS_READ | self::ACCESS_CREATE | self::ACCESS_EDIT | self::ACCESS_DELETE; // 1111 + const ACCESS_FULL = self::ACCESS_READ | self::ACCESS_CREATE | self::ACCESS_EDIT | self::ACCESS_DELETE; // 1111 } if ($user->access & User::ACCESS_EDIT) { From 4a1cdae28e7e3f42dd31382c0533530e4da24034 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Wed, 6 Sep 2017 13:26:34 +0300 Subject: [PATCH 086/210] use my serializer. not a PHP function serialize() --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 29ca17b1..35e9e1db 100644 --- a/README.md +++ b/README.md @@ -66,13 +66,13 @@ Make your names searchable. ```php // What the heck is 448 for? -$result = serialize($data, 448); +$result = $serializer->serialize($data, 448); ``` **Good:** ```php -$json = serialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); +$json = $serializer->serialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); ``` ### Use searchable names (part 2) From 05e0bbc564da4e82091a29c033ba452f47ebbfcf Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Wed, 6 Sep 2017 13:57:45 +0300 Subject: [PATCH 087/210] Not use bitwise offset. It's too complicated --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 35e9e1db..b2eb1731 100644 --- a/README.md +++ b/README.md @@ -91,12 +91,12 @@ if ($user->access & 4) { ```php class User { - const ACCESS_NONE = 0; // 0000 - const ACCESS_READ = 1 << 0; // 0001 - const ACCESS_CREATE = 1 << 1; // 0010 - const ACCESS_EDIT = 1 << 2; // 0100 - const ACCESS_DELETE = 1 << 3; // 1000 - const ACCESS_FULL = self::ACCESS_READ | self::ACCESS_CREATE | self::ACCESS_EDIT | self::ACCESS_DELETE; // 1111 + const ACCESS_NONE = 0; + const ACCESS_READ = 1; + const ACCESS_CREATE = 2; + const ACCESS_EDIT = 4; + const ACCESS_DELETE = 8; + const ACCESS_FULL = self::ACCESS_READ | self::ACCESS_CREATE | self::ACCESS_EDIT | self::ACCESS_DELETE; } if ($user->access & User::ACCESS_EDIT) { From 4b4808c0d633c1ac153e5ce023cf1e0780e7eb19 Mon Sep 17 00:00:00 2001 From: Matan Noam Shavit Date: Wed, 6 Sep 2017 08:33:05 -0400 Subject: [PATCH 088/210] spelling correction Tanslations -> Translations --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bf69a484..f9494444 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ 4. [I: Interface Segregation Principle (ISP)](#interface-segregation-principle-isp) 5. [D: Dependency Inversion Principle (DIP)](#dependency-inversion-principle-dip) 6. [Don’t repeat yourself (DRY)](#dont-repeat-yourself-dry) - 7. [Tanslations](#translations) + 7. [Translations](#translations) ## Introduction From 7ec6881e560e85557b80b122a5621e01bdcaecd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Votruba?= Date: Wed, 6 Sep 2017 22:02:54 +0200 Subject: [PATCH 089/210] README: make human headline --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b9420482..ff063783 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# clean-code-php +# Clean Code PHP ## Table of Contents 1. [Introduction](#introduction) From f9e463afd3409f5ba473fc05317b79d72bd18493 Mon Sep 17 00:00:00 2001 From: Amit Merchant Date: Thu, 7 Sep 2017 10:36:46 +0530 Subject: [PATCH 090/210] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 19214e44..9b85f750 100644 --- a/README.md +++ b/README.md @@ -287,7 +287,7 @@ function emailClients($clients) { ```php function emailClients($clients) { $activeClients = activeClients($clients); - array_walk($activeClients, 'activeClients'); + array_walk($activeClients, 'email'); } function activeClients($clients) { From 3e3bac6a290cc0b6241facf522aba1d085a09201 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 7 Sep 2017 09:00:20 +0300 Subject: [PATCH 091/210] leave only CRUD access --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b2eb1731..36277a12 100644 --- a/README.md +++ b/README.md @@ -91,15 +91,13 @@ if ($user->access & 4) { ```php class User { - const ACCESS_NONE = 0; const ACCESS_READ = 1; const ACCESS_CREATE = 2; - const ACCESS_EDIT = 4; +    const ACCESS_UPDATE = 4; const ACCESS_DELETE = 8; - const ACCESS_FULL = self::ACCESS_READ | self::ACCESS_CREATE | self::ACCESS_EDIT | self::ACCESS_DELETE; } -if ($user->access & User::ACCESS_EDIT) { +if ($user->access & User::ACCESS_UPDATE) { // do edit ... } ``` From a5de98d475410bd8a0776dbeb99c6fa4841f2697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Kobla=C5=84ski?= Date: Thu, 7 Sep 2017 09:55:05 +0200 Subject: [PATCH 092/210] Upgrade of a GOOD example for section "Avoid conditionals" --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ff063783..4de7d355 100644 --- a/README.md +++ b/README.md @@ -766,12 +766,14 @@ class Airplane **Good:** ```php -class Airplane +interface Airplane { // ... + + public function getCruisingAltitude(); } -class Boeing777 extends Airplane +class Boeing777 implements Airplane { // ... @@ -781,7 +783,7 @@ class Boeing777 extends Airplane } } -class AirForceOne extends Airplane +class AirForceOne implements Airplane { // ... @@ -791,7 +793,7 @@ class AirForceOne extends Airplane } } -class Cessna extends Airplane +class Cessna implements Airplane { // ... From 0de08e12751d3368912de7926cf6ba1e96a399e9 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 7 Sep 2017 11:19:36 +0300 Subject: [PATCH 093/210] change preg_split() to explode() --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ff063783..215cd637 100644 --- a/README.md +++ b/README.md @@ -545,7 +545,7 @@ function splitIntoFirstAndLastName() { global $name; - $name = preg_split('/ /', $name); + $name = explode(' ', $name); } splitIntoFirstAndLastName(); @@ -558,7 +558,7 @@ var_dump($name); // ['Ryan', 'McDermott']; ```php function splitIntoFirstAndLastName($name) { - return preg_split('/ /', $name); + return explode(' ', $name); } $name = 'Ryan McDermott'; From 9f0fec652274b39aca7b4eaf6228dd27504a8f0d Mon Sep 17 00:00:00 2001 From: Srihari Thalla Date: Thu, 7 Sep 2017 14:32:44 +0530 Subject: [PATCH 094/210] Fix minor typo in Interface Segregation Principle --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ff063783..d217b04c 100644 --- a/README.md +++ b/README.md @@ -1400,7 +1400,7 @@ class Robot implements Employee public function eat() { - //.... robot can't eating, but it must implement this method + //.... robot can't eat, but it must implement this method } } ``` From e9c915b100ba04521e2803da09500040a7e447db Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 7 Sep 2017 12:16:39 +0300 Subject: [PATCH 095/210] function split() is DEPRECATED --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ff063783..d703dfb1 100644 --- a/README.md +++ b/README.md @@ -356,7 +356,7 @@ function parseBetterJSAlternative($code) // ... ]; - $statements = split(' ', $code); + $statements = explode(' ', $code); $tokens = []; foreach ($regexes as $regex) { foreach ($statements as $statement) { @@ -386,7 +386,7 @@ function tokenize($code) // ... ]; - $statements = split(' ', $code); + $statements = explode(' ', $code); $tokens = []; foreach ($regexes as $regex) { foreach ($statements as $statement) { @@ -430,7 +430,7 @@ class Tokenizer // ... ]; - $statements = split(' ', $code); + $statements = explode(' ', $code); $tokens = []; foreach ($regexes as $regex) { foreach ($statements as $statement) { From bff562b4d27dba283e1ed4970632a2d03f557474 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 7 Sep 2017 12:23:35 +0300 Subject: [PATCH 096/210] correct text and code style --- README.md | 193 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 118 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index ff063783..8500dd7b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Clean Code PHP ## Table of Contents + 1. [Introduction](#introduction) 2. [Variables](#variables) 3. [Functions](#functions) @@ -27,62 +28,74 @@ years of collective experience by the authors of *Clean Code*. Inspired from [clean-code-javascript](https://github.com/ryanmcdermott/clean-code-javascript) -## **Variables** +## Variables + ### Use meaningful and pronounceable variable names **Bad:** + ```php $ymdstr = $moment->format('y-m-d'); ``` -**Good**: +**Good:** + ```php $currentDate = $moment->format('y-m-d'); ``` + **[⬆ back to top](#table-of-contents)** ### Use the same vocabulary for the same type of variable **Bad:** + ```php getUserInfo(); getClientData(); getCustomerRecord(); ``` -**Good**: +**Good:** + ```php getUser(); ``` + **[⬆ back to top](#table-of-contents)** ### Use searchable names + We will read more code than we will ever write. It's important that the code we do write is readable and searchable. By *not* naming variables that end up being meaningful for understanding our program, we hurt our readers. Make your names searchable. **Bad:** + ```php // What the heck is 86400 for? addExpireAt(86400); - ``` -**Good**: +**Good:** + ```php // Declare them as capitalized `const` globals. -interface DateGlobal { +interface DateGlobal +{ const SECONDS_IN_A_DAY = 86400; } addExpireAt(DateGlobal::SECONDS_IN_A_DAY); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#table-of-contents)** ### Use explanatory variables + **Bad:** + ```php $address = 'One Infinite Loop, Cupertino 95014'; $cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/'; @@ -91,7 +104,7 @@ preg_match($cityZipCodeRegex, $address, $matches); saveCityZipCode($matches[1], $matches[2]); ``` -**Not bad**: +**Not bad:** It's better, but we are still heavily dependent on regex. @@ -104,9 +117,10 @@ list(, $city, $zipCode) = $matches; saveCityZipCode($city, $zipCode); ``` -**Good**: +**Good:** Decrease dependence on regex by naming subpatterns. + ```php $address = 'One Infinite Loop, Cupertino 95014'; $cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(?.+?)\s*(?\d{5})?$/'; @@ -114,13 +128,16 @@ preg_match($cityZipCodeRegex, $address, $matches); saveCityZipCode($matches['city'], $matches['zipCode']); ``` + **[⬆ back to top](#table-of-contents)** ### Avoid Mental Mapping + Don’t force the reader of your code to translate what the variable means. Explicit is better than implicit. **Bad:** + ```php $l = ['Austin', 'New York', 'San Francisco']; @@ -136,7 +153,8 @@ for ($i = 0; $i < count($l); $i++) { } ``` -**Good**: +**Good:** + ```php $locations = ['Austin', 'New York', 'San Francisco']; @@ -147,10 +165,10 @@ foreach ($locations as $location) { // ... // ... dispatch($location); -}); +} ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#table-of-contents)** ### Don't add unneeded context @@ -170,7 +188,7 @@ class Car } ``` -**Good**: +**Good:** ```php class Car @@ -210,7 +228,7 @@ function createMicrobrewery($name = null) } ``` -**Good**: +**Good:** If you support only PHP 7+, then you can use [type hinting](http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration) and be sure that the `$breweryName` will not be `NULL`. @@ -222,8 +240,11 @@ function createMicrobrewery(string $breweryName = 'Hipster Brew Co.') ``` **[⬆ back to top](#table-of-contents)** -## **Functions** + +## Functions + ### Function arguments (2 or fewer ideally) + Limiting the amount of function parameters is incredibly important because it makes testing your function easier. Having more than three leads to a combinatorial explosion where you have to test tons of different cases with each separate argument. @@ -234,13 +255,16 @@ arguments then your function is trying to do too much. In cases where it's not, of the time a higher-level object will suffice as an argument. **Bad:** + ```php -function createMenu($title, $body, $buttonText, $cancellable) { +function createMenu($title, $body, $buttonText, $cancellable) +{ // ... } ``` -**Good**: +**Good:** + ```php class MenuConfig { @@ -256,15 +280,16 @@ $config->body = 'Bar'; $config->buttonText = 'Baz'; $config->cancellable = true; -function createMenu(MenuConfig $config) { +function createMenu(MenuConfig $config) +{ // ... } - ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#table-of-contents)** ### Functions should do one thing + This is by far the most important rule in software engineering. When functions do more than one thing, they are harder to compose, test, and reason about. When you can isolate a function to just one action, they can be refactored easily and your code will read much @@ -273,7 +298,8 @@ of many developers. **Bad:** ```php -function emailClients($clients) { +function emailClients($clients) +{ foreach ($clients as $client) { $clientRecord = $db->find($client); if ($clientRecord->isActive()) { @@ -283,22 +309,28 @@ function emailClients($clients) { } ``` -**Good**: +**Good:** + ```php -function emailClients($clients) { +function emailClients($clients) +{ $activeClients = activeClients($clients); array_walk($activeClients, 'email'); } -function activeClients($clients) { +function activeClients($clients) +{ return array_filter($clients, 'isClientActive'); } -function isClientActive($client) { +function isClientActive($client) +{ $clientRecord = $db->find($client); + return $clientRecord->isActive(); } ``` + **[⬆ back to top](#table-of-contents)** ### Function names should say what they do @@ -355,7 +387,7 @@ function parseBetterJSAlternative($code) $regexes = [ // ... ]; - + $statements = split(' ', $code); $tokens = []; foreach ($regexes as $regex) { @@ -363,12 +395,12 @@ function parseBetterJSAlternative($code) // ... } } - + $ast = []; foreach ($tokens as $token) { // lex... } - + foreach ($ast as $node) { // parse... } @@ -385,7 +417,7 @@ function tokenize($code) $regexes = [ // ... ]; - + $statements = split(' ', $code); $tokens = []; foreach ($regexes as $regex) { @@ -393,7 +425,7 @@ function tokenize($code) $tokens[] = /* ... */; } } - + return $tokens; } @@ -403,7 +435,7 @@ function lexer($tokens) foreach ($tokens as $token) { $ast[] = /* ... */; } - + return $ast; } @@ -441,9 +473,7 @@ class Tokenizer return $tokens; } } -``` -```php class Lexer { public function lexify($tokens) @@ -456,9 +486,7 @@ class Lexer return $ast; } } -``` -```php class BetterJSAlternative { private $tokenizer; @@ -502,7 +530,7 @@ function createFile($name, $temp = false) } ``` -**Good**: +**Good:** ```php function createFile($name) @@ -571,6 +599,7 @@ var_dump($newName); // ['Ryan', 'McDermott']; **[⬆ back to top](#table-of-contents)** ### Don't write to global functions + Polluting globals is a bad practice in many languages because you could clash with another library and the user of your API would be none-the-wiser until they get an exception in production. Let's think about an example: what if you wanted to have configuration array. @@ -627,6 +656,7 @@ And now you must use instance of `Configuration` in your application. **[⬆ back to top](#table-of-contents)** ### Don't use a Singleton pattern + Singleton is an [anti-pattern](https://en.wikipedia.org/wiki/Singleton_pattern). **Bad:** @@ -690,7 +720,7 @@ if ($article->state === 'published') { } ``` -**Good**: +**Good:** ```php if ($article->isPublished()) { @@ -824,7 +854,7 @@ function travelToTexas($vehicle) } ``` -**Good**: +**Good:** ```php function travelToTexas(Traveler $vehicle) @@ -860,7 +890,7 @@ function combine($val1, $val2) } ``` -**Good**: +**Good:** ```php function combine(int $val1, int $val2) @@ -909,7 +939,8 @@ inventoryTracker('apples', $request, 'www.inventory-awesome.io'); **[⬆ back to top](#table-of-contents)** -## **Objects and Data Structures** +## Objects and Data Structures + ### Use getters and setters In PHP you can set `public`, `protected` and `private` keywords for methods. @@ -1009,13 +1040,14 @@ echo 'Employee name: '.$employee->name; // Employee name: John Doe class Employee { private $name; - + public function __construct($name) { $this->name = $name; } - - public function getName() { + + public function getName() + { return $this->name; } } @@ -1026,10 +1058,10 @@ echo 'Employee name: '.$employee->getName(); // Employee name: John Doe **[⬆ back to top](#table-of-contents)** - -## **Classes** +## Classes ### Single Responsibility Principle (SRP) + As stated in Clean Code, "There should never be more than one reason for a class to change". It's tempting to jam-pack a class with a lot of functionality, like when you can only take one suitcase on your flight. The issue with this is @@ -1040,6 +1072,7 @@ it can be difficult to understand how that will affect other dependent modules i your codebase. **Bad:** + ```php class UserSettings { @@ -1049,14 +1082,14 @@ class UserSettings { $this->user = $user; } - + public function changeSettings($settings) { if ($this->verifyCredentials()) { // ... } } - + private function verifyCredentials() { // ... @@ -1065,6 +1098,7 @@ class UserSettings ``` **Good:** + ```php class UserAuth { @@ -1081,7 +1115,6 @@ class UserAuth } } - class UserSettings { private $user; @@ -1101,6 +1134,7 @@ class UserSettings } } ``` + **[⬆ back to top](#table-of-contents)** ### Open/Closed Principle (OCP) @@ -1163,12 +1197,12 @@ class HttpRequester } } - protected function makeAjaxCall($url) + private function makeAjaxCall($url) { // request and return promise } - protected function makeHttpCall($url) + private function makeHttpCall($url) { // request and return promise } @@ -1207,7 +1241,7 @@ class HttpRequester { $this->adapter = $adapter; } - + public function fetch($url) { return $this->adapter->request($url); @@ -1324,7 +1358,7 @@ class Rectangle extends Shape class Square extends Shape { - protected $length = 0; + private $length = 0; public function setLength($length) { @@ -1346,7 +1380,7 @@ function renderLargeRectangles($rectangles) $rectangle->setWidth(4); $rectangle->setHeight(5); } - + $area = $rectangle->getArea(); $rectangle->render($area); } @@ -1469,10 +1503,10 @@ it makes your code hard to refactor. ```php class Employee { - public function work() - { - // ....working - } + public function work() + { + // ....working + } } class Robot extends Employee @@ -1542,6 +1576,7 @@ class Manager **[⬆ back to top](#table-of-contents)** ### Use method chaining + This pattern is very useful and commonly used in many libraries such as PHPUnit and Doctrine. It allows your code to be expressive, and less verbose. For that reason, use method chaining and take a look at how clean your code @@ -1549,6 +1584,7 @@ will be. In your class functions, simply use `return $this` at the end of every and you can chain further class methods onto it. **Bad:** + ```php class Car { @@ -1585,6 +1621,7 @@ $car->dump(); ``` **Good:** + ```php class Car { @@ -1628,9 +1665,11 @@ $car = (new Car()) ->setModel('F-150') ->dump(); ``` + **[⬆ back to top](#table-of-contents)** ### Prefer composition over inheritance + As stated famously in [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) by the Gang of Four, you should prefer composition over inheritance where you can. There are lots of good reasons to use inheritance and lots of good reasons to use composition. @@ -1649,12 +1688,13 @@ relationship (Human->Animal vs. User->UserDetails). (Change the caloric expenditure of all animals when they move). **Bad:** + ```php class Employee { private $name; private $email; - + public function __construct($name, $email) { $this->name = $name; @@ -1685,12 +1725,13 @@ class EmployeeTaxData extends Employee ``` **Good:** + ```php class EmployeeTaxData { private $ssn; private $salary; - + public function __construct($ssn, $salary) { $this->ssn = $ssn; @@ -1712,12 +1753,15 @@ class Employee $this->email = $email; } - public function setTaxData($ssn, $salary) { + public function setTaxData($ssn, $salary) + { $this->taxData = new EmployeeTaxData($ssn, $salary); } + // ... } ``` + **[⬆ back to top](#table-of-contents)** ## Don’t repeat yourself (DRY) @@ -1750,7 +1794,7 @@ updating multiple places anytime you want to change one thing. ```php function showDeveloperList($developers) { -    foreach ($developers as $developer) { + foreach ($developers as $developer) { $expectedSalary = $developer->calculateExpectedSalary(); $experience = $developer->getExperience(); $githubLink = $developer->getGithubLink(); @@ -1759,14 +1803,14 @@ function showDeveloperList($developers) $experience, $githubLink ]; - + render($data); } } function showManagerList($managers) { -    foreach ($managers as $manager) { + foreach ($managers as $manager) { $expectedSalary = $manager->calculateExpectedSalary(); $experience = $manager->getExperience(); $githubLink = $manager->getGithubLink(); @@ -1775,7 +1819,7 @@ function showManagerList($managers) $experience, $githubLink ]; - + render($data); } } @@ -1786,16 +1830,16 @@ function showManagerList($managers) ```php function showList($employees) { -    foreach ($employees as $employee) { -        $expectedSalary = $employee->calculateExpectedSalary(); -        $experience = $employee->getExperience(); -        $githubLink = $employee->getGithubLink(); + foreach ($employees as $employee) { + $expectedSalary = $employee->calculateExpectedSalary(); + $experience = $employee->getExperience(); + $githubLink = $employee->getGithubLink(); $data = [ $expectedSalary, $experience, $githubLink ]; - + render($data); } } @@ -1808,7 +1852,7 @@ It is better to use a compact version of the code. ```php function showList($employees) { -    foreach ($employees as $employee) { + foreach ($employees as $employee) { render([ $employee->calculateExpectedSalary(), $employee->getExperience(), @@ -1820,13 +1864,12 @@ function showList($employees) **[⬆ back to top](#table-of-contents)** - - ## Translations This is also available in other languages: - - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese**: - - [yangweijie/clean-code-php](https://github.com/yangweijie/clean-code-php) - - [php-cpm/clean-code-php](https://github.com/php-cpm/clean-code-php) + + * ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese:** + * [yangweijie/clean-code-php](https://github.com/yangweijie/clean-code-php) + * [php-cpm/clean-code-php](https://github.com/php-cpm/clean-code-php) **[⬆ back to top](#table-of-contents)** From 51f6769526c1e2886b73120de690b3240193b06a Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 7 Sep 2017 13:18:29 +0300 Subject: [PATCH 097/210] expand description of Singleton https://stackoverflow.com/a/138012/2455621 --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ff063783..8ea474eb 100644 --- a/README.md +++ b/README.md @@ -627,7 +627,12 @@ And now you must use instance of `Configuration` in your application. **[⬆ back to top](#table-of-contents)** ### Don't use a Singleton pattern -Singleton is an [anti-pattern](https://en.wikipedia.org/wiki/Singleton_pattern). + +Singleton is an [anti-pattern](https://en.wikipedia.org/wiki/Singleton_pattern). Paraphrased from Brian Button: + 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). + 2. They violate the [single responsibility principle](#user-content-single-responsibility-principle-srp): by virtue of the fact that they control their own creation and lifecycle. + 3. They inherently cause code to be tightly [coupled](#user-content-single-responsibility-principle-srp). This makes faking them out under test rather difficult in many cases. + 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 no for unit tests. Why? Because each unit test should be independent from the other. **Bad:** From 5bc8682740934437cff5400761d6e0a4ecbfb4e0 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 7 Sep 2017 13:24:32 +0300 Subject: [PATCH 098/210] correct link to Coupling --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8ea474eb..b27f7d15 100644 --- a/README.md +++ b/README.md @@ -631,7 +631,7 @@ And now you must use instance of `Configuration` in your application. Singleton is an [anti-pattern](https://en.wikipedia.org/wiki/Singleton_pattern). Paraphrased from Brian Button: 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). 2. They violate the [single responsibility principle](#user-content-single-responsibility-principle-srp): by virtue of the fact that they control their own creation and lifecycle. - 3. They inherently cause code to be tightly [coupled](#user-content-single-responsibility-principle-srp). This makes faking them out under test rather difficult in many cases. + 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. 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 no for unit tests. Why? Because each unit test should be independent from the other. **Bad:** From 9d547abb423f4101d314bd8adb81221c9499b5e7 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 7 Sep 2017 13:54:32 +0300 Subject: [PATCH 099/210] change vocabulary example #92 --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ff063783..65946cbd 100644 --- a/README.md +++ b/README.md @@ -46,8 +46,9 @@ $currentDate = $moment->format('y-m-d'); **Bad:** ```php getUserInfo(); -getClientData(); -getCustomerRecord(); +getUserData(); +getUserRecord(); +getUserProfile(); ``` **Good**: From 179cc1369e03dfca8e45eb162d5f484694c145de Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Thu, 7 Sep 2017 13:59:05 +0300 Subject: [PATCH 100/210] add thoughts by Misko Hevery --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index b27f7d15..c4d571a6 100644 --- a/README.md +++ b/README.md @@ -634,6 +634,8 @@ Singleton is an [anti-pattern](https://en.wikipedia.org/wiki/Singleton_pattern). 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. 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 no for unit tests. Why? Because each unit test should be independent from the other. +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/). + **Bad:** ```php From 5e52aa3f26eb6e289beb093fcfc82ae267c9c154 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Fri, 8 Sep 2017 21:33:05 +0300 Subject: [PATCH 101/210] not use config.php file --- README.md | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index ff063783..1d6cd78c 100644 --- a/README.md +++ b/README.md @@ -590,15 +590,6 @@ function config() **Good:** -Create PHP configuration file or something else - -```php -// config.php -return [ - 'foo' => 'bar', -]; -``` - ```php class Configuration { @@ -616,10 +607,12 @@ class Configuration } ``` -Load configuration from file and create instance of `Configuration` class +Load configuration and create instance of `Configuration` class ```php -$configuration = new Configuration($configuration); +$configuration = new Configuration([ + 'foo' => 'bar', +]); ``` And now you must use instance of `Configuration` in your application. From cd7733d0918054fc5a27748051f2a9ecb7393c18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Votruba?= Date: Fri, 8 Sep 2017 21:56:31 +0200 Subject: [PATCH 102/210] remove redundant spaces --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b27cd284..6aecefb9 100644 --- a/README.md +++ b/README.md @@ -409,7 +409,7 @@ function parseBetterJSAlternative($code) $regexes = [ // ... ]; - + $statements = explode(' ', $code); $tokens = []; foreach ($regexes as $regex) { @@ -439,7 +439,7 @@ function tokenize($code) $regexes = [ // ... ]; - + $statements = explode(' ', $code); $tokens = []; foreach ($regexes as $regex) { From 780af00c212905411771a7286bb07ed561e76722 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Sat, 9 Sep 2017 09:25:06 +0300 Subject: [PATCH 103/210] identified important parts --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9a8be43d..d68583ea 100644 --- a/README.md +++ b/README.md @@ -673,10 +673,10 @@ And now you must use instance of `Configuration` in your application. ### Don't use a Singleton pattern Singleton is an [anti-pattern](https://en.wikipedia.org/wiki/Singleton_pattern). Paraphrased from Brian Button: - 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). - 2. They violate the [single responsibility principle](#user-content-single-responsibility-principle-srp): by virtue of the fact that they control their own creation and lifecycle. - 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. - 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 no for unit tests. Why? Because each unit test should be independent from the other. + 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). + 2. They violate the [single responsibility principle](#single-responsibility-principle-srp): by virtue of the fact that **they control their own creation and lifecycle**. + 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. + 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. 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/). From a10956bff469395e77b283748ce9f817363a897d Mon Sep 17 00:00:00 2001 From: Jewei Mak Date: Sun, 10 Sep 2017 22:16:28 +0800 Subject: [PATCH 104/210] Avoid nesting too deeply and return early --- README.md | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/README.md b/README.md index d68583ea..068c0257 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,65 @@ saveCityZipCode($matches['city'], $matches['zipCode']); **[⬆ back to top](#table-of-contents)** +### Avoid nesting too deeply and return early + +Nesting if else too deeply makes reader difficult to follow. Explicit is better +than implicit. + +**Bad:** + +```php +function checkEmailExist() +{ + if (isset($_POST['action'])) { + if (isset($_POST['email'])) { + if (isset($_POST['user_id'])) { + $user = User::find($_POST['user_id']); + if ($user) { + $emails = $user->getEmails(); + if (count($emails) > 0) { + foreach ($emails as $email) { + if ($email === $_POST['email']) { + return true; + } + } + } + } + } + } + } + + return false; +} +``` + +**Good:** + +```php +function checkEmailExist() +{ + if (! isset($_POST['action'], $_POST['email'], $_POST['user_id'])) { + return false; + } + + $user = User::find($_POST['user_id']); + + if (! $user) { + return false; // Or throw exception. + } + + $emails = $user->getEmails(); + + if (empty($emails)) { + return false; + } + + return in_array($_POST['email'], $emails); +} +``` + +**[⬆ back to top](#table-of-contents)** + ### Avoid Mental Mapping Don’t force the reader of your code to translate what the variable means. From 2ba99292cd14974f76143908e84e49ed46ef85f5 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Mon, 11 Sep 2017 12:12:14 +0300 Subject: [PATCH 105/210] correct good example --- README.md | 87 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 48 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index af58d8bb..91c801ba 100644 --- a/README.md +++ b/README.md @@ -1181,28 +1181,25 @@ class Square extends Rectangle } } -function areaVerifier(Rectangle $rectangle) +function printArea(Rectangle $rectangle) { $rectangle->setWidth(4); $rectangle->setHeight(5); // BAD: Will return 25 for Square. Should be 20. - return $rectangle->area() == 20; + echo sprintf('%s has area %d.', get_class($rectangle), $rectangle->area()).PHP_EOL; } $rectangles = [new Rectangle(), new Square()]; foreach ($rectangles as $rectangle) { -    if (!areaVerifier($rectangle)) { - throw new Exception('Bad area!'); - } + printArea($rectangle); } ``` **Not bad:** You can solve the problem by making objects immutable. -But this is not the best solution, because the square specifies the invariants of the rectangle. ```php class Rectangle @@ -1240,41 +1237,64 @@ class Square extends Rectangle } } -function areaVerifier(Rectangle $rectangle) +function printArea(Rectangle $rectangle) { - return $rectangle->area() == $rectangle->width() * $rectangle->height(); + echo sprintf('%s has area %d.', get_class($rectangle), $rectangle->area()).PHP_EOL; } $rectangles = [new Rectangle(4, 5), new Square(5)]; foreach ($rectangles as $rectangle) { - if (!areaVerifier($rectangle)) { - throw new Exception('Bad area!'); - } + printArea($rectangle); } ``` +This solution no longer violates the LSP principle because we can use a subtype. + +```php +function printSquareArea(Square $rectangle) +{ + // ... +} + +printSquareArea(new Rectangle(4, 5)); +``` + +But this is not a best solution, because the square specifies the invariants of the rectangle. + **Good:** -The best way is separate the quadrangles. +The best way is separate the quadrangles and the allocation of a more general subtype for both shape. + Despite the apparent similarity of the square and the rectangle, they are different. A square has much in common with a rhombus, and a rectangle with a parallelogram, but they are not subtype. A square, a rectangle, a rhombus and a parallelogram are separate figures with their own properties, albeit similar. ```php -class Rectangle +interface Shape +{ + public function area(); +} + +class Rectangle implements Shape { private $width = 0; private $height = 0; - public function setWidth($width) + public function __construct($width, $height) { $this->width = $width; + $this->height = $height; } - public function setHeight($height) + public function width() { - $this->height = $height; + return $this->width; + } + + public function height() + { + return $this->height; } public function area() @@ -1283,46 +1303,35 @@ class Rectangle } } -class Square +class Square implements Shape { private $length = 0; - public function setLength($length) + public function __construct($length) { $this->length = $length; } + public function length() + { + return $this->length; + } + public function area() { return pow($this->length, 2); } } -function rectangleAreaVerifier(Rectangle $rectangle) +function printArea(Shape $shape) { - $rectangle->setWidth(4); - $rectangle->setHeight(5); - - return $rectangle->area() == 20; -} - -function squareAreaVerifier(Square $square) -{ - $square->setLength(5); - - return $square->area() == 25; -} - -$rectangle = new Rectangle(); - -if (!rectangleAreaVerifier($rectangle)) { - throw new Exception('Bad area!'); + echo sprintf('%s has area %d.', get_class($shape), $shape->area()).PHP_EOL; } -$square = new Square(); +$shapes = [new Rectangle(4, 5), new Square(5)]; -if (!squareAreaVerifier($square)) { - throw new Exception('Bad area!'); +foreach ($shapes as $shape) { + printArea($shape); } ``` From e7c25159bd65b3f6fa29b85ef4f41b0a1a65f8bc Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Mon, 11 Sep 2017 12:19:56 +0300 Subject: [PATCH 106/210] fix typo --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 91c801ba..cc2d024c 100644 --- a/README.md +++ b/README.md @@ -1252,7 +1252,7 @@ foreach ($rectangles as $rectangle) { This solution no longer violates the LSP principle because we can use a subtype. ```php -function printSquareArea(Square $rectangle) +function printSquareArea(Square $square) { // ... } @@ -1264,7 +1264,7 @@ But this is not a best solution, because the square specifies the invariants of **Good:** -The best way is separate the quadrangles and the allocation of a more general subtype for both shape. +The best way is separate the quadrangles and allocation of a more general subtype for both shapes. Despite the apparent similarity of the square and the rectangle, they are different. A square has much in common with a rhombus, and a rectangle with a parallelogram, but they are not subtype. From 5383a979d36a0abf6d27ec03b1503d3da8a7e974 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Mon, 11 Sep 2017 12:54:39 +0300 Subject: [PATCH 107/210] add links to every sections in table of contents --- README.md | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d68583ea..1583e619 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,40 @@ 1. [Introduction](#introduction) 2. [Variables](#variables) + * [Use meaningful and pronounceable variable names](#use-meaningful-and-pronounceable-variable-names) + * [Use the same vocabulary for the same type of variable](#use-the-same-vocabulary-for-the-same-type-of-variable) + * [Use searchable names (part 1)](#use-searchable-names-part-1) + * [Use searchable names (part 2)](#use-searchable-names-part-2) + * [Use explanatory variables](#use-explanatory-variables) + * [Avoid Mental Mapping](#avoid-mental-mapping) + * [Don't add unneeded context](#dont-add-unneeded-context) + * [Use default arguments instead of short circuiting or conditionals](use-default-arguments-instead-of-short-circuiting-or-conditionals) 3. [Functions](#functions) + * [Function arguments (2 or fewer ideally)](#function-arguments-2-or-fewer-ideally) + * [Functions should do one thing](#functions-should-do-one-thing) + * [Function names should say what they do](#function-names-should-say-what-they-do) + * [Functions should only be one level of abstraction](#functions-should-only-be-one-level-of-abstraction) + * [Don't use flags as function parameters](#dont-use-flags-as-function-parameters) + * [Avoid Side Effects](#avoid-side-effects) + * [Don't write to global functions](#dont-write-to-global-functions) + * [Don't use a Singleton pattern](#dont-use-a-singleton-pattern) + * [Encapsulate conditionals](#encapsulate-conditionals) + * [Avoid negative conditionals](#avoid-negative-conditionals) + * [Avoid conditionals](#avoid-conditionals) + * [Avoid type-checking (part 1)](#avoid-type-checking-part-1) + * [Avoid type-checking (part 2)](#avoid-type-checking-part-2) + * [Remove dead code](#remove-dead-code) 4. [Objects and Data Structures](#objects-and-data-structures) + * [Use getters and setters](#use-getters-and-setters) + * [Make objects have private/protected members](#make-objects-have-privateprotected-members) 5. [Classes](#classes) - 1. [S: Single Responsibility Principle (SRP)](#single-responsibility-principle-srp) - 2. [O: Open/Closed Principle (OCP)](#openclosed-principle-ocp) - 3. [L: Liskov Substitution Principle (LSP)](#liskov-substitution-principle-lsp) - 4. [I: Interface Segregation Principle (ISP)](#interface-segregation-principle-isp) - 5. [D: Dependency Inversion Principle (DIP)](#dependency-inversion-principle-dip) + * [S: Single Responsibility Principle (SRP)](#single-responsibility-principle-srp) + * [O: Open/Closed Principle (OCP)](#openclosed-principle-ocp) + * [L: Liskov Substitution Principle (LSP)](#liskov-substitution-principle-lsp) + * [I: Interface Segregation Principle (ISP)](#interface-segregation-principle-isp) + * [D: Dependency Inversion Principle (DIP)](#dependency-inversion-principle-dip) + * [Use method chaining](#use-method-chaining) + * [Prefer composition over inheritance](#prefer-composition-over-inheritance) 6. [Don’t repeat yourself (DRY)](#dont-repeat-yourself-dry) 7. [Translations](#translations) From 4a5cedb6fbe1bf5784e4707201242717d76c8585 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Mon, 11 Sep 2017 13:00:07 +0300 Subject: [PATCH 108/210] correct link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1583e619..8675bb40 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ * [Use explanatory variables](#use-explanatory-variables) * [Avoid Mental Mapping](#avoid-mental-mapping) * [Don't add unneeded context](#dont-add-unneeded-context) - * [Use default arguments instead of short circuiting or conditionals](use-default-arguments-instead-of-short-circuiting-or-conditionals) + * [Use default arguments instead of short circuiting or conditionals](#use-default-arguments-instead-of-short-circuiting-or-conditionals) 3. [Functions](#functions) * [Function arguments (2 or fewer ideally)](#function-arguments-2-or-fewer-ideally) * [Functions should do one thing](#functions-should-do-one-thing) From 1e615be3f1b3647b1301c9ea7c4653bfc3a68c8f Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Mon, 11 Sep 2017 13:43:25 +0300 Subject: [PATCH 109/210] move out SOLID section --- README.md | 395 +++++++++++++++++++++++++++--------------------------- 1 file changed, 199 insertions(+), 196 deletions(-) diff --git a/README.md b/README.md index d68583ea..9de28587 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,14 @@ 3. [Functions](#functions) 4. [Objects and Data Structures](#objects-and-data-structures) 5. [Classes](#classes) - 1. [S: Single Responsibility Principle (SRP)](#single-responsibility-principle-srp) - 2. [O: Open/Closed Principle (OCP)](#openclosed-principle-ocp) - 3. [L: Liskov Substitution Principle (LSP)](#liskov-substitution-principle-lsp) - 4. [I: Interface Segregation Principle (ISP)](#interface-segregation-principle-isp) - 5. [D: Dependency Inversion Principle (DIP)](#dependency-inversion-principle-dip) - 6. [Don’t repeat yourself (DRY)](#dont-repeat-yourself-dry) - 7. [Translations](#translations) + 6. [SOLID](#solid) + * [Single Responsibility Principle (SRP)](#single-responsibility-principle-srp) + * [Open/Closed Principle (OCP)](#openclosed-principle-ocp) + * [Liskov Substitution Principle (LSP)](#liskov-substitution-principle-lsp) + * [Interface Segregation Principle (ISP)](#interface-segregation-principle-isp) + * [Dependency Inversion Principle (DIP)](#dependency-inversion-principle-dip) + 7. [Don’t repeat yourself (DRY)](#dont-repeat-yourself-dry) + 8. [Translations](#translations) ## Introduction @@ -1083,6 +1084,197 @@ echo 'Employee name: '.$employee->getName(); // Employee name: John Doe ## Classes +### Use method chaining + +This pattern is very useful and commonly used in many libraries such +as PHPUnit and Doctrine. It allows your code to be expressive, and less verbose. +For that reason, use method chaining and take a look at how clean your code +will be. In your class functions, simply use `return $this` at the end of every `set` function, +and you can chain further class methods onto it. + +**Bad:** + +```php +class Car +{ + private $make = 'Honda'; + private $model = 'Accord'; + private $color = 'white'; + + public function setMake($make) + { + $this->make = $make; + } + + public function setModel($model) + { + $this->model = $model; + } + + public function setColor($color) + { + $this->color = $color; + } + + public function dump() + { + var_dump($this->make, $this->model, $this->color); + } +} + +$car = new Car(); +$car->setColor('pink'); +$car->setMake('Ford'); +$car->setModel('F-150'); +$car->dump(); +``` + +**Good:** + +```php +class Car +{ + private $make = 'Honda'; + private $model = 'Accord'; + private $color = 'white'; + + public function setMake($make) + { + $this->make = $make; + + // NOTE: Returning this for chaining + return $this; + } + + public function setModel($model) + { + $this->model = $model; + + // NOTE: Returning this for chaining + return $this; + } + + public function setColor($color) + { + $this->color = $color; + + // NOTE: Returning this for chaining + return $this; + } + + public function dump() + { + var_dump($this->make, $this->model, $this->color); + } +} + +$car = (new Car()) + ->setColor('pink') + ->setMake('Ford') + ->setModel('F-150') + ->dump(); +``` + +**[⬆ back to top](#table-of-contents)** + +### Prefer composition over inheritance + +As stated famously in [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) by the Gang of Four, +you should prefer composition over inheritance where you can. There are lots of +good reasons to use inheritance and lots of good reasons to use composition. +The main point for this maxim is that if your mind instinctively goes for +inheritance, try to think if composition could model your problem better. In some +cases it can. + +You might be wondering then, "when should I use inheritance?" It +depends on your problem at hand, but this is a decent list of when inheritance +makes more sense than composition: + +1. Your inheritance represents an "is-a" relationship and not a "has-a" +relationship (Human->Animal vs. User->UserDetails). +2. You can reuse code from the base classes (Humans can move like all animals). +3. You want to make global changes to derived classes by changing a base class. +(Change the caloric expenditure of all animals when they move). + +**Bad:** + +```php +class Employee +{ + private $name; + private $email; + + public function __construct($name, $email) + { + $this->name = $name; + $this->email = $email; + } + + // ... +} + +// Bad because Employees "have" tax data. +// EmployeeTaxData is not a type of Employee + +class EmployeeTaxData extends Employee +{ + private $ssn; + private $salary; + + public function __construct($name, $email, $ssn, $salary) + { + parent::__construct($name, $email); + + $this->ssn = $ssn; + $this->salary = $salary; + } + + // ... +} +``` + +**Good:** + +```php +class EmployeeTaxData +{ + private $ssn; + private $salary; + + public function __construct($ssn, $salary) + { + $this->ssn = $ssn; + $this->salary = $salary; + } + + // ... +} + +class Employee +{ + private $name; + private $email; + private $taxData; + + public function __construct($name, $email) + { + $this->name = $name; + $this->email = $email; + } + + public function setTaxData($ssn, $salary) + { + $this->taxData = new EmployeeTaxData($ssn, $salary); + } + + // ... +} +``` + +**[⬆ back to top](#table-of-contents)** + +## SOLID + ### Single Responsibility Principle (SRP) As stated in Clean Code, "There should never be more than one reason for a class @@ -1598,195 +1790,6 @@ class Manager **[⬆ back to top](#table-of-contents)** -### Use method chaining - -This pattern is very useful and commonly used in many libraries such -as PHPUnit and Doctrine. It allows your code to be expressive, and less verbose. -For that reason, use method chaining and take a look at how clean your code -will be. In your class functions, simply use `return $this` at the end of every `set` function, -and you can chain further class methods onto it. - -**Bad:** - -```php -class Car -{ - private $make = 'Honda'; - private $model = 'Accord'; - private $color = 'white'; - - public function setMake($make) - { - $this->make = $make; - } - - public function setModel($model) - { - $this->model = $model; - } - - public function setColor($color) - { - $this->color = $color; - } - - public function dump() - { - var_dump($this->make, $this->model, $this->color); - } -} - -$car = new Car(); -$car->setColor('pink'); -$car->setMake('Ford'); -$car->setModel('F-150'); -$car->dump(); -``` - -**Good:** - -```php -class Car -{ - private $make = 'Honda'; - private $model = 'Accord'; - private $color = 'white'; - - public function setMake($make) - { - $this->make = $make; - - // NOTE: Returning this for chaining - return $this; - } - - public function setModel($model) - { - $this->model = $model; - - // NOTE: Returning this for chaining - return $this; - } - - public function setColor($color) - { - $this->color = $color; - - // NOTE: Returning this for chaining - return $this; - } - - public function dump() - { - var_dump($this->make, $this->model, $this->color); - } -} - -$car = (new Car()) - ->setColor('pink') - ->setMake('Ford') - ->setModel('F-150') - ->dump(); -``` - -**[⬆ back to top](#table-of-contents)** - -### Prefer composition over inheritance - -As stated famously in [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) by the Gang of Four, -you should prefer composition over inheritance where you can. There are lots of -good reasons to use inheritance and lots of good reasons to use composition. -The main point for this maxim is that if your mind instinctively goes for -inheritance, try to think if composition could model your problem better. In some -cases it can. - -You might be wondering then, "when should I use inheritance?" It -depends on your problem at hand, but this is a decent list of when inheritance -makes more sense than composition: - -1. Your inheritance represents an "is-a" relationship and not a "has-a" -relationship (Human->Animal vs. User->UserDetails). -2. You can reuse code from the base classes (Humans can move like all animals). -3. You want to make global changes to derived classes by changing a base class. -(Change the caloric expenditure of all animals when they move). - -**Bad:** - -```php -class Employee -{ - private $name; - private $email; - - public function __construct($name, $email) - { - $this->name = $name; - $this->email = $email; - } - - // ... -} - -// Bad because Employees "have" tax data. -// EmployeeTaxData is not a type of Employee - -class EmployeeTaxData extends Employee -{ - private $ssn; - private $salary; - - public function __construct($name, $email, $ssn, $salary) - { - parent::__construct($name, $email); - - $this->ssn = $ssn; - $this->salary = $salary; - } - - // ... -} -``` - -**Good:** - -```php -class EmployeeTaxData -{ - private $ssn; - private $salary; - - public function __construct($ssn, $salary) - { - $this->ssn = $ssn; - $this->salary = $salary; - } - - // ... -} - -class Employee -{ - private $name; - private $email; - private $taxData; - - public function __construct($name, $email) - { - $this->name = $name; - $this->email = $email; - } - - public function setTaxData($ssn, $salary) - { - $this->taxData = new EmployeeTaxData($ssn, $salary); - } - - // ... -} -``` - -**[⬆ back to top](#table-of-contents)** - ## Don’t repeat yourself (DRY) Try to observe the [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) principle. From 23e6a5c399197cbeae6d7f0a1925ee51818b9b3d Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Mon, 11 Sep 2017 13:49:25 +0300 Subject: [PATCH 110/210] add description for SOLID --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 9de28587..e72406dc 100644 --- a/README.md +++ b/README.md @@ -1275,6 +1275,14 @@ class Employee ## SOLID +**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. + + * [S: Single Responsibility Principle (SRP)](#single-responsibility-principle-srp) + * [O: Open/Closed Principle (OCP)](#openclosed-principle-ocp) + * [L: Liskov Substitution Principle (LSP)](#liskov-substitution-principle-lsp) + * [I: Interface Segregation Principle (ISP)](#interface-segregation-principle-isp) + * [D: Dependency Inversion Principle (DIP)](#dependency-inversion-principle-dip) + ### Single Responsibility Principle (SRP) As stated in Clean Code, "There should never be more than one reason for a class From 6051bd55e686cc6c1121c8a748733e362baf27ad Mon Sep 17 00:00:00 2001 From: Jewei Mak Date: Tue, 12 Sep 2017 02:15:02 +0800 Subject: [PATCH 111/210] Better code examples --- README.md | 89 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 068c0257..2110770f 100644 --- a/README.md +++ b/README.md @@ -155,58 +155,91 @@ saveCityZipCode($matches['city'], $matches['zipCode']); ### Avoid nesting too deeply and return early -Nesting if else too deeply makes reader difficult to follow. Explicit is better +Too many if else statemetns can make your code hard to follow. Explicit is better than implicit. **Bad:** ```php -function checkEmailExist() -{ - if (isset($_POST['action'])) { - if (isset($_POST['email'])) { - if (isset($_POST['user_id'])) { - $user = User::find($_POST['user_id']); - if ($user) { - $emails = $user->getEmails(); - if (count($emails) > 0) { - foreach ($emails as $email) { - if ($email === $_POST['email']) { - return true; - } - } - } - } +function isShopOpen($day) +{ + if ($day) { + if (is_string($day)) { + $day = strtolower($day); + if ($day === 'friday') { + return true; + } elseif ($day === 'saturday') { + return true; + } elseif ($day === 'sunday') { + return true; + } else { + return false; } + } else { + return false; } + } else { + return false; } - - return false; } ``` **Good:** ```php -function checkEmailExist() +function isShopOpen($day) { - if (! isset($_POST['action'], $_POST['email'], $_POST['user_id'])) { + if (empty($day) && ! is_string($day)) { return false; } - $user = User::find($_POST['user_id']); + $openingDays = [ + 'friday', 'saturday', 'sunday' + ]; - if (! $user) { - return false; // Or throw exception. + return in_array(strtolower($day), $openingDays); +} +``` + +**Bad:** + +```php +function fibonacci($n) +{ + if ($n < 50) { + if ($n !== 0) { + if ($n !== 1) { + return fibonacci($n - 1) + fibonacci($n - 2); + } else { + return 1; + } + } else { + return 0; + } + } else { + return 'Not supported'; } +} +``` - $emails = $user->getEmails(); +**Good:** - if (empty($emails)) { - return false; +```php +function fibonacci($n) +{ + if ($n === 0) { + return 0; + } + + if ($n === 1) { + return 1; + } + + if ($n > 50) { + return 'Not supported'; } - return in_array($_POST['email'], $emails); + return fibonacci($n - 1) + fibonacci($n - 2); } ``` From a09df2c8ee7f9a532a37ac40cb9cef938e34ebda Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Mon, 11 Sep 2017 22:40:27 +0300 Subject: [PATCH 112/210] add links to table of contents --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a472e349..03d3f081 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ * [Use searchable names (part 1)](#use-searchable-names-part-1) * [Use searchable names (part 2)](#use-searchable-names-part-2) * [Use explanatory variables](#use-explanatory-variables) + * [Avoid nesting too deeply and return early (part 1)](#avoid_nesting_too_deeply_and_return_early_part_1) + * [Avoid nesting too deeply and return early (part 2)](#avoid_nesting_too_deeply_and_return_early_part_2) * [Avoid Mental Mapping](#avoid-mental-mapping) * [Don't add unneeded context](#dont-add-unneeded-context) * [Use default arguments instead of short circuiting or conditionals](#use-default-arguments-instead-of-short-circuiting-or-conditionals) @@ -180,7 +182,7 @@ saveCityZipCode($matches['city'], $matches['zipCode']); **[⬆ back to top](#table-of-contents)** -### Avoid nesting too deeply and return early +### Avoid nesting too deeply and return early (part 1) Too many if else statemetns can make your code hard to follow. Explicit is better than implicit. @@ -228,6 +230,10 @@ function isShopOpen($day) } ``` +**[⬆ back to top](#table-of-contents)** + +### Avoid nesting too deeply and return early (part 2) + **Bad:** ```php From 21828c784ccdfd80f9383d3a97935b74923ca9d9 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Mon, 11 Sep 2017 22:41:09 +0300 Subject: [PATCH 113/210] correct links --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 03d3f081..82bb3f55 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,8 @@ * [Use searchable names (part 1)](#use-searchable-names-part-1) * [Use searchable names (part 2)](#use-searchable-names-part-2) * [Use explanatory variables](#use-explanatory-variables) - * [Avoid nesting too deeply and return early (part 1)](#avoid_nesting_too_deeply_and_return_early_part_1) - * [Avoid nesting too deeply and return early (part 2)](#avoid_nesting_too_deeply_and_return_early_part_2) + * [Avoid nesting too deeply and return early (part 1)](#avoid-nesting-too-deeply-and-return-early-part-1) + * [Avoid nesting too deeply and return early (part 2)](#avoid-nesting-too-deeply-and-return-early-part-2) * [Avoid Mental Mapping](#avoid-mental-mapping) * [Don't add unneeded context](#dont-add-unneeded-context) * [Use default arguments instead of short circuiting or conditionals](#use-default-arguments-instead-of-short-circuiting-or-conditionals) From 924c5630041214296cda14abb2c3f7da687e7679 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 12 Sep 2017 10:11:58 +0300 Subject: [PATCH 114/210] Remove bad eaxample --- README.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/README.md b/README.md index c1ec7305..1caa41fd 100644 --- a/README.md +++ b/README.md @@ -1395,16 +1395,6 @@ foreach ($rectangles as $rectangle) { ``` This solution no longer violates the LSP principle because we can use a subtype. - -```php -function printSquareArea(Square $square) -{ - // ... -} - -printSquareArea(new Rectangle(4, 5)); -``` - But this is not a best solution, because the square specifies the invariants of the rectangle. **Good:** From ab07e05bcd144229790da8d4ba4466e44db6826c Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 12 Sep 2017 10:18:00 +0300 Subject: [PATCH 115/210] use PHP 5.6 pow() --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1caa41fd..3cc46988 100644 --- a/README.md +++ b/README.md @@ -1454,8 +1454,8 @@ class Square implements Shape public function area() { - return pow($this->length, 2); - } +        return $this->length ** 2; +    } } function printArea(Shape $shape) From 8a64dfc5517b01e7bf4e7ab1534a78fe4a8272f0 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 12 Sep 2017 10:57:32 +0300 Subject: [PATCH 116/210] change the title of encapsulation section --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a472e349..9ad75523 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ * [Avoid type-checking (part 2)](#avoid-type-checking-part-2) * [Remove dead code](#remove-dead-code) 4. [Objects and Data Structures](#objects-and-data-structures) - * [Use getters and setters](#use-getters-and-setters) + * [Use object encapsulation](#use-object-encapsulation) * [Make objects have private/protected members](#make-objects-have-privateprotected-members) 5. [Classes](#classes) * [Use method chaining](#use-method-chaining) @@ -1083,7 +1083,7 @@ inventoryTracker('apples', $request, 'www.inventory-awesome.io'); ## Objects and Data Structures -### Use getters and setters +### Use object encapsulation In PHP you can set `public`, `protected` and `private` keywords for methods. Using it, you can control properties modification on an object. @@ -1097,8 +1097,7 @@ to look up and change every accessor in your codebase. * You can lazy load your object's properties, let's say getting it from a server. -Additionally, this is part of Open/Closed principle, from object-oriented -design principles. +Additionally, this is part of [Open/Closed](#openclosed-principle-ocp) principle. **Bad:** From acf92a1997353109f278d257b1772c8c1696263f Mon Sep 17 00:00:00 2001 From: Mariano Custiel Date: Tue, 12 Sep 2017 16:47:27 +0200 Subject: [PATCH 117/210] Proposal to fix issue #37 --- README.md | 34 ++++++---------------------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index b5ae5f08..aafbd01b 100644 --- a/README.md +++ b/README.md @@ -1211,9 +1211,6 @@ renderLargeRectangles($rectangles); ```php abstract class Shape { - protected $width; - protected $height; - abstract public function getArea(); public function render($area) @@ -1224,20 +1221,13 @@ abstract class Shape class Rectangle extends Shape { - public function __construct() - { - parent::__construct(); - $this->width = 0; - $this->height = 0; - } + protected $width; + protected $height; - public function setWidth($width) + public function __construct($width, $height) { + parent::__construct(); $this->width = $width; - } - - public function setHeight($height) - { $this->height = $height; } @@ -1249,14 +1239,9 @@ class Rectangle extends Shape class Square extends Shape { - public function __construct() + public function __construct($length) { parent::__construct(); - $this->length = 0; - } - - public function setLength($length) - { $this->length = $length; } @@ -1269,19 +1254,12 @@ class Square extends Shape function renderLargeRectangles($rectangles) { foreach ($rectangles as $rectangle) { - if ($rectangle instanceof Square) { - $rectangle->setLength(5); - } elseif ($rectangle instanceof Rectangle) { - $rectangle->setWidth(4); - $rectangle->setHeight(5); - } - $area = $rectangle->getArea(); $rectangle->render($area); } } -$shapes = [new Rectangle(), new Rectangle(), new Square()]; +$shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)]; renderLargeRectangles($shapes); ``` From 0d94f3315f89de18af6add292456f35e5726717e Mon Sep 17 00:00:00 2001 From: Mariano Custiel Date: Tue, 12 Sep 2017 16:54:17 +0200 Subject: [PATCH 118/210] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index aafbd01b..5eff6cb0 100644 --- a/README.md +++ b/README.md @@ -1221,8 +1221,8 @@ abstract class Shape class Rectangle extends Shape { - protected $width; - protected $height; + private $width; + private $height; public function __construct($width, $height) { @@ -1239,6 +1239,8 @@ class Rectangle extends Shape class Square extends Shape { + private $length; + public function __construct($length) { parent::__construct(); From c2af508723f7bc2b56596609016897b2cecec3a6 Mon Sep 17 00:00:00 2001 From: Peter Gribanov Date: Tue, 12 Sep 2017 18:34:24 +0300 Subject: [PATCH 119/210] fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3cc46988..47c3c603 100644 --- a/README.md +++ b/README.md @@ -1403,7 +1403,7 @@ The best way is separate the quadrangles and allocation of a more general subtyp Despite the apparent similarity of the square and the rectangle, they are different. A square has much in common with a rhombus, and a rectangle with a parallelogram, but they are not subtype. -A square, a rectangle, a rhombus and a parallelogram are separate figures with their own properties, albeit similar. +A square, a rectangle, a rhombus and a parallelogram are separate shapes with their own properties, albeit similar. ```php interface Shape From 563028b2a97093a4ab7a0a1fe73035fcc3013325 Mon Sep 17 00:00:00 2001 From: Filippo Tessarotto Date: Wed, 13 Sep 2017 10:19:54 +0200 Subject: [PATCH 120/210] Moved `Use method chaining` to `Avoid fluent interfaces` --- README.md | 196 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 102 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index a472e349..c3f82ac4 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,8 @@ * [Use getters and setters](#use-getters-and-setters) * [Make objects have private/protected members](#make-objects-have-privateprotected-members) 5. [Classes](#classes) - * [Use method chaining](#use-method-chaining) * [Prefer composition over inheritance](#prefer-composition-over-inheritance) + * [Avoid fluent interfaces](#avoid-fluent-interfaces) 6. [SOLID](#solid) * [Single Responsibility Principle (SRP)](#single-responsibility-principle-srp) * [Open/Closed Principle (OCP)](#openclosed-principle-ocp) @@ -1202,99 +1202,6 @@ echo 'Employee name: '.$employee->getName(); // Employee name: John Doe ## Classes -### Use method chaining - -This pattern is very useful and commonly used in many libraries such -as PHPUnit and Doctrine. It allows your code to be expressive, and less verbose. -For that reason, use method chaining and take a look at how clean your code -will be. In your class functions, simply use `return $this` at the end of every `set` function, -and you can chain further class methods onto it. - -**Bad:** - -```php -class Car -{ - private $make = 'Honda'; - private $model = 'Accord'; - private $color = 'white'; - - public function setMake($make) - { - $this->make = $make; - } - - public function setModel($model) - { - $this->model = $model; - } - - public function setColor($color) - { - $this->color = $color; - } - - public function dump() - { - var_dump($this->make, $this->model, $this->color); - } -} - -$car = new Car(); -$car->setColor('pink'); -$car->setMake('Ford'); -$car->setModel('F-150'); -$car->dump(); -``` - -**Good:** - -```php -class Car -{ - private $make = 'Honda'; - private $model = 'Accord'; - private $color = 'white'; - - public function setMake($make) - { - $this->make = $make; - - // NOTE: Returning this for chaining - return $this; - } - - public function setModel($model) - { - $this->model = $model; - - // NOTE: Returning this for chaining - return $this; - } - - public function setColor($color) - { - $this->color = $color; - - // NOTE: Returning this for chaining - return $this; - } - - public function dump() - { - var_dump($this->make, $this->model, $this->color); - } -} - -$car = (new Car()) - ->setColor('pink') - ->setMake('Ford') - ->setModel('F-150') - ->dump(); -``` - -**[⬆ back to top](#table-of-contents)** - ### Prefer composition over inheritance As stated famously in [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) by the Gang of Four, @@ -1391,6 +1298,107 @@ class Employee **[⬆ back to top](#table-of-contents)** +### Avoid fluent interfaces +A [Fluent interface](https://en.wikipedia.org/wiki/Fluent_interface) is an object +oriented API that aims to improve the readability of the source code by using +[Method chaining](https://en.wikipedia.org/wiki/Method_chaining). + +While there can be some contexts, frequently builder objects, where this +pattern reduces the verbosity of the code (for example the [PHPUnit Mock Builder](https://phpunit.de/manual/current/en/test-doubles.html) +or the [Doctrine Query Builder](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/query-builder.html)), +more often it comes at some costs: + +1. Breaks [Encapsulation](https://en.wikipedia.org/wiki/Encapsulation_%28object-oriented_programming%29) +2. Breaks [Decorators](https://en.wikipedia.org/wiki/Decorator_pattern) +3. Is harder to [mock](https://en.wikipedia.org/wiki/Mock_object) in a test suite +4. Makes diffs of commits harder to read + +For more informations you can read the full [blog post](https://ocramius.github.io/blog/fluent-interfaces-are-evil/) +on this topic written by [Marco Pivetta](https://github.com/Ocramius). + +**Bad:** +```php +class Car +{ + private $make = 'Honda'; + private $model = 'Accord'; + private $color = 'white'; + + public function setMake($make) + { + $this->make = $make; + + // NOTE: Returning this for chaining + return $this; + } + + public function setModel($model) + { + $this->model = $model; + + // NOTE: Returning this for chaining + return $this; + } + + public function setColor($color) + { + $this->color = $color; + + // NOTE: Returning this for chaining + return $this; + } + + public function dump() + { + var_dump($this->make, $this->model, $this->color); + } +} + +$car = (new Car()) + ->setColor('pink') + ->setMake('Ford') + ->setModel('F-150') + ->dump(); +``` + +**Good:** +```php +class Car +{ + private $make = 'Honda'; + private $model = 'Accord'; + private $color = 'white'; + + public function setMake($make) + { + $this->make = $make; + } + + public function setModel($model) + { + $this->model = $model; + } + + public function setColor($color) + { + $this->color = $color; + } + + public function dump() + { + var_dump($this->make, $this->model, $this->color); + } +} + +$car = new Car(); +$car->setColor('pink'); +$car->setMake('Ford'); +$car->setModel('F-150'); +$car->dump(); +``` + +**[⬆ back to top](#table-of-contents)** + ## SOLID **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. From 98351e4e9f7826bfb6d22cc1f38dadeb68252f0e Mon Sep 17 00:00:00 2001 From: Filippo Tessarotto Date: Wed, 13 Sep 2017 12:35:17 +0200 Subject: [PATCH 121/210] Add travis checks for spaces and table of contents --- .travis-build.php | 70 +++++++++++++++++++++++++++++++++++++++++++++++ .travis.yml | 11 ++++++++ 2 files changed, 81 insertions(+) create mode 100644 .travis-build.php create mode 100644 .travis.yml diff --git a/.travis-build.php b/.travis-build.php new file mode 100644 index 00000000..93333509 --- /dev/null +++ b/.travis-build.php @@ -0,0 +1,70 @@ +setFlags(SplFileObject::DROP_NEW_LINE); + +$cliRedBackground = "\033[37;41m"; +$cliReset = "\033[0m"; +$exitStatus = 0; + +$indentationSteps = 3; +$manIndex = 0; +$linesWithSpaces = []; +$tableOfContentsStarted = null; +$currentTableOfContentsChapters = []; +$chaptersFound = []; +foreach ($readMeFile as $lineNumber => $line) { + if (preg_match('/\s$/', $line)) { + $linesWithSpaces[] = sprintf('%5s: %s', 1 + $lineNumber, $line); + } + if (preg_match('/^(?##+)\s(?.+)/', $line, $matches)) { + if (null === $tableOfContentsStarted) { + $tableOfContentsStarted = true; + continue; + } + $tableOfContentsStarted = false; + + $chaptersFound[] = sprintf('%s [%s](#%s)', + strlen($matches['depth']) === 2 + ? sprintf(' %s.', ++$manIndex) + : ' *' + , + $matches['title'], + preg_replace(['/ /', '/[^-\w]+/'], ['-', ''], strtolower($matches['title'])) + ); + } + if ($tableOfContentsStarted === true && isset($line[0])) { + $currentTableOfContentsChapters[] = $line; + } +} + +if (count($linesWithSpaces)) { + fwrite(STDERR, sprintf("${cliRedBackground}The following lines end with a space character:${cliReset}\n%s\n\n", + implode(PHP_EOL, $linesWithSpaces) + )); + $exitStatus = 1; +} + +$currentTableOfContentsChaptersFilename = __DIR__ . '/current-chapters'; +$chaptersFoundFilename = __DIR__ . '/chapters-found'; + +file_put_contents($currentTableOfContentsChaptersFilename, implode(PHP_EOL, $currentTableOfContentsChapters)); +file_put_contents($chaptersFoundFilename, implode(PHP_EOL, $chaptersFound)); + +$tableOfContentsDiff = shell_exec(sprintf('diff --unified %s %s', + escapeshellarg($currentTableOfContentsChaptersFilename), + escapeshellarg($chaptersFoundFilename) +)); + +@ unlink($currentTableOfContentsChaptersFilename); +@ unlink($chaptersFoundFilename); + +if (!empty($tableOfContentsDiff)) { + fwrite(STDERR, sprintf("${cliRedBackground}The table of contents is not aligned:${cliReset}\n%s\n\n", + $tableOfContentsDiff + )); + $exitStatus = 1; +} + +exit($exitStatus); diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..e4ea3575 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +language: php + +sudo: false + +php: + - nightly + +script: php .travis-build.php + +notifications: + email: false From b2dd441eb9fe9df3bab71c58d4a53500a5f877c9 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Thu, 14 Sep 2017 13:34:29 +0300 Subject: [PATCH 122/210] add info text for private/protected members --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index c3f82ac4..bf43be53 100644 --- a/README.md +++ b/README.md @@ -1159,6 +1159,14 @@ $balance = $bankAccount->getBalance(); ### Make objects have private/protected members +* `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. +* `protected` methods 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. +* `private` methods are your safe harbor in this doubtful world. They guarantee you that code in your methods is dangerous to modify only in boundaries of single class (which means that when you have tests for your protected/public methods that cover all calls of your private method, and as long as you don't do magic, like side effects or usage of global state, you are safe for modifications and you won't have [Jenga effect](http://www.urbandictionary.com/define.php?term=Jengaphobia&defid=2494196)). + +Therefore, use `private` by default and `public/protected` when you need to provide access for external classes. + +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). + **Bad:** ```php From 199392497e13a549664faa268e258eeaeb057576 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Thu, 14 Sep 2017 15:20:45 +0300 Subject: [PATCH 123/210] add type hinting --- README.md | 244 +++++++++++++++++++++++++++--------------------------- 1 file changed, 122 insertions(+), 122 deletions(-) diff --git a/README.md b/README.md index c3f82ac4..a74eafa7 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,7 @@ $address = 'One Infinite Loop, Cupertino 95014'; $cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/'; preg_match($cityZipCodeRegex, $address, $matches); -list(, $city, $zipCode) = $matches; +[, $city, $zipCode] = $matches; saveCityZipCode($city, $zipCode); ``` @@ -188,7 +188,7 @@ than implicit. **Bad:** ```php -function isShopOpen($day) +function isShopOpen($day): bool { if ($day) { if (is_string($day)) { @@ -214,9 +214,9 @@ function isShopOpen($day) **Good:** ```php -function isShopOpen($day) +function isShopOpen(string $day): bool { - if (empty($day) && ! is_string($day)) { + if (empty($day)) { return false; } @@ -231,7 +231,7 @@ function isShopOpen($day) **Bad:** ```php -function fibonacci($n) +function fibonacci(int $n) { if ($n < 50) { if ($n !== 0) { @@ -252,7 +252,7 @@ function fibonacci($n) **Good:** ```php -function fibonacci($n) +function fibonacci(int $n): int { if ($n === 0) { return 0; @@ -263,7 +263,7 @@ function fibonacci($n) } if ($n > 50) { - return 'Not supported'; + throw new \Exception('Not supported'); } return fibonacci($n - 1) + fibonacci($n - 2); @@ -351,7 +351,7 @@ class Car This is not good because `$breweryName` can be `NULL`. ```php -function createMicrobrewery($breweryName = 'Hipster Brew Co.') +function createMicrobrewery($breweryName = 'Hipster Brew Co.'): void {    // ... } @@ -362,7 +362,7 @@ function createMicrobrewery($breweryName = 'Hipster Brew Co.') This opinion is more understandable than the previous version, but it better controls the value of the variable. ```php -function createMicrobrewery($name = null) +function createMicrobrewery($name = null): void {    $breweryName = $name ?: 'Hipster Brew Co.'; // ... @@ -374,7 +374,7 @@ function createMicrobrewery($name = null) If you support only PHP 7+, then you can use [type hinting](http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration) and be sure that the `$breweryName` will not be `NULL`. ```php -function createMicrobrewery(string $breweryName = 'Hipster Brew Co.') +function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void {    // ... } @@ -398,7 +398,7 @@ of the time a higher-level object will suffice as an argument. **Bad:** ```php -function createMenu($title, $body, $buttonText, $cancellable) +function createMenu(string $title, string $body, string $buttonText, bool $cancellable): void { // ... } @@ -421,7 +421,7 @@ $config->body = 'Bar'; $config->buttonText = 'Baz'; $config->cancellable = true; -function createMenu(MenuConfig $config) +function createMenu(MenuConfig $config): void { // ... } @@ -439,7 +439,7 @@ of many developers. **Bad:** ```php -function emailClients($clients) +function emailClients(array $clients): void { foreach ($clients as $client) { $clientRecord = $db->find($client); @@ -453,18 +453,18 @@ function emailClients($clients) **Good:** ```php -function emailClients($clients) +function emailClients(array $clients): void { $activeClients = activeClients($clients); array_walk($activeClients, 'email'); } -function activeClients($clients) +function activeClients(array $clients): array { return array_filter($clients, 'isClientActive'); } -function isClientActive($client) +function isClientActive(int $client): bool { $clientRecord = $db->find($client); @@ -483,7 +483,7 @@ class Email { //... - public function handle() + public function handle(): void { mail($this->to, $this->subject, $this->body); } @@ -501,7 +501,7 @@ class Email { //... - public function send() + public function send(): void { mail($this->to, $this->subject, $this->body); } @@ -523,7 +523,7 @@ testing. **Bad:** ```php -function parseBetterJSAlternative($code) +function parseBetterJSAlternative(string $code): void { $regexes = [ // ... @@ -553,7 +553,7 @@ function parseBetterJSAlternative($code) We have carried out some of the functionality, but the `parseBetterJSAlternative()` function is still very complex and not testable. ```php -function tokenize($code) +function tokenize(string $code): array { $regexes = [ // ... @@ -570,7 +570,7 @@ function tokenize($code) return $tokens; } -function lexer($tokens) +function lexer(array $tokens): array { $ast = []; foreach ($tokens as $token) { @@ -580,7 +580,7 @@ function lexer($tokens) return $ast; } -function parseBetterJSAlternative($code) +function parseBetterJSAlternative(string $code): void { $tokens = tokenize($code); $ast = lexer($tokens); @@ -597,7 +597,7 @@ The best solution is move out the dependencies of `parseBetterJSAlternative()` f ```php class Tokenizer { - public function tokenize($code) + public function tokenize(string $code): array { $regexes = [ // ... @@ -617,7 +617,7 @@ class Tokenizer class Lexer { - public function lexify($tokens) + public function lexify(array $tokens): array { $ast = []; foreach ($tokens as $token) { @@ -639,7 +639,7 @@ class BetterJSAlternative $this->lexer = $lexer; } - public function parse($code) + public function parse(string $code): void { $tokens = $this->tokenizer->tokenize($code); $ast = $this->lexer->lexify($tokens); @@ -661,7 +661,7 @@ based on a boolean. **Bad:** ```php -function createFile($name, $temp = false) +function createFile(string $name, bool $temp = false): void { if ($temp) { touch('./temp/'.$name); @@ -674,12 +674,12 @@ function createFile($name, $temp = false) **Good:** ```php -function createFile($name) +function createFile(string $name): void { touch($name); } -function createTempFile($name) +function createTempFile(string $name): void { touch('./temp/'.$name); } @@ -710,7 +710,7 @@ than the vast majority of other programmers. // If we had another function that used this name, now it'd be an array and it could break it. $name = 'Ryan McDermott'; -function splitIntoFirstAndLastName() +function splitIntoFirstAndLastName(): void { global $name; @@ -725,7 +725,7 @@ var_dump($name); // ['Ryan', 'McDermott']; **Good:** ```php -function splitIntoFirstAndLastName($name) +function splitIntoFirstAndLastName(string $name): array { return explode(' ', $name); } @@ -750,7 +750,7 @@ that tried to do the same thing. **Bad:** ```php -function config() +function config(): array { return [ 'foo' => 'bar', @@ -770,7 +770,7 @@ class Configuration $this->configuration = $configuration; } - public function get($key) + public function get(string $key): ?string { return isset($this->configuration[$key]) ? $this->configuration[$key] : null; } @@ -806,12 +806,12 @@ class DBConnection { private static $instance; - private function __construct($dsn) + private function __construct(string $dsn) { // ... } - public static function getInstance() + public static function getInstance(): DBConnection { if (self::$instance === null) { self::$instance = new self(); @@ -831,7 +831,7 @@ $singleton = DBConnection::getInstance(); ```php class DBConnection { - public function __construct(array $dsn) + public function __construct(string $dsn) { // ... } @@ -875,7 +875,7 @@ if ($article->isPublished()) { **Bad:** ```php -function isDOMNodeNotPresent($node) +function isDOMNodeNotPresent(\DOMNode $node): bool { // ... } @@ -889,7 +889,7 @@ if (!isDOMNodeNotPresent($node)) **Good:** ```php -function isDOMNodePresent($node) +function isDOMNodePresent(\DOMNode $node): bool { // ... } @@ -919,7 +919,7 @@ class Airplane { // ... - public function getCruisingAltitude() + public function getCruisingAltitude(): int { switch ($this->type) { case '777': @@ -940,14 +940,14 @@ interface Airplane { // ... - public function getCruisingAltitude(); + public function getCruisingAltitude(): int; } class Boeing777 implements Airplane { // ... - public function getCruisingAltitude() + public function getCruisingAltitude(): int { return $this->getMaxAltitude() - $this->getPassengerCount(); } @@ -957,7 +957,7 @@ class AirForceOne implements Airplane { // ... - public function getCruisingAltitude() + public function getCruisingAltitude(): int { return $this->getMaxAltitude(); } @@ -967,7 +967,7 @@ class Cessna implements Airplane { // ... - public function getCruisingAltitude() + public function getCruisingAltitude(): int { return $this->getMaxAltitude() - $this->getFuelExpenditure(); } @@ -986,7 +986,7 @@ The first thing to consider is consistent APIs. **Bad:** ```php -function travelToTexas($vehicle) +function travelToTexas($vehicle): void { if ($vehicle instanceof Bicycle) { $vehicle->peddleTo(new Location('texas')); @@ -999,7 +999,7 @@ function travelToTexas($vehicle) **Good:** ```php -function travelToTexas(Traveler $vehicle) +function travelToTexas(Traveler $vehicle): void { $vehicle->travelTo(new Location('texas')); } @@ -1022,7 +1022,7 @@ Otherwise, do all of that but with PHP strict type declaration or strict mode. **Bad:** ```php -function combine($val1, $val2) +function combine($val1, $val2): int { if (!is_numeric($val1) || !is_numeric($val2)) { throw new \Exception('Must be of type Number'); @@ -1035,7 +1035,7 @@ function combine($val1, $val2) **Good:** ```php -function combine(int $val1, int $val2) +function combine(int $val1, int $val2): int { return $val1 + $val2; } @@ -1052,12 +1052,12 @@ in your version history if you still need it. **Bad:** ```php -function oldRequestModule($url) +function oldRequestModule(string $url): void { // ... } -function newRequestModule($url) +function newRequestModule(string $url): void { // ... } @@ -1069,7 +1069,7 @@ inventoryTracker('apples', $request, 'www.inventory-awesome.io'); **Good:** ```php -function requestModule($url) +function requestModule(string $url): void { // ... } @@ -1121,12 +1121,12 @@ class BankAccount { private $balance; - public function __construct($balance = 1000) + public function __construct(int $balance = 1000) { $this->balance = $balance; } - public function withdrawBalance($amount) + public function withdrawBalance(int $amount): void { if ($amount > $this->balance) { throw new \Exception('Amount greater than available balance.'); @@ -1135,12 +1135,12 @@ class BankAccount $this->balance -= $amount; } - public function depositBalance($amount) + public function depositBalance(int $amount): void { $this->balance += $amount; } - public function getBalance() + public function getBalance(): int { return $this->balance; } @@ -1166,7 +1166,7 @@ class Employee { public $name; - public function __construct($name) + public function __construct(string $name) { $this->name = $name; } @@ -1183,12 +1183,12 @@ class Employee { private $name; - public function __construct($name) + public function __construct(string $name) { $this->name = $name; } - public function getName() + public function getName(): string { return $this->name; } @@ -1229,7 +1229,7 @@ class Employee private $name; private $email; - public function __construct($name, $email) + public function __construct(string $name, string $email) { $this->name = $name; $this->email = $email; @@ -1246,7 +1246,7 @@ class EmployeeTaxData extends Employee private $ssn; private $salary; - public function __construct($name, $email, $ssn, $salary) + public function __construct(string $name, string $email, string $ssn, string $salary) { parent::__construct($name, $email); @@ -1266,7 +1266,7 @@ class EmployeeTaxData private $ssn; private $salary; - public function __construct($ssn, $salary) + public function __construct(string $ssn, string $salary) { $this->ssn = $ssn; $this->salary = $salary; @@ -1281,13 +1281,13 @@ class Employee private $email; private $taxData; - public function __construct($name, $email) + public function __construct(string $name, string $email) { $this->name = $name; $this->email = $email; } - public function setTaxData($ssn, $salary) + public function setTaxData(string $ssn, string $salary) { $this->taxData = new EmployeeTaxData($ssn, $salary); } @@ -1324,7 +1324,7 @@ class Car private $model = 'Accord'; private $color = 'white'; - public function setMake($make) + public function setMake(string $make): Car { $this->make = $make; @@ -1332,7 +1332,7 @@ class Car return $this; } - public function setModel($model) + public function setModel(string $model): Car { $this->model = $model; @@ -1340,7 +1340,7 @@ class Car return $this; } - public function setColor($color) + public function setColor(string $color): Car { $this->color = $color; @@ -1348,7 +1348,7 @@ class Car return $this; } - public function dump() + public function dump(): void { var_dump($this->make, $this->model, $this->color); } @@ -1369,22 +1369,22 @@ class Car private $model = 'Accord'; private $color = 'white'; - public function setMake($make) + public function setMake(string $make): void { $this->make = $make; } - public function setModel($model) + public function setModel(string $model): void { $this->model = $model; } - public function setColor($color) + public function setColor(string $color): void { $this->color = $color; } - public function dump() + public function dump(): void { var_dump($this->make, $this->model, $this->color); } @@ -1427,19 +1427,19 @@ class UserSettings { private $user; - public function __construct($user) + public function __construct(User $user) { $this->user = $user; } - public function changeSettings($settings) + public function changeSettings(array $settings): void { if ($this->verifyCredentials()) { // ... } } - private function verifyCredentials() + private function verifyCredentials(): bool { // ... } @@ -1453,12 +1453,12 @@ class UserAuth { private $user; - public function __construct($user) + public function __construct(User $user) { $this->user = $user; } - public function verifyCredentials() + public function verifyCredentials(): bool { // ... } @@ -1469,13 +1469,13 @@ class UserSettings private $user; private $auth; - public function __construct($user) + public function __construct(User $user) { $this->user = $user; $this->auth = new UserAuth($user); } - public function changeSettings($settings) + public function changeSettings(array $settings): void { if ($this->auth->verifyCredentials()) { // ... @@ -1500,7 +1500,7 @@ abstract class Adapter { protected $name; - public function getName() + public function getName(): string { return $this->name; } @@ -1530,12 +1530,12 @@ class HttpRequester { private $adapter; - public function __construct($adapter) + public function __construct(Adapter $adapter) { $this->adapter = $adapter; } - public function fetch($url) + public function fetch(string $url): Promise { $adapterName = $this->adapter->getName(); @@ -1546,12 +1546,12 @@ class HttpRequester } } - private function makeAjaxCall($url) + private function makeAjaxCall(string $url): Promise { // request and return promise } - private function makeHttpCall($url) + private function makeHttpCall(string $url): Promise { // request and return promise } @@ -1563,12 +1563,12 @@ class HttpRequester ```php interface Adapter { - public function request($url); + public function request(string $url): Promise; } class AjaxAdapter implements Adapter { - public function request($url) + public function request(string $url): Promise { // request and return promise } @@ -1576,7 +1576,7 @@ class AjaxAdapter implements Adapter class NodeAdapter implements Adapter { - public function request($url) + public function request(string $url): Promise { // request and return promise } @@ -1591,7 +1591,7 @@ class HttpRequester $this->adapter = $adapter; } - public function fetch($url) + public function fetch(string $url): Promise { return $this->adapter->request($url); } @@ -1623,22 +1623,22 @@ class Rectangle protected $width = 0; protected $height = 0; - public function render($area) + public function render(int $area): void { // ... } - public function setWidth($width) + public function setWidth(int $width): void { $this->width = $width; } - public function setHeight($height) + public function setHeight(int $height): void { $this->height = $height; } - public function getArea() + public function getArea(): int { return $this->width * $this->height; } @@ -1646,18 +1646,18 @@ class Rectangle class Square extends Rectangle { - public function setWidth($width) + public function setWidth(int $width): void { $this->width = $this->height = $width; } - public function setHeight(height) + public function setHeight(int $height): void { $this->width = $this->height = $height; } } -function renderLargeRectangles($rectangles) +function renderLargeRectangles(Rectangle $rectangles): void { foreach ($rectangles as $rectangle) { $rectangle->setWidth(4); @@ -1679,9 +1679,9 @@ abstract class Shape protected $width = 0; protected $height = 0; - abstract public function getArea(); + abstract public function getArea(): int; - public function render($area) + public function render(int $area): void { // ... } @@ -1689,17 +1689,17 @@ abstract class Shape class Rectangle extends Shape { - public function setWidth($width) + public function setWidth(int $width): void { $this->width = $width; } - public function setHeight($height) + public function setHeight(int $height): void { $this->height = $height; } - public function getArea() + public function getArea(): int { return $this->width * $this->height; } @@ -1709,18 +1709,18 @@ class Square extends Shape { private $length = 0; - public function setLength($length) + public function setLength(int $length): void { $this->length = $length; } - public function getArea() + public function getArea(): int { return pow($this->length, 2); } } -function renderLargeRectangles($rectangles) +function renderLargeRectangles(Shape $rectangles): void { foreach ($rectangles as $rectangle) { if ($rectangle instanceof Square) { @@ -1756,19 +1756,19 @@ all of the settings. Making them optional helps prevent having a "fat interface" ```php interface Employee { - public function work(); + public function work(): void; - public function eat(); + public function eat(): void; } class Human implements Employee { - public function work() + public function work(): void { // ....working } - public function eat() + public function eat(): void { // ...... eating in lunch break } @@ -1776,12 +1776,12 @@ class Human implements Employee class Robot implements Employee { - public function work() + public function work(): void { //.... working much more } - public function eat() + public function eat(): void { //.... robot can't eat, but it must implement this method } @@ -1795,12 +1795,12 @@ Not every worker is an employee, but every employee is an worker. ```php interface Workable { - public function work(); + public function work(): void; } interface Feedable { - public function eat(); + public function eat(): void; } interface Employee extends Feedable, Workable @@ -1809,12 +1809,12 @@ interface Employee extends Feedable, Workable class Human implements Employee { - public function work() + public function work(): void { // ....working } - public function eat() + public function eat(): void { //.... eating in lunch break } @@ -1823,7 +1823,7 @@ class Human implements Employee // robot can only work class Robot implements Workable { - public function work() + public function work(): void { // ....working } @@ -1852,7 +1852,7 @@ it makes your code hard to refactor. ```php class Employee { - public function work() + public function work(): void { // ....working } @@ -1860,7 +1860,7 @@ class Employee class Robot extends Employee { - public function work() + public function work(): void { //.... working much more } @@ -1875,7 +1875,7 @@ class Manager $this->employee = $employee; } - public function manage() + public function manage(): void { $this->employee->work(); } @@ -1887,12 +1887,12 @@ class Manager ```php interface Employee { - public function work(); + public function work(): void; } class Human implements Employee { - public function work() + public function work(): void { // ....working } @@ -1900,7 +1900,7 @@ class Human implements Employee class Robot implements Employee { - public function work() + public function work(): void { //.... working much more } @@ -1915,7 +1915,7 @@ class Manager $this->employee = $employee; } - public function manage() + public function manage(): void { $this->employee->work(); } @@ -1952,7 +1952,7 @@ updating multiple places anytime you want to change one thing. **Bad:** ```php -function showDeveloperList($developers) +function showDeveloperList(array $developers): void { foreach ($developers as $developer) { $expectedSalary = $developer->calculateExpectedSalary(); @@ -1968,7 +1968,7 @@ function showDeveloperList($developers) } } -function showManagerList($managers) +function showManagerList(array $managers): void { foreach ($managers as $manager) { $expectedSalary = $manager->calculateExpectedSalary(); @@ -1988,7 +1988,7 @@ function showManagerList($managers) **Good:** ```php -function showList($employees) +function showList(array $employees): void { foreach ($employees as $employee) { $expectedSalary = $employee->calculateExpectedSalary(); @@ -2010,7 +2010,7 @@ function showList($employees) It is better to use a compact version of the code. ```php -function showList($employees) +function showList(array $employees): void { foreach ($employees as $employee) { render([ From bcc9eafda512337e8a2e8d54c30c3a33e3b57113 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Thu, 14 Sep 2017 15:25:40 +0300 Subject: [PATCH 124/210] return self in Car fluent interfaces --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a74eafa7..f3b7a8fb 100644 --- a/README.md +++ b/README.md @@ -1299,6 +1299,7 @@ class Employee **[⬆ back to top](#table-of-contents)** ### Avoid fluent interfaces + A [Fluent interface](https://en.wikipedia.org/wiki/Fluent_interface) is an object oriented API that aims to improve the readability of the source code by using [Method chaining](https://en.wikipedia.org/wiki/Method_chaining). @@ -1317,6 +1318,7 @@ For more informations you can read the full [blog post](https://ocramius.github. on this topic written by [Marco Pivetta](https://github.com/Ocramius). **Bad:** + ```php class Car { @@ -1324,7 +1326,7 @@ class Car private $model = 'Accord'; private $color = 'white'; - public function setMake(string $make): Car + public function setMake(string $make): self { $this->make = $make; @@ -1332,7 +1334,7 @@ class Car return $this; } - public function setModel(string $model): Car + public function setModel(string $model): self { $this->model = $model; @@ -1340,7 +1342,7 @@ class Car return $this; } - public function setColor(string $color): Car + public function setColor(string $color): self { $this->color = $color; @@ -1362,6 +1364,7 @@ $car = (new Car()) ``` **Good:** + ```php class Car { From 429d58494cdfcb9e126f937a587a025b444b6965 Mon Sep 17 00:00:00 2001 From: Owen Kieffer-Jones <okj579@users.noreply.github.com> Date: Fri, 15 Sep 2017 10:01:24 +0200 Subject: [PATCH 125/210] Minor grammatical fixes --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 06501cf2..b35d636f 100644 --- a/README.md +++ b/README.md @@ -989,7 +989,7 @@ The first thing to consider is consistent APIs. function travelToTexas($vehicle): void { if ($vehicle instanceof Bicycle) { - $vehicle->peddleTo(new Location('texas')); + $vehicle->pedalTo(new Location('texas')); } elseif ($vehicle instanceof Car) { $vehicle->driveTo(new Location('texas')); } @@ -1792,7 +1792,7 @@ class Robot implements Employee **Good:** -Not every worker is an employee, but every employee is an worker. +Not every worker is an employee, but every employee is a worker. ```php interface Workable From d6c0ddbec3f1188536a3212eff1566cd3bd2a830 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Fri, 15 Sep 2017 12:33:44 +0300 Subject: [PATCH 126/210] add Russian translation --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 06501cf2..490032ff 100644 --- a/README.md +++ b/README.md @@ -2033,5 +2033,7 @@ This is also available in other languages: * ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese:** * [yangweijie/clean-code-php](https://github.com/yangweijie/clean-code-php) * [php-cpm/clean-code-php](https://github.com/php-cpm/clean-code-php) + * ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian:** + * [peter-gribanov/clean-code-php](https://github.com/peter-gribanov/clean-code-php) **[⬆ back to top](#table-of-contents)** From 6261d5114c270c0bcfe2fc8f4c396ae46f3fa342 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Fri, 15 Sep 2017 16:11:03 +0300 Subject: [PATCH 127/210] add Thai language --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 490032ff..dd4843bc 100644 --- a/README.md +++ b/README.md @@ -2035,5 +2035,7 @@ This is also available in other languages: * [php-cpm/clean-code-php](https://github.com/php-cpm/clean-code-php) * ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian:** * [peter-gribanov/clean-code-php](https://github.com/peter-gribanov/clean-code-php) + * ![th](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Thailand.png) **Thai:** + * [panuwizzle/clean-code-php](https://github.com/panuwizzle/clean-code-php) **[⬆ back to top](#table-of-contents)** From d883307caa527109ac632a2019ffcb4051aabc19 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Fri, 15 Sep 2017 16:13:21 +0300 Subject: [PATCH 128/210] add Portuguese language --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index dd4843bc..a769ecd0 100644 --- a/README.md +++ b/README.md @@ -2035,6 +2035,8 @@ This is also available in other languages: * [php-cpm/clean-code-php](https://github.com/php-cpm/clean-code-php) * ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian:** * [peter-gribanov/clean-code-php](https://github.com/peter-gribanov/clean-code-php) + * ![pt](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Portuguese:** + * [fabioars/clean-code-php](https://github.com/fabioars/clean-code-php) * ![th](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Thailand.png) **Thai:** * [panuwizzle/clean-code-php](https://github.com/panuwizzle/clean-code-php) From f4724b684c08c5c4854b8803cf87caad5a780b52 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Fri, 15 Sep 2017 16:19:48 +0300 Subject: [PATCH 129/210] use emoji --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a769ecd0..10b0bb3a 100644 --- a/README.md +++ b/README.md @@ -2030,14 +2030,14 @@ function showList(array $employees): void This is also available in other languages: - * ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese:** +* :cn: **Chinese:** * [yangweijie/clean-code-php](https://github.com/yangweijie/clean-code-php) * [php-cpm/clean-code-php](https://github.com/php-cpm/clean-code-php) - * ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian:** +* :ru: **Russian:** * [peter-gribanov/clean-code-php](https://github.com/peter-gribanov/clean-code-php) - * ![pt](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Portuguese:** +* :brazil: **Portuguese:** * [fabioars/clean-code-php](https://github.com/fabioars/clean-code-php) - * ![th](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Thailand.png) **Thai:** +* :thailand: **Thai:** * [panuwizzle/clean-code-php](https://github.com/panuwizzle/clean-code-php) **[⬆ back to top](#table-of-contents)** From e11b2cef9a5bd3a753b9f8c516213fc3c434bf1a Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Fri, 15 Sep 2017 16:24:05 +0300 Subject: [PATCH 130/210] add more translations --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 10b0bb3a..aca6b854 100644 --- a/README.md +++ b/README.md @@ -2033,10 +2033,12 @@ This is also available in other languages: * :cn: **Chinese:** * [yangweijie/clean-code-php](https://github.com/yangweijie/clean-code-php) * [php-cpm/clean-code-php](https://github.com/php-cpm/clean-code-php) + * [gbcr/clean-code-php](https://github.com/gbcr/clean-code-php) * :ru: **Russian:** * [peter-gribanov/clean-code-php](https://github.com/peter-gribanov/clean-code-php) * :brazil: **Portuguese:** * [fabioars/clean-code-php](https://github.com/fabioars/clean-code-php) + * [jeanjar/clean-code-php](https://github.com/jeanjar/clean-code-php/tree/pt-br) * :thailand: **Thai:** * [panuwizzle/clean-code-php](https://github.com/panuwizzle/clean-code-php) From cfc058e44c5c0c2eb141aef7490ffe308b8eadbc Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Fri, 15 Sep 2017 17:01:02 +0300 Subject: [PATCH 131/210] change description of private/protected --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bf43be53..34fd2048 100644 --- a/README.md +++ b/README.md @@ -1159,9 +1159,9 @@ $balance = $bankAccount->getBalance(); ### Make objects have private/protected members -* `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. -* `protected` methods 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. -* `private` methods are your safe harbor in this doubtful world. They guarantee you that code in your methods is dangerous to modify only in boundaries of single class (which means that when you have tests for your protected/public methods that cover all calls of your private method, and as long as you don't do magic, like side effects or usage of global state, you are safe for modifications and you won't have [Jenga effect](http://www.urbandictionary.com/define.php?term=Jengaphobia&defid=2494196)). +* `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.** +* `protected` methods 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.** +* `private` methods are guarantee you that code in your methods 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)). Therefore, use `private` by default and `public/protected` when you need to provide access for external classes. From 0b06120c1e29e81763fb119b8b34e10584fca52c Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Fri, 15 Sep 2017 17:13:52 +0300 Subject: [PATCH 132/210] fix mistakes --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 34fd2048..beb9acc9 100644 --- a/README.md +++ b/README.md @@ -1160,8 +1160,8 @@ $balance = $bankAccount->getBalance(); ### Make objects have private/protected members * `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.** -* `protected` methods 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.** -* `private` methods are guarantee you that code in your methods 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)). +* `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.** +* `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)). Therefore, use `private` by default and `public/protected` when you need to provide access for external classes. From a26b56addd220077d25906fb186f108febfbbf84 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Mon, 18 Sep 2017 10:56:17 +0300 Subject: [PATCH 133/210] remove out of date chinese translations --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 3e1a9334..9618e660 100644 --- a/README.md +++ b/README.md @@ -2045,9 +2045,7 @@ function showList(array $employees): void This is also available in other languages: * :cn: **Chinese:** - * [yangweijie/clean-code-php](https://github.com/yangweijie/clean-code-php) * [php-cpm/clean-code-php](https://github.com/php-cpm/clean-code-php) - * [gbcr/clean-code-php](https://github.com/gbcr/clean-code-php) * :ru: **Russian:** * [peter-gribanov/clean-code-php](https://github.com/peter-gribanov/clean-code-php) * :brazil: **Portuguese:** From 0b6ab70f45fde17185855c0d9f60c3b16d9f6d85 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Mon, 18 Sep 2017 10:56:55 +0300 Subject: [PATCH 134/210] add Spanish translation --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9618e660..8058ebfd 100644 --- a/README.md +++ b/README.md @@ -2048,6 +2048,8 @@ This is also available in other languages: * [php-cpm/clean-code-php](https://github.com/php-cpm/clean-code-php) * :ru: **Russian:** * [peter-gribanov/clean-code-php](https://github.com/peter-gribanov/clean-code-php) +* :es: **Spanish:** + * [fikoborquez/clean-code-php](https://github.com/fikoborquez/clean-code-php) * :brazil: **Portuguese:** * [fabioars/clean-code-php](https://github.com/fabioars/clean-code-php) * [jeanjar/clean-code-php](https://github.com/jeanjar/clean-code-php/tree/pt-br) From 51b4b8782133b821e65124f3803add8c079c69f8 Mon Sep 17 00:00:00 2001 From: Yuriy Zinchenko <zinchencko.yu@gmail.com> Date: Mon, 18 Sep 2017 12:38:25 +0300 Subject: [PATCH 135/210] Optimize conditions in 'Avoid nesting too deeply and return early (part 2)' section --- README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3e1a9334..8a616b48 100644 --- a/README.md +++ b/README.md @@ -260,12 +260,8 @@ function fibonacci(int $n) ```php function fibonacci(int $n): int { - if ($n === 0) { - return 0; - } - - if ($n === 1) { - return 1; + if ($n === 0 || $n === 1) { + return $n; } if ($n > 50) { From b488cc2accc1cd84c316c4fc041544ef0b47e41b Mon Sep 17 00:00:00 2001 From: Yuriy Zinchenko <zinchencko.yu@gmail.com> Date: Mon, 18 Sep 2017 17:47:44 +0300 Subject: [PATCH 136/210] fix example in LSP section --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3e1a9334..d90180fa 100644 --- a/README.md +++ b/README.md @@ -1673,7 +1673,7 @@ class Square extends Rectangle } } -function renderLargeRectangles(Rectangle $rectangles): void +function renderLargeRectangles(array $rectangles): void { foreach ($rectangles as $rectangle) { $rectangle->setWidth(4); @@ -1736,7 +1736,7 @@ class Square extends Shape } } -function renderLargeRectangles(Shape $rectangles): void +function renderLargeRectangles(array $rectangles): void { foreach ($rectangles as $rectangle) { if ($rectangle instanceof Square) { From 4df90deaa948f34e9f5bb6894299513a404f7716 Mon Sep 17 00:00:00 2001 From: Alaa Attya <alaa.attya@tajawal.com> Date: Wed, 20 Sep 2017 19:39:17 +0200 Subject: [PATCH 137/210] use true for trick types. Check http://php.net/manual/en/function.in-array.php --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 48425aa1..219ca10d 100644 --- a/README.md +++ b/README.md @@ -226,7 +226,7 @@ function isShopOpen(string $day): bool 'friday', 'saturday', 'sunday' ]; - return in_array(strtolower($day), $openingDays); + return in_array(strtolower($day), $openingDays, true); } ``` From f213dfd4e561a015d2bb87c26f3bafb88265f661 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Fri, 22 Sep 2017 13:44:04 +0300 Subject: [PATCH 138/210] version announce --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 48425aa1..f6f0e1cf 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,8 @@ years of collective experience by the authors of *Clean Code*. Inspired from [clean-code-javascript](https://github.com/ryanmcdermott/clean-code-javascript) +Although many developers still use PHP 5, most of examples in this article work with PHP 7.1+. + ## Variables ### Use meaningful and pronounceable variable names From 1cf35d42f0a1e41227e882d6af3c94776d784a62 Mon Sep 17 00:00:00 2001 From: ZielinskiLukasz <lucas.zielinski@gmail.com> Date: Sat, 23 Sep 2017 11:59:08 +0200 Subject: [PATCH 139/210] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 48425aa1..5fc1a720 100644 --- a/README.md +++ b/README.md @@ -184,7 +184,7 @@ saveCityZipCode($matches['city'], $matches['zipCode']); ### Avoid nesting too deeply and return early (part 1) -Too many if else statemetns can make your code hard to follow. Explicit is better +Too many if else statements can make your code hard to follow. Explicit is better than implicit. **Bad:** From 3239445b8c3e692d4c7477e5d6e8906d0cc57e8f Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Mon, 25 Sep 2017 10:31:14 +0300 Subject: [PATCH 140/210] fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f6f0e1cf..888b95d4 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ years of collective experience by the authors of *Clean Code*. Inspired from [clean-code-javascript](https://github.com/ryanmcdermott/clean-code-javascript) -Although many developers still use PHP 5, most of examples in this article work with PHP 7.1+. +Although many developers still use PHP 5, most of the examples in this article only work with PHP 7.1+. ## Variables From 091009e2d49002354b216fc45e7b0fa3afe348bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Votruba?= <info@tomasvotruba.cz> Date: Wed, 27 Sep 2017 15:37:44 +0200 Subject: [PATCH 141/210] add Rectangle[] typehints --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index d90180fa..94a56920 100644 --- a/README.md +++ b/README.md @@ -1673,6 +1673,9 @@ class Square extends Rectangle } } +/** + * @param Rectangle[] $rectangles + */ function renderLargeRectangles(array $rectangles): void { foreach ($rectangles as $rectangle) { @@ -1736,6 +1739,9 @@ class Square extends Shape } } +/** + * @param Rectangle[] $rectangles + */ function renderLargeRectangles(array $rectangles): void { foreach ($rectangles as $rectangle) { From 0eb9d0405be18aa0c369777324176923586ab792 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Thu, 28 Sep 2017 17:39:57 +0300 Subject: [PATCH 142/210] zipCode is required --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 96757f28..348c1c62 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,7 @@ if ($user->access & User::ACCESS_UPDATE) { ```php $address = 'One Infinite Loop, Cupertino 95014'; -$cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/'; +$cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})$/'; preg_match($cityZipCodeRegex, $address, $matches); saveCityZipCode($matches[1], $matches[2]); @@ -163,7 +163,7 @@ It's better, but we are still heavily dependent on regex. ```php $address = 'One Infinite Loop, Cupertino 95014'; -$cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/'; +$cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})$/'; preg_match($cityZipCodeRegex, $address, $matches); [, $city, $zipCode] = $matches; @@ -176,7 +176,7 @@ Decrease dependence on regex by naming subpatterns. ```php $address = 'One Infinite Loop, Cupertino 95014'; -$cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(?<city>.+?)\s*(?<zipCode>\d{5})?$/'; +$cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(?<city>.+?)\s*(?<zipCode>\d{5})$/'; preg_match($cityZipCodeRegex, $address, $matches); saveCityZipCode($matches['city'], $matches['zipCode']); From dc125f02c93f2be4f25109b19c481f6cba909a46 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Thu, 28 Sep 2017 17:41:05 +0300 Subject: [PATCH 143/210] optimize cityZipCodeRegex --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 348c1c62..d7677928 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,7 @@ if ($user->access & User::ACCESS_UPDATE) { ```php $address = 'One Infinite Loop, Cupertino 95014'; -$cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})$/'; +$cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/'; preg_match($cityZipCodeRegex, $address, $matches); saveCityZipCode($matches[1], $matches[2]); @@ -163,7 +163,7 @@ It's better, but we are still heavily dependent on regex. ```php $address = 'One Infinite Loop, Cupertino 95014'; -$cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})$/'; +$cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/'; preg_match($cityZipCodeRegex, $address, $matches); [, $city, $zipCode] = $matches; @@ -176,7 +176,7 @@ Decrease dependence on regex by naming subpatterns. ```php $address = 'One Infinite Loop, Cupertino 95014'; -$cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(?<city>.+?)\s*(?<zipCode>\d{5})$/'; +$cityZipCodeRegex = '/^[^,]+,\s*(?<city>.+?)\s*(?<zipCode>\d{5})$/'; preg_match($cityZipCodeRegex, $address, $matches); saveCityZipCode($matches['city'], $matches['zipCode']); From 40ca2e5959264ce560bf0d93d47724c80666264c Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Fri, 6 Oct 2017 11:11:00 +0300 Subject: [PATCH 144/210] rename methods in BankAccount --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 96757f28..d762d93e 100644 --- a/README.md +++ b/README.md @@ -1129,7 +1129,7 @@ class BankAccount $this->balance = $balance; } - public function withdrawBalance(int $amount): void + public function withdraw(int $amount): void { if ($amount > $this->balance) { throw new \Exception('Amount greater than available balance.'); @@ -1138,12 +1138,12 @@ class BankAccount $this->balance -= $amount; } - public function depositBalance(int $amount): void + public function deposit(int $amount): void { $this->balance += $amount; } - public function getBalance(): int + public function balance(): int { return $this->balance; } @@ -1152,10 +1152,10 @@ class BankAccount $bankAccount = new BankAccount(); // Buy shoes... -$bankAccount->withdrawBalance($shoesPrice); +$bankAccount->withdraw($shoesPrice); // Get balance -$balance = $bankAccount->getBalance(); +$balance = $bankAccount->balance(); ``` **[⬆ back to top](#table-of-contents)** From 42fcf8c7a938c77f86b28f8e9262439bf70276a8 Mon Sep 17 00:00:00 2001 From: Mariano Custiel <jmcustiel@gmail.com> Date: Fri, 6 Oct 2017 19:13:34 +0200 Subject: [PATCH 145/210] Made changes suggested in code review --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 5eff6cb0..bfd3b894 100644 --- a/README.md +++ b/README.md @@ -1226,7 +1226,6 @@ class Rectangle extends Shape public function __construct($width, $height) { - parent::__construct(); $this->width = $width; $this->height = $height; } @@ -1243,7 +1242,6 @@ class Square extends Shape public function __construct($length) { - parent::__construct(); $this->length = $length; } From 27103f43efd4dee1fcaf5769071f7264ea75bb12 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Sat, 7 Oct 2017 09:48:29 +0300 Subject: [PATCH 146/210] Rename get balance method --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d762d93e..62fd2dc6 100644 --- a/README.md +++ b/README.md @@ -1143,7 +1143,7 @@ class BankAccount $this->balance += $amount; } - public function balance(): int +    public function getBalance(): int { return $this->balance; } @@ -1155,7 +1155,7 @@ $bankAccount = new BankAccount(); $bankAccount->withdraw($shoesPrice); // Get balance -$balance = $bankAccount->balance(); +$balance = $bankAccount->getBalance(); ``` **[⬆ back to top](#table-of-contents)** From 2e03a7689d7df104cad27420c353a7c395311179 Mon Sep 17 00:00:00 2001 From: smaine M <contact@smaine.me> Date: Sat, 7 Oct 2017 09:10:54 +0200 Subject: [PATCH 147/210] yoda condition and identical comparison added --- README.md | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d7677928..b914b794 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,10 @@ * [Avoid Mental Mapping](#avoid-mental-mapping) * [Don't add unneeded context](#dont-add-unneeded-context) * [Use default arguments instead of short circuiting or conditionals](#use-default-arguments-instead-of-short-circuiting-or-conditionals) - 3. [Functions](#functions) + 3. [Comparaison](#comparaison) + * [Use Yodas conditions](#yoda_condition) + * [Use identical comparison](#identical_comparison) + 4. [Functions](#functions) * [Function arguments (2 or fewer ideally)](#function-arguments-2-or-fewer-ideally) * [Functions should do one thing](#functions-should-do-one-thing) * [Function names should say what they do](#function-names-should-say-what-they-do) @@ -29,20 +32,20 @@ * [Avoid type-checking (part 1)](#avoid-type-checking-part-1) * [Avoid type-checking (part 2)](#avoid-type-checking-part-2) * [Remove dead code](#remove-dead-code) - 4. [Objects and Data Structures](#objects-and-data-structures) + 5. [Objects and Data Structures](#objects-and-data-structures) * [Use object encapsulation](#use-object-encapsulation) * [Make objects have private/protected members](#make-objects-have-privateprotected-members) - 5. [Classes](#classes) + 6. [Classes](#classes) * [Prefer composition over inheritance](#prefer-composition-over-inheritance) * [Avoid fluent interfaces](#avoid-fluent-interfaces) - 6. [SOLID](#solid) + 7. [SOLID](#solid) * [Single Responsibility Principle (SRP)](#single-responsibility-principle-srp) * [Open/Closed Principle (OCP)](#openclosed-principle-ocp) * [Liskov Substitution Principle (LSP)](#liskov-substitution-principle-lsp) * [Interface Segregation Principle (ISP)](#interface-segregation-principle-isp) * [Dependency Inversion Principle (DIP)](#dependency-inversion-principle-dip) - 7. [Don’t repeat yourself (DRY)](#dont-repeat-yourself-dry) - 8. [Translations](#translations) + 8. [Don’t repeat yourself (DRY)](#dont-repeat-yourself-dry) + 9. [Translations](#translations) ## Introduction @@ -386,6 +389,59 @@ function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void **[⬆ back to top](#table-of-contents)** +## Comparison + +### Use [Yoda conditions](https://en.wikipedia.org/wiki/Yoda_conditions) + +A Yoda condition places the constant portion of the expression on the left side of the conditional statement +Yoda conditions are part of the [WordPress](https://make.wordpress.org/core/handbook/best-practices/coding-standards/php/) and [Symfony coding standards](http://symfony.com/doc/current/contributing/code/standards.html). + +**Not good:** + + +```php +if ( $value == 42) +{ + // ... +} +``` +Reads like: "If the value equal to 42..." + +**Good:** + + +```php +if ( 42 == $value) +{ + // ... +} +``` +Reads like: "If the 42 equal to $value and it's avoid a common mistake 42 = $value" + +**[⬆ back to top](#table-of-contents)** + +### Use [identical comparison](http://php.net/manual/en/language.operators.comparison.php) + +**Not good:** + +```php +if( $a == $b ) +{ + //... +} +``` + +**Good:** + +```php +if( $a === $b ) +{ + //... +} +``` +**[⬆ back to top](#table-of-contents)** + + ## Functions ### Function arguments (2 or fewer ideally) From 348809d64de136359ee3ae7258d5cc3414678c05 Mon Sep 17 00:00:00 2001 From: smaine M <contact@smaine.me> Date: Mon, 23 Oct 2017 11:12:51 +0200 Subject: [PATCH 148/210] remove yoda condition and keep identical comparison --- README.md | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/README.md b/README.md index b914b794..fe2eb687 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,6 @@ * [Don't add unneeded context](#dont-add-unneeded-context) * [Use default arguments instead of short circuiting or conditionals](#use-default-arguments-instead-of-short-circuiting-or-conditionals) 3. [Comparaison](#comparaison) - * [Use Yodas conditions](#yoda_condition) * [Use identical comparison](#identical_comparison) 4. [Functions](#functions) * [Function arguments (2 or fewer ideally)](#function-arguments-2-or-fewer-ideally) @@ -391,33 +390,6 @@ function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void ## Comparison -### Use [Yoda conditions](https://en.wikipedia.org/wiki/Yoda_conditions) - -A Yoda condition places the constant portion of the expression on the left side of the conditional statement -Yoda conditions are part of the [WordPress](https://make.wordpress.org/core/handbook/best-practices/coding-standards/php/) and [Symfony coding standards](http://symfony.com/doc/current/contributing/code/standards.html). - -**Not good:** - - -```php -if ( $value == 42) -{ - // ... -} -``` -Reads like: "If the value equal to 42..." - -**Good:** - - -```php -if ( 42 == $value) -{ - // ... -} -``` -Reads like: "If the 42 equal to $value and it's avoid a common mistake 42 = $value" - **[⬆ back to top](#table-of-contents)** ### Use [identical comparison](http://php.net/manual/en/language.operators.comparison.php) From 588a2e130d1f413127668eb63c5c79d8b17a6949 Mon Sep 17 00:00:00 2001 From: smaine M <contact@smaine.me> Date: Mon, 23 Oct 2017 11:42:22 +0200 Subject: [PATCH 149/210] more details about identical comparison --- README.md | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index fe2eb687..9165aa93 100644 --- a/README.md +++ b/README.md @@ -397,20 +397,28 @@ function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void **Not good:** ```php -if( $a == $b ) -{ - //... +$a = '42'; +$b = 42; +Use the simple comparison will convert the string in an int + +if( $a != $b ) { + //The expression will always passes } + ``` +The comparison $a != $b return false but in fact it's true ! +The string '42' is different than the int 42 **Good:** - +Use the identical comparison will compare type and value ```php -if( $a === $b ) -{ - //... +if( $a !== $b ) { + //The expression is verified } + ``` +The comparison $a !== $b return true. + **[⬆ back to top](#table-of-contents)** From 79eabeed6b90af50d010f2cbda88a099c86c883c Mon Sep 17 00:00:00 2001 From: Thibaud Courtoison <do.not.press.enter@gmail.com> Date: Sun, 26 Nov 2017 17:27:05 +0100 Subject: [PATCH 150/210] Add french translation I wrote the french translation for this guide --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d827aa24..bef255cf 100644 --- a/README.md +++ b/README.md @@ -2048,7 +2048,7 @@ function showList(array $employees): void This is also available in other languages: -* :cn: **Chinese:** +* :cn: **Chinese:** * [php-cpm/clean-code-php](https://github.com/php-cpm/clean-code-php) * :ru: **Russian:** * [peter-gribanov/clean-code-php](https://github.com/peter-gribanov/clean-code-php) @@ -2059,5 +2059,7 @@ This is also available in other languages: * [jeanjar/clean-code-php](https://github.com/jeanjar/clean-code-php/tree/pt-br) * :thailand: **Thai:** * [panuwizzle/clean-code-php](https://github.com/panuwizzle/clean-code-php) +* :fr: **French:** + * [errorname/clean-code-php](https://github.com/errorname/clean-code-php) **[⬆ back to top](#table-of-contents)** From 1456a97ddf35ae955ee88b2f006a42ff45a645ac Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Mon, 27 Nov 2017 11:03:45 +0300 Subject: [PATCH 151/210] add prifix for getters --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index eaa8ab58..1e160646 100644 --- a/README.md +++ b/README.md @@ -1647,7 +1647,7 @@ class Rectangle $this->height = $height; } - public function area(): int + public function getArea(): int { return $this->width * $this->height; } @@ -1672,7 +1672,7 @@ function printArea(Rectangle $rectangle): void $rectangle->setHeight(5); // BAD: Will return 25 for Square. Should be 20. - echo sprintf('%s has area %d.', get_class($rectangle), $rectangle->area()).PHP_EOL; + echo sprintf('%s has area %d.', get_class($rectangle), $rectangle->getArea()).PHP_EOL; } $rectangles = [new Rectangle(), new Square()]; @@ -1698,17 +1698,17 @@ class Rectangle $this->height = $height; } - public function width(): int + public function getWidth(): int { return $this->width; } - public function height(): int + public function getHeight(): int { return $this->height; } - public function area(): int + public function getArea(): int { return $this->width * $this->height; } @@ -1724,7 +1724,7 @@ class Square extends Rectangle function printArea(Rectangle $rectangle): void { - echo sprintf('%s has area %d.', get_class($rectangle), $rectangle->area()).PHP_EOL; + echo sprintf('%s has area %d.', get_class($rectangle), $rectangle->getArea()).PHP_EOL; } $rectangles = [new Rectangle(4, 5), new Square(5)]; @@ -1748,7 +1748,7 @@ A square, a rectangle, a rhombus and a parallelogram are separate shapes with th ```php interface Shape { - public function area(): int; + public function getArea(): int; } class Rectangle implements Shape @@ -1762,17 +1762,17 @@ class Rectangle implements Shape $this->height = $height; } - public function width(): int + public function getWdth(): int { return $this->width; } - public function height(): int + public function getHeight(): int { return $this->height; } - public function area(): int + public function getArea(): int { return $this->width * $this->height; } @@ -1787,12 +1787,12 @@ class Square implements Shape $this->length = $length; } - public function length(): int + public function getLength(): int { return $this->length; } - public function area(): int + public function getArea(): int {        return $this->length ** 2;    } @@ -1800,7 +1800,7 @@ class Square implements Shape function printArea(Shape $shape): void { - echo sprintf('%s has area %d.', get_class($shape), $shape->area()).PHP_EOL; + echo sprintf('%s has area %d.', get_class($shape), $shape->getArea()).PHP_EOL; } $shapes = [new Rectangle(4, 5), new Square(5)]; From 68a00031cd9cef334a5a362828435a675d3aa33d Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Mon, 27 Nov 2017 15:58:10 +0300 Subject: [PATCH 152/210] remove Not bad section --- README.md | 55 ------------------------------------------------------- 1 file changed, 55 deletions(-) diff --git a/README.md b/README.md index 1e160646..bc7e3073 100644 --- a/README.md +++ b/README.md @@ -1682,61 +1682,6 @@ foreach ($rectangles as $rectangle) { } ``` -**Not bad:** - -You can solve the problem by making objects immutable. - -```php -class Rectangle -{ - private $width = 0; - private $height = 0; - - public function __construct(int $width, int $height) - { - $this->width = $width; - $this->height = $height; - } - - public function getWidth(): int - { - return $this->width; - } - - public function getHeight(): int - { - return $this->height; - } - - public function getArea(): int - { - return $this->width * $this->height; - } -} - -class Square extends Rectangle -{ - public function __construct(int $length) - { - parent::__construct($length, $length); - } -} - -function printArea(Rectangle $rectangle): void -{ - echo sprintf('%s has area %d.', get_class($rectangle), $rectangle->getArea()).PHP_EOL; -} - -$rectangles = [new Rectangle(4, 5), new Square(5)]; - -foreach ($rectangles as $rectangle) { - printArea($rectangle); -} -``` - -This solution no longer violates the LSP principle because we can use a subtype. -But this is not a best solution, because the square specifies the invariants of the rectangle. - **Good:** The best way is separate the quadrangles and allocation of a more general subtype for both shapes. From d807a2454ca852f1e665499ab8b7c66ebb9d6228 Mon Sep 17 00:00:00 2001 From: Dylan Delobel <dylan.delobel66@gmail.com> Date: Thu, 21 Dec 2017 15:59:44 +0100 Subject: [PATCH 153/210] Fix Php version for type hinting --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bef255cf..77fc055a 100644 --- a/README.md +++ b/README.md @@ -375,7 +375,7 @@ function createMicrobrewery($name = null): void **Good:** -If you support only PHP 7+, then you can use [type hinting](http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration) and be sure that the `$breweryName` will not be `NULL`. +If you support only PHP 7.1+, then you can use [type hinting](http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration) and be sure that the `$breweryName` will not be `NULL`. ```php function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void From 1b4a2a12349350591aae32021b6df91e73b51fdb Mon Sep 17 00:00:00 2001 From: hoseinz3 <mhs.abedi@gmail.com> Date: Thu, 28 Dec 2017 12:45:20 +0330 Subject: [PATCH 154/210] Fix const color miss match --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bef255cf..038c9852 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ class User { const ACCESS_READ = 1; const ACCESS_CREATE = 2; -    const ACCESS_UPDATE = 4; + const ACCESS_UPDATE = 4; const ACCESS_DELETE = 8; } From 0662992e4a6af683eb08260f2734b5653175f307 Mon Sep 17 00:00:00 2001 From: Dylan Delobel <dylan.delobel66@gmail.com> Date: Fri, 29 Dec 2017 15:50:26 +0100 Subject: [PATCH 155/210] Add peter-gribanov suggestion --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 77fc055a..aff30e97 100644 --- a/README.md +++ b/README.md @@ -375,7 +375,7 @@ function createMicrobrewery($name = null): void **Good:** -If you support only PHP 7.1+, then you can use [type hinting](http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration) and be sure that the `$breweryName` will not be `NULL`. + You can use [type hinting](http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration) and be sure that the `$breweryName` will not be `NULL`. ```php function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void From 7909ca8bee68636892fe85161996b59480303031 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Tue, 9 Jan 2018 11:12:39 +0300 Subject: [PATCH 156/210] format Comparison section --- README.md | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index f18eb8ef..5edf9583 100644 --- a/README.md +++ b/README.md @@ -390,34 +390,38 @@ function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void ## Comparison -**[⬆ back to top](#table-of-contents)** - ### Use [identical comparison](http://php.net/manual/en/language.operators.comparison.php) **Not good:** +The simple comparison will convert the string in an integer. + ```php $a = '42'; $b = 42; -Use the simple comparison will convert the string in an int -if( $a != $b ) { - //The expression will always passes +if ($a != $b) { + // The expression will always passes } - ``` -The comparison $a != $b return false but in fact it's true ! -The string '42' is different than the int 42 + +The comparison `$a != $b` return `FALSE` but in fact it's `TRUE`! +The string `42` is different than the integer `42`. **Good:** -Use the identical comparison will compare type and value + +The identical comparison will compare type and value. + ```php -if( $a !== $b ) { - //The expression is verified -} +$a = '42'; +$b = 42; +if ($a !== $b) { + // The expression is verified +} ``` -The comparison $a !== $b return true. + +The comparison `$a !== $b` return `TRUE`. **[⬆ back to top](#table-of-contents)** From 858d5f12e38434e21a764c0decbe7e2914c0a0a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=B9i=20Vi=E1=BA=BFt=20H=C6=B0=E1=BB=9Bng?= <viethuong2072000@gmail.com> Date: Mon, 15 Jan 2018 14:24:42 +0700 Subject: [PATCH 157/210] Update README.md Add translate for Vietnamese --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f18eb8ef..d2a88da6 100644 --- a/README.md +++ b/README.md @@ -2085,6 +2085,7 @@ This is also available in other languages: * :thailand: **Thai:** * [panuwizzle/clean-code-php](https://github.com/panuwizzle/clean-code-php) * :fr: **French:** - * [errorname/clean-code-php](https://github.com/errorname/clean-code-php) + * [viethuongdev/clean-code-php](https://github.com/viethuongdev/clean-code-php) +* :vi: **Vietnamese** **[⬆ back to top](#table-of-contents)** From aa7a7ebec5bde33ddc8591842cda1f93b951b638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=B9i=20Vi=E1=BA=BFt=20H=C6=B0=E1=BB=9Bng?= <viethuong2072000@gmail.com> Date: Mon, 15 Jan 2018 14:27:48 +0700 Subject: [PATCH 158/210] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d2a88da6..09dfadfa 100644 --- a/README.md +++ b/README.md @@ -2085,7 +2085,8 @@ This is also available in other languages: * :thailand: **Thai:** * [panuwizzle/clean-code-php](https://github.com/panuwizzle/clean-code-php) * :fr: **French:** - * [viethuongdev/clean-code-php](https://github.com/viethuongdev/clean-code-php) + * [errorname/clean-code-php](https://github.com/errorname/clean-code-php) * :vi: **Vietnamese** + * [viethuongdev/clean-code-php](https://github.com/viethuongdev/clean-code-php) **[⬆ back to top](#table-of-contents)** From 3b30f1eb35384bdd279c39386f21d834d3c19604 Mon Sep 17 00:00:00 2001 From: Borislav Kosun <kosun.b@yandex.ua> Date: Mon, 29 Jan 2018 02:59:00 +0200 Subject: [PATCH 159/210] Fixed a link in the menu (Comparison) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f18eb8ef..b0692799 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ * [Avoid Mental Mapping](#avoid-mental-mapping) * [Don't add unneeded context](#dont-add-unneeded-context) * [Use default arguments instead of short circuiting or conditionals](#use-default-arguments-instead-of-short-circuiting-or-conditionals) - 3. [Comparaison](#comparaison) + 3. [Comparison](#comparison) * [Use identical comparison](#identical_comparison) 4. [Functions](#functions) * [Function arguments (2 or fewer ideally)](#function-arguments-2-or-fewer-ideally) From 6ca513892b40e39ea9e34839570487f4dde6425c Mon Sep 17 00:00:00 2001 From: Borislav Kosun <kosun.b@yandex.ua> Date: Mon, 29 Jan 2018 03:00:45 +0200 Subject: [PATCH 160/210] Fixed a link in the menu (Use identical compar...) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b0692799..0e267865 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ * [Don't add unneeded context](#dont-add-unneeded-context) * [Use default arguments instead of short circuiting or conditionals](#use-default-arguments-instead-of-short-circuiting-or-conditionals) 3. [Comparison](#comparison) - * [Use identical comparison](#identical_comparison) + * [Use identical comparison](#use-identical-comparison) 4. [Functions](#functions) * [Function arguments (2 or fewer ideally)](#function-arguments-2-or-fewer-ideally) * [Functions should do one thing](#functions-should-do-one-thing) From 46a388272160e29f7a4154b48174e5e6ebdc9a74 Mon Sep 17 00:00:00 2001 From: Michael Millar <michael.w.millar@gmail.com> Date: Mon, 29 Jan 2018 22:08:34 +0100 Subject: [PATCH 161/210] fixed some typos and inconsistencies --- README.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index f18eb8ef..ee1d5eaf 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Clean Code PHP +# Clean Code PHP ## Table of Contents @@ -14,7 +14,7 @@ * [Avoid Mental Mapping](#avoid-mental-mapping) * [Don't add unneeded context](#dont-add-unneeded-context) * [Use default arguments instead of short circuiting or conditionals](#use-default-arguments-instead-of-short-circuiting-or-conditionals) - 3. [Comparaison](#comparaison) + 3. [Comparison](#comparison) * [Use identical comparison](#identical_comparison) 4. [Functions](#functions) * [Function arguments (2 or fewer ideally)](#function-arguments-2-or-fewer-ideally) @@ -57,7 +57,7 @@ Not every principle herein has to be strictly followed, and even fewer will be u agreed upon. These are guidelines and nothing more, but they are ones codified over many years of collective experience by the authors of *Clean Code*. -Inspired from [clean-code-javascript](https://github.com/ryanmcdermott/clean-code-javascript) +Inspired from [clean-code-javascript](https://github.com/ryanmcdermott/clean-code-javascript). Although many developers still use PHP 5, most of the examples in this article only work with PHP 7.1+. @@ -188,7 +188,7 @@ saveCityZipCode($matches['city'], $matches['zipCode']); ### Avoid nesting too deeply and return early (part 1) -Too many if else statements can make your code hard to follow. Explicit is better +Too many if-else statements can make your code hard to follow. Explicit is better than implicit. **Bad:** @@ -406,8 +406,8 @@ if( $a != $b ) { } ``` -The comparison $a != $b return false but in fact it's true ! -The string '42' is different than the int 42 +The comparison $a != $b returns false but in fact it's true! +The string '42' is different than the int 42. **Good:** Use the identical comparison will compare type and value @@ -417,7 +417,7 @@ if( $a !== $b ) { } ``` -The comparison $a !== $b return true. +The comparison $a !== $b returns true. **[⬆ back to top](#table-of-contents)** @@ -783,7 +783,7 @@ var_dump($newName); // ['Ryan', 'McDermott']; Polluting globals is a bad practice in many languages because you could clash with another library and the user of your API would be none-the-wiser until they get an exception in -production. Let's think about an example: what if you wanted to have configuration array. +production. Let's think about an example: what if you wanted to have configuration array? You could write global function like `config()`, but it could clash with another library that tried to do the same thing. @@ -1356,10 +1356,10 @@ pattern reduces the verbosity of the code (for example the [PHPUnit Mock Builder or the [Doctrine Query Builder](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/query-builder.html)), more often it comes at some costs: -1. Breaks [Encapsulation](https://en.wikipedia.org/wiki/Encapsulation_%28object-oriented_programming%29) -2. Breaks [Decorators](https://en.wikipedia.org/wiki/Decorator_pattern) -3. Is harder to [mock](https://en.wikipedia.org/wiki/Mock_object) in a test suite -4. Makes diffs of commits harder to read +1. Breaks [Encapsulation](https://en.wikipedia.org/wiki/Encapsulation_%28object-oriented_programming%29). +2. Breaks [Decorators](https://en.wikipedia.org/wiki/Decorator_pattern). +3. Is harder to [mock](https://en.wikipedia.org/wiki/Mock_object) in a test suite. +4. Makes diffs of commits harder to read. For more informations you can read the full [blog post](https://ocramius.github.io/blog/fluent-interfaces-are-evil/) on this topic written by [Marco Pivetta](https://github.com/Ocramius). @@ -1792,7 +1792,7 @@ ISP states that "Clients should not be forced to depend upon interfaces that they do not use." A good example to look at that demonstrates this principle is for -classes that require large settings objects. Not requiring clients to setup +classes that require large settings objects. Not requiring clients to set up huge amounts of options is beneficial, because most of the time they won't need all of the settings. Making them optional helps prevent having a "fat interface". @@ -1982,7 +1982,7 @@ tomatoes, onions, garlic, spices, etc. If you have multiple lists that you keep this on, then all have to be updated when you serve a dish with tomatoes in them. If you only have one list, there's only one place to update! -Oftentimes you have duplicate code because you have two or more slightly +Often you have duplicate code because you have two or more slightly different things, that share a lot in common, but their differences force you to have two or more separate functions that do much of the same things. Removing duplicate code means creating an abstraction that can handle this set of different @@ -1992,7 +1992,7 @@ Getting the abstraction right is critical, that's why you should follow the SOLID principles laid out in the [Classes](#classes) section. Bad abstractions can be worse than duplicate code, so be careful! Having said this, if you can make a good abstraction, do it! Don't repeat yourself, otherwise you'll find yourself -updating multiple places anytime you want to change one thing. +updating multiple places any time you want to change one thing. **Bad:** From e90be29ff38a9a40901591cf339e1a8e2d3aa1b0 Mon Sep 17 00:00:00 2001 From: jopacicdev <jopacicdev@users.noreply.github.com> Date: Fri, 16 Feb 2018 22:35:08 +0100 Subject: [PATCH 162/210] Wording adjustment --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f18eb8ef..077d658c 100644 --- a/README.md +++ b/README.md @@ -399,10 +399,10 @@ function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void ```php $a = '42'; $b = 42; -Use the simple comparison will convert the string in an int +// Use the simple comparison that will convert the string in an int if( $a != $b ) { - //The expression will always passes + // The expression will always pass } ``` From 66bcb6a1c0897215a8b00bbd52d3b6af9fe619b1 Mon Sep 17 00:00:00 2001 From: Spomky <florent@morselli.fr> Date: Fri, 23 Feb 2018 21:40:31 +0100 Subject: [PATCH 163/210] final keyword --- README.md | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/README.md b/README.md index f18eb8ef..6ed85ca7 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ 6. [Classes](#classes) * [Prefer composition over inheritance](#prefer-composition-over-inheritance) * [Avoid fluent interfaces](#avoid-fluent-interfaces) + * [Prefer `final` keyword](#prefer-final-classes) 7. [SOLID](#solid) * [Single Responsibility Principle (SRP)](#single-responsibility-principle-srp) * [Open/Closed Principle (OCP)](#openclosed-principle-ocp) @@ -1449,6 +1450,72 @@ $car->dump(); **[⬆ back to top](#table-of-contents)** +### Prefer final classes + +The `final` should be used whenever possible: + +1. It prevents uncontrolled inheritance chain. +2. It encourages [composition](#prefer-composition-over-inheritance). +3. It encourages the [Single Responsibility Pattern](#single-responsibility-principle-srp). +4. It encourages developers to use your public methods instead of extending the class to get access on protected ones. +5. It allows you to change your code without any break of applications that use your class. + +The only condition is that your class should implement an interface and no other public methods are defined. + +**Bad:** + +```php +final class Car +{ + private $color; + + public function __construct($color) + { + $this->color = $color; + } + + /** + * @return string The color of the vehicule + */ + public function getColor() + { + return $this->color; + } +} +``` + +**Good:** + +```php +interface Vehicule +{ + /** + * @return string The color of the vehicule + */ + public function getColor(); +} + +final class Car interface Vehicule +{ + private $color; + + public function __construct($color) + { + $this->color = $color; + } + + /** + * {@inheritdoc} + */ + public function getColor() + { + return $this->color; + } +} +``` + +**[⬆ back to top](#table-of-contents)** + ## SOLID **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. From e191bf8464c79039b8f7f3a0fec06667585759b3 Mon Sep 17 00:00:00 2001 From: Spomky <florent@morselli.fr> Date: Fri, 23 Feb 2018 21:42:13 +0100 Subject: [PATCH 164/210] Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6ed85ca7..8f2a38a9 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ 6. [Classes](#classes) * [Prefer composition over inheritance](#prefer-composition-over-inheritance) * [Avoid fluent interfaces](#avoid-fluent-interfaces) - * [Prefer `final` keyword](#prefer-final-classes) + * [Prefer `final` classes](#prefer-final-classes) 7. [SOLID](#solid) * [Single Responsibility Principle (SRP)](#single-responsibility-principle-srp) * [Open/Closed Principle (OCP)](#openclosed-principle-ocp) From 88c78655580e7da89da7ec6c35e5250ffbd48048 Mon Sep 17 00:00:00 2001 From: Spomky <florent@morselli.fr> Date: Fri, 23 Feb 2018 22:36:39 +0100 Subject: [PATCH 165/210] Typo --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8f2a38a9..4c0ffa8e 100644 --- a/README.md +++ b/README.md @@ -1475,7 +1475,7 @@ final class Car } /** - * @return string The color of the vehicule + * @return string The color of the vehicle */ public function getColor() { @@ -1487,15 +1487,15 @@ final class Car **Good:** ```php -interface Vehicule +interface Vehicle { /** - * @return string The color of the vehicule + * @return string The color of the vehicle */ public function getColor(); } -final class Car interface Vehicule +final class Car implements Vehicle { private $color; From 9ae7bbf8f634c4ad370e5ea73b1b48ff65db8cf7 Mon Sep 17 00:00:00 2001 From: yujinee <jangy100@nate.com> Date: Mon, 26 Feb 2018 03:24:39 +0900 Subject: [PATCH 166/210] add Korean translation I wrote the korean translation for this guide. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f18eb8ef..40b5d905 100644 --- a/README.md +++ b/README.md @@ -2086,5 +2086,7 @@ This is also available in other languages: * [panuwizzle/clean-code-php](https://github.com/panuwizzle/clean-code-php) * :fr: **French:** * [errorname/clean-code-php](https://github.com/errorname/clean-code-php) - +* 🇰🇷 **Korean:** + * [yujineeee/clean-code-php](https://github.com/yujineeee/clean-code-php) + **[⬆ back to top](#table-of-contents)** From dfe3d111872519bcb780e8b2ea00e34be0463641 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Mon, 26 Feb 2018 23:33:21 +0300 Subject: [PATCH 167/210] use github emoji code for flags of translations --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7ac3712b..dc4b6e48 100644 --- a/README.md +++ b/README.md @@ -2090,9 +2090,9 @@ This is also available in other languages: * [panuwizzle/clean-code-php](https://github.com/panuwizzle/clean-code-php) * :fr: **French:** * [errorname/clean-code-php](https://github.com/errorname/clean-code-php) -* :vi: **Vietnamese** +* :vietnam: **Vietnamese** * [viethuongdev/clean-code-php](https://github.com/viethuongdev/clean-code-php) -* 🇰🇷 **Korean:** +* :kr: **Korean:** * [yujineeee/clean-code-php](https://github.com/yujineeee/clean-code-php) **[⬆ back to top](#table-of-contents)** From 6c1da67e2816b28c25346d5e355a1d8c889fa719 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Tue, 27 Feb 2018 00:05:40 +0300 Subject: [PATCH 168/210] remove not used getters --- README.md | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/README.md b/README.md index e8156bd3..f1f5b6cc 100644 --- a/README.md +++ b/README.md @@ -1747,16 +1747,6 @@ class Rectangle implements Shape $this->height = $height; } - public function getWdth(): int - { - return $this->width; - } - - public function getHeight(): int - { - return $this->height; - } - public function getArea(): int { return $this->width * $this->height; @@ -1772,11 +1762,6 @@ class Square implements Shape $this->length = $length; } - public function getLength(): int - { - return $this->length; - } - public function getArea(): int {        return $this->length ** 2; From ab83eece3f15e6c1207cc3f171f9ded2d89281da Mon Sep 17 00:00:00 2001 From: Spomky <Spomky@users.noreply.github.com> Date: Tue, 27 Feb 2018 09:20:42 +0100 Subject: [PATCH 169/210] Reference to Ocramius blog post added --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4c0ffa8e..a74c2959 100644 --- a/README.md +++ b/README.md @@ -1462,6 +1462,8 @@ The `final` should be used whenever possible: The only condition is that your class should implement an interface and no other public methods are defined. +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/). + **Bad:** ```php From 39d511f84ad955f93efffc937f48a093aeeebe47 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Tue, 10 Apr 2018 12:59:09 +0300 Subject: [PATCH 170/210] rename classes to avoid confusion --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7ac3712b..40dd4e7d 100644 --- a/README.md +++ b/README.md @@ -1810,7 +1810,7 @@ interface Employee public function eat(): void; } -class Human implements Employee +class HumanEmployee implements Employee { public function work(): void { @@ -1823,7 +1823,7 @@ class Human implements Employee } } -class Robot implements Employee +class RobotEmployee implements Employee { public function work(): void { @@ -1856,7 +1856,7 @@ interface Employee extends Feedable, Workable { } -class Human implements Employee +class HumanEmployee implements Employee { public function work(): void { @@ -1870,7 +1870,7 @@ class Human implements Employee } // robot can only work -class Robot implements Workable +class RobotEmployee implements Workable { public function work(): void { From 15bc7a39a4c179a9111df495d71b5ab2fb20da49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C4=B1l=20=C3=96zmen?= <anil@anilozmen.com.tr> Date: Sat, 15 Sep 2018 00:11:56 +0300 Subject: [PATCH 171/210] Add Turkish Translation Add Turkish Translation --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 1cfc65cf..a3c10d87 100644 --- a/README.md +++ b/README.md @@ -2155,5 +2155,7 @@ This is also available in other languages: * [viethuongdev/clean-code-php](https://github.com/viethuongdev/clean-code-php) * :kr: **Korean:** * [yujineeee/clean-code-php](https://github.com/yujineeee/clean-code-php) +* :tr: **Turkish:** + * [anilozmen/clean-code-php](https://github.com/anilozmen/clean-code-php) **[⬆ back to top](#table-of-contents)** From 7a7a1f08e74114436a8eac53aebc6d8aa9c70976 Mon Sep 17 00:00:00 2001 From: Dominik Liebler <liebler.dominik@gmail.com> Date: Fri, 5 Oct 2018 12:52:15 +0200 Subject: [PATCH 172/210] used a better name for Vehicle abstraction --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a3c10d87..cfa29cff 100644 --- a/README.md +++ b/README.md @@ -1044,7 +1044,7 @@ function travelToTexas($vehicle): void **Good:** ```php -function travelToTexas(Traveler $vehicle): void +function travelToTexas(Vehicle $vehicle): void { $vehicle->travelTo(new Location('texas')); } From 6be37a0971b90a0924799a62ee6efadabdcd6552 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Tue, 29 Jan 2019 10:28:12 +0300 Subject: [PATCH 173/210] add default value for User::$access --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index a3c10d87..9398f40e 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,12 @@ $json = $serializer->serialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT **Bad:** ```php +class User +{ + // What the heck is 7 for? + public $access = 7; +} + // What the heck is 4 for? if ($user->access & 4) { // ... @@ -139,6 +145,8 @@ class User const ACCESS_CREATE = 2; const ACCESS_UPDATE = 4; const ACCESS_DELETE = 8; + + public $access = self::ACCESS_READ | self::ACCESS_CREATE | self::ACCESS_UPDATE; } if ($user->access & User::ACCESS_UPDATE) { From f080c5b42d945cc57396191895858abe05ba75b1 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Tue, 29 Jan 2019 10:56:17 +0300 Subject: [PATCH 174/210] add deny access rights example --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 9398f40e..43201fe6 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,9 @@ class User if ($user->access & 4) { // ... } + +// What's going on here? +$user->access ^= 2; ``` **Good:** @@ -146,12 +149,16 @@ class User const ACCESS_UPDATE = 4; const ACCESS_DELETE = 8; + // User as default can read, create and update something public $access = self::ACCESS_READ | self::ACCESS_CREATE | self::ACCESS_UPDATE; } if ($user->access & User::ACCESS_UPDATE) { // do edit ... } + +// Deny access rights to create something +$user->access ^= User::ACCESS_CREATE; ``` **[⬆ back to top](#table-of-contents)** From acb8ba054dae6abc6718118ce101cd5013977919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Make=C5=A1?= <info@makes.cz> Date: Sun, 12 May 2019 22:13:14 +0200 Subject: [PATCH 175/210] Fixed - opposite for condition Opposite for `$n < 50` is `$n >= 50`. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a3c10d87..29791625 100644 --- a/README.md +++ b/README.md @@ -269,7 +269,7 @@ function fibonacci(int $n): int return $n; } - if ($n > 50) { + if ($n >= 50) { throw new \Exception('Not supported'); } From e0fc6a8342ed2a93c30f11f80ca9403fc6f008cf Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Thu, 11 Jul 2019 18:55:09 +0300 Subject: [PATCH 176/210] allow use more of the two depth levels in table of contents --- .travis-build.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.travis-build.php b/.travis-build.php index 93333509..1a8b8358 100644 --- a/.travis-build.php +++ b/.travis-build.php @@ -25,14 +25,15 @@ } $tableOfContentsStarted = false; - $chaptersFound[] = sprintf('%s [%s](#%s)', - strlen($matches['depth']) === 2 - ? sprintf(' %s.', ++$manIndex) - : ' *' - , - $matches['title'], - preg_replace(['/ /', '/[^-\w]+/'], ['-', ''], strtolower($matches['title'])) - ); + if (strlen($matches['depth']) === 2) { + $depth = sprintf(' %s.', ++$manIndex); + } else { + $depth = sprintf(' %s*', str_repeat(' ', strlen($matches['depth']) - 1)); + } + + $link = preg_replace(['/ /', '/[^-\w]+/'], ['-', ''], strtolower($matches['title'])); + + $chaptersFound[] = sprintf('%s [%s](#%s)', $depth, $matches['title'], $link); } if ($tableOfContentsStarted === true && isset($line[0])) { $currentTableOfContentsChapters[] = $line; From 9222ff3a37aecb14953280cbc627be0064a2a303 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Thu, 11 Jul 2019 18:56:09 +0300 Subject: [PATCH 177/210] allow use unicode in link names --- .travis-build.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis-build.php b/.travis-build.php index 1a8b8358..8430d16d 100644 --- a/.travis-build.php +++ b/.travis-build.php @@ -31,7 +31,10 @@ $depth = sprintf(' %s*', str_repeat(' ', strlen($matches['depth']) - 1)); } - $link = preg_replace(['/ /', '/[^-\w]+/'], ['-', ''], strtolower($matches['title'])); + $link = $matches['title']; + $link = strtolower($link); + $link = str_replace(' ', '-', $link); + $link = preg_replace('/[^-\w]+/u', '', $link); $chaptersFound[] = sprintf('%s [%s](#%s)', $depth, $matches['title'], $link); } From 4022b02035af1848dedcf63227b7d40ae04a2af4 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Thu, 11 Jul 2019 18:56:29 +0300 Subject: [PATCH 178/210] ignore links in title --- .travis-build.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis-build.php b/.travis-build.php index 8430d16d..17a63fee 100644 --- a/.travis-build.php +++ b/.travis-build.php @@ -31,6 +31,9 @@ $depth = sprintf(' %s*', str_repeat(' ', strlen($matches['depth']) - 1)); } + // ignore links in title + $matches['title'] = preg_replace('/\[([^\]]+)\]\((?:[^\)]+)\)/u', '$1', $matches['title']); + $link = $matches['title']; $link = strtolower($link); $link = str_replace(' ', '-', $link); From d29db0254a2485e39e8dc0f8c0ac64ddfa39f4c4 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Thu, 11 Jul 2019 19:09:05 +0300 Subject: [PATCH 179/210] not use quotes in table of contents --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a3c10d87..e4b1ca02 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ 6. [Classes](#classes) * [Prefer composition over inheritance](#prefer-composition-over-inheritance) * [Avoid fluent interfaces](#avoid-fluent-interfaces) - * [Prefer `final` classes](#prefer-final-classes) + * [Prefer final classes](#prefer-final-classes) 7. [SOLID](#solid) * [Single Responsibility Principle (SRP)](#single-responsibility-principle-srp) * [Open/Closed Principle (OCP)](#openclosed-principle-ocp) From 7a37fdf88a621101df853c03c2e21443b56ab93e Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Thu, 11 Jul 2019 19:10:32 +0300 Subject: [PATCH 180/210] remove space character at end of lines --- README.md | 108 +++++++++++++++++++++++++++--------------------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index e4b1ca02..d5bb9a6c 100644 --- a/README.md +++ b/README.md @@ -54,8 +54,8 @@ Software engineering principles, from Robert C. Martin's book adapted for PHP. This is not a style guide. It's a guide to producing readable, reusable, and refactorable software in PHP. -Not every principle herein has to be strictly followed, and even fewer will be universally -agreed upon. These are guidelines and nothing more, but they are ones codified over many +Not every principle herein has to be strictly followed, and even fewer will be universally +agreed upon. These are guidelines and nothing more, but they are ones codified over many years of collective experience by the authors of *Clean Code*. Inspired from [clean-code-javascript](https://github.com/ryanmcdermott/clean-code-javascript). @@ -101,8 +101,8 @@ getUser(); ### Use searchable names (part 1) -We will read more code than we will ever write. It's important that the code we do write is -readable and searchable. By *not* naming variables that end up being meaningful for +We will read more code than we will ever write. It's important that the code we do write is +readable and searchable. By *not* naming variables that end up being meaningful for understanding our program, we hurt our readers. Make your names searchable. @@ -431,13 +431,13 @@ The comparison `$a !== $b` returns `TRUE`. ### Function arguments (2 or fewer ideally) -Limiting the amount of function parameters is incredibly important because it makes -testing your function easier. Having more than three leads to a combinatorial explosion +Limiting the amount of function parameters is incredibly important because it makes +testing your function easier. Having more than three leads to a combinatorial explosion where you have to test tons of different cases with each separate argument. -Zero arguments is the ideal case. One or two arguments is ok, and three should be avoided. -Anything more than that should be consolidated. Usually, if you have more than two -arguments then your function is trying to do too much. In cases where it's not, most +Zero arguments is the ideal case. One or two arguments is ok, and three should be avoided. +Anything more than that should be consolidated. Usually, if you have more than two +arguments then your function is trying to do too much. In cases where it's not, most of the time a higher-level object will suffice as an argument. **Bad:** @@ -476,10 +476,10 @@ function createMenu(MenuConfig $config): void ### Functions should do one thing -This is by far the most important rule in software engineering. When functions do more -than one thing, they are harder to compose, test, and reason about. When you can isolate -a function to just one action, they can be refactored easily and your code will read much -cleaner. If you take nothing else away from this guide other than this, you'll be ahead +This is by far the most important rule in software engineering. When functions do more +than one thing, they are harder to compose, test, and reason about. When you can isolate +a function to just one action, they can be refactored easily and your code will read much +cleaner. If you take nothing else away from this guide other than this, you'll be ahead of many developers. **Bad:** @@ -542,7 +542,7 @@ $message->handle(); **Good:** ```php -class Email +class Email { //... @@ -699,8 +699,8 @@ class BetterJSAlternative ### Don't use flags as function parameters -Flags tell your user that this function does more than one thing. Functions should -do one thing. Split out your functions if they are following different code paths +Flags tell your user that this function does more than one thing. Functions should +do one thing. Split out your functions if they are following different code paths based on a boolean. **Bad:** @@ -734,18 +734,18 @@ function createTempFile(string $name): void ### Avoid Side Effects -A function produces a side effect if it does anything other than take a value in and -return another value or values. A side effect could be writing to a file, modifying +A function produces a side effect if it does anything other than take a value in and +return another value or values. A side effect could be writing to a file, modifying some global variable, or accidentally wiring all your money to a stranger. -Now, you do need to have side effects in a program on occasion. Like the previous -example, you might need to write to a file. What you want to do is to centralize where -you are doing this. Don't have several functions and classes that write to a particular +Now, you do need to have side effects in a program on occasion. Like the previous +example, you might need to write to a file. What you want to do is to centralize where +you are doing this. Don't have several functions and classes that write to a particular file. Have one service that does it. One and only one. The main point is to avoid common pitfalls like sharing state between objects without -any structure, using mutable data types that can be written to by anything, and not -centralizing where your side effects occur. If you can do this, you will be happier +any structure, using mutable data types that can be written to by anything, and not +centralizing where your side effects occur. If you can do this, you will be happier than the vast majority of other programmers. **Bad:** @@ -786,10 +786,10 @@ var_dump($newName); // ['Ryan', 'McDermott']; ### Don't write to global functions -Polluting globals is a bad practice in many languages because you could clash with another -library and the user of your API would be none-the-wiser until they get an exception in +Polluting globals is a bad practice in many languages because you could clash with another +library and the user of your API would be none-the-wiser until they get an exception in production. Let's think about an example: what if you wanted to have configuration array? -You could write global function like `config()`, but it could clash with another library +You could write global function like `config()`, but it could clash with another library that tried to do the same thing. **Bad:** @@ -822,7 +822,7 @@ class Configuration } ``` -Load configuration and create instance of `Configuration` class +Load configuration and create instance of `Configuration` class ```php $configuration = new Configuration([ @@ -1130,8 +1130,8 @@ inventoryTracker('apples', $request, 'www.inventory-awesome.io'); ### Use object encapsulation -In PHP you can set `public`, `protected` and `private` keywords for methods. -Using it, you can control properties modification on an object. +In PHP you can set `public`, `protected` and `private` keywords for methods. +Using it, you can control properties modification on an object. * When you want to do more beyond getting an object property, you don't have to look up and change every accessor in your codebase. @@ -1276,7 +1276,7 @@ relationship (Human->Animal vs. User->UserDetails). **Bad:** ```php -class Employee +class Employee { private $name; private $email; @@ -1290,14 +1290,14 @@ class Employee // ... } -// Bad because Employees "have" tax data. +// Bad because Employees "have" tax data. // EmployeeTaxData is not a type of Employee -class EmployeeTaxData extends Employee +class EmployeeTaxData extends Employee { private $ssn; private $salary; - + public function __construct(string $name, string $email, string $ssn, string $salary) { parent::__construct($name, $email); @@ -1313,7 +1313,7 @@ class EmployeeTaxData extends Employee **Good:** ```php -class EmployeeTaxData +class EmployeeTaxData { private $ssn; private $salary; @@ -1327,7 +1327,7 @@ class EmployeeTaxData // ... } -class Employee +class Employee { private $name; private $email; @@ -1474,16 +1474,16 @@ For more informations you can read [the blog post](https://ocramius.github.io/bl final class Car { private $color; - + public function __construct($color) { $this->color = $color; } - + /** * @return string The color of the vehicle */ - public function getColor() + public function getColor() { return $this->color; } @@ -1504,16 +1504,16 @@ interface Vehicle final class Car implements Vehicle { private $color; - + public function __construct($color) { $this->color = $color; } - + /** * {@inheritdoc} */ - public function getColor() + public function getColor() { return $this->color; } @@ -1572,7 +1572,7 @@ class UserSettings **Good:** ```php -class UserAuth +class UserAuth { private $user; @@ -1580,19 +1580,19 @@ class UserAuth { $this->user = $user; } - + public function verifyCredentials(): bool { // ... } } -class UserSettings +class UserSettings { private $user; private $auth; - public function __construct(User $user) + public function __construct(User $user) { $this->user = $user; $this->auth = new UserAuth($user); @@ -1779,7 +1779,7 @@ function printArea(Rectangle $rectangle): void { $rectangle->setWidth(4); $rectangle->setHeight(5); - + // BAD: Will return 25 for Square. Should be 20. echo sprintf('%s has area %d.', get_class($rectangle), $rectangle->getArea()).PHP_EOL; } @@ -1854,7 +1854,7 @@ foreach ($shapes as $shape) { ### Interface Segregation Principle (ISP) ISP states that "Clients should not be forced to depend upon interfaces that -they do not use." +they do not use." A good example to look at that demonstrates this principle is for classes that require large settings objects. Not requiring clients to set up @@ -2038,25 +2038,25 @@ class Manager Try to observe the [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) principle. -Do your absolute best to avoid duplicate code. Duplicate code is bad because -it means that there's more than one place to alter something if you need to +Do your absolute best to avoid duplicate code. Duplicate code is bad because +it means that there's more than one place to alter something if you need to change some logic. -Imagine if you run a restaurant and you keep track of your inventory: all your +Imagine if you run a restaurant and you keep track of your inventory: all your tomatoes, onions, garlic, spices, etc. If you have multiple lists that you keep this on, then all have to be updated when you serve a dish with tomatoes in them. If you only have one list, there's only one place to update! Often you have duplicate code because you have two or more slightly different things, that share a lot in common, but their differences force you -to have two or more separate functions that do much of the same things. Removing -duplicate code means creating an abstraction that can handle this set of different +to have two or more separate functions that do much of the same things. Removing +duplicate code means creating an abstraction that can handle this set of different things with just one function/module/class. Getting the abstraction right is critical, that's why you should follow the SOLID principles laid out in the [Classes](#classes) section. Bad abstractions can be worse than duplicate code, so be careful! Having said this, if you can make -a good abstraction, do it! Don't repeat yourself, otherwise you'll find yourself +a good abstraction, do it! Don't repeat yourself, otherwise you'll find yourself updating multiple places any time you want to change one thing. **Bad:** @@ -2157,5 +2157,5 @@ This is also available in other languages: * [yujineeee/clean-code-php](https://github.com/yujineeee/clean-code-php) * :tr: **Turkish:** * [anilozmen/clean-code-php](https://github.com/anilozmen/clean-code-php) - + **[⬆ back to top](#table-of-contents)** From f9d66a25dadbad3443e10810331309a45f52f746 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Thu, 11 Jul 2019 19:23:37 +0300 Subject: [PATCH 181/210] rename function parseBetterJSAlternative() -> parseBetterPHPAlternative() --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a3c10d87..5bd329d4 100644 --- a/README.md +++ b/README.md @@ -568,7 +568,7 @@ testing. **Bad:** ```php -function parseBetterJSAlternative(string $code): void +function parseBetterPHPAlternative(string $code): void { $regexes = [ // ... @@ -595,7 +595,7 @@ function parseBetterJSAlternative(string $code): void **Bad too:** -We have carried out some of the functionality, but the `parseBetterJSAlternative()` function is still very complex and not testable. +We have carried out some of the functionality, but the `parseBetterPHPAlternative()` function is still very complex and not testable. ```php function tokenize(string $code): array @@ -625,7 +625,7 @@ function lexer(array $tokens): array return $ast; } -function parseBetterJSAlternative(string $code): void +function parseBetterPHPAlternative(string $code): void { $tokens = tokenize($code); $ast = lexer($tokens); @@ -637,7 +637,7 @@ function parseBetterJSAlternative(string $code): void **Good:** -The best solution is move out the dependencies of `parseBetterJSAlternative()` function. +The best solution is move out the dependencies of `parseBetterPHPAlternative()` function. ```php class Tokenizer @@ -673,7 +673,7 @@ class Lexer } } -class BetterJSAlternative +class BetterPHPAlternative { private $tokenizer; private $lexer; From eb31d2d2aadb7e32a027708567c471c7e98fb46f Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Tue, 23 Jul 2019 14:01:43 +0300 Subject: [PATCH 182/210] use VO in example of "Function arguments (2 or fewer ideally)" --- README.md | 70 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index a3c10d87..82c7233f 100644 --- a/README.md +++ b/README.md @@ -443,32 +443,72 @@ of the time a higher-level object will suffice as an argument. **Bad:** ```php -function createMenu(string $title, string $body, string $buttonText, bool $cancellable): void -{ - // ... +class Questionnaire +{ + public function __construct( + string $firstname, + string $lastname, + string $patronymic, + string $region, + string $district, + string $city, + string $phone, + string $email + ) { + // ... + } } ``` **Good:** ```php -class MenuConfig +class Name { - public $title; - public $body; - public $buttonText; - public $cancellable = false; + private $firstname = ''; + private $lastname = ''; + private $patronymic = ''; + + public function __construct(string $firstname, string $lastname, string $patronymic) + { + $this->firstname = $firstname; + $this->lastname = $lastname; + $this->patronymic = $patronymic; + } } -$config = new MenuConfig(); -$config->title = 'Foo'; -$config->body = 'Bar'; -$config->buttonText = 'Baz'; -$config->cancellable = true; +class City +{ + private $region = ''; + private $district = ''; + private $city = ''; + + public function __construct(string $region, string $district, string $city) + { + $this->region = $region; + $this->district = $district; + $this->city = $city; + } +} -function createMenu(MenuConfig $config): void +class Contact { - // ... + private $phone = ''; + private $email = ''; + + public function __construct(string $phone, string $email) + { + $this->phone = $phone; + $this->email = $email; + } +} + +class Questionnaire +{ + public function __construct(Name $name, City $city, Contact $contact) + { + // ... + } } ``` From 4f11e5324e709f14868956273e30a981a4c76b66 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Tue, 23 Jul 2019 16:05:10 +0300 Subject: [PATCH 183/210] add getters comment --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 82c7233f..ed9e37d8 100644 --- a/README.md +++ b/README.md @@ -475,6 +475,8 @@ class Name $this->lastname = $lastname; $this->patronymic = $patronymic; } + + // getters ... } class City @@ -489,6 +491,8 @@ class City $this->district = $district; $this->city = $city; } + + // getters ... } class Contact @@ -501,6 +505,8 @@ class Contact $this->phone = $phone; $this->email = $email; } + + // getters ... } class Questionnaire From 5969ca97e63e7055548cb93830689cb789e829b8 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Fri, 26 Jul 2019 12:20:40 +0300 Subject: [PATCH 184/210] not set default values of string VO properties --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ed9e37d8..50219cb4 100644 --- a/README.md +++ b/README.md @@ -465,9 +465,9 @@ class Questionnaire ```php class Name { - private $firstname = ''; - private $lastname = ''; - private $patronymic = ''; + private $firstname; + private $lastname; + private $patronymic; public function __construct(string $firstname, string $lastname, string $patronymic) { @@ -481,9 +481,9 @@ class Name class City { - private $region = ''; - private $district = ''; - private $city = ''; + private $region; + private $district; + private $city; public function __construct(string $region, string $district, string $city) { @@ -497,8 +497,8 @@ class City class Contact { - private $phone = ''; - private $email = ''; + private $phone; + private $email; public function __construct(string $phone, string $email) { From 5e2d42307286c6fa29c67546dee35d60eaab7447 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Wed, 18 Sep 2019 15:07:17 +0300 Subject: [PATCH 185/210] delete "Functions should do one thing" section --- README.md | 46 ---------------------------------------------- 1 file changed, 46 deletions(-) diff --git a/README.md b/README.md index 0bd3c1f4..2da0d507 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,6 @@ * [Use identical comparison](#use-identical-comparison) 4. [Functions](#functions) * [Function arguments (2 or fewer ideally)](#function-arguments-2-or-fewer-ideally) - * [Functions should do one thing](#functions-should-do-one-thing) * [Function names should say what they do](#function-names-should-say-what-they-do) * [Functions should only be one level of abstraction](#functions-should-only-be-one-level-of-abstraction) * [Don't use flags as function parameters](#dont-use-flags-as-function-parameters) @@ -535,51 +534,6 @@ class Questionnaire **[⬆ back to top](#table-of-contents)** -### Functions should do one thing - -This is by far the most important rule in software engineering. When functions do more -than one thing, they are harder to compose, test, and reason about. When you can isolate -a function to just one action, they can be refactored easily and your code will read much -cleaner. If you take nothing else away from this guide other than this, you'll be ahead -of many developers. - -**Bad:** -```php -function emailClients(array $clients): void -{ - foreach ($clients as $client) { - $clientRecord = $db->find($client); - if ($clientRecord->isActive()) { - email($client); - } - } -} -``` - -**Good:** - -```php -function emailClients(array $clients): void -{ - $activeClients = activeClients($clients); - array_walk($activeClients, 'email'); -} - -function activeClients(array $clients): array -{ - return array_filter($clients, 'isClientActive'); -} - -function isClientActive(int $client): bool -{ - $clientRecord = $db->find($client); - - return $clientRecord->isActive(); -} -``` - -**[⬆ back to top](#table-of-contents)** - ### Function names should say what they do **Bad:** From 8f327f84a5c9d2ac79b142a48009d63b3ca099b2 Mon Sep 17 00:00:00 2001 From: bu4ak <igorbu4@gmail.com> Date: Wed, 2 Oct 2019 17:08:10 +0300 Subject: [PATCH 186/210] Add "public" to const declarations --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2da0d507..c707d324 100644 --- a/README.md +++ b/README.md @@ -143,10 +143,10 @@ $user->access ^= 2; ```php class User { - const ACCESS_READ = 1; - const ACCESS_CREATE = 2; - const ACCESS_UPDATE = 4; - const ACCESS_DELETE = 8; + public const ACCESS_READ = 1; + public const ACCESS_CREATE = 2; + public const ACCESS_UPDATE = 4; + public const ACCESS_DELETE = 8; // User as default can read, create and update something public $access = self::ACCESS_READ | self::ACCESS_CREATE | self::ACCESS_UPDATE; From 390ff76f9324c0588a01549c9a21cce7f9ff239c Mon Sep 17 00:00:00 2001 From: Jack Sheppard <Shpartko@users.noreply.github.com> Date: Wed, 11 Mar 2020 13:33:12 +0100 Subject: [PATCH 187/210] Update Prefer composition over inheritance --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d827aa24..c18d4ea2 100644 --- a/README.md +++ b/README.md @@ -1298,9 +1298,9 @@ class Employee $this->email = $email; } - public function setTaxData(string $ssn, string $salary) + public function setTaxData(EmployeeTaxData $taxData) { - $this->taxData = new EmployeeTaxData($ssn, $salary); + $this->taxData = $taxData; } // ... From 8f6c4ee1a05d2b20ae3183085a0baa0cb3d84756 Mon Sep 17 00:00:00 2001 From: Leonardo do Carmo <leonardo.docarmo@hotmail.com> Date: Thu, 9 Apr 2020 12:28:58 -0300 Subject: [PATCH 188/210] Change example to null coalescing --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c707d324..d305e140 100644 --- a/README.md +++ b/README.md @@ -814,7 +814,7 @@ function config(): array { return [ 'foo' => 'bar', - ] + ]; } ``` @@ -832,7 +832,8 @@ class Configuration public function get(string $key): ?string { - return isset($this->configuration[$key]) ? $this->configuration[$key] : null; + // null coalescing operator + return $this->configuration[$key] ?? null; } } ``` From 2a331563d46cc4fa5c81e3b87e5239e7ea8feabc Mon Sep 17 00:00:00 2001 From: Leonardo do Carmo <leonardo.docarmo@hotmail.com> Date: Thu, 9 Apr 2020 12:31:20 -0300 Subject: [PATCH 189/210] Add null coalescing section --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.md b/README.md index d305e140..b4617939 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ * [Use default arguments instead of short circuiting or conditionals](#use-default-arguments-instead-of-short-circuiting-or-conditionals) 3. [Comparison](#comparison) * [Use identical comparison](#use-identical-comparison) + * [Null coalescing operator](#null-coalescing-operator) 4. [Functions](#functions) * [Function arguments (2 or fewer ideally)](#function-arguments-2-or-fewer-ideally) * [Function names should say what they do](#function-names-should-say-what-they-do) @@ -440,6 +441,28 @@ The comparison `$a !== $b` returns `TRUE`. **[⬆ back to top](#table-of-contents)** +### Null coalescing operator + +Null coalescing is a new operator [introduced in PHP 7](https://www.php.net/manual/en/migration70.new-features.php). The null coalescing operator `??` has been added as syntactic sugar for the common case of needing to use a ternary in conjunction with `isset()`. It returns its first operand if it exists and is not `null`; otherwise it returns its second operand. + +**Bad:** + +```php +if (isset($_GET['name'])) { + $name = $_GET['name']; +} elseif (isset($_POST['name'])) { + $name = $_POST['name']; +} else { + $name = 'nobody'; +} +``` + +**Good:** +```php +$name = $_GET['name'] ?? $_POST['name'] ?? 'nobody'; +``` + +**[⬆ back to top](#table-of-contents)** ## Functions From 2c94c03dd2d6215b252acffd005e0b53c93b57d4 Mon Sep 17 00:00:00 2001 From: Tomas Votruba <tomas.vot@gmail.com> Date: Wed, 28 Oct 2020 23:22:48 +0100 Subject: [PATCH 190/210] [ci] switch travis dummy cs check, to Github Actions and ECS with markdown check --- .github/workflows/coding_standard.yaml | 20 +++++++ .gitignore | 2 + .travis-build.php | 77 -------------------------- .travis.yml | 11 ---- README.md | 2 +- composer.json | 10 ++++ ecs.php | 23 ++++++++ 7 files changed, 56 insertions(+), 89 deletions(-) create mode 100644 .github/workflows/coding_standard.yaml create mode 100644 .gitignore delete mode 100644 .travis-build.php delete mode 100644 .travis.yml create mode 100644 composer.json create mode 100644 ecs.php diff --git a/.github/workflows/coding_standard.yaml b/.github/workflows/coding_standard.yaml new file mode 100644 index 00000000..79d93b3a --- /dev/null +++ b/.github/workflows/coding_standard.yaml @@ -0,0 +1,20 @@ +name: Coding Standard + +on: + pull_request: null + +jobs: + coding_standard: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + # see https://github.com/shivammathur/setup-php + - uses: shivammathur/setup-php@v1 + with: + php-version: 7.4 + coverage: none + + - run: composer install --no-progress + + - run: vendor/bin/ecs check README.md --ansi \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..49c63d28 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +composer.lock +/vendor \ No newline at end of file diff --git a/.travis-build.php b/.travis-build.php deleted file mode 100644 index 17a63fee..00000000 --- a/.travis-build.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php - -$readMeFilepath = __DIR__ . '/README.md'; -$readMeFile = new SplFileObject($readMeFilepath); -$readMeFile->setFlags(SplFileObject::DROP_NEW_LINE); - -$cliRedBackground = "\033[37;41m"; -$cliReset = "\033[0m"; -$exitStatus = 0; - -$indentationSteps = 3; -$manIndex = 0; -$linesWithSpaces = []; -$tableOfContentsStarted = null; -$currentTableOfContentsChapters = []; -$chaptersFound = []; -foreach ($readMeFile as $lineNumber => $line) { - if (preg_match('/\s$/', $line)) { - $linesWithSpaces[] = sprintf('%5s: %s', 1 + $lineNumber, $line); - } - if (preg_match('/^(?<depth>##+)\s(?<title>.+)/', $line, $matches)) { - if (null === $tableOfContentsStarted) { - $tableOfContentsStarted = true; - continue; - } - $tableOfContentsStarted = false; - - if (strlen($matches['depth']) === 2) { - $depth = sprintf(' %s.', ++$manIndex); - } else { - $depth = sprintf(' %s*', str_repeat(' ', strlen($matches['depth']) - 1)); - } - - // ignore links in title - $matches['title'] = preg_replace('/\[([^\]]+)\]\((?:[^\)]+)\)/u', '$1', $matches['title']); - - $link = $matches['title']; - $link = strtolower($link); - $link = str_replace(' ', '-', $link); - $link = preg_replace('/[^-\w]+/u', '', $link); - - $chaptersFound[] = sprintf('%s [%s](#%s)', $depth, $matches['title'], $link); - } - if ($tableOfContentsStarted === true && isset($line[0])) { - $currentTableOfContentsChapters[] = $line; - } -} - -if (count($linesWithSpaces)) { - fwrite(STDERR, sprintf("${cliRedBackground}The following lines end with a space character:${cliReset}\n%s\n\n", - implode(PHP_EOL, $linesWithSpaces) - )); - $exitStatus = 1; -} - -$currentTableOfContentsChaptersFilename = __DIR__ . '/current-chapters'; -$chaptersFoundFilename = __DIR__ . '/chapters-found'; - -file_put_contents($currentTableOfContentsChaptersFilename, implode(PHP_EOL, $currentTableOfContentsChapters)); -file_put_contents($chaptersFoundFilename, implode(PHP_EOL, $chaptersFound)); - -$tableOfContentsDiff = shell_exec(sprintf('diff --unified %s %s', - escapeshellarg($currentTableOfContentsChaptersFilename), - escapeshellarg($chaptersFoundFilename) -)); - -@ unlink($currentTableOfContentsChaptersFilename); -@ unlink($chaptersFoundFilename); - -if (!empty($tableOfContentsDiff)) { - fwrite(STDERR, sprintf("${cliRedBackground}The table of contents is not aligned:${cliReset}\n%s\n\n", - $tableOfContentsDiff - )); - $exitStatus = 1; -} - -exit($exitStatus); diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e4ea3575..00000000 --- a/.travis.yml +++ /dev/null @@ -1,11 +0,0 @@ -language: php - -sudo: false - -php: - - nightly - -script: php .travis-build.php - -notifications: - email: false diff --git a/README.md b/README.md index 0e7551ad..a3318888 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Clean Code PHP +# Clean Code PHP ## Table of Contents diff --git a/composer.json b/composer.json new file mode 100644 index 00000000..733367b9 --- /dev/null +++ b/composer.json @@ -0,0 +1,10 @@ +{ + "require": { + "php": "^7.2", + "symplify/easy-coding-standard": "^8.3" + }, + "scripts": { + "check-cs": "vendor/bin/ecs check README.md", + "fix-cs": "vendor/bin/ecs check README.md --fix" + } +} diff --git a/ecs.php b/ecs.php new file mode 100644 index 00000000..5d564993 --- /dev/null +++ b/ecs.php @@ -0,0 +1,23 @@ +<?php + +declare(strict_types=1); + +use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Symplify\EasyCodingStandard\Configuration\Option; +use Symplify\EasyCodingStandard\ValueObject\Set\SetList; + +return static function (ContainerConfigurator $containerConfigurator): void +{ + $parameters = $containerConfigurator->parameters(); + $parameters->set(Option::PATHS, [__DIR__ . '/src', __DIR__ . '/config', __DIR__ . '/ecs.php']); + + $parameters->set(Option::SETS, [ + SetList::COMMON, + SetList::CLEAN_CODE, + SetList::DEAD_CODE, + SetList::PSR_12, + SetList::PHP_70, + SetList::PHP_71, + SetList::SYMPLIFY, + ]); +}; From d60f4a61454fcd5e3ef9bde9ac9432107d9783c8 Mon Sep 17 00:00:00 2001 From: Tomas Votruba <tomas.vot@gmail.com> Date: Wed, 28 Oct 2020 23:35:53 +0100 Subject: [PATCH 191/210] apply coding standard on README --- .github/workflows/coding_standard.yaml | 2 +- README.md | 260 +++++++++++++++++++------ composer.json | 4 +- ecs.php | 6 +- 4 files changed, 206 insertions(+), 66 deletions(-) diff --git a/.github/workflows/coding_standard.yaml b/.github/workflows/coding_standard.yaml index 79d93b3a..5e34e77c 100644 --- a/.github/workflows/coding_standard.yaml +++ b/.github/workflows/coding_standard.yaml @@ -17,4 +17,4 @@ jobs: - run: composer install --no-progress - - run: vendor/bin/ecs check README.md --ansi \ No newline at end of file + - run: vendor/bin/ecs check-markdown README.md --ansi \ No newline at end of file diff --git a/README.md b/README.md index a3318888..fee6bba4 100644 --- a/README.md +++ b/README.md @@ -69,12 +69,16 @@ Although many developers still use PHP 5, most of the examples in this article o **Bad:** ```php +declare(strict_types=1); + $ymdstr = $moment->format('y-m-d'); ``` **Good:** ```php +declare(strict_types=1); + $currentDate = $moment->format('y-m-d'); ``` @@ -85,6 +89,8 @@ $currentDate = $moment->format('y-m-d'); **Bad:** ```php +declare(strict_types=1); + getUserInfo(); getUserData(); getUserRecord(); @@ -94,6 +100,8 @@ getUserProfile(); **Good:** ```php +declare(strict_types=1); + getUser(); ``` @@ -109,6 +117,8 @@ Make your names searchable. **Bad:** ```php +declare(strict_types=1); + // What the heck is 448 for? $result = $serializer->serialize($data, 448); ``` @@ -116,6 +126,8 @@ $result = $serializer->serialize($data, 448); **Good:** ```php +declare(strict_types=1); + $json = $serializer->serialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); ``` @@ -124,6 +136,8 @@ $json = $serializer->serialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT **Bad:** ```php +declare(strict_types=1); + class User { // What the heck is 7 for? @@ -142,11 +156,16 @@ $user->access ^= 2; **Good:** ```php +declare(strict_types=1); + class User { public const ACCESS_READ = 1; + public const ACCESS_CREATE = 2; + public const ACCESS_UPDATE = 4; + public const ACCESS_DELETE = 8; // User as default can read, create and update something @@ -168,6 +187,8 @@ $user->access ^= User::ACCESS_CREATE; **Bad:** ```php +declare(strict_types=1); + $address = 'One Infinite Loop, Cupertino 95014'; $cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/'; preg_match($cityZipCodeRegex, $address, $matches); @@ -180,6 +201,8 @@ saveCityZipCode($matches[1], $matches[2]); It's better, but we are still heavily dependent on regex. ```php +declare(strict_types=1); + $address = 'One Infinite Loop, Cupertino 95014'; $cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/'; preg_match($cityZipCodeRegex, $address, $matches); @@ -193,6 +216,8 @@ saveCityZipCode($city, $zipCode); Decrease dependence on regex by naming subpatterns. ```php +declare(strict_types=1); + $address = 'One Infinite Loop, Cupertino 95014'; $cityZipCodeRegex = '/^[^,]+,\s*(?<city>.+?)\s*(?<zipCode>\d{5})$/'; preg_match($cityZipCodeRegex, $address, $matches); @@ -210,6 +235,8 @@ than implicit. **Bad:** ```php +declare(strict_types=1); + function isShopOpen($day): bool { if ($day) { @@ -221,30 +248,27 @@ function isShopOpen($day): bool return true; } elseif ($day === 'sunday') { return true; - } else { - return false; } - } else { return false; } - } else { return false; } + return false; } ``` **Good:** ```php +declare(strict_types=1); + function isShopOpen(string $day): bool { if (empty($day)) { return false; } - $openingDays = [ - 'friday', 'saturday', 'sunday' - ]; + $openingDays = ['friday', 'saturday', 'sunday']; return in_array(strtolower($day), $openingDays, true); } @@ -257,27 +281,28 @@ function isShopOpen(string $day): bool **Bad:** ```php +declare(strict_types=1); + function fibonacci(int $n) { if ($n < 50) { if ($n !== 0) { if ($n !== 1) { return fibonacci($n - 1) + fibonacci($n - 2); - } else { - return 1; } - } else { - return 0; + return 1; } - } else { - return 'Not supported'; + return 0; } + return 'Not supported'; } ``` **Good:** ```php +declare(strict_types=1); + function fibonacci(int $n): int { if ($n === 0 || $n === 1) { @@ -285,7 +310,7 @@ function fibonacci(int $n): int } if ($n >= 50) { - throw new \Exception('Not supported'); + throw new Exception('Not supported'); } return fibonacci($n - 1) + fibonacci($n - 2); @@ -319,6 +344,8 @@ for ($i = 0; $i < count($l); $i++) { **Good:** ```php +declare(strict_types=1); + $locations = ['Austin', 'New York', 'San Francisco']; foreach ($locations as $location) { @@ -341,10 +368,14 @@ variable name. **Bad:** ```php +declare(strict_types=1); + class Car { public $carMake; + public $carModel; + public $carColor; //... @@ -354,10 +385,14 @@ class Car **Good:** ```php +declare(strict_types=1); + class Car { public $make; + public $model; + public $color; //... @@ -413,11 +448,13 @@ function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void The simple comparison will convert the string in an integer. ```php +declare(strict_types=1); + $a = '42'; $b = 42; -if ($a != $b) { - // The expression will always pass +if ($a !== $b) { + // The expression will always pass } ``` @@ -429,6 +466,8 @@ The string `42` is different than the integer `42`. The identical comparison will compare type and value. ```php +declare(strict_types=1); + $a = '42'; $b = 42; @@ -448,6 +487,8 @@ Null coalescing is a new operator [introduced in PHP 7](https://www.php.net/manu **Bad:** ```php +declare(strict_types=1); + if (isset($_GET['name'])) { $name = $_GET['name']; } elseif (isset($_POST['name'])) { @@ -459,6 +500,8 @@ if (isset($_GET['name'])) { **Good:** ```php +declare(strict_types=1); + $name = $_GET['name'] ?? $_POST['name'] ?? 'nobody'; ``` @@ -480,6 +523,8 @@ of the time a higher-level object will suffice as an argument. **Bad:** ```php +declare(strict_types=1); + class Questionnaire { public function __construct( @@ -500,10 +545,14 @@ class Questionnaire **Good:** ```php +declare(strict_types=1); + class Name { private $firstname; + private $lastname; + private $patronymic; public function __construct(string $firstname, string $lastname, string $patronymic) @@ -519,7 +568,9 @@ class Name class City { private $region; + private $district; + private $city; public function __construct(string $region, string $district, string $city) @@ -535,6 +586,7 @@ class City class Contact { private $phone; + private $email; public function __construct(string $phone, string $email) @@ -606,6 +658,8 @@ testing. **Bad:** ```php +declare(strict_types=1); + function parseBetterPHPAlternative(string $code): void { $regexes = [ @@ -744,10 +798,12 @@ based on a boolean. **Bad:** ```php +declare(strict_types=1); + function createFile(string $name, bool $temp = false): void { if ($temp) { - touch('./temp/'.$name); + touch('./temp/' . $name); } else { touch($name); } @@ -757,6 +813,8 @@ function createFile(string $name, bool $temp = false): void **Good:** ```php +declare(strict_types=1); + function createFile(string $name): void { touch($name); @@ -764,7 +822,7 @@ function createFile(string $name): void function createTempFile(string $name): void { - touch('./temp/'.$name); + touch('./temp/' . $name); } ``` @@ -789,6 +847,8 @@ than the vast majority of other programmers. **Bad:** ```php +declare(strict_types=1); + // Global variable referenced by following function. // If we had another function that used this name, now it'd be an array and it could break it. $name = 'Ryan McDermott'; @@ -802,12 +862,15 @@ function splitIntoFirstAndLastName(): void splitIntoFirstAndLastName(); -var_dump($name); // ['Ryan', 'McDermott']; +// ['Ryan', 'McDermott']; +var_dump($name); ``` **Good:** ```php +declare(strict_types=1); + function splitIntoFirstAndLastName(string $name): array { return explode(' ', $name); @@ -816,8 +879,10 @@ function splitIntoFirstAndLastName(string $name): array $name = 'Ryan McDermott'; $newName = splitIntoFirstAndLastName($name); -var_dump($name); // 'Ryan McDermott'; -var_dump($newName); // ['Ryan', 'McDermott']; +// 'Ryan McDermott'; +var_dump($name); +// ['Ryan', 'McDermott']; +var_dump($newName); ``` **[⬆ back to top](#table-of-contents)** @@ -833,9 +898,11 @@ that tried to do the same thing. **Bad:** ```php +declare(strict_types=1); + function config(): array { - return [ + return [ 'foo' => 'bar', ]; } @@ -844,6 +911,8 @@ function config(): array **Good:** ```php +declare(strict_types=1); + class Configuration { private $configuration = []; @@ -855,7 +924,7 @@ class Configuration public function get(string $key): ?string { - // null coalescing operator + // null coalescing operator return $this->configuration[$key] ?? null; } } @@ -864,6 +933,8 @@ class Configuration Load configuration and create instance of `Configuration` class ```php +declare(strict_types=1); + $configuration = new Configuration([ 'foo' => 'bar', ]); @@ -886,6 +957,8 @@ There is also very good thoughts by [Misko Hevery](http://misko.hevery.com/about **Bad:** ```php +declare(strict_types=1); + class DBConnection { private static $instance; @@ -895,7 +968,7 @@ class DBConnection // ... } - public static function getInstance(): DBConnection + public static function getInstance(): self { if (self::$instance === null) { self::$instance = new self(); @@ -913,6 +986,8 @@ $singleton = DBConnection::getInstance(); **Good:** ```php +declare(strict_types=1); + class DBConnection { public function __construct(string $dsn) @@ -920,13 +995,15 @@ class DBConnection // ... } - // ... + // ... } ``` Create instance of `DBConnection` class and configure it with [DSN](http://php.net/manual/en/pdo.construct.php#refsect1-pdo.construct-parameters). ```php +declare(strict_types=1); + $connection = new DBConnection($dsn); ``` @@ -939,6 +1016,8 @@ And now you must use instance of `DBConnection` in your application. **Bad:** ```php +declare(strict_types=1); + if ($article->state === 'published') { // ... } @@ -947,6 +1026,8 @@ if ($article->state === 'published') { **Good:** ```php +declare(strict_types=1); + if ($article->isPublished()) { // ... } @@ -959,13 +1040,14 @@ if ($article->isPublished()) { **Bad:** ```php -function isDOMNodeNotPresent(\DOMNode $node): bool +declare(strict_types=1); + +function isDOMNodeNotPresent(DOMNode $node): bool { // ... } -if (!isDOMNodeNotPresent($node)) -{ +if (! isDOMNodeNotPresent($node)) { // ... } ``` @@ -973,7 +1055,9 @@ if (!isDOMNodeNotPresent($node)) **Good:** ```php -function isDOMNodePresent(\DOMNode $node): bool +declare(strict_types=1); + +function isDOMNodePresent(DOMNode $node): bool { // ... } @@ -999,6 +1083,8 @@ just do one thing. **Bad:** ```php +declare(strict_types=1); + class Airplane { // ... @@ -1020,6 +1106,8 @@ class Airplane **Good:** ```php +declare(strict_types=1); + interface Airplane { // ... @@ -1070,6 +1158,8 @@ The first thing to consider is consistent APIs. **Bad:** ```php +declare(strict_types=1); + function travelToTexas($vehicle): void { if ($vehicle instanceof Bicycle) { @@ -1083,6 +1173,8 @@ function travelToTexas($vehicle): void **Good:** ```php +declare(strict_types=1); + function travelToTexas(Vehicle $vehicle): void { $vehicle->travelTo(new Location('texas')); @@ -1106,10 +1198,12 @@ Otherwise, do all of that but with PHP strict type declaration or strict mode. **Bad:** ```php +declare(strict_types=1); + function combine($val1, $val2): int { - if (!is_numeric($val1) || !is_numeric($val2)) { - throw new \Exception('Must be of type Number'); + if (! is_numeric($val1) || ! is_numeric($val2)) { + throw new Exception('Must be of type Number'); } return $val1 + $val2; @@ -1119,6 +1213,8 @@ function combine($val1, $val2): int **Good:** ```php +declare(strict_types=1); + function combine(int $val1, int $val2): int { return $val1 + $val2; @@ -1136,6 +1232,8 @@ in your version history if you still need it. **Bad:** ```php +declare(strict_types=1); + function oldRequestModule(string $url): void { // ... @@ -1153,6 +1251,8 @@ inventoryTracker('apples', $request, 'www.inventory-awesome.io'); **Good:** ```php +declare(strict_types=1); + function requestModule(string $url): void { // ... @@ -1186,6 +1286,8 @@ Additionally, this is part of [Open/Closed](#openclosed-principle-ocp) principle **Bad:** ```php +declare(strict_types=1); + class BankAccount { public $balance = 1000; @@ -1253,6 +1355,8 @@ For more informations you can read the [blog post](http://fabien.potencier.org/p **Bad:** ```php +declare(strict_types=1); + class Employee { public $name; @@ -1264,12 +1368,15 @@ class Employee } $employee = new Employee('John Doe'); -echo 'Employee name: '.$employee->name; // Employee name: John Doe +// Employee name: John Doe +echo 'Employee name: ' . $employee->name; ``` **Good:** ```php +declare(strict_types=1); + class Employee { private $name; @@ -1286,7 +1393,8 @@ class Employee } $employee = new Employee('John Doe'); -echo 'Employee name: '.$employee->getName(); // Employee name: John Doe +// Employee name: John Doe +echo 'Employee name: ' . $employee->getName(); ``` **[⬆ back to top](#table-of-contents)** @@ -1315,9 +1423,12 @@ relationship (Human->Animal vs. User->UserDetails). **Bad:** ```php +declare(strict_types=1); + class Employee { private $name; + private $email; public function __construct(string $name, string $email) @@ -1335,6 +1446,7 @@ class Employee class EmployeeTaxData extends Employee { private $ssn; + private $salary; public function __construct(string $name, string $email, string $ssn, string $salary) @@ -1352,9 +1464,12 @@ class EmployeeTaxData extends Employee **Good:** ```php +declare(strict_types=1); + class EmployeeTaxData { private $ssn; + private $salary; public function __construct(string $ssn, string $salary) @@ -1369,7 +1484,9 @@ class EmployeeTaxData class Employee { private $name; + private $email; + private $taxData; public function __construct(string $name, string $email) @@ -1378,7 +1495,7 @@ class Employee $this->email = $email; } - public function setTaxData(EmployeeTaxData $taxData) + public function setTaxData(EmployeeTaxData $taxData): void { $this->taxData = $taxData; } @@ -1411,10 +1528,14 @@ on this topic written by [Marco Pivetta](https://github.com/Ocramius). **Bad:** ```php +declare(strict_types=1); + class Car { private $make = 'Honda'; + private $model = 'Accord'; + private $color = 'white'; public function setMake(string $make): self @@ -1448,19 +1569,23 @@ class Car } $car = (new Car()) - ->setColor('pink') - ->setMake('Ford') - ->setModel('F-150') - ->dump(); + ->setColor('pink') + ->setMake('Ford') + ->setModel('F-150') + ->dump(); ``` **Good:** ```php +declare(strict_types=1); + class Car { private $make = 'Honda'; + private $model = 'Accord'; + private $color = 'white'; public function setMake(string $make): void @@ -1510,6 +1635,8 @@ For more informations you can read [the blog post](https://ocramius.github.io/bl **Bad:** ```php +declare(strict_types=1); + final class Car { private $color; @@ -1532,6 +1659,8 @@ final class Car **Good:** ```php +declare(strict_types=1); + interface Vehicle { /** @@ -1549,9 +1678,6 @@ final class Car implements Vehicle $this->color = $color; } - /** - * {@inheritdoc} - */ public function getColor() { return $this->color; @@ -1585,6 +1711,8 @@ your codebase. **Bad:** ```php +declare(strict_types=1); + class UserSettings { private $user; @@ -1611,6 +1739,8 @@ class UserSettings **Good:** ```php +declare(strict_types=1); + class UserAuth { private $user; @@ -1629,6 +1759,7 @@ class UserAuth class UserSettings { private $user; + private $auth; public function __construct(User $user) @@ -1658,6 +1789,8 @@ add new functionalities without changing existing code. **Bad:** ```php +declare(strict_types=1); + abstract class Adapter { protected $name; @@ -1723,6 +1856,8 @@ class HttpRequester **Good:** ```php +declare(strict_types=1); + interface Adapter { public function request(string $url): Promise; @@ -1780,9 +1915,12 @@ get into trouble. **Bad:** ```php +declare(strict_types=1); + class Rectangle { protected $width = 0; + protected $height = 0; public function setWidth(int $width): void @@ -1820,7 +1958,7 @@ function printArea(Rectangle $rectangle): void $rectangle->setHeight(5); // BAD: Will return 25 for Square. Should be 20. - echo sprintf('%s has area %d.', get_class($rectangle), $rectangle->getArea()).PHP_EOL; + echo sprintf('%s has area %d.', get_class($rectangle), $rectangle->getArea()) . PHP_EOL; } $rectangles = [new Rectangle(), new Square()]; @@ -1903,6 +2041,8 @@ all of the settings. Making them optional helps prevent having a "fat interface" **Bad:** ```php +declare(strict_types=1); + interface Employee { public function work(): void; @@ -1942,6 +2082,8 @@ class RobotEmployee implements Employee Not every worker is an employee, but every employee is a worker. ```php +declare(strict_types=1); + interface Workable { public function work(): void; @@ -1999,6 +2141,8 @@ it makes your code hard to refactor. **Bad:** ```php +declare(strict_types=1); + class Employee { public function work(): void @@ -2034,6 +2178,8 @@ class Manager **Good:** ```php +declare(strict_types=1); + interface Employee { public function work(): void; @@ -2101,17 +2247,15 @@ updating multiple places any time you want to change one thing. **Bad:** ```php +declare(strict_types=1); + function showDeveloperList(array $developers): void { foreach ($developers as $developer) { $expectedSalary = $developer->calculateExpectedSalary(); $experience = $developer->getExperience(); $githubLink = $developer->getGithubLink(); - $data = [ - $expectedSalary, - $experience, - $githubLink - ]; + $data = [$expectedSalary, $experience, $githubLink]; render($data); } @@ -2123,11 +2267,7 @@ function showManagerList(array $managers): void $expectedSalary = $manager->calculateExpectedSalary(); $experience = $manager->getExperience(); $githubLink = $manager->getGithubLink(); - $data = [ - $expectedSalary, - $experience, - $githubLink - ]; + $data = [$expectedSalary, $experience, $githubLink]; render($data); } @@ -2137,17 +2277,15 @@ function showManagerList(array $managers): void **Good:** ```php +declare(strict_types=1); + function showList(array $employees): void { foreach ($employees as $employee) { $expectedSalary = $employee->calculateExpectedSalary(); $experience = $employee->getExperience(); $githubLink = $employee->getGithubLink(); - $data = [ - $expectedSalary, - $experience, - $githubLink - ]; + $data = [$expectedSalary, $experience, $githubLink]; render($data); } @@ -2159,14 +2297,12 @@ function showList(array $employees): void It is better to use a compact version of the code. ```php +declare(strict_types=1); + function showList(array $employees): void { foreach ($employees as $employee) { - render([ - $employee->calculateExpectedSalary(), - $employee->getExperience(), - $employee->getGithubLink() - ]); + render([$employee->calculateExpectedSalary(), $employee->getExperience(), $employee->getGithubLink()]); } } ``` diff --git a/composer.json b/composer.json index 733367b9..a9d75033 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "symplify/easy-coding-standard": "^8.3" }, "scripts": { - "check-cs": "vendor/bin/ecs check README.md", - "fix-cs": "vendor/bin/ecs check README.md --fix" + "check-cs": "vendor/bin/ecs check-markdown README.md", + "fix-cs": "vendor/bin/ecs check-markdown README.md --fix" } } diff --git a/ecs.php b/ecs.php index 5d564993..82c2f242 100644 --- a/ecs.php +++ b/ecs.php @@ -3,7 +3,7 @@ declare(strict_types=1); use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\EasyCodingStandard\Configuration\Option; +use Symplify\EasyCodingStandard\ValueObject\Option; use Symplify\EasyCodingStandard\ValueObject\Set\SetList; return static function (ContainerConfigurator $containerConfigurator): void @@ -20,4 +20,8 @@ SetList::PHP_71, SetList::SYMPLIFY, ]); + + $parameters->set(Option::SKIP, [ + \PhpCsFixer\Fixer\PhpTag\BlankLineAfterOpeningTagFixer::class => null, + ]); }; From 9df6c175c9c65255451f11983ab1e39c28319429 Mon Sep 17 00:00:00 2001 From: Tomas Votruba <tomas.vot@gmail.com> Date: Wed, 28 Oct 2020 23:47:53 +0100 Subject: [PATCH 192/210] workflow: update php --- .github/workflows/coding_standard.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coding_standard.yaml b/.github/workflows/coding_standard.yaml index 5e34e77c..4e9ee45c 100644 --- a/.github/workflows/coding_standard.yaml +++ b/.github/workflows/coding_standard.yaml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@v2 # see https://github.com/shivammathur/setup-php - - uses: shivammathur/setup-php@v1 + - uses: shivammathur/setup-php@v2 with: php-version: 7.4 coverage: none From 43dfecd7d6ae7f65a1e655a0d6005f2120dd811c Mon Sep 17 00:00:00 2001 From: Tomas Votruba <tomas.vot@gmail.com> Date: Wed, 28 Oct 2020 23:50:49 +0100 Subject: [PATCH 193/210] restore required mallforms --- .github/workflows/coding_standard.yaml | 2 +- README.md | 9 +++++---- ecs.php | 5 ++++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/coding_standard.yaml b/.github/workflows/coding_standard.yaml index 4e9ee45c..fa949ec7 100644 --- a/.github/workflows/coding_standard.yaml +++ b/.github/workflows/coding_standard.yaml @@ -15,6 +15,6 @@ jobs: php-version: 7.4 coverage: none - - run: composer install --no-progress + - run: composer install --no-progress --ansi - run: vendor/bin/ecs check-markdown README.md --ansi \ No newline at end of file diff --git a/README.md b/README.md index fee6bba4..3fd40d93 100644 --- a/README.md +++ b/README.md @@ -453,7 +453,7 @@ declare(strict_types=1); $a = '42'; $b = 42; -if ($a !== $b) { +if ($a != $b) { // The expression will always pass } ``` @@ -862,8 +862,8 @@ function splitIntoFirstAndLastName(): void splitIntoFirstAndLastName(); -// ['Ryan', 'McDermott']; var_dump($name); +// ['Ryan', 'McDermott']; ``` **Good:** @@ -879,10 +879,11 @@ function splitIntoFirstAndLastName(string $name): array $name = 'Ryan McDermott'; $newName = splitIntoFirstAndLastName($name); -// 'Ryan McDermott'; var_dump($name); -// ['Ryan', 'McDermott']; +// 'Ryan McDermott'; + var_dump($newName); +// ['Ryan', 'McDermott']; ``` **[⬆ back to top](#table-of-contents)** diff --git a/ecs.php b/ecs.php index 82c2f242..9555953f 100644 --- a/ecs.php +++ b/ecs.php @@ -2,6 +2,8 @@ declare(strict_types=1); +use PhpCsFixer\Fixer\PhpTag\BlankLineAfterOpeningTagFixer; +use PhpCsFixer\Fixer\Strict\StrictComparisonFixer; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symplify\EasyCodingStandard\ValueObject\Option; use Symplify\EasyCodingStandard\ValueObject\Set\SetList; @@ -22,6 +24,7 @@ ]); $parameters->set(Option::SKIP, [ - \PhpCsFixer\Fixer\PhpTag\BlankLineAfterOpeningTagFixer::class => null, + BlankLineAfterOpeningTagFixer::class => null, + StrictComparisonFixer::class => null, ]); }; From c4bdcb6a5e49085f4f221aaf2fb8cba05c688ba9 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <info@peter-gribanov.ru> Date: Fri, 15 Jan 2021 20:21:34 +0300 Subject: [PATCH 194/210] add link to persian translation --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 3fd40d93..7c9dff0c 100644 --- a/README.md +++ b/README.md @@ -2333,5 +2333,7 @@ This is also available in other languages: * [yujineeee/clean-code-php](https://github.com/yujineeee/clean-code-php) * :tr: **Turkish:** * [anilozmen/clean-code-php](https://github.com/anilozmen/clean-code-php) +* :iran: **Persian:** + * [amirshnll/clean-code-php](https://github.com/amirshnll/clean-code-php) **[⬆ back to top](#table-of-contents)** From 3218b7e11740bd7e117f8dece898f71983a39c47 Mon Sep 17 00:00:00 2001 From: diegoe <diegoe@gnome.org> Date: Sat, 6 Feb 2021 03:32:54 -0500 Subject: [PATCH 195/210] Misc spelling and grammar touch ups --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 3fd40d93..3ee84876 100644 --- a/README.md +++ b/README.md @@ -1351,7 +1351,7 @@ $balance = $bankAccount->getBalance(); Therefore, use `private` by default and `public/protected` when you need to provide access for external classes. -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). +For more information 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). **Bad:** @@ -1523,7 +1523,7 @@ more often it comes at some costs: 3. Is harder to [mock](https://en.wikipedia.org/wiki/Mock_object) in a test suite. 4. Makes diffs of commits harder to read. -For more informations you can read the full [blog post](https://ocramius.github.io/blog/fluent-interfaces-are-evil/) +For more information you can read the full [blog post](https://ocramius.github.io/blog/fluent-interfaces-are-evil/) on this topic written by [Marco Pivetta](https://github.com/Ocramius). **Bad:** @@ -1621,13 +1621,13 @@ $car->dump(); ### Prefer final classes -The `final` should be used whenever possible: +The `final` keyword should be used whenever possible: -1. It prevents uncontrolled inheritance chain. +1. It prevents an uncontrolled inheritance chain. 2. It encourages [composition](#prefer-composition-over-inheritance). 3. It encourages the [Single Responsibility Pattern](#single-responsibility-principle-srp). -4. It encourages developers to use your public methods instead of extending the class to get access on protected ones. -5. It allows you to change your code without any break of applications that use your class. +4. It encourages developers to use your public methods instead of extending the class to get access to protected ones. +5. It allows you to change your code without breaking applications that use your class. The only condition is that your class should implement an interface and no other public methods are defined. @@ -1974,7 +1974,7 @@ foreach ($rectangles as $rectangle) { The best way is separate the quadrangles and allocation of a more general subtype for both shapes. Despite the apparent similarity of the square and the rectangle, they are different. -A square has much in common with a rhombus, and a rectangle with a parallelogram, but they are not subtype. +A square has much in common with a rhombus, and a rectangle with a parallelogram, but they are not subtypes. A square, a rectangle, a rhombus and a parallelogram are separate shapes with their own properties, albeit similar. ```php From 0d478312fdae6045de6c17474bf5b0cf7ff88581 Mon Sep 17 00:00:00 2001 From: andyexeter <palmer.andy@gmail.com> Date: Thu, 15 Apr 2021 10:04:57 +0100 Subject: [PATCH 196/210] Remove declare strict_types from code snippets --- README.md | 136 ------------------------------------------------------ ecs.php | 2 + 2 files changed, 2 insertions(+), 136 deletions(-) diff --git a/README.md b/README.md index 3fd40d93..bf1beccc 100644 --- a/README.md +++ b/README.md @@ -69,16 +69,12 @@ Although many developers still use PHP 5, most of the examples in this article o **Bad:** ```php -declare(strict_types=1); - $ymdstr = $moment->format('y-m-d'); ``` **Good:** ```php -declare(strict_types=1); - $currentDate = $moment->format('y-m-d'); ``` @@ -89,8 +85,6 @@ $currentDate = $moment->format('y-m-d'); **Bad:** ```php -declare(strict_types=1); - getUserInfo(); getUserData(); getUserRecord(); @@ -100,8 +94,6 @@ getUserProfile(); **Good:** ```php -declare(strict_types=1); - getUser(); ``` @@ -117,8 +109,6 @@ Make your names searchable. **Bad:** ```php -declare(strict_types=1); - // What the heck is 448 for? $result = $serializer->serialize($data, 448); ``` @@ -126,8 +116,6 @@ $result = $serializer->serialize($data, 448); **Good:** ```php -declare(strict_types=1); - $json = $serializer->serialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); ``` @@ -136,8 +124,6 @@ $json = $serializer->serialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT **Bad:** ```php -declare(strict_types=1); - class User { // What the heck is 7 for? @@ -156,8 +142,6 @@ $user->access ^= 2; **Good:** ```php -declare(strict_types=1); - class User { public const ACCESS_READ = 1; @@ -187,8 +171,6 @@ $user->access ^= User::ACCESS_CREATE; **Bad:** ```php -declare(strict_types=1); - $address = 'One Infinite Loop, Cupertino 95014'; $cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/'; preg_match($cityZipCodeRegex, $address, $matches); @@ -201,8 +183,6 @@ saveCityZipCode($matches[1], $matches[2]); It's better, but we are still heavily dependent on regex. ```php -declare(strict_types=1); - $address = 'One Infinite Loop, Cupertino 95014'; $cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/'; preg_match($cityZipCodeRegex, $address, $matches); @@ -216,8 +196,6 @@ saveCityZipCode($city, $zipCode); Decrease dependence on regex by naming subpatterns. ```php -declare(strict_types=1); - $address = 'One Infinite Loop, Cupertino 95014'; $cityZipCodeRegex = '/^[^,]+,\s*(?<city>.+?)\s*(?<zipCode>\d{5})$/'; preg_match($cityZipCodeRegex, $address, $matches); @@ -235,8 +213,6 @@ than implicit. **Bad:** ```php -declare(strict_types=1); - function isShopOpen($day): bool { if ($day) { @@ -260,8 +236,6 @@ function isShopOpen($day): bool **Good:** ```php -declare(strict_types=1); - function isShopOpen(string $day): bool { if (empty($day)) { @@ -281,8 +255,6 @@ function isShopOpen(string $day): bool **Bad:** ```php -declare(strict_types=1); - function fibonacci(int $n) { if ($n < 50) { @@ -301,8 +273,6 @@ function fibonacci(int $n) **Good:** ```php -declare(strict_types=1); - function fibonacci(int $n): int { if ($n === 0 || $n === 1) { @@ -344,8 +314,6 @@ for ($i = 0; $i < count($l); $i++) { **Good:** ```php -declare(strict_types=1); - $locations = ['Austin', 'New York', 'San Francisco']; foreach ($locations as $location) { @@ -368,8 +336,6 @@ variable name. **Bad:** ```php -declare(strict_types=1); - class Car { public $carMake; @@ -385,8 +351,6 @@ class Car **Good:** ```php -declare(strict_types=1); - class Car { public $make; @@ -448,8 +412,6 @@ function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void The simple comparison will convert the string in an integer. ```php -declare(strict_types=1); - $a = '42'; $b = 42; @@ -466,8 +428,6 @@ The string `42` is different than the integer `42`. The identical comparison will compare type and value. ```php -declare(strict_types=1); - $a = '42'; $b = 42; @@ -487,8 +447,6 @@ Null coalescing is a new operator [introduced in PHP 7](https://www.php.net/manu **Bad:** ```php -declare(strict_types=1); - if (isset($_GET['name'])) { $name = $_GET['name']; } elseif (isset($_POST['name'])) { @@ -500,8 +458,6 @@ if (isset($_GET['name'])) { **Good:** ```php -declare(strict_types=1); - $name = $_GET['name'] ?? $_POST['name'] ?? 'nobody'; ``` @@ -523,8 +479,6 @@ of the time a higher-level object will suffice as an argument. **Bad:** ```php -declare(strict_types=1); - class Questionnaire { public function __construct( @@ -545,8 +499,6 @@ class Questionnaire **Good:** ```php -declare(strict_types=1); - class Name { private $firstname; @@ -658,8 +610,6 @@ testing. **Bad:** ```php -declare(strict_types=1); - function parseBetterPHPAlternative(string $code): void { $regexes = [ @@ -798,8 +748,6 @@ based on a boolean. **Bad:** ```php -declare(strict_types=1); - function createFile(string $name, bool $temp = false): void { if ($temp) { @@ -813,8 +761,6 @@ function createFile(string $name, bool $temp = false): void **Good:** ```php -declare(strict_types=1); - function createFile(string $name): void { touch($name); @@ -847,8 +793,6 @@ than the vast majority of other programmers. **Bad:** ```php -declare(strict_types=1); - // Global variable referenced by following function. // If we had another function that used this name, now it'd be an array and it could break it. $name = 'Ryan McDermott'; @@ -869,8 +813,6 @@ var_dump($name); **Good:** ```php -declare(strict_types=1); - function splitIntoFirstAndLastName(string $name): array { return explode(' ', $name); @@ -899,8 +841,6 @@ that tried to do the same thing. **Bad:** ```php -declare(strict_types=1); - function config(): array { return [ @@ -912,8 +852,6 @@ function config(): array **Good:** ```php -declare(strict_types=1); - class Configuration { private $configuration = []; @@ -934,8 +872,6 @@ class Configuration Load configuration and create instance of `Configuration` class ```php -declare(strict_types=1); - $configuration = new Configuration([ 'foo' => 'bar', ]); @@ -958,8 +894,6 @@ There is also very good thoughts by [Misko Hevery](http://misko.hevery.com/about **Bad:** ```php -declare(strict_types=1); - class DBConnection { private static $instance; @@ -987,8 +921,6 @@ $singleton = DBConnection::getInstance(); **Good:** ```php -declare(strict_types=1); - class DBConnection { public function __construct(string $dsn) @@ -1003,8 +935,6 @@ class DBConnection Create instance of `DBConnection` class and configure it with [DSN](http://php.net/manual/en/pdo.construct.php#refsect1-pdo.construct-parameters). ```php -declare(strict_types=1); - $connection = new DBConnection($dsn); ``` @@ -1017,8 +947,6 @@ And now you must use instance of `DBConnection` in your application. **Bad:** ```php -declare(strict_types=1); - if ($article->state === 'published') { // ... } @@ -1027,8 +955,6 @@ if ($article->state === 'published') { **Good:** ```php -declare(strict_types=1); - if ($article->isPublished()) { // ... } @@ -1041,8 +967,6 @@ if ($article->isPublished()) { **Bad:** ```php -declare(strict_types=1); - function isDOMNodeNotPresent(DOMNode $node): bool { // ... @@ -1056,8 +980,6 @@ if (! isDOMNodeNotPresent($node)) { **Good:** ```php -declare(strict_types=1); - function isDOMNodePresent(DOMNode $node): bool { // ... @@ -1084,8 +1006,6 @@ just do one thing. **Bad:** ```php -declare(strict_types=1); - class Airplane { // ... @@ -1107,8 +1027,6 @@ class Airplane **Good:** ```php -declare(strict_types=1); - interface Airplane { // ... @@ -1159,8 +1077,6 @@ The first thing to consider is consistent APIs. **Bad:** ```php -declare(strict_types=1); - function travelToTexas($vehicle): void { if ($vehicle instanceof Bicycle) { @@ -1174,8 +1090,6 @@ function travelToTexas($vehicle): void **Good:** ```php -declare(strict_types=1); - function travelToTexas(Vehicle $vehicle): void { $vehicle->travelTo(new Location('texas')); @@ -1199,8 +1113,6 @@ Otherwise, do all of that but with PHP strict type declaration or strict mode. **Bad:** ```php -declare(strict_types=1); - function combine($val1, $val2): int { if (! is_numeric($val1) || ! is_numeric($val2)) { @@ -1214,8 +1126,6 @@ function combine($val1, $val2): int **Good:** ```php -declare(strict_types=1); - function combine(int $val1, int $val2): int { return $val1 + $val2; @@ -1233,8 +1143,6 @@ in your version history if you still need it. **Bad:** ```php -declare(strict_types=1); - function oldRequestModule(string $url): void { // ... @@ -1252,8 +1160,6 @@ inventoryTracker('apples', $request, 'www.inventory-awesome.io'); **Good:** ```php -declare(strict_types=1); - function requestModule(string $url): void { // ... @@ -1287,8 +1193,6 @@ Additionally, this is part of [Open/Closed](#openclosed-principle-ocp) principle **Bad:** ```php -declare(strict_types=1); - class BankAccount { public $balance = 1000; @@ -1356,8 +1260,6 @@ For more informations you can read the [blog post](http://fabien.potencier.org/p **Bad:** ```php -declare(strict_types=1); - class Employee { public $name; @@ -1376,8 +1278,6 @@ echo 'Employee name: ' . $employee->name; **Good:** ```php -declare(strict_types=1); - class Employee { private $name; @@ -1424,8 +1324,6 @@ relationship (Human->Animal vs. User->UserDetails). **Bad:** ```php -declare(strict_types=1); - class Employee { private $name; @@ -1465,8 +1363,6 @@ class EmployeeTaxData extends Employee **Good:** ```php -declare(strict_types=1); - class EmployeeTaxData { private $ssn; @@ -1529,8 +1425,6 @@ on this topic written by [Marco Pivetta](https://github.com/Ocramius). **Bad:** ```php -declare(strict_types=1); - class Car { private $make = 'Honda'; @@ -1579,8 +1473,6 @@ $car = (new Car()) **Good:** ```php -declare(strict_types=1); - class Car { private $make = 'Honda'; @@ -1636,8 +1528,6 @@ For more informations you can read [the blog post](https://ocramius.github.io/bl **Bad:** ```php -declare(strict_types=1); - final class Car { private $color; @@ -1660,8 +1550,6 @@ final class Car **Good:** ```php -declare(strict_types=1); - interface Vehicle { /** @@ -1712,8 +1600,6 @@ your codebase. **Bad:** ```php -declare(strict_types=1); - class UserSettings { private $user; @@ -1740,8 +1626,6 @@ class UserSettings **Good:** ```php -declare(strict_types=1); - class UserAuth { private $user; @@ -1790,8 +1674,6 @@ add new functionalities without changing existing code. **Bad:** ```php -declare(strict_types=1); - abstract class Adapter { protected $name; @@ -1857,8 +1739,6 @@ class HttpRequester **Good:** ```php -declare(strict_types=1); - interface Adapter { public function request(string $url): Promise; @@ -1916,8 +1796,6 @@ get into trouble. **Bad:** ```php -declare(strict_types=1); - class Rectangle { protected $width = 0; @@ -2042,8 +1920,6 @@ all of the settings. Making them optional helps prevent having a "fat interface" **Bad:** ```php -declare(strict_types=1); - interface Employee { public function work(): void; @@ -2083,8 +1959,6 @@ class RobotEmployee implements Employee Not every worker is an employee, but every employee is a worker. ```php -declare(strict_types=1); - interface Workable { public function work(): void; @@ -2142,8 +2016,6 @@ it makes your code hard to refactor. **Bad:** ```php -declare(strict_types=1); - class Employee { public function work(): void @@ -2179,8 +2051,6 @@ class Manager **Good:** ```php -declare(strict_types=1); - interface Employee { public function work(): void; @@ -2248,8 +2118,6 @@ updating multiple places any time you want to change one thing. **Bad:** ```php -declare(strict_types=1); - function showDeveloperList(array $developers): void { foreach ($developers as $developer) { @@ -2278,8 +2146,6 @@ function showManagerList(array $managers): void **Good:** ```php -declare(strict_types=1); - function showList(array $employees): void { foreach ($employees as $employee) { @@ -2298,8 +2164,6 @@ function showList(array $employees): void It is better to use a compact version of the code. ```php -declare(strict_types=1); - function showList(array $employees): void { foreach ($employees as $employee) { diff --git a/ecs.php b/ecs.php index 9555953f..9b945f6b 100644 --- a/ecs.php +++ b/ecs.php @@ -3,6 +3,7 @@ declare(strict_types=1); use PhpCsFixer\Fixer\PhpTag\BlankLineAfterOpeningTagFixer; +use PhpCsFixer\Fixer\Strict\DeclareStrictTypesFixer; use PhpCsFixer\Fixer\Strict\StrictComparisonFixer; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symplify\EasyCodingStandard\ValueObject\Option; @@ -26,5 +27,6 @@ $parameters->set(Option::SKIP, [ BlankLineAfterOpeningTagFixer::class => null, StrictComparisonFixer::class => null, + DeclareStrictTypesFixer::class => null, ]); }; From 8572f4c5ba5d930dba7d85b7007514ab45ca4df0 Mon Sep 17 00:00:00 2001 From: Peter Gribanov <wcode404@gmail.com> Date: Wed, 28 Apr 2021 15:11:17 +0300 Subject: [PATCH 197/210] move "Use default arguments instead of short circuiting or conditionals" section into "Functions" section --- README.md | 78 +++++++++++++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index bf1beccc..58a9e35c 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,11 @@ * [Avoid nesting too deeply and return early (part 2)](#avoid-nesting-too-deeply-and-return-early-part-2) * [Avoid Mental Mapping](#avoid-mental-mapping) * [Don't add unneeded context](#dont-add-unneeded-context) - * [Use default arguments instead of short circuiting or conditionals](#use-default-arguments-instead-of-short-circuiting-or-conditionals) 3. [Comparison](#comparison) * [Use identical comparison](#use-identical-comparison) * [Null coalescing operator](#null-coalescing-operator) 4. [Functions](#functions) + * [Use default arguments instead of short circuiting or conditionals](#use-default-arguments-instead-of-short-circuiting-or-conditionals) * [Function arguments (2 or fewer ideally)](#function-arguments-2-or-fewer-ideally) * [Function names should say what they do](#function-names-should-say-what-they-do) * [Functions should only be one level of abstraction](#functions-should-only-be-one-level-of-abstraction) @@ -365,44 +365,6 @@ class Car **[⬆ back to top](#table-of-contents)** -### Use default arguments instead of short circuiting or conditionals - -**Not good:** - -This is not good because `$breweryName` can be `NULL`. - -```php -function createMicrobrewery($breweryName = 'Hipster Brew Co.'): void -{ -    // ... -} -``` - -**Not bad:** - -This opinion is more understandable than the previous version, but it better controls the value of the variable. - -```php -function createMicrobrewery($name = null): void -{ -    $breweryName = $name ?: 'Hipster Brew Co.'; - // ... -} -``` - -**Good:** - - You can use [type hinting](http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration) and be sure that the `$breweryName` will not be `NULL`. - -```php -function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void -{ -    // ... -} -``` - -**[⬆ back to top](#table-of-contents)** - ## Comparison ### Use [identical comparison](http://php.net/manual/en/language.operators.comparison.php) @@ -465,6 +427,44 @@ $name = $_GET['name'] ?? $_POST['name'] ?? 'nobody'; ## Functions +### Use default arguments instead of short circuiting or conditionals + +**Not good:** + +This is not good because `$breweryName` can be `NULL`. + +```php +function createMicrobrewery($breweryName = 'Hipster Brew Co.'): void +{ + // ... +} +``` + +**Not bad:** + +This opinion is more understandable than the previous version, but it better controls the value of the variable. + +```php +function createMicrobrewery($name = null): void +{ + $breweryName = $name ?: 'Hipster Brew Co.'; + // ... +} +``` + +**Good:** + + You can use [type hinting](http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration) and be sure that the `$breweryName` will not be `NULL`. + +```php +function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void +{ + // ... +} +``` + +**[⬆ back to top](#table-of-contents)** + ### Function arguments (2 or fewer ideally) Limiting the amount of function parameters is incredibly important because it makes From b12e9867039be9cb85f0d3d2421006219ae17a4e Mon Sep 17 00:00:00 2001 From: Ilyes Chouia <celyes02@gmail.com> Date: Tue, 25 May 2021 14:12:39 +0100 Subject: [PATCH 198/210] fix typo change 'Single Responsibility Pattern' to 'Single Responsibility Principle' in `Prefer final classes` chapter --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bf1beccc..2d94352a 100644 --- a/README.md +++ b/README.md @@ -1511,13 +1511,14 @@ $car->dump(); **[⬆ back to top](#table-of-contents)** -### Prefer final classes +### Prefer +classes The `final` should be used whenever possible: 1. It prevents uncontrolled inheritance chain. 2. It encourages [composition](#prefer-composition-over-inheritance). -3. It encourages the [Single Responsibility Pattern](#single-responsibility-principle-srp). +3. It encourages the [Single Responsibility Principle](#single-responsibility-principle-srp). 4. It encourages developers to use your public methods instead of extending the class to get access on protected ones. 5. It allows you to change your code without any break of applications that use your class. From c6808b9448aebcf4a50afff1027e97e380fde5c9 Mon Sep 17 00:00:00 2001 From: Ilyes Chouia <celyes02@gmail.com> Date: Tue, 25 May 2021 14:16:05 +0100 Subject: [PATCH 199/210] fix link issue --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 2d94352a..149f49c3 100644 --- a/README.md +++ b/README.md @@ -1511,8 +1511,7 @@ $car->dump(); **[⬆ back to top](#table-of-contents)** -### Prefer -classes +### Prefer final classes The `final` should be used whenever possible: From 0374baa8a8299780f5284c44da6d23440b081f3d Mon Sep 17 00:00:00 2001 From: TomasVotruba <tomas.vot@gmail.com> Date: Wed, 26 May 2021 11:24:36 +0100 Subject: [PATCH 200/210] update ECS and composer.json, allow PHP 7.2+ --- .github/workflows/coding_standard.yaml | 4 ++-- composer.json | 6 ++++-- ecs.php | 15 +++++---------- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/.github/workflows/coding_standard.yaml b/.github/workflows/coding_standard.yaml index fa949ec7..2fa10664 100644 --- a/.github/workflows/coding_standard.yaml +++ b/.github/workflows/coding_standard.yaml @@ -12,9 +12,9 @@ jobs: # see https://github.com/shivammathur/setup-php - uses: shivammathur/setup-php@v2 with: - php-version: 7.4 + php-version: 8.0 coverage: none - run: composer install --no-progress --ansi - - run: vendor/bin/ecs check-markdown README.md --ansi \ No newline at end of file + - run: vendor/bin/ecs check-markdown README.md --ansi diff --git a/composer.json b/composer.json index a9d75033..9c258aa3 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,9 @@ { + "name": "jupeter/clean-code-php", + "description": "Clean Code concepts adapted for PHP", "require": { - "php": "^7.2", - "symplify/easy-coding-standard": "^8.3" + "php": ">=7.2", + "symplify/easy-coding-standard": "^9.3" }, "scripts": { "check-cs": "vendor/bin/ecs check-markdown README.md", diff --git a/ecs.php b/ecs.php index 9b945f6b..b2e209ef 100644 --- a/ecs.php +++ b/ecs.php @@ -11,19 +11,14 @@ return static function (ContainerConfigurator $containerConfigurator): void { + $containerConfigurator->import(SetList::COMMON); + $containerConfigurator->import(SetList::CLEAN_CODE); + $containerConfigurator->import(SetList::PSR_12); + $containerConfigurator->import(SetList::SYMPLIFY); + $parameters = $containerConfigurator->parameters(); $parameters->set(Option::PATHS, [__DIR__ . '/src', __DIR__ . '/config', __DIR__ . '/ecs.php']); - $parameters->set(Option::SETS, [ - SetList::COMMON, - SetList::CLEAN_CODE, - SetList::DEAD_CODE, - SetList::PSR_12, - SetList::PHP_70, - SetList::PHP_71, - SetList::SYMPLIFY, - ]); - $parameters->set(Option::SKIP, [ BlankLineAfterOpeningTagFixer::class => null, StrictComparisonFixer::class => null, From 9fab86d007d62dbb9fcf85c70b9c8398371099ce Mon Sep 17 00:00:00 2001 From: TomasVotruba <tomas.vot@gmail.com> Date: Wed, 26 May 2021 11:26:19 +0100 Subject: [PATCH 201/210] [CI] run on push too --- .github/workflows/coding_standard.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/coding_standard.yaml b/.github/workflows/coding_standard.yaml index 2fa10664..262b528b 100644 --- a/.github/workflows/coding_standard.yaml +++ b/.github/workflows/coding_standard.yaml @@ -2,6 +2,7 @@ name: Coding Standard on: pull_request: null + push: null jobs: coding_standard: From c829a21050e0c37f09ddaa51c0c030e7a84e746a Mon Sep 17 00:00:00 2001 From: Nayeem <nayeem9812@gmail.com> Date: Sat, 16 Oct 2021 02:58:50 +0600 Subject: [PATCH 202/210] Add link to Bengali translation --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3a6994fb..9ec2dc46 100644 --- a/README.md +++ b/README.md @@ -2198,6 +2198,8 @@ This is also available in other languages: * :tr: **Turkish:** * [anilozmen/clean-code-php](https://github.com/anilozmen/clean-code-php) * :iran: **Persian:** - * [amirshnll/clean-code-php](https://github.com/amirshnll/clean-code-php) + * [amirshnll/clean-code-php](https://github.com/amirshnll/clean-code-php) +* :bangladesh: **Bangla:** + * [nayeemdev/clean-code-php](https://github.com/nayeemdev/clean-code-php) **[⬆ back to top](#table-of-contents)** From c46dfa4816efdd510e30322f3102a036de99bb99 Mon Sep 17 00:00:00 2001 From: Igor Popov <igrppv@gmail.com> Date: Wed, 17 Nov 2021 14:27:02 +0300 Subject: [PATCH 203/210] Add colon after Vietnamese translation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9ec2dc46..030fcfc8 100644 --- a/README.md +++ b/README.md @@ -2191,7 +2191,7 @@ This is also available in other languages: * [panuwizzle/clean-code-php](https://github.com/panuwizzle/clean-code-php) * :fr: **French:** * [errorname/clean-code-php](https://github.com/errorname/clean-code-php) -* :vietnam: **Vietnamese** +* :vietnam: **Vietnamese:** * [viethuongdev/clean-code-php](https://github.com/viethuongdev/clean-code-php) * :kr: **Korean:** * [yujineeee/clean-code-php](https://github.com/yujineeee/clean-code-php) From 51ed366286b8c3cf6deea3b1226d470014a931f3 Mon Sep 17 00:00:00 2001 From: Igor Popov <igrppv@gmail.com> Date: Thu, 25 Nov 2021 23:57:37 +0300 Subject: [PATCH 204/210] Rename pattern to principle --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 030fcfc8..20b0b82f 100644 --- a/README.md +++ b/README.md @@ -1517,7 +1517,7 @@ The `final` keyword should be used whenever possible: 1. It prevents an uncontrolled inheritance chain. 2. It encourages [composition](#prefer-composition-over-inheritance). -3. It encourages the [Single Responsibility Pattern](#single-responsibility-principle-srp). +3. It encourages the [Single Responsibility Principle](#single-responsibility-principle-srp). 4. It encourages developers to use your public methods instead of extending the class to get access to protected ones. 5. It allows you to change your code without breaking applications that use your class. From 6e672f6453fd8f4cc0086a9eaa479f4efa5853f5 Mon Sep 17 00:00:00 2001 From: Ahmed Joda <ahmedjoda02@gmail.com> Date: Sat, 27 Nov 2021 19:50:46 +0200 Subject: [PATCH 205/210] add arabic translation --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 20b0b82f..2708e171 100644 --- a/README.md +++ b/README.md @@ -2201,5 +2201,7 @@ This is also available in other languages: * [amirshnll/clean-code-php](https://github.com/amirshnll/clean-code-php) * :bangladesh: **Bangla:** * [nayeemdev/clean-code-php](https://github.com/nayeemdev/clean-code-php) +* :egypt: **Arabic:** + * [ahmedjoda/clean-code-php](https://github.com/ahmedjoda/clean-code-php) **[⬆ back to top](#table-of-contents)** From cb43aa240d77418f55ece3efe9d5004d34bd4d26 Mon Sep 17 00:00:00 2001 From: Hayato Ando <0du38604360418k@gmail.com> Date: Fri, 11 Feb 2022 18:14:03 +0900 Subject: [PATCH 206/210] add japanese translation --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2708e171..e72604e1 100644 --- a/README.md +++ b/README.md @@ -2203,5 +2203,7 @@ This is also available in other languages: * [nayeemdev/clean-code-php](https://github.com/nayeemdev/clean-code-php) * :egypt: **Arabic:** * [ahmedjoda/clean-code-php](https://github.com/ahmedjoda/clean-code-php) +* :jp: **Japanese:** + * [hayato07/clean-code-php](https://github.com/hayato07/clean-code-php) **[⬆ back to top](#table-of-contents)** From 8595ff59e6ade90bb1b5457663db9c6ccce3c6d3 Mon Sep 17 00:00:00 2001 From: Igor Popov <igrppv@gmail.com> Date: Thu, 19 May 2022 01:13:51 +0300 Subject: [PATCH 207/210] change type hint url --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e72604e1..7993ede2 100644 --- a/README.md +++ b/README.md @@ -454,7 +454,7 @@ function createMicrobrewery($name = null): void **Good:** - You can use [type hinting](http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration) and be sure that the `$breweryName` will not be `NULL`. + You can use [type hinting](https://www.php.net/manual/en/language.types.declarations.php) and be sure that the `$breweryName` will not be `NULL`. ```php function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void From fe8d53bdef70926f0852bb05f4642d38b3fea854 Mon Sep 17 00:00:00 2001 From: Igor Popov <igrppv@gmail.com> Date: Thu, 19 May 2022 11:30:44 +0300 Subject: [PATCH 208/210] change type hinting url --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7993ede2..17756a9c 100644 --- a/README.md +++ b/README.md @@ -1103,7 +1103,7 @@ function travelToTexas(Vehicle $vehicle): void If you are working with basic primitive values like strings, integers, and arrays, and you use PHP 7+ and you can't use polymorphism but you still feel the need to type-check, you should consider -[type declaration](http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration) +[type declaration](https://www.php.net/manual/en/language.types.declarations.php) or strict mode. It provides you with static typing on top of standard PHP syntax. The problem with manually type-checking is that doing it will require so much extra verbiage that the faux "type-safety" you get doesn't make up for the lost From ca4ab1ff526e088ef2339ae1995f65875c2b3445 Mon Sep 17 00:00:00 2001 From: Ahmed Almory <40957053+ahmedalmory@users.noreply.github.com> Date: Thu, 16 Jun 2022 10:59:34 +0200 Subject: [PATCH 209/210] update Arabic translation username --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 17756a9c..df4a4c4c 100644 --- a/README.md +++ b/README.md @@ -2202,7 +2202,7 @@ This is also available in other languages: * :bangladesh: **Bangla:** * [nayeemdev/clean-code-php](https://github.com/nayeemdev/clean-code-php) * :egypt: **Arabic:** - * [ahmedjoda/clean-code-php](https://github.com/ahmedjoda/clean-code-php) + * [ahmedalmory/clean-code-php](https://github.com/ahmedalmory/clean-code-php) * :jp: **Japanese:** * [hayato07/clean-code-php](https://github.com/hayato07/clean-code-php) From eedf6c68bdf7990db6636b4c7489d0cef9fe93b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= <viktor@szepe.net> Date: Sun, 19 Jun 2022 20:21:22 +0000 Subject: [PATCH 210/210] Fix wording in Comparison Fixes #179 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index df4a4c4c..b5b80b0e 100644 --- a/README.md +++ b/README.md @@ -371,7 +371,7 @@ class Car **Not good:** -The simple comparison will convert the string in an integer. +The simple comparison will convert the string into an integer. ```php $a = '42';