Programming Guide for PHP developers part 1 complexity reduction

Source: Internet
Author: User
For the PHP developer's programming guide, the first part of the content is to reduce the complexity, interested friends can refer to PHP is a high degree of freedom programming language. It is a dynamic language that provides a great deal of flexibility for programmers. As a PHP programmer, to make your code more effective, you need to understand a lot of specifications. For many years, I have read many programming books and discussed the code style with many senior programmers. I certainly won't remember which rule comes from which book or which person, but this article (and the next article) expresses my views on how to write better code: code that can withstand the test is usually very easy to read and understand. Such code makes it easier for others to find problems or reuse code.
Reduce the complexity of the function body

Reduce complexity as much as possible in the method or function body. It is relatively low in complexity and can be easily read by others. In addition, this can reduce the possibility of code problems, make them easier to modify, and fix problems.
Number of parentheses in the function

Use as few if, elseif, else, and switch statements as possible. They add more parentheses. This makes the code more difficult and harder to test (because each brace needs to be covered by a test case ). There is always a way to avoid this problem.
Proxy decision ("command, no need to query (Tell, don't ask )")
Sometimes the if statement can be moved to another object, which is clearer. For example:

 if($a->somethingIsTrue()) {  $a->doSomething(); }

You can change it:
$ A-> doSomething ();
Here, the specific judgment is done by the doSomething () method of the $ a object. We don't need to worry more about this. we just need to call doSomething () securely. This method elegantly follows the command and does not require querying. I suggest you have a deep understanding of this principle. this principle applies when you query information of an object and make judgments based on the information.
Use map

You can use the map statement to reduce the use of if, elseif, or else. for example:

if($type==='json') {  return $jsonDecoder->decode($body);}elseif($type==='xml') {  return $xmlDecoder->decode($body);}else{  throw new \LogicException(    'Type "'.$type.'" is not supported'  );}

It can be simplified:

$decoders= ...;// a map of type (string) to corresponding Decoder objects if(!isset($decoders[$type])) {  thrownew\LogicException(    'Type "'.$type.'" is not supported'  );}

In this way, map allows your code to follow the principle of extended openness and modification.
Force type

Many if statements can be avoided by using stricter types, for example:

if($a instanceof A) {  // happy path  return $a->someInformation();}elseif($a=== null) {  // alternative path  return 'default information';}

You can simplify it by forcing $ a to use the type:

return $a->someInformation();

Of course, we can support "null" in other ways. This will be mentioned in later articles.
Return early

In many cases, a branch in a function is not a real branch, but a front or back condition, like this: // The front condition

if(!$a instanceof A) {  throw new \InvalidArgumentException(...);} // happy pathreturn $a->someInformation();

Here, the if statement is not a branch of function execution. it only checks a precondition. Sometimes we can let PHP itself perform the pre-condition check (for example, use the appropriate type prompt ). However, PHP cannot check all the preconditions, so you still need to keep some in the code. To reduce the complexity, we need to return the code as early as possible when an error occurs in the code, when an error is input, or when the result is known.
The result returned as early as possible is that the subsequent code does not need to be indented as before:

// check preconditionif(...) {  thrownew...();} // return earlyif(...) {  return...;} // happy path... return...;

Like the template above, the code changes are easier to read and understand.
Create a small logical unit

If the function is too long, it is difficult to understand what the function is doing. Tracking the use of variables, variable types, variable declaration cycles, Call of auxiliary functions, etc., all consume a lot of brain cells. If the function is small, it is helpful for understanding the function (for example, the function only accepts some input, performs some processing, and then returns the result ).
Auxiliary functions
After using the previous principle to reduce parentheses, you can also split the function into smaller logical units to make the function clearer. You can regard the code lines that implement a subtask as a group of code, which are directly separated by blank lines. Then consider how to split them into auxiliary methods (that is, the extraction method in refactoring ).
The auxiliary method is generally a private method and will only be called by the object of the specific class. Generally, they do not need to access instance variables. in this case, they need to be defined as static methods. In my experience, the auxiliary methods of private (static) are usually summarized into isolated classes and defined as public (static or instance) methods, this is the case where at least one collaboration class is used for test-driven development.
Reduce temporary variables
Long functions usually require some variables to save intermediate results. These temporary variables are difficult to trace: you need to remember whether they have been initialized, used, and the current value.
The helper functions mentioned above help reduce temporary variables:

public function capitalizeAndReverse(array $names) {  $capitalized = array_map('ucfirst', $names);  $capitalizedAndReversed = array_map('strrev', $capitalized);  return $capitalizedAndReversed;}

Using the auxiliary method, we can skip the temporary variables:

public function capitalizeAndReverse(array $names) {  return self::reverse(    self::capitalize($names)  );} private static function reverse(array $names) {  return array_map('strrev', $names);} private static function capitalize(array $names) {  return array_map('ucfirst', $names);}

As you can see, we turn functions into combinations of new functions, making them easier to understand and modify. In some way, the code also complies with "extended open/modify close", because we basically do not need to modify the auxiliary function.
Because many algorithms need to traverse the container to get a new container or calculate a result, the container itself is treated as a "first-class citizen" and associated behavior is appended, this is meaningful:

classNames{  private $names;   public function __construct(array $names)  {    $this->names = $names;  }   public function reverse()  {    return new self(      array_map('strrev', $names)    );  }   public function capitalize()  {    return new self(      array_map('ucfirst', $names)    );  }}$result = (newNames([...]))->capitalize()->reverse();

This can simplify the combination of functions.
Although the reduction of temporary variables usually leads to a good design, there is no need to eliminate all the temporary variables in the above example. Sometimes the use of temporary variables is very clear, and their functions are also clear, so there is no need to streamline them.

Simple type

It is always troublesome to trace the current value of a variable, especially when it is unclear about the type of the variable. If the type of a variable is not fixed, it is a nightmare.
The array only contains values of the same type.
When using an array as a container that can be traversed, make sure that only the same type of value is used. This reduces the complexity of reading data through arrays:

Foreach ($ collection as $ value) {// if you specify the $ value type, you do not need to perform a type Check}

Your code editor will also provide you with an array value type prompt:

/*** @ Param DateTime [] $ collection */public function doSomething (array $ collection) {foreach ($ collection as $ value) {// $ value is of the DateTime type }}

If you are not sure that $ value is of the DateTime type, you have to add a pre-judgment in the function to check its type. The beberlei/assert Library makes this easier:

useAssert\Assertion public function doSomething(array $collection) {  Assertion::allIsInstanceOf($collection, \DateTime::class);   ...}

If the content in the container is not of the DateTime type, an InvalidArgumentException exception is thrown. In addition to forcing the input of values of the same type, the use of assert is also a method to reduce code complexity, because you can perform type checks without the function header.
Simple Return value type
As long as the return value of a function may have different types, the complexity of the calling code is greatly increased:

$result= someFunction();if($result=== false) {  ...}else if(is_int($result)) {  ...}

PHP does not prevent you from returning different types of values (or using different types of parameters ). But this will only cause a lot of confusion, and your program will be filled with if statements everywhere.
The following is an example of a mixed type returned frequently:

/** * @param int $id * @return User|null */public function findById($id){  ...}

This function will return the User object or null. this is a problem. if you do not check whether the return value is a valid User object, we cannot call the return value method. Before PHP 7, this will cause "Fatal error" and then cause the program to crash.
In the next article, we will consider null to show you how to handle them.
Readable expression

We have discussed many ways to reduce the overall complexity of functions. In terms of finer granularity, we can also do something to reduce code complexity.
Hide complex logic

Generally, complex expressions can be converted into auxiliary functions. Take a look at the following code:

if(($a||$b) &&$c) {  ...}

It can be simpler, like this:

if(somethingIsTheCase($a,$b,$c)) {  ...}

When reading the code, you can clearly understand that this judgment depends on the $ a, $ B, and $ c variables, and the function name can also well express the content of the judgment condition.
Use a Boolean expression
The content of an if expression can be converted into a Boolean expression. However, PHP does not force you to provide a boolean value:

$a=new\DateTime();... if($a) {  ...}

$ A is automatically converted to the boolean type. Forced type conversion is one of the main sources of bugs. However, there is another problem that may bring complexity to the understanding of the code, because the type conversion here is implicit. An alternative to implicit conversion in PHP is explicit type conversion. for example:

if($a instanceof DateTime) {  ...}

If you know that the bool type is compared, it can be simplified as follows:

if($b=== false) {  ...}

Use! Operators can also be simplified:

if(!$b) {  ...}

Do not use a Yoda expression
The Yoda expression is like this:

if('hello'===$result) {  ...}

This expression is mainly used to avoid the following errors:

if($result='hello') {  ...}

Here, 'Hello' is assigned to $ result and then becomes the value of the entire expression. 'Hello' is automatically converted to the bool type. here it is converted to true. So the code in the if branch will always be executed here.
The Yoda expression helps you avoid this problem:

if('hello'=$result) {  ...}

I don't think this will happen unless he is still learning the basic PHP syntax. In addition, the Yoda-style code also has a large cost: readability. Such expressions are not easy to read and understand, because they do not conform to natural language habits.

The above is all the content of this article, hoping to help you learn.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.