Zum Inhalt springen
Backend

PHP 8 - Moderne Funktionen und Best Practices fuer Entwickler

Veröffentlicht am:
·5 Min. Lesezeit·Autor: MDS Software Solutions Group

PHP Moderne Funktionen

backend

PHP 8 - Moderne Funktionen und Best Practices

PHP hat in den letzten Jahren eine bemerkenswerte Transformation durchlaufen. Die Versionen 8.0, 8.1 und 8.2 brachten eine Reihe von Funktionen, die die Sprache auf Augenhoehe mit den modernsten Backend-Technologien bringen. Wenn Sie noch PHP-Code im Stil von Version 5 oder 7 schreiben, zeigt Ihnen dieser Leitfaden, warum ein Umstieg lohnt und wie Sie das volle Potenzial von modernem PHP ausschoepfen.

Named Arguments - Selbstdokumentierende Funktionsaufrufe#

Eine der am meisten erwarteten Neuerungen in PHP 8.0 waren benannte Argumente. Sie ermoeglichen es, Werte an Funktionen mit expliziter Angabe des Parameternamens zu uebergeben, was die Lesbarkeit des Codes erheblich verbessert.

Das Problem mit positionellen Argumenten#

// PHP 7 - was bedeuten diese Werte?
array_slice($array, 0, 3, true);
setcookie('session', $value, 0, '/', '', true, true);

Ohne Blick in die Dokumentation ist kaum zu erkennen, wofuer die einzelnen Argumente stehen.

Die Loesung mit Named Arguments#

// PHP 8 - alles auf einen Blick klar
array_slice(array: $array, offset: 0, length: 3, preserve_keys: true);

setcookie(
    name: 'session',
    value: $value,
    path: '/',
    secure: true,
    httponly: true,
);

Named Arguments funktionieren hervorragend mit Standardwerten - Sie koennen Parameter ueberspringen, die keine Aenderung erfordern:

function createUser(
    string $name,
    string $email,
    string $role = 'user',
    bool $active = true,
    ?string $avatar = null,
    string $locale = 'de',
) {
    // ...
}

// Nur das setzen, was noetig ist
$user = createUser(
    name: 'Max Mustermann',
    email: 'max@example.com',
    locale: 'en',
);

Union Types und Intersection Types - Praezise Typisierung#

PHP 8.0 fuehrte Union Types ein, und PHP 8.1 erweiterte das Typsystem um Intersection Types. Zusammen bilden sie einen leistungsstarken Mechanismus zur Definition von Datenerwartungen.

Union Types (PHP 8.0)#

Ein Union Type legt fest, dass ein Parameter oder Rueckgabewert einer von mehreren Typen sein kann:

function processInput(string|int|float $value): string|false
{
    if (is_numeric($value)) {
        return number_format((float) $value, 2, ',', '.');
    }
    return false;
}

// Nullable-Kurzschreibweise
function findUser(int $id): User|null
{
    // Aequivalent zu ?User
    return User::find($id);
}

Intersection Types (PHP 8.1)#

Intersection Types verlangen, dass ein Wert mehrere Interfaces gleichzeitig implementiert:

interface Loggable
{
    public function toLogEntry(): string;
}

interface Serializable
{
    public function serialize(): string;
}

// Parameter muss BEIDE Interfaces implementieren
function processEntity(Loggable&Serializable $entity): void
{
    logger()->info($entity->toLogEntry());
    cache()->put('entity', $entity->serialize());
}

Disjunctive Normal Form Types (PHP 8.2)#

PHP 8.2 ermoeglicht die Kombination von Union und Intersection Types in einem einzigen Ausdruck:

function handleInput((Stringable&Countable)|string $input): string
{
    if (is_string($input)) {
        return $input;
    }
    return (string) $input;
}

Match-Ausdruck - Moderne Alternative zu Switch#

Der match-Ausdruck ersetzt die switch-Anweisung durch eine kompaktere und sicherere Syntax. Im Gegensatz zu switch verwendet match den strikten Vergleich (===), benoetigt kein break und gibt direkt einen Wert zurueck.

