Files
guides/templates/Referenz/IntermediateGuide.md
2026-05-25 19:33:48 +02:00

84 KiB
Raw Blame History

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>PHP Fortgeschritten-Guide</title>
<style>
@page {
  size: A4;
  margin: 22mm 20mm 20mm 20mm;
  @bottom-center {
    content: counter(page) " / " counter(pages);
    font-family: -apple-system, "Segoe UI", sans-serif;
    font-size: 8pt;
    color: #888;
  }
  @bottom-right {
    content: "PHP Fortgeschritten-Guide";
    font-family: -apple-system, "Segoe UI", sans-serif;
    font-size: 8pt;
    color: #888;
  }
}
@page :first {
  margin: 0;
  @bottom-center { content: none; }
  @bottom-right { content: none; }
}

* { box-sizing: border-box; margin: 0; padding: 0; }

:root {
  --php: #777BB4;
  --php-dark: #4F5B93;
  --php-darker: #2C3E66;
  --ink: #1a1a1a;
  --muted: #5a6470;
  --line: #d8dde3;
  --bg-soft: #f5f5fb;
  --code-bg: #1e2a3a;
  --code-fg: #e6e6e6;
  --plus: #2c8a3e;
  --minus: #c0392b;
}

html, body {
  font-family: Charter, "Source Serif Pro", Georgia, serif;
  color: var(--ink);
  font-size: 10.5pt;
  line-height: 1.55;
}

/* ===== COVER ===== */
.cover {
  width: 210mm;
  height: 297mm;
  padding: 35mm 25mm;
  background: linear-gradient(135deg, var(--php-dark) 0%, var(--php-darker) 100%);
  color: white;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  page-break-after: always;
}
.cover-top { display: flex; align-items: center; gap: 8mm; }
.cover-logo {
  width: 28mm; height: 28mm;
  background: linear-gradient(135deg, var(--php) 0%, white 100%);
  border-radius: 7mm;
  display: flex; align-items: center; justify-content: center;
  font-family: -apple-system, sans-serif;
  font-size: 22pt; font-weight: 800;
  color: var(--php-dark);
}
.cover-meta {
  font-family: -apple-system, sans-serif;
  font-size: 9pt;
  text-transform: uppercase;
  letter-spacing: 2pt;
  opacity: 0.8;
}
.cover-main h1 {
  font-family: -apple-system, sans-serif;
  font-size: 56pt;
  font-weight: 800;
  letter-spacing: -2pt;
  line-height: 0.95;
  margin-bottom: 8mm;
}
.cover-main h1 .accent { color: var(--php); }
.cover-main .subtitle {
  font-size: 16pt;
  font-weight: 400;
  line-height: 1.3;
  opacity: 0.9;
  font-family: -apple-system, sans-serif;
  max-width: 130mm;
}
.cover-bottom {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 10mm;
  align-items: end;
  font-family: -apple-system, sans-serif;
}
.cover-promise {
  font-size: 10pt;
  line-height: 1.5;
  opacity: 0.85;
  max-width: 110mm;
  padding-top: 4mm;
  border-top: 1pt solid rgba(255,255,255,0.3);
}
.cover-promise b { color: var(--php); text-transform: uppercase; letter-spacing: 1pt; font-size: 8pt; display: block; margin-bottom: 2mm; }
.cover-tag {
  background: var(--php);
  color: white;
  padding: 3mm 6mm;
  border-radius: 2mm;
  font-weight: 800;
  font-size: 11pt;
}

/* ===== TOC ===== */
.toc { page-break-after: always; }
.toc h2 {
  font-family: -apple-system, sans-serif;
  font-size: 24pt;
  font-weight: 800;
  color: var(--php-dark);
  margin-bottom: 6mm;
  border-bottom: 2pt solid var(--php);
  padding-bottom: 3mm;
}
.toc-section {
  font-family: -apple-system, sans-serif;
  font-size: 9pt;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1pt;
  color: var(--muted);
  margin: 6mm 0 2mm 0;
}
.toc-item {
  display: grid;
  grid-template-columns: 8mm 1fr auto;
  gap: 4mm;
  padding: 2.5mm 0;
  border-bottom: 0.3pt solid var(--line);
  align-items: baseline;
}
.toc-num {
  font-family: -apple-system, sans-serif;
  font-size: 14pt;
  font-weight: 800;
  color: var(--php);
  line-height: 1;
}
.toc-text { font-family: -apple-system, sans-serif; }
.toc-text h3 {
  font-size: 10.5pt;
  font-weight: 700;
  color: var(--ink);
  margin-bottom: 0.5mm;
}
.toc-text p {
  font-size: 8.5pt;
  color: var(--muted);
  margin: 0;
}
.toc-time {
  font-family: -apple-system, sans-serif;
  font-size: 7.5pt;
  color: var(--muted);
  background: var(--bg-soft);
  padding: 0.8mm 2.5mm;
  border-radius: 1.5mm;
  white-space: nowrap;
}

/* ===== CHAPTER ===== */
.chapter { page-break-before: always; }
.chapter-head {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 6mm;
  align-items: center;
  border-bottom: 2pt solid var(--ink);
  padding-bottom: 4mm;
  margin-bottom: 6mm;
}
.chapter-num {
  font-family: -apple-system, sans-serif;
  font-size: 42pt;
  font-weight: 800;
  color: var(--php);
  line-height: 0.9;
  width: 22mm;
  text-align: center;
}
.chapter-title h1 {
  font-family: -apple-system, sans-serif;
  font-size: 22pt;
  font-weight: 800;
  color: var(--php-dark);
  letter-spacing: -0.5pt;
  line-height: 1.1;
}
.chapter-title .subtitle {
  font-family: -apple-system, sans-serif;
  font-size: 11pt;
  color: var(--muted);
  margin-top: 1.5mm;
}

/* ===== GAP ===== */
.gap {
  background: var(--bg-soft);
  border-left: 3pt solid var(--php);
  padding: 4mm 5mm;
  margin: 4mm 0 6mm 0;
  font-style: italic;
  font-size: 10.5pt;
}
.gap b {
  font-style: normal;
  color: var(--php-dark);
  font-family: -apple-system, sans-serif;
  text-transform: uppercase;
  font-size: 8pt;
  letter-spacing: 1pt;
  display: block;
  margin-bottom: 1.5mm;
}

/* ===== SECTIONS ===== */
.chapter h2 {
  font-family: -apple-system, sans-serif;
  font-size: 14pt;
  font-weight: 700;
  color: var(--php-dark);
  margin: 7mm 0 3mm 0;
  page-break-after: avoid;
}
.chapter h3 {
  font-family: -apple-system, sans-serif;
  font-size: 11pt;
  font-weight: 700;
  color: var(--ink);
  margin: 5mm 0 2mm 0;
  page-break-after: avoid;
}
.chapter p {
  margin-bottom: 3mm;
  text-align: justify;
  hyphens: auto;
}
.chapter p b { color: var(--php-dark); }
.chapter ul, .chapter ol { margin: 2mm 0 4mm 6mm; }
.chapter li { margin-bottom: 1.5mm; }

