Elegance is what PHPer is all about, so let’s learn the basics to make PHP code more elegant
Using a reference
Scenario 1: Iterate through an array to get a new data structure
You might write something like this:
$TMP = []; $TMP = []; Foreach ($arr as $k = > $v) {/ / remove the data you want $TMP [$k] [' youwant to] [' youwant to] = $v; . // get the data you want if (...) { $tmp[$k]['youwantbyjudge'] = 'TIGERB'; }... } / / finally to array $TMP you want -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- / / may you feel the above writing is not very good, Foreach ($arr as $k => $v) {$arr as $k => $v; {/ / facsimile value is what you want $arr [$k] [' youwantbyjudge] = 'TIGERB'}... Unset ($arr[$k]['youwantdel']); } // Finally we get our target array $arrCopy the code
Next we use the reference value:
Foreach ($arr as &$v) {// if (...) $v[' youWantByJudge '] = 'TIGERB'}... Unset ($v['youwantdel']); } unset($v); // Finally we get our target array $arrCopy 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 spring ($arr); $arr = $arr; The catch... Class UserModel {public function login($username = "", $password =" ") {code... if (...) {// The user does not have return-1; } code... if (...) {// Password error return-2; } code... } } class UserController { public function login($username = '', $password = '') { $model = new UserModel(); $res = $model->login($username, $password); If ($res = = = 1) {return [' code '= >' 404 ', 'message' = > 'user does not exist']. } the if ($res = = = 2) {return [' code '= >' 400 ', 'message' = > 'wrong password']. } code... }}Copy the code
We use the try… The catch… After the rewrite:
class UserModel { public function login($username = '', $password = '') { code... if (...) {// throw new Exception(' user does not exist ', '404'); } code... if (...) Throw new Exception(' password error ', '400'); } code... } } class UserController { public function login($username = '', $password = '') { try { $model = new UserModel(); $res = $model->login($username, $password); $db->commit(); $db->commit(); $db->rollback(); $db->rollback(); return [ 'code' => $e->getCode(), 'message' => $e->getMessage() ] } } }Copy the code
By using try… The 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(...) ; . } function format() {function format() {// Format the code segment... }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 down like this. Why? Because I think format is a subset of doSomething in this context.
function doSomething() { ... $package = function (...) use (...) {// Also use arguments can pass references // format code snippets... }; . package(...) ; . package(...) ; . }Copy the code
Implementing lazy loading for classes and least Know for design patterns
Suppose you have the following code:
class One { private $instance; Public function __construct() {$this-> int2 = new Two(); $this-> int2 = new Two(); } public function doSomething() { if (...) $this->instance->do($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 (...) $this->closure(); $this->closure(); $instance->do(...) }... }}... $instance = new One(function () {return new Two(); }); $instance->doSomething(); .Copy the code
To reduce the if… The 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:
Function doSomething() {if (...)} function doSomething() {if (... {// return... ; } if (...) {// return... ; } // Normal logic... } class One {public function doSomething() {if (...) {public function doSomething() {if (...)} {// throw new Exception(...) ; } if (...) {// throw new Exception(...) ; } // Normal logic... }}Copy the code
Associative arrays to 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\ b', 'c' => 'namespace\ c']; private $map = ['a' => 'namespace\ b', 'c' => 'namespace\ c'] public function doSomething() { ... $instance = new $this->map[$strategy]; / / $strategy is' 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 function promote(...) ; } class OnePromotion implement Promotion { public function doSomething(...) {... } } class TwoPromotion implement Promotion { public function 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