EMZETT.
Login

Kurz: function f(int|string id, ?array optionen = null, mixed beliebig = null): string|int|null { return id; } function wirf(): never { throw new Exception(“nie zurück”); }

Teil des Kurses PHP

Kapitel 14 von 15 im Kurs PHP (Abschnitt „Fortgeschritten“). Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Typsystem

<?php
declare(strict_types=1);
 
function f(int|string $id, ?array $optionen = null, mixed $beliebig = null): string|int|null { return $id; }
function wirf(): never { throw new Exception("nie zurück"); }
 
interface A {} interface B {}
class AB implements A, B {}
function schnitt(A&B $x): string { return $x::class; }       // Intersection Types (8.1)
echo schnitt(new AB()), " ", f("x"), "\n";
 
class Mensch
{
    public function __construct(
        public readonly string $name,
        public readonly int $alter = 0,
        private array $tags = [],
    ) {}
 
    public function mit(string $name): static
    {
        return new static($name, $this->alter, $this->tags);   // "wither"-Muster für unveränderliche Objekte
    }
}
$m = new Mensch("Mia", alter: 17);
echo $m->mit("Tom")->name, " ", $m->name, "\n";

Ausgabe:

AB x
Tom Mia

match, Null-Safe und weitere Neuerungen

<?php
class Adresse { public function __construct(public ?string $ort = null) {} }
class Kunde { public function __construct(public ?Adresse $adresse = null) {} }
 
$k1 = new Kunde(new Adresse("Berlin"));
$k2 = new Kunde();
echo $k1->adresse?->ort ?? "-", " ", $k2->adresse?->ort ?? "-", "\n";     // Nullsafe-Operator ?->
 
echo str_contains("Hallo", "all") ? "ja" : "nein", " ", 1_000_000, " ", 0o17, "\n";    // 0o: Oktal-Schreibweise (8.1)
 
$liste = [...[1, 2], ...["a" => "x"], 3];                                  // Spread mit String-Keys (8.1)
print_r($liste);
 
function benannt(int $a = 1, int $b = 2, int $c = 3): string { return "$a$b$c"; }
echo benannt(c: 9), " ", benannt(1, c: 5), "\n";

Ausgabe:

Berlin -
ja 1000000 15
Array
(
    [0] => 1
    [1] => 2
    [a] => x
    [2] => 3
)
129 125

Attribute

Attribute (PHP 8) sind strukturierte Metadaten, die Frameworks per Reflection auslesen:

<?php
#[Attribute(Attribute::TARGET_PROPERTY)]
class Laenge
{
    public function __construct(public int $min = 0, public int $max = PHP_INT_MAX) {}
}
 
class Registrierung
{
    #[Laenge(min: 3, max: 10)]
    public string $name = "ab";
}
 
function validiere(object $obj): array
{
    $fehler = [];
    foreach ((new ReflectionClass($obj))->getProperties() as $prop) {
        foreach ($prop->getAttributes(Laenge::class) as $attr) {
            $regel = $attr->newInstance();
            $len = strlen((string) $prop->getValue($obj));
            if ($len < $regel->min || $len > $regel->max) {
                $fehler[] = "{$prop->getName()}: Länge {$len} nicht in {$regel->min}..{$regel->max}";
            }
        }
    }
    return $fehler;
}
print_r(validiere(new Registrierung()));

Ausgabe:

Array
(
    [0] => name: Länge 2 nicht in 3..10
)

First-class Callables, Closures und Generatoren

<?php
$upper = strtoupper(...);
class Rechner { public function doppelt(int $x): int { return $x * 2; } public static function plus1(int $x): int { return $x + 1; } }
$r = new Rechner();
$d = $r->doppelt(...);
$p = Rechner::plus1(...);
echo $upper("a"), $d(4), $p(4), "\n";
 
$pipeline = array_reduce([fn($x) => $x + 1, fn($x) => $x * 10, $d], fn($carry, $f) => fn($x) => $f($carry($x)), fn($x) => $x);
echo $pipeline(1), "\n";
 
function gen_lines(string $text): Generator { foreach (explode("\n", $text) as $i => $l) yield $i + 1 => $l; return "fertig"; }
$g = gen_lines("a\nb");
foreach ($g as $nr => $zeile) echo "$nr:$zeile ";
echo $g->getReturn(), "\n";
function mit_yield_from(): Generator { yield 1; yield from [2, 3]; yield 4; }
echo implode(",", iterator_to_array(mit_yield_from(), false)), "\n";

Ausgabe:

A85
40
1:a 2:b fertig
1,2,3,4

Weitere eingebaute Klassen und Funktionen

<?php
$s = new SplStack(); $s->push(1); $s->push(2);
echo $s->pop(), $s->top(), " ";
$q = new SplQueue(); $q->enqueue("a"); $q->enqueue("b");
echo $q->dequeue(), count($q), " ";
$h = new SplMinHeap(); foreach ([5, 1, 3] as $x) $h->insert($x);
echo $h->extract(), " ";
$o = new SplObjectStorage(); $obj = new stdClass(); $o[$obj] = "daten";
echo $o[$obj], " ";
$ao = new ArrayObject(["x" => 1]);
echo count($ao), "\n";
echo implode(",", array_map(fn($c) => $c->name, [new ReflectionClass('ArrayObject')])), " ", PHP_VERSION_ID >= 80000 ? "PHP 8+" : "alt", " ", PHP_OS_FAMILY !== "" ? "os" : "", "\n";

Ausgabe:

21 a1 1 daten 1
ArrayObject PHP 8+ os

Leistung und Betrieb

  • OPcache speichert kompilierten Bytecode (in Produktion aktivieren)
  • JIT (ab 8.0) bringt vor allem bei rechenintensivem Code etwas
  • PHP-FPM + nginx/Apache als Standard-Setup; FrankenPHP, Swoole, RoadRunner halten die Anwendung zwischen Anfragen im Speicher
  • Caching (APCu, Redis), Profiling (Xdebug, Blackfire)

Merke

  • Union-, Intersection-Typen, never, readonly, Enums, match
  • Nullsafe ?->, benannte Argumente, Constructor Promotion, First-class Callables
  • Attribute für Metadaten, Reflection zum Auslesen
  • OPcache und PHP-FPM für den Betrieb

Übungsaufgabe

Baue ein eigenes Attribut #[Pflicht] und eine Funktion, die prüft, ob alle so markierten Eigenschaften gesetzt sind.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Composer, Namespaces und Tests

Weiter: Referenz und Spickzettel

Alle Kapitel: PHP im Überblick