// Alter Ansatz mit switch
switch ($statusCode) {
    case 200:
    case 201:
        $message = 'Erfolg';
        break;
    case 404:
        $message = 'Nicht gefunden';
        break;
    case 500:
        $message = 'Serverfehler';
        break;
    default:
        $message = 'Unbekannter Status';
}

// Moderner Ansatz mit match
$message = match($statusCode) {
    200, 201 => 'Erfolg',
    404      => 'Nicht gefunden',
    500      => 'Serverfehler',
    default  => 'Unbekannter Status',
};

match entfaltet seine volle Staerke in Kombination mit Enums und komplexeren Entscheidungsszenarien:

$result = match(true) {
    $age < 13  => 'Kind',
    $age < 18  => 'Jugendlicher',
    $age < 65  => 'Erwachsener',
    default    => 'Senior',
};

Enums - Native Aufzaehlungstypen (PHP 8.1)#

PHP 8.1 brachte endlich native Enums und macht damit Workarounds mit Klassenkonstanten oder Drittanbieter-Bibliotheken ueberfluessig.

Einfache Enums (Pure Enums)#

enum Status
{
    case Draft;
    case Published;
    case Archived;
}

function updateArticle(int $id, Status $status): void
{
    // Typsicher - ungueltige Werte koennen nicht uebergeben werden
}

updateArticle(1, Status::Published);

Backed Enums - Enums mit skalaren Werten#

enum Color: string
{
    case Red = '#FF0000';
    case Green = '#00FF00';
    case Blue = '#0000FF';

    // Enums koennen Methoden haben
    public function label(): string
    {
        return match($this) {
            self::Red   => 'Rot',
            self::Green => 'Gruen',
            self::Blue  => 'Blau',
        };
    }
}

// Aus einem Wert erstellen
$color = Color::from('#FF0000');      // Color::Red
$color = Color::tryFrom('#FFFFFF');   // null - keine Ausnahme

// Zugriff auf Wert und Name
echo Color::Red->value;  // #FF0000
echo Color::Red->name;   // Red
echo Color::Red->label(); // Rot

Enums mit Interfaces#

interface HasDescription
{
    public function description(): string;
}

enum PaymentMethod: string implements HasDescription
{
    case CreditCard = 'credit_card';
    case BankTransfer = 'bank_transfer';
    case Sofort = 'sofort';

    public function description(): string
    {
        return match($this) {
            self::CreditCard   => 'Kredit- oder Debitkarte',
            self::BankTransfer => 'Bankueberweisung',
            self::Sofort       => 'Sofortueberweisung',
        };
    }

    public function processingTime(): string
    {
        return match($this) {
            self::CreditCard   => 'sofort',
            self::BankTransfer => '1-2 Werktage',
            self::Sofort       => 'sofort',
        };
    }
}

Fibers - Leichtgewichtige Nebenlaeufigkeit (PHP 8.1)#

Fibers sind ein Nebenlaeufigkeitsmechanismus auf Benutzerebene, der es ermoeglicht, die Codeausfuehrung zu unterbrechen und wieder aufzunehmen. Sie bilden das Fundament fuer moderne asynchrone PHP-Frameworks wie AMPHP und ReactPHP.

$fiber = new Fiber(function (): void {
    $value = Fiber::suspend('erste Unterbrechung');
    echo "Empfangen: $value\n";

    $value = Fiber::suspend('zweite Unterbrechung');
    echo "Empfangen: $value\n";
});

// Fiber starten
$result = $fiber->start();
echo $result . "\n"; // "erste Unterbrechung"

// Mit einem Wert fortsetzen
$result = $fiber->resume('hallo');
echo $result . "\n"; // "zweite Unterbrechung"

$fiber->resume('welt');

Praxisbeispiel - Parallele HTTP-Anfragen#

function asyncHttpRequest(string $url): Fiber
{
    return new Fiber(function () use ($url): array {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 10,
        ]);

        // Fiber unterbrechen - andere Aufgaben ausfuehren lassen
        Fiber::suspend();

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        return [
            'status' => $httpCode,
            'body' => json_decode($response, true),
        ];
    });
}

