Programming Guide for PHP developers The first part to reduce complexity _php skills

Source: Internet
Author: User
Tags assert datetime readable

PHP is a very high degree of freedom programming language. It's a dynamic language and has a lot of latitude for programmers. As a PHP programmer, you need to know a lot of specs to make your code more efficient. For many years, I have read many programming books and discussed the code style with many senior programmers. I'm sure I won't remember exactly which Rule comes from which book or who, but this article (and the next one) expresses my view of how to write better code: the code that can withstand the test is usually very readable and easy to understand. Such code, others can be easier to find problems, but also more simple to reuse code.
reduce the complexity of the function body

In the method or function body, reduce complexity as much as possible. Relatively low complexity makes it easier for others to read the code. In addition, this can reduce the possibility of code problems, easier to modify, problems are easier to fix.
reduce the number of parentheses in a function

Use if, ElseIf, else and switch these statements as little as possible. They will add more parentheses. This makes the code more difficult to understand and harder to test (because each bracket requires a test case overlay). There are always ways to avoid this problem.
Agent Decision ("command, don't Go to query" (Tell, don ' t ask))
Sometimes if statements can be moved to another object, which makes it clearer. For example:

 if ($a->somethingistrue ()) {
  $a->dosomething ();
 }

Can be changed to:
$a->dosomething ();
here, the specific judgment is done by the dosomething () method of the $a object. We don't need to think about it any more, just call dosomething () safely. This approach gracefully follows the instructions and does not query the principle. I suggest you take a closer look at this principle, and you can apply this principle when you are querying information from an object and making judgments based on that information.
using Map

You can sometimes use a map statement to reduce the usage 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 '
  );

Can be streamlined to:

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

This way of using the map also allows your code to follow the extended open, closed modification principle.
Coercion Type

Many if statements can be avoided by using more restrictive types, such as:

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

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

Return $a->someinformation ();

Of course, we can support "null" in other ways. This will be mentioned in the following article.
Return early

In many cases, a branch of a function is not a real branch, but rather a precondition for a predecessor or a post, like this://precondition

if (! $a instanceof a) {
  throw new \invalidargumentexception (...);
}
 
Happy path return
$a->someinformation ();

Here the IF statement is not a branch of function execution, it is just a check of a precondition. Sometimes we can let PHP itself do the pre-condition check (for example, using the appropriate type hint). However, PHP is not able to complete all of the precondition of the check, so still need to keep some in the code. To reduce complexity, we need to return as early as possible when we know the code is going wrong, when we enter an error, and when we already know the result.
The effect of returning early is that the following code does not need to be indented as before:

Check precondition
if (...) {
  thrownew ... ();
}
 
Return early
if (...) {return
  ...;
}
 
Happy path
...
 
return ...;

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

If the function body is too long, it is difficult to understand what the function is doing. The use of trace variables, variable types, variable declaration cycles, invoked auxiliary functions, and so on, all consume a lot of brain cells. If the function is small, it is helpful to understand the function (for example, the function simply accepts some input, does some processing, and returns the result).
using Auxiliary functions
After using the previous principle to reduce parentheses, you can also make the function clearer by splitting the function into smaller logical units. You can think of a line of code that implements a subtask as a set of code that is separated directly by a blank line. Then consider how to split them into auxiliary methods (i.e., the refinement method in refactoring).
Auxiliary methods are generally private methods that are invoked only by objects of the particular class to which they belong. Often they do not need to access the instance's variables, which need to be defined as static methods. In my experience, private (static) auxiliary methods are often rolled up into separate classes and defined as public (static or instance) methods, at least when testing-driven development uses a collaborative class.
reduce temporary variables
Long functions usually require some variables to hold the intermediate results. These temporary variables are tricky to track: You need to remember whether they have been initialized, whether they are useful, how much value is now, and so on.
The auxiliary functions mentioned in the previous section help reduce temporary variables:

Public function Capitalizeandreverse (array $names) {
  $capitalized = array_map (' Ucfirst ', $names);
  $capitalizedAndReversed = Array_map (' Strrev ', $capitalized);
  return $capitalizedAndReversed;
}

Using the helper method, we can use no 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 the function into a combination of new functions, which makes it easier to understand and easier to modify. In some ways, the code is somewhat compliant with "Extended Open/modified shutdown" because we basically don't need to modify the auxiliary function.
Since many algorithms need to traverse the container to get a new container or to compute a result, it makes sense to treat the container itself as a "first-class citizen" and attach the relevant behavior:

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 ();

Doing so simplifies the combination of functions.
Although reducing temporary variables usually leads to good design, there is no need to kill all the temporary variables in the above example. Sometimes the usefulness of temporary variables is very clear, the role is also at a glance, there is no need to streamline.

Use a simple Type

It is always troublesome to trace the current value of a variable, especially if the type of the variable is not known. And if the type of a variable is not fixed, it is a nightmare.
array contains only values of the same type
when using arrays as a reusable container, be sure to use only values of the same type, regardless of the circumstances. This reduces the complexity of looping through array read data:

foreach ($collection as $value) {
  //If you specify the type of $value, you do not need to do type checking
}

Your code Editor will also provide you with the type hint for the array value:

/**
 * @param datetime[] $collection
/Public Function dosomething (array $collection) {
  foreach ($ Collection as $value) {
    //$value is datetime type
  }
}

And if you're not sure $value is a DateTime type, you'll have to add a forward judgment in the function to check its type. Beberlei/assert Library can make this thing simpler:

Useassert\assertion Public
 
function dosomething (array $collection) {
  assertion::allisinstanceof ($ collection, \datetime::class);
 
  ...
}

This throws a InvalidArgumentException exception if there is content in the container that is not a DateTime type. In addition to forcing you to enter values of the same type, using assertions (assert) is also a way to reduce the complexity of the code, because you can not do type checking on the head of a function.
Simple return value type
as long as the return value of the function may have different types, it can greatly increase the complexity of the calling-side code:

$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 doing so will only create a lot of confusion, and your program will be filled with if statements.
The following is an example of a frequently encountered return blend type:

/**
 * @param int $id
 * @return user|null
 *
/Public Function FindByID ($id)
{
  ...
}

This function returns the user object or null, which is problematic, and we cannot invoke the method that returns the value if the user object does not check if the return value is legitimate. Before PHP 7, this will cause "Fatal error", and then the program crashes.
In the next article we'll consider NULL to show you how to deal with them.
a readable expression

We have discussed many ways to reduce the overall complexity of a function. In finer granularity we can also do something to reduce the complexity of the code.
Hide Complex Logic

It is often possible to turn complex expressions into auxiliary functions. Look at the following code:

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

Can be simpler, like this:

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

Reading the code makes it clear that this judgment relies on $a, $b and $c three variables, and that the function name can also be used to express the content of the judgment condition well.
Using Boolean expressions
The contents of an if expression can be converted to a Boolean expression. But PHP does not force you to provide a Boolean value:

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

$a is automatically converted to a Boolean type. Coercion of type conversions is one of the major sources of bugs, but there is also a problem with the complexity of understanding the code because the type conversions here are implicit. An alternative to an implicit conversion of PHP is to explicitly type conversions, such as:

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

If you know the type of bool to compare, you can simplify this:

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

Use! The operator can also simplify:

if (! $b) {
  ...
}

Don't Yoda style expressions
Yoda-style expressions are like this:

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

This expression is intended primarily to avoid the following error:

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

Here ' Hello ' assigns a value to $result and then becomes the value of the entire expression. The ' Hello ' is automatically converted to the bool type, where it is converted to true. So the code in the If branch will always be executed here.
Using Yoda-style expressions can help you avoid these types of problems:

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

I don't think it's true that people are going to get this wrong, unless he's learning PHP's basic syntax. Also, Yoda-style code has a very small price to pay: readability. Such expressions are not easy to read and are not easily understood because they do not conform to the natural language habit.

The above is the entire content of this article, I hope 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.