code quality and tooling

Comments and Docblocks

Comments and docblocks are tools for explaining code, but they solve different problems. A normal comment explains a decision in the code. A docblock describes a symbol in a structured way that humans and tools can read.

Good PHP code should not need comments for every line. Clear names, small functions, and native types should do most of the work. Comments are useful when the reason behind the code is not obvious from the code itself.

Avoid comments that repeat the code

This comment adds noise because the code already says what happens.

PHP example
<?php

declare(strict_types=1);

// Add the price to the total.
$total += $price;

The next version is better because the comment explains the business reason.

PHP example
<?php

declare(strict_types=1);

// Gift cards are excluded because finance reports them separately.
if ($productType !== 'gift_card') {
    $total += $price;
}

Use comments to explain "why", not to narrate obvious syntax.

Native types come first

If PHP can express the type directly, use the language before reaching for a docblock.

PHP example
<?php

declare(strict_types=1);

function formatMoney(int $pennies): string
{
    return 'GBP ' . number_format($pennies / 100, 2);
}

This function does not need @param int and @return string because the signature already says that.

Docblocks help with array shapes

PHP can type a parameter as array, but it cannot fully describe the keys and value types in the function signature. PHPDoc can add that missing detail for readers and static analysis tools.

PHP example
<?php

declare(strict_types=1);

/**
 * @param array{name: string, price: int} $product
 */
function reportLine(array $product): string
{
    return $product['name'] . ': GBP ' . number_format($product['price'] / 100, 2);
}

echo reportLine(['name' => 'Notebook', 'price' => 499]);

// Prints:
// Notebook: GBP 4.99

The docblock explains the expected array shape. That is much more useful than a generic @param array.

Docblocks help with lists

When a function expects a list of product arrays, a docblock can describe the nested shape.

PHP example
<?php

declare(strict_types=1);

/**
 * @param list<array{name: string, price: int}> $products
 */
function productTotal(array $products): int
{
    $total = 0;

    foreach ($products as $product) {
        $total += $product['price'];
    }

    return $total;
}

echo productTotal([
    ['name' => 'Notebook', 'price' => 499],
    ['name' => 'Pen', 'price' => 129],
]);

// Prints:
// 628

This kind of docblock is common in codebases that use PHPStan or Psalm.

Document exceptions when they matter

If a function can throw a meaningful exception that callers need to know about, a docblock can make that contract visible.

PHP example
<?php

declare(strict_types=1);

/**
 * @throws InvalidArgumentException when the product name is empty.
 */
function validateProduct(array $product): void
{
    if (trim($product['name'] ?? '') === '') {
        throw new InvalidArgumentException('Product name is required.');
    }
}

Do not add @throws to every function automatically. Use it when the exception is part of how the caller should understand the function.

Keep docblocks true

An outdated docblock is worse than no docblock. It can trick a reader or a static analysis tool into trusting information that is no longer correct.

If a function changes from returning pennies to returning formatted text, update or remove the docblock in the same change.

PHP example
<?php

declare(strict_types=1);

/**
 * @return non-empty-string
 */
function defaultCurrency(): string
{
    return 'GBP';
}

Specific docblocks are useful only when they stay accurate.

Comments in real reviews

When reviewing comments and docblocks, ask:

  • Does the comment explain a reason that the code itself cannot show?
  • Is the docblock more specific than the native PHP type?
  • Does the docblock match the current code?
  • Would renaming a function or variable remove the need for the comment?
  • Is a complex rule documented close to the code that enforces it?

Before moving on, make sure you can remove noisy comments, add useful PHPDoc for array shapes, and recognise when native PHP types are enough.

Practice

Task: Replace Noisy Comments With PHPDoc

Improve the comments and docblocks in this script without changing its output.

PHP example
<?php

declare(strict_types=1);

// This function totals products.
function productTotal(array $products): int
{
    // Set total to zero.
    $total = 0;

    foreach ($products as $product) {
        // Add price.
        $total += $product['price'];
    }

    // Return total.
    return $total;
}

$products = [
    ['name' => 'Notebook', 'price' => 499],
    ['name' => 'Pen', 'price' => 129],
];

echo productTotal($products);

// Prints:
// 628

Requirements

  • Remove comments that only repeat the code.
  • Add useful PHPDoc for the product list shape.
  • Keep the native array and int types.
  • Keep the output the same.
Show solution
PHP example
<?php

declare(strict_types=1);

/**
 * @param list<array{name: string, price: int}> $products
 */
function productTotal(array $products): int
{
    $total = 0;

    foreach ($products as $product) {
        $total += $product['price'];
    }

    return $total;
}

$products = [
    ['name' => 'Notebook', 'price' => 499],
    ['name' => 'Pen', 'price' => 129],
];

echo productTotal($products);

// Prints:
// 628

The noisy comments are gone. The docblock now adds information that the native array type cannot express: this function expects a list of arrays with name and price keys.