// Mehrere Anfragen "parallel" starten
$fibers = [
    asyncHttpRequest('https://api.example.com/users'),
    asyncHttpRequest('https://api.example.com/products'),
    asyncHttpRequest('https://api.example.com/orders'),
];

foreach ($fibers as $fiber) {
    $fiber->start();
}

// Ergebnisse sammeln
$results = [];
foreach ($fibers as $fiber) {
    $results[] = $fiber->resume();
}

In der Praxis werden Fibers meist intern von Frameworks verwendet, aber das Verstaendnis ihrer Funktionsweise ermoeglicht effizienteren asynchronen Code.

Readonly-Eigenschaften und -Klassen (PHP 8.1 / 8.2)#

Readonly-Eigenschaften (PHP 8.1)#

Readonly-Eigenschaften koennen nur einmal zugewiesen werden - typischerweise im Konstruktor. Jeder weitere Aenderungsversuch loest einen Fehler aus:

class Invoice
{
    public function __construct(
        public readonly string $number,
        public readonly float $amount,
        public readonly DateTimeImmutable $issuedAt,
        public readonly string $currency = 'EUR',
    ) {}
}

$invoice = new Invoice(
    number: 'RE/2025/001',
    amount: 1500.00,
    issuedAt: new DateTimeImmutable(),
);

echo $invoice->number; // RE/2025/001
// $invoice->number = 'RE/2025/002'; // Error: Cannot modify readonly property

Readonly-Klassen (PHP 8.2)#

PHP 8.2 erlaubt es, eine gesamte Klasse als readonly zu markieren - alle Eigenschaften werden automatisch readonly:

readonly class Money
{
    public function __construct(
        public float $amount,
        public string $currency,
    ) {}

    public function add(Money $other): self
    {
        if ($this->currency !== $other->currency) {
            throw new InvalidArgumentException('Waehrungen muessen uebereinstimmen');
        }
        return new self($this->amount + $other->amount, $this->currency);
    }

    public function multiply(float $factor): self
    {
        return new self($this->amount * $factor, $this->currency);
    }
}

$price = new Money(100.00, 'EUR');
$tax = $price->multiply(0.19);
$total = $price->add($tax);

echo "$total->amount $total->currency"; // 119 EUR

Readonly-Klassen passen perfekt zum Value-Object-Muster und helfen beim Aufbau unveränderlicher Datenstrukturen.

Constructor Promotion - Weniger Boilerplate, mehr Klarheit#

Die Konstruktor-Promotion, eingefuehrt in PHP 8.0, reduziert den Codeaufwand fuer einfache Klassen erheblich:

// PHP 7 - viele Wiederholungen
class Product
{
    private string $name;
    private float $price;
    private string $sku;
    private int $stock;

    public function __construct(
        string $name,
        float $price,
        string $sku,
        int $stock
    ) {
        $this->name = $name;
        $this->price = $price;
        $this->sku = $sku;
        $this->stock = $stock;
    }
}

// PHP 8 - kompakt und lesbar
class Product
{
    public function __construct(
        private string $name,
        private float $price,
        private string $sku,
        private int $stock = 0,
    ) {}

    public function isAvailable(): bool
    {
        return $this->stock > 0;
    }
}

Die Kombination von Constructor Promotion mit Readonly ergibt aeusserst kompakte DTOs und Value Objects:

readonly class OrderDTO
{
    public function __construct(
        public string $customerEmail,
        public array $items,
        public float $totalAmount,
        public string $currency = 'EUR',
        public ?string $couponCode = null,
    ) {}
}

Null-safe-Operator - Sichere Objektnavigation#

Der ?->-Operator beseitigt die kaskadenartigen Null-Pruefungen, die bisher den Code unuebersichtlich machten:

// PHP 7 - Kette von Null-Pruefungen
$country = null;
if ($user !== null) {
    $address = $user->getAddress();
    if ($address !== null) {
        $city = $address->getCity();
        if ($city !== null) {
            $country = $city->getCountry();
        }
    }
}

