The Right Way of PHP Development in Venddor IO

Introduction

Before diving into coding for Venddor IO, make sure to thoroughly review "PHP The Right Way." As Uncle Bob mentioned at O'Reilly Commons, adopt the Boy Scouts' principle: "Always leave the campground cleaner than you found it." If you encounter any code that isn't in line with the best practices and standards provided here, take the initiative to improve it, even if it's not your code.

Standards to Follow

1. General Style Guidelines

2. Naming Conventions for Variables, Array Keys, and Class Properties

Right: $counter, $user_id, $product, $is_valid

Wrong: , $uid, $obj, $flag

For variables holding a list of similar objects, use the _list suffix like $products_list.

Right:

foreach ($applied_promotion_list as $applied_promotion) {
  // Code here is easy to distinguish
}

Wrong:

foreach ($applied_promotions as $applied_promotion) {
  // This might be confusing
}

Boolean variables should start with prefixes such as is_, has_, or other suitable verbs.

Right: $is_valid, $has_rendered, $has_children, $use_cache

Wrong: $valid, $render_flag, $parentness_status, $cache

Avoid starting variable names with an underscore. Instances like $cache, $_cache, $__cache have been problematic in the past.

3. Constants' Naming and Declaration

4. String Literals

5. Avoid Magic Numbers

6. Commenting

7. PHPDoc Guidelines

Here's the example of formatting done right:

/**
 * Generates date-time intervals of a given period for sales reports
 *
 * @param Timezone[] $timezone_list  List of timezones to be used
 * @param int        $interval_id    Sales reports interval ID
 * @param int        $timestamp_from Timestamp of report period beginning date
 * @param int        $timestamp_to   Timestamp of report period end date
 * @param int        $limit          Maximal number of the generated intervals.
 *                                   Also, this string is used to illustrate
 *                                   the wrapped and aligned long comment.
 *
 * @deprecated 4.4.1
 * @return array
 */

8. Performance Tips

Be cautious about using the Registry::get() method inside loops as it can impact performance.

9. Code Smells

Avoid excessive nesting or indentation in your code. If a function has more than 3 levels of indentation, consider refactoring.

Here are 2 examples:

<?php
function foobar($foo, $bar, $baz = null)
{
    if (!empty($foo['foo_bar'])) {
        $foo_bar = $foo['foo_bar'];
        if (!empty($bar) && $foo_bar > 10) {
            if (!empty($baz)) {
                // No actions even take place until this point.
            }
        }
    }
    return false;
}
<php
public static function filterPickupPoints($data, $service_params)
{
    $pickup_points = array();
    if (!empty($service_params['deliveries'])) {
        foreach ($data as $key => $delivery) {
            if (!empty($delivery['is_pickup_point']) && in_array($key, $service_params['deliveries'])) {
                foreach ($delivery['pickupPoints'] as $pickup) {
                    $pickup_points[$pickup['id']] = $pickup;
                }
            }
        }
    }
    return $pickup_points;
}

Data Types and Type Casting

PHP is a dynamically typed language, which means any declared variable can be assigned any data type. While this offers flexibility, it also introduces potential issues, which might result in unpredictable outcomes during code execution.

It's essential to understand the data type a variable should hold and structure your code accordingly. By doing so, you won't accidentally compare strings with integers or mix arrays with zeros.

By documenting the expected and returned data types using PHPDoc, you can provide clarity when you create a function or method. This ensures that you're always aware of the data type you're working with. Consequently, you can frequently employ the === strict comparison operator, making debugging easier for you and your peers.

In PHP 7, it's encouraged to use strict types for function return values and parameters.

Default Empty Values

You might often notice empty strings being used as default values. However, in PHP, there's a designated data type for that – null. Using 0 or an empty string can cause logical errors in your code. Therefore, always use null and the === operator for your conditions.

Clarity Over Inverted Conditions

Avoid using conditions like !empty($_REQUEST), as they can be hard to read, especially when part of intricate expressions. Only use inverted conditions if the alternative would make the code even more unclear.

Function Guidelines

Naming

Function names should be in lowercase and start either with fn_, or with db_, like fn_get_module_option_variants.

Arguments

When multiple arguments have default values or are secondary in importance, group them into a $params array. This streamlines the process by passing only the main arguments and the $params array.

DRY Principle (Don't Repeat Yourself)

If you find the same piece of code in multiple locations within a controller or function, it's a sign that it should be extracted into a separate function.

Returning Values is Beneficial

Avoid modifying the value of a passed variable by reference unless absolutely necessary. Instead, let the function return the modified value. This approach is more transparent and avoids unintended side-effects. Passing variables by reference also doesn't offer memory-saving benefits in PHP.

Single Exit Point for Functions

Ideally, a function should have just one exit point. Multiple exits are permissible if:

Comments on Deprecated Functions

If a function becomes outdated, it's crucial to mark it as deprecated to prevent its unintentional use.

Object-Oriented Programming (OOP)

Follow CamelCase for naming classes, interfaces, and traits. Ensure that your classes, interfaces, and traits belong to the appropriate namespace. In Vendor IO, the base namespace is Tygh.

Entity Naming Conventions

Correct: $a->getApiUrl(), $a = new Rest();, class ApiTest

Incorrect: $a->getAPIURL(), $a = new REST();, class APITest

Constants

The rules for naming constants within classes mirror those for external constants. Example:

class Api
{
    const DEFAULT_REQUEST_FORMAT = 'text/plain';
}

Properties

Methods

Namespaces

All core and module-related entities in Venddor io should be under the Tygh namespace. If entities relate to a particular functionality, they should have a shared sub-namespace, like block management classes (Tygh\BlockManager).

Files utilizing classes, interfaces, or traits should have a use directive at the start to specify the utilized namespaces. If class names from varying namespaces collide, use aliasing to resolve conflicts.

Each entity, be it class, interface, or trait, should reside in its distinct file. Although developers often break this rule by declaring both a class and an exception in the same file, it's best avoided.

Modules should confine their classes, interfaces, and traits to their dedicated namespace, \Tygh\Addons\ModuleName.

The use directives should be grouped collectively for clarity.

Design Patterns

Avoid crafting singleton classes and classes composed only of static methods. They pose challenges when writing unit tests.

SQL Queries

Formulate your queries adhering to specific standards:

General Guidelines

Using Exceptions

When encountering an error that hinders further program execution, Venddor io has exceptions to facilitate debugging.

To use an exception, summon it like this:

use Tygh\Exceptions\DeveloperException;
...