/* ===== CODE ===== */
.chapter pre {
  background: var(--code-bg);
  color: var(--code-fg);
  font-family: "SF Mono", Consolas, monospace;
  font-size: 8.5pt;
  line-height: 1.5;
  padding: 3mm 4mm;
  border-radius: 2mm;
  margin: 3mm 0 4mm 0;
  white-space: pre;
  overflow: hidden;
  page-break-inside: avoid;
}
.c { color: #6b8aae; font-style: italic; }
.k { color: #ff79c6; }
.s { color: #f1c40f; }
.f { color: #50fa7b; }
.t { color: #8be9fd; }
.v { color: #ffb86c; }

code.inline {
  font-family: "SF Mono", Consolas, monospace;
  font-size: 9pt;
  background: var(--bg-soft);
  padding: 0.3mm 1.5mm;
  border-radius: 1mm;
  color: var(--php-dark);
}

/* ===== CALLOUTS ===== */
.callout {
  border-radius: 2mm;
  padding: 3mm 4mm;
  margin: 4mm 0;
  font-size: 10pt;
  page-break-inside: avoid;
  display: grid;
  grid-template-columns: 6mm 1fr;
  gap: 3mm;
  align-items: start;
}
.callout-icon {
  font-family: -apple-system, sans-serif;
  font-weight: 800;
  font-size: 14pt;
  line-height: 1;
  text-align: center;
}
.callout-body > b:first-child {
  font-family: -apple-system, sans-serif;
  text-transform: uppercase;
  font-size: 8pt;
  letter-spacing: 1pt;
  display: block;
  margin-bottom: 1.5mm;
}
.callout.tip { background: #e8f4ea; border-left: 3pt solid var(--plus); }
.callout.tip .callout-icon, .callout.tip .callout-body > b:first-child { color: var(--plus); }
.callout.warn { background: #fdecea; border-left: 3pt solid var(--minus); }
.callout.warn .callout-icon, .callout.warn .callout-body > b:first-child { color: var(--minus); }
.callout.note { background: var(--bg-soft); border-left: 3pt solid var(--php); }
.callout.note .callout-icon, .callout.note .callout-body > b:first-child { color: var(--php-dark); }

/* ===== RECALL ===== */
.recall {
  background: linear-gradient(135deg, var(--php-dark) 0%, var(--php-darker) 100%);
  color: white;
  padding: 5mm 6mm;
  border-radius: 2mm;
  margin: 6mm 0 0 0;
  page-break-inside: avoid;
}
.recall b {
  font-family: -apple-system, sans-serif;
  display: block;
  text-transform: uppercase;
  letter-spacing: 1.5pt;
  font-size: 8.5pt;
  color: var(--php);
  margin-bottom: 2.5mm;
}
.recall ol {
  margin: 0;
  padding-left: 5mm;
  font-size: 10pt;
}
.recall li {
  margin-bottom: 1.5mm;
  color: rgba(255,255,255,0.95);
}
.recall li::marker { color: var(--php); font-weight: 700; }

/* ===== TABLES ===== */
.chapter table {
  width: 100%;
  border-collapse: collapse;
  margin: 3mm 0 4mm 0;
  font-size: 9.5pt;
  font-family: -apple-system, sans-serif;
}
.chapter th {
  background: var(--php-dark);
  color: white;
  padding: 2mm 3mm;
  text-align: left;
  font-weight: 700;
  font-size: 9pt;
  text-transform: uppercase;
  letter-spacing: 0.5pt;
}
.chapter td {
  padding: 2mm 3mm;
  border-bottom: 0.5pt solid var(--line);
  vertical-align: top;
}
.chapter td code {
  font-family: "SF Mono", Consolas, monospace;
  font-size: 8.5pt;
  color: var(--php-dark);
}

/* ===== ENDING ===== */
.ending { page-break-before: always; }
.ending h1 {
  font-family: -apple-system, sans-serif;
  font-size: 28pt;
  font-weight: 800;
  color: var(--php-dark);
  margin-bottom: 6mm;
  border-bottom: 2pt solid var(--php);
  padding-bottom: 3mm;
}
.ending h2 {
  font-family: -apple-system, sans-serif;
  font-size: 14pt;
  font-weight: 700;
  color: var(--php-dark);
  margin: 7mm 0 3mm 0;
}
.spaced-plan {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 3mm;
  margin: 4mm 0;
}
.spaced-day {
  background: var(--bg-soft);
  border-top: 3pt solid var(--php);
  padding: 4mm 3mm;
  border-radius: 2mm;
}
.spaced-day b {
  font-family: -apple-system, sans-serif;
  display: block;
  color: var(--php-dark);
  font-size: 9pt;
  text-transform: uppercase;
  letter-spacing: 0.5pt;
  margin-bottom: 2mm;
}
.spaced-day p {
  font-size: 9pt;
  margin: 0;
  text-align: left;
}
</style>
</head>
<body>

<!-- ===== COVER ===== -->
<section class="cover">
  <div class="cover-top">
    <div class="cover-logo">php</div>
    <div class="cover-meta">Fortgeschritten-Guide · 3h · Stand 2026</div>
  </div>

  <div class="cover-main">
    <h1>PHP<br><span class="accent">tiefer.</span></h1>
    <p class="subtitle">Von Interfaces und Generators bis Tests  Production-PHP, das hält.</p>
  </div>

  <div class="cover-bottom">
    <div class="cover-promise">
      <b>Was du danach kannst</b>
      OOP-Patterns idiomatisch einsetzen · funktional mit Closures und Generators arbeiten · Attribute für saubere Metadaten nutzen · Datenbanken mit PDO sicher anbinden · APIs konsumieren · mit PHPStan und PHPUnit produktionsreifen Code schreiben.
    </div>
    <div class="cover-tag">PHP 8.4</div>
  </div>
</section>

<!-- ===== TOC ===== -->
<section class="toc">
  <h2>Inhalt</h2>

  <div class="toc-section">Teil 1 · OOP-Patterns</div>

  <div class="toc-item">
    <div class="toc-num">1</div>
    <div class="toc-text">
      <h3>Interfaces und abstrakte Klassen</h3>
      <p>Kontrakte definieren, Implementierungen trennen</p>
    </div>
    <div class="toc-time">15 Min</div>
  </div>

  <div class="toc-item">
    <div class="toc-num">2</div>
    <div class="toc-text">
      <h3>Traits</h3>
      <p>Code-Reuse jenseits klassischer Vererbung</p>
    </div>
    <div class="toc-time">15 Min</div>
  </div>

  <div class="toc-item">
    <div class="toc-num">3</div>
    <div class="toc-text">
      <h3>Enums richtig nutzen</h3>
      <p>Backed Enums, Methods, Pattern-Matching</p>
    </div>
    <div class="toc-time">15 Min</div>
  </div>

  <div class="toc-item">
    <div class="toc-num">4</div>
    <div class="toc-text">
      <h3>Attribute (PHP 8)</h3>
      <p>Metadaten typensicher statt Annotations</p>
    </div>
    <div class="toc-time">15 Min</div>
  </div>

  <div class="toc-section">Teil 2 · Funktional &amp; Generators</div>

  <div class="toc-item">
    <div class="toc-num">5</div>
    <div class="toc-text">
      <h3>Closures und Arrow Functions</h3>
      <p>First-Class Callables, use-Capture</p>
    </div>
    <div class="toc-time">15 Min</div>
  </div>

  <div class="toc-item">
    <div class="toc-num">6</div>
    <div class="toc-text">
      <h3>Higher-Order Functions</h3>
      <p>array_map, array_filter, array_reduce in der Praxis</p>
    </div>
    <div class="toc-time">15 Min</div>
  </div>

  <div class="toc-item">
    <div class="toc-num">7</div>
    <div class="toc-text">
      <h3>Iterators und Generators</h3>
      <p>Lazy Evaluation mit yield</p>
    </div>
    <div class="toc-time">15 Min</div>
  </div>

  <div class="toc-item">
    <div class="toc-num">8</div>
    <div class="toc-text">
      <h3>Generics via PHPDoc</h3>
      <p>Templates ohne native Generics</p>
    </div>
    <div class="toc-time">15 Min</div>
  </div>

  <div class="toc-section">Teil 3 · Production-Tools</div>

  <div class="toc-item">
    <div class="toc-num">9</div>
    <div class="toc-text">
      <h3>PDO und sichere Datenbanken</h3>
      <p>Prepared Statements, Transaktionen</p>
    </div>
    <div class="toc-time">15 Min</div>
  </div>

  <div class="toc-item">
    <div class="toc-num">10</div>
    <div class="toc-text">
      <h3>HTTP-Requests mit Guzzle</h3>
      <p>APIs konsumieren, async Requests</p>
    </div>
    <div class="toc-time">15 Min</div>
  </div>

  <div class="toc-item">
    <div class="toc-num">11</div>
    <div class="toc-text">
      <h3>PHPStan und Static Analysis</h3>
      <p>Bugs vor der Ausführung finden</p>
    </div>
    <div class="toc-time">15 Min</div>
  </div>

  <div class="toc-item">
    <div class="toc-num">12</div>
    <div class="toc-text">
      <h3>Testen mit PHPUnit</h3>
      <p>Unit-Tests, Mocks, Data Providers</p>
    </div>
    <div class="toc-time">15 Min</div>
  </div>
</section>

<!-- ===== KAPITEL 1 ===== -->
<section class="chapter">
  <div class="chapter-head">
    <div class="chapter-num">01</div>
    <div class="chapter-title">
      <h1>Interfaces und abstrakte Klassen</h1>
      <div class="subtitle">Kontrakte definieren, Implementierungen trennen</div>
    </div>
  </div>

  <div class="gap">
    <b>Frage zum Einstieg</b>
    Du hast drei verschiedene Logger  Datei, Datenbank, Sentry. Sie sollen austauschbar sein, ohne dass der aufrufende Code etwas merkt. Wie zwingst du sie zu einer gemeinsamen API, ohne sie zu Geschwistern in einer Klassen-Hierarchie zu machen? Interfaces sind die Antwort.
  </div>

  <h2>Interface als reiner Kontrakt</h2>
  <p>Ein <b>Interface</b> definiert nur, <i>was</i> Methoden tun, nicht <i>wie</i>. Klassen, die das Interface implementieren, müssen alle Methoden bereitstellen:</p>

<pre><span class="k">interface</span> <span class="t">LoggerInterface</span> {
  <span class="k">public function</span> <span class="f">info</span>(<span class="t">string</span> <span class="v">$message</span>): <span class="t">void</span>;
  <span class="k">public function</span> <span class="f">error</span>(<span class="t">string</span> <span class="v">$message</span>, <span class="t">array</span> <span class="v">$context</span> = []): <span class="t">void</span>;
}

<span class="k">class</span> <span class="t">FileLogger</span> <span class="k">implements</span> <span class="t">LoggerInterface</span> {
  <span class="k">public function</span> <span class="f">__construct</span>(<span class="k">private readonly</span> <span class="t">string</span> <span class="v">$path</span>) {}

  <span class="k">public function</span> <span class="f">info</span>(<span class="t">string</span> <span class="v">$message</span>): <span class="t">void</span> {
    <span class="f">file_put_contents</span>(<span class="v">$this</span>-&gt;path, <span class="s">"[INFO] </span><span class="v">$message</span><span class="s">\n"</span>, FILE_APPEND);
  }

  <span class="k">public function</span> <span class="f">error</span>(<span class="t">string</span> <span class="v">$message</span>, <span class="t">array</span> <span class="v">$context</span> = []): <span class="t">void</span> {
    <span class="f">file_put_contents</span>(<span class="v">$this</span>-&gt;path, <span class="s">"[ERROR] </span><span class="v">$message</span><span class="s">\n"</span>, FILE_APPEND);
  }
}</pre>

  <p>Der Gewinn: jede Funktion kann den Typ <code class="inline">LoggerInterface</code> erwarten und akzeptiert dadurch <i>jede</i> Implementierung  ohne deren Klasse zu kennen.</p>

  <h2>Dependency Injection in Aktion</h2>

<pre><span class="k">class</span> <span class="t">OrderService</span> {
  <span class="k">public function</span> <span class="f">__construct</span>(
    <span class="k">private readonly</span> <span class="t">LoggerInterface</span> <span class="v">$logger</span>
  ) {}

  <span class="k">public function</span> <span class="f">place</span>(<span class="t">Order</span> <span class="v">$order</span>): <span class="t">void</span> {
    <span class="v">$this</span>-&gt;logger-&gt;<span class="f">info</span>(<span class="s">"Order </span><span class="v">$order</span><span class="s">-&gt;id placed"</span>);
    <span class="c">// ...</span>
  }
}

<span class="c">// In Tests: Fake-Logger reinreichen</span>
<span class="c">// In Production: FileLogger oder SentryLogger</span>
<span class="v">$service</span> = <span class="k">new</span> <span class="t">OrderService</span>(<span class="k">new</span> <span class="t">FileLogger</span>(<span class="s">'/var/log/app.log'</span>));</pre>

  <h2>Abstrakte Klassen</h2>
  <p>Wenn du <b>gemeinsamen Code</b> teilen willst, aber bestimmte Methoden offen lassen, nimmst du eine abstrakte Klasse. Sie kann Properties, fertige Methoden und abstrakte Methoden mischen:</p>

<pre><span class="k">abstract class</span> <span class="t">Notification</span> {
  <span class="k">public function</span> <span class="f">__construct</span>(<span class="k">protected readonly</span> <span class="t">string</span> <span class="v">$recipient</span>) {}

  <span class="c">// Fertige Methode</span>
  <span class="k">public function</span> <span class="f">send</span>(<span class="t">string</span> <span class="v">$message</span>): <span class="t">void</span> {
    <span class="v">$formatted</span> = <span class="v">$this</span>-&gt;<span class="f">format</span>(<span class="v">$message</span>);
    <span class="v">$this</span>-&gt;<span class="f">deliver</span>(<span class="v">$formatted</span>);
  }

  <span class="c">// Muss von Subklassen implementiert werden</span>
  <span class="k">abstract protected function</span> <span class="f">format</span>(<span class="t">string</span> <span class="v">$msg</span>): <span class="t">string</span>;
  <span class="k">abstract protected function</span> <span class="f">deliver</span>(<span class="t">string</span> <span class="v">$msg</span>): <span class="t">void</span>;
}</pre>

  <h2>Interface oder abstrakte Klasse?</h2>

  <table>
    <tr><th>Use Case</th><th>Wähle</th></tr>
    <tr><td>Reiner Kontrakt</td><td>Interface</td></tr>
    <tr><td>Mehrere "Verträge" erfüllen</td><td>Interface (mehrfach implementierbar)</td></tr>
    <tr><td>Gemeinsame Code-Basis teilen</td><td>Abstrakte Klasse</td></tr>
    <tr><td>Template Method Pattern</td><td>Abstrakte Klasse</td></tr>
  </table>

  <div class="callout tip">
    <div class="callout-icon">✓</div>
    <div class="callout-body">
      <b>PSR-Interfaces nutzen</b>
      Die PHP-FIG hat Standard-Interfaces für Logger, Container, HTTP und mehr definiert: <code class="inline">Psr\Log\LoggerInterface</code>, <code class="inline">Psr\Container\ContainerInterface</code>. Wenn du deine Klassen daran ausrichtest, passen sie zu jedem Framework.
    </div>
  </div>

  <div class="recall">
    <b>Recall</b>
    <ol>
      <li>Was ist der Hauptunterschied zwischen Interface und abstrakter Klasse?</li>
      <li>Wie viele Interfaces darf eine Klasse implementieren?</li>
      <li>Was ist Dependency Injection in einem Satz?</li>
    </ol>
  </div>
</section>

<!-- ===== KAPITEL 2 ===== -->
<section class="chapter">
  <div class="chapter-head">
    <div class="chapter-num">02</div>
    <div class="chapter-title">
      <h1>Traits</h1>
      <div class="subtitle">Code-Reuse jenseits klassischer Vererbung</div>
    </div>
  </div>

  <div class="gap">
    <b>Frage zum Einstieg</b>
    PHP unterstützt nur Einfach-Vererbung  eine Klasse hat genau eine Eltern-Klasse. Aber was, wenn zwei nicht verwandte Klassen denselben Code brauchen? Logging-Methoden in <code>User</code> und <code>Order</code> kopieren? Traits lösen das eleganter.
  </div>

  <h2>Trait: wiederverwendbarer Code-Block</h2>
  <p>Ein <b>Trait</b> ist wie eine Klasse, aber du erstellst keine Instanz davon. Stattdessen wirst du in andere Klassen "einkopiert". Mehrere Traits pro Klasse sind erlaubt:</p>

<pre><span class="k">trait</span> <span class="t">Timestampable</span> {
  <span class="k">private</span> ?\DateTimeImmutable <span class="v">$createdAt</span> = <span class="k">null</span>;
  <span class="k">private</span> ?\DateTimeImmutable <span class="v">$updatedAt</span> = <span class="k">null</span>;

  <span class="k">public function</span> <span class="f">touch</span>(): <span class="t">void</span> {
    <span class="v">$this</span>-&gt;updatedAt = <span class="k">new</span> \DateTimeImmutable();
    <span class="v">$this</span>-&gt;createdAt ??= <span class="v">$this</span>-&gt;updatedAt;
  }

  <span class="k">public function</span> <span class="f">getCreatedAt</span>(): ?\DateTimeImmutable {
    <span class="k">return</span> <span class="v">$this</span>-&gt;createdAt;
  }
}

<span class="k">class</span> <span class="t">Article</span> {
  <span class="k">use</span> <span class="t">Timestampable</span>;

  <span class="k">public function</span> <span class="f">__construct</span>(<span class="k">public</span> <span class="t">string</span> <span class="v">$title</span>) {
    <span class="v">$this</span>-&gt;<span class="f">touch</span>();
  }
}

<span class="k">class</span> <span class="t">Comment</span> {
  <span class="k">use</span> <span class="t">Timestampable</span>;       <span class="c">// gleiche Methoden, ohne Doppelung</span>
}</pre>

  <h2>Mehrere Traits kombinieren</h2>

<pre><span class="k">trait</span> <span class="t">SoftDeletable</span> {
  <span class="k">private</span> ?\DateTimeImmutable <span class="v">$deletedAt</span> = <span class="k">null</span>;

  <span class="k">public function</span> <span class="f">delete</span>(): <span class="t">void</span> {
    <span class="v">$this</span>-&gt;deletedAt = <span class="k">new</span> \DateTimeImmutable();
  }

  <span class="k">public function</span> <span class="f">isDeleted</span>(): <span class="t">bool</span> {
    <span class="k">return</span> <span class="v">$this</span>-&gt;deletedAt !== <span class="k">null</span>;
  }
}

<span class="k">class</span> <span class="t">Article</span> {
  <span class="k">use</span> <span class="t">Timestampable</span>, <span class="t">SoftDeletable</span>;   <span class="c">// beide Sets von Methoden</span>
}</pre>

  <h2>Konflikt-Auflösung</h2>
  <p>Wenn zwei Traits Methoden mit gleichem Namen haben, gibt es einen Konflikt. PHP zwingt dich, explizit zu wählen:</p>

<pre><span class="k">trait</span> <span class="t">A</span> { <span class="k">public function</span> <span class="f">hello</span>(): <span class="t">string</span> { <span class="k">return</span> <span class="s">'A'</span>; } }
<span class="k">trait</span> <span class="t">B</span> { <span class="k">public function</span> <span class="f">hello</span>(): <span class="t">string</span> { <span class="k">return</span> <span class="s">'B'</span>; } }

<span class="k">class</span> <span class="t">X</span> {
  <span class="k">use</span> <span class="t">A</span>, <span class="t">B</span> {
    A::hello <span class="k">insteadof</span> B;       <span class="c">// nimm A's hello, ignoriere B's</span>
    B::hello <span class="k">as</span> helloFromB;     <span class="c">// B's hello als 'helloFromB' verfügbar</span>
  }
}</pre>

  <div class="callout warn">
    <div class="callout-icon">!</div>
    <div class="callout-body">
      <b>Traits sind kein Multi-Inheritance-Ersatz</b>
      Traits machen <b>Code-Reuse</b>, keinen <b>Typ-Polymorphismus</b>. Eine Klasse mit Trait ist nicht "vom Typ Trait"  nutze dafür Interfaces. Faustregel: Trait für Implementierung, Interface für Vertrag.
    </div>
  </div>

  <h2>Wann Trait, wann Komposition?</h2>
  <p>Traits sind verlockend, aber sie binden Code <i>statisch</i> in Klassen ein. Eine moderne Alternative ist <b>Komposition</b>: ein Service-Objekt als Property, das die Logik kapselt. Faustregel:</p>

  <ul>
    <li>Trait, wenn die Logik <i>Teil des Objekts ist</i> (Timestamps, Soft-Delete)</li>
    <li>Komposition, wenn die Logik <i>austauschbar</i> sein soll (verschiedene Logger, Strategien)</li>
  </ul>

  <div class="recall">
    <b>Recall</b>
    <ol>
      <li>Was ist der Unterschied zwischen Trait und abstrakter Klasse?</li>
      <li>Wie löst du Methoden-Konflikte zwischen zwei Traits?</li>
      <li>Wann nutzt du Komposition statt Trait?</li>
    </ol>
  </div>
</section>

<!-- ===== KAPITEL 3 ===== -->
<section class="chapter">
  <div class="chapter-head">
    <div class="chapter-num">03</div>
    <div class="chapter-title">
      <h1>Enums richtig nutzen</h1>
      <div class="subtitle">Backed Enums, Methods, Pattern-Matching</div>
    </div>
  </div>

  <div class="gap">
    <b>Frage zum Einstieg</b>
    Früher waren Status-Werte oft Strings ("draft", "published") oder Klassen-Konstanten  fehleranfällig und ohne Typ-Sicherheit. PHP 8.1 hat echte <b>Enums</b> gebracht. Wie nutzt du sie idiomatisch?
  </div>

  <h2>Einfache Enums</h2>

<pre><span class="k">enum</span> <span class="t">Status</span> {
  <span class="k">case</span> <span class="t">Draft</span>;
  <span class="k">case</span> <span class="t">Published</span>;
  <span class="k">case</span> <span class="t">Archived</span>;
}

<span class="v">$status</span> = <span class="t">Status</span>::<span class="t">Draft</span>;

<span class="k">if</span> (<span class="v">$status</span> === <span class="t">Status</span>::<span class="t">Draft</span>) {
  <span class="k">echo</span> <span class="s">'noch nicht veröffentlicht'</span>;
}</pre>

  <p>Die Cases sind Singletons  jede Verwendung von <code class="inline">Status::Draft</code> ist dasselbe Objekt. Vergleich mit <code class="inline">===</code> funktioniert wie erwartet.</p>

  <h2>Backed Enums (mit Wert)</h2>
  <p>Für Serialisierung  etwa in Datenbank oder JSON  brauchst du <b>Backed Enums</b> mit konkretem String oder Int als Backing-Value:</p>

<pre><span class="k">enum</span> <span class="t">Status</span>: <span class="t">string</span> {
  <span class="k">case</span> <span class="t">Draft</span> = <span class="s">'draft'</span>;
  <span class="k">case</span> <span class="t">Published</span> = <span class="s">'published'</span>;
  <span class="k">case</span> <span class="t">Archived</span> = <span class="s">'archived'</span>;
}

<span class="c">// Wert auslesen</span>
<span class="t">Status</span>::<span class="t">Draft</span>-&gt;value;        <span class="c">// 'draft'</span>

<span class="c">// Aus Wert zurückbauen</span>
<span class="t">Status</span>::<span class="f">from</span>(<span class="s">'draft'</span>);      <span class="c">// Status::Draft</span>
<span class="t">Status</span>::<span class="f">tryFrom</span>(<span class="s">'unknown'</span>); <span class="c">// null (statt Exception)</span></pre>

  <h2>Methoden in Enums</h2>
  <p>Enums dürfen Methoden haben. Damit packst du Verhalten direkt zum Wert, statt es in Helper-Funktionen zu verstecken:</p>

<pre><span class="k">enum</span> <span class="t">Status</span>: <span class="t">string</span> {
  <span class="k">case</span> <span class="t">Draft</span> = <span class="s">'draft'</span>;
  <span class="k">case</span> <span class="t">Published</span> = <span class="s">'published'</span>;
  <span class="k">case</span> <span class="t">Archived</span> = <span class="s">'archived'</span>;

  <span class="k">public function</span> <span class="f">label</span>(): <span class="t">string</span> {
    <span class="k">return match</span>(<span class="v">$this</span>) {
      <span class="t">self</span>::<span class="t">Draft</span> =&gt; <span class="s">'Entwurf'</span>,
      <span class="t">self</span>::<span class="t">Published</span> =&gt; <span class="s">'Veröffentlicht'</span>,
      <span class="t">self</span>::<span class="t">Archived</span> =&gt; <span class="s">'Archiv'</span>,
    };
  }

  <span class="k">public function</span> <span class="f">isPublic</span>(): <span class="t">bool</span> {
    <span class="k">return</span> <span class="v">$this</span> === <span class="t">self</span>::<span class="t">Published</span>;
  }
}

<span class="t">Status</span>::<span class="t">Draft</span>-&gt;<span class="f">label</span>();      <span class="c">// 'Entwurf'</span>
<span class="t">Status</span>::<span class="t">Draft</span>-&gt;<span class="f">isPublic</span>();   <span class="c">// false</span></pre>

  <h2>Alle Cases iterieren</h2>

<pre><span class="k">foreach</span> (<span class="t">Status</span>::<span class="f">cases</span>() <span class="k">as</span> <span class="v">$status</span>) {
  <span class="k">echo</span> <span class="v">$status</span>-&gt;<span class="f">label</span>() . <span class="s">"\n"</span>;
}
<span class="c">// Entwurf</span>
<span class="c">// Veröffentlicht</span>
<span class="c">// Archiv</span></pre>

  <p>Praktisch für Dropdowns, Validierung oder Migration zwischen Status.</p>

  <div class="callout tip">
    <div class="callout-icon">✓</div>
    <div class="callout-body">
      <b>Statt String-Constants immer Enum</b>
      Wenn du heute <code class="inline">const STATUS_DRAFT = 'draft'</code> schreibst, ist es fast immer besser, ein Enum zu nehmen. Typ-Sicherheit, Methoden direkt am Wert, IDE-Vervollständigung, exhaustive match-Checks.
    </div>
  </div>

  <div class="recall">
    <b>Recall</b>
    <ol>
      <li>Was ist der Unterschied zwischen einfachem Enum und Backed Enum?</li>
      <li>Wie konvertierst du einen String sicher zu einem Enum-Case?</li>
      <li>Was gibt <code style="color:#777BB4">Status::cases()</code> zurück?</li>
    </ol>
  </div>
</section>

<!-- ===== KAPITEL 4 ===== -->
<section class="chapter">
  <div class="chapter-head">
    <div class="chapter-num">04</div>
    <div class="chapter-title">
      <h1>Attribute (PHP 8)</h1>
      <div class="subtitle">Metadaten typensicher statt Annotations</div>
    </div>
  </div>

  <div class="gap">
    <b>Frage zum Einstieg</b>
    Bevor PHP 8 nutzten Frameworks wie Symfony und Doctrine <b>Doc-Block-Annotations</b> für Metadaten: <code>/** @Route("/users") */</code>. Kommentare, die geparst werden  brüchig, ohne Typ-Check. PHP 8 brachte native <b>Attribute</b>. Wie schreibst und nutzt du sie?
  </div>

  <h2>Attribut definieren</h2>
  <p>Ein Attribut ist eine Klasse, die mit <code class="inline">#[Attribute]</code> markiert ist. Sie kann Konstruktor-Parameter haben wie jede andere Klasse:</p>

<pre><span class="k">#[\Attribute(\Attribute::TARGET_METHOD)]</span>
<span class="k">class</span> <span class="t">Route</span> {
  <span class="k">public function</span> <span class="f">__construct</span>(
    <span class="k">public readonly</span> <span class="t">string</span> <span class="v">$path</span>,
    <span class="k">public readonly</span> <span class="t">string</span> <span class="v">$method</span> = <span class="s">'GET'</span>,
  ) {}
}</pre>

  <p>Das <code class="inline">TARGET_METHOD</code> sagt: dieses Attribut darf nur auf Methoden. Andere Optionen: <code class="inline">TARGET_CLASS</code>, <code class="inline">TARGET_PROPERTY</code>, <code class="inline">TARGET_PARAMETER</code>.</p>

  <h2>Attribut anwenden</h2>

<pre><span class="k">class</span> <span class="t">UserController</span> {
  <span class="k">#[Route(<span class="s">'/users'</span>)]</span>
  <span class="k">public function</span> <span class="f">list</span>(): Response { <span class="c">/* ... */</span> }

  <span class="k">#[Route(<span class="s">'/users/{id}'</span>, method: <span class="s">'GET'</span>)]</span>
  <span class="k">public function</span> <span class="f">show</span>(<span class="t">int</span> <span class="v">$id</span>): Response { <span class="c">/* ... */</span> }

  <span class="k">#[Route(<span class="s">'/users'</span>, method: <span class="s">'POST'</span>)]</span>
  <span class="k">public function</span> <span class="f">create</span>(): Response { <span class="c">/* ... */</span> }
}</pre>

  <h2>Attribute zur Laufzeit auslesen</h2>
  <p>Mit der Reflection-API liest du Attribute aus Klassen, Methoden oder Properties aus  das ist die Grundlage für Frameworks wie Symfony, Doctrine oder ORM-Libraries:</p>

<pre><span class="v">$reflection</span> = <span class="k">new</span> \<span class="t">ReflectionClass</span>(<span class="t">UserController</span>::<span class="k">class</span>);

<span class="k">foreach</span> (<span class="v">$reflection</span>-&gt;<span class="f">getMethods</span>() <span class="k">as</span> <span class="v">$method</span>) {
  <span class="k">foreach</span> (<span class="v">$method</span>-&gt;<span class="f">getAttributes</span>(<span class="t">Route</span>::<span class="k">class</span>) <span class="k">as</span> <span class="v">$attr</span>) {
    <span class="v">$route</span> = <span class="v">$attr</span>-&gt;<span class="f">newInstance</span>();      <span class="c">// Route-Instanz</span>
    <span class="k">echo</span> <span class="s">"</span><span class="v">$route</span><span class="s">-&gt;method </span><span class="v">$route</span><span class="s">-&gt;path → "</span> . <span class="v">$method</span>-&gt;name . <span class="s">"\n"</span>;
  }
}
<span class="c">// Output:</span>
<span class="c">// GET /users → list</span>
<span class="c">// GET /users/{id} → show</span>
<span class="c">// POST /users → create</span></pre>

  <h2>Typische Anwendungsfälle</h2>

  <table>
    <tr><th>Framework</th><th>Beispiel-Attribute</th></tr>
    <tr><td>Symfony</td><td><code>#[Route]</code>, <code>#[AsCommand]</code></td></tr>
    <tr><td>Doctrine</td><td><code>#[ORM\Entity]</code>, <code>#[ORM\Column]</code></td></tr>
    <tr><td>PHPUnit</td><td><code>#[DataProvider]</code>, <code>#[Test]</code></td></tr>
    <tr><td>Validator</td><td><code>#[Assert\NotBlank]</code></td></tr>
  </table>

  <div class="callout note">
    <div class="callout-icon">i</div>
    <div class="callout-body">
      <b>Attribute statt Doc-Block-Annotations</b>
      Alter Code mit <code class="inline">/** @Route("/users") */</code> funktioniert noch, aber das Doctrine/Symfony-Ökosystem migriert auf native Attribute. Bei Neuentwicklungen: immer Attribute, nie Annotations.
    </div>
  </div>

  <div class="recall">
    <b>Recall</b>
    <ol>
      <li>Was ist der Vorteil von Attributen gegenüber Doc-Block-Annotations?</li>
      <li>Wie liest du Attribute zur Laufzeit aus?</li>
      <li>Welche Frameworks nutzen Attribute intensiv?</li>
    </ol>
  </div>
</section>

<!-- ===== KAPITEL 5 ===== -->
<section class="chapter">
  <div class="chapter-head">
    <div class="chapter-num">05</div>
    <div class="chapter-title">
      <h1>Closures und Arrow Functions</h1>
      <div class="subtitle">First-Class Callables, use-Capture</div>
    </div>
  </div>

  <div class="gap">
    <b>Frage zum Einstieg</b>
    Funktionen als Werte  das ist seit Jahren in JavaScript Standard. Auch PHP kann das, mit Closures, Arrow Functions und seit PHP 8.1 First-Class Callable Syntax. Wann nutzt du was?
  </div>

  <h2>Closures: anonyme Funktionen</h2>
  <p>Eine <b>Closure</b> ist eine Funktion ohne Namen, die in einer Variable lebt. Du übergibst sie als Argument oder gibst sie zurück:</p>

<pre><span class="v">$double</span> = <span class="k">function</span>(<span class="t">int</span> <span class="v">$x</span>): <span class="t">int</span> {
  <span class="k">return</span> <span class="v">$x</span> * <span class="s">2</span>;
};

<span class="k">echo</span> <span class="v">$double</span>(<span class="s">5</span>);                <span class="c">// 10</span>

<span class="c">// Als Argument an array_map</span>
<span class="v">$numbers</span> = [<span class="s">1</span>, <span class="s">2</span>, <span class="s">3</span>];
<span class="v">$doubled</span> = <span class="f">array_map</span>(<span class="v">$double</span>, <span class="v">$numbers</span>); <span class="c">// [2, 4, 6]</span></pre>

  <h2>Variablen aus dem Kontext: use</h2>
  <p>Im Gegensatz zu normalen Funktionen haben Closures Zugriff auf den umgebenden Scope  aber nur, wenn du es explizit mit <code class="inline">use</code> erlaubst:</p>

<pre><span class="v">$prefix</span> = <span class="s">'Mr. '</span>;

<span class="v">$greet</span> = <span class="k">function</span>(<span class="t">string</span> <span class="v">$name</span>) <span class="k">use</span> (<span class="v">$prefix</span>): <span class="t">string</span> {
  <span class="k">return</span> <span class="v">$prefix</span> . <span class="v">$name</span>;
};

<span class="k">echo</span> <span class="v">$greet</span>(<span class="s">'Marek'</span>);            <span class="c">// 'Mr. Marek'</span>

<span class="c">// use kopiert standardmäßig: spätere Änderung wirkt nicht</span>
<span class="v">$prefix</span> = <span class="s">'Dr. '</span>;
<span class="k">echo</span> <span class="v">$greet</span>(<span class="s">'Marek'</span>);            <span class="c">// immer noch 'Mr. Marek'</span>

<span class="c">// use mit Referenz: spätere Änderung wirkt</span>
<span class="v">$counter</span> = <span class="s">0</span>;
<span class="v">$increment</span> = <span class="k">function</span>() <span class="k">use</span> (&amp;<span class="v">$counter</span>): <span class="t">void</span> {
  <span class="v">$counter</span>++;
};
<span class="v">$increment</span>();
<span class="v">$increment</span>();
<span class="k">echo</span> <span class="v">$counter</span>;                  <span class="c">// 2</span></pre>

  <h2>Arrow Functions: kompakt</h2>
  <p>Für einzeilige Closures gibt es seit PHP 7.4 die kürzere <b>Arrow Function Syntax</b>. Sie capturet Variablen automatisch (kein <code class="inline">use</code> nötig):</p>

<pre><span class="v">$multiplier</span> = <span class="s">3</span>;
<span class="v">$multiply</span> = <span class="k">fn</span>(<span class="t">int</span> <span class="v">$x</span>): <span class="t">int</span> =&gt; <span class="v">$x</span> * <span class="v">$multiplier</span>;

<span class="k">echo</span> <span class="v">$multiply</span>(<span class="s">5</span>);              <span class="c">// 15</span>

<span class="c">// Praktisch in array_map / array_filter</span>
<span class="v">$names</span> = [<span class="s">'marek'</span>, <span class="s">'anna'</span>, <span class="s">'tom'</span>];
<span class="v">$upper</span> = <span class="f">array_map</span>(<span class="k">fn</span>(<span class="v">$n</span>) =&gt; <span class="f">strtoupper</span>(<span class="v">$n</span>), <span class="v">$names</span>);</pre>

  <h2>First-Class Callable Syntax</h2>
  <p>Seit PHP 8.1 kannst du jede Funktion oder Methode als Closure referenzieren  ohne sie aufzurufen:</p>

<pre><span class="c">// Globale Funktion</span>
<span class="v">$upper</span> = <span class="f">strtoupper</span>(...);
<span class="v">$upper</span>(<span class="s">'hallo'</span>);                 <span class="c">// 'HALLO'</span>

<span class="c">// Methode</span>
<span class="v">$user</span> = <span class="k">new</span> <span class="t">User</span>(<span class="s">'Marek'</span>);
<span class="v">$greet</span> = <span class="v">$user</span>-&gt;greet(...);
<span class="v">$greet</span>();                          <span class="c">// 'Hallo, Marek'</span>

<span class="c">// Static Methode</span>
<span class="v">$square</span> = <span class="t">Math</span>::<span class="f">square</span>(...);
<span class="v">$square</span>(<span class="s">5</span>);                       <span class="c">// 25</span>

<span class="c">// Direkt an array_map übergeben</span>
<span class="v">$upper</span> = <span class="f">array_map</span>(<span class="f">strtoupper</span>(...), <span class="v">$names</span>);</pre>

  <div class="callout tip">
    <div class="callout-icon">✓</div>
    <div class="callout-body">
      <b>Arrow für einfach, Closure für komplex</b>
      Faustregel: Arrow Function (<code class="inline">fn</code>) wenn der Body ein einzelner Ausdruck ist. Volle Closure (<code class="inline">function</code>) wenn du mehrere Statements brauchst oder explizites Variablen-Capturing willst.
    </div>
  </div>

  <div class="recall">
    <b>Recall</b>
    <ol>
      <li>Was ist der Unterschied zwischen <code style="color:#777BB4">use ($x)</code> und <code style="color:#777BB4">use (&amp;$x)</code>?</li>
      <li>Was capturen Arrow Functions automatisch?</li>
      <li>Wofür nutzt du First-Class Callable Syntax <code style="color:#777BB4">strtoupper(...)</code>?</li>
    </ol>
  </div>
</section>

<!-- ===== KAPITEL 6 ===== -->
<section class="chapter">
  <div class="chapter-head">
    <div class="chapter-num">06</div>
    <div class="chapter-title">
      <h1>Higher-Order Functions</h1>
      <div class="subtitle">array_map, array_filter, array_reduce in der Praxis</div>
    </div>
  </div>

  <div class="gap">
    <b>Frage zum Einstieg</b>
    Du willst aus einer Liste von Bestellungen die Beträge aller bezahlten zusammenrechnen. Klassisch mit <code>foreach</code>, Zwischenvariable, Bedingung  fünf Zeilen. Mit Higher-Order Functions wird daraus ein lesbarer Einzeiler. Wie?
  </div>

  <h2>array_map: transformieren</h2>
  <p><code class="inline">array_map</code> wendet eine Funktion auf jedes Element an und gibt ein neues Array zurück:</p>

<pre><span class="v">$prices</span> = [<span class="s">10</span>, <span class="s">25</span>, <span class="s">99</span>];

<span class="v">$withTax</span> = <span class="f">array_map</span>(<span class="k">fn</span>(<span class="v">$p</span>) =&gt; <span class="v">$p</span> * <span class="s">1.19</span>, <span class="v">$prices</span>);
<span class="c">// [11.9, 29.75, 117.81]</span>

<span class="c">// Objekte transformieren</span>
<span class="v">$users</span> = [<span class="k">new</span> <span class="t">User</span>(<span class="s">'Marek'</span>), <span class="k">new</span> <span class="t">User</span>(<span class="s">'Anna'</span>)];
<span class="v">$names</span> = <span class="f">array_map</span>(<span class="k">fn</span>(<span class="t">User</span> <span class="v">$u</span>) =&gt; <span class="v">$u</span>-&gt;name, <span class="v">$users</span>);
<span class="c">// ['Marek', 'Anna']</span></pre>

  <h2>array_filter: filtern</h2>
  <p><code class="inline">array_filter</code> behält nur Elemente, für die die Funktion <code class="inline">true</code> zurückgibt:</p>

<pre><span class="v">$numbers</span> = [<span class="s">1</span>, <span class="s">2</span>, <span class="s">3</span>, <span class="s">4</span>, <span class="s">5</span>, <span class="s">6</span>];

<span class="v">$even</span> = <span class="f">array_filter</span>(<span class="v">$numbers</span>, <span class="k">fn</span>(<span class="v">$n</span>) =&gt; <span class="v">$n</span> % <span class="s">2</span> === <span class="s">0</span>);
<span class="c">// [2, 4, 6]</span>

<span class="c">// Achtung: Keys werden beibehalten!</span>
<span class="c">// $even ist [1 =&gt; 2, 3 =&gt; 4, 5 =&gt; 6]</span>

<span class="c">// Re-indexen mit array_values</span>
<span class="v">$even</span> = <span class="f">array_values</span>(<span class="f">array_filter</span>(<span class="v">$numbers</span>, <span class="k">fn</span>(<span class="v">$n</span>) =&gt; <span class="v">$n</span> % <span class="s">2</span> === <span class="s">0</span>));
<span class="c">// [2, 4, 6] mit Keys 0, 1, 2</span></pre>

  <h2>array_reduce: zusammenfassen</h2>
  <p><code class="inline">array_reduce</code> reduziert ein Array zu einem einzelnen Wert. Der Akkumulator startet mit einem Initialwert und wird mit jedem Element aktualisiert:</p>

<pre><span class="v">$prices</span> = [<span class="s">10</span>, <span class="s">25</span>, <span class="s">99</span>];

<span class="v">$total</span> = <span class="f">array_reduce</span>(
  <span class="v">$prices</span>,
  <span class="k">fn</span>(<span class="t">float</span> <span class="v">$sum</span>, <span class="t">int</span> <span class="v">$price</span>) =&gt; <span class="v">$sum</span> + <span class="v">$price</span>,
  <span class="s">0.0</span>   <span class="c">// Initialwert</span>
);
<span class="c">// 134.0</span></pre>

  <h2>Verkettung in der Praxis</h2>
  <p>Das volle Potenzial entfaltet sich, wenn du mehrere Funktionen kombinierst  z.B. Beträge der bezahlten Bestellungen aufsummieren:</p>

<pre><span class="v">$paidTotal</span> = <span class="f">array_reduce</span>(
  <span class="f">array_filter</span>(<span class="v">$orders</span>, <span class="k">fn</span>(<span class="v">$o</span>) =&gt; <span class="v">$o</span>-&gt;status === <span class="t">Status</span>::<span class="t">Paid</span>),
  <span class="k">fn</span>(<span class="v">$sum</span>, <span class="v">$o</span>) =&gt; <span class="v">$sum</span> + <span class="v">$o</span>-&gt;amount,
  <span class="s">0.0</span>
);

<span class="c">// Mit Zwischen-Variablen leichter zu lesen</span>
<span class="v">$paidOrders</span> = <span class="f">array_filter</span>(<span class="v">$orders</span>, <span class="k">fn</span>(<span class="v">$o</span>) =&gt; <span class="v">$o</span>-&gt;<span class="f">isPaid</span>());
<span class="v">$amounts</span> = <span class="f">array_map</span>(<span class="k">fn</span>(<span class="v">$o</span>) =&gt; <span class="v">$o</span>-&gt;amount, <span class="v">$paidOrders</span>);
<span class="v">$total</span> = <span class="f">array_sum</span>(<span class="v">$amounts</span>);</pre>

  <p>Letzteres ist oft lesbarer als verschachtelte Aufrufe  nicht immer ist die kompakteste Version die beste.</p>

  <div class="callout note">
    <div class="callout-icon">i</div>
    <div class="callout-body">
      <b>illuminate/collections für komplexere Fälle</b>
      Wenn du oft mit verketteten Operationen arbeitest, lohnt sich Laravels Collection-Library (auch standalone nutzbar): <code class="inline">collect($users)-&gt;filter(...)-&gt;map(...)-&gt;sum()</code>. Lesbarer als verschachtelte PHP-Standardfunktionen.
    </div>
  </div>

  <div class="recall">
    <b>Recall</b>
    <ol>
      <li>Was ist die Reihenfolge der Argumente bei <code style="color:#777BB4">array_map</code> vs. <code style="color:#777BB4">array_filter</code>?</li>
      <li>Welches Detail bei <code style="color:#777BB4">array_filter</code> überrascht oft?</li>
      <li>Wann ist eine verschachtelte Verkettung weniger lesbar als Zwischenvariablen?</li>
    </ol>
  </div>
</section>

<!-- ===== KAPITEL 7 ===== -->
<section class="chapter">
  <div class="chapter-head">
    <div class="chapter-num">07</div>
    <div class="chapter-title">
      <h1>Iterators und Generators</h1>
      <div class="subtitle">Lazy Evaluation mit yield</div>
    </div>
  </div>

  <div class="gap">
    <b>Frage zum Einstieg</b>
    Du sollst die Zeilen einer 5-GB-Logdatei verarbeiten. Komplett ins Array laden? Speicher reicht nicht. Klassische File-Funktionen mit Schleife? Funktioniert, aber Code wird unhandlich, wenn er weiterverarbeitet werden soll. <b>Generators</b> lösen das elegant.
  </div>

  <h2>yield: Werte häppchenweise produzieren</h2>
  <p>Ein <b>Generator</b> ist eine Funktion, die Werte mit <code class="inline">yield</code> ausgibt statt mit <code class="inline">return</code>. Sie pausiert zwischen den Werten  Speicher bleibt frei:</p>

<pre><span class="k">function</span> <span class="f">readLines</span>(<span class="t">string</span> <span class="v">$path</span>): \<span class="t">Generator</span> {
  <span class="v">$handle</span> = <span class="f">fopen</span>(<span class="v">$path</span>, <span class="s">'r'</span>);
  <span class="k">while</span> ((<span class="v">$line</span> = <span class="f">fgets</span>(<span class="v">$handle</span>)) !== <span class="k">false</span>) {
    <span class="k">yield</span> <span class="f">rtrim</span>(<span class="v">$line</span>);
  }
  <span class="f">fclose</span>(<span class="v">$handle</span>);
}

<span class="c">// Aufruf liefert sofort einen Generator  Datei wird noch nicht gelesen</span>
<span class="v">$lines</span> = <span class="f">readLines</span>(<span class="s">'/var/log/huge.log'</span>);

<span class="c">// Erst die Iteration liest tatsächlich</span>
<span class="k">foreach</span> (<span class="v">$lines</span> <span class="k">as</span> <span class="v">$line</span>) {
  <span class="k">if</span> (<span class="f">str_contains</span>(<span class="v">$line</span>, <span class="s">'ERROR'</span>)) {
    <span class="k">echo</span> <span class="v">$line</span>;
  }
}</pre>

  <p>Der Speicher-Vorteil: zu jedem Zeitpunkt ist nur <i>eine</i> Zeile geladen, egal wie groß die Datei ist.</p>

  <h2>yield with key</h2>

<pre><span class="k">function</span> <span class="f">readCsv</span>(<span class="t">string</span> <span class="v">$path</span>): \<span class="t">Generator</span> {
  <span class="v">$handle</span> = <span class="f">fopen</span>(<span class="v">$path</span>, <span class="s">'r'</span>);
  <span class="v">$headers</span> = <span class="f">fgetcsv</span>(<span class="v">$handle</span>);
  <span class="v">$rowNumber</span> = <span class="s">0</span>;

  <span class="k">while</span> ((<span class="v">$row</span> = <span class="f">fgetcsv</span>(<span class="v">$handle</span>)) !== <span class="k">false</span>) {
    <span class="k">yield</span> <span class="v">$rowNumber</span>++ =&gt; <span class="f">array_combine</span>(<span class="v">$headers</span>, <span class="v">$row</span>);
  }
  <span class="f">fclose</span>(<span class="v">$handle</span>);
}

<span class="k">foreach</span> (<span class="f">readCsv</span>(<span class="s">'users.csv'</span>) <span class="k">as</span> <span class="v">$num</span> =&gt; <span class="v">$row</span>) {
  <span class="k">echo</span> <span class="s">"Zeile </span><span class="v">$num</span><span class="s">: "</span> . <span class="v">$row</span>[<span class="s">'name'</span>] . <span class="s">"\n"</span>;
}</pre>

  <h2>Unendliche Sequenzen</h2>
  <p>Da Generators lazy sind, kannst du theoretisch unendliche Sequenzen erzeugen  solange du sie nicht komplett auswertest:</p>

<pre><span class="k">function</span> <span class="f">fibonacci</span>(): \<span class="t">Generator</span> {
  <span class="v">$a</span> = <span class="s">0</span>; <span class="v">$b</span> = <span class="s">1</span>;
  <span class="k">while</span> (<span class="k">true</span>) {
    <span class="k">yield</span> <span class="v">$a</span>;
    [<span class="v">$a</span>, <span class="v">$b</span>] = [<span class="v">$b</span>, <span class="v">$a</span> + <span class="v">$b</span>];
  }
}

<span class="c">// Erste 10 Fibonacci-Zahlen</span>
<span class="v">$count</span> = <span class="s">0</span>;
<span class="k">foreach</span> (<span class="f">fibonacci</span>() <span class="k">as</span> <span class="v">$n</span>) {
  <span class="k">echo</span> <span class="v">$n</span> . <span class="s">' '</span>;
  <span class="k">if</span> (++<span class="v">$count</span> &gt;= <span class="s">10</span>) <span class="k">break</span>;
}
<span class="c">// 0 1 1 2 3 5 8 13 21 34</span></pre>

  <h2>yield from: Generators verschachteln</h2>
  <p>Mit <code class="inline">yield from</code> delegierst du an einen anderen Generator  nützlich, um Logik zu kapseln:</p>

<pre><span class="k">function</span> <span class="f">range1to3</span>(): \<span class="t">Generator</span> {
  <span class="k">yield</span> <span class="s">1</span>;
  <span class="k">yield</span> <span class="s">2</span>;
  <span class="k">yield</span> <span class="s">3</span>;
}

<span class="k">function</span> <span class="f">range1to6</span>(): \<span class="t">Generator</span> {
  <span class="k">yield from</span> <span class="f">range1to3</span>();   <span class="c">// gibt 1, 2, 3</span>
  <span class="k">yield</span> <span class="s">4</span>;
  <span class="k">yield</span> <span class="s">5</span>;
  <span class="k">yield</span> <span class="s">6</span>;
}</pre>

  <div class="callout warn">
    <div class="callout-icon">!</div>
    <div class="callout-body">
      <b>Generator nur einmal durchlaufen</b>
      Generatoren sind <b>nicht rewindable</b>. Wenn du sie zweimal mit <code class="inline">foreach</code> durchläufst, ist der zweite Durchlauf leer. Brauchst du Mehrfach-Zugriff, konvertiere mit <code class="inline">iterator_to_array(<span class="v">$gen</span>)</code> zu einem Array.
    </div>
  </div>

  <div class="recall">
    <b>Recall</b>
    <ol>
      <li>Welcher Vorteil von Generators bei großen Datenmengen?</li>
      <li>Was passiert, wenn du einen Generator zweimal mit <code style="color:#777BB4">foreach</code> durchläufst?</li>
      <li>Wofür ist <code style="color:#777BB4">yield from</code> da?</li>
    </ol>
  </div>
</section>

<!-- ===== KAPITEL 8 ===== -->
<section class="chapter">
  <div class="chapter-head">
    <div class="chapter-num">08</div>
    <div class="chapter-title">
      <h1>Generics via PHPDoc</h1>
      <div class="subtitle">Templates ohne native Generics</div>
    </div>
  </div>

  <div class="gap">
    <b>Frage zum Einstieg</b>
    PHP hat keine nativen Generics wie TypeScript oder Java. Wenn du eine Collection-Klasse schreibst, weiß die IDE nicht, ob da User oder Order drin sind  jeder Zugriff wird zu <code>mixed</code>. Die Lösung: PHPDoc-Generics mit PHPStan oder Psalm.
  </div>

  <h2>Das Problem ohne Generics</h2>

<pre><span class="k">class</span> <span class="t">Collection</span> {
  <span class="k">private array</span> <span class="v">$items</span> = [];

  <span class="k">public function</span> <span class="f">add</span>(<span class="t">mixed</span> <span class="v">$item</span>): <span class="t">void</span> {
    <span class="v">$this</span>-&gt;items[] = <span class="v">$item</span>;
  }

  <span class="k">public function</span> <span class="f">first</span>(): <span class="t">mixed</span> {
    <span class="k">return</span> <span class="v">$this</span>-&gt;items[<span class="s">0</span>] ?? <span class="k">null</span>;
  }
}

<span class="v">$users</span> = <span class="k">new</span> <span class="t">Collection</span>();
<span class="v">$users</span>-&gt;<span class="f">add</span>(<span class="k">new</span> <span class="t">User</span>(<span class="s">'Marek'</span>));

<span class="v">$user</span> = <span class="v">$users</span>-&gt;<span class="f">first</span>();         <span class="c">// IDE: mixed, keine Autocompletion</span>
<span class="v">$user</span>-&gt;name;                       <span class="c">// keine Hilfe von IDE oder PHPStan</span></pre>

  <h2>Generics in PHPDoc</h2>
  <p>Mit <b>PHPDoc-Annotationen</b> kannst du Templates definieren. PHPStan und Psalm verstehen sie und prüfen Typen statisch:</p>

<pre><span class="c">/**
 * @template T
 */</span>
<span class="k">class</span> <span class="t">Collection</span> {
  <span class="c">/** @var array&lt;T&gt; */</span>
  <span class="k">private array</span> <span class="v">$items</span> = [];

  <span class="c">/**
   * @param T $item
   */</span>
  <span class="k">public function</span> <span class="f">add</span>(<span class="t">mixed</span> <span class="v">$item</span>): <span class="t">void</span> {
    <span class="v">$this</span>-&gt;items[] = <span class="v">$item</span>;
  }

  <span class="c">/**
   * @return T|null
   */</span>
  <span class="k">public function</span> <span class="f">first</span>(): <span class="t">mixed</span> {
    <span class="k">return</span> <span class="v">$this</span>-&gt;items[<span class="s">0</span>] ?? <span class="k">null</span>;
  }
}</pre>

  <h2>Konkreten Typ angeben</h2>
  <p>Bei der Verwendung gibst du den konkreten Typ als PHPDoc-Annotation an. PHPStan ersetzt dann <code class="inline">T</code> intern:</p>

<pre><span class="c">/** @var Collection&lt;User&gt; $users */</span>
<span class="v">$users</span> = <span class="k">new</span> <span class="t">Collection</span>();
<span class="v">$users</span>-&gt;<span class="f">add</span>(<span class="k">new</span> <span class="t">User</span>(<span class="s">'Marek'</span>));

<span class="v">$user</span> = <span class="v">$users</span>-&gt;<span class="f">first</span>();         <span class="c">// PHPStan weiß: User|null</span>
<span class="v">$user</span>-&gt;name;                       <span class="c">// OK, IDE und PHPStan kennen User</span>

<span class="v">$users</span>-&gt;<span class="f">add</span>(<span class="s">'string'</span>);            <span class="c">// PHPStan-Fehler: erwartet User, nicht string</span></pre>

  <h2>Constraints und Bounds</h2>

<pre><span class="c">/**
 * @template T of \Throwable
 */</span>
<span class="k">class</span> <span class="t">ErrorCollection</span> {
  <span class="c">/** @var array&lt;T&gt; */</span>
  <span class="k">private array</span> <span class="v">$errors</span> = [];

  <span class="c">/**
   * @param T $error
   */</span>
  <span class="k">public function</span> <span class="f">add</span>(\<span class="t">Throwable</span> <span class="v">$error</span>): <span class="t">void</span> {
    <span class="v">$this</span>-&gt;errors[] = <span class="v">$error</span>;
  }
}

<span class="c">// Erlaubt nur Throwables und Subklassen</span>
<span class="c">/** @var ErrorCollection&lt;\RuntimeException&gt; $errors */</span>
<span class="v">$errors</span> = <span class="k">new</span> <span class="t">ErrorCollection</span>();</pre>

  <h2>array-shape für strukturierte Arrays</h2>
  <p>PHP-Arrays sind oft "halb-Objekte" mit festen Keys. PHPStan kennt <b>Array-Shapes</b>:</p>

<pre><span class="c">/**
 * @return array{name: string, age: int, email: string|null}
 */</span>
<span class="k">function</span> <span class="f">getUser</span>(<span class="t">int</span> <span class="v">$id</span>): <span class="t">array</span> {
  <span class="k">return</span> [<span class="s">'name'</span> =&gt; <span class="s">'Marek'</span>, <span class="s">'age'</span> =&gt; <span class="s">34</span>, <span class="s">'email'</span> =&gt; <span class="k">null</span>];
}

<span class="v">$user</span> = <span class="f">getUser</span>(<span class="s">42</span>);
<span class="k">echo</span> <span class="v">$user</span>[<span class="s">'name'</span>];                <span class="c">// PHPStan weiß: string</span>
<span class="k">echo</span> <span class="v">$user</span>[<span class="s">'phone'</span>];               <span class="c">// PHPStan-Fehler: Key existiert nicht</span></pre>

  <div class="callout note">
    <div class="callout-icon">i</div>
    <div class="callout-body">
      <b>Native Generics kommen vielleicht</b>
      Es gibt seit Jahren Diskussionen um native Generics in PHP. Ein RFC ist mehrfach gescheitert wegen Implementierungs-Komplexität (Runtime-Type-Erasure vs. Reified). Bis dahin sind PHPDoc-Generics mit PHPStan/Psalm der Standard.
    </div>
  </div>

  <div class="recall">
    <b>Recall</b>
    <ol>
      <li>Welche Tools verstehen PHPDoc-Generics?</li>
      <li>Wie deklarierst du eine generische Klasse?</li>
      <li>Wofür ist <code style="color:#777BB4">array{name: string, age: int}</code>?</li>
    </ol>
  </div>
</section>

<!-- ===== KAPITEL 9 ===== -->
<section class="chapter">
  <div class="chapter-head">
    <div class="chapter-num">09</div>
    <div class="chapter-title">
      <h1>PDO und sichere Datenbanken</h1>
      <div class="subtitle">Prepared Statements, Transaktionen</div>
    </div>
  </div>

  <div class="gap">
    <b>Frage zum Einstieg</b>
    SQL-Injection ist seit 20 Jahren die häufigste Web-Sicherheitslücke. Sie ist trivial zu verhindern  mit <b>Prepared Statements</b>. PDO ist PHPs eingebaute, datenbank-agnostische Schnittstelle dafür. Wie nutzt du sie richtig?
  </div>

  <h2>PDO-Verbindung aufbauen</h2>
  <p>PDO unterstützt MySQL, PostgreSQL, SQLite und mehr über dieselbe API. Der DSN-String unterscheidet sich, der Rest ist gleich:</p>

<pre><span class="v">$dsn</span> = <span class="s">'mysql:host=localhost;dbname=myapp;charset=utf8mb4'</span>;
<span class="v">$pdo</span> = <span class="k">new</span> \<span class="t">PDO</span>(<span class="v">$dsn</span>, <span class="s">'username'</span>, <span class="s">'password'</span>, [
  PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
  PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
  PDO::ATTR_EMULATE_PREPARES => <span class="k">false</span>,
]);</pre>

  <p>Die drei Options sind <b>Pflicht in jedem Projekt</b>: Exceptions bei Fehlern, assoziative Arrays beim Fetch, echte Prepared Statements (statt emulierter Client-Side).</p>

  <h2>Prepared Statements (immer!)</h2>
  <p><b>Niemals</b> User-Input direkt in SQL-Strings konkatenieren. Stattdessen: Placeholder mit <code class="inline">?</code> oder Named Parameters:</p>

<pre><span class="c">// Falsch: SQL-Injection-Lücke</span>
<span class="v">$id</span> = <span class="v">$_GET</span>[<span class="s">'id'</span>];
<span class="v">$result</span> = <span class="v">$pdo</span>-&gt;<span class="f">query</span>(<span class="s">"SELECT * FROM users WHERE id = </span><span class="v">$id</span><span class="s">"</span>);
<span class="c">// $_GET['id'] = "1; DROP TABLE users" → katastrophal</span>

<span class="c">// Richtig: Prepared Statement mit Positional Parameters</span>
<span class="v">$stmt</span> = <span class="v">$pdo</span>-&gt;<span class="f">prepare</span>(<span class="s">'SELECT * FROM users WHERE id = ?'</span>);
<span class="v">$stmt</span>-&gt;<span class="f">execute</span>([<span class="v">$id</span>]);
<span class="v">$user</span> = <span class="v">$stmt</span>-&gt;<span class="f">fetch</span>();

<span class="c">// Oder mit Named Parameters</span>
<span class="v">$stmt</span> = <span class="v">$pdo</span>-&gt;<span class="f">prepare</span>(<span class="s">'SELECT * FROM users WHERE id = :id AND active = :active'</span>);
<span class="v">$stmt</span>-&gt;<span class="f">execute</span>([<span class="s">':id'</span> =&gt; <span class="v">$id</span>, <span class="s">':active'</span> =&gt; <span class="k">true</span>]);</pre>

  <h2>Ergebnisse abrufen</h2>

<pre><span class="c">// Einzelne Zeile</span>
<span class="v">$user</span> = <span class="v">$stmt</span>-&gt;<span class="f">fetch</span>();              <span class="c">// assoc array oder false</span>

<span class="c">// Alle Zeilen</span>
<span class="v">$users</span> = <span class="v">$stmt</span>-&gt;<span class="f">fetchAll</span>();           <span class="c">// Array von Zeilen</span>

<span class="c">// Iterativ für große Result-Sets</span>
<span class="k">while</span> (<span class="v">$row</span> = <span class="v">$stmt</span>-&gt;<span class="f">fetch</span>()) {
  <span class="f">processRow</span>(<span class="v">$row</span>);
}

<span class="c">// Direkt in Objekte mappen (PDO::FETCH_CLASS)</span>
<span class="v">$stmt</span>-&gt;<span class="f">setFetchMode</span>(PDO::FETCH_CLASS, <span class="t">User</span>::<span class="k">class</span>);
<span class="v">$users</span> = <span class="v">$stmt</span>-&gt;<span class="f">fetchAll</span>();</pre>

  <h2>Transaktionen</h2>
  <p>Wenn mehrere Operationen <b>atomar</b> sein müssen (alle oder keine), nutzt du Transaktionen:</p>

<pre><span class="v">$pdo</span>-&gt;<span class="f">beginTransaction</span>();

<span class="k">try</span> {
  <span class="v">$pdo</span>-&gt;<span class="f">prepare</span>(<span class="s">'UPDATE accounts SET balance = balance - ? WHERE id = ?'</span>)
      -&gt;<span class="f">execute</span>([<span class="s">100</span>, <span class="v">$fromId</span>]);

  <span class="v">$pdo</span>-&gt;<span class="f">prepare</span>(<span class="s">'UPDATE accounts SET balance = balance + ? WHERE id = ?'</span>)
      -&gt;<span class="f">execute</span>([<span class="s">100</span>, <span class="v">$toId</span>]);

  <span class="v">$pdo</span>-&gt;<span class="f">commit</span>();
} <span class="k">catch</span> (\<span class="t">PDOException</span> <span class="v">$e</span>) {
  <span class="v">$pdo</span>-&gt;<span class="f">rollBack</span>();
  <span class="k">throw</span> <span class="v">$e</span>;
}</pre>

  <div class="callout warn">
    <div class="callout-icon">!</div>
    <div class="callout-body">
      <b>Niemals mysql_* oder mysqli ohne Prepared Statements</b>
      Die alten <code class="inline">mysql_*</code>-Funktionen sind seit PHP 7 entfernt. <code class="inline">mysqli</code> existiert noch, aber nur mit Prepared Statements nutzen. Standard heute: <b>PDO</b> für direkten DB-Zugriff, <b>Doctrine DBAL</b> für Query-Builder, <b>Doctrine ORM</b> für komplexe Domänen.
    </div>
  </div>

  <div class="recall">
    <b>Recall</b>
    <ol>
      <li>Welche drei Options gehören in jeden PDO-Konstruktor?</li>
      <li>Warum sind Prepared Statements sicherer als String-Konkatenation?</li>
      <li>Wann nutzt du eine Transaktion?</li>
    </ol>
  </div>
</section>

<!-- ===== KAPITEL 10 ===== -->
<section class="chapter">
  <div class="chapter-head">
    <div class="chapter-num">10</div>
    <div class="chapter-title">
      <h1>HTTP-Requests mit Guzzle</h1>
      <div class="subtitle">APIs konsumieren, async Requests</div>
    </div>
  </div>

  <div class="gap">
    <b>Frage zum Einstieg</b>
    Du musst eine externe API ansprechen  REST mit JSON, Authentifizierung, vielleicht Retry bei 503. Mit nativen Funktionen wie <code>file_get_contents</code> oder cURL geht das, aber wird schnell hässlich. <b>Guzzle</b> ist Standard-Library für HTTP in PHP.
  </div>

  <h2>Setup</h2>

<pre>composer require guzzlehttp/guzzle</pre>

<pre><span class="k">use</span> GuzzleHttp\Client;

<span class="v">$client</span> = <span class="k">new</span> Client([
  <span class="s">'base_uri'</span> =&gt; <span class="s">'https://api.example.com'</span>,
  <span class="s">'timeout'</span> =&gt; <span class="s">5</span>,
  <span class="s">'headers'</span> =&gt; [
    <span class="s">'Accept'</span> =&gt; <span class="s">'application/json'</span>,
    <span class="s">'Authorization'</span> =&gt; <span class="s">'Bearer '</span> . <span class="v">$token</span>,
  ],
]);</pre>

  <h2>GET und POST</h2>

<pre><span class="c">// GET</span>
<span class="v">$response</span> = <span class="v">$client</span>-&gt;<span class="f">get</span>(<span class="s">'/users/42'</span>);
<span class="v">$data</span> = <span class="f">json_decode</span>(<span class="v">$response</span>-&gt;<span class="f">getBody</span>()-&gt;<span class="f">getContents</span>(), <span class="k">true</span>);

<span class="c">// GET mit Query-Parametern</span>
<span class="v">$response</span> = <span class="v">$client</span>-&gt;<span class="f">get</span>(<span class="s">'/users'</span>, [
  <span class="s">'query'</span> =&gt; [<span class="s">'page'</span> =&gt; <span class="s">2</span>, <span class="s">'limit'</span> =&gt; <span class="s">50</span>],
]);

<span class="c">// POST mit JSON-Body</span>
<span class="v">$response</span> = <span class="v">$client</span>-&gt;<span class="f">post</span>(<span class="s">'/users'</span>, [
  <span class="s">'json'</span> =&gt; [<span class="s">'name'</span> =&gt; <span class="s">'Marek'</span>, <span class="s">'email'</span> =&gt; <span class="s">'m@e.de'</span>],
]);

<span class="c">// Status und Header</span>
<span class="v">$response</span>-&gt;<span class="f">getStatusCode</span>();          <span class="c">// 201</span>
<span class="v">$response</span>-&gt;<span class="f">getHeader</span>(<span class="s">'Content-Type'</span>);</pre>

  <h2>Fehlerbehandlung</h2>
  <p>Guzzle wirft bei HTTP-Status 4xx/5xx Exceptions. Die hierarchische Struktur erlaubt gezieltes Catchen:</p>

<pre><span class="k">use</span> GuzzleHttp\Exception\ClientException;
<span class="k">use</span> GuzzleHttp\Exception\ServerException;
<span class="k">use</span> GuzzleHttp\Exception\ConnectException;

<span class="k">try</span> {
  <span class="v">$response</span> = <span class="v">$client</span>-&gt;<span class="f">get</span>(<span class="s">'/users/42'</span>);
} <span class="k">catch</span> (<span class="t">ClientException</span> <span class="v">$e</span>) {
  <span class="c">// 4xx Fehler  z.B. 404 Not Found, 401 Unauthorized</span>
  <span class="v">$status</span> = <span class="v">$e</span>-&gt;<span class="f">getResponse</span>()-&gt;<span class="f">getStatusCode</span>();
} <span class="k">catch</span> (<span class="t">ServerException</span> <span class="v">$e</span>) {
  <span class="c">// 5xx Fehler  z.B. 500, 503</span>
} <span class="k">catch</span> (<span class="t">ConnectException</span> <span class="v">$e</span>) {
  <span class="c">// Netzwerk-Fehler, Timeout, DNS</span>
}</pre>

  <h2>Async Requests</h2>
  <p>Mehrere Requests parallel statt seriell? Guzzle unterstützt Promises:</p>

<pre><span class="k">use</span> GuzzleHttp\Promise;

<span class="v">$promises</span> = [
  <span class="s">'users'</span> =&gt; <span class="v">$client</span>-&gt;<span class="f">getAsync</span>(<span class="s">'/users'</span>),
  <span class="s">'orders'</span> =&gt; <span class="v">$client</span>-&gt;<span class="f">getAsync</span>(<span class="s">'/orders'</span>),
  <span class="s">'stats'</span> =&gt; <span class="v">$client</span>-&gt;<span class="f">getAsync</span>(<span class="s">'/stats'</span>),
];

<span class="c">// Warte bis alle fertig sind</span>
<span class="v">$results</span> = Promise\Utils::<span class="f">settle</span>(<span class="v">$promises</span>)-&gt;<span class="f">wait</span>();

<span class="k">foreach</span> (<span class="v">$results</span> <span class="k">as</span> <span class="v">$key</span> =&gt; <span class="v">$result</span>) {
  <span class="k">if</span> (<span class="v">$result</span>[<span class="s">'state'</span>] === <span class="s">'fulfilled'</span>) {
    <span class="v">$body</span> = (<span class="t">string</span>) <span class="v">$result</span>[<span class="s">'value'</span>]-&gt;<span class="f">getBody</span>();
    <span class="c">// ...</span>
  }
}</pre>

  <p>Drei API-Calls in der Zeit eines einzelnen  wenn die API es zulässt, ist das ein riesiger Performance-Gewinn.</p>

  <div class="callout tip">
    <div class="callout-icon">✓</div>
    <div class="callout-body">
      <b>PSR-18 als alternative</b>
      Wenn du framework-agnostisch bleiben willst, programmiere gegen <code class="inline">Psr\Http\Client\ClientInterface</code>. Guzzle implementiert das, aber auch andere Clients (z.B. Symfony HttpClient). Du tauschst sie dann ohne Code-Änderung aus.
    </div>
  </div>

  <div class="recall">
    <b>Recall</b>
    <ol>
      <li>Wofür ist <code style="color:#777BB4">base_uri</code> in der Client-Konfiguration?</li>
      <li>Welche Exception fängst du für HTTP 4xx?</li>
      <li>Wann lohnen sich async Requests?</li>
    </ol>
  </div>
</section>

<!-- ===== KAPITEL 11 ===== -->
<section class="chapter">
  <div class="chapter-head">
    <div class="chapter-num">11</div>
    <div class="chapter-title">
      <h1>PHPStan und Static Analysis</h1>
      <div class="subtitle">Bugs vor der Ausführung finden</div>
    </div>
  </div>

  <div class="gap">
    <b>Frage zum Einstieg</b>
    Ein Tippfehler im Property-Namen, ein null-Wert, der zu einer Method-Aufruf-Exception führt, eine Funktion mit falscher Argument-Anzahl  all das passiert in PHP zur Laufzeit, oft erst in Production. <b>PHPStan</b> findet diese Bugs ohne den Code auszuführen.
  </div>

  <h2>Setup</h2>

<pre>composer require --dev phpstan/phpstan</pre>

<pre><span class="c"># phpstan.neon</span>
parameters:
  level: <span class="s">8</span>           <span class="c"># strikteste Stufe</span>
  paths:
    - src
  excludePaths:
    - vendor</pre>

<pre><span class="c"># Analyse ausführen</span>
vendor/bin/phpstan analyse</pre>

  <h2>Die Level-Stufen</h2>
  <p>PHPStan kennt Stufen von 0 (loose) bis 9 (sehr strikt). Beim Einstieg in ein Projekt fängst du niedrig an und arbeitest dich hoch:</p>

  <table>
    <tr><th>Level</th><th>Was geprüft wird</th></tr>
    <tr><td>0</td><td>Existenz von Klassen und Funktionen</td></tr>
    <tr><td>2</td><td>Falsche Typen in Operatoren</td></tr>
    <tr><td>5</td><td>Typen der Funktionsargumente</td></tr>
    <tr><td>7</td><td>Möglicherweise null-Werte</td></tr>
    <tr><td>8</td><td>Calls auf nullable-Typen</td></tr>
    <tr><td>9</td><td>mixed-Werte korrekt einschränken</td></tr>
  </table>

  <h2>Typische Bugs, die PHPStan findet</h2>

<pre><span class="c">// 1. Tippfehler in Property</span>
<span class="k">class</span> <span class="t">User</span> {
  <span class="k">public function</span> <span class="f">__construct</span>(<span class="k">public readonly</span> <span class="t">string</span> <span class="v">$name</span>) {}
}

<span class="v">$user</span> = <span class="k">new</span> <span class="t">User</span>(<span class="s">'Marek'</span>);
<span class="k">echo</span> <span class="v">$user</span>-&gt;naem;                <span class="c">// PHPStan: Access to undefined property</span>

<span class="c">// 2. null-Aufruf</span>
<span class="k">function</span> <span class="f">findUser</span>(<span class="t">int</span> <span class="v">$id</span>): ?<span class="t">User</span> { <span class="c">/* ... */</span> }

<span class="v">$user</span> = <span class="f">findUser</span>(<span class="s">42</span>);
<span class="k">echo</span> <span class="v">$user</span>-&gt;name;               <span class="c">// PHPStan: $user might be null</span>

<span class="c">// 3. Falsche Argument-Typen</span>
<span class="k">function</span> <span class="f">double</span>(<span class="t">int</span> <span class="v">$x</span>): <span class="t">int</span> { <span class="k">return</span> <span class="v">$x</span> * <span class="s">2</span>; }

<span class="f">double</span>(<span class="s">'5'</span>);                    <span class="c">// PHPStan: expects int, gets string</span></pre>

  <h2>PHPDoc für Inferenz nutzen</h2>
  <p>Je präziser deine PHPDoc-Annotationen, desto mehr findet PHPStan. Besonders mächtig bei Arrays:</p>

<pre><span class="c">/**
 * @param array&lt;User&gt; $users
 * @return array&lt;string&gt;
 */</span>
<span class="k">function</span> <span class="f">getNames</span>(<span class="t">array</span> <span class="v">$users</span>): <span class="t">array</span> {
  <span class="k">return</span> <span class="f">array_map</span>(<span class="k">fn</span>(<span class="v">$u</span>) =&gt; <span class="v">$u</span>-&gt;name, <span class="v">$users</span>);
}

<span class="c">// Ohne PHPDoc würde PHPStan nicht wissen, was $users enthält</span>
<span class="c">// Mit PHPDoc kann es fehlerhafte Aufrufe finden:</span>
<span class="f">getNames</span>([<span class="k">new</span> <span class="t">User</span>(<span class="s">'M'</span>), <span class="s">'string'</span>]);  <span class="c">// PHPStan: erwartet User, nicht string</span></pre>

  <h2>Baseline für Legacy-Code</h2>
  <p>Wenn du PHPStan zu einem bestehenden Projekt hinzufügst, sind oft hunderte Fehler in altem Code. Eine <b>Baseline</b> ignoriert bestehende Fehler  neue müssen gefixt werden:</p>

<pre>vendor/bin/phpstan analyse --generate-baseline</pre>

  <p>Das erzeugt <code class="inline">phpstan-baseline.neon</code>. Bestehende Fehler werden eingefroren, neuer Code wird streng geprüft. Über die Zeit räumst du die Baseline ab.</p>

  <div class="callout note">
    <div class="callout-icon">i</div>
    <div class="callout-body">
      <b>Psalm als Alternative</b>
      <b>Psalm</b> ist ein vergleichbares Tool von Vimeo. Die Features überschneiden sich zu 95%  PHPStan ist verbreiteter, Psalm geht bei bestimmten Edge Cases tiefer. In Symfony und Laravel-Welt: meist PHPStan.
    </div>
  </div>

  <div class="recall">
    <b>Recall</b>
    <ol>
      <li>Was tut PHPStan, was PHP-Runtime nicht tut?</li>
      <li>Welche Level-Stufe sollte langfristig das Ziel sein?</li>
      <li>Wofür ist eine PHPStan-Baseline?</li>
    </ol>
  </div>
</section>

<!-- ===== KAPITEL 12 ===== -->
<section class="chapter">
  <div class="chapter-head">
    <div class="chapter-num">12</div>
    <div class="chapter-title">
      <h1>Testen mit PHPUnit</h1>
      <div class="subtitle">Unit-Tests, Mocks, Data Providers</div>
    </div>
  </div>

  <div class="gap">
    <b>Frage zum Einstieg</b>
    Tests fühlen sich als zusätzliche Arbeit an  bis du das erste Mal eine Änderung machst und in Sekunden weißt, dass nichts kaputt ist. <b>PHPUnit</b> ist Standard für PHP-Tests. Wie schreibst du erste Tests und welche Patterns lohnen sich?
  </div>

  <h2>Setup</h2>

<pre>composer require --dev phpunit/phpunit</pre>

<pre><span class="c">// phpunit.xml</span>
<span class="t">&lt;?xml version="1.0"?&gt;</span>
<span class="t">&lt;phpunit</span> xmlns:xsi=<span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span><span class="t">&gt;</span>
  <span class="t">&lt;testsuites&gt;</span>
    <span class="t">&lt;testsuite</span> name=<span class="s">"default"</span><span class="t">&gt;</span>
      <span class="t">&lt;directory&gt;</span>tests<span class="t">&lt;/directory&gt;</span>
    <span class="t">&lt;/testsuite&gt;</span>
  <span class="t">&lt;/testsuites&gt;</span>
<span class="t">&lt;/phpunit&gt;</span></pre>

  <h2>Erste Test-Klasse</h2>

<pre><span class="c">// src/Calculator.php</span>
<span class="k">namespace</span> App;

<span class="k">class</span> <span class="t">Calculator</span> {
  <span class="k">public function</span> <span class="f">add</span>(<span class="t">int</span> <span class="v">$a</span>, <span class="t">int</span> <span class="v">$b</span>): <span class="t">int</span> {
    <span class="k">return</span> <span class="v">$a</span> + <span class="v">$b</span>;
  }

  <span class="k">public function</span> <span class="f">divide</span>(<span class="t">int</span> <span class="v">$a</span>, <span class="t">int</span> <span class="v">$b</span>): <span class="t">float</span> {
    <span class="k">if</span> (<span class="v">$b</span> === <span class="s">0</span>) <span class="k">throw new</span> \<span class="t">DivisionByZeroError</span>();
    <span class="k">return</span> <span class="v">$a</span> / <span class="v">$b</span>;
  }
}

<span class="c">// tests/CalculatorTest.php</span>
<span class="k">namespace</span> App\Tests;

<span class="k">use</span> App\Calculator;
<span class="k">use</span> PHPUnit\Framework\TestCase;

<span class="k">class</span> <span class="t">CalculatorTest</span> <span class="k">extends</span> <span class="t">TestCase</span> {
  <span class="k">public function</span> <span class="f">testAdd</span>(): <span class="t">void</span> {
    <span class="v">$calc</span> = <span class="k">new</span> <span class="t">Calculator</span>();
    <span class="v">$this</span>-&gt;<span class="f">assertSame</span>(<span class="s">5</span>, <span class="v">$calc</span>-&gt;<span class="f">add</span>(<span class="s">2</span>, <span class="s">3</span>));
  }

  <span class="k">public function</span> <span class="f">testDivideByZero</span>(): <span class="t">void</span> {
    <span class="v">$this</span>-&gt;<span class="f">expectException</span>(\<span class="t">DivisionByZeroError</span>::<span class="k">class</span>);
    (<span class="k">new</span> <span class="t">Calculator</span>())-&gt;<span class="f">divide</span>(<span class="s">10</span>, <span class="s">0</span>);
  }
}</pre>

  <p>Ausführen: <code class="inline">vendor/bin/phpunit</code>. Du siehst grüne Punkte für jeden bestandenen Test.</p>

  <h2>Data Providers</h2>
  <p>Wenn du eine Methode mit vielen Input-Output-Paaren testen willst, ist ein Data Provider effizienter als einzelne Testmethoden:</p>

<pre><span class="k">use</span> PHPUnit\Framework\Attributes\DataProvider;

<span class="k">class</span> <span class="t">CalculatorTest</span> <span class="k">extends</span> <span class="t">TestCase</span> {
  <span class="k">#[DataProvider(<span class="s">'addCases'</span>)]</span>
  <span class="k">public function</span> <span class="f">testAdd</span>(<span class="t">int</span> <span class="v">$a</span>, <span class="t">int</span> <span class="v">$b</span>, <span class="t">int</span> <span class="v">$expected</span>): <span class="t">void</span> {
    <span class="v">$this</span>-&gt;<span class="f">assertSame</span>(<span class="v">$expected</span>, (<span class="k">new</span> <span class="t">Calculator</span>())-&gt;<span class="f">add</span>(<span class="v">$a</span>, <span class="v">$b</span>));
  }

  <span class="k">public static function</span> <span class="f">addCases</span>(): <span class="t">array</span> {
    <span class="k">return</span> [
      <span class="s">'positives'</span> =&gt; [<span class="s">2</span>, <span class="s">3</span>, <span class="s">5</span>],
      <span class="s">'negatives'</span> =&gt; [<span class="s">-1</span>, <span class="s">-2</span>, <span class="s">-3</span>],
      <span class="s">'mixed'</span>     =&gt; [<span class="s">-5</span>, <span class="s">10</span>, <span class="s">5</span>],
      <span class="s">'zero'</span>      =&gt; [<span class="s">0</span>, <span class="s">0</span>, <span class="s">0</span>],
    ];
  }
}</pre>

  <p>PHPUnit ruft <code class="inline">testAdd</code> einmal pro Datensatz auf. Bei Fehler zeigt es den Key (z.B. <code class="inline">'negatives'</code>), du findest den problematischen Fall sofort.</p>

  <h2>Mocks für Abhängigkeiten</h2>
  <p>Wenn deine Klasse externe Services nutzt (DB, API), willst du im Test nicht wirklich zugreifen. Mit <b>Mocks</b> simulierst du das Verhalten:</p>

<pre><span class="k">public function</span> <span class="f">testOrderServiceLogsCreation</span>(): <span class="t">void</span> {
  <span class="c">// Mock erstellen</span>
  <span class="v">$logger</span> = <span class="v">$this</span>-&gt;<span class="f">createMock</span>(<span class="t">LoggerInterface</span>::<span class="k">class</span>);

  <span class="c">// Erwarte: info wird genau 1x mit 'Order created' aufgerufen</span>
  <span class="v">$logger</span>-&gt;<span class="f">expects</span>(<span class="v">$this</span>-&gt;<span class="f">once</span>())
         -&gt;<span class="f">method</span>(<span class="s">'info'</span>)
         -&gt;<span class="f">with</span>(<span class="v">$this</span>-&gt;<span class="f">stringContains</span>(<span class="s">'Order created'</span>));

  <span class="v">$service</span> = <span class="k">new</span> <span class="t">OrderService</span>(<span class="v">$logger</span>);
  <span class="v">$service</span>-&gt;<span class="f">create</span>(<span class="k">new</span> <span class="t">Order</span>(<span class="s">42</span>));
}</pre>

  <h2>Was zuerst testen?</h2>
  <p>Faustregel für Anfang:</p>

  <ul>
    <li><b>Business-Logik</b>  Berechnungen, Domänen-Regeln, Validierung</li>
    <li><b>Edge Cases</b>  null, leere Strings, Grenzwerte</li>
    <li><b>Bug Fixes</b>  jeder gefixte Bug bekommt einen Test, damit er nicht wiederkehrt</li>
  </ul>

  <p>Was du <i>nicht</i> testen musst: Getter/Setter, fremde Library-Funktionen, Framework-Code.</p>

  <div class="callout tip">
    <div class="callout-icon">✓</div>
    <div class="callout-body">
      <b>Test-Pyramid: viele Unit, wenige E2E</b>
      Schreibe viele Unit-Tests (schnell, isoliert), weniger Integration-Tests (echte DB, API), und wenige End-to-End-Tests (gesamte App). Eine Pyramide, keine Eisbecher-Form. Schnelles Feedback ist wichtiger als 100% Test-Coverage.
    </div>
  </div>

  <div class="recall">
    <b>Recall</b>
    <ol>
      <li>Was prüft <code style="color:#777BB4">assertSame</code> im Gegensatz zu <code style="color:#777BB4">assertEquals</code>?</li>
      <li>Wofür sind Data Providers?</li>
      <li>Warum nutzt du Mocks statt echter Abhängigkeiten in Tests?</li>
    </ol>
  </div>
</section>

<!-- ===== ENDING ===== -->
<section class="ending">
  <h1>Wie es weitergeht</h1>

  <p>Du hast PHP jetzt von OOP-Patterns über funktionale Werkzeuge bis zu Production-Tools durchlaufen. Damit baust du wartbare, getestete Applikationen. Aber Praxis schlägt Theorie  setze diese Patterns in echtem Code ein.</p>

  <h2>Spaced-Repetition-Plan</h2>
  <div class="spaced-plan">
    <div class="spaced-day">
      <b>Heute</b>
      <p>Guide gelesen, Recall-Fragen aus jedem Kapitel beantwortet.</p>
    </div>
    <div class="spaced-day">
      <b>+3 Tage</b>
      <p>Drei beliebige Kapitel überfliegen, Recall-Fragen aus dem Kopf.</p>
    </div>
    <div class="spaced-day">
      <b>+14 Tage</b>
      <p>Mini-Projekt: REST-API mit PDO, Tests und PHPStan Level 8.</p>
    </div>
    <div class="spaced-day">
      <b>+60 Tage</b>
      <p>Bestehendes Projekt um Tests und Static Analysis erweitern.</p>
    </div>
  </div>

  <h2>Was als nächstes lernen</h2>
  <p>Mit diesen Werkzeugen kannst du in jede Spezialisierung tiefer einsteigen:</p>

  <ul>
    <li><b>Frameworks</b>  Symfony oder Laravel mit voller Tiefe (DI, Events, Forms, Security)</li>
    <li><b>Doctrine ORM</b>  Domain-Modelle mit komplexen Beziehungen abbilden</li>
    <li><b>Event-Driven Architecture</b>  Messenger, Queues, Async-Processing</li>
    <li><b>Performance</b>  Profiling mit Blackfire, OpCache, Caching-Strategien</li>
    <li><b>Security</b>  Authentication, OWASP Top 10, Code-Review</li>
    <li><b>Microservices</b>  kleine PHP-Services, API-Gateways, Service-Discovery</li>
  </ul>

  <h2>Begleitmaterial</h2>
  <p>Dieser Guide ist Teil eines Sets:</p>
  <ul>
    <li><b>PHP OnePager</b>  die visuelle Übersicht</li>
    <li><b>PHP Cheatsheet</b>  die dichte Referenz</li>
    <li><b>PHP Mini-Guide</b>  der 15-Min-Schnelleinstieg</li>
    <li><b>PHP Anfänger-Guide</b>  die Grundlagen</li>
    <li><b>PHP Fortgeschritten-Guide</b> (dieses Dokument)  Patterns und Production</li>
  </ul>
</section>

</body>
</html>