Code Style Guidelines

Spacing Consistency

Avoid skimping on spaces within your code. For instance:

$amount=5;

if(amount>5){
}

foreach($rows as $key=>$value){
}
Instead, adhere to consistent spacing like this:

$amount = 5;

if (amount > 5) {
}

foreach ($rows as $key => $value) {
}

Clarity in Naming

Refuse to abbreviate method names, ensuring clarity and understanding:

public function isBtnExists($btn)
{
   ...
}

Prefer clear and descriptive names:

public function isButtonExists($buttonName)
{
   ...
}

Array Formatting for Readability

The main goal is the readability of the code, and everything else is secondary. Prioritize code readability when defining arrays:

$tabSettings = array (
                        "templateFolder"  => "/templates/",
                        "keyInGetArray" =>  $this->keyInGetArray);

Format arrays for clear visibility:

$tabSettings = array(
   "templateFolder" => "/templates/",
   "keyInGetArray"  => $this->keyInGetArray
);

Consistent Naming Conventions

Adopt "camel" casing for variables and attributes:

$productAmount
$productIDsToUserID

Avoid numerical characters in names; spell out numbers instead:

$usersToType
$modelForProduct

Method Names in Classes

Follow "camel" casing for method names:

getUsers

It is also often the case that many duplicate the class name in the method name, the main question is why? If your class is named UserSearch, then there is no need to name a method updateUserSearchData in it; the method should have been named updateData.

Always specify the isolation level even if the method is public:

public function setSomething()
{
   ...
}

There are also certain rules for forming method names depending on their purpose.

Standardized Method Naming Conventions

Methods that should handle AJAX requests should start with the prefix onAjax: For example: onAjaxUpdateContactForm, onAjaxProductInformation.

Methods that display XML or JSON data should have a suffix Xml, Json. For example: displayUserCartXml, displayInventoryJson.

And there can be combined options, for example, when we send an AJAX request and expect a JSON response: onAjaxCartListJson.

Methods for Displaying Information

Prefix display or onDisplay for methods displaying content:

displaySignupForm
displayMainPage
onDisplayDashboard

Methods Returning HTML Fragments

Begin fetch for methods returning HTML:

fetchUserCart

Request Processing Methods

These are, for example, form handlers and similar methods, such methods should start with the prefix do. If the method handler displays information, then the prefix contains its type, for example, doAjax, doJson, doXml.

doAjaxUpdateAdForm
doJsonGroupRemoveAds
doChangeStatus

Ensure consistency in response handling:

$response = new Response(Response::JSON);
$response->status = false;
$response->errors = $this->errorMessages;
$response->send();

It is important to adhere to the construction of the names of these methods, as the correct handling of Exceptions is determined by them.

Methods Perform Logical Checks

The return result of their call is Boolean (true / false) in most cases. Such methods should start with the prefix is or has, for example:

hasUserEmail
isProductExists

Getter and Setter Methods

In our projects, the use of standard getter and setter methods accepted in PHP is not welcome. All methods that either return the state of an entity or modify it should start with the prefix get or set. For example:

getContent
setUserEmail
getSystemErrors

Callbacks and Event Handlers.

If the method is an event handler, its name should start with the prefix on:

public function onUserSignIn()
{
   ...
}

The same applies to hooks and similar stuff:

add_action('admin_head', array(&$this, 'onAdminHeadAction'));

Private Methods and Attributes.

Prefix private methods and attributes with an underscore::

$_productsInCart

private function _getUserOrder()
{

}

Code Block Formatting

The bodies of methods always start on a new line, and all operators start from the current line, for example:

public function hasErrors()
{
 ...
}

if ($this->hasErrors()) {
  ...
} else if ($this->isWork()) {
  ...
} else {
  ...
}

If Statement Formatting

Writing ifs in the format:

if ($a != 5 && $a > $b || $c > 7) 
is forbidden.

One of the most important rules is the readability of the code, and the example above will only be understood by the programmer who wrote it, and even then only for a few days. All such conditions should be moved to a separate method and have a logical name, for example:

if ($this->isAllUnitHasWappon($units))

If the method name is too long, you can do this:

$result = $this->hasExperienceIntoUserInventory();

if ($result) {

}

Global Usage

Using global is prohibited, meaning you cannot write:

global $jimbo;

You might say that you encounter such entries in the code, and that's true - it used to be used all the time, but now it is FORBIDDEN! About what to replace $jimbo with is described in the Working with Core section.

