PSR + Experience = PHP code convention

maksimepikhin

Maksim N Epikhin (PIKHTA)

Posted on December 15, 2020

PSR + Experience = PHP code convention

The following points were formed by analyzing the existing approaches of companies and personal experience.

File and folder naming rules

All names for folders and files should be meaningful and self-explanatory (not requiring additional clarification).

Folders

All folders are named in lowercase, word-delimited using the - (minus) character.

If the folder contains classes that belong to the namespace, then the folder is named according to the name of the namespace.

Allowed characters for naming folders: Latin letters and the - (minus) symbol.

Files

All files related to the project are named in lowercase word-delimited using the - (minus) character.

If the file is a class file, it is named according to the class name.

Rules for naming namespaces, classes, methods and variables

All names should be meaningful and self-explanatory (not requiring additional clarification).

Namespaces

Namespace names must be in lowercase and consist of one word. If it is necessary to name namespaces for more than one word, it is split into constituent parts, which are nested namespaces.

Classes

Class names must match PascalCase. In PascalCase, each word starts with a capital letter.

Traits are postfixed with Trait. Interfaces are postfixed with Interface. Abstract classes are prefixed with Abstract.

Methods

Method names must match camelCase. camelCase must start with a lowercase letter, and the first letter of each subsequent word must be capitalized. All words are written together

The following rules apply to method names:

  1. The name of the method should convey the intentions of the programmer
  2. The name of the method should tell why this method exists, that it does and how it is used (isPostRequest, getRequestType, parseSchemaElement, renderPageWithSetupsAndTeardowns)
  3. The name of the method should not be short
  4. The name of the method must begin with a verb
  5. Names of boolean methods must contain the verb is, has or can

Variables

Variable names must match camelCase. camelCase must start with a lowercase letter, and the first letter of each subsequent word must be capitalized. All words are written together

Constants must match UPPER_CASE_SNAKE_CASE. In UPPER_CASE_SNAKE_CASE, all words are written in capital letters, and spaces are replaced with underscores.

The following rules apply to variable names:

  1. The name of the variable should convey the intentions of the programmer
  2. The name of the variable should tell you why this variable exists, what it does and how it is used
  3. The name of the variable should not be short
  4. The name of the variable must not use the data type. The exception is Map ($typesMap, $statesMap), because otherwise, it cannot be distinguished from an array with data.
  5. If a variable stores a trait, then it must be included in the name (unpaidProject)
  6. Variables reflecting the properties of the object must include the name of the object (userIsAdmin, messageIsSend, figureCanBePainted, projectName)
  7. Variables and properties of an object must be nouns and named so that they are read correctly when used, and not when initialized

Poorly:

$object->expire_at
$object->setExpireAt($date);
$object->getExpireAt();
Enter fullscreen mode Exit fullscreen mode

Good:

$ object->expiration_date;
$ object->setExpirationDate($date);
$ object->getExpirationDate();
Enter fullscreen mode Exit fullscreen mode
  1. Names of boolean variables must contain the verb is, has or can
  2. Negative logical names are prohibited

Poorly:

if ($project->isInvalid()) {
    // ...
}
if ($ project->isNotValid()) {
    // ...
}
if ($accessManager->isAccessDenied()) {
    // ...
}
Enter fullscreen mode Exit fullscreen mode

Good:

if (!$project->isValid()) {
    // ...
}
if (!$accessManager->isAccessAllowed()) {
    // ...
}
if ($accessManager->canAccess()) {
    // ...
}
Enter fullscreen mode Exit fullscreen mode
  1. If there are properties (clause 8 and similar), the order of the variable name consists of: the name of the object in relation to which the variable is used), the property and the continuation of the variable name (userHasRoleAdmin, statusIsActive)

Code design rules

First of all, the namespace is put, which is used (if any). Next, the constructs of using classes (use) are written. In case of using several
Classes of the same namespace are grouped using the {...} construction. Next comes the class declaration.

Curly braces appear on the same line and are separated by a space.

Round brackets:

  1. Inside are not separated by a space.
  2. Outside separated by spaces control structures
  3. There is no space after the method / function name.

Each variable must be declared on a new line.

Conditions and service calls of methods are separated by line breaks, variables and work with them by line breaks are not separated.

There is no additional line break inside conditions and loops.