// PHP 8 - eine Zeile
$country = $user?->getAddress()?->getCity()?->getCountry();

Der Null-safe-Operator laesst sich gut mit anderen PHP-8-Funktionen kombinieren:

// Kombination mit Named Arguments und match
$shippingLabel = match($order?->getShippingMethod()?->getType()) {
    'express'  => 'Expressversand',
    'standard' => 'Standardversand',
    'pickup'   => 'Selbstabholung',
    default    => 'Keine Versandinformation verfuegbar',
};

// Null-safe mit Methodenaufrufen und Fallback
$length = $user?->getProfile()?->getBio()?->length() ?? 0;

First-Class Callable Syntax (PHP 8.1)#

PHP 8.1 fuehrte eine elegante Syntax zum Erstellen von Funktions- und Methodenreferenzen mit dem (...)-Operator ein:

// Alter Ansatz
$lengths = array_map(function ($str) {
    return strlen($str);
}, $strings);

// PHP 8.1 - First-Class Callable
$lengths = array_map(strlen(...), $strings);

// Funktioniert mit Objektmethoden
class StringHelper
{
    public function capitalize(string $str): string
    {
        return mb_strtoupper(mb_substr($str, 0, 1)) . mb_substr($str, 1);
    }
}

$helper = new StringHelper();
$capitalized = array_map($helper->capitalize(...), ['php', 'javascript', 'python']);
// ['Php', 'Javascript', 'Python']

// Statische Methoden
$filtered = array_filter($items, Validator::isValid(...));

// Aufbau einer Verarbeitungspipeline
$pipeline = [
    trim(...),
    strtolower(...),
    htmlspecialchars(...),
];

$result = array_reduce(
    $pipeline,
    fn($carry, $fn) => $fn($carry),
    $input,
);

JIT-Compiler - PHP auf der Ueberholspur#

Der Just-In-Time-Compiler (JIT) in PHP 8.0 stellt einen grundlegenden Wandel in der Ausfuehrung von PHP-Code dar. JIT kompiliert PHP in nativen Maschinencode und liefert bei bestimmten Workloads dramatische Leistungssteigerungen.

JIT konfigurieren#

; php.ini
opcache.enable=1
opcache.jit_buffer_size=256M
opcache.jit=1255

; JIT-Modi:
; 1205 - Function JIT (sicher, guter Startpunkt)
; 1235 - Tracing JIT mit Optimierungen
; 1255 - Tracing JIT mit vollen Optimierungen (maximale Leistung)

Wann macht der JIT den Unterschied?#

JIT bringt die groessten Vorteile bei CPU-intensivem Code:

// Mathematische Operationen - bis zu 3-4x Beschleunigung
function fibonacci(int $n): int
{
    if ($n <= 1) return $n;
    return fibonacci($n - 1) + fibonacci($n - 2);
}

// Bildverarbeitung, Kryptographie, ML-Algorithmen
// - alles CPU-Gebundene profitiert vom JIT

// Typische Webanwendungen (I/O-gebunden) sehen geringere Gewinne,
// aber JIT lohnt sich dennoch fuer die allgemeine Verbesserung

PHP 8 vs. PHP 7 - Benchmarks#

Fuer typische Webanwendungen bietet PHP 8 mit JIT:

  • Symfony/Laravel-Anwendungen: 5-15 % schnellere Antwortzeiten
  • String-Operationen: bis zu 30 % schnellere Verarbeitung
  • Mathematische Berechnungen: bis zu 300 % Beschleunigung
  • Speicherverbrauch: vergleichbar mit PHP 7.4, leichter Anstieg durch JIT-Puffer

Weitere bemerkenswerte Funktionen#

Attribute (PHP 8.0)#

#[Route('/api/users', methods: ['GET'])]
#[Middleware('auth')]
public function listUsers(): JsonResponse
{
    // ...
}

#[Deprecated('Use newMethod() instead')]
public function oldMethod(): void
{
    // ...
}

Der Rueckgabetyp never (PHP 8.1)#

