Table of Contents

  1. introduce
  2. variable
  3. function

introduce

PHP code clen PHP code clen PHP code

Principles for software engineers, excerpted from Robert C. Martin’s Clean Code book, apply to PHP. This is not a style guide. This is a guide to developing readable, reusable, and reconfigurable PHP software.

Not all of these principles are followed, and few are universally accepted. These are just guidelines, but they have been developed over the years by Clean Code’s authors.

Inspired from clean-code-javascript

variable

Variables of the same type use the same vocabulary

Bad:

getUserInfo();
getClientData();
getCustomerRecord();Copy the code

Good:

getUser();Copy the code

Pass back to top

Use names that are easy to retrieve

We read more code than we write. It is important that we write readable and searchable code by making it easy to search.

Bad:

// What the heck is 86400 for?
addExpireAt(86400);
Copy the code

Good:

// Declare them as capitalized `const` globals.
interface DateGlobal {
  const SECONDS_IN_A_DAY = 86400;
}

addExpireAt(DateGlobal: :SECONDS_IN_A_DAY);Copy the code

Pass back to top

Use explanatory variables

Bad:

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

Good:

$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,\ \]+ [,\ \ \s]+(.+?).\s*(\d{5})?$/ ';
preg_match($cityZipCodeRegex.$address.$matches);
list(, $city.$zipCode) = $matchers;
saveCityZipCode($city.$zipCode);Copy the code

Pass back to top

Avoid mental mapping

Explicit is better than implicit.

Bad:

$l = ['Austin'.'New York'.'San Francisco'];
foreach($i=0; $i<count($l); $i++) {
  oStuff();
  doSomeOtherStuff();
  //.
  //.
  //.
  //Wait, what does' $L 'stand for?
  dispatch($l);
}Copy the code

Good:

$locations = ['Austin'.'New York'.'San Francisco'];
foreach($i=0; $i<count($locations); $i++) {
  $location = $locations[$i];

  doStuff();
  doSomeOtherStuff();
  //.
  //.
  //.
  dispatch($location);
});Copy the code

Pass back to top

Don’t add unnecessary context

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

Bad:

$car = [
  'carMake'  = > 'Honda'.
  'carModel' = > 'Accord'.
  'carColor' = > 'Blue'.
];

function paintCar(&$car) {
  $car['carColor'] = 'Red';
}Copy the code

Good:

$car = [
  'make'  = > 'Honda'.
  'model' = > 'Accord'.
  'color' = > 'Blue'.
];

function paintCar(&$car) {
  $car['color'] = 'Red';
}Copy the code

Pass back to top

Use parameter defaults instead of short-circuit or conditional statements.

Bad:

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

Good:

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

Pass back to top

function

The function should only do one thing

This is by far the most important rule in software engineering. When functions do more than one thing, they are difficult to implement, test, and understand. When you isolate functions to just one function, they’re easier to refactor, and your code reads clearer. If you just follow this rule, you’ll be ahead of most developers.

Bad:

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

Good:

function emailClients($clients) {
  $activeClients = activeClients($clients);
  array_walk($activeClients.'email');
}

function activeClients($clients) {
  return array_filter($clients.'isClientActive');
}

function isClientActive($client) {
  $clientRecord = $db->find($client);
  return $clientRecord->isActive();
}Copy the code

Pass back to top

Function names should describe what they do

Bad:

function addToDate($date.$month) {
  //.
}

$date = new \DateTime(a);

// It's hard to to tell from the function name what is added
addToDate($date.1);Copy the code

Good:

function addMonthToDate($month.$date) {
  //.
}

$date = new \DateTime(a);
addMonthToDate(1.$date);Copy the code

Pass back to top

Functions should only be one level of abstraction, and when you go beyond one level of abstraction, functions are doing multiple things. The split feature is easy to reuse and ease of use. .

Bad:

function parseBetterJSAlternative($code) {
  $regexes = [
    //.
  ];

  $statements = split(' '.$code);
  $tokens = [];
  foreach($regexes as $regex) {
    foreach($statements as $statement) {
      //.
    }
  }

  $ast = [];
  foreach($tokens as $token) {
     // lex...
  }

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

Good:

function tokenize($code) {
  $regexes = [
    //.
  ];

  $statements = split(' '.$code);
  $tokens = [];
  foreach($regexes as $regex) {
    foreach($statements as $statement) {
      $tokens[] = / *.* /;
    });
  });

  return tokens;
}

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

  return ast;
}

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

Pass back to top

Remove duplicate code

Do your best to avoid duplicate code. Duplicate code is bad because it means that if you change some logic, you have to synchronize it in more than one place.

Imagine running a restaurant and keeping track of its inventory: all your tomatoes, Onions, garlic, spices, etc. If you keep multiple lists, when you serve a dish with tomatoes, all records have to be updated. If you only have one list, you only need to change one place!

Often you tolerate duplicate code because you have two or more things that have common parts but are slightly different forcing you to use two or more independent functions to do the same thing. Removing duplicate code means creating an abstraction that handles this different set of things, requiring only one function/module/class.

Getting the abstraction right is important, which is why you should follow the SOLID principle (the principle that underpins a Class). Bad abstractions can be worse than duplicate code, because be careful. With that in mind, if you can abstract well, do it! Don’t repeat yourself, or you’ll find yourself updating and maintaining multiple things anytime you want to change one thing.

Bad:

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);
  }
}Copy the code

Good:

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

    render($data);
  }
}Copy the code

Pass back to top

Default values are set by object assignment

Bad:

$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);Copy the code

Good:

