PSR-12 i PHP-CS-Fixer

🎯 Po tej lekcji zainstalujesz PHP-CS-Fixer jako zależność deweloperską, uruchomisz formatowanie projektu i skonfigurujesz reguły PSR-12.

Gdy w projekcie pracuje kilka osób, każda może formatować kod inaczej — jedne używają tabulatorów, inne spacji; jedne stawiają klamrę otwierającą w tej samej linii co deklarację funkcji, inne w następnej. PSR-12 to standard stylu kodu PHP, który eliminuje te spory. PHP-CS-Fixer automatycznie stosuje ten standard w Twoich plikach.

Czym jest PSR-12?

PSR-12 (PHP Standards Recommendation 12) to zestaw reguł formatowania kodu wydany przez PHP-FIG (Framework Interoperability Group). Określa m.in.:

  • Wcięcia: 4 spacje (nie tabulatory)
  • Klamra otwierająca klas i metod: w nowej linii
  • Klamra otwierająca pętli i warunków: w tej samej linii
  • Spacje wokół operatorów: $a + $b, nie $a+$b
  • Brak zbędnych spacji na końcu linii
  • Jedna pusta linia po declare(strict_types=1);
  • Kody pliku: UTF-8, zakończenia linii Unix (\n)

PSR-12 jest oparty na PSR-1 i PSR-2 (poprzednie wersje) i jest dziś domyślnym standardem większości frameworków PHP.

Instalacja PHP-CS-Fixer jako --dev

PHP-CS-Fixer to narzędzie deweloperskie — nie jest potrzebne na produkcji. Instalujemy je z flagą --dev:

composer require --dev friendsofphp/php-cs-fixer

Composer doda pakiet do sekcji require-dev w composer.json:

{
    "require": {
        "php": ">=8.1",
        "monolog/monolog": "^3.0"
    },
    "require-dev": {
        "friendsofphp/php-cs-fixer": "^3.0"
    }
}

Plik wykonywalny znajdziesz w vendor/bin/:

vendor/bin/php-cs-fixer --version

Użycie: fix — formatuj kod

Żeby sformatować cały katalog src/ według PSR-12:

vendor/bin/php-cs-fixer fix src/ --rules=@PSR12

Opcja --diff pokazuje zmiany bez ich stosowania (tryb podglądu):

vendor/bin/php-cs-fixer fix src/ --rules=@PSR12 --diff --dry-run

Przykładowy wynik:

   1) src/Services/Email.php (braces, indentation_type, no_extra_blank_lines)
Fixed 1 of 1 files in 0.123 seconds, 14.0 MB memory used

Plik konfiguracyjny .php-cs-fixer.php

Zamiast podawać reguły za każdym razem w terminalu, utwórz plik .php-cs-fixer.php w katalogu głównym projektu:

<?php
declare(strict_types=1);

$finder = PhpCsFixer\Finder::create()
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/tests');

return (new PhpCsFixer\Config())
    ->setRules([
        '@PSR12' => true,
        'array_syntax' => ['syntax' => 'short'], // [] zamiast array()
        'ordered_imports' => ['sort_algorithm' => 'alpha'],
        'no_unused_imports' => true,
    ])
    ->setFinder($finder);

Teraz samo vendor/bin/php-cs-fixer fix odczyta konfigurację automatycznie:

vendor/bin/php-cs-fixer fix

Najważniejsze reguły PSR-12 — przykłady

Wcięcia (4 spacje) i klamry klas

<?php
declare(strict_types=1);

// DOBRZE — klamra otwierająca klasy w nowej linii
class Kalkulator
{
    public function dodaj(int $a, int $b): int
    {
        return $a + $b;
    }
}

Klamry w pętlach i warunkach

<?php
declare(strict_types=1);

// DOBRZE — klamra w tej samej linii co if/for/foreach
function filtrujDoroslych(array $osoby): array
{
    $wynik = [];
    foreach ($osoby as $osoba) {
        if ($osoba['wiek'] >= 18) {
            $wynik[] = $osoba;
        }
    }

    return $wynik;
}