Returning a List of Errors From a Method

If your method of checking or processing has logic for returning a list of errors, then you need to pass an array (ArrayObject) by reference and write errors into it. If the method does not contain a return logic, then you should return a boolean. For example:

protected function doUpdateProfile(array $formInfo, array $values, ArrayObject &$errors): bool
{
    ...

    return true;
}

80-character Rule in One Line in IDE.

There is one wonderful rule, invented during the Fortran era, that one line in the IDE should not exceed 80 characters. This rule is good for improper use of one-line ifs, nested loops, large methods.

Closing php Tag

In files with php extensions, if they are not templates, you do not need to close the tag

<?php

Naming Files

One class one file. That is, if there is a class User, it should be in a separate file and named User.php.

If your file is application-level (templates, css, images), it should be named: update_user_data.php

Errors in Return Statements.

Often many write such code:

$result = fwrite($file, $content);

if ($result) {
    return true;
}

return false;

What differs the above code from:

$result = fwrite($file, $content);

return !empty($result);

Working With Files

Often we see such code:

$file = fopen($filePath, "w");

$result = fwrite($file, $content);

fclose($file);

if ($result) {
    return true;
}

return false;

In this example, there are at least 3 errors:

  1. If you use functions that work with files, streams, sockets, you always need to check the result of their opening:
$file = fopen($filePath, "w");

if ($file === false) {
    throw new Exception("Permission denied: ". $filePath);
}
  1. The code should be stable or predictable, not introduce chaos in the search for bugs, meaning you need to check the correct execution of function calls, in this case fwrite:
$result = fwrite($file, $content);

if ($result === false) {
    throw new Exception("Write error");
}
  1. There is no need to reinvent the wheel, and it would be easier to write the code above like this:
$result = file_put_contents($filePath, $content);

return $result !== false;

That is, if you write the code as in point 3, you deprive yourself of unnecessary hassle with checks, and the argument that fopen is faster than file_put_contents always brings a smile.

Class Initialization

When initializing classes, it's necessary to include (), like this:

$page = new FaqPage();

Default Parameter Values

Rarely is it necessary to pass an empty string as a default value. In most cases, the default value should be false or null, depending on the parameter type. If it's a primitive type, use false; if it's an object, use null.

In 95% of cases, avoid writing like this:

public function displayInfo(string $key, array $options, string $parameter = ''): bool
{
    ...

    return true;
}

Instead, use:

public function displayInfo(string $key, array $options, ?string $parameter = null): bool
{
    ...

    return true;
}

Abbreviating Parameter Array Names

Avoid using different abbreviations for parameter arrays within the same project. The most common aliases are:

  • attrs
  • args
  • params
  • parameters
  • arguments
  • options

The first two should generally be avoided altogether. Parameters is unnecessary, as params is more suitable for passing parameter arrays. Arguments should be avoided, as it usually refers to parameters passed when launching the system. Options typically refers to an array of parameters that affect the behavior of an object, method, or system as a whole, but it is not simply parameters.

Spaces in Expressions

Use spaces between arithmetic operations but not for string concatenation.

$a = $b + $c;
$strOne = $strTwo.'append'.$d;

Naming Templates

In Progress

Naming Variables That Load Entity Data

This naming rule applies to data associated with entities, not collections (arrays of entities).

When fetching data, name variables correctly for immediate understanding of their contents. The main naming formats are:

$productData
$productInfo
$productValuesObject
$productValues

Understand when and what to name:

Data - means that the data is loaded in its raw form as it exists in the database or storage;

$productData = $this->object->get($idProduct);
$productData = $this->plugin->products->load($idProduct);

Info - when data has been loaded with additional information or has been formatted according to some business logic;

$productInfo = $this->plugin->products->getInfo($idProduct);

...

public function getInfo(int $idProduct): array
{
    $info = $this->object->get($idProduct);
    $info['coupon'] = $this->object->getCoupon($idProduct);
    $info['owner'] = $this->plugin->user->load($info['id_owner']);

    return $info;
}

ValuesObject - when you have a ValuesObject object through which you obtain data;

$productData = $this->object->get($idProduct);
$productValuesObject = new ProductValuesObject($productData);
...
$productValuesObject = $this->plugin->products->loadValuesObject($idProduct);

Values - this is an array that is formed for updating or adding data to the database;

