preface

It’s been almost two years since I became a PHPer, and I’ve developed some ideas about how to write readable, extensible code.

Using a reference

Scenario 1: Iterate through an array to get a new data structure

You might write something like this:

// Declare a new array to assemble the data you want$tmp = [];
foreach ($arr as $k= >$v) {// Fetch the data you want$tmp[$k] ['youwant'] = $v['youwant']; . // A series of judgments to get the data you wantif(...). {$tmp[$k] ['youwantbyjudge'] = 'TIGERB'; }... } // Finally get the array you want$tmp-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- / / may you feel the above writing is not very good, that we here in these foreach ($arr as $k= >$v) {// a series of judgments to get the data you wantif(...). {// Copy the value you want$arr[$k] ['youwantbyjudge'] = 'TIGERB'}... // Kill structures you don't wantunset($arr[$k] ['youwantdel']); } // Finally we get our target array$arr
Copy the code

Next we use the reference value:

foreach ($arrAs &$v) {// a series of judgments to get the data you wantif(...). {// Copy the value you want$v['youwantbyjudge'] = 'TIGERB'}... // Kill structures you don't wantunset($v['youwantdel']);
}
unset($v); // Finally we get our target array$arr
Copy the code

Using references doesn’t make our code much cleaner, but it also saves memory compared to the first method, especially when manipulating a large array.

Scenario 2: Passing a value to a function to get a new value

This is basically the same as array traversal, we just need to declare this function as a reference, as follows:

function decorate(&$arr = []) {
    # code...
}

$arr= [...]. ; // Call the function callback ($arr); // Get the new value as shown above$arr, the advantage is to save memory spaceCopy the code

Using a try… catch…

Consider the following logic:

class UserModel
{
    public function login($username = ' '.$password = ' ')
    {
        code...
        if(...). {// The user does not existreturn- 1; } code...if(...). {// The password is incorrectreturn- 2; } code... } } class UserController { publicfunction login($username = ' '.$password = ' ')
    {
        $model = new UserModel();
        $res   = $model->login($username.$password);
        if ($res= = = 1) {return [
                'code'= >'404'.'message'= >'User does not exist'
            ];
        }
        if ($res= = = 2) {return [
                'code'= >'400'.'message'= >'Password error']; } code... }}Copy the code

We use the try… catch… After the rewrite:

class UserModel
{
    public function login($username = ' '.$password = ' ')
    {
        code...
        if(...). {// the user does not throw new Exception('User does not exist'.'404');
        }
        code...
        if(...). {// password error throw new Exception('Password error'.'400');
        }
        code...
    }
}

class UserController
{
    public function login($username = ' '.$password = ' ')
    {
        try {
            $model = new UserModel();
            $res   = $model->login($username.$password); // If necessary, we can commit the database transaction here //$db->commit();
        } catch (Exception $e) {// we can rollback database transactions here if necessary$db->rollback();
            return [
                'code'= >$e->getCode(),
                'message'= >$e->getMessage()
            ]
        }
    }
}
Copy the code

By using try… catch… To make our code logic clearer, try… In, we only need to pay attention to normal business conditions, and the processing of exceptions is unified in catch. So, when we’re writing upstream code, we just throw exceptions.

Using anonymous functions

** Builds a block of code inside a function or method **

If we have a piece of logic that we need to format data in a function or method, but the code fragment that formats the data appears several times, if we write directly, we might think like this:

function doSomething(...) {... // Format code snippets... . // Format code snippets [duplicate code]... }Copy the code

I’m sure most people don’t write like this, they probably write like this:

function doSomething(...) {... format(...) ; . format(...) ; . } // Declare a function or method that formats the codefunction format() {// Format code snippet... }Copy the code

There is nothing wrong with writing this way, minimising our code snippets, but what if the format function or method is just used by doSomething? I usually write it like this. Why? Because I think format is a subset of doSomething in this context.

function doSomething() {...$package = function(...). use (...) {  // We can also use the following argument to pass the reference // format the code segment... }; . package(...) ; . package(...) ; . }Copy the code

Implement lazy loading of classes and least know principles for implementing design patterns

Suppose you have the following code:

class One
{
    private $instance; // Class One internally relies on class Two // the least known principle public that does not conform to the design patternfunction __construct()
    {  
        $this->intance = new Two();
    }

    public function doSomething()
    {
        if(...). {// Call the instance method of class Two if something happens$this->instance->do(...). ; }... }}...$instance = new One();
$instance->doSomething(); .Copy the code

What’s wrong with the way I wrote it?

  • Not conforming to the least-know principle of design patterns, class One internally relies directly on class Two
  • An instance of class Two is not used in all contexts, so it is a waste of resources. Some people say to make a singleton, but it cannot solve the embarrassment of not using the instantiation

So let’s use anonymous functions to solve the above problem. Let’s rewrite it like this:

class One
{
    private $closure;

    public function __construct(Closure $closure)
    {  
        $this->closure = $closure;
    }

    public function doSomething()
    {
        if(...). {// implement lazy loading$instance = $this->closure();
            $instance->do(...). }... }}...$instance = new One(function() {// Class One externally depends on class Tworeturn new Two();
});
$instance->doSomething(); .Copy the code

To reduce the if… else… The use of

If you run into this type of code, it’s a black hole.

function doSomething() {
    if(...). {if(...). {... } esle { ... }}else {
        if(...). {... } esle { ... }}}Copy the code

** Early return exception **

If you’re careful, you might notice that the above case, probably most of the else code is handling an exception, or more likely the exception code is extremely simple, and I usually do this:

//  If I were in a function I would deal with the exception first, and then advancereturnCode, and then normal logicfunction doSomething() {
    if(...). {// Abnormal conditionreturn. ; }if(...). {// Abnormal conditionreturn. ; } //  Normal logic... } //  Similarly, if I were in a class I would handle the exception first and then throw the exception class One {publicfunction doSomething()
    {
        if(...). {// throw new Exception(...) ; }if(...). {// throw new Exception(...) ; } //  Normal logic... }}Copy the code

** Associative arrays do map **

If we are making a decision on the client side, we usually decide which strategy to choose in different context, usually using if or switch as follows:

class One
{
    public function doSomething()
    {
        if(...). {$instance= new A(); }  elseif (...) {$instance = new A();
        } else {
            $instance = new C();
        }
        $instance->doSomething(...) ; . }}Copy the code

The above version usually has a large number of if or switch statements, and I usually use a map to map the different policies, like the following:

class One
{
    private $map = [
        'a'= >'namespace\A'// Add namespaces because variables are dynamic'b'= >'namespace\B'.'c'= >'namespace\C'
    ];
    public function doSomething() {...$instance = new $this->map[$strategy]; //$strategyis'a'or'b'or'c'
        $instance->doSomething(...) ; . }}Copy the code

Using an interface

Why use interfaces? For example, to design a preferential system, different commodities just have different preferential behaviors under different preferential strategies. We define an interface of preferential behaviors, and finally program the interface. The pseudo-code is as follows

Interface Promotion
{
    public functionpromote(...) ; } class OnePromotion implement Promotion { publicfunction doSomething(...) {... } } class TwoPromotion implement Promotion { publicfunction doSomething(...) {... }}Copy the code

The controller rejects direct DB operations

The last thing I want to say is never to operate DB directly in your Controller, why? Most of the operations of our program are basically add, delete, change and search, maybe because the query where conditions and fields are different, so sometimes we can abstract the method of add, delete, change and search to the database into the model, and expose our WHERE and fields conditions through parameters. Often this can greatly improve efficiency and code reuse. For example:

class DemoModel implement Model
{
    public function getMultiDate($where= [].$fields = ['id'].$orderby = 'id asc')
    {
        $this->where($where)
             ->field($fields)
             ->orderby($orderby) ->get(); }}Copy the code

The last

If there is something wrong, please correct it