preface

A few days ago on GitHub, I saw a translation of the simple way to write PHP. I thought it was good, so I transferred it to my blog. But there seemed to be no translation in some places, plus some small problems in typeset, so I decided to translate it myself. Original address :github.com/jupeter/cle…


variable

  • Use more meaningful and straightforward naming

Unfriendly:

$ymdstr = $moment->format('y-m-d');
Copy the code

Friendly:

$currentDate = $moment->format('y-m-d');
Copy the code
  • Use the same variable name for the same entity

Unfriendly:

getUserInfo();
getUserData();
getUserRecord();
getUserProfile();
Copy the code

Friendly:

getUser();
Copy the code
  • Use variables that can be found

We read more code than we write. Therefore, writing code that is readable and searchable is extremely important. Writing variable names that are difficult to understand in our programs can even end up being very nerve-racking. So make your name searchable.

Unfriendly:

// What does 448 mean?$result = $serializer->serialize($data, 448);
Copy the code

Friendly:

$json = $serializer->serialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | >JSON_UNESCAPED_UNICODE);
Copy the code
  • Use explanatory variables

Don’t friendly

$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*(.+?) \s*(\d{5})$/';
preg_match($cityZipCodeRegex.$address.$matches);

saveCityZipCode($matches[1].$matches[2]);
Copy the code

Not as bad as that:

A little better, but it depends on how good we are at regex.

$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*(.+?) \s*(\d{5})$/';
preg_match($cityZipCodeRegex.$address.$matches);

[, $city.$zipCode] = $matches;
saveCityZipCode($city.$zipCode);
Copy the code

Friendly:

Renaming subpatterns reduces our familiarity with and dependence on the re.

$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*(? 
       
        .+?) \s*(? 
        
         \d{5})$/'
        
       ;
preg_match($cityZipCodeRegex.$address.$matches);

saveCityZipCode($matches['city'].$matches['zipCode']);
Copy the code
  • Nested boundless, turn back

Too much if else nesting can make your code difficult to read and maintain. More straightforward code would be much better.

  1. demo1

Unfriendly:

function isShopOpen($day): bool
{
   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; }}Copy the code

Friendly:

function isShopOpen(string $day): bool
{
   if (empty($day)) {
       return false;
   }

   $openingDays = [
       'friday'.'saturday'.'sunday'
   ];

   return in_array(strtolower($day), $openingDays.true);
}
Copy the code
  1. demo2

Unfriendly:

function fibonacci(int $n)
{
   if ($n < 50) {
       if ($n! = = 0) {if ($n! = = 1) {return fibonacci($n - 1) + fibonacci($n - 2);
           } else {
               return1; }}else {
           return0; }}else {
       return 'Not supported'; }}Copy the code

Friendly:

function fibonacci(int $n): int
{
   if ($n= = = 0 | |$n= = = 1) {return $n;
   }

   if ($n > 50) {
       throw new \Exception('Not supported');
   }

   return fibonacci($n - 1) + fibonacci($n - 2);
}
Copy the code
  • Avoid unreasonable variable names

Don’t let others guess what your variable names mean. More straightforward code would be much better.

Unfriendly:

$l = ['Austin'.'New York'.'San Francisco'];

for ($i = 0; $i < count($l); $i{+ +)$li = $l[$i];
   doStuff();
   doSomeOtherStuff(); / /... / /... / /... // Wait, this one$liWhat does that mean? dispatch($li);
}
Copy the code

Friendly:

$locations = ['Austin'.'New York'.'San Francisco'];

foreach ($locations as $location) {
   doStuff();
   doSomeOtherStuff();
   // ...
   // ...
   // ...
   dispatch($location);
}
Copy the code

Don’t add unnecessary context

If the name of your class or object tells you something, don’t repeat it in the variable name.

Don’t friendly

class Car
{
   public $carMake;
   public $carModel;
   public $carColor; / /... }Copy the code

friendly

class Car
{
   public $make;
   public $model;
   public $color; / /... }Copy the code
  • Replace short-circuit or conditional operations with parameter defaults

Don’t friendly

This doesn’t make sense because the variable $breweryName might be NULL.

