This specification is based on PSR and actual project experience, which has been implemented in the company and shared as follows.

It is divided into two parts: coding format and programming.

Encoding format

Based on PSR-1, PSR-2, and PSR-12.

Sample:

<? php /** * this is a example class */declare(strict_types=1);

namespace Vendor\Package;

use Vendor\Package\{ClassA as A, ClassB, ClassC as C};
use Vendor\Package\SomeNamespace\ClassD as D;

use function Vendor\Package\{functionA, functionB, functionC};

use const Vendor\Package\{ConstantA, ConstantB, ConstantC};

class Foo extends Bar implements FooInterface
{
    public function sampleFunction(int $a, int $b = null): array
    {
        if ($a= = =$b) {
            bar();
        } elseif ($a > $b) {
            $foo->bar($arg1);
        } else {
            BazClass::bar($arg2.$arg3);
        }
    }

    final public static function bar()
    {
        // method body
    }
}
Copy the code

File:

  • The PHP codeMust beuse<? php ? >Tag, without the closing tag if it is pure PHP code? >;
  • Encoding: PHP code files must be encoded in UTF-8 without BOM (please search for BOM and any problems in PHP);
  • <? php,declare,namespace,useblockMust beWrite in order and must be followed by a blank line;
  • useBlocks: classes, functions (use function), constant (use const)useWrite in this order and there must be an empty line between each block;

Line:

  • Each line should contain no more than 80 characters. Lines larger than 80 characters should be broken into multiple lines.
  • There must be no extra Spaces after a non-blank line.
  • There must be no more than one statement per line

Indentation:

  • Code must be indent with 4 Spaces (please set IDE to Tab to 4 Spaces);

Key words:

  • PHP keywords must be lowercase and use abbreviations (such as bool instead of Boolean);

Name:

  • Class names must conform to the hump rule of capitalizing the first letter;
  • Methods and functions must be named with the hump rule of lowercase letters;
  • Constants must be named in all uppercase letters separated by an underscore;
  • Methods and properties should not have leading underscores to indicate their accessibility, but should use the appropriate access modifiers;
  • The names of classes, methods, and properties should reflect their meaning,banUsing techniques such as$a,$dddSuch meaningless naming;
  • Business concept naming should be given priority, and pure technical naming should be avoided as far as possible. For example, sendCoupon is a business term, and createUserCoupon is a pure technical term.
  • On the premise of a clear concept, the naming should be as concise as possible and unnecessary words should be avoided. $orders and getOrders are better names than $orderList and ajaxGetOrderList. UserCoupon::send() is superior to UserCoupon::sendCoupon(). The former expresses its meaning exactly, while the latter needlessly repeats the word Coupon;
  • Should not beUse generic variable names. Instead, use specific names to enhance readability. As relative to use$list.$usersMore contextual, easier to understand and maintain;
  • Do not use non-generic abbreviations that cause difficulty in understanding;
  • Avoid prefixes and suffixes for purely technical elements, such as ajaxGetOrders (as an interface, it is not necessary and should not restrict consumers to using Ajax);
  • $orders instead of $orderList should be used.

Namespaces and classes:

  • Namespace and class names must conform to PSR-4;
  • Only one class is defined per file;
  • Class naming: capital hump rule;
  • Do not put classes in a top-level namespace, use at least one level of namespace (some special frameworks or historical projects do not adhere to this rule);
  • Create the class:$cls = new MyClass();Parentheses are used with or without arguments;
  • traits: use traits: must be placed one line below the class opening brace, eachtraitA single line, with its ownuse. Use traits block should be followed by a blank line;

Ex. :

class ClassName extends ParentClass implements \ArrayAccess, \Countable { // constants, properties, methods } class ClassName extends ParentClass implements \ArrayAccess, \Countable, \Serializable { // constants, properties, methods } class ClassName { use FirstTrait; use SecondTrait; use ThirdTrait; } class Talker { use A, B, C { B::smallTalk insteadof A; A::bigTalk insteadof C; C::mediumTalk as FooBar; }}Copy the code

Class constants, attributes, and methods:

  • Constants: all letters in uppercase, separated by underscores, e.gORDER_TYPE;
  • Properties:
    • Lowercase hump naming, e.g$order;
    • Must beAccess modifier is used and cannot be usedvarModify attributes;
    • Do not use underscores to distinguish accessibility;
  • Methods:
    • A lowercase hump, as insubmitOrder;
    • Access modifiers must be used;
    • Do not use underscores to distinguish accessibility;
    • Method names must not have Spaces after them;
    • In the argument list, each comma must be followed by a space, and the comma must not be preceded by a space.
    • Argument lists can be separated into multiple lines. If so, each argument, including the first argument, must be on a separate line, and the closing brace and method opening brace must be on the same line, separated by a space.

Ex. :