The content of the class is delimited at the top by one blank line.

    class InterfaceType {

        private $property = 'myProp';

        public function getProperty(): string {
            return $ this-> property;
        }
    }
Enter fullscreen mode Exit fullscreen mode

The return value (return) must be preceded by a line break if the method does not consist of a single line.

If the condition is large, then be sure to select it in one or more semantic expressions and use it (them) in the condition.

Poorly:

 if (IS_REGISTRATOR () && (($params.status === 'W' || $params.status === 'D' || $params.status === 'A') && $params.remark && ( ($params.subres_level == 0 && ($user_info->selected_title->tid == $params.boss || $user_info->selected_title->tid == $doc_signer_tid ||!$params.usertid) || $params.subres_level > 0 && $user_info->selected_title->tid == $params.usertid))) {...}
Enter fullscreen mode Exit fullscreen mode

Good:

$docIsInWorkAcceptOrDraft = ...;
$bossHasSignerPriviledge = ...;
$userCanSign = ...;

if ($docIsInWorkAcceptOrDraft && $bossHasSignerPriviledge && $userCanSign) {
  // ...
}
Enter fullscreen mode Exit fullscreen mode

Code commenting

In general, comments are prohibited (NOT "always"). Any piece of code that needs to be highlighted or commented out should be placed in a separate method.

Comments should be placed before the declaration of classes, variables and methods and should be formatted in accordance with PHPDoc. The comment before the class should describe the business logic and reflect its purpose with examples of use.

Single-line comments are denoted by //, and multi-line /*...*/.

Finished algorithms taken from an external source must be marked with a link to the source.

Rules for writing code

Where it makes sense, declare (strict_types = 1);

Functions / methods use null instead of missing scalar value. 0 and an empty string cannot be used as an indicator of a missing value.

function sendEmail(string $title, string $message = null, string $date = null): void {
    // ...
}

// message was not sent
$object->sendEmail('Title', null, '2017-01-01');

// an empty message was sent
$object->sendEmail('Title', '', '2017-01-01');
Enter fullscreen mode Exit fullscreen mode

You cannot change the variables that are passed to the method as input (exception - if this variable is an object).

You cannot assign the same value to several variables. To check if a key exists in an associative array, use array_key_exists, not isset. You cannot mix string and numeric keys in an array. Associative arrays cannot be sorted.

Lines are surrounded by single quotes. Double quotes are only used if:

  1. There must be single quotes inside the string
  2. Variable substitution is used inside the string
  3. Specials are used inside the line. characters \n, \r, \t, etc.

Instead of unnecessary concatenation, we use variable substitution in double quotes

Methods should use as much typing as possible, including the return type (: type). All parameters and their types must be described in the method declaration or in PHPDoc. Name methods that start with check and validate should throw exceptions and not return values.

All class methods should be private by default. If the method is used by the heirs of the class, then it is declared protected. If used by third party classes then public.

If the method can return null, then it is desirable to implement the Null object design pattern, or throw an exception, or return a special case object (example: an empty array).

When returning from a data method of the json type - it is unacceptable to writereturn true, always use the construction return ['success' => ['message' => '.....']] or ['error' => ['message' => '.....']] . message is given as an example, you can use any keys in an unlimited number.

The method must clearly distinguish between normal and exceptional situations.

By default, exception texts should not be shown to the user. They are meant for logging and debugging. The exception text can be shown to the user if it is explicitly intended to do so.

In a conditional statement, only the boolean value must be checked. When comparing non-boolean variables, strict comparison with type casting (===) is used, automatic casting and loose comparison are not used.

Poorly:

if ($user) {
     // ...
}
if ($request->postData('amount') == 100) {
     // ...
}
if (!$request->postData('amount')) {
     // ...
}
Enter fullscreen mode Exit fullscreen mode

Good:

if ($user === null) {// $user is of type object
     // ...
}
if ((int)$request->postData('amount') === 100) {
     // ...
}
if ($booking->comment === '') {
     // ...
}
Enter fullscreen mode Exit fullscreen mode

When using the AND and OR operators in a conditional expression at the same time, be sure to prioritize parentheses.

💖 💪 🙅 🚩
maksimepikhin
Maksim N Epikhin (PIKHTA)

Posted on December 15, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related