function createMicrobrewery($breweryName = 'Hipster Brew Co.'): void
{
   // ...
}
Copy the code

It can’t be that bad

This is a little easier to understand than the previous version, but it would be nice if you could control variable value fetching.

function createMicrobrewery($name = null): void
{
   $breweryName = $name? :'Hipster Brew Co.';
   // ...
}
Copy the code

friendly

If you only support PHP 7+, You can use type constraints and ensure $http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration variables are not NULL.

function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void
{
   // ...
}
Copy the code

function

  • Function parameters should be limited to two or fewer

Limiting the parameters of a function is important when testing a function. Having more than three optional parameters will multiply your testing workload.

Ideally, there are no parameters. One or two parameters are ok, but three parameters should be avoided. The more parameters we have, the more we have to maintain. In general, if your function > number has more than 2 arguments, then your function has too many things to handle. If you do need that many arguments, then in most cases it is probably more appropriate to handle them with a single object.

Unfriendly:

function createMenu(string $title, string $body, string $buttonText, bool $cancellable): void
{
   // ...
}
Copy the code

Friendly:

class MenuConfig
{
   public $title;
   public $body;
   public $buttonText;
   public $cancellable = false;
}

$config = new MenuConfig();
$config->title = 'Foo';
$config->body = 'Bar';
$config->buttonText = 'Baz';
$config->cancellable = true;

function createMenu(MenuConfig $config): void
{
   // ...
}
Copy the code
  • A function only does one thing

In the software engineering industry, this is the most important criterion. When a function handles more than one thing, it becomes difficult to implement, test, and understand. When you can make a function do just one thing, they become easier to refactor and clearer to understand. Just implementing this principle will make you the best developer you can be.

Unfriendly:

function emailClients(array $clients): void
{
   foreach ($clients as $client) {
       $clientRecord = $db->find($client);
       if ($clientRecord->isActive()) {
           email($client); }}}Copy the code

Friendly:

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();
}
Copy the code
  • Function names should mean what they say

Unfriendly:

class Email
{
   //...

   public function handle(): void
   {
       mail($this->to, $this->subject, $this->body); }}$message= new Email(...) ; // What is this? What is the 'handle' method? Should we write to a file now?$message->handle();
Copy the code

Friendly:

class Email 
{
   //...

   public function send(): void
   {
       mail($this->to, $this->subject, $this->body); }}$message= new Email(...) ; // Clear and obvious$message->send();
Copy the code
  • Functions should have only one layer of abstraction

When your function has more than one layer of abstraction it means that the function is doing too much. Decoupling this function makes it reusable and easier to test.

Unfriendly:

function parseBetterJSAlternative(string $code): void
{
   $regexes= [//...] ;$statements = explode(' '.$code);
   $tokens = [];
   foreach ($regexes as $regex) {
       foreach ($statements as $statement{/ /... }}$ast = [];
   foreach ($tokens as $token) {
       // lex...
   }

   foreach ($ast as $node) { // parse... }}Copy the code

Not very friendly either:

We’ve split a few things out of the function, but the parseBetterJSAlternative() function is still too complex to test.

function tokenize(string $code): array
{
   $regexes= [//...] ;$statements = explode(' '.$code);
   $tokens = [];
   foreach ($regexes as $regex) {
       foreach ($statements as $statement) {
           $tokens[] = /* ... */;
       }
   }

   return $tokens;
}

function lexer(array $tokens): array
{
   $ast = [];
   foreach ($tokens as $token) {
       $ast[] = / *... * /. }return $ast;
}

function parseBetterJSAlternative(string $code): void
{
   $tokens = tokenize($code);
   $ast = lexer($tokens);
   foreach ($ast as $node) { // parse... }}Copy the code

friendly

The best solution is to separate out what the parseBetterJSAlternative() function depends on.

class Tokenizer
{
   public function tokenize(string $code): array
   {
       $regexes= [//...] ;$statements = explode(' '.$code);
       $tokens = [];
       foreach ($regexes as $regex) {
           foreach ($statements as $statement) {
               $tokens[] = /* ... */;
           }
       }

      return $tokens;
   }
}

class Lexer
{
   public function lexify(array $tokens): array
   {
       $ast = [];
       foreach ($tokens as $token) {
           $ast[] = / *... * /. }return $ast;
   }
}

class BetterJSAlternative
{
   private $tokenizer;
   private $lexer;

   public function __construct(Tokenizer $tokenizer, Lexer $lexer)
   {
       $this->tokenizer = $tokenizer;
       $this->lexer = $lexer;
   }

   public function parse(string $code): void
   {
       $tokens = $this->tokenizer->tokenize($code);
       $ast = $this->lexer->lexify($tokens);
       foreach ($ast as $node) { // parse... }}}Copy the code
  • Don’t substitute in the functionflagRelated parameters

When you use flag it means that your function is doing more than one thing. As we mentioned earlier, functions should only do one thing. If your code depends on a Boolean, split it out.

Don’t friendly

function createFile(string $name, bool $temp = false): void
{
   if ($temp) {
       touch('./temp/'.$name);
   } else {
       touch($name); }}Copy the code

friendly

function createFile(string $name): void
{
   touch($name);
}

function createTempFile(string $name): void
{
   touch('./temp/'.$name);
}
Copy the code
  • Avoid the side effects of functions

Side effects can occur when functions have data input and output. This side effect could be written to a file, modifying global variables, or accidentally transferring your money to a stranger.

At the moment, you may sometimes need these side effects. As mentioned earlier, you may need to write to a file. What you need to be careful about is keeping what you do under control. Don’t let individual functions or classes write to a particular file. All should be treated equally. There is one and only one result.

It is important to avoid common pitfalls such as sharing unstructured objects, using mutable data that can be written to any type, and not dealing centrally with side effects. If you can, you’ll have an easier time than most programmers.

Don’t friendly

// The global variable is referenced by the following function. // If we use this in another function$name'variable, then may become an array or the program is interrupted.$name = 'Ryan McDermott';

function splitIntoFirstAndLastName(): void
{
   global $name;

   $name = explode(' '.$name);
}

splitIntoFirstAndLastName();

var_dump($name); / / /'Ryan'.'McDermott'];
Copy the code

friendly

function splitIntoFirstAndLastName(string $name): array
{
   return explode(' '.$name);
}

$name = 'Ryan McDermott';
$newName = splitIntoFirstAndLastName($name);

var_dump($name); // 'Ryan McDermott';
var_dump($newName); / / /'Ryan'.'McDermott'];
Copy the code
  • Avoid writing global methods

In most languages, it’s not a good practice to pollute global variables, because when you introduce another package it will collide and people using your API won’t know until they throw an exception. Let’s assume a simple example: If you want a configuration array. You might write a global function like config(), but it conflicts when you introduce other packages and try to do the same thing elsewhere.

Don’t friendly

function config(): array
{
   return  [
       'foo'= >'bar']},Copy the code

Don’t friendly

class Configuration
{
   private $configuration = [];

   public function __construct(array $configuration)
   {
       $this->configuration = $configuration;
   }

   public function get(string $key) :? string {return isset($this->configuration[$key])?$this->configuration[$key] : null; }}Copy the code

Introduce the Configuration by creating an instance of the Configuration class

$configuration = new Configuration([
   'foo'= >'bar',]);Copy the code

At this point, you can use this configuration in your projects.

  • Avoid the singleton pattern

The singleton pattern is an anti-pattern. Why not:

  1. They usually use a global instance. Why is that so bad? Because you hide dependencies in your project’s code, instead of exposing them through interfaces. You should make a conscious effort to avoid the big picture.
  2. They violate the single responsibility principle: they control their own life cycle.
  3. This pattern naturally couples the code together. This makes them inconsistent in a lot of situations.
  4. They continue throughout the life of the project. Another big blow is when you need to sort tests, which can be a big headache in unit testing. Why is that? Because every unit test should depend on another.

Don’t friendly

class DBConnection
{
   private static $instance;

   private function __construct(string $dsn{/ /... } public staticfunction getInstance(): DBConnection
   {
       if (self::$instance === null) {
           self::$instance = new self();
       }

       return self::$instance;
   }

   // ...
}

$singleton = DBConnection::getInstance();
Copy the code

friendly

class DBConnection
{
   public function __construct(string $dsn{/ /... } / /... }Copy the code

Use the DSN configuration to create a singleton of the DBConnection class.

$connection = new DBConnection($dsn);
Copy the code

At this point, you must use a DBConnection singleton in your project.

  • Package the conditional judgment

Don’t friendly

if ($article->state === 'published'{/ /... }Copy the code

friendly

if ($article->isPublished()) {
   // ...
}
Copy the code
  • Avoid negating conditions

Don’t friendly

function isDOMNodeNotPresent(\DOMNode $node): bool
{
   // ...
}

if(! isDOMNodeNotPresent($node)) {/ /... }Copy the code

friendly

function isDOMNodePresent(\DOMNode $node): bool
{
   // ...
}

if (isDOMNodePresent($node)) {/ /... }Copy the code
  • Avoid too much nesting of conditions

It seems like an impossible task. The first question that goes through many people’s minds is “What else could I do without the if condition?” . The answer is that, for the most part, you can use polymorphism to solve this problem. In addition, one might say, “Even if polymorphism could do it, why would we do it?” Our explanation for this is that a function should only do one thing, which is exactly what we mentioned earlier to make code cleaner. When you use too many if conditions in a function, it means your function is doing more than one thing. Remember: Be single-minded.

Unfriendly:

class Airplane
{
   // ...

   public function getCruisingAltitude(): int
   {
       switch ($this->type) {
           case '777':
               return $this->getMaxAltitude() - $this->getPassengerCount();
           case 'Air Force One':
               return $this->getMaxAltitude();
           case 'Cessna':
               return $this->getMaxAltitude() - $this->getFuelExpenditure(); }}}Copy the code

Friendly:

interface Airplane
{
   // ...

   public function getCruisingAltitude(): int;
}

class Boeing777 implements Airplane
{
   // ...

   public function getCruisingAltitude(): int
   {
       return $this->getMaxAltitude() - $this->getPassengerCount();
   }
}

class AirForceOne implements Airplane
{
   // ...

   public function getCruisingAltitude(): int
   {
       return $this->getMaxAltitude();
   }
}

class Cessna implements Airplane
{
   // ...

   public function getCruisingAltitude(): int
   {
       return $this->getMaxAltitude() - $this->getFuelExpenditure(); }}Copy the code
  • Avoiding Type Detection (Part 1)

PHP is a weakly typed language, which means your functions can take arguments of any type. It gives you unlimited freedom and at the same time bothers you, because sometimes you need to do a type check. There are a number of ways to avoid this, the first being a unified API.

Unfriendly:

function travelToTexas($vehicle): void
{
   if ($vehicle instanceof Bicycle) {
       $vehicle->pedalTo(new Location('texas'));
   } elseif ($vehicle instanceof Car) {
       $vehicle->driveTo(new Location('texas')); }}Copy the code

Friendly:

function travelToTexas(Traveler $vehicle): void
{
   $vehicle->travelTo(new Location('texas'));
}
Copy the code
  • Avoiding Type Detection (Part 2)

If you are using primitive types such as strings, integers, and arrays and require PHP 7+, no polymorphism, and type detection, you should consider type declarations or strict schemas. It provides static typing based on standard PHP syntax. The problem with manually checking types is that doing so requires a lot of nonsense, as if the loss of readability can be compromised for safety. Keep your PHP code clean, write good tests, and have a good habit of reviewing your code. Otherwise, use PHP’s strict type declarations and strict schemas to ensure safety.

Unfriendly:

function combine($val1.$val2): int
{
   if(! is_numeric($val1) | |! is_numeric($val2)) {
       throw new \Exception('Must be of type Number');
   }

   return $val1 + $val2;
}
Copy the code

Friendly:

function combine(int $val1, int $val2): int
{
   return $val1 + $val2;
}
Copy the code
  • Remove unused code

Code that is not reused is just as bad as duplicate code. There is absolutely no need to keep them in your code base. If you’re sure you don’t use it anymore, get rid of it! If you want to use it someday, you can also find it in your version record.

Unfriendly:

function oldRequestModule(string $url): void
{
   // ...
}

function newRequestModule(string $url): void
{
   // ...
}

$request = newRequestModule($requestUrl);
inventoryTracker('apples'.$request.'www.inventory-awesome.io');
Copy the code

Friendly:

function requestModule(string $url): void
{
   // ...
}

$request = requestModule($requestUrl);
inventoryTracker('apples'.$request.'www.inventory-awesome.io');
Copy the code

Objects and data structures

  • Using object encapsulation

In PHP you can set the keywords public, protected, and private to decorate your methods. When you use them, you can control the modification of these properties within an object.

  • When you want to do something other than “get” on an object’s properties, you don’t have to browse and change permissions in the code base.

  • It’s easier to do logical validation in your code when you’re doing something to change a property.

  • Encapsulate the internal representation.

  • It’s easier to add a log or error when you’re getting and setting properties.

  • When other classes inherit from the base class, you can override the default method.

  • You can retrieve the property value of this object for a service delay.

Not very friendly:

class BankAccount
{
   public $balance = 1000;
}

$bankAccount = new BankAccount();

// Buy shoes...
$bankAccount->balance -= 100;
Copy the code

Friendly:

class BankAccount
{
   private $balance;

   public function __construct(int $balance = 1000)
   {
     $this->balance = $balance;
   }

   public function withdraw(int $amount): void
   {
       if ($amount > $this->balance) {
           throw new \Exception('Amount greater than available balance.');
       }

       $this->balance -= $amount;
   }

   public function deposit(int $amount): void
   {
       $this->balance += $amount;
   }

   public function getBalance(): int
   {
       return $this->balance; }}$bankAccount = new BankAccount();

// Buy shoes...
$bankAccount->withdraw($shoesPrice);

// Get balance
$balance = $bankAccount->getBalance();
Copy the code
  • Can be used on properties of objectsprivate/protectedqualified
  • publicModifier methods and attributes are dangerous to modify as above, because external code can easily depend on them and you have no control over which code depends on them.For all users’ classes, being able to modify in a class is quite dangerous.
  • protectedDecorator andpublicAlso dangerous, because they can operate in the chain of inheritance. The difference is limited to the permission mechanism, and encapsulation remains the same.Modification in a class is also quite dangerous for all subclasses.
  • privateModifiers ensure that code is only available whenIt is the internal modifications of your own classes that are dangerous.

Therefore, use the private modifier instead of public/protected when you need to set permissions for external classes.

For more information you can read this article by Fabien Potencier

Not very friendly:

class Employee
{
   public $name;

   public function __construct(string $name)
   {
       $this->name = $name; }}$employee = new Employee('John Doe');
echo 'Employee name: '.$employee->name; // Employee name: John Doe
Copy the code

Friendly:

class Employee
{
   private $name;

   public function __construct(string $name)
   {
       $this->name = $name;
   }

   public function getName(): string
   {
       return $this->name; }}$employee = new Employee('John Doe');
echo 'Employee name: '.$employee->getName(); // Employee name: John Doe
Copy the code
  • Composition over Inheritance

As the Gang of Four said in the famous _Design Patterns_, you should use composition rather than inheritance whenever possible. There are many advantages to using both composition and inheritance. The most important rule is that when you instinctively want to use inheritance, think about whether composition can make your problem solving more elegant. In some cases it is.

You may be asking, “So when should I use inheritance?” It all depends on the problem at hand, but here are some examples of inheritance being better than composition:

  1. Your inheritance expresses a “is a” relationship instead of a “have a” relationship (Human->Animal vs. User->UserDetails).

  2. Humans can move like all animals.

  3. You want to Change the caloric expenditure of all animals when they move by changing the code through the base class.

Unfriendly:

class Employee 
{
   private $name;
   private $email;

   public function __construct(string $name, string $email)
   {
       $this->name = $name;
       $this->email = $email; } / /... } // The reason why this doesn't make sense is that not all employees have 'tax' characteristics. class EmployeeTaxData extends Employee { private$ssn;
   private $salary;
  
   public function __construct(string $name, string $email, string $ssn, string $salary)
   {
       parent::__construct($name.$email);

       $this->ssn = $ssn;
       $this->salary = $salary;
   }

   // ...
}
Copy the code

Friendly:

class EmployeeTaxData 
{
   private $ssn;
   private $salary;

   public function __construct(string $ssn, string $salary)
   {
       $this->ssn = $ssn;
       $this->salary = $salary;
   }

   // ...
}

class Employee 
{
   private $name;
   private $email;
   private $taxData;

   public function __construct(string $name, string $email)
   {
       $this->name = $name;
       $this->email = $email;
   }

   public function setTaxData(string $ssn, string $salary)
   {
       $this->taxData = new EmployeeTaxData($ssn.$salary);
   }

   // ...
}
Copy the code
  • Avoid chain calls (coherent interfaces)

When using some chained methods, this coherent interface can constantly point to the current object to make our code more legible.

In general, we can take advantage of its context when building objects, because this pattern reduces code redundancy, but as mentioned in PHPUnit Mock Builder or Doctrine Query Builder, this approach can sometimes cause problems:

  1. Destroy the encapsulation
  2. Destroy the design
  3. It is difficult to test
  4. It can be difficult to read

For more information you can read this article by Marco Pivetta

Friendly:

class Car
{
   private $make = 'Honda';
   private $model = 'Accord';
   private $color = 'white';

   public function setMake(string $make): self
   {
       $this->make = $make;

       // NOTE: Returning this for chaining
       return $this;
   }

   public function setModel(string $model): self
   {
       $this->model = $model;

       // NOTE: Returning this for chaining
       return $this;
   }

   public function setColor(string $color): self
   {
       $this->color = $color;

       // NOTE: Returning this for chaining
       return $this;
   }

   public function dump(): void
   {
       var_dump($this->make, $this->model, $this->color); }}$car = (new Car())
 ->setColor('pink') - >setMake('Ford') - >setModel('F-150')
 ->dump();
Copy the code

Unfriendly:

class Car
{
   private $make = 'Honda';
   private $model = 'Accord';
   private $color = 'white';

   public function setMake(string $make): void
   {
       $this->make = $make;
   }

   public function setModel(string $model): void
  {
       $this->model = $model;
   }

   public function setColor(string $color): void
   {
       $this->color = $color;
   }

   public function dump(): void
   {
       var_dump($this->make, $this->model, $this->color); }}$car = new Car();
$car->setColor('pink');
$car->setMake('Ford');
$car->setModel('F-150');
$car->dump();
Copy the code

SOLID

SOLID started out as five principles by Robert Martin and eventually named by Michael Feathers as a shorthand for the five basic principles of object-oriented design.

  • S: Single Responsibility Principle (SRP)

  • O: Open close Principle (OCP)

  • L: Richter’s Substitution Principle (LSP)

  • I: Interface Isolation Principle (ISP)

  • D: Dependency Inversion Principle (DIP)

  • Single Responsibility Principle (SRP)

As Clean Code states, “There should be only one reason to change a class.” We always like to cram too many methods into our classes, like when you pack your suitcase on an airplane. In this case your class has no notion of high cohesion and leaves a lot of room for modification. It’s important to minimize the amount of time you need to modify classes. If you have too many methods in a single class and you change them often, it will be very difficult to understand if other code bases introduce such modules.

Unfriendly:

class UserSettings
{
   private $user;

   public function __construct(User $user)
   {
       $this->user = $user;
   }

   public function changeSettings(array $settings): void
   {
       if ($this->verifyCredentials()) {
           // ...
       }
   }

   private functionverifyCredentials(): bool { // ... }}Copy the code

Friendly:

class UserAuth 
{
   private $user;

   public function __construct(User $user)
   {
       $this->user = $user;
   }
 
   public function verifyCredentials(): bool
   {
       // ...
   }
}

class UserSettings 
{
   private $user;
   private $auth;

   public function __construct(User $user) 
   {
       $this->user = $user;
       $this->auth = new UserAuth($user);
   }

   public function changeSettings(array $settings): void
   {
       if ($this->auth->verifyCredentials()) { // ... }}}Copy the code
  • Open close Principle (OCP)

As Bertrand Meyer said, “Software development should be closed to extension, closed to modification.” What does that mean? This principle basically means that you should allow others to add new features without changing existing ones.

Don’t friendly

abstract class Adapter
{
   protected $name;

   public function getName(): string
   {
       return $this->name;
   }
}

class AjaxAdapter extends Adapter
{
   public function __construct()
   {
      parent::__construct();

       $this->name = 'ajaxAdapter';
   }
}

class NodeAdapter extends Adapter
{
   public function __construct()
   {
       parent::__construct();

       $this->name = 'nodeAdapter';
   }
}

class HttpRequester
{
   private $adapter;

   public function __construct(Adapter $adapter)
   {
       $this->adapter = $adapter;
   }

   public function fetch(string $url): Promise
   {
       $adapterName = $this->adapter->getName();

       if ($adapterName= = ='ajaxAdapter') {
           return $this->makeAjaxCall($url);
       } elseif ($adapterName= = ='httpNodeAdapter') {
           return $this->makeHttpCall($url);
       }
   }

   private function makeAjaxCall(string $url): Promise
   {
       // request and return promise
   }

   private function makeHttpCall(string $url): Promise
   {
       // request and return promise
   }
}
Copy the code

Friendly:

interface Adapter
{
   public function request(string $url): Promise;
}

class AjaxAdapter implements Adapter
{
   public function request(string $url): Promise
   {
       // request and return promise
   }
}

class NodeAdapter implements Adapter
{
   public function request(string $url): Promise
   {
       // request and return promise
   }
}

class HttpRequester
{
   private $adapter;

   public function __construct(Adapter $adapter)
   {
       $this->adapter = $adapter;
   }

   public function fetch(string $url): Promise
   {
       return $this->adapter->request($url); }}Copy the code
  • Richter’s Substitution Principle (LSP)

It’s a very simple principle in itself but it’s given a rather confusing name. This principle is usually defined as “if S is a subclass of T, then object T can be replaced without warning by its subclasses (e.g., object S may replace object T) with some more appropriate properties.” It’s a little harder to understand.

The best way to say it is that if you have a parent and a child, then your parent and child classes can be swapped back and forth. This might still be hard to understand, but let’s take a square and a rectangle. In mathematics, a rectangle belongs to a rectangle, but it would be wrong to use the “IS-A” relationship in your model by inheritance.

Unfriendly:

class Rectangle
{
   protected $width = 0;
   protected $height = 0;

   public function render(int $area): void
   {
       // ...
   }

   public function setWidth(int $width): void
   {
       $this->width = $width;
   }

   public function setHeight(int $height): void
   {
       $this->height = $height;
   }

   public function getArea(): int
   {
      return $this->width * $this->height;
   }
}

class Square extends Rectangle
{
   public function setWidth(int $width): void
   {
       $this->width = $this->height = $width;
   }

   public function setHeight(int $height): void
   {
       $this->width = $this->height = $height; }}function renderLargeRectangles(array $rectangles): void
{
   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()];
renderLargeRectangles($rectangles);
Copy the code

Friendly:

abstract class Shape
{
   protected $width = 0;
   protected $height = 0;

   abstract public function getArea(): int;

   public function render(int $area): void
  {
       // ...
   }
}

class Rectangle extends Shape
{
   public function setWidth(int $width): void
   {
       $this->width = $width;
   }

   public function setHeight(int $height): void
   {
       $this->height = $height;
   }

   public function getArea(): int
   {
       return $this->width * $this->height;
   }
}

class Square extends Shape
{
   private $length = 0;

   public function setLength(int $length): void
   {
       $this->length = $length;
   }

   public function getArea(): int
   {
       return pow($this->length, 2); }}function renderLargeRectangles(array $rectangles): void
{
   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()];
renderLargeRectangles($shapes);
Copy the code
  • Interface Isolation Principle (ISP)

An ISP means “a user should not be forced to use an interface it does not need”.

A good example to illustrate this principle is when a class requires a lot of setup. It takes a lot of setup to call this interface, but in most cases it’s not necessary. Forcing them to use these Settings makes the interface bloated.

Unfriendly:

interface Employee
{
   public function work(): void;

   public function eat(): void;
}

class Human implements Employee
{
   public functionwork(): void { // .... working } publicfunction eat(): void
   {
       // ...... eating in lunch break
   }
}

class Robot implements Employee
{
   public function work(): void
   {
       //.... working much more
   }

   public function eat(): void
   {
       //.... robot can't eat, but it must implement this method } }Copy the code

Friendly:

Not every worker is an employee, but every employee is a worker.

interface Workable
{
   public function work(): void;
}

interface Feedable
{
   public function eat(): void;
}

interface Employee extends Feedable, Workable
{
}

class Human implements Employee
{
   public functionwork(): void { // .... working } publicfunction eat(): void
   {
       //.... eating in lunch break
   }
}

// robot can only work
class Robot implements Workable
{
   public functionwork(): void { // .... working } }Copy the code
  • Dependency Inversion principle (DIP)

There are two caveats to this principle:

  1. A higher-order module cannot depend on a lower-order module. They should all rely on abstractions.

  2. Abstraction should not depend on implementation; implementation should depend on abstraction.

The first point may be a little hard to understand, but if you’ve ever used a PHP framework like Symfony, you’ve probably seen principles like dependency injection implemented. Although they are different concepts, DIP allows higher-order modules to be separated from what we know as lower-order modules. This can be done by means of DI. A huge benefit is that it decouples the different modules. Coupling is a very bad development pattern because it makes your code difficult to refactor.

Unfriendly:

class Employee
{
   public functionwork(): void { // .... working } } class Robot extends Employee { publicfunction work(): void
   {
       //.... working much more
   }
}

class Manager
{
   private $employee;

   public function __construct(Employee $employee)
   {
       $this->employee = $employee;
   }

   public function manage(): void
   {
       $this->employee->work(); }}Copy the code

friendly

interface Employee
{
   public function work(): void;
}

class Human implements Employee
{
   public functionwork(): void { // .... working } } class Robot implements Employee { publicfunction work(): void
   {
       //.... working much more
   }
}

class Manager
{
   private $employee;

   public function __construct(Employee $employee)
   {
       $this->employee = $employee;
   }

   public function manage(): void
   {
       $this->employee->work(); }}Copy the code

Don’t duplicate your code (DRY)

Try exploring the DRY principle.

Try not to copy code. Copying code is bad because it means you’ll need to change more than one piece of business logic in the future when it needs to be changed.

Imagine you’re running a restaurant and you need to keep your inventory: all your potatoes, Onions, garlic, peppers, etc. If you have multiple lists to manage sales records, you will need to update all lists when you use some of these potatoes for cooking. Only one place to update if you only have one list!

Most of the time you have duplicate code because you have more than two nuances, most of which are the same, but so different that you have to split them up into different ways of doing the same thing. Removing this duplicate code means that you need to create an abstraction that can be handled with a method/module/class.

Using an abstraction is key, which is why you should follow the SOLID principle in your classes. An inelegant abstraction is often worse than repetitive code, so use it with caution! Having said that, if you can already construct an elegant abstraction, do it! Don’t duplicate your code, or you’ll find yourself changing a lot of things when you need to.

Unfriendly:

function showDeveloperList(array $developers): void
{
   foreach ($developers as $developer) {
       $expectedSalary = $developer->calculateExpectedSalary();
       $experience = $developer->getExperience();
       $githubLink = $developer->getGithubLink();
       $data = [
           $expectedSalary.$experience.$githubLink
       ];

       render($data); }}function showManagerList(array $managers): void
{
   foreach ($managers as $manager) {
       $expectedSalary = $manager->calculateExpectedSalary();
       $experience = $manager->getExperience();
       $githubLink = $manager->getGithubLink();
       $data = [
           $expectedSalary.$experience.$githubLink
       ];

       render($data); }}Copy the code

Friendly:

function showList(array $employees): void
{
   foreach ($employees as $employee) {
       $expectedSalary = $employee->calculateExpectedSalary();
       $experience = $employee->getExperience();
       $githubLink = $employee->getGithubLink();
       $data = [
           $expectedSalary.$experience.$githubLink
       ];

       render($data); }}Copy the code

Very elegant:

It would be nice if it were simpler.

function showList(array $employees): void
{
   foreach ($employees as $employee) {
       render([
           $employee->calculateExpectedSalary(),
           $employee->getExperience(),
           $employee->getGithubLink() ]); }}Copy the code

The original address

clean-code-php


Original: www.hellonine.top/index.php/a…