$productValues = array(
    'id_owner' => $this->getUserID(),
    'price'  => $price
    ...
);

$this->object->add($productValues);

This rule also applies to simple variables: $info, $data, $values, $objectValues

Writing Calls with Long Method Names and Multiple Parameters

If a method or function call is longer than 80 characters, for example:

update_post_meta($idPost, WooCommerceProductValuesObject::PRODUCT_REGULAR_PRICE_KEY, '1');

write it like this:

update_post_meta(
    $idPost, 
    WooCommerceProductValuesObject::PRODUCT_REGULAR_PRICE_KEY, 
    '1'
);

Naming Variables and Methods with IDs

All variables containing ID indications should be prefixed with id:

$idUser = $this->getUserID();
$idProduct = $this->_getProductID();

If there is an indication that this array contains IDs, then move ID to the postfix by adding the letter "s":

$usersIDs = $this->loadAllUsersIDs();
$productsIDs = $this->_getProductIDs();

In method names, we move ID to the postfix:

$this->getUserID();
$this->setUserID($idUser);

$this->getUserIDs();
$this->setUserIDs(...);

Using Acronyms in SQL Queries

Avoid using abbreviations for table names to avoid confusing logic and complicating code readability.

Incorrect:

$sql = "SELECT 
               id_account, from_label, from_name, from_company_name, 
               from_phone_number, from_street, from_city, from_state, 
               from_zip, carrier_name
        FROM company_settings
            JOIN carrier_addresses as cd
                ON cd.ident = company_settings.id_carrier
            JOIN companies as co
                ON co.ident = company_settings.id_company 
            JOIN carrier_types as ct
                ON ct.ident = company_settings.id_carrier_type";

Correct:

$sql = "SELECT 
               id_account, from_label, from_name, from_company_name, 
               from_phone_number, from_street, from_city, from_state, 
               from_zip, carrier_name
        FROM company_settings
            JOIN carrier_addresses 
                ON (carrier_addresses.ident = company_settings.id_carrier)
            JOIN companies
                ON (companies.ident = company_settings.id_company)
            JOIN carrier_types
                ON (carrier_types.ident = company_settings.id_carrier_type)";

Methods That Perform Checks

Methods that perform checks should have a prefix verify, for example:

public function verifyUserPermission(UserValuesObject $userValuesObject):void
{
  ...
}

Methods That Prepare Data by Reference

Methods that prepare, format, or supplement data by reference should have a prefix prepare:

private function _prepareTimesheetRow(array &$row): void
{
  ...
}

Event Class

We adhere to the practice of using constructors instead of associative arrays in events. Below is an example of our recommended approach:


class BeforeLoadProductEvent
{
    public const TYPE = 'before_load_product';

    protected IProduct $product;

    public function __construct(IProduct $product) 
    {
        $this->product = $product;

        parent::__construct(static::TYPE);
    }

    public function &getProduct(): IProduct
    {
        return $this->product;
    }
}

Separate Actions

When writing code, it's important to split actions onto separate lines. This practice enhances readability and simplifies debugging and error logging.

Avoid combining actions on a single line:

$result[] = (new MysqlColumnConvertor($columnOptions))->convert();

Instead, split actions onto separate lines:

$convertor = new MysqlColumnConvertor($columnOptions);

$result[] = $convertor->convert();

By separating the instantiation and method call, you make the code easier to read and debug. If an error occurs, it's clearer whether the issue lies in the object creation or the method execution.

Plugin Structure and Naming Conventions

Our projects utilize a plugin-based structure. Plugins are organized in separate folders within the main plugins directory. Each plugin may contain a domain folder where different logical components are placed in their respective subfolders.

Folder and Namespace Naming

When naming folders and namespaces within the plugin structure, adhere to the following guidelines:

  1. Plugin folders: Use camelCase for the main plugin folder names.
  2. Subfolders within plugins: Use a single word in all lowercase letters.
  3. Namespaces: Use the same naming convention as the corresponding folders.

Example folder structure:

...
- plugins
    - HumanResources
        - domain
            - event
            - listener
            - model
            - store
        - static
            - css
            - js
        - templates

Corresponding namespace structure:

namespace plugin\HumanResources\domain\event;
namespace plugin\HumanResources\domain\listener;
namespace plugin\HumanResources\domain\model;
namespace plugin\HumanResources\domain\store;

Conclusion

And remember the most important rule when writing code: cleanliness is not where you clean up, but where you don't litter!