code quality and tooling
PHPDoc
PHPDoc is structured documentation written in docblocks. It helps developers, IDEs, and static analysis tools understand information that native PHP types cannot fully express.
Native PHP types should come first. PHPDoc is most valuable when it adds detail: array shapes, lists, nullable meanings, exceptions, template types, and tool-specific contracts.
Basic tags
The most common PHPDoc tags are @param, @return, @throws, and sometimes @var.
<?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.');
}
}
This docblock adds useful information because callers need to know the function can reject a product.
Do not duplicate native types
This is not useful:
<?php
declare(strict_types=1);
/**
* @param int $pennies
* @return string
*/
function formatMoney(int $pennies): string
{
return 'GBP ' . number_format($pennies / 100, 2);
}
The function signature already says int and string. Repeating that in PHPDoc creates extra maintenance without adding meaning.
Array shapes
PHP has no native syntax for saying "this array must have a name key and a price key". PHPDoc can describe that shape.
<?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
This is one of the most job-relevant PHPDoc patterns for junior developers because many older PHP codebases use arrays heavily.
Lists of shaped arrays
A list is an array with sequential integer keys starting at 0. Static analysis tools understand list<...>.
<?php
declare(strict_types=1);
/**
* @param list<array{name: string, price: int}> $products
*/
function totalPrice(array $products): int
{
$total = 0;
foreach ($products as $product) {
$total += $product['price'];
}
return $total;
}
This says the function expects a list of product-shaped arrays, not just any array.
Optional keys and nullable values
Some arrays have optional keys. Mark optional keys with ? after the key name.
<?php
declare(strict_types=1);
/**
* @param array{name: string, email?: string} $customer
*/
function displayCustomer(array $customer): string
{
$email = $customer['email'] ?? 'no email';
return $customer['name'] . ' <' . $email . '>';
}
echo displayCustomer(['name' => 'Ada']);
// Prints:
// Ada <no email>
An optional key is not the same as a nullable value. email?: string means the key may be absent. email: string|null means the key exists but the value may be null.
Variable PHPDoc
Use @var when a tool cannot infer a variable type, but keep it close to the value it describes.
<?php
declare(strict_types=1);
/** @var list<array{name: string, price: int}> $products */
$products = require __DIR__ . '/products.php';
This is useful when require returns data from another file and the type is not visible from the assignment.
Template types at recognition level
Advanced PHPDoc can express generic-like relationships using templates. You do not need to write many of these as a beginner, but you should recognise them in libraries.
<?php
declare(strict_types=1);
/**
* @template T
* @param T $value
* @return T
*/
function identity(mixed $value): mixed
{
return $value;
}
This says the function returns the same type it receives. Static analysis tools can use that information even though native PHP only sees mixed.
Keep PHPDoc honest
PHPDoc can become wrong. When it does, it actively harms the codebase because tools and developers trust it.
Update PHPDoc when:
- a parameter shape changes
- a return value changes
- an exception is added or removed
- an optional key becomes required
- a function starts accepting
null
Before moving on, make sure you can add PHPDoc for a shaped product array and explain why that is more useful than a generic @param array.
Practice
Task: Document A Product List Shape
Add useful PHPDoc to this function. Do not duplicate the native array and int types unless you are adding more detail.
<?php
declare(strict_types=1);
function totalPrice(array $products): int
{
$total = 0;
foreach ($products as $product) {
$total += $product['price'];
}
return $total;
}
Requirements
- Document that
$productsis a list. - Document that each product has a
namestring andpriceinteger. - Keep the function behaviour unchanged.
- Keep the code valid PHP.
Show solution
<?php
declare(strict_types=1);
/**
* @param list<array{name: string, price: int}> $products
*/
function totalPrice(array $products): int
{
$total = 0;
foreach ($products as $product) {
$total += $product['price'];
}
return $total;
}
The native signature says the function accepts an array and returns an integer. The PHPDoc adds the missing detail: the array should be a list of product arrays with name and price keys.