function redirect(string $url): never
{
    header("Location: $url");
    exit;
}

function throwNotFound(): never
{
    throw new NotFoundException();
}

Konstanten in Interfaces und Traits (PHP 8.2)#

enum DatabaseDriver: string
{
    case MySQL = 'mysql';
    case PostgreSQL = 'pgsql';
    case SQLite = 'sqlite';

    public function defaultPort(): int
    {
        return match($this) {
            self::MySQL      => 3306,
            self::PostgreSQL => 5432,
            self::SQLite     => 0,
        };
    }
}

Praxisbeispiel - Eine moderne Serviceklasse#

Kombinieren wir alle vorgestellten Funktionen in einem praxisnahen Beispiel:

readonly class OrderService
{
    public function __construct(
        private OrderRepository $orders,
        private PaymentGateway $payments,
        private NotificationService $notifications,
        private LoggerInterface $logger,
    ) {}

    public function placeOrder(OrderDTO $dto): Order
    {
        $order = new Order(
            items: $dto->items,
            total: new Money(amount: $dto->totalAmount, currency: $dto->currency),
            status: OrderStatus::Pending,
            createdAt: new DateTimeImmutable(),
        );

        $paymentResult = match($dto->paymentMethod) {
            PaymentMethod::CreditCard   => $this->payments->chargeCard(order: $order),
            PaymentMethod::BankTransfer => $this->payments->initTransfer(order: $order),
            PaymentMethod::Sofort       => $this->payments->processSofort(order: $order),
        };

        $order = $paymentResult?->isSuccessful()
            ? $order->withStatus(OrderStatus::Paid)
            : $order->withStatus(OrderStatus::PaymentFailed);

        $this->orders->save($order);

        $this->notifications->send(
            recipient: $dto->customerEmail,
            template: $order->status->notificationTemplate(),
            context: ['order' => $order],
        );

        $this->logger->info('Bestellung aufgegeben', [
            'orderId' => $order->id,
            'status' => $order->status->value,
            'amount' => "$order->total->amount $order->total->currency",
        ]);

        return $order;
    }
}

Migration auf PHP 8 - Wo anfangen?#

  1. Abhaengigkeiten aktualisieren - pruefen Sie die Kompatibilitaet mit composer why-not php ^8.2
  2. Strict Types aktivieren - fuegen Sie declare(strict_types=1) in jede Datei ein
  3. Statische Analyse einsetzen - PHPStan und Psalm erkennen Probleme fruehzeitig
  4. Schrittweise migrieren - beginnen Sie mit neuen Dateien und refaktorisieren Sie bestehenden Code nach und nach
  5. JIT aktivieren - konfigurieren Sie Opcache fuer Ihre Produktionsumgebung

Zusammenfassung#

PHP 8 ist kein kleines Update - es ist eine grundlegende Weiterentwicklung in der Art, wie PHP-Code geschrieben wird. Named Arguments, Union Types, Enums, Readonly-Klassen, Match-Ausdruecke und der JIT-Compiler schaffen ein Oekosystem, in dem PHP mit Sprachen wie TypeScript und Kotlin auf Augenhoehe steht.

Modernes PHP ist typsicher, ausdrucksstark, leistungsfaehig und macht Freude in der Anwendung. Wenn Sie diese Funktionen noch nicht nutzen, ist jetzt der beste Zeitpunkt, damit zu beginnen.


Brauchen Sie Unterstuetzung bei der Migration auf PHP 8 oder beim Aufbau einer modernen Backend-Anwendung? Das Team von MDS Software Solutions Group ist spezialisiert auf die Entwicklung leistungsstarker, skalierbarer Loesungen mit PHP, Node.js und .NET. Kontaktieren Sie uns - wir helfen Ihnen, das volle Potenzial moderner Technologien auszuschoepfen.

Autor
MDS Software Solutions Group

Team von Programmierexperten, die sich auf moderne Webtechnologien spezialisiert haben.

PHP 8 - Moderne Funktionen und Best Practices fuer Entwickler | MDS Software Solutions Group | MDS Software Solutions Group