PHP Clean Code (Part 2)

Posted by Kiubbo on Fri, 27 Mar 2020 11:33:02 +0100

PHP Clean Code (Part I)

Use default parameters instead of short circuit operation or condition judgment

Bad practice:

This is not good because $breweryName can be NULL

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

  

It's OK:

This approach is easier to understand than the above, but it needs to control the value of variables well

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

  

Good practice:

You can use Type hint And you can guarantee that $breweryName will not be NULL

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

  

 

Contrast

 

Use Equality operator

Bad practice:

$a = '42';
$b = 42;
Using a simple equality operator converts a string type to a number type

if( $a != $b ) {
   //This conditional expression always passes
}

  

The expression $a! = $B returns false, but it should be true!
String type '42' is different from number type 42

Good practice:
Use the full operator to compare types and values

if( $a !== $b ) {
    //This condition is passed
}

  

The expression $a! = = $B returns true.

 

function

Function arguments (2 or less)

It is very important to limit the number of function parameters

It's easier to test your functions. There are more than three optional parameters that will lead to an explosive combination growth, and you will have tons of independent parameters to test.

No parameter is ideal. One or two can be used. It's better to avoid three.

More needs to be reinforced. Usually if your function has more than two arguments, it has too much to deal with. If you have to pass in a lot of data, it is recommended to encapsulate a high-level object as a parameter.

unamiable:

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

  

Amicable:

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
{
    // ...
}

  

Function should do only one thing

This is by far the most important principle of software engineering. When functions do more than one thing, they become difficult to write, test, and derive. When a function does only one thing, it's very simple to refactor, and the code is very clear to read. With this principle in hand, you will be ahead of many other developers.

It is bad:

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

  

Well:

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

  

The name of the function should be clear about the example of what it does not do well:
class Email
{
    //...

    public function handle(): void
    {
        mail($this->to, $this->subject, $this->body);
    }
}

$message = new Email(...);
// What is this? A handle for the message? Are we writing to a file now?
$message->handle()

  

; 

A good example:

class Email 
{
    //...

    public function send(): void
    {
        mail($this->to, $this->subject, $this->body);
    }
}

$message = new Email(...);
// Clear and obvious
$message->send();

  

Function can only be an abstraction level

When you have multiple levels of abstraction, you usually do too much function. Partitioning functions makes reusability and testing easier. .

Not good:

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...
    }
}

  

Also not very good:

We have completed some functions, but the parsebetterjscalternative() function is still very complex and troublesome 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...
    }
}

  

Very good:

The best solution is to extract the dependency of the parsebetterjalternative() function

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...
        }
    }
}

  

For more information, please visit:

Tencent T3-T4 standard boutique PHP architect tutorial directory, as long as you read it to ensure a higher salary (continuous update)

Topics: PHP brew less