PSR + Experience = PHP code convention
Tags: php, webdev, convention
Published: 2020-12-15 22:14:04

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:**
  
```php
$object->expire_at
$object->setExpireAt($date);
$object->getExpireAt();
```

**Good:**
```php
$ object->expiration_date;
$ object->setExpirationDate($date);
$ object->getExpirationDate();
```

8. Names of `boolean` variables must contain the verb `is`, `has` or `can`
9. Negative logical names are prohibited

**Poorly:**
```php
if ($project->isInvalid()) {
    // ...
}
if ($ project->isNotValid()) {
    // ...
}
if ($accessManager->isAccessDenied()) {
    // ...
}
```

**Good:**
```php
if (!$project->isValid()) {
    // ...
}
if (!$accessManager->isAccessAllowed()) {
    // ...
}
if ($accessManager->canAccess()) {
    // ...
}
```

10. 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] (https://www.php.net/manual/ru/language.control-structures.php)**
   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.

 ```php
    class InterfaceType {
    
        private $property = 'myProp';
        
        public function getProperty(): string {
            return $ this-> property;
        }
    }
```

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:**
```php
 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))) {...}
```

**Good:**
```php
$docIsInWorkAcceptOrDraft = ...;
$bossHasSignerPriviledge = ...;
$userCanSign = ...;

if ($docIsInWorkAcceptOrDraft && $bossHasSignerPriviledge && $userCanSign) {
  // ...
}
```

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

```php
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');
```

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 write` return 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:
```php
if ($user) {
     // ...
}
if ($request->postData('amount') == 100) {
     // ...
}
if (!$request->postData('amount')) {
     // ...
}
```

Good:
```php
if ($user === null) {// $user is of type object
     // ...
}
if ((int)$request->postData('amount') === 100) {
     // ...
}
if ($booking->comment === '') {
     // ...
}
```

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