code quality and tooling

PSR-12

PSR-12 is an extended PHP coding style standard. PSR-1 gives the basic naming and file rules; PSR-12 adds detailed layout rules for modern PHP code.

You will not hand-format every line forever. Real teams usually use PHP_CodeSniffer, PHP-CS-Fixer, or framework tooling. Still, you need to understand the style these tools are enforcing so reviews and tool errors make sense.

File layout

A typical class file follows a predictable order:

PHP example
<?php

declare(strict_types=1);

namespace App\Reporting;

use DateTimeImmutable;

class PriceReport
{
}

The important shape is:

  • opening <?php tag
  • file-level declare statement
  • namespace
  • imports
  • class, interface, trait, enum, or other code

Blank lines separate those sections so the file is easy to scan.

Indentation and braces

PSR-12 uses four spaces for indentation. Classes and methods put the opening brace on the next line.

PHP example
<?php

declare(strict_types=1);

class ReceiptFormatter
{
    public function formatLine(string $name, int $price): string
    {
        return $name . ': GBP ' . number_format($price / 100, 2);
    }
}

This layout makes the class body and method body obvious.

Control structures keep the brace on the same line

Control structures such as if, foreach, for, while, and switch use a different brace style from classes and methods.

PHP example
<?php

declare(strict_types=1);

function activeProductTotal(array $products): int
{
    $total = 0;

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

    return $total;
}

Notice the blank line before return. PSR-12 does not require blank lines everywhere, but thoughtful spacing helps separate setup, work, and return values.

Visibility should be explicit

Class properties and methods should declare visibility with public, protected, or private.

PHP example
<?php

declare(strict_types=1);

class Product
{
    private string $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public function name(): string
    {
        return $this->name;
    }
}

Explicit visibility makes the public API of a class easier to see. It also avoids accidental assumptions when someone changes the class later.

Keywords are lowercase

PHP keywords and type keywords should be lowercase.

PHP example
<?php

declare(strict_types=1);

function hasItems(array $items): bool
{
    return count($items) > 0;
}

var_dump(hasItems(['Notebook']));

// Prints:
// bool(true)

Use true, false, null, array, int, string, and bool in lowercase.

Method arguments and line breaks

When a call or declaration becomes too long, split it across multiple lines in a predictable way.

PHP example
<?php

declare(strict_types=1);

function formatReceiptLine(
    string $name,
    int $quantity,
    int $unitPrice
): string {
    return $quantity . ' x ' . $name . ': GBP ' . number_format(($quantity * $unitPrice) / 100, 2);
}

Each argument sits on its own line, and the closing parenthesis lines up with the start of the function declaration.

Style is not behaviour

Formatting should not change what the code does. This is why automated formatters are useful: they make mechanical style changes consistently, leaving developers to review behaviour.

When reviewing a diff, separate the questions:

  • Does the code follow the project style?
  • Does the code behave correctly?
  • Did the diff mix formatting changes with logic changes?

If a large formatting change is needed, it is often better as a separate commit or pull request.

What to remember

PSR-12 gives PHP projects a consistent visual structure. The main beginner-level points are four-space indentation, predictable file sections, explicit visibility, lowercase keywords, clear brace placement, and readable line breaks.

Before moving on, make sure you can reformat a messy class into PSR-12-style PHP without changing its output.

Practice

Task: Reformat A Class With PSR-12

Reformat this class so it follows the PSR-12 style covered in the lesson. Do not change the output.

PHP example
<?php
declare(strict_types=1);
class ReceiptFormatter{
private string $currency;
public function __construct(string $currency){$this->currency=$currency;}
public function formatLine(string $name,int $quantity,int $unitPrice): string{if($quantity<1){return 'Invalid quantity';}return $quantity.' x '.$name.': '.$this->currency.' '.number_format(($quantity*$unitPrice)/100,2);}
}
$formatter = new ReceiptFormatter('GBP');
echo $formatter->formatLine('Notebook', 2, 499);

// Prints:
// 2 x Notebook: GBP 9.98

Requirements

  • Keep declare(strict_types=1);.
  • Use four-space indentation.
  • Put class and method opening braces on their own lines.
  • Put control-structure braces on the same line.
  • Keep property and method visibility explicit.
  • Keep the output the same.
Show solution
PHP example
<?php

declare(strict_types=1);

class ReceiptFormatter
{
    private string $currency;

    public function __construct(string $currency)
    {
        $this->currency = $currency;
    }

    public function formatLine(string $name, int $quantity, int $unitPrice): string
    {
        if ($quantity < 1) {
            return 'Invalid quantity';
        }

        return $quantity . ' x ' . $name . ': ' . $this->currency . ' ' . number_format(($quantity * $unitPrice) / 100, 2);
    }
}

$formatter = new ReceiptFormatter('GBP');

echo $formatter->formatLine('Notebook', 2, 499);

// Prints:
// 2 x Notebook: GBP 9.98

The refactor changes layout, spacing, and readability only. The class still produces the same output for the same input.