PHP.nl
Deep Dive

PHP-FIG en de PSR standaarden uitgelegd

P
PHP.nl Redactie
📖 6 min leestijd 👁 411

Dat je in een Laravel-project zonder problemen een logger, HTTP-client of cache-library gebruikt die oorspronkelijk voor Symfony is geschreven, is geen toeval. Het is het werk van de PHP Framework Interop Group, kortweg PHP-FIG, en de standaarden die zij publiceert: de PSR's. In dit artikel leggen we uit wat de PHP-FIG is, welke PSR's je in je dagelijkse werk tegenkomt, en hoe je ze gebruikt om code te schrijven die niet aan één framework vastzit.

Wat is de PHP-FIG?

In 2009 kwamen ontwikkelaars van een aantal grote PHP-projecten bij elkaar op de conferentie php|tek in Chicago. Elk framework had zijn eigen manier van autoloading, zijn eigen logger-interface en zijn eigen codestijl. Wie componenten van verschillende projecten wilde combineren, moest overal adapters voor schrijven. Het doel van de groep was eenvoudig: afspraken maken zodat projecten beter samenwerken.

Leden van de PHP-FIG zijn projecten, geen individuen, en sinds de herstructurering in 2016 (FIG 3.0) is er een kerncomité dat over nieuwe standaarden stemt, met werkgroepen die ze uitwerken. Belangrijk om te weten: een PSR is een aanbeveling. Niemand is verplicht hem te volgen. Dat ze toch overal gebruikt worden, komt doordat Composer, Packagist en de grote frameworks erop bouwen.

Wat is een PSR?