$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);Copy the code

Pass back to top

Do not use flags as arguments to functions; flags tell your users that the function does a lot of work. The function should only do one thing. Break up your complex functions with paths that differ by Boolean values.

Bad:

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

Good:

function createFile($name) {
  touch(name);
}

function createTempFile($name) {
  touch('./temp/'.$name);
}Copy the code

Pass back to top

Avoid side effects

A function that does more than get a value and then return another value or values can have side effects if. The side effects could be writing a file, modifying some global variable or accidentally giving all your money to a stranger.

Now, you do need to have side effects in a program or situation, as in the previous example, you might need to write a file. What you want to do is centralize the places where you do this. Don’t use several functions and classes to write to a particular file. Do it with a service, one at a time.

The emphasis is on avoiding common pitfalls such as sharing unstructured data between objects, using mutable data types that can be written to any object, and not focusing on where side effects occur. If you do that you’ll be happier than most programmers.

Bad:

// 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() {
  $name = preg_split('/ / '.$name);
}

splitIntoFirstAndLastName();

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

Good:

$name = 'Ryan McDermott';

function splitIntoFirstAndLastName($name) {
  return preg_split('/ / '.$name);
}

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

var_export($name); // 'Ryan McDermott';
var_export($newName); // ['Ryan', 'McDermott'];Copy the code

Pass back to top

Don’t write global functions

Polluting global variables is a bad practice in most languages, because you may collide with other libraries and users of your API won’t understand why until they get an exception from the product. Let’s look at an example: if you want to configure an array, you might write a global function like config(), but that might conflict with other libraries trying to do the same thing. This is why singleton design patterns and simple configurations are better.

Bad:

function config() {
  return  [
    'foo': 'bar'.
  ]
};Copy the code

Good:

class Configuration {
  private static $instance;
  private function __construct($configuration) {/ * * /}
  public static function getInstance() {
     if(self: :$instance = = = null) {
         self: :$instance = new Configuration(a);
     }
     return self: :$instance;
 }
 public function get($key) {/ * * /}
 public function getAll() {/ * * /}
}

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

Pass back to top

Encapsulating conditional statement

Bad:

if ($fsm->state = = = 'fetching' && is_empty($listNode)) {
  //.
}Copy the code

Good:

function shouldShowSpinner($fsm.$listNode) {
  return $fsm->state = = = 'fetching' && is_empty(listNode);
}

if (shouldShowSpinner($fsmInstance.$listNodeInstance)) {
  //.
}Copy the code

Pass back to top

Avoid negative conditions

Bad:

function isDOMNodeNotPresent($node) {
  //.
}

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

Good:

function isDOMNodePresent($node) {
  //.
}

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

Pass back to top

Avoid conditional declarations

It seems like an impossible task. That’s what people say when they first hear it. “There is no if declaration.” The answer is that you can use polymorphism to accomplish many tasks in case statements. The second question is very common, “So why did I do that?” The answer is a clean code principle we learned earlier: a function should only do one thing. When you have classes and functions that have a lot of if declarations, you know yourself that your function does more than one thing. Remember, just do one thing.

Bad:

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

Good:

class Airplane {
  //.
}

class Boeing777 extends Airplane {
  //.
  public function getCruisingAltitude() {
    return $this->getMaxAltitude() - $this->getPassengerCount();
  }
}

class AirForceOne extends Airplane {
  //.
  public function getCruisingAltitude() {
    return $this->getMaxAltitude();
  }
}

class Cessna extends Airplane {
  //.
  public function getCruisingAltitude() {
    return $this->getMaxAltitude() - $this->getFuelExpenditure();
  }
}Copy the code

Pass back to top

Avoid Type checking (Part 1)

PHP is weakly typed, which means your functions can accept arguments of any type. Sometimes you suffer from this freedom and gradually try type checking in your functions. There are ways to avoid this. The first is to consider API consistency.

Bad:

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'));
  }
}Copy the code

Good:

function travelToTexas($vehicle) {
  $vehicle->move($this->currentLocation.new Location('texas'));
}Copy the code

Pass back to top

Avoiding Type Checking (Part 2)

If you’re using primitive values like strings, integers, and arrays, you can’t use polymorphism, and you still feel the need for type detection, you should consider type declarations or strict schemas. This gives you 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 clean, write tests, and do code reviews. If not, use PHP’s strict type declarations and strict schemas to ensure security.

Bad:

function combine($val1.$val2) {
  if (is_numeric($val1) && is_numeric(val2)) {
    return val1 + val2;
  }

  throw new \Exception('Must be of type Number');
}Copy the code

Good:

function combine(int $val1.int $val2) {
  return $val1 + $val2;
}Copy the code

Pass back to top

Removing zombie code

Zombie code is just as bad as duplicate code. There’s no reason to keep it in your code base. If it’s never called, go to hell! It’s safe to do so if you still need it in your repository.

Bad:

function oldRequestModule($url) {
  //.
}

function newRequestModule($url) {
  //.
}

$req = new newRequestModule(a);
inventoryTracker('apples'.$req.'www.inventory-awesome.io');
Copy the code

Good:

function newRequestModule($url) {
  //.
}

$req = new newRequestModule(a);
inventoryTracker('apples'.$req.'www.inventory-awesome.io');Copy the code

Pass back to top

Problem feedback

If you have any questions in use, please feel free to give me feedback. You can contact me through the following contact information

  • Email (yangweijiest#gmail.com, replace # with @)
  • QQ: 917647288
  • Weibo: @Black & White World 4648
  • Everyone: @Yang Weijie