Import klas (use)

<?php
declare(strict_types=1);

namespace App\Services;

// DOBRZE — use po deklaracji namespace, jedno use na linię, alphabetycznie
use App\Models\Produkt;
use App\Models\Uzytkownik;
use Monolog\Logger;

class ZamowienieService
{
    // ...
}

Widoczność właściwości

<?php
declare(strict_types=1);

class Produkt
{
    // DOBRZE — najpierw stałe, potem właściwości, potem metody
    public const WALUTA = 'PLN';

    private string $nazwa;
    private float $cena;

    public function __construct(string $nazwa, float $cena)
    {
        $this->nazwa = $nazwa;
        $this->cena  = $cena;
    }

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

Dodanie skryptu do composer.json

Możesz skrócić wywoływanie PHP-CS-Fixer przez sekcję scripts w composer.json:

{
    "scripts": {
        "fix": "vendor/bin/php-cs-fixer fix",
        "check": "vendor/bin/php-cs-fixer fix --dry-run --diff"
    }
}

Potem wystarczy:

composer fix    # formatuj kod
composer check  # sprawdź bez zmian
💡 PHP-CS-Fixer w CI/CD: W procesie ciągłej integracji (GitHub Actions, GitLab CI) często dodaje się krok composer check, który kończy się błędem, jeśli kod nie spełnia standardu PSR-12. Dzięki temu do repozytorium nie trafia niesformatowany kod.

require-dev na produkcji — pomiń narzędzia deweloperskie

Na serwerze produkcyjnym nie instaluj pakietów deweloperskich:

composer install --no-dev

Flaga --no-dev pomija sekcję require-dev. PHP-CS-Fixer, PHPUnit i inne narzędzia deweloperskie nie trafią na produkcję, co zmniejsza rozmiar katalogu vendor/ i powierzchnię ataku.

Podsumowanie modułu

W tym module przeszedłeś przez cały nowoczesny warsztat PHP:

  1. Composer — zarządzanie zależnościami, composer.json, Packagist
  2. Autoloading PSR-4 — mapowanie namespace → katalog, composer dump-autoload
  3. Zewnętrzna biblioteka — Monolog, vendor/, composer.lock, .gitignore
  4. PSR-12 i PHP-CS-Fixer — spójny styl kodu, automatyczne formatowanie

To podstawy warsztatu, których używasz w każdym profesjonalnym projekcie PHP.

Sprawdź się

Jakiej flagi używasz, żeby zainstalować pakiet tylko dla środowiska deweloperskiego?

W jakiej sekcji composer.json lądują pakiety zainstalowane przez composer require --dev?

Jaką komendą uruchomisz PHP-CS-Fixer na całym katalogu src/?

Ćwiczenie

Zainstaluj PHP-CS-Fixer jako --dev. Napisz celowo 'brzydki' plik src/Brzydki.php (złe wcięcia, brak spacji wokół operatorów, niepotrzebne spacje). Uruchom vendor/bin/php-cs-fixer fix src/Brzydki.php --rules=@PSR12 i sprawdź, co zostało zmienione. Opcjonalnie: dodaj plik .php-cs-fixer.php z konfiguracją i uruchom fix bez podawania reguł ręcznie.

Pokaż rozwiązanie
# Instalacja:
composer require --dev friendsofphp/php-cs-fixer

# src/Brzydki.php (celowo źle sformatowany)
<?php
declare(strict_types=1);
class Brzydki{
public function licz($a,$b){return $a+$b;}
}

# Formatowanie:
vendor/bin/php-cs-fixer fix src/Brzydki.php --rules=@PSR12

# Wynik (po naprawieniu przez fixer):
<?php
declare(strict_types=1);

class Brzydki
{
    public function licz($a, $b)
    {
        return $a + $b;
    }
}

# Plik .php-cs-fixer.php:
<?php
declare(strict_types=1);

$finder = PhpCsFixer\Finder::create()->in(__DIR__ . '/src');
return (new PhpCsFixer\Config())
    ->setRules(['@PSR12' => true])
    ->setFinder($finder);