Variables

Meaningful

Use meaningful and pronounceable variable names

Bad:

PHP
$ymdstr = $moment->format('y-m-d');

Good:

PHP
$currentDate = $moment->format('y-m-d');

Vocabulary

Use the same vocabulary for the same type of variable

Bad:

PHP
getUserInfo();
getUserData();
getUserRecord();
getUserProfile();

Good:

PHP
getUser();

Searchable

We will read more code than we will ever write. It's important that the code we do write is readable and searchable. By not naming variables that end up being meaningful for understanding our program, we hurt our readers. Make your names searchable.

Bad:

PHP
// What the heck is 448 for?
$result = $serializer->serialize($data, 448);

Good:

PHP
$json = $serializer->serialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);

Bad:

PHP
class User
{
    // What the heck is 7 for?
    public $access = 7;
}

// What the heck is 4 for?
if ($user->access & 4) {
    // ...
}

// What's going on here?
$user->access ^= 2;

Good:

PHP
class User
{
    public const ACCESS_READ = 1;
    public const ACCESS_CREATE = 2;
    public const ACCESS_UPDATE = 4;
    public const ACCESS_DELETE = 8;

    // User as default can read, create and update something
    public $access = self::ACCESS_READ | self::ACCESS_CREATE | self::ACCESS_UPDATE;
}

if ($user->access & User::ACCESS_UPDATE) {
    // do edit ...
}

// Deny access rights to create something
$user->access ^= User::ACCESS_CREATE;

Explanatory

Use explanatory variables

Bad:

PHP
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/';
preg_match($cityZipCodeRegex, $address, $matches);

saveCityZipCode($matches[1], $matches[2]);

Not Bad:

It's better, but we are still heavily dependent on regex.

PHP
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/';
preg_match($cityZipCodeRegex, $address, $matches);

[, $city, $zipCode] = $matches;
saveCityZipCode($city, $zipCode);

Good:

PHP
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*(?<city>.+?)\s*(?<zipCode>\d{5})$/';
preg_match($cityZipCodeRegex, $address, $matches);

saveCityZipCode($matches['city'], $matches['zipCode']);

Nesting

Avoid nesting too deeply and return early (part 1) Too many if-else statements can make your code hard to follow. Explicit is better than implicit.

Bad:

PHP
function isShopOpen($day): bool
{
    if ($day) {
        if (is_string($day)) {
            $day = strtolower($day);
            if ($day === 'friday') {
                return true;
            } elseif ($day === 'saturday') {
                return true;
            } elseif ($day === 'sunday') {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    } else {
        return false;
    }
}

Good:

PHP
function isShopOpen(string $day): bool
{
    if (empty($day)) {
        return false;
    }

    $openingDays = [
        'friday', 'saturday', 'sunday'
    ];

    return in_array(strtolower($day), $openingDays, true);
}

Bad:

PHP
function fibonacci(int $n)
{
    if ($n < 50) {
        if ($n !== 0) {
            if ($n !== 1) {
                return fibonacci($n - 1) + fibonacci($n - 2);
            } else {
                return 1;
            }
        } else {
            return 0;
        }
    } else {
        return 'Not supported';
    }
}

Good:

PHP
function fibonacci(int $n): int
{
    if ($n === 0 || $n === 1) {
        return $n;
    }

    if ($n >= 50) {
        throw new \Exception('Not supported');
    }

    return fibonacci($n - 1) + fibonacci($n - 2);
}

Mapping

Avoid Mental Mapping, don’t force the reader of your code to translate what the variable means. Explicit is better than implicit.

Bad:

PHP
$l = ['Austin', 'New York', 'San Francisco'];

for ($i = 0; $i < count($l); $i++) {
    $li = $l[$i];
    doStuff();
    doSomeOtherStuff();
    // ...
    // ...
    // ...
    // Wait, what is `$li` for again?
    dispatch($li);
}

Good:

PHP
$locations = ['Austin', 'New York', 'San Francisco'];

foreach ($locations as $location) {
    doStuff();
    doSomeOtherStuff();
    // ...
    // ...
    // ...
    dispatch($location);
}

Context

Don't add unneeded context, If your class/object name tells you something, don't repeat that in your variable name.

Bad:

PHP
class Car
{
    public $carMake;
    public $carModel;
    public $carColor;

    //...
}

Good:

PHP
class Car
{
    public $make;
    public $model;
    public $color;

    //...
}

Defaults

Use default arguments instead of short circuiting or conditionals

Bad: This is not good because $breweryName can be NULL.

PHP
function createMicrobrewery($breweryName = 'Hipster Brew Co.'): void
{
    // ...
}

Not bad: This opinion is more understandable than the previous version, but it better controls the value of the variable.

PHP
function createMicrobrewery($name = null): void
{
    $breweryName = $name ?: 'Hipster Brew Co.';
    // ...
}

Good: You can use type hinting and be sure that the $breweryName will not be NULL.

PHP
function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void
{
    // ...
}