	class ClassName
	{
		private $name ='lisi';

		public function aVeryLongMethodName(
	        	ClassTypeHint $arg1,
	        	&$arg2,
	        	array $arg3= []) {// the contents of the method}}Copy the code

Use of modifiers:

  • abstractfinalThe statement,Must beBefore the access modifier;
  • static Must beWrite after;

Ex. :

abstract class ClassName
{
    protected static $foo;

    abstract protected function zim();

    final public static function bar()
    {
        // method body
    }
}
Copy the code

Method and function calls:

  • When a method or function is called, there must be no space between the method name or function name and the parameter opening parenthesis, and there must be no space before the parameter closing parenthesis. Each parameter must not be preceded by a space, but must be followed by a space.
  • Arguments can be separated into multiple lines. In this case, each parameter including the first parameter must be in a separate line.

Ex. :

bar();
$foo->bar($arg1);
Foo::bar($arg2.$arg3);
$foo->bar(
    $longArgument.$longerArgument.$muchLongerArgument
);
Copy the code

Control structure:

  • Control structure keywords must be followed by a space;
  • Open parenthesis (must not have Spaces after it;
  • There must also be no Spaces before the closing parenthesis.
  • There must be a space between the closing brace and the opening brace {;
  • The body of the structure must have an indent;
  • Closing curly braces} must be a separate line after the body of the structure;
  • The body of each structure must be enclosed in a pair of curly braces, even if there is only one statement;
  • Use keywordselseifInstead ofelse if;
  • If: there are too many conditions in if, but each condition can be on a single line. The first condition must be on a separate line.
  • switch: caseStatements must be relativeswitchI’m going to indent, andbreakStatements andcaseAll other statements in thecaseMake an indent;

Ex. :

if ($expr1{/ /if body
} elseif ($expr2) {
    // elseif body
} else {
    // else body;
}

if (
    $expr1
    && $expr2{/ /if body
} elseif (
    $expr3
    && $expr4
) {
    // elseif body
}

switch ($expr) {
    caseZero:echo 'First case, with a break';
        break;
    case 1:
        echo 'Second case, which falls through';
        // no break
    case 2:
    case 3:
    case 4:
        echo 'Third case, return instead of break';
        return;
    default:
        echo 'Default case';
        break;
}

while ($expr) {
    // structure body
}

for ($i = 0; $iThe < 10;$i+ +) {/ /for body
}

foreach ($iterable as $key= >$value) {
    // foreach body
}

try {
    // try body
} catch (FirstExceptionType $e) {
    // catch body
} catch (OtherExceptionType $e) {
    // catch body
}
Copy the code

Use of curly braces:

  • Classes and methods: The opening and closing curly braces must be separate lines, and there must be no empty lines before and after the opening curly braces.
  • Flow control statement: The opening curly brace is not a separate line, the closing curly brace is a separate line;
  • Any close parentheses}It must not be followed by comments or other statements;

Ex. :

class Foo extends Bar implements FooInterface
{
    public function sampleFunction($a.$b = null)
    {
        if ($a= = =$b) {
            bar();
        } elseif ($a > $b) {
            $foo->bar($arg1);
        } else {
            BazClass::bar($arg2.$arg3); }}}Copy the code

Operator:

  • All binary and ternary operators must be preceded by a space;
  • Unary operator!No Spaces after;

Ex. :

if ($a= = =$b) {
    $foo = $bar ?? $a ?? $b;
} elseif ($a > $b) {
    $variable = $foo ? 'foo' : 'bar';
}
Copy the code

Closure:

  • When a closure is declared, the keywordfunctionPostand keywordsuseBefore and afterMust beThere should be a space;
  • The opening brace must be on the same line as the declaration, and the closing brace must follow the line following the end of the body.
  • There must be no Spaces after and before the closing parentheses of argument and variable lists.
  • In argument and variable lists, there must be no space before and after commas.
  • Argument lists and variable lists can be divided into multiple lines, so that each argument or variable, including the first one, must be on its own line, and the closing bracket of the list must be on the same line as the opening curly bracket of the closure;

Ex. :

$closureWithArgs = function ($arg1.$arg2) {
    // body
};

$closureWithArgsAndVars = function ($arg1.$arg2) use ($var1.$var2) {
    // body
};

$noArgs_longVars = function () use (
    $longVar1.$longerVar2.$muchLongerVar3
) {
   // body
};

$longArgs_longVars = function (
    $longArgument.$longerArgument.$muchLongerArgument
) use (
    $longVar1.$longerVar2.$muchLongerVar3
) {
   // body
};

$foo->bar(
    $arg1.function ($arg2) use ($var1) {
        // body
    },
    $arg3
);
Copy the code

Code comments:

  • Classes, methods, and functions must be commented;
  • Classes and methods must use block-level comments, and code segments must use block-level or inline comments as appropriate.
  • Comments should include function descriptions, parameter lists, return types, exception throws;
  • Comment text with one and only one space between //;
  • More complex pieces of code should be properly commented;
  • Don’t make unnecessary comments, such as the following:
// If the user existsif ($user{/ /do something...
}
Copy the code

Programming section

Note: this specification does not consider the status of historical items, historical items may not meet in some places, you can decide whether to comply with the actual situation.

Exception:

  • Definition of exceptions: Exceptions that cause the process to fail to proceed normally or fail to obtain expected results are exceptions. For example, the divisor value is 0 and users are not found on the interface for obtaining user information.
  • Exceptions in code should be thrown and not returned as error codes (except for the outermost layer, such as the controller layer, which needs to convert the exception to a suitable format for output to the user or log). The principle of throwing an exception instead of returning an error code is as follows: Business logic is separated from error handling (non-business logic). The code that processes business logic only needs to throw an exception (tell the upper layer), and the upper layer may or may not handle the exception (directly send it to the upper layer).
  • Exceptions should contain explicit error codes and exception descriptions, where error codes should be uniformly defined in the project as constants rather than written down as numbers (readability, maintainability);
  • The controller layer must catch and handle exceptions appropriately and cannot continue to throw them up. The handling methods include but are not limited to returning appropriate error codes, recording logs, and sending alarm notifications.

Status code/Error code:

  • Numeric status codes should not be written directly in the program, but status code constants (or constants like) should be defined in a consistent place in the project;
  • Status code constants should conform to the specification described in the naming section;
  • A status Code should not be returned at the non-controller layer, but should be replaced with an appropriate exception (the status Code is represented in the exception instance’s Code accordingly);
  • Common status codes should not be used, and each error should define its own, unique status code;
  • The status codes should be planned at the project level. The status codes are allowed to be repeated for different projects. The same status code cannot be used for different status descriptions within a project, and vice versa.

Log:

  • In principle, logs should be recorded only at the application layer (such as services and controllers at the application layer) and not at the domain layer (service logic layer). However, this rule does not absolutely require logs to be recorded.
  • The log content includes but is not limited to request number, request details, response content, error platform, error description, and call stack.
  • In principle all exceptions should be logged and traceable;
  • It is recommended to log all external requests and API calls of the system for troubleshooting when exceptions occur.
  • The logging implementation should follow the PSR-3 logging interface specification;

Cache:

  • You should set up a front-end browser cache (nginx or other Web server) for static resources such as JS, HTML, CSS, image, etc.
  • Enable compression for JS, HTML, CSS resources (configure Nginx or other Web server);
  • Memory caches such as Redis and Memcache should be used for frequently accessed but less modified data.
  • The cached data should be updated/invalidated in time after updating;
  • Only hot data should be cached, with an appropriate cache duration. It is recommended that the back-end cache expire within 7 days.
  • You should not cache data with a large volume but not all of the thermal data;
  • Back-end caching should be implemented in accordance with the PSR-16 caching interface specification;

Database:

  • Table fields must be annotated in principle, except for well-known fields such as ID and IS_deleted;
  • Table fields cannot be ambiguous (One field indicates multiple service meanings. For example, in user login table, whether user_id is empty indicates whether a user is logged in. User_id indicates the user ID and login status. However, it needs to be distinguished that “polysemy” and “multi-value” are different. For example, the status field is used to store multiple states through multi-value and operation, where the meaning of status is still clear);
  • The design of the data table should be “straightforward” and should not impose implicit business logic on the fields. For example, if the user_id is null to indicate whether the user is logged in, there is implicit business logic, resulting in instability of the table structure (because at this time, the underlying storage structure depends on the business logic of the upper layer, and the upper layer is always more unstable than the lower layer).
  • When using strings to store JSON, you must carefully consider whether the fields are likely to be retrieved, and if so, this design can cause trouble;
  • You must create appropriate indexes for the tables based on the business situation, even if the current data volume is small (you must view the current situation dynamically, and just because it is small now does not mean it will be small later);
  • In principle, it is forbidden to write and then read the same data in a request to prevent data inconsistency in read/write separation. If this must be done, it is recommended to sleep 1-2 seconds after writing before reading;
  • Should not beuse*Query database field, should be clear field;
  • Table query: The association of more than four tables should be careful, and need to be audited by more than two members of the team;
  • Prohibit direct operation of non-system/project database, must call the relevant interface, for example, prohibit direct operation of ticket system database on wechat terminal;
  • Table fields: similar tolast_update_timeFields like thisMust beSet up theon update current_timestampEnsure updating;
  • Disallow remote calls in database transactions, which can lead to long transactions and database crashes under high concurrency. Solution: Either remove the transaction or take the remote call out of the transaction;

Controller:

  • Do not use static variables or methods in controllers. (completely unnecessary and problematic in frameworks such as easySwoole);
  • banIn the base classControllerWrite Action, the base class, inControllerYou can’t provide an API (otherwise any subclass will own the API and have no way of knowing which controllers are actually accessing the API);
  • Base class controllers can only provide convenience properties and internal convenience methods, as well as some pre – and post-processing logic, which should be protected;
  • Do not write a lot of service logic in a controller. Instead, put it in the logical layer to keep the controller layer simple.

The Session:

  • Sessions should only hold “Session” information, that is, the (public) information that must be used in the context of the Session; other information should be cached. For example, the basic information of the logon of merchant platform, the set of permissions they have, the layer they are currently in (group, oil station group, oil station) and other public information closely related to the login session;
  • Instead of using $_SESSION directly in the domain layer (business logic), you should provide what the method needs by passing parameters. In other words, use sessions only in the application layer (such as controllers) to prevent Session contamination;
  • Session addition and modification should be carried out in a unified place, such as successful login, logging out, switching merchant level, etc. It is forbidden to set Session arbitrarily in the business code.

API interface:

  • External API interfaces must have synchronous and detailed documentation. Currently, the interface documentation is uniformly written in ShowDoc.
  • Updates to API interfaces must ensure forward compatibility (unless callers can be identified and changes can be negotiated);
  • Write apis (add, update, delete) must ensure the idempotency of multiple calls (for example, multiple calls will not lead to the repeated addition of multiple data) to facilitate retry and manual compensation.
  • The data structures returned by the API must be consistent, including field, structure, and data type consistency. For example, a certain field cannot be missing in a certain case, and a certain field type is inconsistent in different cases.
  • All list requests must support paging unless, in theory, they cannot exceed 50 data;

Other:

  • Non-business domain code should not be written in the business logic layer, but should be isolated into infrastructure, local services, or third-party interfaces (remote services). For example, although sending an SMS verification code is part of the user registration process, the logic of sending an SMS verification code does not belong to the service domain of user registration, and should be removed.
  • Do not copy large chunks of code, should be reconstituted method or class;
  • A method or function should have no more than 120 lines and a class should have no more than 800 lines;
  • Use static methods with caution because they are not generally considered testable from a unit testing perspective;
  • Query methods should not have side effects (modify system state, database records, insert data, etc.), only return relevant data (that is, ensure that the query method is read-only);
  • The business model should not directly depend on the input parameters such as GET and POST, that is, the input parameters should not be directly thrown to the business model (or even directly inserted into the database). The business model should explicitly define its own parameters.
  • Function and method parameter design:
    • Method parameters should be self-explanatory, meaning that each parameter has a clear meaning;
    • A multi-parameter delivery strategy with clear meaning is preferred. If the number of parameters is too large, you can pass objects (DTO). Try not to pass arrays directly, because array elements are not self-explanatory or constraining and cannot be maintained, which is the next best thing.
    • Example: User login verification parameter transmission:
      • Recommendation:$login->verify($username, $password);Multi-parameter parameter transmission, with self-explanatory;
      • If there are too many parameters (for example, more than 7 parameters), use the object transfer mode:$login->verify(LoginDTO $loginDTO);Because objects are clearly defined and interpretive;
      • The very:$login->verify($params);No one knows what’s in the $params;
      • The bottom line:$login->verify($request->params()), just throw in all the browser input, how do you let future generations maintain?