PSR staat voor PHP Standards Recommendation. Een PSR is een document met afspraken, meestal in de vorm van een of meer interfaces die als Composer-package beschikbaar zijn onder psr/*. Een PSR doorloopt de statussen draft, review en accepted. Een geaccepteerde PSR kan later deprecated worden als er een betere opvolger is, of abandoned als hij nooit afgerond wordt.

Er zijn twee soorten:

  • Afspraken over code, zoals autoloading en codestijl.
  • Interfaces, zoals een logger of een HTTP-request, die bibliotheken kunnen implementeren en gebruiken.

De PSR's die je moet kennen

PSR-4: autoloading

De belangrijkste PSR van allemaal, al merk je er weinig van. PSR-4 beschrijft hoe een volledig gekwalificeerde klassenaam naar een bestandspad vertaalt. App\Models\Gebruiker met de prefix App\ gekoppeld aan src/ staat in src/Models/Gebruiker.php. Composer gebruikt dit om je klassen automatisch in te laden; zie ons artikel Wat is Composer en hoe werkt dependency management?. PSR-4 verving in 2014 de oudere PSR-0, die inmiddels deprecated is.

PSR-1, PSR-12 en PER Coding Style: codestijl

PSR-1 legt de basis vast: klassen in StudlyCaps, methoden in camelCase, constanten in hoofdletters, en geen bestand dat zowel symbolen declareert als side effects heeft. PSR-12 werkte dat uit tot een volledige stijlgids voor inspringing, accolades, spaties en regellengte, en verving daarmee het oudere PSR-2.

Omdat PHP sneller verandert dan een PSR bijgewerkt kan worden, is de codestijl sinds 2022 ondergebracht in de PER Coding Style: een PHP Evolving Recommendation die nieuwe versies krijgt als de taal nieuwe syntax krijgt, zoals enums of property hooks. Tools als PHP-CS-Fixer en Laravel Pint hebben er kant-en-klare rulesets voor.

PSR-3: logging

Eén interface, Psr\Log\LoggerInterface, met methoden voor de acht logniveaus uit RFC 5424: van debug() tot emergency(). Elke library die iets wil loggen, vraagt om deze interface in plaats van om een concrete logger:

use Psr\Log\LoggerInterface;

final class Betaalprovider
{
    public function __construct(private LoggerInterface $logger) {}

    public function verwerk(Betaling $betaling): void
    {
        $this->logger->info('Betaling gestart', ['id' => $betaling->id]);
        // ...
    }
}

Of jouw applicatie nu Monolog, de logger van Laravel of iets zelfgeschrevens gebruikt: de library werkt ermee.

PSR-7, PSR-17 en PSR-18: HTTP

PSR-7 beschrijft hoe HTTP-requests, responses, URI's en streams als objecten worden gerepresenteerd. Een kenmerk dat in het begin verrast: PSR-7-objecten zijn immutable. Elke wijziging levert een nieuw object op:

$request = $request->withHeader('Accept', 'application/json');

Vergeet je de toewijzing, dan gebeurt er niets. PSR-17 voegt factories toe om die objecten te maken, en PSR-18 een interface voor een HTTP-client. Samen maken ze het mogelijk dat een SDK, bijvoorbeeld van een betaalprovider, werkt met elke HTTP-client die jij al gebruikt, of dat nu Guzzle, de Symfony HttpClient of iets anders is.

PSR-15: middleware

PSR-15 definieert request handlers en middleware op basis van PSR-7. Frameworks als Slim en Mezzio zijn er volledig op gebouwd, zodat middleware tussen die projecten uitwisselbaar is. Laravel en Symfony hebben hun eigen middleware-systemen, maar kunnen via de PSR-7-bridge van Symfony met PSR-7-objecten overweg.

PSR-11: containers

Een minimale interface voor dependency injection containers, met alleen get() en has(). De containers van Laravel en Symfony implementeren hem. Bewust heel klein gehouden: het registreren van services verschilt per container en valt buiten de standaard.

PSR-6 en PSR-16: caching

Twee caching-standaarden: PSR-6 met pools en cache items voor geavanceerde scenario's, en PSR-16 met een eenvoudige get()/set()-interface voor de meeste gevallen. Kies voor je eigen libraries bij voorkeur PSR-16, tenzij je echt de extra controle van PSR-6 nodig hebt.

PSR-14 en PSR-20

PSR-14 standaardiseert event dispatchers en listeners; de EventDispatcher-component van Symfony implementeert hem. PSR-20, uit 2022, is de jongste: een ClockInterface met één methode, now(). Klein, maar erg nuttig. Code die de huidige tijd via een clock binnenkrijgt in plaats van new DateTimeImmutable() aan te roepen, kun je in tests op een vast moment zetten.

PSR's in je eigen code gebruiken

Je hoeft geen library te publiceren om profijt te hebben van PSR's. Een paar praktische toepassingen:

  • Typehint op PSR-interfaces in domeinlogica. Vraag in je services om een LoggerInterface of ClockInterface in plaats van een framework-facade. Je code wordt beter testbaar en minder afhankelijk van het framework. Dit is dependency inversion uit SOLID in de praktijk.
  • Kies libraries die PSR-18 accepteren. Dan hoef je geen tweede HTTP-client in je project te halen omdat een SDK toevallig een andere verwacht.
  • Automatiseer je codestijl. Laat PHP-CS-Fixer of Pint met de PER-ruleset in je CI draaien, en discussies over spaties in code reviews zijn verleden tijd.

Samenvatting

De PHP-FIG maakte van een verzameling losse frameworks één ecosysteem. PSR-0 en later PSR-4 maakten Composer-autoloading mogelijk, PSR-3, PSR-7 en PSR-18 maakten libraries uitwisselbaar, en PER Coding Style houdt de codestijl actueel. Wie tegen deze interfaces programmeert, schrijft code die een frameworkwissel of een major upgrade een stuk beter overleeft.

Werk je met een codebase die nog op PSR-0 of eigen autoloaders draait? Dat is vaak een teken dat er meer achterloopt. Lees hoe je een legacy PHP-applicatie moderniseert, of laat de stand van zaken eerst onafhankelijk vaststellen met een PHP code audit.