368 KiB
368 KiB
<!DOCTYPE html><html lang="de"><head><meta charset="utf-8"><title>PHP – Der EndGuide</title><style>
/* ============================================================
PHP KOMPLETT-GUIDE · Stylesheet (WeasyPrint)
Aufbauend auf dem Mini-Guide-Stil, erweitert für Buchumfang
============================================================ */
/* ---------- PAGE ---------- */
@page {
size: A4;
margin: 20mm 18mm 18mm 18mm;
@bottom-center {
content: counter(page);
font-family: -apple-system, "Segoe UI", sans-serif;
font-size: 8pt;
color: #888;
}
@bottom-right {
content: "PHP EndGuide";
font-family: -apple-system, "Segoe UI", sans-serif;
font-size: 8pt;
color: #888;
}
@top-right {
content: string(chaptertitle);
font-family: -apple-system, "Segoe UI", sans-serif;
font-size: 8pt;
color: #aaa;
}
}
/* Cover-Seite: keine Kopf-/Fußzeile */
@page cover {
margin: 0;
@bottom-center { content: none; }
@bottom-right { content: none; }
@top-right { content: none; }
}
/* Kapitel-Startseiten: keine Kopfzeile, da Titel dort prominent */
@page chapterstart {
@top-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;
--neutral: #b8860b;
}
/* Darkmode — die App setzt data-theme="dark" auf <html>; Druck/PDF bleibt hell */
@media screen {
html[data-theme="dark"] {
--ink: #e6e8ee;
--muted: #9aa3b2;
--line: #2c3038;
--bg-soft: #23262e;
}
html[data-theme="dark"] body { background: #15171c; }
}
html, body {
font-family: Charter, "Source Serif Pro", Georgia, serif;
color: var(--ink);
font-size: 10.5pt;
line-height: 1.55;
}
/* ============================================================
COVER
============================================================ */
.cover {
page: cover;
page-break-after: always;
height: 297mm;
background: linear-gradient(150deg, var(--php-darker) 0%, var(--php-dark) 45%, var(--php) 100%);
color: white;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
padding: 0 22mm;
}
.cover-logo {
width: 30mm; height: 30mm;
background: rgba(255,255,255,0.14);
border: 1.5pt solid rgba(255,255,255,0.5);
border-radius: 7mm;
display: flex; align-items: center; justify-content: center;
font-family: -apple-system, sans-serif;
font-size: 20pt; font-weight: 800;
margin-bottom: 14mm;
letter-spacing: -1pt;
}
.cover h1 {
font-family: -apple-system, sans-serif;
font-size: 42pt;
font-weight: 800;
line-height: 1.02;
letter-spacing: -1.5pt;
margin-bottom: 6mm;
}
.cover h1 .light { font-weight: 300; display:block; font-size: 30pt; opacity: 0.85; }
.cover .sub {
font-family: -apple-system, sans-serif;
font-size: 13pt;
font-weight: 400;
opacity: 0.9;
line-height: 1.5;
max-width: 130mm;
margin-bottom: 18mm;
}
.cover .meta-row {
font-family: -apple-system, sans-serif;
font-size: 9.5pt;
opacity: 0.85;
border-top: 1pt solid rgba(255,255,255,0.3);
padding-top: 5mm;
display: flex;
gap: 8mm;
}
.cover .meta-row b { font-weight: 700; }
.cover-deco {
position: absolute;
font-family: "SF Mono", Consolas, monospace;
font-size: 120pt;
font-weight: 800;
opacity: 0.06;
bottom: 10mm;
right: 12mm;
line-height: 1;
}
/* ============================================================
INHALTSVERZEICHNIS
============================================================ */
.toc {
page-break-after: always;
}
.toc h2 {
font-family: -apple-system, sans-serif;
font-size: 22pt;
font-weight: 800;
color: var(--php-dark);
margin-bottom: 8mm;
border: none;
}
.toc-part {
font-family: -apple-system, sans-serif;
font-size: 10pt;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 1.5pt;
color: var(--php);
margin: 6mm 0 2mm 0;
padding-bottom: 1mm;
border-bottom: 1pt solid var(--line);
}
.toc-part:first-of-type { margin-top: 0; }
.toc-entry {
display: flex;
font-family: -apple-system, sans-serif;
font-size: 9.5pt;
margin: 1.6mm 0;
align-items: baseline;
}
.toc-num {
color: var(--php-dark);
font-weight: 700;
width: 11mm;
flex-shrink: 0;
}
.toc-title { color: var(--ink); }
.toc-dots {
flex: 1;
border-bottom: 1pt dotted var(--line);
margin: 0 2mm;
transform: translateY(-1mm);
}
.toc-page { color: var(--muted); font-variant-numeric: tabular-nums; }
/* ============================================================
TEIL-TRENNER (Part divider)
============================================================ */
.part-divider {
page: chapterstart;
page-break-before: always;
page-break-after: always;
height: 257mm;
display: flex;
flex-direction: column;
justify-content: center;
background: var(--bg-soft);
border-radius: 4mm;
padding: 0 20mm;
}
.part-divider .part-kicker {
font-family: -apple-system, sans-serif;
font-size: 11pt;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 3pt;
color: var(--php);
margin-bottom: 4mm;
}
.part-divider h1 {
font-family: -apple-system, sans-serif;
font-size: 34pt;
font-weight: 800;
color: var(--php-darker);
letter-spacing: -1pt;
line-height: 1.05;
margin-bottom: 6mm;
}
.part-divider .part-desc {
font-size: 12pt;
color: var(--muted);
font-style: italic;
max-width: 120mm;
}
.part-divider .part-chapters {
margin-top: 10mm;
font-family: -apple-system, sans-serif;
font-size: 9.5pt;
color: var(--php-dark);
}
.part-divider .part-chapters span {
display: block;
margin: 1.5mm 0;
padding-left: 5mm;
border-left: 2pt solid var(--php);
}
/* ============================================================
KAPITEL
============================================================ */
.chapter {
margin-top: 8mm;
}
.chapter-head {
string-set: chaptertitle content();
margin-bottom: 6mm;
padding-bottom: 3mm;
border-bottom: 2pt solid var(--ink);
}
.chapter-num {
font-family: -apple-system, sans-serif;
font-size: 9pt;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 2pt;
color: var(--php);
display: block;
margin-bottom: 1.5mm;
}
.chapter-head h1 {
font-family: -apple-system, sans-serif;
font-size: 24pt;
font-weight: 800;
color: var(--php-dark);
letter-spacing: -0.5pt;
line-height: 1.1;
}
/* ============================================================
ÜBERSCHRIFTEN IM FLIESSTEXT
============================================================ */
h2 {
font-family: -apple-system, sans-serif;
font-size: 14pt;
font-weight: 700;
color: var(--php-dark);
margin: 6mm 0 2.5mm 0;
page-break-after: avoid;
}
h3 {
font-family: -apple-system, sans-serif;
font-size: 11pt;
font-weight: 700;
color: var(--ink);
margin: 4mm 0 1.5mm 0;
page-break-after: avoid;
}
p {
margin-bottom: 2.5mm;
text-align: justify;
hyphens: auto;
}
p b, li b { color: var(--php-dark); }
ul, ol { margin: 1.5mm 0 3mm 6mm; }
li { margin-bottom: 1mm; }
/* ============================================================
CODE
============================================================ */
pre {
background: var(--code-bg);
color: var(--code-fg);
font-family: "SF Mono", Consolas, monospace;
font-size: 8.3pt;
line-height: 1.5;
padding: 3mm 4mm;
border-radius: 2mm;
margin: 2.5mm 0 3.5mm 0;
white-space: pre;
overflow: hidden;
page-break-inside: avoid;
}
.c { color: #6b8aae; font-style: italic; } /* comment */
.k { color: #ff79c6; } /* keyword */
.s { color: #f1c40f; } /* string/number */
.f { color: #50fa7b; } /* function */
.t { color: #8be9fd; } /* tag/type */
.v { color: #ffb86c; } /* variable */
.a { color: #bd93f9; } /* attribute/annotation */
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: 2.5mm 4mm;
margin: 3mm 0;
font-size: 10pt;
page-break-inside: avoid;
display: grid;
grid-template-columns: 6mm 1fr;
gap: 3mm;
}
.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: 1mm;
}
.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); }
.callout.deep { background: #fff8e8; border-left: 3pt solid var(--neutral); }
.callout.deep .callout-icon, .callout.deep .callout-body > b:first-child { color: var(--neutral); }
/* ============================================================
TABELLEN
============================================================ */
table {
border-collapse: collapse;
width: 100%;
margin: 2.5mm 0 3.5mm 0;
font-family: -apple-system, sans-serif;
font-size: 9pt;
page-break-inside: avoid;
}
th {
background: var(--php-dark);
color: white;
text-align: left;
padding: 1.8mm 3mm;
font-weight: 700;
}
td {
padding: 1.5mm 3mm;
border-bottom: 1pt solid var(--line);
vertical-align: top;
}
tr:nth-child(even) td { background: var(--bg-soft); }
td code, th code {
font-family: "SF Mono", Consolas, monospace;
font-size: 8.2pt;
}
/* kleine Helfer */
.lead {
font-size: 11.5pt;
color: var(--muted);
font-style: italic;
margin-bottom: 4mm;
}
</style></head><body>
<section class="cover">
<div class="cover-logo">php</div>
<h1>PHP<span class="light">Der EndGuide</span></h1>
<div class="sub">Von der ersten Zeile bis in jede Nische – in durchdachter Progression und durchgehend nach einem Prinzip: jedes Thema wird erklärt und sofort mit lauffähigem Beispiel gezeigt. Modernes PHP 8.5.</div>
<div class="meta-row">
<span><b>PHP 8.5</b> · Stand 2026</span>
<span><b>17 Teile</b> · 97 Kapitel</span>
<span>Erklärung + Beispiel je Konzept</span>
</div>
<div class="cover-deco"><?php</div>
</section><section class="toc"><h2>Inhalt</h2>
<div class="toc-part">Teil 1 · Fundament</div>
<div class="toc-entry"><span class="toc-num">1</span><span class="toc-title">PHP einrichten & ausführen</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">2</span><span class="toc-title">Variablen & Datentypen</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">3</span><span class="toc-title">Operatoren & Ausdrücke</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">4</span><span class="toc-title">Bedingungen & Verzweigungen</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">5</span><span class="toc-title">Schleifen</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">6</span><span class="toc-title">Klassische Kontrollstrukturen</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">7</span><span class="toc-title">Funktionen</span><span class="toc-dots"></span></div>
<div class="toc-part">Teil 2 · Daten verarbeiten: Strings, Arrays & Muster</div>
<div class="toc-entry"><span class="toc-num">8</span><span class="toc-title">Strings im Detail</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">9</span><span class="toc-title">Arrays</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">10</span><span class="toc-title">Reguläre Ausdrücke: Grundlagen</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">11</span><span class="toc-title">Reguläre Ausdrücke in der Praxis</span><span class="toc-dots"></span></div>
<div class="toc-part">Teil 3 · Code strukturieren</div>
<div class="toc-entry"><span class="toc-num">12</span><span class="toc-title">Code aufteilen: include & require</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">13</span><span class="toc-title">Namespaces</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">14</span><span class="toc-title">Composer & Autoloading</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">15</span><span class="toc-title">Fehler & Exceptions</span><span class="toc-dots"></span></div>
<div class="toc-part">Teil 4 · Das Typsystem & moderne Sprache</div>
<div class="toc-entry"><span class="toc-num">16</span><span class="toc-title">Typen & strict_types</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">17</span><span class="toc-title">Union-, Nullable- & spezielle Typen</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">18</span><span class="toc-title">Enums</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">19</span><span class="toc-title">Moderne Syntax</span><span class="toc-dots"></span></div>
<div class="toc-part">Teil 5 · Objektorientierung: Grundlagen</div>
<div class="toc-entry"><span class="toc-num">20</span><span class="toc-title">Klassen & Objekte</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">21</span><span class="toc-title">Sichtbarkeit & Kapselung</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">22</span><span class="toc-title">Konstruktoren modern</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">23</span><span class="toc-title">Vererbung</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">24</span><span class="toc-title">Abstrakte Klassen & Interfaces</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">25</span><span class="toc-title">Traits</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">26</span><span class="toc-title">Statisches & Konstanten</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">27</span><span class="toc-title">Magische Methoden</span><span class="toc-dots"></span></div>
<div class="toc-part">Teil 6 · Objektorientierung: fortgeschritten</div>
<div class="toc-entry"><span class="toc-num">28</span><span class="toc-title">Collections & Generics-Denken</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">29</span><span class="toc-title">Iteratoren & Generatoren</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">30</span><span class="toc-title">Closures & Bindung</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">31</span><span class="toc-title">Attribute</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">32</span><span class="toc-title">Reflection</span><span class="toc-dots"></span></div>
<div class="toc-part">Teil 7 · Die Standardbibliothek</div>
<div class="toc-entry"><span class="toc-num">33</span><span class="toc-title">Mathematik & Zahlen</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">34</span><span class="toc-title">Datum & Zeit</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">35</span><span class="toc-title">Dateien & Dateisystem</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">36</span><span class="toc-title">Streams & Filter</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">37</span><span class="toc-title">SPL-Datenstrukturen</span><span class="toc-dots"></span></div>
<div class="toc-part">Teil 8 · Datenformate & Serialisierung</div>
<div class="toc-entry"><span class="toc-num">38</span><span class="toc-title">JSON</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">39</span><span class="toc-title">XML</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">40</span><span class="toc-title">CSV & Tabellendaten</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">41</span><span class="toc-title">serialize & JsonSerializable</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">42</span><span class="toc-title">Weitere Formate: INI, YAML, Base64</span><span class="toc-dots"></span></div>
<div class="toc-part">Teil 9 · Datenbanken mit PDO</div>
<div class="toc-entry"><span class="toc-num">43</span><span class="toc-title">Verbindung & Grundlagen</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">44</span><span class="toc-title">Abfragen & Prepared Statements</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">45</span><span class="toc-title">Ergebnisse verarbeiten</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">46</span><span class="toc-title">Transaktionen</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">47</span><span class="toc-title">Datenbankdesign-Grundlagen</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">48</span><span class="toc-title">ORM-Konzepte & Doctrine</span><span class="toc-dots"></span></div>
<div class="toc-part">Teil 10 · Web-Entwicklung mit purem PHP</div>
<div class="toc-entry"><span class="toc-num">49</span><span class="toc-title">Wie PHP & Webserver zusammenspielen</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">50</span><span class="toc-title">Request & Superglobals</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">51</span><span class="toc-title">Formulare verarbeiten</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">52</span><span class="toc-title">Eingabevalidierung & Filter</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">53</span><span class="toc-title">Sessions</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">54</span><span class="toc-title">Cookies</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">55</span><span class="toc-title">Datei-Uploads</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">56</span><span class="toc-title">HTTP-Clients & APIs</span><span class="toc-dots"></span></div>
<div class="toc-part">Teil 11 · Sicherheit</div>
<div class="toc-entry"><span class="toc-num">57</span><span class="toc-title">Das Grundprinzip: niemals vertrauen</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">58</span><span class="toc-title">SQL-Injection im Detail</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">59</span><span class="toc-title">XSS & CSRF im Detail</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">60</span><span class="toc-title">Authentifizierung & Passwörter</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">61</span><span class="toc-title">Sessions & Cookies absichern</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">62</span><span class="toc-title">Kryptografie & Sodium</span><span class="toc-dots"></span></div>
<div class="toc-part">Teil 12 · Sauberes Design</div>
<div class="toc-entry"><span class="toc-num">63</span><span class="toc-title">SOLID-Prinzipien</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">64</span><span class="toc-title">Dependency Injection</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">65</span><span class="toc-title">Wertobjekte & DTOs</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">66</span><span class="toc-title">Fehlerbehandlung als Architektur</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">67</span><span class="toc-title">Häufige Entwurfsmuster</span><span class="toc-dots"></span></div>
<div class="toc-part">Teil 13 · Entwurfsmuster & Architektur</div>
<div class="toc-entry"><span class="toc-num">68</span><span class="toc-title">Erzeugungsmuster</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">69</span><span class="toc-title">Strukturmuster</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">70</span><span class="toc-title">Verhaltensmuster</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">71</span><span class="toc-title">MVC & Schichtenarchitektur</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">72</span><span class="toc-title">Domain-Driven Design</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">73</span><span class="toc-title">Event-getriebene Architektur</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">74</span><span class="toc-title">Hexagonale Architektur</span><span class="toc-dots"></span></div>
<div class="toc-part">Teil 14 · Qualität & Performance</div>
<div class="toc-entry"><span class="toc-num">75</span><span class="toc-title">Testen mit PHPUnit</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">76</span><span class="toc-title">Statische Analyse</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">77</span><span class="toc-title">Code-Style & Tooling</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">78</span><span class="toc-title">Debugging & Xdebug</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">79</span><span class="toc-title">Performance & OPcache</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">80</span><span class="toc-title">Speicher & Referenzen</span><span class="toc-dots"></span></div>
<div class="toc-part">Teil 15 · CLI, Prozesse & asynchrones PHP</div>
<div class="toc-entry"><span class="toc-num">81</span><span class="toc-title">CLI-Programme bauen</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">82</span><span class="toc-title">Prozesse & FFI</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">83</span><span class="toc-title">Das klassische Modell & Fibers</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">84</span><span class="toc-title">Event-Loops & ReactPHP</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">85</span><span class="toc-title">Swoole, Amp & FrankenPHP</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">86</span><span class="toc-title">Echte Parallelität</span><span class="toc-dots"></span></div>
<div class="toc-part">Teil 16 · Das Ökosystem</div>
<div class="toc-entry"><span class="toc-num">87</span><span class="toc-title">Composer in der Tiefe</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">88</span><span class="toc-title">Die PSR-Standards</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">89</span><span class="toc-title">Symfony im Überblick</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">90</span><span class="toc-title">Laravel im Überblick</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">91</span><span class="toc-title">Wichtige Bibliotheken</span><span class="toc-dots"></span></div>
<div class="toc-part">Teil 17 · In Produktion</div>
<div class="toc-entry"><span class="toc-num">92</span><span class="toc-title">Projektstruktur & Organisation</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">93</span><span class="toc-title">Deployment & Betrieb</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">94</span><span class="toc-title">Logging & Monitoring</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">95</span><span class="toc-title">Internationalisierung</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">96</span><span class="toc-title">Migration: von PHP 5 zu 8.5</span><span class="toc-dots"></span></div>
<div class="toc-entry"><span class="toc-num">97</span><span class="toc-title">Weiterlernen & Community</span><span class="toc-dots"></span></div>
</section><section class="part-divider"><div class="part-kicker">Teil 1</div><h1>Fundament</h1><div class="part-desc">Der Einstieg: PHP einrichten, die Bausteine der Sprache und der vollständige Kontrollfluss – genug, um echte kleine Programme zu schreiben.</div><div class="part-chapters"><span>1 · PHP einrichten & ausführen</span><span>2 · Variablen & Datentypen</span><span>3 · Operatoren & Ausdrücke</span><span>4 · Bedingungen & Verzweigungen</span><span>5 · Schleifen</span><span>6 · Klassische Kontrollstrukturen</span><span>7 · Funktionen</span></div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 1</span><h1>PHP einrichten & ausführen</h1></div><p class="lead">PHP ist eine serverseitige Skriptsprache: Der Code läuft auf dem Server und erzeugt meist HTML für den Browser. Ebenso treibt PHP Kommandozeilen-Programme, Skripte und APIs an. In diesem Kapitel bringen wir das erste Skript zum Laufen.</p>
<h2>PHP-Tags</h2>
<p>PHP-Code steht zwischen <code class="inline"><?php</code> und <code class="inline">?></code>. Alles außerhalb der Tags wird unverändert ausgegeben – so verzahnt sich PHP mit HTML:</p>
<pre><span class="t"><p></span>Statischer Text.<span class="t"></p></span>
<span class="t"><?php</span> <span class="k">echo</span> <span class="s">"Dynamisch aus PHP."</span>; <span class="t">?></span></pre>
<p>In reinen PHP-Dateien (ohne HTML) lässt man das schließende <code class="inline">?></code> bewusst weg – das verhindert versehentliche Leerzeichen in der Ausgabe.</p>
<h2>Etwas ausgeben</h2>
<pre><span class="k">echo</span> <span class="s">"Hallo Welt\n"</span>; <span class="c">// gibt Text aus</span>
<span class="k">print</span> <span class="s">"geht auch\n"</span>; <span class="c">// wie echo, liefert zusätzlich 1 zurück</span>
<span class="k">var_dump</span>([<span class="s">1</span>, <span class="s">2</span>]); <span class="c">// Debug-Ausgabe mit Typ und Struktur</span></pre>
<h2>Anweisungen & Semikolon</h2>
<p>Jede Anweisung endet mit einem Semikolon; Zeilenumbrüche sind für PHP bedeutungslos:</p>
<pre><span class="v">$a</span> = <span class="s">5</span>;
<span class="v">$b</span> = <span class="s">10</span>; <span class="k">echo</span> <span class="v">$a</span> + <span class="v">$b</span>; <span class="c">// 15</span></pre>
<h2>Kommentare</h2>
<pre><span class="c">// einzeilig</span>
<span class="c"># ebenfalls einzeilig</span>
<span class="c">/* mehrzeilig –
über mehrere Zeilen */</span></pre>
<h2>Ein Skript ausführen</h2>
<p>Zwei Wege führen ein Skript aus. Über die Kommandozeile direkt:</p>
<pre><span class="c"># Datei ausführen</span>
php skript.php</pre>
<p>Oder mit dem eingebauten Entwicklungs-Webserver – ideal zum Ausprobieren ohne Apache/Nginx:</p>
<pre><span class="c"># bedient http://localhost:8000 aus dem aktuellen Ordner</span>
php -S localhost:8000</pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Version & Konfiguration</b><code class="inline">php -v</code> zeigt die installierte Version (dieser Guide setzt PHP 8.5 voraus). Globale Einstellungen stehen in der <code class="inline">php.ini</code>; ihren Pfad verrät <code class="inline">php --ini</code>. Den eingebauten Server nutzt man nur zur Entwicklung, nie in Produktion.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 2</span><h1>Variablen & Datentypen</h1></div><p class="lead">Eine Variable ist ein benannter Behälter für einen Wert. PHP ist dynamisch typisiert: Der Typ ergibt sich aus dem Wert und darf wechseln. Es gibt acht Grundtypen – hier jeder einzeln mit Beispiel.</p>
<h2>Variablen</h2>
<p>Ein <code class="inline">$</code>, dann der Name. Zuweisen mit <code class="inline">=</code>:</p>
<pre><span class="v">$name</span> = <span class="s">"Anna"</span>;
<span class="v">$age</span> = <span class="s">30</span>;
<span class="k">echo</span> <span class="s">"$name ist $age"</span>; <span class="c">// Anna ist 30</span></pre>
<h2>int</h2>
<p>Ganzzahlen ohne Nachkommastellen – auch hexadezimal, binär oder mit Unterstrichen lesbar geschrieben:</p>
<pre><span class="v">$n</span> = <span class="s">42</span>;
<span class="k">var_dump</span>(<span class="v">$n</span>); <span class="c">// int(42)</span>
<span class="k">var_dump</span>(<span class="s">0xFF</span>, <span class="s">0b101</span>, <span class="s">1_000_000</span>); <span class="c">// hex, binär, mit Trennstrichen lesbar</span></pre>
<h2>float</h2>
<p>Zahlen mit Nachkommastellen (Fließkommazahlen):</p>
<pre><span class="v">$pi</span> = <span class="s">3.14</span>;
<span class="k">var_dump</span>(<span class="v">$pi</span>); <span class="c">// float(3.14)</span></pre>
<h2>string</h2>
<p>Zeichenketten, also beliebiger Text:</p>
<pre><span class="v">$s</span> = <span class="s">"Text"</span>;
<span class="k">var_dump</span>(<span class="v">$s</span>); <span class="c">// string(4) "Text"</span></pre>
<h2>bool</h2>
<p>Ein Wahrheitswert mit nur zwei möglichen Inhalten, <code class="inline">true</code> oder <code class="inline">false</code>:</p>
<pre><span class="v">$ok</span> = <span class="k">true</span>;
<span class="k">var_dump</span>(<span class="v">$ok</span>); <span class="c">// bool(true)</span></pre>
<h2>null</h2>
<p>Steht für „kein Wert" – eine Variable, die bewusst nichts enthält:</p>
<pre><span class="v">$empty</span> = <span class="k">null</span>;
<span class="k">var_dump</span>(<span class="v">$empty</span>); <span class="c">// NULL</span></pre>
<h2>array</h2>
<p>Liste oder Schlüssel-Wert-Sammlung; ein eigenes Kapitel widmet sich ihr vollständig:</p>
<pre><span class="v">$list</span> = [<span class="s">1</span>, <span class="s">2</span>, <span class="s">3</span>];
<span class="k">var_dump</span>(<span class="k">count</span>(<span class="v">$list</span>)); <span class="c">// int(3)</span></pre>
<h2>object</h2>
<p>Eine konkrete Instanz einer Klasse; die Objektorientierung vertieft das ein eigener Teil:</p>
<pre><span class="v">$obj</span> = <span class="k">new</span> <span class="t">stdClass</span>();
<span class="v">$obj</span>-><span class="v">x</span> = <span class="s">1</span>;
<span class="k">echo</span> <span class="v">$obj</span>-><span class="v">x</span>; <span class="c">// 1</span></pre>
<p>Die beiden verbleibenden Grundtypen – <b>callable</b> (aufrufbares) und <b>resource</b> (Handle auf z. B. eine Datei) – begegnen uns später bei Funktionen und Dateien.</p>
<h2>Typ prüfen & umwandeln</h2>
<pre><span class="k">echo</span> <span class="k">gettype</span>(<span class="s">42</span>); <span class="c">// integer</span>
<span class="k">var_dump</span>(<span class="k">is_int</span>(<span class="s">42</span>)); <span class="c">// bool(true)</span>
<span class="k">var_dump</span>((<span class="t">int</span>) <span class="s">"13 Äpfel"</span>); <span class="c">// int(13) – explizite Umwandlung</span>
<span class="k">var_dump</span>((<span class="t">bool</span>) <span class="s">0</span>); <span class="c">// bool(false)</span></pre>
<h2>Typ-Jonglage</h2>
<p>PHP wandelt Typen bei Bedarf automatisch um – bequem, aber eine Fehlerquelle. Der Operator entscheidet, wie ein Wert gelesen wird:</p>
<pre><span class="k">echo</span> <span class="s">"5"</span> + <span class="s">3</span>; <span class="c">// 8 – der String wird zur Zahl</span>
<span class="k">echo</span> <span class="s">"5"</span> . <span class="s">3</span>; <span class="c">// 53 – der Punkt verkettet zu Text</span></pre>
<h2>Konstanten</h2>
<p>Ein Wert, der sich nach dem Festlegen nicht mehr ändert:</p>
<pre><span class="k">const</span> <span class="t">VAT</span> = <span class="s">0.19</span>;
<span class="k">define</span>(<span class="s">"VERSION"</span>, <span class="s">"1.0"</span>);
<span class="k">echo</span> <span class="t">VAT</span>; <span class="c">// 0.19</span>
<span class="k">echo</span> <span class="t">VERSION</span>; <span class="c">// 1.0</span></pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Aussagekräftige Namen</b>Variablennamen sind in PHP case-sensitiv (<code class="inline">$name</code> ≠ <code class="inline">$Name</code>) und beginnen mit Buchstabe oder Unterstrich. Wähle sprechende Namen (<code class="inline">$gesamtPreis</code> statt <code class="inline">$gp</code>) – Code wird viel öfter gelesen als geschrieben.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 3</span><h1>Operatoren & Ausdrücke</h1></div><p class="lead">Operatoren verknüpfen Werte zu neuen Werten – für jede Berechnung, jeden Vergleich, jede Bit-Manipulation. Hier vollständig, jeder mit Beispiel.</p>
<h2>Arithmetik</h2>
<pre><span class="k">echo</span> <span class="s">7</span> + <span class="s">3</span>; <span class="c">// 10</span>
<span class="k">echo</span> <span class="s">7</span> - <span class="s">3</span>; <span class="c">// 4</span>
<span class="k">echo</span> <span class="s">7</span> * <span class="s">3</span>; <span class="c">// 21</span>
<span class="k">echo</span> <span class="s">7</span> / <span class="s">2</span>; <span class="c">// 3.5</span>
<span class="k">echo</span> <span class="s">7</span> % <span class="s">3</span>; <span class="c">// 1 (Rest der Division, „Modulo")</span>
<span class="k">echo</span> <span class="s">2</span> ** <span class="s">8</span>; <span class="c">// 256 (Potenz)</span></pre>
<h2>Zuweisungs-Kurzformen</h2>
<p>Zu <b>jedem</b> binären Operator gibt es eine zusammengesetzte Zuweisung – sie wendet den Operator an und schreibt zurück. <code class="inline">$x += 3</code> ist <code class="inline">$x = $x + 3</code>:</p>
<pre><span class="v">$x</span> = <span class="s">10</span>;
<span class="v">$x</span> += <span class="s">3</span>; <span class="c">// 13 Addition</span>
<span class="v">$x</span> -= <span class="s">1</span>; <span class="c">// 12 Subtraktion</span>
<span class="v">$x</span> *= <span class="s">2</span>; <span class="c">// 24 Multiplikation</span>
<span class="v">$x</span> /= <span class="s">4</span>; <span class="c">// 6 Division</span>
<span class="v">$x</span> %= <span class="s">4</span>; <span class="c">// 2 Modulo</span>
<span class="v">$x</span> **= <span class="s">3</span>; <span class="c">// 8 Potenz</span></pre>
<p>Dazu die Kurzformen für Strings und Null-Koaleszenz sowie – analog – für jeden Bit-Operator:</p>
<pre><span class="v">$s</span> = <span class="s">"Hallo"</span>;
<span class="v">$s</span> .= <span class="s">" Welt"</span>; <span class="c">// "Hallo Welt" – String anhängen</span>
<span class="v">$name</span> = <span class="k">null</span>;
<span class="v">$name</span> ??= <span class="s">"Gast"</span>; <span class="c">// "Gast" – nur zuweisen, wenn null/ungesetzt</span>
<span class="v">$f</span> = <span class="s">0b0011</span>;
<span class="v">$f</span> &= <span class="s">0b0110</span>; <span class="c">// UND-Zuweisung</span>
<span class="v">$f</span> |= <span class="s">0b1000</span>; <span class="c">// ODER-Zuweisung</span>
<span class="v">$f</span> ^= <span class="s">0b0001</span>; <span class="c">// XOR-Zuweisung</span>
<span class="v">$f</span> <<= <span class="s">1</span>; <span class="c">// links schieben</span>
<span class="v">$f</span> >>= <span class="s">2</span>; <span class="c">// rechts schieben</span></pre>
<h2>Inkrement & Dekrement</h2>
<p>Um 1 erhöhen/verringern. Die Stellung entscheidet, ob vor oder nach der Auswertung verändert wird:</p>
<pre><span class="v">$i</span> = <span class="s">5</span>;
<span class="k">echo</span> <span class="v">$i</span>++; <span class="c">// 5 – erst ausgeben, dann erhöhen ($i ist danach 6)</span>
<span class="k">echo</span> ++<span class="v">$i</span>; <span class="c">// 7 – erst erhöhen, dann ausgeben</span>
<span class="v">$i</span>--; <span class="c">// verringern</span></pre>
<h2>Vergleiche</h2>
<p><code class="inline">==</code> prüft den Wert mit Typumwandlung, <code class="inline">===</code> zusätzlich den Typ:</p>
<pre><span class="k">var_dump</span>(<span class="s">1</span> == <span class="s">"1"</span>); <span class="c">// true – Werte gleich</span>
<span class="k">var_dump</span>(<span class="s">1</span> === <span class="s">"1"</span>); <span class="c">// false – verschiedene Typen</span></pre>
<p>Für „ungleich" gibt es <code class="inline">!=</code> (und gleichbedeutend <code class="inline"><></code>) sowie streng <code class="inline">!==</code>:</p>
<pre><span class="k">var_dump</span>(<span class="s">5</span> != <span class="s">3</span>); <span class="c">// true</span>
<span class="k">var_dump</span>(<span class="s">5</span> <> <span class="s">3</span>); <span class="c">// true – identisch zu !=</span>
<span class="k">var_dump</span>(<span class="s">5</span> !== <span class="s">"5"</span>); <span class="c">// true – Typ verschieden</span></pre>
<p>Die Größenvergleiche und der Raumschiff-Operator, der -1, 0 oder 1 liefert:</p>
<pre><span class="k">var_dump</span>(<span class="s">3</span> < <span class="s">5</span>, <span class="s">5</span> >= <span class="s">5</span>); <span class="c">// true, true</span>
<span class="k">echo</span> <span class="s">3</span> <=> <span class="s">5</span>; <span class="c">// -1 (links kleiner; 0 = gleich, 1 = größer)</span></pre>
<h2>Logik</h2>
<pre><span class="v">$a</span> = <span class="k">true</span>; <span class="v">$b</span> = <span class="k">false</span>;
<span class="k">var_dump</span>(<span class="v">$a</span> && <span class="v">$b</span>); <span class="c">// false (und)</span>
<span class="k">var_dump</span>(<span class="v">$a</span> || <span class="v">$b</span>); <span class="c">// true (oder)</span>
<span class="k">var_dump</span>(!<span class="v">$a</span>); <span class="c">// false (nicht)</span></pre>
<p>Es gibt auch die Wortformen <code class="inline">and</code>, <code class="inline">or</code>, <code class="inline">xor</code>. <b>Achtung:</b> Sie binden schwächer als <code class="inline">=</code> – eine berüchtigte Falle:</p>
<pre><span class="v">$ok</span> = <span class="k">true</span> && <span class="k">false</span>; <span class="c">// false (&& bindet stärker als =)</span>
<span class="v">$ok</span> = <span class="k">true</span> <span class="k">and</span> <span class="k">false</span>; <span class="c">// true! (= greift vor „and")</span>
<span class="k">var_dump</span>(<span class="k">true</span> <span class="k">xor</span> <span class="k">false</span>); <span class="c">// true – genau einer ist wahr</span></pre>
<h2>Bit-Operatoren</h2>
<p>Arbeiten direkt auf den Bits einer Ganzzahl – für Flags, Masken, hardwarenahe Rechnung:</p>
<pre><span class="k">echo</span> <span class="s">6</span> & <span class="s">3</span>; <span class="c">// 2 UND: 110 & 011 = 010</span>
<span class="k">echo</span> <span class="s">6</span> | <span class="s">3</span>; <span class="c">// 7 ODER: 110 | 011 = 111</span>
<span class="k">echo</span> <span class="s">6</span> ^ <span class="s">3</span>; <span class="c">// 5 XOR: 110 ^ 011 = 101</span>
<span class="k">echo</span> ~<span class="s">5</span>; <span class="c">// -6 NICHT (alle Bits kippen)</span>
<span class="k">echo</span> <span class="s">1</span> << <span class="s">4</span>; <span class="c">// 16 links schieben (×2 je Stelle)</span>
<span class="k">echo</span> <span class="s">32</span> >> <span class="s">2</span>; <span class="c">// 8 rechts schieben (÷2 je Stelle)</span></pre>
<h2>String-, ternärer & Null-Koaleszenz-Operator</h2>
<pre><span class="k">echo</span> <span class="s">"Hallo"</span> . <span class="s">" "</span> . <span class="s">"Welt"</span>; <span class="c">// Hallo Welt (verketten)</span>
<span class="k">echo</span> <span class="v">$age</span> >= <span class="s">18</span> ? <span class="s">"erwachsen"</span> : <span class="s">"minderjährig"</span>; <span class="c">// ternär</span>
<span class="k">echo</span> <span class="v">$name</span> ?? <span class="s">"Gast"</span>; <span class="c">// erster nicht-null Wert</span></pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Zwei Fallen</b><code class="inline">==</code> wandelt Typen um und überrascht (vergleiche im Zweifel mit <code class="inline">===</code>). Und <code class="inline">and</code>/<code class="inline">or</code> binden schwächer als die Zuweisung, weshalb <code class="inline">$x = $a or $b</code> selten das tut, was man denkt – nutze <code class="inline">&&</code> und <code class="inline">||</code>.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 4</span><h1>Bedingungen & Verzweigungen</h1></div><p class="lead">Verzweigungen entscheiden, welcher Code unter welchen Umständen läuft. PHP bietet von der klassischen <code class="inline">if</code>-Kette bis zum modernen <code class="inline">match</code> mehrere Werkzeuge.</p>
<h2>if / elseif / else</h2>
<p>Der erste zutreffende Block läuft, der Rest wird übersprungen:</p>
<pre><span class="v">$points</span> = <span class="s">75</span>;
<span class="k">if</span> (<span class="v">$points</span> >= <span class="s">90</span>) {
<span class="k">echo</span> <span class="s">"Sehr gut"</span>;
} <span class="k">elseif</span> (<span class="v">$points</span> >= <span class="s">50</span>) {
<span class="k">echo</span> <span class="s">"Bestanden"</span>; <span class="c">// läuft</span>
} <span class="k">else</span> {
<span class="k">echo</span> <span class="s">"Durchgefallen"</span>;
}</pre>
<h2>Der ternäre Operator</h2>
<p>Eine kompakte if/else-Form, die einen Wert liefert:</p>
<pre><span class="v">$status</span> = <span class="v">$age</span> >= <span class="s">18</span> ? <span class="s">"erwachsen"</span> : <span class="s">"minderjährig"</span>;</pre>
<p>Die Kurzform <code class="inline">?:</code> nimmt den linken Wert, wenn er „truthy" ist:</p>
<pre><span class="v">$name</span> = <span class="v">$input</span> ?: <span class="s">"Gast"</span>; <span class="c">// $input, sonst "Gast"</span></pre>
<h2>Null-Koaleszenz</h2>
<p>Liefert den ersten Wert, der existiert und nicht <code class="inline">null</code> ist – ideal für Standardwerte ohne Fehler bei fehlenden Schlüsseln:</p>
<pre><span class="v">$page</span> = <span class="v">$_GET</span>[<span class="s">"seite"</span>] ?? <span class="s">"start"</span>; <span class="c">// kein Warning, wenn nicht gesetzt</span></pre>
<h2>match</h2>
<p>Seit PHP 8 kompakt, typsicher (<code class="inline">===</code>) und mit Rückgabewert. Anders als <code class="inline">switch</code> gibt es kein versehentliches Durchfallen:</p>
<pre><span class="v">$tag</span> = <span class="s">3</span>;
<span class="v">$name</span> = <span class="k">match</span>(<span class="v">$tag</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">"Werktag"</span>,
<span class="s">6</span>, <span class="s">7</span> => <span class="s">"Wochenende"</span>,
<span class="k">default</span> => <span class="s">"ungültig"</span>,
};
<span class="k">echo</span> <span class="v">$name</span>; <span class="c">// Werktag</span></pre>
<p><code class="inline">match</code> kann auch ohne Argument als saubere if-Kette dienen, indem die Bedingungen direkt geprüft werden:</p>
<pre><span class="v">$note</span> = <span class="k">match</span>(<span class="k">true</span>) {
<span class="v">$points</span> >= <span class="s">90</span> => <span class="s">"1"</span>,
<span class="v">$points</span> >= <span class="s">50</span> => <span class="s">"3"</span>,
<span class="k">default</span> => <span class="s">"5"</span>,
};</pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>match vor switch</b>Für Mehrfachverzweigungen mit Rückgabewert ist <code class="inline">match</code> meist die bessere Wahl: typsicher, ausdrucksstark und ohne <code class="inline">break</code>. Den klassischen <code class="inline">switch</code> – und wann er noch sinnvoll ist – behandelt das nächste Kapitel.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 5</span><h1>Schleifen</h1></div><p class="lead">Schleifen wiederholen Code. PHP hat vier Formen; welche passt, hängt davon ab, ob du über eine Sammlung läufst oder einen Zähler steuerst.</p>
<h2>while</h2>
<p>Wiederholt, solange die Bedingung wahr ist – sie wird <b>vor</b> jedem Durchlauf geprüft:</p>
<pre><span class="v">$i</span> = <span class="s">1</span>;
<span class="k">while</span> (<span class="v">$i</span> <= <span class="s">3</span>) {
<span class="k">echo</span> <span class="v">$i</span>; <span class="c">// 1, 2, 3</span>
<span class="v">$i</span>++;
}</pre>
<h2>do-while</h2>
<p>Wie <code class="inline">while</code>, aber die Prüfung erfolgt <b>am Ende</b> – der Block läuft also mindestens einmal:</p>
<pre><span class="v">$attempt</span> = <span class="s">0</span>;
<span class="k">do</span> {
<span class="v">$attempt</span>++;
} <span class="k">while</span> (<span class="v">$attempt</span> < <span class="s">3</span>);
<span class="k">echo</span> <span class="v">$attempt</span>; <span class="c">// 3</span></pre>
<h2>for</h2>
<p>Drei Teile in einer Zeile: Initialisierung, Bedingung, Schritt. Ideal, wenn die Anzahl bekannt ist:</p>
<pre><span class="k">for</span> (<span class="v">$i</span> = <span class="s">0</span>; <span class="v">$i</span> < <span class="s">3</span>; <span class="v">$i</span>++) {
<span class="k">echo</span> <span class="v">$i</span>; <span class="c">// 0, 1, 2</span>
}</pre>
<h2>foreach</h2>
<p>Läuft über jedes Element einer Sammlung, ohne Zähler:</p>
<pre><span class="v">$colors</span> = [<span class="s">"rot"</span>, <span class="s">"grün"</span>];
<span class="k">foreach</span> (<span class="v">$colors</span> <span class="k">as</span> <span class="v">$color</span>) {
<span class="k">echo</span> <span class="v">$color</span>; <span class="c">// rot, grün</span>
}</pre>
<p>Mit Schlüssel und Wert zugleich:</p>
<pre><span class="v">$prices</span> = [<span class="s">"apfel"</span> => <span class="s">2</span>, <span class="s">"birne"</span> => <span class="s">3</span>];
<span class="k">foreach</span> (<span class="v">$prices</span> <span class="k">as</span> <span class="v">$name</span> => <span class="v">$price</span>) {
<span class="k">echo</span> <span class="s">"$name: $price\n"</span>; <span class="c">// apfel: 2 / birne: 3</span>
}</pre>
<p>Mit <code class="inline">&</code> bekommst du eine Referenz und veränderst das Original:</p>
<pre><span class="k">foreach</span> (<span class="v">$prices</span> <span class="k">as</span> &<span class="v">$p</span>) { <span class="v">$p</span> *= <span class="s">2</span>; }
<span class="c">// alle Preise verdoppelt</span></pre>
<h2>break & continue</h2>
<p><code class="inline">break</code> verlässt die Schleife, <code class="inline">continue</code> springt zum nächsten Durchlauf:</p>
<pre><span class="k">foreach</span> ([<span class="s">1</span>, -<span class="s">2</span>, <span class="s">3</span>, <span class="s">99</span>] <span class="k">as</span> <span class="v">$n</span>) {
<span class="k">if</span> (<span class="v">$n</span> < <span class="s">0</span>) <span class="k">continue</span>; <span class="c">// negative überspringen</span>
<span class="k">if</span> (<span class="v">$n</span> > <span class="s">10</span>) <span class="k">break</span>; <span class="c">// ab 11 abbrechen</span>
<span class="k">echo</span> <span class="v">$n</span>; <span class="c">// 1, 3</span>
}</pre>
<p>In verschachtelten Schleifen verlässt <code class="inline">break 2;</code> zwei Ebenen auf einmal:</p>
<pre><span class="k">foreach</span> (<span class="v">$lines</span> <span class="k">as</span> <span class="v">$line</span>) {
<span class="k">foreach</span> (<span class="v">$line</span> <span class="k">as</span> <span class="v">$cell</span>) {
<span class="k">if</span> (<span class="v">$cell</span> === <span class="k">null</span>) <span class="k">break</span> <span class="s">2</span>; <span class="c">// beide Schleifen verlassen</span>
}
}</pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>foreach ist dein Standard</b>In der Praxis ist <code class="inline">foreach</code> die mit Abstand häufigste Schleife. <code class="inline">for</code> brauchst du nur, wenn du den Zähler selbst kontrollieren musst; <code class="inline">while</code>, wenn die Anzahl der Durchläufe vorher unbekannt ist.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 6</span><h1>Klassische Kontrollstrukturen</h1></div><p class="lead">Neben <code class="inline">if</code>, <code class="inline">match</code> und den Schleifen kennt PHP weitere Steuerungskonstrukte. Im Alltag seltener, in fremdem Code aber häufig – du solltest sie lesen und einordnen können.</p>
<h2>switch</h2>
<p>Die klassische Mehrfachverzweigung. Jeder Zweig braucht ein <code class="inline">break</code>, sonst läuft die Ausführung in den nächsten Fall durch:</p>
<pre><span class="k">switch</span> (<span class="v">$role</span>) {
<span class="k">case</span> <span class="s">"admin"</span>:
<span class="v">$rights</span> = <span class="s">"alle"</span>;
<span class="k">break</span>;
<span class="k">case</span> <span class="s">"redakteur"</span>:
<span class="v">$rights</span> = <span class="s">"schreiben"</span>;
<span class="k">break</span>;
<span class="k">default</span>:
<span class="v">$rights</span> = <span class="s">"lesen"</span>;
}</pre>
<p>Der Vergleich ist locker (<code class="inline">==</code>), nicht typsicher – genau deshalb ist <code class="inline">match</code> heute meist besser.</p>
<h2>Gewolltes Fall-Through</h2>
<p>Mehrere Fälle teilen sich einen Block, indem das <code class="inline">break</code> weggelassen wird:</p>
<pre><span class="k">switch</span> (<span class="v">$tag</span>) {
<span class="k">case</span> <span class="s">"Sa"</span>:
<span class="k">case</span> <span class="s">"So"</span>:
<span class="k">echo</span> <span class="s">"Wochenende"</span>;
<span class="k">break</span>;
<span class="k">default</span>:
<span class="k">echo</span> <span class="s">"Werktag"</span>;
}</pre>
<h2>Alternative Syntax für Templates</h2>
<p>In HTML-Vorlagen sind geschweifte Klammern unübersichtlich. PHP bietet eine Doppelpunkt-Form mit sprechendem Abschluss – ideal zwischen HTML:</p>
<pre><span class="t"><?php</span> <span class="k">if</span> (<span class="v">$loggedIn</span>): <span class="t">?></span>
<span class="t"><p></span>Willkommen zurück<span class="t"></p></span>
<span class="t"><?php</span> <span class="k">else</span>: <span class="t">?></span>
<span class="t"><a</span> <span class="a">href</span>=<span class="s">"/login"</span><span class="t">></span>Anmelden<span class="t"></a></span>
<span class="t"><?php</span> <span class="k">endif</span>; <span class="t">?></span></pre>
<p>Dieselbe Form gibt es als <code class="inline">endforeach</code>, <code class="inline">endfor</code>, <code class="inline">endwhile</code> und <code class="inline">endswitch</code>.</p>
<h2>goto</h2>
<p>PHP hat ein <code class="inline">goto</code>, das zu einer Marke springt. Es macht den Programmfluss schwer nachvollziehbar und hat in modernem Code praktisch keinen Platz – du wirst es höchstens in sehr altem Code antreffen:</p>
<pre><span class="k">goto</span> ende;
<span class="k">echo</span> <span class="s">"wird übersprungen"</span>;
ende:
<span class="k">echo</span> <span class="s">"hier geht es weiter"</span>;</pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>switch oder match?</b>Nutze <code class="inline">match</code>, wenn du einen Wert <i>zurückgibst</i> und typsicher vergleichen willst. <code class="inline">switch</code> bleibt brauchbar, wenn ein Fall mehrere Anweisungen ausführt oder du bewusst Fall-Through nutzt.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 7</span><h1>Funktionen</h1></div><p class="lead">Eine Funktion bündelt Code, den du benennen und wiederverwenden kannst. Sie nimmt Eingaben (Parameter) und liefert oft ein Ergebnis (Rückgabewert). PHP bietet dabei viel: Typen, Standardwerte, benannte und beliebig viele Argumente.</p>
<h2>Definition & Aufruf</h2>
<pre><span class="k">function</span> <span class="f">greet</span>(<span class="v">$name</span>) {
<span class="k">echo</span> <span class="s">"Hallo, $name!\n"</span>;
}
<span class="f">greet</span>(<span class="s">"Anna"</span>); <span class="c">// Hallo, Anna!</span></pre>
<h2>Rückgabewerte</h2>
<p><code class="inline">return</code> beendet die Funktion und liefert einen Wert zurück:</p>
<pre><span class="k">function</span> <span class="f">add</span>(<span class="v">$a</span>, <span class="v">$b</span>) {
<span class="k">return</span> <span class="v">$a</span> + <span class="v">$b</span>;
}
<span class="k">echo</span> <span class="f">add</span>(<span class="s">3</span>, <span class="s">5</span>); <span class="c">// 8</span></pre>
<h2>Typdeklarationen</h2>
<p>Parameter- und Rückgabetypen machen Absichten klar und fangen Fehler früh ab:</p>
<pre><span class="k">function</span> <span class="f">area</span>(<span class="t">float</span> <span class="v">$b</span>, <span class="t">float</span> <span class="v">$h</span>): <span class="t">float</span> {
<span class="k">return</span> <span class="v">$b</span> * <span class="v">$h</span>;
}
<span class="k">echo</span> <span class="f">area</span>(<span class="s">2.5</span>, <span class="s">4</span>); <span class="c">// 10</span></pre>
<h2>Standardwerte</h2>
<p>Parameter mit Vorgabe dürfen beim Aufruf weggelassen werden:</p>
<pre><span class="k">function</span> <span class="f">connect</span>(<span class="t">string</span> <span class="v">$host</span>, <span class="t">int</span> <span class="v">$port</span> = <span class="s">3306</span>): <span class="t">string</span> {
<span class="k">return</span> <span class="s">"$host:$port"</span>;
}
<span class="k">echo</span> <span class="f">connect</span>(<span class="s">"localhost"</span>); <span class="c">// localhost:3306</span>
<span class="k">echo</span> <span class="f">connect</span>(<span class="s">"localhost"</span>, <span class="s">5432</span>); <span class="c">// localhost:5432</span></pre>
<h2>Benannte Argumente</h2>
<p>Seit PHP 8 kannst du Argumente beim Namen nennen – die Reihenfolge ist dann egal, und optionale lassen sich gezielt setzen:</p>
<pre><span class="k">echo</span> <span class="f">connect</span>(<span class="v">port:</span> <span class="s">5432</span>, <span class="v">host:</span> <span class="s">"db"</span>); <span class="c">// db:5432</span></pre>
<h2>Beliebig viele Argumente</h2>
<p>Drei Punkte sammeln alle weiteren Argumente in ein Array:</p>
<pre><span class="k">function</span> <span class="f">sum</span>(<span class="t">int</span> ...<span class="v">$numbers</span>): <span class="t">int</span> {
<span class="k">return</span> <span class="k">array_sum</span>(<span class="v">$numbers</span>);
}
<span class="k">echo</span> <span class="f">sum</span>(<span class="s">1</span>, <span class="s">2</span>, <span class="s">3</span>, <span class="s">4</span>); <span class="c">// 10</span></pre>
<h2>Übergabe per Referenz</h2>
<p>Mit <code class="inline">&</code> arbeitet die Funktion am Original statt an einer Kopie:</p>
<pre><span class="k">function</span> <span class="f">increment</span>(<span class="t">int</span> &<span class="v">$n</span>): <span class="t">void</span> {
<span class="v">$n</span>++;
}
<span class="v">$x</span> = <span class="s">5</span>;
<span class="f">increment</span>(<span class="v">$x</span>);
<span class="k">echo</span> <span class="v">$x</span>; <span class="c">// 6 – $x selbst wurde verändert</span></pre>
<h2>Pfeilfunktionen</h2>
<p>Kurzschreibweise für kleine Funktionen; sie übernehmen den Geltungsbereich automatisch:</p>
<pre><span class="v">$double</span> = <span class="k">fn</span>(<span class="v">$n</span>) => <span class="v">$n</span> * <span class="s">2</span>;
<span class="k">echo</span> <span class="v">$double</span>(<span class="s">21</span>); <span class="c">// 42</span></pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Eine Funktion, eine Aufgabe</b>Eine gute Funktion tut genau eine Sache und hat einen Namen, der das verrät. Wird sie lang oder schwer benennbar, ist das ein Zeichen, sie aufzuteilen. Funktionen als Werte (Closures) und ihre Feinheiten vertieft ein späteres Kapitel.</div>
</div></section><section class="part-divider"><div class="part-kicker">Teil 2</div><h1>Daten verarbeiten: Strings, Arrays & Muster</h1><div class="part-desc">Die Werkzeuge, mit denen du täglich Daten formst: Texte, Listen und – für variable Muster – reguläre Ausdrücke von Grund auf.</div><div class="part-chapters"><span>8 · Strings im Detail</span><span>9 · Arrays</span><span>10 · Reguläre Ausdrücke: Grundlagen</span><span>11 · Reguläre Ausdrücke in der Praxis</span></div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 8</span><h1>Strings im Detail</h1></div><p class="lead">Text ist allgegenwärtig. PHP bietet dutzende String-Funktionen – hier die wichtigsten, jede einzeln erklärt und sofort gezeigt.</p>
<h2>Einfache vs. doppelte Anführungszeichen</h2>
<p>In doppelten Anführungszeichen werden Variablen eingesetzt (Interpolation), in einfachen nicht:</p>
<pre><span class="v">$name</span> = <span class="s">"Anna"</span>;
<span class="k">echo</span> <span class="s">"Hallo $name"</span>; <span class="c">// Hallo Anna</span>
<span class="k">echo</span> <span class="s">'Hallo $name'</span>; <span class="c">// Hallo $name</span>
<span class="k">echo</span> <span class="s">"Wert: {</span><span class="v">$arr</span><span class="s">['x']}"</span>; <span class="c">// {} grenzt komplexe Ausdrücke ab</span></pre>
<h2>Heredoc & Nowdoc</h2>
<p>Für lange Texte. Heredoc interpoliert wie doppelte Quotes, Nowdoc (mit <code class="inline">'…'</code>) wie einfache:</p>
<pre><span class="v">$html</span> = <span class="t"><<<HTML</span>
<span class="s"><h1>$name</h1></span>
<span class="t">HTML</span>;
<span class="v">$raw</span> = <span class="t"><<<'TXT'</span>
<span class="s">$name bleibt wörtlich.</span>
<span class="t">TXT</span>;</pre>
<h2>Länge</h2>
<p><code class="inline">strlen</code> zählt Bytes, <code class="inline">mb_strlen</code> zählt Zeichen – bei Umlauten ein Unterschied:</p>
<pre><span class="v">$w</span> = <span class="s">"Größe"</span>;
<span class="k">echo</span> <span class="k">strlen</span>(<span class="v">$w</span>); <span class="c">// 6 (ö belegt 2 Bytes)</span>
<span class="k">echo</span> <span class="k">mb_strlen</span>(<span class="v">$w</span>); <span class="c">// 5 (Zeichen)</span></pre>
<h2>Ausschnitt</h2>
<p><code class="inline">substr</code> schneidet einen Teil nach Byte-Position heraus, <code class="inline">mb_substr</code> arbeitet zeichenweise und damit umlautsicher:</p>
<pre><span class="k">echo</span> <span class="k">substr</span>(<span class="s">"Hallo Welt"</span>, <span class="s">0</span>, <span class="s">5</span>); <span class="c">// Hallo</span>
<span class="k">echo</span> <span class="k">substr</span>(<span class="s">"Hallo Welt"</span>, -<span class="s">4</span>); <span class="c">// Welt (von hinten)</span>
<span class="k">echo</span> <span class="k">mb_substr</span>(<span class="s">"Größe"</span>, <span class="s">0</span>, <span class="s">3</span>); <span class="c">// Grö (umlautsicher)</span></pre>
<h2>Suchen</h2>
<p>Die drei modernen Funktionen liefern direkt <code class="inline">bool</code>; <code class="inline">strpos</code> liefert die Position oder <code class="inline">false</code>:</p>
<pre><span class="v">$s</span> = <span class="s">"info@example.com"</span>;
<span class="k">var_dump</span>(<span class="k">str_contains</span>(<span class="v">$s</span>, <span class="s">"@"</span>)); <span class="c">// true</span>
<span class="k">var_dump</span>(<span class="k">str_starts_with</span>(<span class="v">$s</span>, <span class="s">"info"</span>)); <span class="c">// true</span>
<span class="k">var_dump</span>(<span class="k">str_ends_with</span>(<span class="v">$s</span>, <span class="s">".com"</span>)); <span class="c">// true</span>
<span class="k">echo</span> <span class="k">strpos</span>(<span class="v">$s</span>, <span class="s">"@"</span>); <span class="c">// 4</span></pre>
<h2>Ersetzen</h2>
<p><code class="inline">str_replace</code> tauscht alle Vorkommen eines Teilstrings aus, <code class="inline">substr_replace</code> ersetzt einen Bereich nach Position und Länge:</p>
<pre><span class="k">echo</span> <span class="k">str_replace</span>(<span class="s">"Welt"</span>, <span class="s">"PHP"</span>, <span class="s">"Hallo Welt"</span>); <span class="c">// Hallo PHP</span>
<span class="k">echo</span> <span class="k">str_replace</span>([<span class="s">"a"</span>, <span class="s">"e"</span>], [<span class="s">"4"</span>, <span class="s">"3"</span>], <span class="s">"hase"</span>); <span class="c">// h4s3 (mehrere)</span>
<span class="k">echo</span> <span class="k">substr_replace</span>(<span class="s">"2026-01-01"</span>, <span class="s">"12"</span>, <span class="s">5</span>, <span class="s">2</span>); <span class="c">// 2026-12-01</span></pre>
<h2>Groß-/Kleinschreibung</h2>
<p><code class="inline">strtoupper</code> und <code class="inline">strtolower</code> wandeln komplett um, <code class="inline">ucfirst</code> nur den ersten Buchstaben, <code class="inline">ucwords</code> jeden Wortanfang:</p>
<pre><span class="k">echo</span> <span class="k">strtoupper</span>(<span class="s">"php"</span>); <span class="c">// PHP</span>
<span class="k">echo</span> <span class="k">strtolower</span>(<span class="s">"PHP"</span>); <span class="c">// php</span>
<span class="k">echo</span> <span class="k">ucfirst</span>(<span class="s">"hallo"</span>); <span class="c">// Hallo</span>
<span class="k">echo</span> <span class="k">ucwords</span>(<span class="s">"max mustermann"</span>); <span class="c">// Max Mustermann</span>
<span class="k">echo</span> <span class="k">mb_strtoupper</span>(<span class="s">"größe"</span>); <span class="c">// GRÖSSE (umlautsicher)</span></pre>
<h2>Trimmen</h2>
<p>Entfernt Zeichen an den Rändern – <code class="inline">trim</code> auf beiden Seiten, <code class="inline">ltrim</code> und <code class="inline">rtrim</code> nur links bzw. rechts:</p>
<pre><span class="k">echo</span> <span class="s">"["</span> . <span class="k">trim</span>(<span class="s">" text "</span>) . <span class="s">"]"</span>; <span class="c">// [text]</span>
<span class="k">echo</span> <span class="k">ltrim</span>(<span class="s">" text"</span>); <span class="c">// "text" (nur links)</span>
<span class="k">echo</span> <span class="k">rtrim</span>(<span class="s">"datei.tmp"</span>, <span class="s">".tmp"</span>); <span class="c">// "datei" (diese Zeichen rechts)</span></pre>
<h2>Aufteilen & Zusammenfügen</h2>
<p><code class="inline">explode</code> zerlegt einen String an einem Trennzeichen in ein Array, <code class="inline">implode</code> fügt ein Array zu einem String zusammen, <code class="inline">str_split</code> zerlegt in einzelne Zeichen:</p>
<pre><span class="v">$parts</span> = <span class="k">explode</span>(<span class="s">","</span>, <span class="s">"php,sql,css"</span>); <span class="c">// ["php", "sql", "css"]</span>
<span class="k">echo</span> <span class="k">implode</span>(<span class="s">" / "</span>, <span class="v">$parts</span>); <span class="c">// php / sql / css</span>
<span class="k">print_r</span>(<span class="k">str_split</span>(<span class="s">"abc"</span>)); <span class="c">// ["a", "b", "c"]</span></pre>
<h2>Formatieren</h2>
<p><code class="inline">printf</code>/<code class="inline">sprintf</code> bauen Text nach einer Vorlage, <code class="inline">number_format</code> formatiert Zahlen, <code class="inline">str_pad</code> füllt auf eine Mindestlänge auf, <code class="inline">str_repeat</code> wiederholt:</p>
<pre><span class="k">printf</span>(<span class="s">"%s ist %d Jahre alt.\n"</span>, <span class="s">"Anna"</span>, <span class="s">30</span>);
<span class="k">echo</span> <span class="k">sprintf</span>(<span class="s">"%.2f €"</span>, <span class="s">3.5</span>); <span class="c">// 3.50 €</span>
<span class="k">echo</span> <span class="k">number_format</span>(<span class="s">1234567.89</span>, <span class="s">2</span>, <span class="s">","</span>, <span class="s">"."</span>); <span class="c">// 1.234.567,89</span>
<span class="k">echo</span> <span class="k">str_pad</span>(<span class="s">"7"</span>, <span class="s">3</span>, <span class="s">"0"</span>, <span class="t">STR_PAD_LEFT</span>); <span class="c">// 007</span>
<span class="k">echo</span> <span class="k">str_repeat</span>(<span class="s">"=-"</span>, <span class="s">3</span>); <span class="c">// =-=-=-</span></pre>
<h2>Vergleichen</h2>
<p><code class="inline">strcmp</code> vergleicht zwei Strings (Rückgabe negativ, 0 oder positiv), <code class="inline">strcasecmp</code> tut dasselbe ohne Rücksicht auf Groß-/Kleinschreibung:</p>
<pre><span class="k">var_dump</span>(<span class="s">"abc"</span> === <span class="s">"abc"</span>); <span class="c">// true (Wert + Typ)</span>
<span class="k">echo</span> <span class="k">strcmp</span>(<span class="s">"a"</span>, <span class="s">"b"</span>); <span class="c">// -1 (a kommt vor b)</span>
<span class="k">echo</span> <span class="k">strcasecmp</span>(<span class="s">"PHP"</span>, <span class="s">"php"</span>); <span class="c">// 0 (gleich, Groß/Klein egal)</span></pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>mb_-Funktionen bei Umlauten</b>Sobald Text Umlaute, Akzente oder Emojis enthalten kann, nutze die <code class="inline">mb_*</code>-Varianten. Die Byte-Funktionen zerschneiden sonst Mehrbyte-Zeichen. Setze früh <code class="inline">mb_internal_encoding("UTF-8")</code>.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 9</span><h1>Arrays</h1></div><p class="lead">Ein Array speichert mehrere Werte unter einem Namen – als Liste, Schlüssel-Wert-Sammlung oder verschachtelt. PHP hat eine der größten Funktionsbibliotheken dafür; hier jede gruppiert nach Zweck, einzeln gezeigt.</p>
<h2>Anlegen & Zugriff</h2>
<pre><span class="v">$colors</span> = [<span class="s">"rot"</span>, <span class="s">"grün"</span>, <span class="s">"blau"</span>];
<span class="k">echo</span> <span class="v">$colors</span>[<span class="s">0</span>]; <span class="c">// rot</span>
<span class="v">$person</span> = [<span class="s">"name"</span> => <span class="s">"Anna"</span>, <span class="s">"alter"</span> => <span class="s">30</span>];
<span class="k">echo</span> <span class="v">$person</span>[<span class="s">"name"</span>]; <span class="c">// Anna</span></pre>
<h2>Hinzufügen</h2>
<p>Elemente anfügen – <code class="inline">[]</code> hängt am Ende an, <code class="inline">array_push</code> auch mehrere auf einmal, <code class="inline">array_unshift</code> am Anfang:</p>
<pre><span class="v">$l</span> = [<span class="s">"a"</span>, <span class="s">"b"</span>];
<span class="v">$l</span>[] = <span class="s">"c"</span>; <span class="c">// ["a","b","c"] – am Ende</span>
<span class="k">array_push</span>(<span class="v">$l</span>, <span class="s">"d"</span>, <span class="s">"e"</span>); <span class="c">// mehrere am Ende</span>
<span class="k">array_unshift</span>(<span class="v">$l</span>, <span class="s">"x"</span>); <span class="c">// am Anfang</span></pre>
<h2>Entfernen</h2>
<p>Elemente herausnehmen – <code class="inline">array_pop</code> das letzte, <code class="inline">array_shift</code> das erste (beide liefern es zurück), <code class="inline">unset</code> löscht einen bestimmten Index:</p>
<pre><span class="v">$last</span> = <span class="k">array_pop</span>(<span class="v">$l</span>); <span class="c">// entfernt & liefert das letzte</span>
<span class="v">$first</span> = <span class="k">array_shift</span>(<span class="v">$l</span>); <span class="c">// entfernt & liefert das erste</span>
<span class="k">unset</span>(<span class="v">$l</span>[<span class="s">1</span>]); <span class="c">// löscht Index 1</span></pre>
<h2>Suchen</h2>
<p>Prüfen, ob und wo ein Wert vorkommt – <code class="inline">in_array</code> liefert einen Wahrheitswert, <code class="inline">array_search</code> den Schlüssel, <code class="inline">array_key_exists</code> prüft auf einen Schlüssel:</p>
<pre><span class="v">$fruit</span> = [<span class="s">"apfel"</span>, <span class="s">"birne"</span>];
<span class="k">var_dump</span>(<span class="k">in_array</span>(<span class="s">"birne"</span>, <span class="v">$fruit</span>, <span class="k">true</span>)); <span class="c">// true (strikt)</span>
<span class="k">echo</span> <span class="k">array_search</span>(<span class="s">"birne"</span>, <span class="v">$fruit</span>); <span class="c">// 1 (Schlüssel)</span>
<span class="k">var_dump</span>(<span class="k">array_key_exists</span>(<span class="s">0</span>, <span class="v">$fruit</span>)); <span class="c">// true</span></pre>
<h2>Schlüssel & Werte</h2>
<p>Schlüssel und Werte auslesen und umformen:</p>
<pre><span class="v">$p</span> = [<span class="s">"apfel"</span> => <span class="s">2</span>, <span class="s">"birne"</span> => <span class="s">3</span>];
<span class="k">print_r</span>(<span class="k">array_keys</span>(<span class="v">$p</span>)); <span class="c">// ["apfel", "birne"]</span>
<span class="k">print_r</span>(<span class="k">array_values</span>(<span class="v">$p</span>)); <span class="c">// [2, 3]</span>
<span class="k">print_r</span>(<span class="k">array_flip</span>(<span class="v">$p</span>)); <span class="c">// [2 => "apfel", 3 => "birne"]</span>
<span class="k">print_r</span>(<span class="k">array_unique</span>([<span class="s">1</span>, <span class="s">1</span>, <span class="s">2</span>])); <span class="c">// [1, 2]</span>
<span class="k">print_r</span>(<span class="k">array_combine</span>([<span class="s">"a"</span>, <span class="s">"b"</span>], [<span class="s">1</span>, <span class="s">2</span>])); <span class="c">// ["a"=>1, "b"=>2]</span>
<span class="v">$team</span> = [[<span class="s">"name"</span> => <span class="s">"Anna"</span>], [<span class="s">"name"</span> => <span class="s">"Ben"</span>]];
<span class="k">print_r</span>(<span class="k">array_column</span>(<span class="v">$team</span>, <span class="s">"name"</span>)); <span class="c">// ["Anna", "Ben"]</span></pre>
<h2>Transformieren</h2>
<p>Aus einem Array ein neues bilden – <code class="inline">array_map</code> wandelt jedes Element um, <code class="inline">array_filter</code> siebt aus, <code class="inline">array_reduce</code> fasst zu einem Wert zusammen, <code class="inline">array_walk</code> verändert direkt:</p>
<pre><span class="v">$z</span> = [<span class="s">1</span>, <span class="s">2</span>, <span class="s">3</span>, <span class="s">4</span>];
<span class="k">print_r</span>(<span class="k">array_map</span>(<span class="k">fn</span>(<span class="v">$n</span>) => <span class="v">$n</span> ** <span class="s">2</span>, <span class="v">$z</span>)); <span class="c">// [1, 4, 9, 16]</span>
<span class="k">print_r</span>(<span class="k">array_filter</span>(<span class="v">$z</span>, <span class="k">fn</span>(<span class="v">$n</span>) => <span class="v">$n</span> % <span class="s">2</span> === <span class="s">0</span>)); <span class="c">// [1=>2, 3=>4]</span>
<span class="k">echo</span> <span class="k">array_reduce</span>(<span class="v">$z</span>, <span class="k">fn</span>(<span class="v">$t</span>, <span class="v">$n</span>) => <span class="v">$t</span> + <span class="v">$n</span>, <span class="s">0</span>); <span class="c">// 10</span>
<span class="k">array_walk</span>(<span class="v">$z</span>, <span class="k">function</span>(&<span class="v">$n</span>) { <span class="v">$n</span> *= <span class="s">10</span>; }); <span class="c">// verändert direkt</span></pre>
<p>Ohne Callback entfernt <code class="inline">array_filter</code> alle „falsy" Werte (0, "", null, false). <code class="inline">array_filter</code> behält die Schlüssel – mit <code class="inline">array_values</code> nummerierst du neu durch.</p>
<h2>Sortieren</h2>
<p>Alle Sortierfunktionen verändern das Array <b>an Ort und Stelle</b>. <code class="inline">sort</code>/<code class="inline">rsort</code> sortieren nach Wert auf- bzw. absteigend und vergeben neue Schlüssel:</p>
<pre><span class="v">$z</span> = [<span class="s">3</span>, <span class="s">1</span>, <span class="s">2</span>];
<span class="k">sort</span>(<span class="v">$z</span>); <span class="c">// [1, 2, 3]</span>
<span class="k">rsort</span>(<span class="v">$z</span>); <span class="c">// [3, 2, 1]</span></pre>
<p><code class="inline">asort</code>/<code class="inline">arsort</code> sortieren nach Wert, aber <i>erhalten</i> die Schlüssel; <code class="inline">ksort</code>/<code class="inline">krsort</code> sortieren nach Schlüssel:</p>
<pre><span class="v">$m</span> = [<span class="s">"b"</span> => <span class="s">2</span>, <span class="s">"a"</span> => <span class="s">1</span>];
<span class="k">asort</span>(<span class="v">$m</span>); <span class="c">// a=>1, b=>2 (nach Wert, Schlüssel bleiben)</span>
<span class="k">ksort</span>(<span class="v">$m</span>); <span class="c">// a, b (nach Schlüssel)</span>
<span class="k">krsort</span>(<span class="v">$m</span>); <span class="c">// b, a (Schlüssel absteigend)</span></pre>
<p>Mit einer eigenen Vergleichsfunktion sortiert <code class="inline">usort</code> (neue Schlüssel), <code class="inline">uasort</code> (Schlüssel bleiben) bzw. <code class="inline">uksort</code> (auf Schlüssel). <code class="inline">natsort</code> sortiert „natürlich":</p>
<pre><span class="v">$people</span> = [[<span class="s">"alter"</span> => <span class="s">40</span>], [<span class="s">"alter"</span> => <span class="s">30</span>]];
<span class="k">usort</span>(<span class="v">$people</span>, <span class="k">fn</span>(<span class="v">$a</span>, <span class="v">$b</span>) => <span class="v">$a</span>[<span class="s">"alter"</span>] <=> <span class="v">$b</span>[<span class="s">"alter"</span>]); <span class="c">// 30 vor 40</span>
<span class="v">$d</span> = [<span class="s">"datei10"</span>, <span class="s">"datei2"</span>];
<span class="k">natsort</span>(<span class="v">$d</span>); <span class="c">// datei2 vor datei10</span></pre>
<h2>Kombinieren & Aufteilen</h2>
<p>Mehrere Arrays zusammenführen oder eines zerteilen:</p>
<pre><span class="k">print_r</span>(<span class="k">array_merge</span>([<span class="s">1</span>, <span class="s">2</span>], [<span class="s">3</span>, <span class="s">4</span>])); <span class="c">// [1, 2, 3, 4]</span>
<span class="k">print_r</span>(<span class="k">array_slice</span>([<span class="s">"a"</span>, <span class="s">"b"</span>, <span class="s">"c"</span>], <span class="s">1</span>, <span class="s">2</span>)); <span class="c">// ["b", "c"]</span>
<span class="v">$x</span> = [<span class="s">"a"</span>, <span class="s">"b"</span>, <span class="s">"c"</span>];
<span class="k">array_splice</span>(<span class="v">$x</span>, <span class="s">1</span>, <span class="s">1</span>, [<span class="s">"B1"</span>, <span class="s">"B2"</span>]); <span class="c">// ["a","B1","B2","c"]</span>
<span class="k">print_r</span>(<span class="k">array_chunk</span>([<span class="s">1</span>, <span class="s">2</span>, <span class="s">3</span>], <span class="s">2</span>)); <span class="c">// [[1,2], [3]]</span>
<span class="k">print_r</span>(<span class="k">array_diff</span>([<span class="s">1</span>, <span class="s">2</span>, <span class="s">3</span>], [<span class="s">2</span>])); <span class="c">// [0=>1, 2=>3]</span>
<span class="k">print_r</span>(<span class="k">array_intersect</span>([<span class="s">1</span>, <span class="s">2</span>], [<span class="s">2</span>, <span class="s">9</span>])); <span class="c">// [1=>2]</span></pre>
<h2>Aggregieren</h2>
<p>Die Werte eines Arrays zu einer Kennzahl zusammenfassen:</p>
<pre><span class="v">$w</span> = [<span class="s">10</span>, <span class="s">3</span>, <span class="s">7</span>];
<span class="k">echo</span> <span class="k">count</span>(<span class="v">$w</span>); <span class="c">// 3</span>
<span class="k">echo</span> <span class="k">array_sum</span>(<span class="v">$w</span>); <span class="c">// 20</span>
<span class="k">echo</span> <span class="k">array_product</span>(<span class="v">$w</span>); <span class="c">// 210</span>
<span class="k">echo</span> <span class="k">max</span>(<span class="v">$w</span>); <span class="c">// 10</span>
<span class="k">echo</span> <span class="k">min</span>(<span class="v">$w</span>); <span class="c">// 3</span></pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>map/filter/reduce statt Schleifen</b>Für Transformationen sind diese drei oft klarer als eine <code class="inline">foreach</code>-Schleife – sie sagen direkt, was passiert (umwandeln, aussieben, zusammenfassen). Sie lassen sich verketten und mit dem Pipe-Operator von PHP 8.5 von links nach rechts lesbar machen.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 10</span><h1>Reguläre Ausdrücke: Grundlagen</h1></div><p class="lead">Reguläre Ausdrücke (Regex) beschreiben <i>Muster</i> in Text – etwa „eine Folge von Ziffern" oder „etwas, das wie eine E-Mail aussieht". Wo feste Teilstrings nicht reichen, sind sie das Werkzeug. PHP nutzt die PCRE-Syntax.</p>
<h2>Aufbau eines Musters</h2>
<p>Ein Muster steht zwischen Trennzeichen (meist <code class="inline">/</code>). <code class="inline">preg_match</code> liefert 1 bei Treffer:</p>
<pre><span class="k">var_dump</span>(<span class="k">preg_match</span>(<span class="s">"/katze/"</span>, <span class="s">"meine katze"</span>)); <span class="c">// 1 (Treffer)</span>
<span class="k">var_dump</span>(<span class="k">preg_match</span>(<span class="s">"/hund/"</span>, <span class="s">"meine katze"</span>)); <span class="c">// 0</span></pre>
<h2>Zeichenklassen</h2>
<p><code class="inline">\d</code> Ziffer, <code class="inline">\w</code> Wortzeichen, <code class="inline">\s</code> Leerraum, <code class="inline">.</code> beliebiges Zeichen. In eckigen Klammern eine eigene Auswahl:</p>
<pre><span class="k">var_dump</span>(<span class="k">preg_match</span>(<span class="s">"/\d/"</span>, <span class="s">"abc7"</span>)); <span class="c">// 1 (enthält Ziffer)</span>
<span class="k">var_dump</span>(<span class="k">preg_match</span>(<span class="s">"/[aeiou]/"</span>, <span class="s">"xyz"</span>)); <span class="c">// 0 (kein Vokal)</span>
<span class="k">var_dump</span>(<span class="k">preg_match</span>(<span class="s">"/[^0-9]/"</span>, <span class="s">"123"</span>)); <span class="c">// 0 (^ negiert: kein Nicht-Ziffer)</span></pre>
<h2>Quantoren</h2>
<p><code class="inline">*</code> = null oder mehr, <code class="inline">+</code> = eins oder mehr, <code class="inline">?</code> = optional, <code class="inline">{n,m}</code> = Bereich:</p>
<pre><span class="k">var_dump</span>(<span class="k">preg_match</span>(<span class="s">"/^\d+$/"</span>, <span class="s">"12345"</span>)); <span class="c">// 1 (nur Ziffern)</span>
<span class="k">var_dump</span>(<span class="k">preg_match</span>(<span class="s">"/^a\d{3}$/"</span>, <span class="s">"a042"</span>)); <span class="c">// 1 (a + genau 3 Ziffern)</span>
<span class="k">var_dump</span>(<span class="k">preg_match</span>(<span class="s">"/colou?r/"</span>, <span class="s">"color"</span>)); <span class="c">// 1 (u optional)</span></pre>
<h2>Anker</h2>
<p><code class="inline">^</code> bindet an den Anfang, <code class="inline">$</code> ans Ende – wichtig, um den <i>ganzen</i> String zu prüfen statt nur eines Teils:</p>
<pre><span class="k">var_dump</span>(<span class="k">preg_match</span>(<span class="s">"/^\d+$/"</span>, <span class="s">"12a"</span>)); <span class="c">// 0 (a am Ende)</span>
<span class="k">var_dump</span>(<span class="k">preg_match</span>(<span class="s">"/\d+/"</span>, <span class="s">"12a"</span>)); <span class="c">// 1 (Teil reicht ohne Anker)</span></pre>
<h2>Modifikatoren</h2>
<p>Nach dem schließenden Trennzeichen: <code class="inline">i</code> ignoriert Groß/Klein, <code class="inline">m</code> mehrzeilig, <code class="inline">u</code> für UTF-8:</p>
<pre><span class="k">var_dump</span>(<span class="k">preg_match</span>(<span class="s">"/php/i"</span>, <span class="s">"Ich liebe PHP"</span>)); <span class="c">// 1 (i: Groß/Klein egal)</span></pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Sonderzeichen maskieren</b>Zeichen wie <code class="inline">. + * ? ( ) [ ]</code> haben in Regex eine Bedeutung. Willst du sie wörtlich suchen, stelle einen Backslash voran (<code class="inline">\.</code>) oder nutze <code class="inline">preg_quote()</code>. Für reine Festtext-Suche bleibt <code class="inline">str_contains</code> einfacher und schneller.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 11</span><h1>Reguläre Ausdrücke in der Praxis</h1></div><p class="lead">Mit den Grundlagen im Rücken geht es ans Anwenden: Treffer auslesen, ersetzen, aufteilen – und die typischen Fallstricke vermeiden.</p>
<h2>preg_match mit Gruppen</h2>
<p>Klammern bilden Gruppen, deren Inhalt im Treffer-Array landet – so liest du Teile aus:</p>
<pre><span class="k">preg_match</span>(<span class="s">"/(\d{4})-(\d{2})-(\d{2})/"</span>, <span class="s">"Datum: 2026-05-31"</span>, <span class="v">$m</span>);
<span class="k">echo</span> <span class="v">$m</span>[<span class="s">0</span>]; <span class="c">// 2026-05-31 (ganzer Treffer)</span>
<span class="k">echo</span> <span class="v">$m</span>[<span class="s">1</span>]; <span class="c">// 2026 (erste Gruppe)</span>
<span class="k">echo</span> <span class="v">$m</span>[<span class="s">2</span>]; <span class="c">// 05</span></pre>
<h2>Benannte Gruppen</h2>
<p><code class="inline">(?<name>…)</code> macht den Code lesbarer als Zahlenindizes:</p>
<pre><span class="k">preg_match</span>(<span class="s">"/(?<jahr>\d{4})-(?<monat>\d{2})/"</span>, <span class="s">"2026-05"</span>, <span class="v">$m</span>);
<span class="k">echo</span> <span class="v">$m</span>[<span class="s">"jahr"</span>]; <span class="c">// 2026</span>
<span class="k">echo</span> <span class="v">$m</span>[<span class="s">"monat"</span>]; <span class="c">// 05</span></pre>
<h2>preg_match_all</h2>
<p>Findet nicht nur den ersten, sondern alle Treffer und sammelt sie:</p>
<pre><span class="k">preg_match_all</span>(<span class="s">"/\d+/"</span>, <span class="s">"a1 b22 c333"</span>, <span class="v">$m</span>);
<span class="k">print_r</span>(<span class="v">$m</span>[<span class="s">0</span>]); <span class="c">// ["1", "22", "333"]</span></pre>
<h2>preg_replace</h2>
<p>Mit <code class="inline">$1</code> greifst du im Ersatz auf Gruppen zurück:</p>
<pre><span class="k">echo</span> <span class="k">preg_replace</span>(<span class="s">"/\s+/"</span>, <span class="s">" "</span>, <span class="s">"zu viel Raum"</span>); <span class="c">// "zu viel Raum"</span>
<span class="k">echo</span> <span class="k">preg_replace</span>(<span class="s">"/(\d{4})-(\d{2})/"</span>, <span class="s">"$2/$1"</span>, <span class="s">"2026-05"</span>); <span class="c">// "05/2026"</span></pre>
<h2>preg_replace_callback</h2>
<p>Ersetzt Treffer durch das Ergebnis einer Funktion – so kann die Ersetzung von jedem Treffer abhängen:</p>
<pre><span class="k">echo</span> <span class="k">preg_replace_callback</span>(<span class="s">"/\d+/"</span>,
<span class="k">fn</span>(<span class="v">$t</span>) => <span class="v">$t</span>[<span class="s">0</span>] * <span class="s">2</span>, <span class="s">"a3 b10"</span>); <span class="c">// "a6 b20"</span></pre>
<h2>preg_split</h2>
<p>Zerlegt einen String dort, wo ein Muster passt:</p>
<pre><span class="k">print_r</span>(<span class="k">preg_split</span>(<span class="s">"/[\s,]+/"</span>, <span class="s">"a, b, c"</span>)); <span class="c">// ["a", "b", "c"]</span></pre>
<h2>Lookarounds</h2>
<p>Ein Lookahead <code class="inline">(?=…)</code> prüft, was folgt, ohne es zu verbrauchen – hier: nur Zahlen vor „€":</p>
<pre><span class="k">preg_match_all</span>(<span class="s">"/\d+(?= €)/"</span>, <span class="s">"5 € und 10 Stück"</span>, <span class="v">$m</span>);
<span class="k">print_r</span>(<span class="v">$m</span>[<span class="s">0</span>]); <span class="c">// ["5"] (10 wird nicht von € gefolgt)</span></pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Gier vermeiden</b>Quantoren sind „gierig": <code class="inline">.*</code> greift so viel wie möglich. In <code class="inline">"<a><b>"</code> trifft <code class="inline">/<.*>/</code> den ganzen String. Mit <code class="inline">?</code> machst du sie genügsam: <code class="inline">/<.*?>/</code> trifft nur <code class="inline">"<a>"</code>. Und: E-Mail-Adressen oder HTML mit Regex vollständig zu validieren ist ein Fass ohne Boden – nutze dafür <code class="inline">filter_var</code> bzw. einen echten Parser.</div>
</div></section><section class="part-divider"><div class="part-kicker">Teil 3</div><h1>Code strukturieren</h1><div class="part-desc">Sobald Programme wachsen, müssen sie sich aufteilen lassen: Dateien einbinden, Namensräume ordnen, Pakete verwalten und Fehler kontrolliert behandeln.</div><div class="part-chapters"><span>12 · Code aufteilen: include & require</span><span>13 · Namespaces</span><span>14 · Composer & Autoloading</span><span>15 · Fehler & Exceptions</span></div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 12</span><h1>Code aufteilen: include & require</h1></div><p class="lead">Sobald Programme wachsen, teilst du sie auf mehrere Dateien auf. <code class="inline">include</code> und <code class="inline">require</code> binden eine andere PHP-Datei an Ort und Stelle ein.</p>
<h2>include vs. require</h2>
<p>Beide führen die eingebundene Datei aus. Der Unterschied liegt im Fehlerfall: <code class="inline">include</code> warnt nur und läuft weiter, <code class="inline">require</code> bricht hart ab:</p>
<pre><span class="k">include</span> <span class="s">"optional.php"</span>; <span class="c">// fehlt sie: Warnung, Programm läuft weiter</span>
<span class="k">require</span> <span class="s">"datenbank.php"</span>; <span class="c">// fehlt sie: fataler Fehler, Stopp</span></pre>
<p>Faustregel: <code class="inline">require</code> für alles, ohne das es nicht weitergeht.</p>
<h2>Nur einmal einbinden</h2>
<p>Die <code class="inline">_once</code>-Varianten binden eine Datei höchstens einmal ein – das verhindert doppelte Funktions- oder Klassendefinitionen:</p>
<pre><span class="k">require_once</span> <span class="s">"helfer.php"</span>;
<span class="k">require_once</span> <span class="s">"helfer.php"</span>; <span class="c">// tut nichts mehr – keine Doppeldefinition</span></pre>
<h2>Werte zurückgeben</h2>
<p>Eine eingebundene Datei kann mit <code class="inline">return</code> einen Wert liefern – ein verbreitetes Muster für Konfigurationsdateien:</p>
<pre><span class="c">// config.php</span>
<span class="k">return</span> [<span class="s">"host"</span> => <span class="s">"localhost"</span>, <span class="s">"port"</span> => <span class="s">3306</span>];
<span class="c">// woanders:</span>
<span class="v">$config</span> = <span class="k">require</span> <span class="s">"config.php"</span>;
<span class="k">echo</span> <span class="v">$config</span>[<span class="s">"host"</span>]; <span class="c">// localhost</span></pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Im Alltag selten von Hand</b>In modernen Projekten bindest du Klassen kaum noch selbst ein – das übernimmt der Autoloader von Composer (gleich im Composer-Kapitel). <code class="inline">require</code> brauchst du dann fast nur noch einmal für Composers <code class="inline">vendor/autoload.php</code> und für Konfigurationsdateien.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 13</span><h1>Namespaces</h1></div><p class="lead">Namespaces verhindern Namenskollisionen: Zwei Bibliotheken dürfen beide eine Klasse <code class="inline">Logger</code> haben, solange sie in verschiedenen Namensräumen liegen. Sie sind außerdem die Grundlage des Autoloadings.</p>
<h2>Definieren</h2>
<p><code class="inline">namespace</code> steht ganz oben in der Datei und ordnet alles darin ein:</p>
<pre><span class="k">namespace</span> <span class="t">App\Service</span>;
<span class="k">class</span> <span class="t">Logger</span> {
<span class="k">public function</span> <span class="f">write</span>(<span class="t">string</span> <span class="v">$text</span>): <span class="t">void</span> {
<span class="k">echo</span> <span class="v">$text</span>;
}
}</pre>
<h2>Verwenden</h2>
<p>Der voll qualifizierte Name enthält den Namespace. Mit <code class="inline">use</code> importierst du ihn und sparst dir die Wiederholung:</p>
<pre><span class="k">use</span> <span class="t">App\Service\Logger</span>;
<span class="v">$log</span> = <span class="k">new</span> <span class="t">Logger</span>(); <span class="c">// dank use kurz</span>
<span class="v">$log</span>-><span class="f">write</span>(<span class="s">"Start"</span>);</pre>
<h2>Aliasnamen</h2>
<p>Kollidieren zwei importierte Namen, benennst du einen per <code class="inline">as</code> um:</p>
<pre><span class="k">use</span> <span class="t">App\Service\Logger</span> <span class="k">as</span> <span class="t">AppLogger</span>;
<span class="k">use</span> <span class="t">Monolog\Logger</span> <span class="k">as</span> <span class="t">MonoLogger</span>;</pre>
<h2>Globale Funktionen & der führende Backslash</h2>
<p>Innerhalb eines Namespaces verweist ein führender <code class="inline">\</code> auf den globalen Raum – nützlich, um eingebaute Funktionen oder Klassen eindeutig zu erreichen:</p>
<pre><span class="k">namespace</span> <span class="t">App</span>;
<span class="v">$now</span> = <span class="k">new</span> <span class="t">\DateTime</span>(); <span class="c">// die globale DateTime-Klasse</span>
<span class="k">echo</span> <span class="t">\strlen</span>(<span class="s">"abc"</span>); <span class="c">// die globale Funktion (oft auch ohne \)</span></pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Ein Namespace pro Verzeichnis</b>Der PSR-4-Standard (im Ökosystem-Teil) bildet Namespaces auf Ordner ab: <code class="inline">App\Service\Logger</code> liegt in <code class="inline">src/Service/Logger.php</code>. Hältst du dich daran, findet Composers Autoloader jede Klasse automatisch – ohne ein einziges <code class="inline">require</code>.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 14</span><h1>Composer & Autoloading</h1></div><p class="lead">Composer ist der Paketmanager von PHP. Er installiert Bibliotheken, verwaltet ihre Versionen und – fast noch wichtiger – lädt deine Klassen automatisch nach, sobald du sie benutzt.</p>
<h2>Ein Projekt starten</h2>
<p><code class="inline">composer.json</code> beschreibt das Projekt und seine Abhängigkeiten. <code class="inline">composer require</code> fügt ein Paket hinzu:</p>
<pre><span class="c"># neues Paket installieren (landet in vendor/)</span>
composer require monolog/monolog
<span class="c"># alle Abhängigkeiten aus composer.json installieren</span>
composer install</pre>
<h2>Der Autoloader</h2>
<p>Eine einzige Zeile bindet Composers Autoloader ein. Danach werden alle Klassen aus deinem Code <i>und</i> aus den Paketen bei Bedarf automatisch geladen:</p>
<pre><span class="k">require</span> <span class="s">"vendor/autoload.php"</span>;
<span class="k">use</span> <span class="t">Monolog\Logger</span>;
<span class="v">$log</span> = <span class="k">new</span> <span class="t">Logger</span>(<span class="s">"app"</span>); <span class="c">// kein require für die Klasse nötig</span></pre>
<h2>Eigene Klassen autoladen (PSR-4)</h2>
<p>Im <code class="inline">autoload</code>-Abschnitt der <code class="inline">composer.json</code> bildest du einen Namespace auf einen Ordner ab:</p>
<pre><span class="c">// composer.json</span>
{
<span class="s">"autoload"</span>: {
<span class="s">"psr-4"</span>: { <span class="s">"App\\"</span>: <span class="s">"src/"</span> }
}
}</pre>
<p>Nach <code class="inline">composer dump-autoload</code> findet PHP <code class="inline">App\Service\Logger</code> automatisch in <code class="inline">src/Service/Logger.php</code>.</p>
<h2>Versionsbereiche</h2>
<p>Das <code class="inline">^</code> erlaubt verträgliche Updates (gleiche Hauptversion):</p>
<pre><span class="s">"require"</span>: {
<span class="s">"monolog/monolog"</span>: <span class="s">"^3.0"</span> <span class="c">// 3.x, aber nicht 4.0</span>
}</pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>composer.lock einchecken</b>Die <code class="inline">composer.lock</code> hält die <i>exakt</i> installierten Versionen fest. Sie gehört ins Repository, damit alle – und der Server – identische Stände bekommen. <code class="inline">composer install</code> folgt der Lock-Datei, <code class="inline">composer update</code> aktualisiert sie.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 15</span><h1>Fehler & Exceptions</h1></div><p class="lead">Wenn etwas schiefgeht, wirft PHP eine <i>Exception</i> – ein Objekt, das den Fehler beschreibt. Du fängst sie gezielt ab und reagierst, statt das Programm abstürzen zu lassen.</p>
<h2>try / catch</h2>
<p>Code, der scheitern kann, kommt in <code class="inline">try</code>; der <code class="inline">catch</code>-Block fängt die Exception:</p>
<pre><span class="k">try</span> {
<span class="v">$result</span> = <span class="s">10</span> / <span class="v">$divisor</span>;
<span class="k">if</span> (<span class="v">$divisor</span> === <span class="s">0</span>) {
<span class="k">throw</span> <span class="k">new</span> <span class="t">InvalidArgumentException</span>(<span class="s">"Teiler ist 0"</span>);
}
} <span class="k">catch</span> (<span class="t">InvalidArgumentException</span> <span class="v">$e</span>) {
<span class="k">echo</span> <span class="s">"Fehler: "</span> . <span class="v">$e</span>-><span class="f">getMessage</span>(); <span class="c">// Fehler: Teiler ist 0</span>
}</pre>
<h2>finally</h2>
<p>Der <code class="inline">finally</code>-Block läuft immer – ob Fehler oder nicht. Ideal zum Aufräumen:</p>
<pre><span class="k">try</span> {
<span class="v">$file</span> = <span class="k">fopen</span>(<span class="s">"daten.txt"</span>, <span class="s">"r"</span>);
<span class="c">// ... verarbeiten ...</span>
} <span class="k">finally</span> {
<span class="k">if</span> (<span class="k">isset</span>(<span class="v">$file</span>)) <span class="k">fclose</span>(<span class="v">$file</span>); <span class="c">// läuft in jedem Fall</span>
}</pre>
<h2>Eigene Exceptions</h2>
<p>Eigene Klassen, die von <code class="inline">Exception</code> erben, machen Fehler im Code unterscheidbar:</p>
<pre><span class="k">class</span> <span class="t">PaymentFailed</span> <span class="k">extends</span> <span class="t">Exception</span> {}
<span class="k">function</span> <span class="f">charge</span>(<span class="t">int</span> <span class="v">$cent</span>): <span class="t">void</span> {
<span class="k">if</span> (<span class="v">$cent</span> <= <span class="s">0</span>) {
<span class="k">throw</span> <span class="k">new</span> <span class="t">PaymentFailed</span>(<span class="s">"Betrag ungültig"</span>);
}
}
<span class="k">try</span> { <span class="f">charge</span>(-<span class="s">5</span>); }
<span class="k">catch</span> (<span class="t">PaymentFailed</span> <span class="v">$e</span>) { <span class="k">echo</span> <span class="v">$e</span>-><span class="f">getMessage</span>(); } <span class="c">// Betrag ungültig</span></pre>
<h2>Mehrere Typen fangen</h2>
<p>Ein <code class="inline">catch</code> kann mehrere Exception-Typen behandeln; die Reihenfolge geht von speziell zu allgemein:</p>
<pre><span class="k">try</span> {
<span class="c">// ...</span>
} <span class="k">catch</span> (<span class="t">TypeError</span> | <span class="t">ValueError</span> <span class="v">$e</span>) {
<span class="k">echo</span> <span class="s">"Eingabeproblem"</span>;
} <span class="k">catch</span> (<span class="t">Throwable</span> <span class="v">$e</span>) {
<span class="k">echo</span> <span class="s">"Irgendetwas anderes"</span>; <span class="c">// Throwable fängt wirklich alles</span>
}</pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Nicht stillschweigend schlucken</b>Ein leerer <code class="inline">catch</code>-Block versteckt Fehler und macht die Suche zur Qual. Fange nur, was du wirklich behandeln kannst; alles andere lass nach oben durchreichen, wo eine zentrale Stelle es protokolliert. <code class="inline">Error</code> (z. B. <code class="inline">TypeError</code>) und <code class="inline">Exception</code> haben mit <code class="inline">Throwable</code> eine gemeinsame Basis.</div>
</div></section><section class="part-divider"><div class="part-kicker">Teil 4</div><h1>Das Typsystem & moderne Sprache</h1><div class="part-desc">Was modernes PHP ausmacht: ein ausdrucksstarkes Typsystem, Enums und die Syntax-Neuerungen von PHP 8.</div><div class="part-chapters"><span>16 · Typen & strict_types</span><span>17 · Union-, Nullable- & spezielle Typen</span><span>18 · Enums</span><span>19 · Moderne Syntax</span></div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 16</span><h1>Typen & strict_types</h1></div><p class="lead">PHP erlaubt Typdeklarationen für Parameter, Rückgaben und Eigenschaften. Sie machen Code selbsterklärend und fangen Fehler früh – besonders mit aktiviertem strikten Modus.</p>
<h2>Skalare Typen deklarieren</h2>
<pre><span class="k">function</span> <span class="f">repeat</span>(<span class="t">string</span> <span class="v">$text</span>, <span class="t">int</span> <span class="v">$times</span>): <span class="t">string</span> {
<span class="k">return</span> <span class="k">str_repeat</span>(<span class="v">$text</span>, <span class="v">$times</span>);
}
<span class="k">echo</span> <span class="f">repeat</span>(<span class="s">"ab"</span>, <span class="s">3</span>); <span class="c">// ababab</span></pre>
<h2>strict_types</h2>
<p>Ohne strikten Modus wandelt PHP <code class="inline">"3"</code> stillschweigend in <code class="inline">3</code> um. Die Zeile <code class="inline">declare(strict_types=1)</code> ganz oben in der Datei erzwingt exakte Typen:</p>
<pre><span class="k">declare</span>(<span class="v">strict_types</span>=<span class="s">1</span>);
<span class="f">repeat</span>(<span class="s">"ab"</span>, <span class="s">"3"</span>); <span class="c">// TypeError! "3" ist kein int</span>
<span class="f">repeat</span>(<span class="s">"ab"</span>, <span class="s">3</span>); <span class="c">// ok</span></pre>
<h2>Eigenschafts-Typen</h2>
<pre><span class="k">class</span> <span class="t">Account</span> {
<span class="k">public</span> <span class="t">int</span> <span class="v">$balance</span> = <span class="s">0</span>;
<span class="k">public</span> <span class="t">string</span> <span class="v">$owner</span>;
}
<span class="v">$k</span> = <span class="k">new</span> <span class="t">Account</span>();
<span class="v">$k</span>-><span class="v">balance</span> = <span class="s">100</span>; <span class="c">// ok</span>
<span class="c">// $k->balance = "viel"; // TypeError</span></pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>strict_types in jede Datei</b>Setze <code class="inline">declare(strict_types=1)</code> als erste Zeile in jede PHP-Datei. Du fängst Tippfehler und falsche Übergaben sofort ab, statt dass sie sich als kuriose Werte durch das Programm ziehen.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 17</span><h1>Union-, Nullable- & spezielle Typen</h1></div><p class="lead">Manchmal passt ein Wert in mehrere Typen. PHP drückt das mit Union-Typen aus – und kennt nützliche Sondertypen wie <code class="inline">mixed</code>, <code class="inline">void</code> oder <code class="inline">self</code>.</p>
<h2>Nullable</h2>
<p>Das Fragezeichen erlaubt zusätzlich <code class="inline">null</code> – typisch für „nicht gefunden":</p>
<pre><span class="k">function</span> <span class="f">find</span>(<span class="t">int</span> <span class="v">$id</span>): <span class="t">?string</span> {
<span class="k">return</span> <span class="v">$id</span> === <span class="s">1</span> ? <span class="s">"Anna"</span> : <span class="k">null</span>;
}
<span class="k">var_dump</span>(<span class="f">find</span>(<span class="s">1</span>)); <span class="c">// string "Anna"</span>
<span class="k">var_dump</span>(<span class="f">find</span>(<span class="s">9</span>)); <span class="c">// NULL</span></pre>
<h2>Union-Typen</h2>
<p>Mehrere erlaubte Typen, getrennt mit <code class="inline">|</code>:</p>
<pre><span class="k">function</span> <span class="f">id</span>(<span class="t">int</span>|<span class="t">string</span> <span class="v">$value</span>): <span class="t">string</span> {
<span class="k">return</span> <span class="s">"ID-"</span> . <span class="v">$value</span>;
}
<span class="k">echo</span> <span class="f">id</span>(<span class="s">42</span>); <span class="c">// ID-42</span>
<span class="k">echo</span> <span class="f">id</span>(<span class="s">"abc"</span>); <span class="c">// ID-abc</span></pre>
<h2>void & never</h2>
<p><code class="inline">void</code> heißt „gibt nichts zurück", <code class="inline">never</code> „kehrt nie zurück" (wirft oder beendet):</p>
<pre><span class="k">function</span> <span class="f">log</span>(<span class="t">string</span> <span class="v">$t</span>): <span class="t">void</span> { <span class="k">echo</span> <span class="v">$t</span>; }
<span class="k">function</span> <span class="f">abort</span>(): <span class="t">never</span> { <span class="k">throw</span> <span class="k">new</span> <span class="t">RuntimeException</span>(<span class="s">"Stopp"</span>); }</pre>
<h2>mixed & self</h2>
<p><code class="inline">mixed</code> akzeptiert jeden Typ; <code class="inline">self</code> bzw. <code class="inline">static</code> stehen für die eigene Klasse – wichtig bei verketteten Methoden:</p>
<pre><span class="k">class</span> <span class="t">Box</span> {
<span class="k">private</span> <span class="t">array</span> <span class="v">$content</span> = [];
<span class="k">public function</span> <span class="f">add</span>(<span class="t">mixed</span> <span class="v">$x</span>): <span class="k">static</span> {
<span class="v">$this</span>-><span class="v">content</span>[] = <span class="v">$x</span>;
<span class="k">return</span> <span class="v">$this</span>; <span class="c">// erlaubt $box->add(1)->add(2)</span>
}
}
<span class="k">echo</span> <span class="k">count</span>((<span class="k">new</span> <span class="t">Box</span>())-><span class="f">add</span>(<span class="s">1</span>)-><span class="f">add</span>(<span class="s">2</span>)-><span class="v">content</span> ?? []); <span class="c">// 2</span></pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>So eng wie möglich</b>Je genauer der Typ, desto mehr Sicherheit. Nutze <code class="inline">mixed</code> nur, wenn wirklich alles erlaubt ist; ein Union-Typ wie <code class="inline">int|string</code> sagt mehr aus und lässt die IDE und statische Analyse besser helfen.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 18</span><h1>Enums</h1></div><p class="lead">Ein Enum (seit PHP 8.1) ist ein Typ mit einer festen, benannten Menge von Werten. Statt magischer Strings wie <code class="inline">"offen"</code> bekommst du echte, typsichere Fälle.</p>
<h2>Reines Enum</h2>
<pre><span class="k">enum</span> <span class="t">Status</span> {
<span class="k">case</span> <span class="t">Open</span>;
<span class="k">case</span> <span class="t">Paid</span>;
<span class="k">case</span> <span class="t">Cancelled</span>;
}
<span class="k">function</span> <span class="f">handle</span>(<span class="t">Status</span> <span class="v">$s</span>): <span class="t">string</span> {
<span class="k">return</span> <span class="k">match</span>(<span class="v">$s</span>) {
<span class="t">Status</span>::<span class="t">Open</span> => <span class="s">"wartet"</span>,
<span class="t">Status</span>::<span class="t">Paid</span> => <span class="s">"erledigt"</span>,
<span class="t">Status</span>::<span class="t">Cancelled</span> => <span class="s">"abgebrochen"</span>,
};
}
<span class="k">echo</span> <span class="f">handle</span>(<span class="t">Status</span>::<span class="t">Paid</span>); <span class="c">// erledigt</span></pre>
<p>Der Gewinn: An eine Funktion mit <code class="inline">Status</code>-Parameter kann man nichts Falsches übergeben, und <code class="inline">match</code> ohne <code class="inline">default</code> zwingt dich, alle Fälle zu behandeln.</p>
<h2>Backed Enum</h2>
<p>Hängt jedem Fall einen Skalar an, z. B. für Datenbank oder API:</p>
<pre><span class="k">enum</span> <span class="t">Role</span>: <span class="t">string</span> {
<span class="k">case</span> <span class="t">Admin</span> = <span class="s">"admin"</span>;
<span class="k">case</span> <span class="t">Editor</span> = <span class="s">"editor"</span>;
}
<span class="k">echo</span> <span class="t">Role</span>::<span class="t">Admin</span>-><span class="v">value</span>; <span class="c">// admin</span>
<span class="v">$r</span> = <span class="t">Role</span>::<span class="f">from</span>(<span class="s">"editor"</span>); <span class="c">// aus dem Wert zurück: Rolle::Redakteur</span>
<span class="v">$r</span> = <span class="t">Role</span>::<span class="f">tryFrom</span>(<span class="s">"x"</span>); <span class="c">// null statt Fehler bei Unbekanntem</span></pre>
<h2>Methoden im Enum</h2>
<p>Enums dürfen Methoden haben – praktisch für Anzeigetexte:</p>
<pre><span class="k">enum</span> <span class="t">TrafficLight</span> {
<span class="k">case</span> <span class="t">Red</span>;
<span class="k">case</span> <span class="t">Green</span>;
<span class="k">public function</span> <span class="f">drive</span>(): <span class="t">bool</span> {
<span class="k">return</span> <span class="v">$this</span> === <span class="t">TrafficLight</span>::<span class="t">Green</span>;
}
}
<span class="k">var_dump</span>(<span class="t">TrafficLight</span>::<span class="t">Red</span>-><span class="f">drive</span>()); <span class="c">// false</span></pre>
<p>Alle Fälle bekommst du über <code class="inline">cases()</code>:</p>
<pre><span class="k">foreach</span> (<span class="t">Role</span>::<span class="f">cases</span>() <span class="k">as</span> <span class="v">$role</span>) {
<span class="k">echo</span> <span class="v">$role</span>-><span class="v">value</span>; <span class="c">// admin, editor</span>
}</pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Enums statt Konstanten-Sammlungen</b>Wo früher eine Klasse mit <code class="inline">const STATUS_OFFEN = "offen"</code> stand, ist heute ein Enum die bessere Wahl: typsicher, mit Methoden und mit <code class="inline">cases()</code> aufzählbar. Nutze <code class="inline">tryFrom()</code> statt <code class="inline">from()</code>, wenn Eingaben unbekannt sein könnten.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 19</span><h1>Moderne Syntax</h1></div><p class="lead">PHP 8 hat die Sprache spürbar moderner gemacht. Diese Schmankerl begegnen dir in jedem aktuellen Projekt – hier jedes mit Beispiel.</p>
<h2>Nullsafe-Operator</h2>
<p><code class="inline">?-></code> ruft eine Methode nur auf, wenn das Objekt nicht <code class="inline">null</code> ist – kein Fehler bei fehlenden Zwischenwerten:</p>
<pre><span class="v">$country</span> = <span class="v">$user</span>?-><span class="f">getAddress</span>()?-><span class="v">country</span>; <span class="c">// null, wenn etwas in der Kette fehlt</span></pre>
<h2>Named Arguments</h2>
<p>Argumente beim Namen nennen – die Reihenfolge wird egal, optionale gezielt setzbar:</p>
<pre><span class="k">array_slice</span>(<span class="v">array:</span> [<span class="s">1</span>, <span class="s">2</span>, <span class="s">3</span>, <span class="s">4</span>], <span class="v">offset:</span> <span class="s">1</span>, <span class="v">length:</span> <span class="s">2</span>); <span class="c">// [2, 3]</span></pre>
<h2>Konstruktor-Promotion</h2>
<p>Parameter mit Sichtbarkeit werden automatisch zu Eigenschaften – kein Boilerplate mehr:</p>
<pre><span class="k">class</span> <span class="t">Point</span> {
<span class="k">public function</span> <span class="f">__construct</span>(
<span class="k">public</span> <span class="t">float</span> <span class="v">$x</span>,
<span class="k">public</span> <span class="t">float</span> <span class="v">$y</span>,
) {}
}
<span class="k">echo</span> (<span class="k">new</span> <span class="t">Point</span>(<span class="s">2</span>, <span class="s">3</span>))-><span class="v">x</span>; <span class="c">// 2</span></pre>
<h2>Destrukturierung</h2>
<pre>[<span class="v">$a</span>, <span class="v">$b</span>] = [<span class="s">1</span>, <span class="s">2</span>];
<span class="k">echo</span> <span class="v">$a</span>; <span class="c">// 1</span>
[<span class="s">"name"</span> => <span class="v">$name</span>] = [<span class="s">"name"</span> => <span class="s">"Anna"</span>, <span class="s">"alter"</span> => <span class="s">30</span>];
<span class="k">echo</span> <span class="v">$name</span>; <span class="c">// Anna</span></pre>
<h2>First-Class Callable</h2>
<p><code class="inline">(...)</code> macht aus einer Funktion einen Wert, den man weiterreichen kann:</p>
<pre><span class="v">$fn</span> = <span class="k">strtoupper</span>(...);
<span class="k">print_r</span>(<span class="k">array_map</span>(<span class="v">$fn</span>, [<span class="s">"a"</span>, <span class="s">"b"</span>])); <span class="c">// ["A", "B"]</span></pre>
<h2>Pipe-Operator (PHP 8.5)</h2>
<p>Der neue <code class="inline">|></code> reicht einen Wert von links nach rechts durch eine Kette – lesbarer als verschachtelte Aufrufe:</p>
<pre><span class="v">$result</span> = <span class="s">" Hallo Welt "</span>
|> <span class="k">trim</span>(...)
|> <span class="k">strtolower</span>(...);
<span class="k">echo</span> <span class="v">$result</span>; <span class="c">// hallo welt</span></pre>
<h2>clone with (PHP 8.5)</h2>
<p>Klont ein Objekt und ändert dabei einzelne Eigenschaften in einem Ausdruck – ideal für unveränderliche Wertobjekte:</p>
<pre><span class="v">$p2</span> = <span class="k">clone</span> <span class="v">$point</span> <span class="k">with</span> { <span class="v">x</span>: <span class="s">10</span> }; <span class="c">// Kopie mit neuem x, Rest gleich</span></pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Nutzen, wo es Klarheit bringt</b>Diese Features sind additiv – alter Code läuft unverändert weiter. Konstruktor-Promotion und Enums lohnen sich sofort; den Pipe-Operator setzt man am besten dort ein, wo eine Transformationskette sonst tief verschachtelt wäre.</div>
</div></section><section class="part-divider"><div class="part-kicker">Teil 5</div><h1>Objektorientierung: Grundlagen</h1><div class="part-desc">Das Rückgrat größerer Programme: von der ersten Klasse über Vererbung und Interfaces bis zu Traits und magischen Methoden.</div><div class="part-chapters"><span>20 · Klassen & Objekte</span><span>21 · Sichtbarkeit & Kapselung</span><span>22 · Konstruktoren modern</span><span>23 · Vererbung</span><span>24 · Abstrakte Klassen & Interfaces</span><span>25 · Traits</span><span>26 · Statisches & Konstanten</span><span>27 · Magische Methoden</span></div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 20</span><h1>Klassen & Objekte</h1></div><p class="lead">Eine Klasse ist ein Bauplan; ein Objekt eine konkrete Ausführung davon. Klassen bündeln Daten (Eigenschaften) und Verhalten (Methoden) zu einer Einheit – das Rückgrat größerer Programme.</p>
<h2>Eine Klasse definieren</h2>
<pre><span class="k">class</span> <span class="t">Dog</span> {
<span class="k">public</span> <span class="t">string</span> <span class="v">$name</span>; <span class="c">// Eigenschaft</span>
<span class="k">public function</span> <span class="f">bark</span>(): <span class="t">string</span> { <span class="c">// Methode</span>
<span class="k">return</span> <span class="v">$this</span>-><span class="v">name</span> . <span class="s">" sagt Wuff"</span>;
}
}</pre>
<p><code class="inline">$this</code> verweist innerhalb der Klasse auf das aktuelle Objekt.</p>
<h2>Objekte erzeugen & nutzen</h2>
<pre><span class="v">$dog</span> = <span class="k">new</span> <span class="t">Dog</span>();
<span class="v">$dog</span>-><span class="v">name</span> = <span class="s">"Bello"</span>; <span class="c">// Eigenschaft setzen</span>
<span class="k">echo</span> <span class="v">$dog</span>-><span class="f">bark</span>(); <span class="c">// Bello sagt Wuff</span></pre>
<h2>Mehrere unabhängige Objekte</h2>
<p>Jedes Objekt hat seinen eigenen Zustand:</p>
<pre><span class="v">$a</span> = <span class="k">new</span> <span class="t">Dog</span>(); <span class="v">$a</span>-><span class="v">name</span> = <span class="s">"Rex"</span>;
<span class="v">$b</span> = <span class="k">new</span> <span class="t">Dog</span>(); <span class="v">$b</span>-><span class="v">name</span> = <span class="s">"Luna"</span>;
<span class="k">echo</span> <span class="v">$a</span>-><span class="f">bark</span>(); <span class="c">// Rex sagt Wuff</span>
<span class="k">echo</span> <span class="v">$b</span>-><span class="f">bark</span>(); <span class="c">// Luna sagt Wuff</span></pre>
<h2>Objekte sind Referenzen</h2>
<p>Eine Zuweisung kopiert nicht das Objekt, sondern den Verweis darauf – beide zeigen auf dasselbe:</p>
<pre><span class="v">$x</span> = <span class="k">new</span> <span class="t">Dog</span>(); <span class="v">$x</span>-><span class="v">name</span> = <span class="s">"Rex"</span>;
<span class="v">$y</span> = <span class="v">$x</span>;
<span class="v">$y</span>-><span class="v">name</span> = <span class="s">"Bello"</span>;
<span class="k">echo</span> <span class="v">$x</span>-><span class="v">name</span>; <span class="c">// Bello – $x und $y sind dasselbe Objekt</span></pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Klasse vs. Objekt</b>Die Klasse schreibst du einmal; Objekte erzeugst du beliebig oft daraus. Eine echte Kopie eines Objekts bekommst du mit <code class="inline">clone</code> – mehr dazu bei den magischen Methoden.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 21</span><h1>Sichtbarkeit & Kapselung</h1></div><p class="lead">Kapselung bedeutet: Eine Klasse verbirgt ihre Interna und gibt nur einen kontrollierten Zugang nach außen. Sichtbarkeits-Schlüsselwörter steuern, wer worauf zugreifen darf.</p>
<h2>public, protected, private</h2>
<p><code class="inline">public</code> ist überall erreichbar, <code class="inline">protected</code> in der Klasse und ihren Erben, <code class="inline">private</code> nur in der Klasse selbst:</p>
<pre><span class="k">class</span> <span class="t">Account</span> {
<span class="k">private</span> <span class="t">int</span> <span class="v">$balance</span> = <span class="s">0</span>; <span class="c">// von außen unsichtbar</span>
<span class="k">public function</span> <span class="f">deposit</span>(<span class="t">int</span> <span class="v">$amount</span>): <span class="t">void</span> {
<span class="k">if</span> (<span class="v">$amount</span> > <span class="s">0</span>) <span class="v">$this</span>-><span class="v">balance</span> += <span class="v">$amount</span>;
}
<span class="k">public function</span> <span class="f">balance</span>(): <span class="t">int</span> { <span class="k">return</span> <span class="v">$this</span>-><span class="v">balance</span>; }
}
<span class="v">$k</span> = <span class="k">new</span> <span class="t">Account</span>();
<span class="v">$k</span>-><span class="f">deposit</span>(<span class="s">100</span>);
<span class="k">echo</span> <span class="v">$k</span>-><span class="f">balance</span>(); <span class="c">// 100</span>
<span class="c">// $k->balance = -999; // Fehler: private, der Schutz greift</span></pre>
<p>So kann niemand den Kontostand direkt manipulieren – jede Änderung muss durch <code class="inline">deposit()</code> und dessen Prüfung.</p>
<h2>Getter und Setter</h2>
<p>Lesen und kontrolliertes Schreiben über Methoden:</p>
<pre><span class="k">class</span> <span class="t">Temperature</span> {
<span class="k">private</span> <span class="t">float</span> <span class="v">$celsius</span> = <span class="s">0</span>;
<span class="k">public function</span> <span class="f">setCelsius</span>(<span class="t">float</span> <span class="v">$c</span>): <span class="t">void</span> {
<span class="k">if</span> (<span class="v">$c</span> < -<span class="s">273.15</span>) <span class="k">throw</span> <span class="k">new</span> <span class="t">ValueError</span>(<span class="s">"zu kalt"</span>);
<span class="v">$this</span>-><span class="v">celsius</span> = <span class="v">$c</span>;
}
<span class="k">public function</span> <span class="f">fahrenheit</span>(): <span class="t">float</span> { <span class="k">return</span> <span class="v">$this</span>-><span class="v">celsius</span> * <span class="s">9</span> / <span class="s">5</span> + <span class="s">32</span>; }
}
<span class="v">$t</span> = <span class="k">new</span> <span class="t">Temperature</span>();
<span class="v">$t</span>-><span class="f">setCelsius</span>(<span class="s">100</span>);
<span class="k">echo</span> <span class="v">$t</span>-><span class="f">fahrenheit</span>(); <span class="c">// 212</span></pre>
<h2>Asymmetrische Sichtbarkeit (PHP 8.4)</h2>
<p>Eine Eigenschaft kann öffentlich lesbar, aber nur intern schreibbar sein – ganz ohne Getter:</p>
<pre><span class="k">class</span> <span class="t">Article</span> {
<span class="k">public</span> <span class="k">private</span>(<span class="k">set</span>) <span class="t">string</span> <span class="v">$status</span> = <span class="s">"entwurf"</span>;
<span class="k">public function</span> <span class="f">publish</span>(): <span class="t">void</span> { <span class="v">$this</span>-><span class="v">status</span> = <span class="s">"live"</span>; }
}
<span class="v">$a</span> = <span class="k">new</span> <span class="t">Article</span>();
<span class="k">echo</span> <span class="v">$a</span>-><span class="v">status</span>; <span class="c">// entwurf (lesen erlaubt)</span>
<span class="v">$a</span>-><span class="f">publish</span>();
<span class="k">echo</span> <span class="v">$a</span>-><span class="v">status</span>; <span class="c">// live</span>
<span class="c">// $a->status = "x"; // Fehler: von außen nicht schreibbar</span></pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Standardmäßig privat denken</b>Mache Eigenschaften so privat wie möglich und öffne nur, was wirklich von außen gebraucht wird. Das hält den inneren Zustand konsistent und erlaubt dir, die Umsetzung später zu ändern, ohne den restlichen Code zu brechen.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 22</span><h1>Konstruktoren modern</h1></div><p class="lead">Der Konstruktor <code class="inline">__construct</code> richtet ein Objekt bei der Erzeugung ein. PHP 8 macht ihn besonders knapp – und mit <code class="inline">readonly</code> sogar unveränderlich.</p>
<h2>Der klassische Konstruktor</h2>
<pre><span class="k">class</span> <span class="t">Point</span> {
<span class="k">public</span> <span class="t">float</span> <span class="v">$x</span>;
<span class="k">public</span> <span class="t">float</span> <span class="v">$y</span>;
<span class="k">public function</span> <span class="f">__construct</span>(<span class="t">float</span> <span class="v">$x</span>, <span class="t">float</span> <span class="v">$y</span>) {
<span class="v">$this</span>-><span class="v">x</span> = <span class="v">$x</span>;
<span class="v">$this</span>-><span class="v">y</span> = <span class="v">$y</span>;
}
}
<span class="v">$p</span> = <span class="k">new</span> <span class="t">Point</span>(<span class="s">2</span>, <span class="s">3</span>);
<span class="k">echo</span> <span class="v">$p</span>-><span class="v">x</span>; <span class="c">// 2</span></pre>
<h2>Konstruktor-Promotion</h2>
<p>Die Schreibweise von oben ist so häufig, dass PHP sie abkürzt: Sichtbarkeit direkt am Parameter macht ihn automatisch zur Eigenschaft – das obige Beispiel schrumpft auf:</p>
<pre><span class="k">class</span> <span class="t">Point</span> {
<span class="k">public function</span> <span class="f">__construct</span>(
<span class="k">public</span> <span class="t">float</span> <span class="v">$x</span>,
<span class="k">public</span> <span class="t">float</span> <span class="v">$y</span>,
) {}
}
<span class="k">echo</span> (<span class="k">new</span> <span class="t">Point</span>(<span class="s">2</span>, <span class="s">3</span>))-><span class="v">y</span>; <span class="c">// 3</span></pre>
<h2>readonly</h2>
<p>Eine <code class="inline">readonly</code>-Eigenschaft lässt sich nur im Konstruktor belegen und danach nie wieder ändern – die Grundlage unveränderlicher Objekte:</p>
<pre><span class="k">class</span> <span class="t">Money</span> {
<span class="k">public function</span> <span class="f">__construct</span>(
<span class="k">public</span> <span class="k">readonly</span> <span class="t">int</span> <span class="v">$cent</span>,
<span class="k">public</span> <span class="k">readonly</span> <span class="t">string</span> <span class="v">$currency</span> = <span class="s">"EUR"</span>,
) {}
}
<span class="v">$price</span> = <span class="k">new</span> <span class="t">Money</span>(<span class="s">1999</span>);
<span class="k">echo</span> <span class="v">$price</span>-><span class="v">cent</span>; <span class="c">// 1999</span>
<span class="c">// $price->cent = 0; // Fehler: readonly</span></pre>
<h2>Mit benannten Argumenten erzeugen</h2>
<pre><span class="v">$price</span> = <span class="k">new</span> <span class="t">Money</span>(<span class="v">currency:</span> <span class="s">"USD"</span>, <span class="v">cent:</span> <span class="s">500</span>);
<span class="k">echo</span> <span class="v">$price</span>-><span class="v">currency</span>; <span class="c">// USD</span></pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Objekte gültig geboren</b>Lass den Konstruktor alle Pflichtwerte entgegennehmen und prüfen. Ist das Objekt erst einmal erzeugt, sollte es in einem gültigen Zustand sein – das erspart später endlose „ist das gesetzt?"-Prüfungen. Mit <code class="inline">readonly</code> bleibt es das garantiert.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 23</span><h1>Vererbung</h1></div><p class="lead">Vererbung lässt eine Klasse Eigenschaften und Methoden einer anderen übernehmen und erweitern. Sie modelliert eine „ist ein"-Beziehung – ein Kreis <i>ist eine</i> Form.</p>
<h2>extends</h2>
<pre><span class="k">class</span> <span class="t">Animal</span> {
<span class="k">public function</span> <span class="f">__construct</span>(<span class="k">protected</span> <span class="t">string</span> <span class="v">$name</span>) {}
<span class="k">public function</span> <span class="f">sound</span>(): <span class="t">string</span> { <span class="k">return</span> <span class="s">"..."</span>; }
}
<span class="k">class</span> <span class="t">Cat</span> <span class="k">extends</span> <span class="t">Animal</span> {
<span class="k">public function</span> <span class="f">sound</span>(): <span class="t">string</span> { <span class="k">return</span> <span class="s">"Miau"</span>; } <span class="c">// überschreibt</span>
}
<span class="v">$k</span> = <span class="k">new</span> <span class="t">Cat</span>(<span class="s">"Minka"</span>);
<span class="k">echo</span> <span class="v">$k</span>-><span class="f">sound</span>(); <span class="c">// Miau</span></pre>
<p>Die abgeleitete Klasse erbt den Konstruktor und kann Methoden überschreiben.</p>
<h2>parent::</h2>
<p>Beim Überschreiben rufst du oft die ursprüngliche Methode mit auf:</p>
<pre><span class="k">class</span> <span class="t">Dog</span> <span class="k">extends</span> <span class="t">Animal</span> {
<span class="k">public function</span> <span class="f">__construct</span>(<span class="t">string</span> <span class="v">$name</span>, <span class="k">public</span> <span class="t">string</span> <span class="v">$breed</span>) {
<span class="k">parent</span>::<span class="f">__construct</span>(<span class="v">$name</span>); <span class="c">// Basis-Konstruktor</span>
}
<span class="k">public function</span> <span class="f">sound</span>(): <span class="t">string</span> { <span class="k">return</span> <span class="s">"Wuff"</span>; }
}
<span class="v">$h</span> = <span class="k">new</span> <span class="t">Dog</span>(<span class="s">"Rex"</span>, <span class="s">"Terrier"</span>);
<span class="k">echo</span> <span class="v">$h</span>-><span class="v">breed</span>; <span class="c">// Terrier</span></pre>
<h2>protected</h2>
<p>Im Beispiel oben ist <code class="inline">$name</code> <code class="inline">protected</code> und damit in <code class="inline">Dog</code> und <code class="inline">Cat</code> nutzbar, von außen aber verborgen.</p>
<h2>final</h2>
<p><code class="inline">final</code> verhindert weiteres Überschreiben bzw. Erben:</p>
<pre><span class="k">final class</span> <span class="t">UUID</span> {} <span class="c">// kann nicht erweitert werden</span>
<span class="k">class</span> <span class="t">Base</span> {
<span class="k">final public function</span> <span class="f">id</span>(): <span class="t">int</span> { <span class="k">return</span> <span class="s">1</span>; } <span class="c">// nicht überschreibbar</span>
}</pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Komposition vor Vererbung</b>Vererbung bindet Klassen eng aneinander. Oft ist es flexibler, Verhalten <i>einzubauen</i> (ein Objekt hält ein anderes) statt es zu <i>erben</i>. Nutze Vererbung für echte „ist ein"-Beziehungen – nicht nur, um Code zu teilen; dafür sind Traits oder Komposition meist besser.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 24</span><h1>Abstrakte Klassen & Interfaces</h1></div><p class="lead">Beide definieren einen Vertrag, den andere Klassen erfüllen. Ein Interface schreibt nur vor, <i>was</i> es können muss; eine abstrakte Klasse kann zusätzlich gemeinsamen Code mitbringen.</p>
<h2>Interface</h2>
<p>Ein Interface listet Methoden ohne Rumpf. Wer es <code class="inline">implements</code>, muss sie ausfüllen:</p>
<pre><span class="k">interface</span> <span class="t">Shape</span> {
<span class="k">public function</span> <span class="f">area</span>(): <span class="t">float</span>;
}
<span class="k">class</span> <span class="t">Circle</span> <span class="k">implements</span> <span class="t">Shape</span> {
<span class="k">public function</span> <span class="f">__construct</span>(<span class="k">private</span> <span class="t">float</span> <span class="v">$r</span>) {}
<span class="k">public function</span> <span class="f">area</span>(): <span class="t">float</span> { <span class="k">return</span> <span class="s">3.14159</span> * <span class="v">$this</span>-><span class="v">r</span> ** <span class="s">2</span>; }
}
<span class="k">class</span> <span class="t">Square</span> <span class="k">implements</span> <span class="t">Shape</span> {
<span class="k">public function</span> <span class="f">__construct</span>(<span class="k">private</span> <span class="t">float</span> <span class="v">$s</span>) {}
<span class="k">public function</span> <span class="f">area</span>(): <span class="t">float</span> { <span class="k">return</span> <span class="v">$this</span>-><span class="v">s</span> ** <span class="s">2</span>; }
}</pre>
<p>Der Gewinn: Code kann mit jeder <code class="inline">Shape</code> arbeiten, ohne die konkrete Klasse zu kennen:</p>
<pre><span class="k">function</span> <span class="f">describe</span>(<span class="t">Shape</span> <span class="v">$f</span>): <span class="t">string</span> {
<span class="k">return</span> <span class="s">"Fläche: "</span> . <span class="k">round</span>(<span class="v">$f</span>-><span class="f">area</span>(), <span class="s">2</span>);
}
<span class="k">echo</span> <span class="f">describe</span>(<span class="k">new</span> <span class="t">Circle</span>(<span class="s">2</span>)); <span class="c">// Fläche: 12.57</span>
<span class="k">echo</span> <span class="f">describe</span>(<span class="k">new</span> <span class="t">Square</span>(<span class="s">3</span>)); <span class="c">// Fläche: 9</span></pre>
<h2>Mehrere Interfaces</h2>
<p>Eine Klasse kann beliebig viele Interfaces erfüllen:</p>
<pre><span class="k">interface</span> <span class="t">Printable</span> { <span class="k">public function</span> <span class="f">output</span>(): <span class="t">void</span>; }
<span class="k">class</span> <span class="t">Receipt</span> <span class="k">implements</span> <span class="t">Shape</span>, <span class="t">Printable</span> { <span class="c">/* beide Verträge erfüllen */</span> }</pre>
<h2>Abstrakte Klasse</h2>
<p>Sie kann nicht selbst instanziiert werden, gibt aber fertige Methoden mit und lässt einzelne offen:</p>
<pre><span class="k">abstract class</span> <span class="t">Payment</span> {
<span class="k">abstract public function</span> <span class="f">book</span>(<span class="t">int</span> <span class="v">$cent</span>): <span class="t">string</span>; <span class="c">// muss gefüllt werden</span>
<span class="k">public function</span> <span class="f">receipt</span>(<span class="t">int</span> <span class="v">$cent</span>): <span class="t">string</span> { <span class="c">// fertig vererbt</span>
<span class="k">return</span> <span class="s">"Beleg über "</span> . <span class="k">number_format</span>(<span class="v">$cent</span> / <span class="s">100</span>, <span class="s">2</span>) . <span class="s">" €"</span>;
}
}
<span class="k">class</span> <span class="t">PayPal</span> <span class="k">extends</span> <span class="t">Payment</span> {
<span class="k">public function</span> <span class="f">book</span>(<span class="t">int</span> <span class="v">$cent</span>): <span class="t">string</span> { <span class="k">return</span> <span class="s">"via PayPal"</span>; }
}
<span class="k">echo</span> (<span class="k">new</span> <span class="t">PayPal</span>())-><span class="f">receipt</span>(<span class="s">1999</span>); <span class="c">// Beleg über 19.99 €</span></pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Interface oder abstrakte Klasse?</b>Nimm ein <b>Interface</b>, wenn du nur den Vertrag brauchst – eine Klasse kann mehrere erfüllen. Nimm eine <b>abstrakte Klasse</b>, wenn mehrere Unterklassen sich echten Code teilen sollen. Programmiere gegen Interfaces, nicht gegen konkrete Klassen – das hält den Code austauschbar und testbar.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 25</span><h1>Traits</h1></div><p class="lead">Ein Trait ist ein wiederverwendbarer Methodenbaustein. Da eine Klasse nur von <i>einer</i> Klasse erben kann, lösen Traits das Problem, Code über mehrere unverwandte Klassen zu teilen.</p>
<h2>Definieren & einbinden</h2>
<pre><span class="k">trait</span> <span class="t">Timestamps</span> {
<span class="k">public</span> <span class="t">?int</span> <span class="v">$created</span> = <span class="k">null</span>;
<span class="k">public function</span> <span class="f">stamp</span>(): <span class="t">void</span> { <span class="v">$this</span>-><span class="v">created</span> = <span class="k">time</span>(); }
}
<span class="k">class</span> <span class="t">Post</span> { <span class="k">use</span> <span class="t">Timestamps</span>; }
<span class="k">class</span> <span class="t">Comment</span> { <span class="k">use</span> <span class="t">Timestamps</span>; }
<span class="v">$b</span> = <span class="k">new</span> <span class="t">Post</span>();
<span class="v">$b</span>-><span class="f">stamp</span>();
<span class="k">var_dump</span>(<span class="k">is_int</span>(<span class="v">$b</span>-><span class="v">created</span>)); <span class="c">// true – beide Klassen haben die Methode</span></pre>
<p>Beide Klassen teilen denselben Code, ohne miteinander verwandt zu sein.</p>
<h2>Mehrere Traits</h2>
<pre><span class="k">trait</span> <span class="t">Counting</span> {
<span class="k">public</span> <span class="t">int</span> <span class="v">$count</span> = <span class="s">0</span>;
<span class="k">public function</span> <span class="f">up</span>(): <span class="t">void</span> { <span class="v">$this</span>-><span class="v">count</span>++; }
}
<span class="k">class</span> <span class="t">Gallery</span> {
<span class="k">use</span> <span class="t">Timestamps</span>, <span class="t">Counting</span>; <span class="c">// mehrere Bausteine kombinieren</span>
}
<span class="v">$g</span> = <span class="k">new</span> <span class="t">Gallery</span>();
<span class="v">$g</span>-><span class="f">up</span>(); <span class="v">$g</span>-><span class="f">up</span>();
<span class="k">echo</span> <span class="v">$g</span>-><span class="v">count</span>; <span class="c">// 2</span></pre>
<h2>Konflikte auflösen</h2>
<p>Bringen zwei Traits eine gleichnamige Methode mit, entscheidest du mit <code class="inline">insteadof</code> und <code class="inline">as</code>:</p>
<pre><span class="k">class</span> <span class="t">Report</span> {
<span class="k">use</span> <span class="t">A</span>, <span class="t">B</span> {
<span class="t">A</span>::<span class="f">hello</span> <span class="k">insteadof</span> <span class="t">B</span>; <span class="c">// A gewinnt</span>
<span class="t">B</span>::<span class="f">hello</span> <span class="k">as</span> <span class="f">helloB</span>; <span class="c">// Bs Version unter neuem Namen</span>
}
}</pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Sparsam einsetzen</b>Traits sind praktisch, verstecken aber, woher Methoden kommen, und neigen zu verborgenen Abhängigkeiten auf <code class="inline">$this</code>. Für echtes Teilen von Verhalten ist Komposition (ein eingebautes Hilfsobjekt) oft klarer. Nutze Traits gezielt für kleine, eigenständige Fähigkeiten.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 26</span><h1>Statisches & Konstanten</h1></div><p class="lead">Statische Member gehören zur <i>Klasse</i> statt zu einem Objekt. Konstanten halten unveränderliche Werte. Beide erreichst du ohne Instanz – über den Klassennamen.</p>
<h2>Klassenkonstanten</h2>
<pre><span class="k">class</span> <span class="t">Circle</span> {
<span class="k">const</span> <span class="t">PI</span> = <span class="s">3.14159</span>;
<span class="k">public function</span> <span class="f">__construct</span>(<span class="k">private</span> <span class="t">float</span> <span class="v">$r</span>) {}
<span class="k">public function</span> <span class="f">area</span>(): <span class="t">float</span> { <span class="k">return</span> <span class="k">self</span>::<span class="t">PI</span> * <span class="v">$this</span>-><span class="v">r</span> ** <span class="s">2</span>; }
}
<span class="k">echo</span> <span class="t">Circle</span>::<span class="t">PI</span>; <span class="c">// 3.14159 (ohne Objekt)</span>
<span class="k">echo</span> (<span class="k">new</span> <span class="t">Circle</span>(<span class="s">2</span>))-><span class="f">area</span>(); <span class="c">// 12.56636</span></pre>
<p>Seit PHP 8.3 dürfen Konstanten typisiert sein: <code class="inline">const float PI = 3.14159;</code>.</p>
<h2>Statische Eigenschaften & Methoden</h2>
<p>Sie existieren einmal pro Klasse, nicht pro Objekt – nützlich z. B. für einen Zähler:</p>
<pre><span class="k">class</span> <span class="t">Connection</span> {
<span class="k">public static</span> <span class="t">int</span> <span class="v">$open</span> = <span class="s">0</span>;
<span class="k">public static function</span> <span class="f">open</span>(): <span class="t">void</span> { <span class="k">self</span>::<span class="v">$open</span>++; }
}
<span class="t">Connection</span>::<span class="f">open</span>();
<span class="t">Connection</span>::<span class="f">open</span>();
<span class="k">echo</span> <span class="t">Connection</span>::<span class="v">$open</span>; <span class="c">// 2 – über alle „Objekte" geteilt</span></pre>
<h2>self vs. static</h2>
<p><code class="inline">self</code> bezieht sich auf die Klasse, in der der Code steht; <code class="inline">static</code> auf die tatsächlich aufgerufene (Late Static Binding) – wichtig bei Vererbung:</p>
<pre><span class="k">class</span> <span class="t">Base</span> {
<span class="k">public static function</span> <span class="f">create</span>(): <span class="k">static</span> { <span class="k">return</span> <span class="k">new</span> <span class="k">static</span>(); }
}
<span class="k">class</span> <span class="t">Child</span> <span class="k">extends</span> <span class="t">Base</span> {}
<span class="k">var_dump</span>(<span class="t">Child</span>::<span class="f">create</span>() <span class="k">instanceof</span> <span class="t">Child</span>); <span class="c">// true (dank static)</span></pre>
<h2>::class</h2>
<p>Liefert den voll qualifizierten Klassennamen als String – tippsicher:</p>
<pre><span class="k">echo</span> <span class="t">Circle</span>::<span class="k">class</span>; <span class="c">// Kreis (bzw. mit Namespace)</span></pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Statik sparsam</b>Statische Eigenschaften sind globaler Zustand und erschweren Tests, weil sie zwischen Aufrufen bestehen bleiben. Konstanten und statische Hilfsfunktionen ohne Zustand sind unkritisch; veränderlichen Zustand verwaltest du besser in Objekten, die der DI-Container reicht (späterer Teil).</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 27</span><h1>Magische Methoden</h1></div><p class="lead">Magische Methoden (Namen mit doppeltem Unterstrich) werden von PHP automatisch in bestimmten Situationen aufgerufen – beim Erzeugen, beim Zugriff auf fehlende Eigenschaften, beim Ausgeben als String und mehr.</p>
<h2>__construct & __destruct</h2>
<p>Beim Erzeugen bzw. beim Aufräumen eines Objekts:</p>
<pre><span class="k">class</span> <span class="t">File</span> {
<span class="k">public function</span> <span class="f">__construct</span>(<span class="k">private</span> <span class="t">string</span> <span class="v">$path</span>) { <span class="k">echo</span> <span class="s">"öffne\n"</span>; }
<span class="k">public function</span> <span class="f">__destruct</span>() { <span class="k">echo</span> <span class="s">"schließe\n"</span>; }
}
<span class="v">$d</span> = <span class="k">new</span> <span class="t">File</span>(<span class="s">"a.txt"</span>); <span class="c">// öffne</span>
<span class="k">unset</span>(<span class="v">$d</span>); <span class="c">// schließe (kein Verweis mehr)</span></pre>
<h2>__toString</h2>
<p>Macht ein Objekt als String darstellbar:</p>
<pre><span class="k">class</span> <span class="t">Money</span> {
<span class="k">public function</span> <span class="f">__construct</span>(<span class="k">private</span> <span class="t">int</span> <span class="v">$cent</span>) {}
<span class="k">public function</span> <span class="f">__toString</span>(): <span class="t">string</span> { <span class="k">return</span> <span class="k">number_format</span>(<span class="v">$this</span>-><span class="v">cent</span> / <span class="s">100</span>, <span class="s">2</span>) . <span class="s">" €"</span>; }
}
<span class="k">echo</span> <span class="k">new</span> <span class="t">Money</span>(<span class="s">1999</span>); <span class="c">// 19.99 €</span></pre>
<h2>__get, __set, __isset, __unset</h2>
<p>Fangen Zugriffe auf nicht (öffentlich) vorhandene Eigenschaften ab:</p>
<pre><span class="k">class</span> <span class="t">Bag</span> {
<span class="k">private</span> <span class="t">array</span> <span class="v">$data</span> = [];
<span class="k">public function</span> <span class="f">__get</span>(<span class="t">string</span> <span class="v">$k</span>): <span class="t">mixed</span> { <span class="k">return</span> <span class="v">$this</span>-><span class="v">data</span>[<span class="v">$k</span>] ?? <span class="k">null</span>; }
<span class="k">public function</span> <span class="f">__set</span>(<span class="t">string</span> <span class="v">$k</span>, <span class="t">mixed</span> <span class="v">$v</span>): <span class="t">void</span> { <span class="v">$this</span>-><span class="v">data</span>[<span class="v">$k</span>] = <span class="v">$v</span>; }
<span class="k">public function</span> <span class="f">__isset</span>(<span class="t">string</span> <span class="v">$k</span>): <span class="t">bool</span> { <span class="k">return</span> <span class="k">isset</span>(<span class="v">$this</span>-><span class="v">data</span>[<span class="v">$k</span>]); }
}
<span class="v">$t</span> = <span class="k">new</span> <span class="t">Bag</span>();
<span class="v">$t</span>-><span class="v">color</span> = <span class="s">"rot"</span>; <span class="c">// __set</span>
<span class="k">echo</span> <span class="v">$t</span>-><span class="v">color</span>; <span class="c">// __get → rot</span>
<span class="k">var_dump</span>(<span class="k">isset</span>(<span class="v">$t</span>-><span class="v">color</span>)); <span class="c">// __isset → true</span></pre>
<h2>__call & __callStatic</h2>
<p>Fangen Aufrufe nicht vorhandener Methoden ab – Basis vieler „magischer" Framework-APIs:</p>
<pre><span class="k">class</span> <span class="t">Fluent</span> {
<span class="k">public function</span> <span class="f">__call</span>(<span class="t">string</span> <span class="v">$name</span>, <span class="t">array</span> <span class="v">$args</span>): <span class="t">string</span> {
<span class="k">return</span> <span class="s">"$name("</span> . <span class="k">implode</span>(<span class="s">","</span>, <span class="v">$args</span>) . <span class="s">")"</span>;
}
}
<span class="k">echo</span> (<span class="k">new</span> <span class="t">Fluent</span>())-><span class="f">whatever</span>(<span class="s">1</span>, <span class="s">2</span>); <span class="c">// wasAuchImmer(1,2)</span></pre>
<h2>__invoke</h2>
<p>Lässt ein Objekt wie eine Funktion aufrufen:</p>
<pre><span class="k">class</span> <span class="t">Doubler</span> {
<span class="k">public function</span> <span class="f">__invoke</span>(<span class="t">int</span> <span class="v">$n</span>): <span class="t">int</span> { <span class="k">return</span> <span class="v">$n</span> * <span class="s">2</span>; }
}
<span class="v">$f</span> = <span class="k">new</span> <span class="t">Doubler</span>();
<span class="k">echo</span> <span class="v">$f</span>(<span class="s">21</span>); <span class="c">// 42</span></pre>
<h2>__clone</h2>
<p>Greift beim Kopieren mit <code class="inline">clone</code> ein – etwa um enthaltene Objekte mitzukopieren:</p>
<pre><span class="k">class</span> <span class="t">Folder</span> {
<span class="k">public</span> <span class="t">array</span> <span class="v">$pages</span> = [];
<span class="k">public function</span> <span class="f">__clone</span>() { <span class="k">echo</span> <span class="s">"kopiert\n"</span>; }
}
<span class="v">$copy</span> = <span class="k">clone</span> <span class="k">new</span> <span class="t">Folder</span>(); <span class="c">// kopiert</span></pre>
<p>Weitere magische Methoden steuern Sonderfälle: <code class="inline">__serialize</code>/<code class="inline">__unserialize</code> (Serialisierung), <code class="inline">__debugInfo</code> (Ausgabe bei <code class="inline">var_dump</code>) und <code class="inline">__set_state</code> (für <code class="inline">var_export</code>).</p>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Magie mit Maß</b>Magische Methoden sind mächtig, machen Code aber schwer durchschaubar und für IDEs/statische Analyse undurchsichtig. Bevorzuge echte, deklarierte Eigenschaften und Methoden; greife zu <code class="inline">__get</code>/<code class="inline">__call</code> nur, wo dynamisches Verhalten der eigentliche Zweck ist.</div>
</div></section><section class="part-divider"><div class="part-kicker">Teil 6</div><h1>Objektorientierung: fortgeschritten</h1><div class="part-desc">Das tiefere Handwerk hinter Frameworks: typisierte Sammlungen, Generatoren, Closures, Attribute und Reflection.</div><div class="part-chapters"><span>28 · Collections & Generics-Denken</span><span>29 · Iteratoren & Generatoren</span><span>30 · Closures & Bindung</span><span>31 · Attribute</span><span>32 · Reflection</span></div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 28</span><h1>Collections & Generics-Denken</h1></div><p class="lead">Ein nacktes Array kann alles enthalten – das ist flexibel, aber fehleranfällig. Eine Collection ist eine Klasse, die ein Array kapselt und nur Werte eines bestimmten Typs zulässt. So wird aus „irgendein Array" ein klar benannter, typsicherer Behälter.</p>
<h2>Eine typsichere Collection</h2>
<pre><span class="k">class</span> <span class="t">ArticleList</span> {
<span class="k">private</span> <span class="t">array</span> <span class="v">$article</span> = [];
<span class="k">public function</span> <span class="f">add</span>(<span class="t">Article</span> <span class="v">$a</span>): <span class="k">void</span> { <span class="c">// nur Artikel erlaubt</span>
<span class="v">$this</span>-><span class="v">article</span>[] = <span class="v">$a</span>;
}
<span class="k">public function</span> <span class="f">total</span>(): <span class="t">int</span> {
<span class="k">return</span> <span class="k">array_sum</span>(<span class="k">array_map</span>(<span class="k">fn</span>(<span class="t">Article</span> <span class="v">$a</span>) => <span class="v">$a</span>-><span class="v">cent</span>, <span class="v">$this</span>-><span class="v">article</span>));
}
}</pre>
<p>Der Typ am Parameter <code class="inline">add(Artikel $a)</code> garantiert, dass nie etwas Falsches hineinrutscht – anders als bei <code class="inline">$list[] = $irgendwas</code>.</p>
<h2>Wie ein Array benutzbar machen</h2>
<p>Mit den SPL-Interfaces <code class="inline">Countable</code> und <code class="inline">IteratorAggregate</code> lässt sich die Collection mit <code class="inline">count()</code> zählen und mit <code class="inline">foreach</code> durchlaufen:</p>
<pre><span class="k">class</span> <span class="t">Names</span> <span class="k">implements</span> <span class="t">\Countable</span>, <span class="t">\IteratorAggregate</span> {
<span class="k">public function</span> <span class="f">__construct</span>(<span class="k">private</span> <span class="t">array</span> <span class="v">$items</span> = []) {}
<span class="k">public function</span> <span class="f">count</span>(): <span class="t">int</span> { <span class="k">return</span> <span class="k">count</span>(<span class="v">$this</span>-><span class="v">items</span>); }
<span class="k">public function</span> <span class="f">getIterator</span>(): <span class="t">\Iterator</span> { <span class="k">return</span> <span class="k">new</span> <span class="t">\ArrayIterator</span>(<span class="v">$this</span>-><span class="v">items</span>); }
}
<span class="v">$n</span> = <span class="k">new</span> <span class="t">Names</span>([<span class="s">"Anna"</span>, <span class="s">"Ben"</span>]);
<span class="k">echo</span> <span class="k">count</span>(<span class="v">$n</span>); <span class="c">// 2</span>
<span class="k">foreach</span> (<span class="v">$n</span> <span class="k">as</span> <span class="v">$name</span>) <span class="k">echo</span> <span class="v">$name</span>; <span class="c">// AnnaBen</span></pre>
<h2>„Generics" in PHP</h2>
<p>Echte Generics (wie <code class="inline">List<Artikel></code>) kennt PHP zur Laufzeit nicht. Den gewünschten Effekt erreichst du über getypte Collection-Klassen wie oben – und über DocBlock-Annotationen, die statische Analyse-Werkzeuge auswerten:</p>
<pre><span class="c">/** @var array<int, Artikel> $article */</span>
<span class="k">private</span> <span class="t">array</span> <span class="v">$article</span> = [];</pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Eigene Collection statt nacktem Array</b>Sobald ein Array immer denselben Objekttyp hält und feste Operationen darauf laufen (summieren, filtern, suchen), lohnt sich eine eigene Klasse. Sie dokumentiert die Absicht, erzwingt den Typ und bietet sprechende Methoden statt verstreuter Array-Funktionen.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 29</span><h1>Iteratoren & Generatoren</h1></div><p class="lead">Iteratoren machen beliebige Objekte mit <code class="inline">foreach</code> durchlaufbar. Generatoren erzeugen Werte <i>bei Bedarf</i> – ideal für große oder unendliche Folgen, ohne alles im Speicher zu halten.</p>
<h2>Das Iterator-Interface</h2>
<p>Wer die fünf Methoden erfüllt, ist durchlaufbar. In der Praxis nimmt man dafür meist Generatoren (gleich), aber so sieht das Grundprinzip aus:</p>
<pre><span class="k">class</span> <span class="t">Numbers</span> <span class="k">implements</span> <span class="t">\Iterator</span> {
<span class="k">private</span> <span class="t">int</span> <span class="v">$i</span> = <span class="s">0</span>;
<span class="k">public function</span> <span class="f">__construct</span>(<span class="k">private</span> <span class="t">int</span> <span class="v">$limit</span>) {}
<span class="k">public function</span> <span class="f">current</span>(): <span class="t">mixed</span> { <span class="k">return</span> <span class="v">$this</span>-><span class="v">i</span>; }
<span class="k">public function</span> <span class="f">key</span>(): <span class="t">mixed</span> { <span class="k">return</span> <span class="v">$this</span>-><span class="v">i</span>; }
<span class="k">public function</span> <span class="f">next</span>(): <span class="t">void</span> { <span class="v">$this</span>-><span class="v">i</span>++; }
<span class="k">public function</span> <span class="f">rewind</span>(): <span class="t">void</span> { <span class="v">$this</span>-><span class="v">i</span> = <span class="s">0</span>; }
<span class="k">public function</span> <span class="f">valid</span>(): <span class="t">bool</span> { <span class="k">return</span> <span class="v">$this</span>-><span class="v">i</span> < <span class="v">$this</span>-><span class="v">limit</span>; }
}
<span class="k">foreach</span> (<span class="k">new</span> <span class="t">Numbers</span>(<span class="s">3</span>) <span class="k">as</span> <span class="v">$n</span>) <span class="k">echo</span> <span class="v">$n</span>; <span class="c">// 012</span></pre>
<h2>Generatoren mit yield</h2>
<p>Ein <code class="inline">yield</code> macht aus einer Funktion einen Generator: Sie liefert Werte einzeln, pausiert dazwischen und merkt sich ihren Stand. Dasselbe wie oben, aber drastisch kürzer:</p>
<pre><span class="k">function</span> <span class="f">numbers</span>(<span class="t">int</span> <span class="v">$limit</span>): <span class="t">\Generator</span> {
<span class="k">for</span> (<span class="v">$i</span> = <span class="s">0</span>; <span class="v">$i</span> < <span class="v">$limit</span>; <span class="v">$i</span>++) {
<span class="k">yield</span> <span class="v">$i</span>;
}
}
<span class="k">foreach</span> (<span class="f">numbers</span>(<span class="s">3</span>) <span class="k">as</span> <span class="v">$n</span>) <span class="k">echo</span> <span class="v">$n</span>; <span class="c">// 012</span></pre>
<h2>Schlüssel mitliefern</h2>
<pre><span class="k">function</span> <span class="f">pairs</span>(): <span class="t">\Generator</span> {
<span class="k">yield</span> <span class="s">"a"</span> => <span class="s">1</span>;
<span class="k">yield</span> <span class="s">"b"</span> => <span class="s">2</span>;
}
<span class="k">foreach</span> (<span class="f">pairs</span>() <span class="k">as</span> <span class="v">$k</span> => <span class="v">$v</span>) <span class="k">echo</span> <span class="s">"$k=$v "</span>; <span class="c">// a=1 b=2</span></pre>
<h2>Faulheit spart Speicher</h2>
<p>Der entscheidende Vorteil: Ein Generator hält immer nur den aktuellen Wert – so liest man riesige Dateien Zeile für Zeile, ohne sie komplett zu laden:</p>
<pre><span class="k">function</span> <span class="f">lines</span>(<span class="t">string</span> <span class="v">$file</span>): <span class="t">\Generator</span> {
<span class="v">$f</span> = <span class="k">fopen</span>(<span class="v">$file</span>, <span class="s">"r"</span>);
<span class="k">while</span> ((<span class="v">$z</span> = <span class="k">fgets</span>(<span class="v">$f</span>)) !== <span class="k">false</span>) {
<span class="k">yield</span> <span class="k">rtrim</span>(<span class="v">$z</span>); <span class="c">// nur eine Zeile zur Zeit im Speicher</span>
}
<span class="k">fclose</span>(<span class="v">$f</span>);
}</pre>
<h2>yield from</h2>
<p>Delegiert an einen anderen Generator oder ein iterierbares:</p>
<pre><span class="k">function</span> <span class="f">all</span>(): <span class="t">\Generator</span> {
<span class="k">yield</span> <span class="s">0</span>;
<span class="k">yield from</span> [<span class="s">1</span>, <span class="s">2</span>]; <span class="c">// fügt deren Werte ein</span>
}
<span class="k">echo</span> <span class="k">implode</span>(<span class="s">","</span>, <span class="k">iterator_to_array</span>(<span class="f">all</span>())); <span class="c">// 0,1,2</span></pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Generatoren für große Datenmengen</b>Verarbeitest du Dateien, Datenbank-Ergebnisse oder Streams, die nicht komplett in den Speicher passen, sind Generatoren das Mittel der Wahl. Du iterierst wie über ein Array, hältst aber nur ein Element zur Zeit.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 30</span><h1>Closures & Bindung</h1></div><p class="lead">Eine Closure ist eine Funktion als Wert: Du kannst sie einer Variablen zuweisen, herumreichen und später aufrufen. Sie kann sich Werte aus ihrer Umgebung „merken".</p>
<h2>Closure als Wert</h2>
<pre><span class="v">$greet</span> = <span class="k">function</span>(<span class="t">string</span> <span class="v">$name</span>): <span class="t">string</span> {
<span class="k">return</span> <span class="s">"Hallo, $name"</span>;
};
<span class="k">echo</span> <span class="v">$greet</span>(<span class="s">"Anna"</span>); <span class="c">// Hallo, Anna</span></pre>
<h2>use</h2>
<p>Mit <code class="inline">use</code> holt eine Closure Variablen von außen herein – standardmäßig als Kopie:</p>
<pre><span class="v">$factor</span> = <span class="s">3</span>;
<span class="v">$times</span> = <span class="k">function</span>(<span class="t">int</span> <span class="v">$n</span>) <span class="k">use</span> (<span class="v">$factor</span>) {
<span class="k">return</span> <span class="v">$n</span> * <span class="v">$factor</span>;
};
<span class="k">echo</span> <span class="v">$times</span>(<span class="s">5</span>); <span class="c">// 15</span></pre>
<p>Mit <code class="inline">&</code> bindest du per Referenz – Änderungen wirken zurück:</p>
<pre><span class="v">$sum</span> = <span class="s">0</span>;
<span class="v">$add</span> = <span class="k">function</span>(<span class="t">int</span> <span class="v">$n</span>) <span class="k">use</span> (&<span class="v">$sum</span>) { <span class="v">$sum</span> += <span class="v">$n</span>; };
<span class="v">$add</span>(<span class="s">5</span>); <span class="v">$add</span>(<span class="s">3</span>);
<span class="k">echo</span> <span class="v">$sum</span>; <span class="c">// 8</span></pre>
<h2>Pfeilfunktionen fangen automatisch ein</h2>
<p>Eine Arrow-Funktion (<code class="inline">fn</code>) übernimmt die Umgebung von selbst – kein <code class="inline">use</code> nötig:</p>
<pre><span class="v">$factor</span> = <span class="s">3</span>;
<span class="v">$times</span> = <span class="k">fn</span>(<span class="t">int</span> <span class="v">$n</span>) => <span class="v">$n</span> * <span class="v">$factor</span>; <span class="c">// $factor automatisch</span>
<span class="k">echo</span> <span class="v">$times</span>(<span class="s">5</span>); <span class="c">// 15</span></pre>
<h2>Als Argument übergeben</h2>
<p>Closures sind das, was viele Array-Funktionen erwarten:</p>
<pre><span class="k">print_r</span>(<span class="k">array_map</span>(<span class="k">fn</span>(<span class="v">$n</span>) => <span class="v">$n</span> ** <span class="s">2</span>, [<span class="s">1</span>, <span class="s">2</span>, <span class="s">3</span>])); <span class="c">// [1, 4, 9]</span></pre>
<h2>$this binden</h2>
<p>Eine Closure kann an ein Objekt gebunden werden und dann auf dessen Interna zugreifen:</p>
<pre><span class="k">class</span> <span class="t">Counter</span> { <span class="k">private</span> <span class="t">int</span> <span class="v">$n</span> = <span class="s">10</span>; }
<span class="v">$read</span> = <span class="k">Closure</span>::<span class="f">bind</span>(<span class="k">fn</span>() => <span class="v">$this</span>-><span class="v">n</span>, <span class="k">new</span> <span class="t">Counter</span>(), <span class="t">Counter</span>::<span class="k">class</span>);
<span class="k">echo</span> <span class="v">$read</span>(); <span class="c">// 10</span></pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>fn oder function?</b>Nimm die kurze <code class="inline">fn</code>-Form für knappe Einzeiler, die nur Umgebungswerte lesen. Brauchst du mehrere Anweisungen oder Bindung per Referenz, ist die ausführliche <code class="inline">function() use(...)</code>-Form richtig.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 31</span><h1>Attribute</h1></div><p class="lead">Attribute (seit PHP 8) hängen strukturierte Metadaten direkt an Klassen, Methoden, Eigenschaften oder Parameter – maschinenlesbar, statt in Kommentaren versteckt. Frameworks nutzen sie für Routing, Validierung, ORM-Mappings und mehr.</p>
<h2>Eingebaute Attribute</h2>
<p><code class="inline">#[\Override]</code> sagt aus, dass eine Methode bewusst überschreibt – PHP meldet einen Fehler, wenn es in der Basis nichts zu überschreiben gibt:</p>
<pre><span class="k">class</span> <span class="t">Base</span> { <span class="k">public function</span> <span class="f">run</span>(): <span class="t">void</span> {} }
<span class="k">class</span> <span class="t">Child</span> <span class="k">extends</span> <span class="t">Base</span> {
<span class="a">#[\Override]</span>
<span class="k">public function</span> <span class="f">run</span>(): <span class="t">void</span> {} <span class="c">// abgesichert: tippt man "luaf", gibt es einen Fehler</span>
}</pre>
<p>Weitere eingebaute: <code class="inline">#[\Deprecated]</code> markiert Veraltetes (mit Warnung bei Nutzung) und – neu in PHP 8.5 – <code class="inline">#[\NoDiscard]</code> warnt, wenn ein wichtiger Rückgabewert ignoriert wird.</p>
<h2>Ein eigenes Attribut</h2>
<p>Ein Attribut ist eine Klasse mit dem Attribut <code class="inline">#[Attribute]</code>. Sein Konstruktor nimmt die Werte entgegen:</p>
<pre><span class="a">#[\Attribute]</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</span> <span class="t">string</span> <span class="v">$path</span>) {}
}
<span class="k">class</span> <span class="t">StartController</span> {
<span class="a">#[Route(</span><span class="s">"/start"</span><span class="a">)]</span>
<span class="k">public function</span> <span class="f">index</span>(): <span class="t">string</span> { <span class="k">return</span> <span class="s">"Startseite"</span>; }
}</pre>
<p>Bis hierher ist nichts „passiert" – das Attribut steht nur am Code. Lebendig wird es erst, wenn jemand es ausliest.</p>
<h2>Attribute auslesen</h2>
<p>Per Reflection (nächstes Kapitel) holst du die Metadaten zur Laufzeit – so liest ein Router seine Pfade:</p>
<pre><span class="v">$m</span> = <span class="k">new</span> <span class="t">\ReflectionMethod</span>(<span class="t">StartController</span>::<span class="k">class</span>, <span class="s">"index"</span>);
<span class="k">foreach</span> (<span class="v">$m</span>-><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>-><span class="f">newInstance</span>(); <span class="c">// erzeugt das Route-Objekt</span>
<span class="k">echo</span> <span class="v">$route</span>-><span class="v">path</span>; <span class="c">// /start</span>
}</pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Metadaten statt Konfiguration</b>Attribute halten Konfiguration dort, wo sie wirkt – direkt am Code. Statt eine Route in einer separaten Datei zu pflegen, steht sie an der Methode. Genau so arbeiten moderne Frameworks; du selbst brauchst eigene Attribute selten, begegnest ihnen aber überall.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 32</span><h1>Reflection</h1></div><p class="lead">Reflection erlaubt es, zur Laufzeit in Klassen hineinzuschauen: Welche Methoden, Eigenschaften, Parameter und Attribute haben sie? Darauf bauen DI-Container, ORMs, Test- und Serialisierungs-Bibliotheken auf.</p>
<h2>Eine Klasse untersuchen</h2>
<pre><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</span> <span class="t">string</span> <span class="v">$name</span>, <span class="k">private</span> <span class="t">int</span> <span class="v">$age</span>) {}
<span class="k">public function</span> <span class="f">greet</span>(): <span class="t">string</span> { <span class="k">return</span> <span class="s">"Hi $this->name"</span>; }
}
<span class="v">$r</span> = <span class="k">new</span> <span class="t">\ReflectionClass</span>(<span class="t">User</span>::<span class="k">class</span>);
<span class="k">foreach</span> (<span class="v">$r</span>-><span class="f">getMethods</span>() <span class="k">as</span> <span class="v">$m</span>) {
<span class="k">echo</span> <span class="v">$m</span>-><span class="f">getName</span>() . <span class="s">" "</span>; <span class="c">// __construct begruessen</span>
}</pre>
<h2>Parameter und Typen lesen</h2>
<p>Das ist der Kern eines DI-Containers: Welche Typen braucht der Konstruktor?</p>
<pre><span class="v">$ctor</span> = (<span class="k">new</span> <span class="t">\ReflectionClass</span>(<span class="t">User</span>::<span class="k">class</span>))-><span class="f">getConstructor</span>();
<span class="k">foreach</span> (<span class="v">$ctor</span>-><span class="f">getParameters</span>() <span class="k">as</span> <span class="v">$p</span>) {
<span class="k">echo</span> <span class="v">$p</span>-><span class="f">getName</span>() . <span class="s">":"</span> . <span class="v">$p</span>-><span class="f">getType</span>() . <span class="s">" "</span>; <span class="c">// name:string alter:int</span>
}</pre>
<h2>Objekte ohne new erzeugen</h2>
<p>Reflection kann Instanzen mit dynamischen Argumenten bauen – die Grundlage automatischer Verdrahtung:</p>
<pre><span class="v">$obj</span> = (<span class="k">new</span> <span class="t">\ReflectionClass</span>(<span class="t">User</span>::<span class="k">class</span>))
-><span class="f">newInstanceArgs</span>([<span class="s">"Anna"</span>, <span class="s">30</span>]);
<span class="k">echo</span> <span class="v">$obj</span>-><span class="f">greet</span>(); <span class="c">// Hi Anna</span></pre>
<h2>Auf Privates zugreifen</h2>
<p>Für Tests und Serialisierung lässt sich (mit Bedacht) auch Verborgenes lesen:</p>
<pre><span class="v">$prop</span> = <span class="k">new</span> <span class="t">\ReflectionProperty</span>(<span class="t">User</span>::<span class="k">class</span>, <span class="s">"alter"</span>);
<span class="k">echo</span> <span class="v">$prop</span>-><span class="f">getValue</span>(<span class="v">$obj</span>); <span class="c">// 30</span></pre>
<h2>Ein Mini-Container</h2>
<p>Reflection und Attribute zusammen ergeben automatische Objekt-Erzeugung – stark vereinfacht:</p>
<pre><span class="k">function</span> <span class="f">create</span>(<span class="t">string</span> <span class="v">$className</span>): <span class="t">object</span> {
<span class="v">$ctor</span> = (<span class="k">new</span> <span class="t">\ReflectionClass</span>(<span class="v">$className</span>))-><span class="f">getConstructor</span>();
<span class="v">$args</span> = <span class="k">array_map</span>(
<span class="k">fn</span>(<span class="v">$p</span>) => <span class="f">create</span>((<span class="t">string</span>) <span class="v">$p</span>-><span class="f">getType</span>()), <span class="c">// rekursiv Abhängigkeiten bauen</span>
<span class="v">$ctor</span> ? <span class="v">$ctor</span>-><span class="f">getParameters</span>() : []
);
<span class="k">return</span> <span class="k">new</span> <span class="v">$className</span>(...<span class="v">$args</span>);
}</pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Mächtig, aber langsam und indirekt</b>Reflection ist langsamer als direkter Code und umgeht die Kapselung. Sie ist das richtige Werkzeug für Frameworks und Werkzeuge, die generisch über fremde Klassen arbeiten – im normalen Anwendungscode brauchst du sie selten. Container cachen die Ergebnisse, um den Aufwand zu vermeiden.</div>
</div></section><section class="part-divider"><div class="part-kicker">Teil 7</div><h1>Die Standardbibliothek</h1><div class="part-desc">PHP bringt enorm viel ab Werk mit: Zahlen, Datum und Zeit, das Dateisystem, Streams und die SPL-Datenstrukturen.</div><div class="part-chapters"><span>33 · Mathematik & Zahlen</span><span>34 · Datum & Zeit</span><span>35 · Dateien & Dateisystem</span><span>36 · Streams & Filter</span><span>37 · SPL-Datenstrukturen</span></div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 33</span><h1>Mathematik & Zahlen</h1></div><p class="lead">PHP bringt für jede Rechenaufgabe eine Funktion mit – und für Geldbeträge einen wichtigen Sonderweg. Hier die Grundfunktionen, jede mit Beispiel.</p>
<h2>Runden & Ganzzahlen</h2>
<pre><span class="k">echo</span> <span class="k">abs</span>(-<span class="s">7</span>); <span class="c">// 7 Betrag</span>
<span class="k">echo</span> <span class="k">round</span>(<span class="s">3.14159</span>, <span class="s">2</span>); <span class="c">// 3.14 kaufmännisch runden</span>
<span class="k">echo</span> <span class="k">floor</span>(<span class="s">4.7</span>); <span class="c">// 4 abrunden</span>
<span class="k">echo</span> <span class="k">ceil</span>(<span class="s">4.2</span>); <span class="c">// 5 aufrunden</span>
<span class="k">echo</span> <span class="k">intdiv</span>(<span class="s">17</span>, <span class="s">5</span>); <span class="c">// 3 Ganzzahldivision</span>
<span class="k">echo</span> <span class="s">17</span> % <span class="s">5</span>; <span class="c">// 2 Rest dazu</span></pre>
<h2>Potenz & Wurzel</h2>
<pre><span class="k">echo</span> <span class="k">pow</span>(<span class="s">2</span>, <span class="s">10</span>); <span class="c">// 1024</span>
<span class="k">echo</span> <span class="s">2</span> ** <span class="s">10</span>; <span class="c">// 1024 (gleichbedeutend)</span>
<span class="k">echo</span> <span class="k">sqrt</span>(<span class="s">144</span>); <span class="c">// 12</span></pre>
<h2>Größtes, Kleinstes, Zufall</h2>
<pre><span class="k">echo</span> <span class="k">max</span>(<span class="s">3</span>, <span class="s">9</span>, <span class="s">5</span>); <span class="c">// 9</span>
<span class="k">echo</span> <span class="k">min</span>([<span class="s">4</span>, <span class="s">1</span>, <span class="s">8</span>]); <span class="c">// 1 (auch mit Array)</span>
<span class="k">echo</span> <span class="k">random_int</span>(<span class="s">1</span>, <span class="s">6</span>); <span class="c">// kryptografisch sichere Zufallszahl 1–6</span></pre>
<p>Für Zufall, auf den es ankommt (Tokens, Passwörter), immer <code class="inline">random_int</code> bzw. <code class="inline">random_bytes</code> statt des alten <code class="inline">rand</code>.</p>
<h2>Zahlen formatieren</h2>
<pre><span class="k">echo</span> <span class="k">number_format</span>(<span class="s">1234567.5</span>, <span class="s">2</span>, <span class="s">","</span>, <span class="s">"."</span>); <span class="c">// 1.234.567,50</span></pre>
<h2>Die Tücke der Fließkommazahlen</h2>
<p>Floats speichern Dezimalzahlen nicht exakt – ein Problem in jeder Sprache:</p>
<pre><span class="k">var_dump</span>(<span class="s">0.1</span> + <span class="s">0.2</span> === <span class="s">0.3</span>); <span class="c">// false!</span>
<span class="c">// 0.1 + 0.2 ergibt 0.30000000000000004</span></pre>
<p>Vergleiche Floats deshalb nie direkt, sondern über eine kleine Toleranz:</p>
<pre><span class="v">$equal</span> = <span class="k">abs</span>((<span class="s">0.1</span> + <span class="s">0.2</span>) - <span class="s">0.3</span>) < <span class="s">0.00001</span>;
<span class="k">var_dump</span>(<span class="v">$equal</span>); <span class="c">// true</span></pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Geld nie als Float</b>Rechne Geldbeträge in der kleinsten Einheit als <code class="inline">int</code> (Cent), nicht als Float – sonst summieren sich Rundungsfehler. <code class="inline">1999</code> Cent statt <code class="inline">19.99</code> Euro. Zur Anzeige teilst du am Ende durch 100 und formatierst mit <code class="inline">number_format</code>.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 34</span><h1>Datum & Zeit</h1></div><p class="lead">Zeit ist tückisch: Zeitzonen, Sommerzeit, Schaltjahre. PHP nimmt dir das mit den DateTime-Klassen ab – arbeite mit Objekten statt mit rohen Zeitstempeln.</p>
<h2>Zeitstempel & date()</h2>
<pre><span class="k">echo</span> <span class="k">time</span>(); <span class="c">// Sekunden seit 1970 (Unix-Zeitstempel)</span>
<span class="k">echo</span> <span class="k">date</span>(<span class="s">"d.m.Y H:i"</span>); <span class="c">// z. B. 31.05.2026 14:30</span></pre>
<p>Die Formatzeichen sind zahlreich (<code class="inline">Y</code> Jahr, <code class="inline">m</code> Monat, <code class="inline">d</code> Tag, <code class="inline">H:i:s</code> Zeit …); die vollständige Liste steht in der PHP-Doku.</p>
<h2>DateTimeImmutable</h2>
<p>Unveränderliche Datumsobjekte: Jede Änderung liefert ein neues Objekt, das Original bleibt unangetastet – das verhindert eine ganze Klasse von Fehlern:</p>
<pre><span class="v">$now</span> = <span class="k">new</span> <span class="t">\DateTimeImmutable</span>(<span class="s">"2026-05-31 10:00"</span>);
<span class="v">$later</span> = <span class="v">$now</span>-><span class="f">modify</span>(<span class="s">"+2 days"</span>);
<span class="k">echo</span> <span class="v">$now</span>-><span class="f">format</span>(<span class="s">"d.m."</span>); <span class="c">// 31.05.</span>
<span class="k">echo</span> <span class="v">$later</span>-><span class="f">format</span>(<span class="s">"d.m."</span>); <span class="c">// 02.06. (Original unverändert)</span></pre>
<h2>Aus einem Format einlesen</h2>
<pre><span class="v">$d</span> = <span class="t">\DateTimeImmutable</span>::<span class="f">createFromFormat</span>(<span class="s">"d.m.Y"</span>, <span class="s">"24.12.2026"</span>);
<span class="k">echo</span> <span class="v">$d</span>-><span class="f">format</span>(<span class="s">"Y-m-d"</span>); <span class="c">// 2026-12-24</span></pre>
<h2>Differenzen mit diff()</h2>
<p>Liefert ein <code class="inline">DateInterval</code> mit Tagen, Stunden usw.:</p>
<pre><span class="v">$a</span> = <span class="k">new</span> <span class="t">\DateTimeImmutable</span>(<span class="s">"2026-01-01"</span>);
<span class="v">$b</span> = <span class="k">new</span> <span class="t">\DateTimeImmutable</span>(<span class="s">"2026-12-31"</span>);
<span class="v">$diff</span> = <span class="v">$a</span>-><span class="f">diff</span>(<span class="v">$b</span>);
<span class="k">echo</span> <span class="v">$diff</span>-><span class="v">days</span>; <span class="c">// 364</span>
<span class="k">echo</span> <span class="v">$diff</span>-><span class="f">format</span>(<span class="s">"%m Monate"</span>); <span class="c">// 11 Monate</span></pre>
<h2>Vergleichen & Rechnen</h2>
<pre><span class="k">var_dump</span>(<span class="v">$a</span> < <span class="v">$b</span>); <span class="c">// true – Datumsobjekte sind direkt vergleichbar</span>
<span class="v">$deadline</span> = <span class="v">$a</span>-><span class="f">add</span>(<span class="k">new</span> <span class="t">\DateInterval</span>(<span class="s">"P30D"</span>)); <span class="c">// + 30 Tage</span></pre>
<h2>Zeitzonen</h2>
<pre><span class="v">$tz</span> = <span class="k">new</span> <span class="t">\DateTimeZone</span>(<span class="s">"Europe/Berlin"</span>);
<span class="v">$d</span> = <span class="k">new</span> <span class="t">\DateTimeImmutable</span>(<span class="s">"now"</span>, <span class="v">$tz</span>);
<span class="k">echo</span> <span class="v">$d</span>-><span class="f">format</span>(<span class="s">"H:i e"</span>); <span class="c">// Uhrzeit samt Zone</span></pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Immutable + UTC</b>Nimm <code class="inline">DateTimeImmutable</code> statt <code class="inline">DateTime</code> – so kann dir kein Aufruf unbemerkt das Datum „unter den Füßen" ändern. Speichere Zeiten intern und in der Datenbank in UTC und rechne erst zur Anzeige in die lokale Zone um. Für komfortablere APIs ist die Bibliothek Carbon verbreitet.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 35</span><h1>Dateien & Dateisystem</h1></div><p class="lead">PHP liest und schreibt Dateien, legt Verzeichnisse an und durchsucht sie. Für kleine Dateien gibt es bequeme Einzeiler, für große den Strom-orientierten Weg.</p>
<h2>Ganze Datei lesen & schreiben</h2>
<pre><span class="k">file_put_contents</span>(<span class="s">"notiz.txt"</span>, <span class="s">"Hallo\n"</span>); <span class="c">// schreibt (überschreibt)</span>
<span class="k">file_put_contents</span>(<span class="s">"notiz.txt"</span>, <span class="s">"mehr\n"</span>, <span class="t">FILE_APPEND</span>); <span class="c">// hängt an</span>
<span class="k">echo</span> <span class="k">file_get_contents</span>(<span class="s">"notiz.txt"</span>); <span class="c">// gesamten Inhalt lesen</span>
<span class="k">print_r</span>(<span class="k">file</span>(<span class="s">"notiz.txt"</span>)); <span class="c">// als Array von Zeilen</span></pre>
<h2>Zeilenweise für große Dateien</h2>
<p>Statt alles in den Speicher zu laden, öffnest du die Datei und liest Zeile für Zeile:</p>
<pre><span class="v">$f</span> = <span class="k">fopen</span>(<span class="s">"gross.log"</span>, <span class="s">"r"</span>);
<span class="k">while</span> ((<span class="v">$line</span> = <span class="k">fgets</span>(<span class="v">$f</span>)) !== <span class="k">false</span>) {
<span class="c">// $line verarbeiten ...</span>
}
<span class="k">fclose</span>(<span class="v">$f</span>);</pre>
<h2>Existenz & Eigenschaften prüfen</h2>
<pre><span class="k">var_dump</span>(<span class="k">file_exists</span>(<span class="s">"notiz.txt"</span>)); <span class="c">// true</span>
<span class="k">var_dump</span>(<span class="k">is_dir</span>(<span class="s">"/tmp"</span>)); <span class="c">// true</span>
<span class="k">echo</span> <span class="k">filesize</span>(<span class="s">"notiz.txt"</span>); <span class="c">// Größe in Bytes</span></pre>
<h2>Pfade zerlegen</h2>
<pre><span class="v">$p</span> = <span class="s">"/var/www/config.php"</span>;
<span class="k">echo</span> <span class="k">dirname</span>(<span class="v">$p</span>); <span class="c">// /var/www</span>
<span class="k">echo</span> <span class="k">basename</span>(<span class="v">$p</span>); <span class="c">// config.php</span>
<span class="k">echo</span> <span class="k">pathinfo</span>(<span class="v">$p</span>)[<span class="s">"extension"</span>]; <span class="c">// php</span>
<span class="k">echo</span> <span class="k">realpath</span>(<span class="s">"./../config.php"</span>); <span class="c">// löst .. zu absolutem Pfad auf</span></pre>
<h2>Verzeichnisse</h2>
<pre><span class="k">mkdir</span>(<span class="s">"daten/cache"</span>, <span class="s">0755</span>, <span class="k">true</span>); <span class="c">// rekursiv anlegen</span>
<span class="k">print_r</span>(<span class="k">glob</span>(<span class="s">"daten/*.txt"</span>)); <span class="c">// alle .txt per Muster</span>
<span class="k">unlink</span>(<span class="s">"notiz.txt"</span>); <span class="c">// Datei löschen</span></pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Fehler behandeln & Pfade prüfen</b><code class="inline">fopen</code> & Co. liefern bei Problemen <code class="inline">false</code> oder werfen Warnungen – prüfe die Rückgabe. Baue Pfade nie ungeprüft aus Benutzereingaben (Gefahr von „Path Traversal" mit <code class="inline">../</code>); validiere mit <code class="inline">realpath</code> gegen ein erlaubtes Basisverzeichnis. Schließe geöffnete Handles immer wieder.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 36</span><h1>Streams & Filter</h1></div><p class="lead">Hinter Dateien, Netzwerk und sogar Speicher steckt in PHP ein einheitliches Konzept: der Stream. Dieselben Funktionen (<code class="inline">fopen</code>, <code class="inline">fread</code> …) arbeiten auf allen, nur die „Adresse" (der Wrapper) unterscheidet sich.</p>
<h2>Wrapper</h2>
<p>Ein Präfix wie <code class="inline">file://</code>, <code class="inline">php://</code>, <code class="inline">http://</code> oder <code class="inline">data://</code> bestimmt die Quelle. <code class="inline">data://</code> trägt den Inhalt direkt im Pfad:</p>
<pre><span class="k">echo</span> <span class="k">file_get_contents</span>(<span class="s">"data://text/plain,Hallo Welt"</span>); <span class="c">// Hallo Welt</span></pre>
<h2>php://memory</h2>
<p>Ein Stream, der seine Daten nur im Arbeitsspeicher hält – praktisch als Puffer oder in Tests:</p>
<pre><span class="v">$f</span> = <span class="k">fopen</span>(<span class="s">"php://memory"</span>, <span class="s">"r+"</span>);
<span class="k">fwrite</span>(<span class="v">$f</span>, <span class="s">"zwischenspeicher"</span>);
<span class="k">rewind</span>(<span class="v">$f</span>);
<span class="k">echo</span> <span class="k">stream_get_contents</span>(<span class="v">$f</span>); <span class="c">// zwischenspeicher</span></pre>
<h2>Standard-Ein-/Ausgabe im CLI</h2>
<pre><span class="k">fwrite</span>(<span class="t">STDOUT</span>, <span class="s">"normale Ausgabe\n"</span>);
<span class="k">fwrite</span>(<span class="t">STDERR</span>, <span class="s">"Fehler-Ausgabe\n"</span>);
<span class="v">$input</span> = <span class="k">fgets</span>(<span class="t">STDIN</span>); <span class="c">// liest eine Zeile von Tastatur/Pipe</span></pre>
<h2>Netzwerk transparent</h2>
<p>Mit dem <code class="inline">http://</code>-Wrapper liest sich eine URL wie eine Datei (sofern <code class="inline">allow_url_fopen</code> aktiv ist):</p>
<pre><span class="v">$content</span> = <span class="k">file_get_contents</span>(<span class="s">"https://example.com"</span>); <span class="c">// HTML der Seite</span></pre>
<h2>Stream-Kontext</h2>
<p>Ein Kontext gibt dem Wrapper Optionen mit – etwa HTTP-Header oder eine POST-Anfrage:</p>
<pre><span class="v">$ctx</span> = <span class="k">stream_context_create</span>([
<span class="s">"http"</span> => [<span class="s">"method"</span> => <span class="s">"POST"</span>, <span class="s">"header"</span> => <span class="s">"Content-Type: application/json"</span>,
<span class="s">"content"</span> => <span class="s">'{"x":1}'</span>],
]);
<span class="v">$response</span> = <span class="k">file_get_contents</span>(<span class="s">"https://api.example.com"</span>, <span class="k">false</span>, <span class="v">$ctx</span>);</pre>
<h2>Filter</h2>
<p>Stream-Filter verändern Daten beim Lesen/Schreiben – z. B. Großschreibung:</p>
<pre><span class="v">$f</span> = <span class="k">fopen</span>(<span class="s">"php://memory"</span>, <span class="s">"r+"</span>);
<span class="k">stream_filter_append</span>(<span class="v">$f</span>, <span class="s">"string.toupper"</span>);
<span class="k">fwrite</span>(<span class="v">$f</span>, <span class="s">"hallo"</span>); <span class="k">rewind</span>(<span class="v">$f</span>);
<span class="k">echo</span> <span class="k">stream_get_contents</span>(<span class="v">$f</span>); <span class="c">// HALLO</span></pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Ein Konzept, viele Quellen</b>Weil Datei, Speicher und Netzwerk dieselbe Stream-Schnittstelle teilen, kannst du Code gegen „irgendeinen Strom" schreiben und ihn in Tests mit <code class="inline">php://memory</code> füttern statt mit echten Dateien. Für ernsthafte HTTP-Aufrufe nimmt man aber besser einen echten Client wie Guzzle (Web-Teil).</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 37</span><h1>SPL-Datenstrukturen</h1></div><p class="lead">Die Standard PHP Library (SPL) liefert fertige Datenstrukturen, die als Klassen daherkommen – Stapel, Schlangen, Prioritätswarteschlangen und mehr. Sie sind oft klarer und manchmal effizienter als ein nacktes Array.</p>
<h2>SplStack</h2>
<p>Zuletzt Hineingelegtes kommt zuerst heraus:</p>
<pre><span class="v">$s</span> = <span class="k">new</span> <span class="t">\SplStack</span>();
<span class="v">$s</span>-><span class="f">push</span>(<span class="s">"a"</span>);
<span class="v">$s</span>-><span class="f">push</span>(<span class="s">"b"</span>);
<span class="k">echo</span> <span class="v">$s</span>-><span class="f">pop</span>(); <span class="c">// b (zuletzt rein, zuerst raus)</span></pre>
<h2>SplQueue</h2>
<p>Eine Warteschlange: Zuerst Hineingelegtes kommt zuerst wieder heraus (FIFO):</p>
<pre><span class="v">$q</span> = <span class="k">new</span> <span class="t">\SplQueue</span>();
<span class="v">$q</span>-><span class="f">enqueue</span>(<span class="s">"erst"</span>);
<span class="v">$q</span>-><span class="f">enqueue</span>(<span class="s">"dann"</span>);
<span class="k">echo</span> <span class="v">$q</span>-><span class="f">dequeue</span>(); <span class="c">// erst (zuerst rein, zuerst raus)</span></pre>
<h2>SplPriorityQueue</h2>
<p>Gibt Elemente nicht nach Reihenfolge, sondern nach zugewiesener Priorität aus:</p>
<pre><span class="v">$p</span> = <span class="k">new</span> <span class="t">\SplPriorityQueue</span>();
<span class="v">$p</span>-><span class="f">insert</span>(<span class="s">"normal"</span>, <span class="s">1</span>);
<span class="v">$p</span>-><span class="f">insert</span>(<span class="s">"dringend"</span>, <span class="s">5</span>);
<span class="k">echo</span> <span class="v">$p</span>-><span class="f">extract</span>(); <span class="c">// dringend (höchste Priorität zuerst)</span></pre>
<h2>SplObjectStorage</h2>
<p>Speichert Objekte eindeutig und kann ihnen Daten zuordnen – ideal als Beobachter-Liste:</p>
<pre><span class="v">$store</span> = <span class="k">new</span> <span class="t">\SplObjectStorage</span>();
<span class="v">$a</span> = <span class="k">new</span> <span class="t">\stdClass</span>();
<span class="v">$store</span>-><span class="f">attach</span>(<span class="v">$a</span>, <span class="s">"info zu a"</span>);
<span class="k">var_dump</span>(<span class="v">$store</span>-><span class="f">contains</span>(<span class="v">$a</span>)); <span class="c">// true</span>
<span class="k">echo</span> <span class="v">$store</span>[<span class="v">$a</span>]; <span class="c">// info zu a</span></pre>
<h2>SplFixedArray</h2>
<p>Ein Array mit fester Länge und nur Ganzzahl-Indizes – sparsamer im Speicher bei großen, gleichförmigen Datenmengen:</p>
<pre><span class="v">$fix</span> = <span class="k">new</span> <span class="t">\SplFixedArray</span>(<span class="s">3</span>);
<span class="v">$fix</span>[<span class="s">0</span>] = <span class="s">"x"</span>;
<span class="k">echo</span> <span class="v">$fix</span>[<span class="s">0</span>]; <span class="c">// x</span>
<span class="k">echo</span> <span class="k">count</span>(<span class="v">$fix</span>); <span class="c">// 3</span></pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Wann SPL, wann Array?</b>Für die meisten Fälle reicht das gewöhnliche Array. Greife zur SPL, wenn die <i>Absicht</i> davon profitiert: Ein <code class="inline">SplStack</code> sagt „hier wird gestapelt", <code class="inline">SplObjectStorage</code> verwaltet Objektmengen sauber, und <code class="inline">SplPriorityQueue</code> erspart eigenes Sortieren.</div>
</div></section><section class="part-divider"><div class="part-kicker">Teil 8</div><h1>Datenformate & Serialisierung</h1><div class="part-desc">Daten verlassen dein Programm und kommen wieder herein – als JSON, XML, CSV oder PHP-eigene Serialisierung.</div><div class="part-chapters"><span>38 · JSON</span><span>39 · XML</span><span>40 · CSV & Tabellendaten</span><span>41 · serialize & JsonSerializable</span><span>42 · Weitere Formate: INI, YAML, Base64</span></div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 38</span><h1>JSON</h1></div><p class="lead">JSON ist das Standardformat für APIs und Konfiguration. PHP wandelt mit zwei Funktionen zwischen PHP-Werten und JSON hin und her.</p>
<h2>Kodieren mit json_encode</h2>
<pre><span class="v">$data</span> = [<span class="s">"name"</span> => <span class="s">"Anna"</span>, <span class="s">"aktiv"</span> => <span class="k">true</span>, <span class="s">"tags"</span> => [<span class="s">"a"</span>, <span class="s">"b"</span>]];
<span class="k">echo</span> <span class="k">json_encode</span>(<span class="v">$data</span>);
<span class="c">// {"name":"Anna","aktiv":true,"tags":["a","b"]}</span></pre>
<h2>Dekodieren mit json_decode</h2>
<p>Das zweite Argument <code class="inline">true</code> liefert ein assoziatives Array statt eines Objekts:</p>
<pre><span class="v">$arr</span> = <span class="k">json_decode</span>(<span class="s">'{"name":"Anna","alter":30}'</span>, <span class="k">true</span>);
<span class="k">echo</span> <span class="v">$arr</span>[<span class="s">"name"</span>]; <span class="c">// Anna</span>
<span class="v">$obj</span> = <span class="k">json_decode</span>(<span class="s">'{"name":"Anna"}'</span>);
<span class="k">echo</span> <span class="v">$obj</span>-><span class="v">name</span>; <span class="c">// Anna (als Objekt)</span></pre>
<h2>Optionen (Flags)</h2>
<p><code class="inline">JSON_PRETTY_PRINT</code> formatiert lesbar, <code class="inline">JSON_UNESCAPED_UNICODE</code> lässt Umlaute stehen, <code class="inline">JSON_UNESCAPED_SLASHES</code> maskiert Schrägstriche nicht:</p>
<pre><span class="k">echo</span> <span class="k">json_encode</span>([<span class="s">"ort"</span> => <span class="s">"Köln"</span>], <span class="t">JSON_UNESCAPED_UNICODE</span>);
<span class="c">// {"ort":"Köln"} (ohne Flag: \u00f6)</span>
<span class="k">echo</span> <span class="k">json_encode</span>([<span class="s">"url"</span> => <span class="s">"https://php.net"</span>], <span class="t">JSON_UNESCAPED_SLASHES</span>);
<span class="c">// {"url":"https://php.net"}</span></pre>
<h2>Fehler abfangen</h2>
<p>Mit <code class="inline">JSON_THROW_ON_ERROR</code> wirft ungültiges JSON eine Exception statt still <code class="inline">null</code> zu liefern:</p>
<pre><span class="k">try</span> {
<span class="v">$x</span> = <span class="k">json_decode</span>(<span class="s">"{kaputt}"</span>, <span class="k">true</span>, <span class="s">512</span>, <span class="t">JSON_THROW_ON_ERROR</span>);
} <span class="k">catch</span> (<span class="t">\JsonException</span> <span class="v">$e</span>) {
<span class="k">echo</span> <span class="s">"ungültiges JSON"</span>;
}</pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Immer mit THROW_ON_ERROR</b>Ohne dieses Flag liefert <code class="inline">json_decode</code> bei Fehlern <code class="inline">null</code> – nicht von echtem <code class="inline">null</code> zu unterscheiden. Setze es grundsätzlich, dann scheitern fehlerhafte Daten laut und früh statt leise und spät.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 39</span><h1>XML</h1></div><p class="lead">XML ist sperriger als JSON, aber in Altsystemen, Konfigurationen und Standards (SOAP, RSS) verbreitet. PHP bietet drei Wege – je nach Größe und Aufgabe.</p>
<h2>SimpleXML</h2>
<p>Für überschaubares XML der bequemste Weg: Knoten werden zu Objekteigenschaften:</p>
<pre><span class="v">$xml</span> = <span class="k">simplexml_load_string</span>(<span class="s">"<buch><titel>PHP</titel><jahr>2026</jahr></buch>"</span>);
<span class="k">echo</span> <span class="v">$xml</span>-><span class="v">title</span>; <span class="c">// PHP</span>
<span class="k">echo</span> <span class="v">$xml</span>-><span class="v">year</span>; <span class="c">// 2026</span></pre>
<p>Über mehrere gleichnamige Knoten iterierst du mit <code class="inline">foreach</code>:</p>
<pre><span class="v">$xml</span> = <span class="k">simplexml_load_string</span>(<span class="s">"<liste><e>a</e><e>b</e></liste>"</span>);
<span class="k">foreach</span> (<span class="v">$xml</span>-><span class="v">e</span> <span class="k">as</span> <span class="v">$e</span>) <span class="k">echo</span> <span class="v">$e</span>; <span class="c">// ab</span></pre>
<h2>DOMDocument</h2>
<p>Mächtiger: XML aufbauen oder mit XPath gezielt abfragen:</p>
<pre><span class="v">$dom</span> = <span class="k">new</span> <span class="t">\DOMDocument</span>();
<span class="v">$root</span> = <span class="v">$dom</span>-><span class="f">createElement</span>(<span class="s">"gruss"</span>, <span class="s">"Hallo"</span>);
<span class="v">$dom</span>-><span class="f">appendChild</span>(<span class="v">$root</span>);
<span class="k">echo</span> <span class="v">$dom</span>-><span class="f">saveXML</span>(); <span class="c">// <?xml ...><gruss>Hallo</gruss></span></pre>
<p>Mit XPath suchst du in vorhandenem XML:</p>
<pre><span class="v">$dom</span>-><span class="f">loadXML</span>(<span class="s">"<b><t>X</t><t>Y</t></b>"</span>);
<span class="v">$xpath</span> = <span class="k">new</span> <span class="t">\DOMXPath</span>(<span class="v">$dom</span>);
<span class="k">foreach</span> (<span class="v">$xpath</span>-><span class="f">query</span>(<span class="s">"//t"</span>) <span class="k">as</span> <span class="v">$n</span>) <span class="k">echo</span> <span class="v">$n</span>-><span class="v">nodeValue</span>; <span class="c">// XY</span></pre>
<h2>XMLReader</h2>
<p>Liest XML strömend, ohne alles in den Speicher zu laden – das Pendant zu Generatoren für XML:</p>
<pre><span class="v">$r</span> = <span class="k">new</span> <span class="t">\XMLReader</span>();
<span class="v">$r</span>-><span class="f">open</span>(<span class="s">"gross.xml"</span>);
<span class="k">while</span> (<span class="v">$r</span>-><span class="f">read</span>()) {
<span class="k">if</span> (<span class="v">$r</span>-><span class="v">nodeType</span> === <span class="t">\XMLReader</span>::<span class="t">ELEMENT</span> && <span class="v">$r</span>-><span class="v">name</span> === <span class="s">"eintrag"</span>) {
<span class="c">// einen Eintrag verarbeiten</span>
}
}</pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>XXE: externe Entitäten</b>Beim Einlesen fremden XML drohen „XML External Entity"-Angriffe (Zugriff auf lokale Dateien). Moderne PHP-Versionen laden externe Entitäten standardmäßig nicht mehr; lade trotzdem nie unkontrolliert fremdes XML mit aktivierter Entity-Auflösung.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 40</span><h1>CSV & Tabellendaten</h1></div><p class="lead">CSV ist das kleinste gemeinsame Format für Tabellen – jede Software liest es. PHP hat eingebaute Funktionen, die Feinheiten wie Anführungszeichen und Trennzeichen korrekt behandeln.</p>
<h2>Schreiben mit fputcsv</h2>
<p>Über einen Stream, Zeile für Zeile. Sonderzeichen und Trennzeichen in Werten werden automatisch korrekt maskiert:</p>
<pre><span class="v">$f</span> = <span class="k">fopen</span>(<span class="s">"export.csv"</span>, <span class="s">"w"</span>);
<span class="k">fputcsv</span>(<span class="v">$f</span>, [<span class="s">"Name"</span>, <span class="s">"Stadt"</span>]); <span class="c">// Kopfzeile</span>
<span class="k">fputcsv</span>(<span class="v">$f</span>, [<span class="s">"Anna"</span>, <span class="s">"Köln; Zentrum"</span>]); <span class="c">// Wert mit ; wird gequotet</span>
<span class="k">fclose</span>(<span class="v">$f</span>);</pre>
<h2>Lesen mit fgetcsv</h2>
<pre><span class="v">$f</span> = <span class="k">fopen</span>(<span class="s">"export.csv"</span>, <span class="s">"r"</span>);
<span class="k">while</span> ((<span class="v">$line</span> = <span class="k">fgetcsv</span>(<span class="v">$f</span>)) !== <span class="k">false</span>) {
<span class="k">print_r</span>(<span class="v">$line</span>); <span class="c">// ["Anna", "Köln; Zentrum"]</span>
}
<span class="k">fclose</span>(<span class="v">$f</span>);</pre>
<h2>Kopfzeile als Schlüssel nutzen</h2>
<p>Ein verbreitetes Muster: erste Zeile als Spaltennamen, danach jede Zeile als assoziatives Array:</p>
<pre><span class="v">$f</span> = <span class="k">fopen</span>(<span class="s">"export.csv"</span>, <span class="s">"r"</span>);
<span class="v">$header</span> = <span class="k">fgetcsv</span>(<span class="v">$f</span>);
<span class="k">while</span> ((<span class="v">$z</span> = <span class="k">fgetcsv</span>(<span class="v">$f</span>)) !== <span class="k">false</span>) {
<span class="v">$record</span> = <span class="k">array_combine</span>(<span class="v">$header</span>, <span class="v">$z</span>);
<span class="k">echo</span> <span class="v">$record</span>[<span class="s">"Name"</span>]; <span class="c">// Anna</span>
}</pre>
<h2>Einen einzelnen String parsen</h2>
<pre><span class="k">print_r</span>(<span class="k">str_getcsv</span>(<span class="s">'a,"b,c",d'</span>)); <span class="c">// ["a", "b,c", "d"]</span></pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Nie von Hand splitten</b>Zerlege CSV niemals mit <code class="inline">explode(",", …)</code> – das zerbricht an Werten, die selbst Kommas oder Zeilenumbrüche enthalten. Die <code class="inline">fgetcsv</code>/<code class="inline">str_getcsv</code>-Funktionen kennen die Quoting-Regeln. Für Excel-Dateien (.xlsx) nutzt man die Bibliothek PhpSpreadsheet.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 41</span><h1>serialize & JsonSerializable</h1></div><p class="lead">Manchmal willst du einen PHP-Wert komplett speichern und später unverändert zurückholen – mit Typen und Objektstruktur. Dafür gibt es PHPs eigene Serialisierung und Schnittstellen, um die JSON-Darstellung zu steuern.</p>
<h2>serialize & unserialize</h2>
<p>Wandeln beliebige PHP-Werte in einen String und zurück – inklusive Typen und Objekten:</p>
<pre><span class="v">$data</span> = [<span class="s">"n"</span> => <span class="s">42</span>, <span class="s">"aktiv"</span> => <span class="k">true</span>];
<span class="v">$str</span> = <span class="k">serialize</span>(<span class="v">$data</span>);
<span class="k">echo</span> <span class="v">$str</span>; <span class="c">// a:2:{s:1:"n";i:42;s:5:"aktiv";b:1;}</span>
<span class="v">$restored</span> = <span class="k">unserialize</span>(<span class="v">$str</span>);
<span class="k">var_dump</span>(<span class="v">$restored</span>[<span class="s">"n"</span>]); <span class="c">// int(42) – Typ erhalten</span></pre>
<h2>JsonSerializable</h2>
<p>Implementiert eine Klasse dieses Interface, bestimmt sie selbst, wie <code class="inline">json_encode</code> sie darstellt – etwa um Privates zu verbergen:</p>
<pre><span class="k">class</span> <span class="t">User</span> <span class="k">implements</span> <span class="t">\JsonSerializable</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">$name</span>,
<span class="k">private</span> <span class="t">string</span> <span class="v">$password</span>,
) {}
<span class="k">public function</span> <span class="f">jsonSerialize</span>(): <span class="t">array</span> {
<span class="k">return</span> [<span class="s">"name"</span> => <span class="v">$this</span>-><span class="v">name</span>]; <span class="c">// Passwort bewusst weglassen</span>
}
}
<span class="k">echo</span> <span class="k">json_encode</span>(<span class="k">new</span> <span class="t">User</span>(<span class="s">"Anna"</span>, <span class="s">"geheim"</span>)); <span class="c">// {"name":"Anna"}</span></pre>
<h2>__serialize & __unserialize</h2>
<p>Steuern, was bei PHPs <code class="inline">serialize</code> gespeichert und wie es wiederhergestellt wird – z. B. um eine offene Verbindung beim Speichern auszulassen:</p>
<pre><span class="k">class</span> <span class="t">Cache</span> {
<span class="k">public</span> <span class="t">array</span> <span class="v">$data</span> = [];
<span class="k">public function</span> <span class="f">__serialize</span>(): <span class="t">array</span> { <span class="k">return</span> [<span class="s">"daten"</span> => <span class="v">$this</span>-><span class="v">data</span>]; }
<span class="k">public function</span> <span class="f">__unserialize</span>(<span class="t">array</span> <span class="v">$d</span>): <span class="t">void</span> { <span class="v">$this</span>-><span class="v">data</span> = <span class="v">$d</span>[<span class="s">"daten"</span>]; }
}</pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>unserialize nie auf fremde Daten</b>PHPs <code class="inline">unserialize</code> kann beim Wiederherstellen Objekte erzeugen und Code auslösen („Object Injection"). Wende es niemals auf Daten an, die von außen kommen (Cookies, Requests). Für den Datenaustausch nimm JSON; <code class="inline">serialize</code> nur intern, wo du der Quelle vollständig vertraust.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 42</span><h1>Weitere Formate: INI, YAML, Base64</h1></div><p class="lead">Neben JSON, XML und CSV begegnen dir im Alltag weitere kleine Formate – für Konfiguration, lesbare Datenstrukturen und binärsichere Kodierung.</p>
<h2>INI</h2>
<p>Das klassische Schlüssel-Wert-Format mit Abschnitten, eingebaut lesbar:</p>
<pre><span class="c">// app.ini: [db] host = localhost port = 3306</span>
<span class="v">$cfg</span> = <span class="k">parse_ini_file</span>(<span class="s">"app.ini"</span>, <span class="k">true</span>); <span class="c">// true: Abschnitte behalten</span>
<span class="k">echo</span> <span class="v">$cfg</span>[<span class="s">"db"</span>][<span class="s">"host"</span>]; <span class="c">// localhost</span></pre>
<p>Auch direkt aus einem String:</p>
<pre><span class="v">$c</span> = <span class="k">parse_ini_string</span>(<span class="s">"name = Anna\nalter = 30"</span>);
<span class="k">echo</span> <span class="v">$c</span>[<span class="s">"name"</span>]; <span class="c">// Anna</span></pre>
<h2>Base64</h2>
<p>Macht beliebige Bytes (z. B. ein Bild) als reinen Text transportierbar – etwa für Data-URLs oder JSON:</p>
<pre><span class="v">$encoded</span> = <span class="k">base64_encode</span>(<span class="s">"Hällo"</span>);
<span class="k">echo</span> <span class="v">$encoded</span>; <span class="c">// SMOkbGxv</span>
<span class="k">echo</span> <span class="k">base64_decode</span>(<span class="v">$encoded</span>); <span class="c">// Hällo</span></pre>
<p>Wichtig: Base64 ist <b>keine</b> Verschlüsselung – nur eine Umkodierung. Jeder kann es zurückrechnen.</p>
<h2>URL-Kodierung & Query-Strings</h2>
<pre><span class="k">echo</span> <span class="k">urlencode</span>(<span class="s">"a b&c"</span>); <span class="c">// a+b%26c</span>
<span class="k">echo</span> <span class="k">http_build_query</span>([<span class="s">"q"</span> => <span class="s">"php 8"</span>, <span class="s">"seite"</span> => <span class="s">2</span>]); <span class="c">// q=php+8&seite=2</span></pre>
<h2>YAML</h2>
<p>Sehr lesbar, in der PHP-Welt vor allem aus Symfony bekannt. Ein YAML-Parser ist nicht im Kern – entweder über die PECL-Erweiterung <code class="inline">ext-yaml</code> oder die Bibliothek <code class="inline">symfony/yaml</code>:</p>
<pre><span class="k">use</span> <span class="t">Symfony\Component\Yaml\Yaml</span>;
<span class="v">$data</span> = <span class="t">Yaml</span>::<span class="f">parse</span>(<span class="s">"name: Anna\ntags: [a, b]"</span>);
<span class="k">echo</span> <span class="v">$data</span>[<span class="s">"name"</span>]; <span class="c">// Anna</span></pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Das richtige Format wählen</b>JSON für APIs und Datenaustausch, INI oder YAML für menschenlesbare Konfiguration, CSV für Tabellen, Base64 nur zum Transport von Binärdaten. Für Konfiguration in PHP-Projekten ist oft auch eine <code class="inline">.php</code>-Datei mit <code class="inline">return [...]</code> die einfachste Wahl – sie wird vom OPcache mitgecacht.</div>
</div></section><section class="part-divider"><div class="part-kicker">Teil 9</div><h1>Datenbanken mit PDO</h1><div class="part-desc">Fast jede Anwendung speichert Daten. PDO ist PHPs einheitliche, sichere Schnittstelle – von der Verbindung bis zu Transaktionen und ORM-Konzepten.</div><div class="part-chapters"><span>43 · Verbindung & Grundlagen</span><span>44 · Abfragen & Prepared Statements</span><span>45 · Ergebnisse verarbeiten</span><span>46 · Transaktionen</span><span>47 · Datenbankdesign-Grundlagen</span><span>48 · ORM-Konzepte & Doctrine</span></div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 43</span><h1>Verbindung & Grundlagen</h1></div><p class="lead">PDO (PHP Data Objects) ist die einheitliche, sichere Schnittstelle zu Datenbanken. Derselbe Code spricht – bis auf den Verbindungsstring – MySQL, PostgreSQL, SQLite und mehr.</p>
<h2>Verbinden</h2>
<p>Der DSN beschreibt Treiber, Host und Datenbank. Drei Optionen solltest du immer setzen:</p>
<pre><span class="v">$pdo</span> = <span class="k">new</span> <span class="t">\PDO</span>(
<span class="s">"mysql:host=localhost;dbname=shop;charset=utf8mb4"</span>,
<span class="s">"benutzer"</span>, <span class="s">"passwort"</span>,
[
<span class="t">\PDO</span>::<span class="t">ATTR_ERRMODE</span> => <span class="t">\PDO</span>::<span class="t">ERRMODE_EXCEPTION</span>, <span class="c">// Fehler werfen</span>
<span class="t">\PDO</span>::<span class="t">ATTR_DEFAULT_FETCH_MODE</span> => <span class="t">\PDO</span>::<span class="t">FETCH_ASSOC</span>, <span class="c">// Standard: assoz. Array</span>
<span class="t">\PDO</span>::<span class="t">ATTR_EMULATE_PREPARES</span> => <span class="k">false</span>, <span class="c">// echte Prepared Statements</span>
]
);</pre>
<h2>SQLite</h2>
<p>Ideal zum Lernen und für Tests – die ganze Datenbank ist eine Datei (oder im Speicher):</p>
<pre><span class="v">$pdo</span> = <span class="k">new</span> <span class="t">\PDO</span>(<span class="s">"sqlite::memory:"</span>); <span class="c">// flüchtig, nur im RAM</span>
<span class="v">$pdo</span>-><span class="f">setAttribute</span>(<span class="t">\PDO</span>::<span class="t">ATTR_ERRMODE</span>, <span class="t">\PDO</span>::<span class="t">ERRMODE_EXCEPTION</span>);
<span class="v">$pdo</span>-><span class="f">exec</span>(<span class="s">"CREATE TABLE nutzer (id INTEGER PRIMARY KEY, name TEXT)"</span>);</pre>
<h2>Direkte Befehle mit exec</h2>
<p><code class="inline">exec</code> führt SQL ohne Ergebnismenge aus und liefert die Zahl betroffener Zeilen:</p>
<pre><span class="v">$count</span> = <span class="v">$pdo</span>-><span class="f">exec</span>(<span class="s">"DELETE FROM nutzer WHERE id = 5"</span>);
<span class="k">echo</span> <span class="v">$count</span>; <span class="c">// z. B. 1</span></pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>ERRMODE_EXCEPTION immer setzen</b>Ohne diesen Modus schluckt PDO Fehler still und du suchst stundenlang. Mit ihm wird jeder Datenbankfehler zu einer <code class="inline">PDOException</code>, die du fangen kannst. Setze außerdem <code class="inline">charset=utf8mb4</code>, sonst gehen Emojis und manche Zeichen verloren.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 44</span><h1>Abfragen & Prepared Statements</h1></div><p class="lead">Das Wichtigste an PDO: Werte gehören <b>nie</b> direkt in den SQL-String, sondern als Parameter in ein Prepared Statement. Das ist der einzige verlässliche Schutz vor SQL-Injection.</p>
<h2>Das Problem (so NICHT)</h2>
<pre><span class="c">// GEFÄHRLICH – niemals so:</span>
<span class="v">$pdo</span>-><span class="f">query</span>(<span class="s">"SELECT * FROM nutzer WHERE name = '"</span> . <span class="v">$_GET</span>[<span class="s">"name"</span>] . <span class="s">"'"</span>);
<span class="c">// Eingabe ' OR '1'='1 hebelt die Bedingung aus</span></pre>
<h2>Prepared Statement mit benannten Platzhaltern</h2>
<p>Der Wert kommt getrennt vom SQL – die Datenbank behandelt ihn garantiert als Daten, nie als Befehl:</p>
<pre><span class="v">$stmt</span> = <span class="v">$pdo</span>-><span class="f">prepare</span>(<span class="s">"SELECT * FROM nutzer WHERE name = :name"</span>);
<span class="v">$stmt</span>-><span class="f">execute</span>([<span class="s">"name"</span> => <span class="v">$_GET</span>[<span class="s">"name"</span>]]);
<span class="v">$lines</span> = <span class="v">$stmt</span>-><span class="f">fetchAll</span>();</pre>
<h2>Positionsplatzhalter</h2>
<p>Statt Namen gehen auch Fragezeichen – die Werte in derselben Reihenfolge:</p>
<pre><span class="v">$stmt</span> = <span class="v">$pdo</span>-><span class="f">prepare</span>(<span class="s">"INSERT INTO nutzer (name, stadt) VALUES (?, ?)"</span>);
<span class="v">$stmt</span>-><span class="f">execute</span>([<span class="s">"Anna"</span>, <span class="s">"Köln"</span>]);
<span class="k">echo</span> <span class="v">$pdo</span>-><span class="f">lastInsertId</span>(); <span class="c">// ID des neuen Datensatzes</span></pre>
<h2>Dasselbe Statement mehrfach nutzen</h2>
<p>Einmal vorbereiten, mehrfach ausführen – effizient bei vielen Einfügungen:</p>
<pre><span class="v">$stmt</span> = <span class="v">$pdo</span>-><span class="f">prepare</span>(<span class="s">"INSERT INTO tags (wort) VALUES (?)"</span>);
<span class="k">foreach</span> ([<span class="s">"php"</span>, <span class="s">"sql"</span>, <span class="s">"web"</span>] <span class="k">as</span> <span class="v">$tag</span>) {
<span class="v">$stmt</span>-><span class="f">execute</span>([<span class="v">$tag</span>]);
}</pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Spalten- und Tabellennamen sind keine Parameter</b>Platzhalter funktionieren nur für <i>Werte</i>, nicht für Tabellen- oder Spaltennamen oder <code class="inline">ORDER BY</code>-Richtungen. Muss so etwas dynamisch sein, prüfe es gegen eine feste Erlaubnisliste (Allowlist) – niemals direkt aus Benutzereingaben übernehmen.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 45</span><h1>Ergebnisse verarbeiten</h1></div><p class="lead">Nach einer Abfrage holst du die Zeilen ab. Der Fetch-Modus bestimmt die Form – Array, Objekt, einzelne Spalte oder direkt in eine Klasse.</p>
<h2>Eine Zeile oder alle</h2>
<pre><span class="v">$stmt</span> = <span class="v">$pdo</span>-><span class="f">query</span>(<span class="s">"SELECT id, name FROM nutzer"</span>);
<span class="v">$one</span> = <span class="v">$stmt</span>-><span class="f">fetch</span>(); <span class="c">// nächste Zeile oder false</span>
<span class="v">$all</span> = <span class="v">$pdo</span>-><span class="f">query</span>(<span class="s">"SELECT id, name FROM nutzer"</span>)-><span class="f">fetchAll</span>(); <span class="c">// alle Zeilen</span></pre>
<h2>Die Fetch-Modi</h2>
<p>Als assoziatives Array, als Objekt, nur die erste Spalte als Liste oder als Schlüssel-Wert-Paare:</p>
<pre><span class="v">$pdo</span>-><span class="f">query</span>(<span class="s">"SELECT * FROM nutzer"</span>)-><span class="f">fetch</span>(<span class="t">\PDO</span>::<span class="t">FETCH_ASSOC</span>); <span class="c">// ["id"=>1, "name"=>"Anna"]</span>
<span class="v">$o</span> = <span class="v">$pdo</span>-><span class="f">query</span>(<span class="s">"SELECT * FROM nutzer"</span>)-><span class="f">fetch</span>(<span class="t">\PDO</span>::<span class="t">FETCH_OBJ</span>); <span class="c">// $o->name</span>
<span class="v">$ids</span> = <span class="v">$pdo</span>-><span class="f">query</span>(<span class="s">"SELECT id FROM nutzer"</span>)-><span class="f">fetchAll</span>(<span class="t">\PDO</span>::<span class="t">FETCH_COLUMN</span>); <span class="c">// [1, 2, 3]</span>
<span class="v">$map</span> = <span class="v">$pdo</span>-><span class="f">query</span>(<span class="s">"SELECT id, name FROM nutzer"</span>)-><span class="f">fetchAll</span>(<span class="t">\PDO</span>::<span class="t">FETCH_KEY_PAIR</span>); <span class="c">// [1=>"Anna"]</span></pre>
<h2>Direkt in eine Klasse</h2>
<p><code class="inline">FETCH_CLASS</code> füllt die Spalten in Objekte deiner Klasse – Datensätze werden zu typisierten Objekten:</p>
<pre><span class="k">class</span> <span class="t">User</span> { <span class="k">public</span> <span class="t">int</span> <span class="v">$id</span>; <span class="k">public</span> <span class="t">string</span> <span class="v">$name</span>; }
<span class="v">$user</span> = <span class="v">$pdo</span>-><span class="f">query</span>(<span class="s">"SELECT id, name FROM nutzer"</span>)
-><span class="f">fetchAll</span>(<span class="t">\PDO</span>::<span class="t">FETCH_CLASS</span>, <span class="t">User</span>::<span class="k">class</span>);
<span class="k">echo</span> <span class="v">$user</span>[<span class="s">0</span>]-><span class="v">name</span>; <span class="c">// Anna</span></pre>
<h2>Über Zeilen iterieren</h2>
<p>Ein Statement ist direkt durchlaufbar – speicherschonend, da Zeile für Zeile:</p>
<pre><span class="k">foreach</span> (<span class="v">$pdo</span>-><span class="f">query</span>(<span class="s">"SELECT name FROM nutzer"</span>) <span class="k">as</span> <span class="v">$line</span>) {
<span class="k">echo</span> <span class="v">$line</span>[<span class="s">"name"</span>];
}</pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>fetchAll vs. iterieren</b><code class="inline">fetchAll</code> ist bequem, lädt aber alle Zeilen in den Speicher. Bei großen Ergebnismengen iteriere über das Statement (oben) – so hältst du immer nur eine Zeile. <code class="inline">rowCount()</code> liefert bei <code class="inline">INSERT/UPDATE/DELETE</code> die betroffenen Zeilen.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 46</span><h1>Transaktionen</h1></div><p class="lead">Eine Transaktion bündelt mehrere Änderungen zu einer Einheit: Entweder alle gelingen, oder keine. Das hält die Daten konsistent, wenn mittendrin etwas schiefgeht.</p>
<h2>Alles-oder-nichts</h2>
<p>Das klassische Beispiel: Geld von einem Konto auf ein anderes – beide Buchungen müssen zusammen gelingen:</p>
<pre><span class="k">try</span> {
<span class="v">$pdo</span>-><span class="f">beginTransaction</span>();
<span class="v">$pdo</span>-><span class="f">prepare</span>(<span class="s">"UPDATE konto SET stand = stand - ? WHERE id = ?"</span>)
-><span class="f">execute</span>([<span class="s">100</span>, <span class="s">1</span>]);
<span class="v">$pdo</span>-><span class="f">prepare</span>(<span class="s">"UPDATE konto SET stand = stand + ? WHERE id = ?"</span>)
-><span class="f">execute</span>([<span class="s">100</span>, <span class="s">2</span>]);
<span class="v">$pdo</span>-><span class="f">commit</span>(); <span class="c">// beide Buchungen endgültig</span>
} <span class="k">catch</span> (<span class="t">\Throwable</span> <span class="v">$e</span>) {
<span class="v">$pdo</span>-><span class="f">rollBack</span>(); <span class="c">// bei Fehler: alles zurücknehmen</span>
<span class="k">throw</span> <span class="v">$e</span>;
}</pre>
<p>Ohne Transaktion könnte das erste Konto belastet werden, die Gutschrift aber scheitern – Geld wäre verschwunden.</p>
<h2>Wann lohnt sich das?</h2>
<p>Immer, wenn mehrere Schreibvorgänge logisch zusammengehören: eine Bestellung samt ihren Positionen, ein Nutzer samt Profil. Auch zur Geschwindigkeit – viele Einfügungen in einer Transaktion sind deutlich schneller als einzeln.</p>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>try/commit/rollBack als Muster</b>Verpacke zusammengehörige Schreibvorgänge immer in dieses Dreierschema: <code class="inline">beginTransaction</code> im <code class="inline">try</code>, <code class="inline">commit</code> am Ende, <code class="inline">rollBack</code> im <code class="inline">catch</code>. Wirf den Fehler danach weiter, damit der aufrufende Code von der gescheiterten Operation erfährt.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 47</span><h1>Datenbankdesign-Grundlagen</h1></div><p class="lead">Wie du Daten in Tabellen organisierst, entscheidet über Wartbarkeit und Geschwindigkeit. Ein paar Grundregeln ersparen später viel Schmerz.</p>
<h2>Tabellen, Schlüssel, Typen</h2>
<p>Jede Tabelle braucht einen Primärschlüssel, der jede Zeile eindeutig identifiziert. Wähle passende Spaltentypen:</p>
<pre><span class="k">CREATE TABLE</span> nutzer (
id <span class="t">INT</span> <span class="k">AUTO_INCREMENT PRIMARY KEY</span>,
email <span class="t">VARCHAR</span>(<span class="s">255</span>) <span class="k">NOT NULL UNIQUE</span>,
erstellt <span class="t">DATETIME</span> <span class="k">NOT NULL</span>
);</pre>
<h2>Beziehungen über Fremdschlüssel</h2>
<p>Statt Daten zu wiederholen, verweist eine Tabelle per Fremdschlüssel auf eine andere. Eine Bestellung gehört zu einem Nutzer:</p>
<pre><span class="k">CREATE TABLE</span> bestellung (
id <span class="t">INT</span> <span class="k">AUTO_INCREMENT PRIMARY KEY</span>,
nutzer_id <span class="t">INT</span> <span class="k">NOT NULL</span>,
betrag <span class="t">INT</span> <span class="k">NOT NULL</span>, <span class="c">-- in Cent!</span>
<span class="k">FOREIGN KEY</span> (nutzer_id) <span class="k">REFERENCES</span> nutzer(id)
);</pre>
<h2>Normalisierung</h2>
<p>Speichere jede Tatsache genau einmal. Statt den Nutzernamen in jede Bestellung zu schreiben, steht er nur in <code class="inline">nutzer</code> und wird per Verknüpfung geholt:</p>
<pre><span class="k">SELECT</span> b.id, n.email
<span class="k">FROM</span> bestellung b
<span class="k">JOIN</span> nutzer n <span class="k">ON</span> n.id = b.nutzer_id;</pre>
<p>Das verhindert Widersprüche: Ändert sich die E-Mail, gibt es nur eine Stelle zu pflegen.</p>
<h2>Indizes für Tempo</h2>
<p>Ein Index beschleunigt das Suchen in einer Spalte enorm – setze ihn auf Spalten, nach denen du häufig filterst oder verknüpfst:</p>
<pre><span class="k">CREATE INDEX</span> idx_bestellung_nutzer <span class="k">ON</span> bestellung(nutzer_id);</pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Indizes mit Maß</b>Indizes beschleunigen das Lesen, verlangsamen aber das Schreiben und brauchen Platz. Indiziere gezielt die Spalten in <code class="inline">WHERE</code>- und <code class="inline">JOIN</code>-Bedingungen, nicht blind alles. Primär- und Fremdschlüssel sind meist automatisch oder sinnvollerweise indiziert.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 48</span><h1>ORM-Konzepte & Doctrine</h1></div><p class="lead">Ein ORM (Object-Relational Mapper) bildet Datenbankzeilen auf Objekte ab. Statt SQL zu schreiben, arbeitest du mit Entitäten – das große PHP-ORM ist Doctrine.</p>
<h2>Die Idee</h2>
<p>Eine Tabelle wird zu einer Klasse (Entität), eine Zeile zu einem Objekt, Spalten zu Eigenschaften. Das Mapping beschreibst du heute mit Attributen:</p>
<pre><span class="k">use</span> <span class="t">Doctrine\ORM\Mapping</span> <span class="k">as</span> <span class="t">ORM</span>;
<span class="a">#[ORM\Entity]</span>
<span class="k">class</span> <span class="t">Product</span> {
<span class="a">#[ORM\Id]</span> <span class="a">#[ORM\GeneratedValue]</span> <span class="a">#[ORM\Column]</span>
<span class="k">public</span> <span class="t">int</span> <span class="v">$id</span>;
<span class="a">#[ORM\Column]</span>
<span class="k">public</span> <span class="t">string</span> <span class="v">$name</span>;
<span class="a">#[ORM\Column]</span>
<span class="k">public</span> <span class="t">int</span> <span class="v">$cent</span>;
}</pre>
<h2>Speichern & Laden</h2>
<p>Der EntityManager übersetzt zwischen Objekten und SQL. Du fügst Objekte hinzu und rufst <code class="inline">flush()</code> – Doctrine erzeugt die passenden INSERT/UPDATE:</p>
<pre><span class="v">$p</span> = <span class="k">new</span> <span class="t">Product</span>();
<span class="v">$p</span>-><span class="v">name</span> = <span class="s">"Buch"</span>; <span class="v">$p</span>-><span class="v">cent</span> = <span class="s">1999</span>;
<span class="v">$em</span>-><span class="f">persist</span>(<span class="v">$p</span>); <span class="c">// vormerken</span>
<span class="v">$em</span>-><span class="f">flush</span>(); <span class="c">// in die DB schreiben</span>
<span class="v">$found</span> = <span class="v">$em</span>-><span class="f">find</span>(<span class="t">Product</span>::<span class="k">class</span>, <span class="s">1</span>); <span class="c">// per ID laden</span>
<span class="k">echo</span> <span class="v">$found</span>-><span class="v">name</span>;</pre>
<h2>Repositories für Abfragen</h2>
<p>Wiederkehrende Abfragen kapselt ein Repository – sprechende Methoden statt SQL überall:</p>
<pre><span class="v">$repo</span> = <span class="v">$em</span>-><span class="f">getRepository</span>(<span class="t">Product</span>::<span class="k">class</span>);
<span class="v">$expensive</span> = <span class="v">$repo</span>-><span class="f">findBy</span>([<span class="s">"name"</span> => <span class="s">"Buch"</span>]);
<span class="v">$single</span> = <span class="v">$repo</span>-><span class="f">findOneBy</span>([<span class="s">"id"</span> => <span class="s">1</span>]);</pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>ORM oder pures PDO?</b>Ein ORM nimmt dir viel Schreibarbeit ab und hält die Domäne objektorientiert – ideal für große Anwendungen mit vielen Entitäten. Für einfache Skripte, Reports oder hochoptimierte Abfragen ist pures PDO direkter und schneller. Viele Projekte mischen beides bewusst.</div>
</div></section><section class="part-divider"><div class="part-kicker">Teil 10</div><h1>Web-Entwicklung mit purem PHP</h1><div class="part-desc">Das Fundament unter jedem Framework: Requests, Formulare, Sessions, Cookies, Uploads und HTTP.</div><div class="part-chapters"><span>49 · Wie PHP & Webserver zusammenspielen</span><span>50 · Request & Superglobals</span><span>51 · Formulare verarbeiten</span><span>52 · Eingabevalidierung & Filter</span><span>53 · Sessions</span><span>54 · Cookies</span><span>55 · Datei-Uploads</span><span>56 · HTTP-Clients & APIs</span></div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 49</span><h1>Wie PHP & Webserver zusammenspielen</h1></div><p class="lead">PHP wurde für das Web geboren. Pro HTTP-Anfrage startet ein Skript, baut eine Antwort und endet wieder – dieses „bei jeder Anfrage von vorn"-Modell prägt alles Weitere.</p>
<h2>Der Ablauf einer Anfrage</h2>
<p>Der Browser schickt eine Anfrage, der Webserver reicht sie an PHP, PHP führt das Skript aus, dessen Ausgabe wird zur HTTP-Antwort. Danach ist der gesamte Zustand wieder weg.</p>
<pre><span class="c">// index.php – läuft komplett bei jeder Anfrage neu</span>
<span class="k">echo</span> <span class="s">"<h1>Hallo Welt</h1>"</span>; <span class="c">// wird zum Antwort-Body</span></pre>
<h2>Zustandslosigkeit</h2>
<p>Weil jede Anfrage frisch startet, gibt es keine Variablen, die zwischen zwei Aufrufen überleben. Wer Daten behalten will, braucht Sessions, Cookies oder eine Datenbank (kommt gleich).</p>
<h2>Front-Controller</h2>
<p>Moderne Anwendungen leiten alle Anfragen über eine einzige Datei – den Front-Controller. Der Webserver schreibt jede URL auf <code class="inline">index.php</code> um, die dann entscheidet, was zu tun ist:</p>
<pre><span class="v">$path</span> = <span class="k">parse_url</span>(<span class="v">$_SERVER</span>[<span class="s">"REQUEST_URI"</span>], <span class="t">PHP_URL_PATH</span>);
<span class="k">echo</span> <span class="k">match</span>(<span class="v">$path</span>) {
<span class="s">"/"</span> => <span class="s">"Startseite"</span>,
<span class="s">"/hilfe"</span> => <span class="s">"Hilfeseite"</span>,
<span class="k">default</span> => <span class="k">http_response_code</span>(<span class="s">404</span>) . <span class="s">"Nicht gefunden"</span>,
};</pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Das Mentalmodell</b>„Ein Request, ein frischer Prozess, eine Antwort, Ende." Anders als bei lang laufenden Servern (Node) musst du in klassischem PHP nichts manuell aufräumen – aber auch nichts im Speicher zwischenspeichern. Genau hier setzen die asynchronen Laufzeiten aus Teil 15 an.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 50</span><h1>Request & Superglobals</h1></div><p class="lead">Alle Daten einer HTTP-Anfrage stellt PHP in vordefinierten „Superglobals" bereit – Arrays, die überall verfügbar sind.</p>
<h2>$_GET und $_POST</h2>
<p><code class="inline">$_GET</code> enthält die Query-Parameter aus der URL, <code class="inline">$_POST</code> die Felder eines abgeschickten Formulars:</p>
<pre><span class="c">// Aufruf: /suche?q=php&seite=2</span>
<span class="k">echo</span> <span class="v">$_GET</span>[<span class="s">"q"</span>] ?? <span class="s">""</span>; <span class="c">// php</span>
<span class="k">echo</span> <span class="v">$_POST</span>[<span class="s">"name"</span>] ?? <span class="s">""</span>; <span class="c">// aus einem <form method="post"></span></pre>
<p>Immer mit <code class="inline">??</code> absichern – fehlt der Schlüssel, gäbe es sonst eine Warnung.</p>
<h2>$_SERVER</h2>
<p>Enthält Informationen über die Anfrage und die Serverumgebung:</p>
<pre><span class="k">echo</span> <span class="v">$_SERVER</span>[<span class="s">"REQUEST_METHOD"</span>]; <span class="c">// GET, POST, ...</span>
<span class="k">echo</span> <span class="v">$_SERVER</span>[<span class="s">"REQUEST_URI"</span>]; <span class="c">// /suche?q=php</span>
<span class="k">echo</span> <span class="v">$_SERVER</span>[<span class="s">"HTTP_USER_AGENT"</span>]; <span class="c">// Browser-Kennung</span></pre>
<h2>Rohdaten & JSON-Body</h2>
<p>Schickt ein Client JSON (statt Formularfeldern), liest du den rohen Body über einen Stream:</p>
<pre><span class="v">$raw</span> = <span class="k">file_get_contents</span>(<span class="s">"php://input"</span>);
<span class="v">$data</span> = <span class="k">json_decode</span>(<span class="v">$raw</span>, <span class="k">true</span>, <span class="s">512</span>, <span class="t">JSON_THROW_ON_ERROR</span>);</pre>
<h2>Weitere Superglobals</h2>
<p><code class="inline">$_COOKIE</code> (gesendete Cookies), <code class="inline">$_FILES</code> (Uploads), <code class="inline">$_SESSION</code> (Sitzungsdaten) und <code class="inline">$_REQUEST</code> (GET+POST gemischt – besser meiden) – jedes bekommt sein eigenes Kapitel bzw. wird gleich behandelt.</p>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Superglobals sind roh und ungeprüft</b>Alles in <code class="inline">$_GET</code>, <code class="inline">$_POST</code> & Co. kommt direkt vom Client und kann beliebig manipuliert sein. Nie ungeprüft in SQL, HTML oder Dateipfade einsetzen – immer erst validieren (nächstes Kapitel) und beim Ausgeben passend maskieren.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 51</span><h1>Formulare verarbeiten</h1></div><p class="lead">Formulare sind der häufigste Weg, Daten vom Nutzer zu bekommen. Der Ablauf: anzeigen, abschicken, auf dem Server prüfen, reagieren.</p>
<h2>GET oder POST?</h2>
<p>GET für harmlose Abfragen (Suche, Filter) – die Daten stehen in der URL. POST für alles, was etwas ändert oder vertraulich ist (Anmeldung, Speichern) – die Daten stehen im Body.</p>
<h2>Ein Formular im selben Skript</h2>
<p>Ein verbreitetes Muster: dieselbe Seite zeigt das Formular und verarbeitet die Eingabe:</p>
<pre><span class="k">if</span> (<span class="v">$_SERVER</span>[<span class="s">"REQUEST_METHOD"</span>] === <span class="s">"POST"</span>) {
<span class="v">$name</span> = <span class="k">trim</span>(<span class="v">$_POST</span>[<span class="s">"name"</span>] ?? <span class="s">""</span>);
<span class="k">if</span> (<span class="v">$name</span> === <span class="s">""</span>) {
<span class="v">$error</span> = <span class="s">"Name fehlt"</span>;
} <span class="k">else</span> {
<span class="c">// speichern, weiterleiten ...</span>
<span class="k">echo</span> <span class="s">"Danke, "</span> . <span class="k">htmlspecialchars</span>(<span class="v">$name</span>);
}
}</pre>
<h2>Werte im HTML korrekt einsetzen</h2>
<p>Jeder Wert, der ins HTML zurückgeschrieben wird (z. B. um Eingaben zu erhalten), muss durch <code class="inline">htmlspecialchars</code> – sonst droht XSS:</p>
<pre><span class="t"><input</span> <span class="a">name</span>=<span class="s">"name"</span> <span class="a">value</span>=<span class="s">"<?= htmlspecialchars($name ?? "") ?>"</span><span class="t">></span></pre>
<h2>Post/Redirect/Get</h2>
<p>Nach erfolgreichem POST leitest du per Redirect auf eine GET-Seite um. Das verhindert doppeltes Absenden beim Neuladen:</p>
<pre><span class="c">// nach dem Speichern:</span>
<span class="k">header</span>(<span class="s">"Location: /danke"</span>, <span class="k">true</span>, <span class="s">303</span>);
<span class="k">exit</span>;</pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Serverseitig prüfen ist Pflicht</b>HTML-Attribute wie <code class="inline">required</code> oder <code class="inline">type="email"</code> sind reiner Komfort – sie lassen sich umgehen. Die echte Prüfung passiert immer auf dem Server. Und gegen Fremd-Absenden gehört ein CSRF-Token ins Formular (Sicherheits-Teil).</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 52</span><h1>Eingabevalidierung & Filter</h1></div><p class="lead">Jede Eingabe ist erst einmal verdächtig. Validierung prüft, ob ein Wert die erwartete Form hat; Filterung bringt ihn in eine saubere Form. PHP bringt dafür <code class="inline">filter_var</code> mit.</p>
<h2>Validieren mit filter_var</h2>
<p>Liefert den Wert zurück, wenn er gültig ist, sonst <code class="inline">false</code>:</p>
<pre><span class="k">var_dump</span>(<span class="k">filter_var</span>(<span class="s">"a@b.de"</span>, <span class="t">FILTER_VALIDATE_EMAIL</span>)); <span class="c">// "a@b.de"</span>
<span class="k">var_dump</span>(<span class="k">filter_var</span>(<span class="s">"abc"</span>, <span class="t">FILTER_VALIDATE_INT</span>)); <span class="c">// false</span>
<span class="k">var_dump</span>(<span class="k">filter_var</span>(<span class="s">"3.14"</span>, <span class="t">FILTER_VALIDATE_FLOAT</span>)); <span class="c">// 3.14</span>
<span class="k">var_dump</span>(<span class="k">filter_var</span>(<span class="s">"https://php.net"</span>, <span class="t">FILTER_VALIDATE_URL</span>)); <span class="c">// die URL</span>
<span class="k">var_dump</span>(<span class="k">filter_var</span>(<span class="s">"yes"</span>, <span class="t">FILTER_VALIDATE_BOOLEAN</span>)); <span class="c">// true</span></pre>
<h2>Mit Optionen</h2>
<p>Ein Wertebereich für Zahlen – außerhalb gibt es <code class="inline">false</code>:</p>
<pre><span class="v">$age</span> = <span class="k">filter_var</span>(<span class="v">$_POST</span>[<span class="s">"alter"</span>] ?? <span class="s">""</span>, <span class="t">FILTER_VALIDATE_INT</span>, [
<span class="s">"options"</span> => [<span class="s">"min_range"</span> => <span class="s">0</span>, <span class="s">"max_range"</span> => <span class="s">120</span>],
]);</pre>
<h2>Eigene Regeln</h2>
<p>Für alles, was kein eingebauter Filter abdeckt, prüfst du selbst – klar und früh:</p>
<pre><span class="v">$name</span> = <span class="k">trim</span>(<span class="v">$_POST</span>[<span class="s">"name"</span>] ?? <span class="s">""</span>);
<span class="v">$error</span> = [];
<span class="k">if</span> (<span class="k">mb_strlen</span>(<span class="v">$name</span>) < <span class="s">2</span>) <span class="v">$error</span>[] = <span class="s">"Name zu kurz"</span>;
<span class="k">if</span> (!<span class="k">preg_match</span>(<span class="s">"/^[\p{L} -]+$/u"</span>, <span class="v">$name</span>)) <span class="v">$error</span>[] = <span class="s">"ungültige Zeichen"</span>;</pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Validieren ≠ Maskieren</b>Trenne zwei Dinge: <i>Validierung</i> beim Hereinkommen (hat die E-Mail die richtige Form?) und <i>kontextgerechtes Maskieren</i> beim Hinausgeben (<code class="inline">htmlspecialchars</code> für HTML, Prepared Statements für SQL). Beides ist nötig – das eine ersetzt das andere nicht.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 53</span><h1>Sessions</h1></div><p class="lead">Eine Session hält Daten über mehrere Anfragen desselben Nutzers hinweg – die Antwort auf PHPs Zustandslosigkeit. Typischer Einsatz: „wer ist angemeldet?".</p>
<h2>Starten & nutzen</h2>
<p><code class="inline">session_start()</code> muss vor jeder Ausgabe stehen. Danach ist <code class="inline">$_SESSION</code> ein Array, das gespeichert und beim nächsten Request wieder geladen wird:</p>
<pre><span class="k">session_start</span>();
<span class="v">$_SESSION</span>[<span class="s">"nutzer_id"</span>] = <span class="s">42</span>; <span class="c">// merken</span>
<span class="c">// bei der nächsten Anfrage:</span>
<span class="k">echo</span> <span class="v">$_SESSION</span>[<span class="s">"nutzer_id"</span>] ?? <span class="s">"Gast"</span>;</pre>
<p>Technisch bekommt der Browser ein Cookie mit der Session-ID; die eigentlichen Daten liegen auf dem Server.</p>
<h2>Anmeldung merken</h2>
<pre><span class="k">session_start</span>();
<span class="k">if</span> (<span class="f">passwordMatches</span>(<span class="v">$_POST</span>[<span class="s">"pw"</span>])) {
<span class="k">session_regenerate_id</span>(<span class="k">true</span>); <span class="c">// neue ID gegen Session-Fixation</span>
<span class="v">$_SESSION</span>[<span class="s">"nutzer_id"</span>] = <span class="v">$id</span>;
}</pre>
<h2>Abmelden</h2>
<pre><span class="k">session_start</span>();
<span class="v">$_SESSION</span> = []; <span class="c">// Daten leeren</span>
<span class="k">session_destroy</span>(); <span class="c">// Session serverseitig beenden</span></pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>ID bei Anmeldung erneuern</b>Rufe nach erfolgreicher Anmeldung <code class="inline">session_regenerate_id(true)</code> auf. Sonst könnte ein Angreifer dem Opfer vorab eine bekannte Session-ID unterschieben und nach dessen Login mitschwimmen (Session-Fixation). Wie man die Session-Cookies härtet, zeigt der Sicherheits-Teil.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 54</span><h1>Cookies</h1></div><p class="lead">Cookies sind kleine Werte, die der Browser speichert und bei jeder Anfrage mitschickt. Sessions nutzen sie für die ID; daneben dienen sie für Einstellungen oder „Angemeldet bleiben".</p>
<h2>Setzen mit setcookie</h2>
<p>Muss vor jeder Ausgabe stehen. Das Optionen-Array steuert Gültigkeit und Sicherheit:</p>
<pre><span class="k">setcookie</span>(<span class="s">"theme"</span>, <span class="s">"dunkel"</span>, [
<span class="s">"expires"</span> => <span class="k">time</span>() + <span class="s">86400</span> * <span class="s">30</span>, <span class="c">// 30 Tage</span>
<span class="s">"path"</span> => <span class="s">"/"</span>,
<span class="s">"secure"</span> => <span class="k">true</span>, <span class="c">// nur über HTTPS</span>
<span class="s">"httponly"</span> => <span class="k">true</span>, <span class="c">// für JavaScript unsichtbar</span>
<span class="s">"samesite"</span> => <span class="s">"Lax"</span>, <span class="c">// gegen CSRF</span>
]);</pre>
<h2>Lesen</h2>
<p>Gesendete Cookies stehen in <code class="inline">$_COOKIE</code> – verfügbar ab der <i>nächsten</i> Anfrage, nicht in derselben:</p>
<pre><span class="k">echo</span> <span class="v">$_COOKIE</span>[<span class="s">"theme"</span>] ?? <span class="s">"hell"</span>; <span class="c">// dunkel</span></pre>
<h2>Löschen</h2>
<p>Ein Cookie löscht man, indem man es mit einem Ablaufdatum in der Vergangenheit überschreibt:</p>
<pre><span class="k">setcookie</span>(<span class="s">"theme"</span>, <span class="s">""</span>, [<span class="s">"expires"</span> => <span class="k">time</span>() - <span class="s">3600</span>, <span class="s">"path"</span> => <span class="s">"/"</span>]);</pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Cookies sind manipulierbar</b>Der Inhalt liegt beim Nutzer und kann verändert werden – speichere dort nie etwas Vertrauliches oder Sicherheitsrelevantes im Klartext (etwa „ist_admin=1"). Setze <code class="inline">httponly</code>, <code class="inline">secure</code> und <code class="inline">samesite</code> immer; speichere Sensibles in der serverseitigen Session.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 55</span><h1>Datei-Uploads</h1></div><p class="lead">Hochgeladene Dateien landen in <code class="inline">$_FILES</code>. Hier ist besondere Vorsicht nötig: Nichts an einer hochgeladenen Datei darf man dem Client glauben.</p>
<h2>Das Formular</h2>
<pre><span class="t"><form</span> <span class="a">method</span>=<span class="s">"post"</span> <span class="a">enctype</span>=<span class="s">"multipart/form-data"</span><span class="t">></span>
<span class="t"><input</span> <span class="a">type</span>=<span class="s">"file"</span> <span class="a">name</span>=<span class="s">"bild"</span><span class="t">></span>
<span class="t"></form></span></pre>
<p>Ohne <code class="inline">enctype="multipart/form-data"</code> kommt keine Datei an.</p>
<h2>Sicher entgegennehmen</h2>
<p>Prüfe den Fehlercode, bestimme den Typ selbst (nicht aus dem Client-Header) und verschiebe mit <code class="inline">move_uploaded_file</code>:</p>
<pre><span class="v">$f</span> = <span class="v">$_FILES</span>[<span class="s">"bild"</span>];
<span class="k">if</span> (<span class="v">$f</span>[<span class="s">"error"</span>] !== <span class="t">UPLOAD_ERR_OK</span>) {
<span class="k">throw</span> <span class="k">new</span> <span class="t">\RuntimeException</span>(<span class="s">"Upload fehlgeschlagen"</span>);
}
<span class="v">$type</span> = <span class="k">mime_content_type</span>(<span class="v">$f</span>[<span class="s">"tmp_name"</span>]); <span class="c">// echten Typ aus dem Inhalt</span>
<span class="k">if</span> (!<span class="k">in_array</span>(<span class="v">$type</span>, [<span class="s">"image/png"</span>, <span class="s">"image/jpeg"</span>], <span class="k">true</span>)) {
<span class="k">throw</span> <span class="k">new</span> <span class="t">\RuntimeException</span>(<span class="s">"nur PNG/JPEG"</span>);
}
<span class="v">$target</span> = <span class="s">"uploads/"</span> . <span class="k">bin2hex</span>(<span class="k">random_bytes</span>(<span class="s">8</span>)) . <span class="s">".bin"</span>; <span class="c">// eigener Name</span>
<span class="k">move_uploaded_file</span>(<span class="v">$f</span>[<span class="s">"tmp_name"</span>], <span class="v">$target</span>);</pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Drei Regeln für Uploads</b>(1) Vertraue nie dem mitgelieferten Dateinamen oder MIME-Typ – bestimme den Typ aus dem Inhalt und vergib einen eigenen Namen. (2) Lege Uploads außerhalb des öffentlichen Verzeichnisses ab oder verbiete dort die Ausführung – sonst lädt jemand ein PHP-Skript hoch. (3) Begrenze Größe und Typ. Eine als „bild.jpg" benannte Datei kann beliebigen Inhalt haben.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 56</span><h1>HTTP-Clients & APIs</h1></div><p class="lead">Oft ist PHP nicht nur Server, sondern auch Client: Es ruft fremde APIs auf. Für einfache Fälle reicht cURL bzw. Streams, für echte Projekte ein Client wie Guzzle.</p>
<h2>GET mit cURL</h2>
<pre><span class="v">$ch</span> = <span class="k">curl_init</span>(<span class="s">"https://api.example.com/status"</span>);
<span class="k">curl_setopt</span>(<span class="v">$ch</span>, <span class="t">CURLOPT_RETURNTRANSFER</span>, <span class="k">true</span>); <span class="c">// Antwort zurückgeben statt ausgeben</span>
<span class="v">$response</span> = <span class="k">curl_exec</span>(<span class="v">$ch</span>);
<span class="v">$code</span> = <span class="k">curl_getinfo</span>(<span class="v">$ch</span>, <span class="t">CURLINFO_HTTP_CODE</span>);
<span class="k">curl_close</span>(<span class="v">$ch</span>);
<span class="k">echo</span> <span class="v">$code</span>; <span class="c">// 200</span></pre>
<h2>POST mit JSON</h2>
<pre><span class="v">$ch</span> = <span class="k">curl_init</span>(<span class="s">"https://api.example.com/nutzer"</span>);
<span class="k">curl_setopt_array</span>(<span class="v">$ch</span>, [
<span class="t">CURLOPT_RETURNTRANSFER</span> => <span class="k">true</span>,
<span class="t">CURLOPT_POST</span> => <span class="k">true</span>,
<span class="t">CURLOPT_HTTPHEADER</span> => [<span class="s">"Content-Type: application/json"</span>],
<span class="t">CURLOPT_POSTFIELDS</span> => <span class="k">json_encode</span>([<span class="s">"name"</span> => <span class="s">"Anna"</span>]),
]);
<span class="v">$data</span> = <span class="k">json_decode</span>(<span class="k">curl_exec</span>(<span class="v">$ch</span>), <span class="k">true</span>);</pre>
<h2>Mit Guzzle</h2>
<p>Komfortabler und besser testbar – der De-facto-Standard-Client:</p>
<pre><span class="k">use</span> <span class="t">GuzzleHttp\Client</span>;
<span class="v">$client</span> = <span class="k">new</span> <span class="t">Client</span>();
<span class="v">$res</span> = <span class="v">$client</span>-><span class="f">post</span>(<span class="s">"https://api.example.com/nutzer"</span>, [
<span class="s">"json"</span> => [<span class="s">"name"</span> => <span class="s">"Anna"</span>],
]);
<span class="k">echo</span> <span class="v">$res</span>-><span class="f">getStatusCode</span>(); <span class="c">// 201</span>
<span class="v">$data</span> = <span class="k">json_decode</span>((<span class="t">string</span>) <span class="v">$res</span>-><span class="f">getBody</span>(), <span class="k">true</span>);</pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Timeouts & Fehler einplanen</b>Externe APIs sind langsam oder fallen aus. Setze immer ein Timeout, prüfe den Status-Code und fange Netzwerkfehler ab – sonst hängt deine Seite an einem fremden Dienst. Standardisierte Clients (PSR-18) und Guzzle nehmen dir Wiederholungen, Header und Fehlerbehandlung weitgehend ab.</div>
</div></section><section class="part-divider"><div class="part-kicker">Teil 11</div><h1>Sicherheit</h1><div class="part-desc">Sicherheit ist eine Haltung, kein nachträgliches Feature. Die großen Angriffsklassen einzeln – und wie man sie schließt.</div><div class="part-chapters"><span>57 · Das Grundprinzip: niemals vertrauen</span><span>58 · SQL-Injection im Detail</span><span>59 · XSS & CSRF im Detail</span><span>60 · Authentifizierung & Passwörter</span><span>61 · Sessions & Cookies absichern</span><span>62 · Kryptografie & Sodium</span></div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 57</span><h1>Das Grundprinzip: niemals vertrauen</h1></div><p class="lead">Sicherheit ist keine Funktion, die man am Ende anschraubt, sondern eine Haltung beim Schreiben jeder Zeile. Das Kernprinzip: Traue keiner Eingabe – egal woher.</p>
<h2>Die zwei Bewegungsrichtungen</h2>
<p>Daten kommen herein (Request, Datei, API) und gehen hinaus (HTML, SQL, Shell). An jeder Grenze gilt eine eigene Regel:</p>
<pre><span class="c">// HEREIN: validieren – hat der Wert die erwartete Form?</span>
<span class="v">$age</span> = <span class="k">filter_var</span>(<span class="v">$_POST</span>[<span class="s">"alter"</span>] ?? <span class="s">""</span>, <span class="t">FILTER_VALIDATE_INT</span>);
<span class="c">// HINAUS: kontextgerecht maskieren – passend zum Ziel</span>
<span class="k">echo</span> <span class="k">htmlspecialchars</span>(<span class="v">$name</span>); <span class="c">// fürs HTML</span>
<span class="v">$stmt</span>-><span class="f">execute</span>([<span class="v">$name</span>]); <span class="c">// für SQL (Prepared Statement)</span></pre>
<h2>Defense in Depth</h2>
<p>Verlasse dich nie auf eine einzige Schutzschicht. Eingaben werden validiert <i>und</i> Ausgaben maskiert <i>und</i> Rechte geprüft. Fällt eine Schicht, hält die nächste.</p>
<h2>Least Privilege</h2>
<p>Gib jedem Teil nur die Rechte, die er braucht: Der Datenbank-Benutzer der Web-App darf keine Tabellen löschen, Upload-Ordner sind nicht ausführbar, Fehlermeldungen verraten nichts über interne Pfade.</p>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Die Denkweise</b>Frage bei jeder Eingabe: „Was, wenn hier das Bösartigste steht, das technisch möglich ist?" Und bei jeder Ausgabe: „In welchem Kontext landet dieser Wert – HTML, SQL, Shell, URL?" Die folgenden Kapitel sind nur konkrete Anwendungen dieser zwei Fragen.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 58</span><h1>SQL-Injection im Detail</h1></div><p class="lead">SQL-Injection entsteht, wenn Benutzereingaben als Teil eines SQL-Befehls interpretiert werden. Sie gehört zu den ältesten und gefährlichsten Lücken – und ist vollständig vermeidbar.</p>
<h2>Wie der Angriff funktioniert</h2>
<pre><span class="c">// verwundbar:</span>
<span class="v">$sql</span> = <span class="s">"SELECT * FROM nutzer WHERE name = '"</span> . <span class="v">$_GET</span>[<span class="s">"name"</span>] . <span class="s">"'"</span>;
<span class="c">// Eingabe: '; DROP TABLE nutzer; --</span>
<span class="c">// ergibt: SELECT ... WHERE name = ''; DROP TABLE nutzer; --'</span></pre>
<p>Die Eingabe bricht aus dem String aus und schmuggelt einen eigenen Befehl ein.</p>
<h2>Die Lösung: Prepared Statements</h2>
<p>Werte gehören als Parameter übergeben, getrennt vom SQL. Die Datenbank behandelt sie dann garantiert als Daten:</p>
<pre><span class="v">$stmt</span> = <span class="v">$pdo</span>-><span class="f">prepare</span>(<span class="s">"SELECT * FROM nutzer WHERE name = :name"</span>);
<span class="v">$stmt</span>-><span class="f">execute</span>([<span class="s">"name"</span> => <span class="v">$_GET</span>[<span class="s">"name"</span>]]); <span class="c">// sicher, egal was drinsteht</span></pre>
<h2>Was Platzhalter NICHT können</h2>
<p>Tabellen-/Spaltennamen und Sortierrichtungen sind keine Werte. Müssen sie dynamisch sein, gegen eine feste Liste prüfen:</p>
<pre><span class="v">$allowed</span> = [<span class="s">"name"</span>, <span class="s">"datum"</span>];
<span class="v">$column</span> = <span class="k">in_array</span>(<span class="v">$_GET</span>[<span class="s">"sort"</span>] ?? <span class="s">""</span>, <span class="v">$allowed</span>, <span class="k">true</span>)
? <span class="v">$_GET</span>[<span class="s">"sort"</span>] : <span class="s">"name"</span>;
<span class="v">$sql</span> = <span class="s">"SELECT * FROM eintrag ORDER BY $column"</span>; <span class="c">// nur erlaubte Spalten</span></pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Niemals Eingaben in SQL kleben</b>Es gibt keinen sicheren Weg, Benutzereingaben per String-Verkettung in SQL einzubauen – auch nicht „durch Escaping selbst gemacht". Nutze immer Prepared Statements. Für Strukturteile (Spalten, Tabellen) gilt: Allowlist statt Eingabe.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 59</span><h1>XSS & CSRF im Detail</h1></div><p class="lead">Zwei der häufigsten Web-Angriffe: XSS schmuggelt fremdes JavaScript in deine Seite; CSRF lässt das Opfer ungewollt Aktionen auslösen. Beide haben klare Gegenmittel.</p>
<h2>XSS</h2>
<p>Gibt man Benutzereingaben ungefiltert ins HTML, kann der Angreifer Skripte einschleusen, die im Browser anderer Nutzer laufen:</p>
<pre><span class="c">// verwundbar:</span>
<span class="k">echo</span> <span class="s">"<p>"</span> . <span class="v">$_GET</span>[<span class="s">"text"</span>] . <span class="s">"</p>"</span>;
<span class="c">// Eingabe: <script>stehleCookies()</script></span></pre>
<p>Schutz: jeden ausgegebenen Wert mit <code class="inline">htmlspecialchars</code> maskieren – aus <code class="inline"><</code> wird <code class="inline">&lt;</code>, der Code wird Text statt Befehl:</p>
<pre><span class="k">echo</span> <span class="s">"<p>"</span> . <span class="k">htmlspecialchars</span>(<span class="v">$_GET</span>[<span class="s">"text"</span>], <span class="t">ENT_QUOTES</span>) . <span class="s">"</p>"</span>;</pre>
<p>Template-Engines wie Twig maskieren automatisch – einer ihrer großen Sicherheitsvorteile.</p>
<h2>CSRF</h2>
<p>Ein eingeloggter Nutzer besucht eine fremde Seite, die heimlich ein Formular an deine App schickt. Da der Browser die Session-Cookies mitsendet, sieht die Aktion echt aus.</p>
<p>Schutz: ein unvorhersehbares Token, das nur dein Formular kennt:</p>
<pre><span class="c">// beim Anzeigen des Formulars:</span>
<span class="v">$_SESSION</span>[<span class="s">"csrf"</span>] = <span class="k">bin2hex</span>(<span class="k">random_bytes</span>(<span class="s">32</span>));
<span class="c">// im Formular: <input type="hidden" name="csrf" value="..."></span>
<span class="c">// beim Verarbeiten:</span>
<span class="k">if</span> (!<span class="k">hash_equals</span>(<span class="v">$_SESSION</span>[<span class="s">"csrf"</span>] ?? <span class="s">""</span>, <span class="v">$_POST</span>[<span class="s">"csrf"</span>] ?? <span class="s">""</span>)) {
<span class="k">http_response_code</span>(<span class="s">403</span>);
<span class="k">exit</span>(<span class="s">"ungültiges Token"</span>);
}</pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Kontext entscheidet die Maskierung</b><code class="inline">htmlspecialchars</code> schützt im HTML-Textkontext. In einem JavaScript-Block, einem URL-Attribut oder CSS gelten andere Regeln. Im Zweifel gibt man Daten nicht in solche Kontexte – und ein <code class="inline">SameSite</code>-Cookie plus CSRF-Token deckt die Formular-Seite ab.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 60</span><h1>Authentifizierung & Passwörter</h1></div><p class="lead">Passwörter sind das wertvollste, was eine App verwahrt. PHP macht es richtig einfach, sie sicher zu behandeln – man muss nur die zwei eingebauten Funktionen nutzen.</p>
<h2>Hashen, nie verschlüsseln</h2>
<p><code class="inline">password_hash</code> erzeugt einen sicheren, gesalzenen Hash (standardmäßig bcrypt). Den speicherst du – nie das Klartext-Passwort:</p>
<pre><span class="v">$hash</span> = <span class="k">password_hash</span>(<span class="v">$_POST</span>[<span class="s">"pw"</span>], <span class="t">PASSWORD_DEFAULT</span>);
<span class="c">// in die DB: ein langer String wie $2y$12$...</span></pre>
<h2>Prüfen mit password_verify</h2>
<p>Beim Login vergleichst du die Eingabe gegen den gespeicherten Hash – sicher und in konstanter Zeit:</p>
<pre><span class="k">if</span> (<span class="k">password_verify</span>(<span class="v">$_POST</span>[<span class="s">"pw"</span>], <span class="v">$hashFromDb</span>)) {
<span class="c">// Passwort korrekt – anmelden</span>
} <span class="k">else</span> {
<span class="c">// falsch</span>
}</pre>
<h2>Hashes aktuell halten</h2>
<p>Wird das Standardverfahren stärker, prüfst du beim erfolgreichen Login, ob ein Neu-Hashen sinnvoll ist:</p>
<pre><span class="k">if</span> (<span class="k">password_needs_rehash</span>(<span class="v">$hashFromDb</span>, <span class="t">PASSWORD_DEFAULT</span>)) {
<span class="v">$fresh</span> = <span class="k">password_hash</span>(<span class="v">$_POST</span>[<span class="s">"pw"</span>], <span class="t">PASSWORD_DEFAULT</span>);
<span class="c">// $fresh in der DB speichern</span>
}</pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Tabu-Liste</b>Niemals Passwörter im Klartext speichern, niemals mit <code class="inline">md5</code> oder <code class="inline">sha1</code> „hashen" (zu schnell, knackbar), niemals selbst ein Salt basteln – <code class="inline">password_hash</code> erledigt das. Vergleiche Hashes nur mit <code class="inline">password_verify</code> bzw. <code class="inline">hash_equals</code>, nie mit <code class="inline">==</code> (zeitbasierte Angriffe).</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 61</span><h1>Sessions & Cookies absichern</h1></div><p class="lead">Eine Session ist nur so sicher wie ihr Cookie und ihr Lebenszyklus. Ein paar Einstellungen und Gewohnheiten verhindern die häufigsten Übernahmen.</p>
<h2>Sichere Cookie-Einstellungen</h2>
<p>Setze die Session-Cookie-Optionen, bevor du die Session startest:</p>
<pre><span class="k">session_set_cookie_params</span>([
<span class="s">"secure"</span> => <span class="k">true</span>, <span class="c">// nur über HTTPS übertragen</span>
<span class="s">"httponly"</span> => <span class="k">true</span>, <span class="c">// kein Zugriff per JavaScript (gegen XSS-Diebstahl)</span>
<span class="s">"samesite"</span> => <span class="s">"Lax"</span>, <span class="c">// gegen CSRF</span>
]);
<span class="k">session_start</span>();</pre>
<h2>ID bei Rechtewechsel erneuern</h2>
<p>Nach Anmeldung oder Rechteänderung eine frische ID vergeben – gegen Session-Fixation:</p>
<pre><span class="k">session_regenerate_id</span>(<span class="k">true</span>); <span class="c">// alte ID verfällt</span></pre>
<h2>Sauber abmelden & ablaufen lassen</h2>
<pre><span class="v">$_SESSION</span> = [];
<span class="k">session_destroy</span>();</pre>
<p>Begrenze außerdem die Lebensdauer und prüfe Inaktivität – eine ewig gültige Session ist ein Risiko:</p>
<pre><span class="k">if</span> ((<span class="v">$_SESSION</span>[<span class="s">"letzte_aktion"</span>] ?? <span class="s">0</span>) < <span class="k">time</span>() - <span class="s">1800</span>) {
<span class="k">session_destroy</span>(); <span class="c">// nach 30 Min. Inaktivität raus</span>
}
<span class="v">$_SESSION</span>[<span class="s">"letzte_aktion"</span>] = <span class="k">time</span>();</pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>HTTPS ist die Voraussetzung</b>Ohne HTTPS lässt sich das Session-Cookie im Netzwerk mitlesen, und alle obigen Maßnahmen verpuffen. <code class="inline">secure</code> erzwingt die Übertragung nur über verschlüsselte Verbindungen – im Web heute Pflicht, nicht Kür.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 62</span><h1>Kryptografie & Sodium</h1></div><p class="lead">Wenn du Daten verschlüsseln oder signieren musst, baue nichts selbst. PHP hat mit der Sodium-Bibliothek moderne, sichere Bausteine direkt eingebaut.</p>
<h2>Zufall</h2>
<p>Sichere Tokens, Salts und Schlüssel kommen nur aus einem kryptografischen Zufallsgenerator:</p>
<pre><span class="v">$token</span> = <span class="k">bin2hex</span>(<span class="k">random_bytes</span>(<span class="s">32</span>)); <span class="c">// 64 Hex-Zeichen, unvorhersehbar</span>
<span class="k">echo</span> <span class="k">random_int</span>(<span class="s">100000</span>, <span class="s">999999</span>); <span class="c">// sicherer 6-stelliger Code</span></pre>
<h2>Symmetrisch verschlüsseln mit Sodium</h2>
<p>Ein Schlüssel ver- und entschlüsselt. Sodium erledigt Nonce und Authentifizierung korrekt:</p>
<pre><span class="v">$key</span> = <span class="k">sodium_crypto_secretbox_keygen</span>();
<span class="v">$nonce</span> = <span class="k">random_bytes</span>(<span class="t">SODIUM_CRYPTO_SECRETBOX_NONCEBYTES</span>);
<span class="v">$secret</span> = <span class="k">sodium_crypto_secretbox</span>(<span class="s">"vertraulich"</span>, <span class="v">$nonce</span>, <span class="v">$key</span>);
<span class="v">$plain</span> = <span class="k">sodium_crypto_secretbox_open</span>(<span class="v">$secret</span>, <span class="v">$nonce</span>, <span class="v">$key</span>);
<span class="k">echo</span> <span class="v">$plain</span>; <span class="c">// vertraulich</span></pre>
<p>Der <code class="inline">$nonce</code> muss pro Nachricht einmalig sein, darf aber öffentlich neben dem Geheimtext stehen.</p>
<h2>Werte vergleichen ohne Timing-Leck</h2>
<p>Geheimnisse (Tokens, Hashes) vergleichst du in konstanter Zeit, damit kein Angreifer aus der Antwortzeit Rückschlüsse zieht:</p>
<pre><span class="k">if</span> (<span class="k">hash_equals</span>(<span class="v">$expected</span>, <span class="v">$input</span>)) {
<span class="c">// gleich – sicher verglichen</span>
}</pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Niemals eigene Krypto</b>Selbstgebaute Verschlüsselung ist fast immer angreifbar. Nutze Sodium für Verschlüsselung/Signaturen, <code class="inline">password_hash</code> für Passwörter, <code class="inline">random_bytes</code>/<code class="inline">random_int</code> für Zufall. Verwechsle Kodierung (Base64) nicht mit Verschlüsselung – Base64 schützt nichts.</div>
</div></section><section class="part-divider"><div class="part-kicker">Teil 12</div><h1>Sauberes Design</h1><div class="part-desc">Wie man Code so baut, dass er wachsen, sich ändern und testen lässt: SOLID, Dependency Injection, Wertobjekte und eine durchdachte Fehlerstrategie.</div><div class="part-chapters"><span>63 · SOLID-Prinzipien</span><span>64 · Dependency Injection</span><span>65 · Wertobjekte & DTOs</span><span>66 · Fehlerbehandlung als Architektur</span><span>67 · Häufige Entwurfsmuster</span></div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 63</span><h1>SOLID-Prinzipien</h1></div><p class="lead">SOLID ist eine Eselsbrücke für fünf Prinzipien, die Code wandelbar und testbar halten. Jedes mit einer knappen Idee und einem Beispiel.</p>
<h2>S – Single Responsibility</h2>
<p>Eine Klasse hat genau einen Grund, sich zu ändern. Statt eine Klasse, die rechnet <i>und</i> speichert <i>und</i> mailt, trennst du das:</p>
<pre><span class="k">class</span> <span class="t">InvoiceCalculator</span> { <span class="k">public function</span> <span class="f">sum</span>(<span class="t">array</span> <span class="v">$items</span>): <span class="t">int</span> { <span class="k">return</span> <span class="k">array_sum</span>(<span class="v">$items</span>); } }
<span class="k">class</span> <span class="t">InvoiceStorage</span> { <span class="k">public function</span> <span class="f">save</span>(<span class="t">int</span> <span class="v">$sum</span>): <span class="t">void</span> {} }
<span class="c">// jede Klasse ein Zuständigkeitsbereich</span></pre>
<h2>O – Open/Closed</h2>
<p>Offen für Erweiterung, geschlossen für Änderung: Neues Verhalten durch neue Klassen, nicht durch Umbau bestehender. Eine neue Versandart ist eine neue Klasse, kein weiteres <code class="inline">if</code>:</p>
<pre><span class="k">interface</span> <span class="t">Shipping</span> { <span class="k">public function</span> <span class="f">cost</span>(): <span class="t">int</span>; }
<span class="k">class</span> <span class="t">Standard</span> <span class="k">implements</span> <span class="t">Shipping</span> { <span class="k">public function</span> <span class="f">cost</span>(): <span class="t">int</span> { <span class="k">return</span> <span class="s">499</span>; } }
<span class="k">class</span> <span class="t">Express</span> <span class="k">implements</span> <span class="t">Shipping</span> { <span class="k">public function</span> <span class="f">cost</span>(): <span class="t">int</span> { <span class="k">return</span> <span class="s">999</span>; } }</pre>
<h2>L – Liskov Substitution</h2>
<p>Ein Untertyp muss überall dort funktionieren, wo der Basistyp erwartet wird – ohne Überraschungen. Wer <code class="inline">Shipping</code> erwartet, darf <code class="inline">Express</code> bekommen und sich auf <code class="inline">cost()</code> verlassen.</p>
<h2>I – Interface Segregation</h2>
<p>Viele kleine, gezielte Interfaces statt eines großen. Niemand soll Methoden implementieren müssen, die er nicht braucht:</p>
<pre><span class="k">interface</span> <span class="t">Readable</span> { <span class="k">public function</span> <span class="f">read</span>(): <span class="t">string</span>; }
<span class="k">interface</span> <span class="t">Writable</span> { <span class="k">public function</span> <span class="f">write</span>(<span class="t">string</span> <span class="v">$s</span>): <span class="t">void</span>; }
<span class="c">// eine Klasse erfüllt nur, was sie wirklich kann</span></pre>
<h2>D – Dependency Inversion</h2>
<p>Hänge von Abstraktionen ab, nicht von konkreten Klassen. Der Bestellprozess kennt das Interface <code class="inline">Shipping</code>, nicht <code class="inline">Express</code> – die konkrete Wahl wird hineingereicht (nächstes Kapitel).</p>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Prinzipien, keine Gesetze</b>SOLID sind Heuristiken gegen starren, schwer testbaren Code – kein Selbstzweck. Wende sie an, wo Code sich tatsächlich ändert; übertreibe es nicht bei kleinen, stabilen Skripten. Der rote Faden: gegen Interfaces programmieren und Verantwortlichkeiten trennen.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 64</span><h1>Dependency Injection</h1></div><p class="lead">Dependency Injection (DI) heißt schlicht: Eine Klasse bekommt ihre Abhängigkeiten von außen gereicht, statt sie selbst zu erzeugen. Das macht sie austauschbar und testbar.</p>
<h2>Ohne DI</h2>
<p>Erzeugt eine Klasse ihre Abhängigkeit selbst, ist sie fest damit verdrahtet und im Test nicht austauschbar:</p>
<pre><span class="k">class</span> <span class="t">Order</span> {
<span class="k">private</span> <span class="t">MySqlStorage</span> <span class="v">$db</span>;
<span class="k">public function</span> <span class="f">__construct</span>() {
<span class="v">$this</span>-><span class="v">db</span> = <span class="k">new</span> <span class="t">MySqlStorage</span>(); <span class="c">// fest – im Test nicht ersetzbar</span>
}
}</pre>
<h2>Mit DI</h2>
<p>Die Abhängigkeit kommt als Konstruktor-Parameter, typisiert auf ein Interface:</p>
<pre><span class="k">interface</span> <span class="t">Storage</span> { <span class="k">public function</span> <span class="f">save</span>(<span class="t">array</span> <span class="v">$d</span>): <span class="t">void</span>; }
<span class="k">class</span> <span class="t">Order</span> {
<span class="k">public function</span> <span class="f">__construct</span>(<span class="k">private</span> <span class="t">Storage</span> <span class="v">$storage</span>) {}
<span class="k">public function</span> <span class="f">complete</span>(<span class="t">array</span> <span class="v">$d</span>): <span class="t">void</span> {
<span class="v">$this</span>-><span class="v">storage</span>-><span class="f">save</span>(<span class="v">$d</span>);
}
}
<span class="v">$b</span> = <span class="k">new</span> <span class="t">Order</span>(<span class="k">new</span> <span class="t">MySqlStorage</span>()); <span class="c">// echte DB</span>
<span class="v">$test</span> = <span class="k">new</span> <span class="t">Order</span>(<span class="k">new</span> <span class="t">StorageStub</span>()); <span class="c">// im Test ein Dummy</span></pre>
<p>Dieselbe Klasse funktioniert mit echter Datenbank, In-Memory-Attrappe oder einem anderen Backend – ohne sie zu ändern.</p>
<h2>Der DI-Container</h2>
<p>In größeren Projekten erzeugt ein Container die Objekte samt ihrer Abhängigkeiten automatisch (per Reflection, vgl. Teil 6). Du fragst nach einem Typ und bekommst ein fertig verdrahtetes Objekt:</p>
<pre><span class="v">$order</span> = <span class="v">$container</span>-><span class="f">get</span>(<span class="t">Order</span>::<span class="k">class</span>);
<span class="c">// Container baut selbst den passenden Speicher und reicht ihn hinein</span></pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Konstruktor-Injection als Standard</b>Reiche Pflicht-Abhängigkeiten über den Konstruktor herein und typisiere sie auf Interfaces. So ist auf einen Blick sichtbar, was eine Klasse braucht, sie lässt sich isoliert testen, und der Container kann sie automatisch zusammenbauen. Vermeide es, den Container überall durchzureichen (das wäre ein „Service Locator" – ein Anti-Muster).</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 65</span><h1>Wertobjekte & DTOs</h1></div><p class="lead">Nicht jedes Objekt hat eine Identität. Ein Wertobjekt ist allein durch seinen Wert definiert und unveränderlich; ein DTO transportiert nur Daten. Beide machen Code klarer und sicherer.</p>
<h2>Wertobjekt</h2>
<p>Ein Geldbetrag, eine E-Mail, ein Datum: gleich, wenn der Wert gleich ist, und immer gültig, weil der Konstruktor prüft:</p>
<pre><span class="k">final class</span> <span class="t">Email</span> {
<span class="k">public function</span> <span class="f">__construct</span>(<span class="k">public</span> <span class="k">readonly</span> <span class="t">string</span> <span class="v">$value</span>) {
<span class="k">if</span> (!<span class="k">filter_var</span>(<span class="v">$value</span>, <span class="t">FILTER_VALIDATE_EMAIL</span>)) {
<span class="k">throw</span> <span class="k">new</span> <span class="t">\InvalidArgumentException</span>(<span class="s">"keine E-Mail"</span>);
}
}
}
<span class="v">$e</span> = <span class="k">new</span> <span class="t">Email</span>(<span class="s">"a@b.de"</span>); <span class="c">// ab hier garantiert gültig</span>
<span class="k">echo</span> <span class="v">$e</span>-><span class="v">value</span>; <span class="c">// a@b.de</span>
<span class="c">// new Email("kaputt"); // wirft sofort</span></pre>
<p>Der Vorteil: Wer eine <code class="inline">Email</code> in der Hand hält, muss nie wieder prüfen, ob sie gültig ist.</p>
<h2>Ändern heißt: neues Objekt</h2>
<p>Da Wertobjekte unveränderlich sind, liefert eine „Änderung" eine neue Instanz – mit PHP 8.5 elegant per <code class="inline">clone with</code>:</p>
<pre><span class="k">final class</span> <span class="t">Money</span> {
<span class="k">public function</span> <span class="f">__construct</span>(<span class="k">public</span> <span class="k">readonly</span> <span class="t">int</span> <span class="v">$cent</span>) {}
<span class="k">public function</span> <span class="f">plus</span>(<span class="t">Money</span> <span class="v">$x</span>): <span class="k">static</span> { <span class="k">return</span> <span class="k">new</span> <span class="k">static</span>(<span class="v">$this</span>-><span class="v">cent</span> + <span class="v">$x</span>-><span class="v">cent</span>); }
}
<span class="k">echo</span> (<span class="k">new</span> <span class="t">Money</span>(<span class="s">1000</span>))-><span class="f">plus</span>(<span class="k">new</span> <span class="t">Money</span>(<span class="s">500</span>))-><span class="v">cent</span>; <span class="c">// 1500</span></pre>
<h2>DTO</h2>
<p>Ein Data Transfer Object bündelt zusammengehörige Felder, etwa eine validierte Formulareingabe, ohne Verhalten:</p>
<pre><span class="k">final class</span> <span class="t">RegistrationData</span> {
<span class="k">public function</span> <span class="f">__construct</span>(
<span class="k">public</span> <span class="k">readonly</span> <span class="t">string</span> <span class="v">$name</span>,
<span class="k">public</span> <span class="k">readonly</span> <span class="t">Email</span> <span class="v">$email</span>,
) {}
}</pre>
<p>Statt einem unklaren Array mit Stringschlüsseln reicht man ein typisiertes Objekt herum – die IDE kennt jedes Feld.</p>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Primitive Obsession vermeiden</b>Wo du sonst überall <code class="inline">string $email</code> oder <code class="inline">int $cent</code> durchreichst, lohnt ein Wertobjekt: Es bündelt die Gültigkeitsprüfung an einer Stelle und macht falsche Verwendung unmöglich. <code class="inline">readonly</code> und <code class="inline">final</code> sind dabei die natürlichen Begleiter.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 66</span><h1>Fehlerbehandlung als Architektur</h1></div><p class="lead">Wie eine Anwendung mit Fehlern umgeht, ist eine Designentscheidung – kein Detail. Eine durchdachte Strategie macht den Unterschied zwischen einer robusten App und einem Kartenhaus.</p>
<h2>Eigene, sprechende Exception-Typen</h2>
<p>Statt überall generische Exceptions zu werfen, baue eine kleine Hierarchie, die deine Domäne abbildet:</p>
<pre><span class="k">class</span> <span class="t">DomainException</span> <span class="k">extends</span> <span class="t">\Exception</span> {}
<span class="k">class</span> <span class="t">AccountOverdrawn</span> <span class="k">extends</span> <span class="t">DomainException</span> {}
<span class="k">class</span> <span class="t">AccountLocked</span> <span class="k">extends</span> <span class="t">DomainException</span> {}
<span class="k">function</span> <span class="f">withdraw</span>(<span class="t">int</span> <span class="v">$balance</span>, <span class="t">int</span> <span class="v">$amount</span>): <span class="t">int</span> {
<span class="k">if</span> (<span class="v">$amount</span> > <span class="v">$balance</span>) <span class="k">throw</span> <span class="k">new</span> <span class="t">AccountOverdrawn</span>();
<span class="k">return</span> <span class="v">$balance</span> - <span class="v">$amount</span>;
}</pre>
<h2>Fangen, wo man handeln kann</h2>
<p>Eine Exception steigt auf, bis eine Stelle sie sinnvoll behandeln kann. Tief im Code wirfst du nur; oben – z. B. im Controller – entscheidest du über die Reaktion:</p>
<pre><span class="k">try</span> {
<span class="v">$fresh</span> = <span class="f">withdraw</span>(<span class="v">$balance</span>, <span class="v">$amount</span>);
} <span class="k">catch</span> (<span class="t">AccountOverdrawn</span>) {
<span class="k">http_response_code</span>(<span class="s">422</span>);
<span class="k">echo</span> <span class="s">"Betrag übersteigt das Guthaben"</span>;
}</pre>
<h2>Eine zentrale Fehlerstelle</h2>
<p>Ganz außen fängt ein globaler Handler alles Unerwartete, protokolliert es und zeigt dem Nutzer eine neutrale Meldung – nie einen Stacktrace:</p>
<pre><span class="k">set_exception_handler</span>(<span class="k">function</span>(<span class="t">\Throwable</span> <span class="v">$e</span>) {
<span class="k">error_log</span>((<span class="t">string</span>) <span class="v">$e</span>); <span class="c">// für Entwickler ins Log</span>
<span class="k">http_response_code</span>(<span class="s">500</span>);
<span class="k">echo</span> <span class="s">"Es ist ein Fehler aufgetreten."</span>; <span class="c">// für den Nutzer neutral</span>
});</pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Erwartet vs. unerwartet trennen</b>Fachliche, erwartbare Fälle (Konto überzogen) behandelst du gezielt nah am Geschehen. Unerwartete Fehler (DB weg, Bug) lässt du bis zum zentralen Handler durchlaufen, der protokolliert und neutral antwortet. Zeige Endnutzern niemals interne Details – das hilft nur Angreifern.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 67</span><h1>Häufige Entwurfsmuster</h1></div><p class="lead">Bevor der nächste Teil den vollständigen Muster-Katalog systematisch durchgeht, hier die drei, die dir im PHP-Alltag am häufigsten begegnen – jeweils mit lauffähigem Mini-Beispiel.</p>
<h2>Strategy</h2>
<p>Ein Algorithmus wird hinter einem Interface gekapselt und zur Laufzeit gewählt:</p>
<pre><span class="k">interface</span> <span class="t">Discount</span> { <span class="k">public function</span> <span class="f">apply</span>(<span class="t">int</span> <span class="v">$cent</span>): <span class="t">int</span>; }
<span class="k">class</span> <span class="t">NoDiscount</span> <span class="k">implements</span> <span class="t">Discount</span> { <span class="k">public function</span> <span class="f">apply</span>(<span class="t">int</span> <span class="v">$c</span>): <span class="t">int</span> { <span class="k">return</span> <span class="v">$c</span>; } }
<span class="k">class</span> <span class="t">PercentageDiscount</span> <span class="k">implements</span> <span class="t">Discount</span> {
<span class="k">public function</span> <span class="f">__construct</span>(<span class="k">private</span> <span class="t">int</span> <span class="v">$percent</span>) {}
<span class="k">public function</span> <span class="f">apply</span>(<span class="t">int</span> <span class="v">$c</span>): <span class="t">int</span> { <span class="k">return</span> (<span class="t">int</span>) (<span class="v">$c</span> * (<span class="s">100</span> - <span class="v">$this</span>-><span class="v">percent</span>) / <span class="s">100</span>); }
}
<span class="k">function</span> <span class="f">finalPrice</span>(<span class="t">int</span> <span class="v">$c</span>, <span class="t">Discount</span> <span class="v">$r</span>): <span class="t">int</span> { <span class="k">return</span> <span class="v">$r</span>-><span class="f">apply</span>(<span class="v">$c</span>); }
<span class="k">echo</span> <span class="f">finalPrice</span>(<span class="s">1000</span>, <span class="k">new</span> <span class="t">PercentageDiscount</span>(<span class="s">20</span>)); <span class="c">// 800</span></pre>
<h2>Factory</h2>
<p>Eine Funktion oder Methode entscheidet, welches Objekt entsteht – der Aufrufer muss die konkreten Klassen nicht kennen:</p>
<pre><span class="k">function</span> <span class="f">discountFor</span>(<span class="t">string</span> <span class="v">$customer</span>): <span class="t">Discount</span> {
<span class="k">return</span> <span class="k">match</span>(<span class="v">$customer</span>) {
<span class="s">"stamm"</span> => <span class="k">new</span> <span class="t">PercentageDiscount</span>(<span class="s">10</span>),
<span class="k">default</span> => <span class="k">new</span> <span class="t">NoDiscount</span>(),
};
}
<span class="k">echo</span> <span class="f">finalPrice</span>(<span class="s">1000</span>, <span class="f">discountFor</span>(<span class="s">"stamm"</span>)); <span class="c">// 900</span></pre>
<h2>Observer</h2>
<p>Ein Subjekt benachrichtigt mehrere Beobachter, ohne sie konkret zu kennen:</p>
<pre><span class="k">interface</span> <span class="t">Observer</span> { <span class="k">public function</span> <span class="f">notify</span>(<span class="t">string</span> <span class="v">$event</span>): <span class="t">void</span>; }
<span class="k">class</span> <span class="t">Newsletter</span> <span class="k">implements</span> <span class="t">Observer</span> {
<span class="k">public function</span> <span class="f">notify</span>(<span class="t">string</span> <span class="v">$e</span>): <span class="t">void</span> { <span class="k">echo</span> <span class="s">"Mail: $e\n"</span>; }
}
<span class="k">class</span> <span class="t">Shop</span> {
<span class="k">private</span> <span class="t">array</span> <span class="v">$observers</span> = [];
<span class="k">public function</span> <span class="f">subscribe</span>(<span class="t">Observer</span> <span class="v">$b</span>): <span class="t">void</span> { <span class="v">$this</span>-><span class="v">observers</span>[] = <span class="v">$b</span>; }
<span class="k">public function</span> <span class="f">sell</span>(): <span class="t">void</span> {
<span class="k">foreach</span> (<span class="v">$this</span>-><span class="v">observers</span> <span class="k">as</span> <span class="v">$b</span>) <span class="v">$b</span>-><span class="f">notify</span>(<span class="s">"Verkauf"</span>);
}
}
<span class="v">$shop</span> = <span class="k">new</span> <span class="t">Shop</span>(); <span class="v">$shop</span>-><span class="f">subscribe</span>(<span class="k">new</span> <span class="t">Newsletter</span>());
<span class="v">$shop</span>-><span class="f">sell</span>(); <span class="c">// Mail: Verkauf</span></pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Muster sind Vokabeln</b>Entwurfsmuster sind bewährte Lösungen mit Namen – sie helfen vor allem, im Team über Struktur zu reden. Setze sie ein, wenn das Problem wirklich da ist, nicht auf Vorrat. Der nächste Teil ordnet den vollständigen Katalog systematisch.</div>
</div></section><section class="part-divider"><div class="part-kicker">Teil 13</div><h1>Entwurfsmuster & Architektur</h1><div class="part-desc">Der systematische Muster-Katalog und die großen Architekturansätze – von MVC über DDD bis zur hexagonalen Architektur.</div><div class="part-chapters"><span>68 · Erzeugungsmuster</span><span>69 · Strukturmuster</span><span>70 · Verhaltensmuster</span><span>71 · MVC & Schichtenarchitektur</span><span>72 · Domain-Driven Design</span><span>73 · Event-getriebene Architektur</span><span>74 · Hexagonale Architektur</span></div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 68</span><h1>Erzeugungsmuster</h1></div><p class="lead">Erzeugungsmuster (Creational Patterns) regeln, <i>wie</i> Objekte entstehen – getrennt von ihrer Verwendung. So bleibt der Code flexibel, wenn sich die Erzeugung ändert.</p>
<h2>Factory Method</h2>
<p>Eine Methode liefert das passende Objekt, der Aufrufer kennt nur das Interface:</p>
<pre><span class="k">interface</span> <span class="t">Exporter</span> { <span class="k">public function</span> <span class="f">export</span>(<span class="t">array</span> <span class="v">$d</span>): <span class="t">string</span>; }
<span class="k">class</span> <span class="t">JsonExporter</span> <span class="k">implements</span> <span class="t">Exporter</span> {
<span class="k">public function</span> <span class="f">export</span>(<span class="t">array</span> <span class="v">$d</span>): <span class="t">string</span> { <span class="k">return</span> <span class="k">json_encode</span>(<span class="v">$d</span>); }
}
<span class="k">function</span> <span class="f">exporter</span>(<span class="t">string</span> <span class="v">$format</span>): <span class="t">Exporter</span> {
<span class="k">return</span> <span class="k">match</span>(<span class="v">$format</span>) { <span class="s">"json"</span> => <span class="k">new</span> <span class="t">JsonExporter</span>() };
}
<span class="k">echo</span> <span class="f">exporter</span>(<span class="s">"json"</span>)-><span class="f">export</span>([<span class="s">"a"</span> => <span class="s">1</span>]); <span class="c">// {"a":1}</span></pre>
<h2>Builder</h2>
<p>Baut ein komplexes Objekt Schritt für Schritt – lesbar durch verkettete Aufrufe:</p>
<pre><span class="k">class</span> <span class="t">QueryBuilder</span> {
<span class="k">private</span> <span class="t">array</span> <span class="v">$parts</span> = [];
<span class="k">public function</span> <span class="f">select</span>(<span class="t">string</span> <span class="v">$s</span>): <span class="k">static</span> { <span class="v">$this</span>-><span class="v">parts</span>[<span class="s">"select"</span>] = <span class="v">$s</span>; <span class="k">return</span> <span class="v">$this</span>; }
<span class="k">public function</span> <span class="f">from</span>(<span class="t">string</span> <span class="v">$t</span>): <span class="k">static</span> { <span class="v">$this</span>-><span class="v">parts</span>[<span class="s">"from"</span>] = <span class="v">$t</span>; <span class="k">return</span> <span class="v">$this</span>; }
<span class="k">public function</span> <span class="f">sql</span>(): <span class="t">string</span> { <span class="k">return</span> <span class="s">"SELECT {</span><span class="v">$this</span>-><span class="v">parts</span>[<span class="s">'select'</span>]<span class="s">} FROM {</span><span class="v">$this</span>-><span class="v">parts</span>[<span class="s">'from'</span>]<span class="s">}"</span>; }
}
<span class="k">echo</span> (<span class="k">new</span> <span class="t">QueryBuilder</span>())-><span class="f">select</span>(<span class="s">"*"</span>)-><span class="f">from</span>(<span class="s">"nutzer"</span>)-><span class="f">sql</span>(); <span class="c">// SELECT * FROM nutzer</span></pre>
<h2>Singleton</h2>
<p>Stellt sicher, dass es von einer Klasse nur eine Instanz gibt:</p>
<pre><span class="k">class</span> <span class="t">Config</span> {
<span class="k">private static</span> <span class="t">?Konfig</span> <span class="v">$instance</span> = <span class="k">null</span>;
<span class="k">private function</span> <span class="f">__construct</span>() {}
<span class="k">public static function</span> <span class="f">get</span>(): <span class="k">static</span> {
<span class="k">return</span> <span class="k">self</span>::<span class="v">$instance</span> ??= <span class="k">new</span> <span class="k">static</span>();
}
}
<span class="k">var_dump</span>(<span class="t">Config</span>::<span class="f">get</span>() === <span class="t">Config</span>::<span class="f">get</span>()); <span class="c">// true – stets dasselbe Objekt</span></pre>
<p>Singletons sind globaler Zustand und erschweren Tests – in modernem Code übernimmt meist der DI-Container die „eine Instanz".</p>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Erzeugung vom Rest trennen</b>Der gemeinsame Nutzen: Wo und wie ein Objekt entsteht, ist von seiner Verwendung entkoppelt. Ändert sich die Konstruktion (neue Pflichtabhängigkeit, anderes Backend), bleibt der nutzende Code unberührt.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 69</span><h1>Strukturmuster</h1></div><p class="lead">Strukturmuster (Structural Patterns) setzen Klassen und Objekte zu größeren Strukturen zusammen – etwa um Schnittstellen anzupassen oder Verhalten zu ergänzen.</p>
<h2>Adapter</h2>
<p>Macht eine fremde Klasse mit unpassender Schnittstelle nutzbar, ohne sie zu ändern:</p>
<pre><span class="k">interface</span> <span class="t">Logger</span> { <span class="k">public function</span> <span class="f">log</span>(<span class="t">string</span> <span class="v">$m</span>): <span class="t">void</span>; }
<span class="k">class</span> <span class="t">ThirdPartyLog</span> { <span class="k">public function</span> <span class="f">write</span>(<span class="t">string</span> <span class="v">$t</span>): <span class="t">void</span> { <span class="k">echo</span> <span class="v">$t</span>; } }
<span class="k">class</span> <span class="t">ThirdPartyLogAdapter</span> <span class="k">implements</span> <span class="t">Logger</span> {
<span class="k">public function</span> <span class="f">__construct</span>(<span class="k">private</span> <span class="t">ThirdPartyLog</span> <span class="v">$f</span>) {}
<span class="k">public function</span> <span class="f">log</span>(<span class="t">string</span> <span class="v">$m</span>): <span class="t">void</span> { <span class="v">$this</span>-><span class="v">f</span>-><span class="f">write</span>(<span class="v">$m</span>); } <span class="c">// übersetzt log() auf write()</span>
}
(<span class="k">new</span> <span class="t">ThirdPartyLogAdapter</span>(<span class="k">new</span> <span class="t">ThirdPartyLog</span>()))-><span class="f">log</span>(<span class="s">"Hallo"</span>); <span class="c">// Hallo</span></pre>
<h2>Decorator</h2>
<p>Legt sich um ein Objekt und erweitert es, ohne dessen Klasse zu ändern – beliebig stapelbar:</p>
<pre><span class="k">class</span> <span class="t">TimestampLog</span> <span class="k">implements</span> <span class="t">Logger</span> {
<span class="k">public function</span> <span class="f">__construct</span>(<span class="k">private</span> <span class="t">Logger</span> <span class="v">$inner</span>) {}
<span class="k">public function</span> <span class="f">log</span>(<span class="t">string</span> <span class="v">$m</span>): <span class="t">void</span> {
<span class="v">$this</span>-><span class="v">inner</span>-><span class="f">log</span>(<span class="s">"[2026] "</span> . <span class="v">$m</span>); <span class="c">// ergänzt, dann weiterreichen</span>
}
}
(<span class="k">new</span> <span class="t">TimestampLog</span>(<span class="k">new</span> <span class="t">ThirdPartyLogAdapter</span>(<span class="k">new</span> <span class="t">ThirdPartyLog</span>())))-><span class="f">log</span>(<span class="s">"X"</span>); <span class="c">// [2026] X</span></pre>
<h2>Facade</h2>
<p>Bündelt ein kompliziertes Zusammenspiel hinter einer schlichten Schnittstelle:</p>
<pre><span class="k">class</span> <span class="t">Checkout</span> {
<span class="k">public function</span> <span class="f">__construct</span>(<span class="k">private</span> <span class="t">Inventory</span> <span class="v">$l</span>, <span class="k">private</span> <span class="t">Payment</span> <span class="v">$z</span>, <span class="k">private</span> <span class="t">Shipping</span> <span class="v">$v</span>) {}
<span class="k">public function</span> <span class="f">complete</span>(<span class="t">int</span> <span class="v">$id</span>): <span class="t">void</span> {
<span class="v">$this</span>-><span class="v">l</span>-><span class="f">reserve</span>(<span class="v">$id</span>); <span class="v">$this</span>-><span class="v">z</span>-><span class="f">book</span>(<span class="v">$id</span>); <span class="v">$this</span>-><span class="v">v</span>-><span class="f">start</span>(<span class="v">$id</span>);
} <span class="c">// ein Aufruf statt drei Subsysteme von Hand zu orchestrieren</span>
}</pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Komposition als Werkzeug</b>Strukturmuster bauen alle auf demselben Gedanken: ein Objekt hält ein anderes und leitet weiter. Adapter übersetzt, Decorator ergänzt, Facade vereinfacht. Genau das ist „Komposition vor Vererbung" in der Praxis.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 70</span><h1>Verhaltensmuster</h1></div><p class="lead">Verhaltensmuster (Behavioral Patterns) regeln, wie Objekte zusammenarbeiten und Verantwortung verteilen – wer wen wann aufruft.</p>
<h2>Strategy</h2>
<p>Austauschbare Algorithmen hinter einem Interface (im vorigen Teil eingeführt). Kurz als Erinnerung mit Ausgabe:</p>
<pre><span class="k">interface</span> <span class="t">Sorting</span> { <span class="k">public function</span> <span class="f">sort</span>(<span class="t">array</span> <span class="v">$a</span>): <span class="t">array</span>; }
<span class="k">class</span> <span class="t">Ascending</span> <span class="k">implements</span> <span class="t">Sorting</span> {
<span class="k">public function</span> <span class="f">sort</span>(<span class="t">array</span> <span class="v">$a</span>): <span class="t">array</span> { <span class="k">sort</span>(<span class="v">$a</span>); <span class="k">return</span> <span class="v">$a</span>; }
}
<span class="k">print_r</span>((<span class="k">new</span> <span class="t">Ascending</span>())-><span class="f">sort</span>([<span class="s">3</span>, <span class="s">1</span>, <span class="s">2</span>])); <span class="c">// [1, 2, 3]</span></pre>
<h2>Observer</h2>
<p>Mehrere Zuhörer reagieren auf ein Ereignis – die Basis von Event-Systemen (eigenes Architektur-Kapitel folgt).</p>
<h2>Command</h2>
<p>Eine Aktion wird zum Objekt mit einer <code class="inline">ausfuehren()</code>-Methode – so lässt sie sich speichern, in eine Queue legen oder rückgängig machen:</p>
<pre><span class="k">interface</span> <span class="t">Command</span> { <span class="k">public function</span> <span class="f">ausfuehren</span>(): <span class="t">string</span>; }
<span class="k">class</span> <span class="t">SendMail</span> <span class="k">implements</span> <span class="t">Command</span> {
<span class="k">public function</span> <span class="f">__construct</span>(<span class="k">private</span> <span class="t">string</span> <span class="v">$to</span>) {}
<span class="k">public function</span> <span class="f">ausfuehren</span>(): <span class="t">string</span> { <span class="k">return</span> <span class="s">"Mail an {</span><span class="v">$this</span>-><span class="v">to</span><span class="s">}"</span>; }
}
<span class="v">$queue</span> = [<span class="k">new</span> <span class="t">SendMail</span>(<span class="s">"a@b.de"</span>), <span class="k">new</span> <span class="t">SendMail</span>(<span class="s">"c@d.de"</span>)];
<span class="k">foreach</span> (<span class="v">$queue</span> <span class="k">as</span> <span class="v">$k</span>) <span class="k">echo</span> <span class="v">$k</span>-><span class="f">ausfuehren</span>() . <span class="s">"\n"</span>; <span class="c">// abgearbeitet</span></pre>
<h2>Template Method</h2>
<p>Eine Basisklasse legt den Ablauf fest und lässt einzelne Schritte offen (vgl. abstrakte Klassen):</p>
<pre><span class="k">abstract class</span> <span class="t">Import</span> {
<span class="k">public function</span> <span class="f">run</span>(): <span class="t">string</span> { <span class="k">return</span> <span class="v">$this</span>-><span class="f">read</span>() . <span class="s">" -> gespeichert"</span>; } <span class="c">// fester Ablauf</span>
<span class="k">abstract protected function</span> <span class="f">read</span>(): <span class="t">string</span>; <span class="c">// variabler Schritt</span>
}
<span class="k">class</span> <span class="t">CsvImport</span> <span class="k">extends</span> <span class="t">Import</span> {
<span class="k">protected function</span> <span class="f">read</span>(): <span class="t">string</span> { <span class="k">return</span> <span class="s">"CSV gelesen"</span>; }
}
<span class="k">echo</span> (<span class="k">new</span> <span class="t">CsvImport</span>())-><span class="f">run</span>(); <span class="c">// CSV gelesen -> gespeichert</span></pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Verantwortung verteilen</b>Verhaltensmuster beantworten „wer macht was?". Strategy lagert <i>wie</i> aus, Command verpackt <i>was</i>, Observer verteilt <i>Reaktionen</i>, Template Method fixiert die <i>Reihenfolge</i>. Viele moderne Frameworks bestehen im Kern aus diesen Mustern.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 71</span><h1>MVC & Schichtenarchitektur</h1></div><p class="lead">MVC und Schichten ordnen eine ganze Anwendung. Sie trennen, was zusammengehört: Darstellung, Ablaufsteuerung und Fachlogik bekommen je ihren Platz.</p>
<h2>Die drei Rollen von MVC</h2>
<p><b>Model</b> hält Daten und Fachlogik, <b>View</b> stellt dar, <b>Controller</b> nimmt die Anfrage entgegen und koordiniert:</p>
<pre><span class="c">// Model: Fachlogik, kennt weder HTTP noch HTML</span>
<span class="k">class</span> <span class="t">Cart</span> {
<span class="k">private</span> <span class="t">array</span> <span class="v">$items</span> = [];
<span class="k">public function</span> <span class="f">add</span>(<span class="t">int</span> <span class="v">$cent</span>): <span class="t">void</span> { <span class="v">$this</span>-><span class="v">items</span>[] = <span class="v">$cent</span>; }
<span class="k">public function</span> <span class="f">sum</span>(): <span class="t">int</span> { <span class="k">return</span> <span class="k">array_sum</span>(<span class="v">$this</span>-><span class="v">items</span>); }
}
<span class="c">// Controller: verbindet Request, Model und View</span>
<span class="k">class</span> <span class="t">CartController</span> {
<span class="k">public function</span> <span class="f">show</span>(<span class="t">Cart</span> <span class="v">$k</span>): <span class="t">string</span> {
<span class="k">return</span> <span class="s">"Summe: "</span> . <span class="k">number_format</span>(<span class="v">$k</span>-><span class="f">sum</span>() / <span class="s">100</span>, <span class="s">2</span>) . <span class="s">" €"</span>; <span class="c">// View-Aufbereitung</span>
}
}
<span class="v">$k</span> = <span class="k">new</span> <span class="t">Cart</span>(); <span class="v">$k</span>-><span class="f">add</span>(<span class="s">1999</span>); <span class="v">$k</span>-><span class="f">add</span>(<span class="s">500</span>);
<span class="k">echo</span> (<span class="k">new</span> <span class="t">CartController</span>())-><span class="f">show</span>(<span class="v">$k</span>); <span class="c">// Summe: 24.99 €</span></pre>
<h2>Schichten</h2>
<p>Etwas allgemeiner ordnet man Code in Schichten, die nur nach unten zeigen: <b>Präsentation</b> (Controller, Views) → <b>Anwendung/Domäne</b> (Fachlogik) → <b>Infrastruktur</b> (Datenbank, externe Dienste). Die Domäne kennt keine Datenbank, nur ein Interface – sie bleibt rein.</p>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Logik raus aus dem Controller</b>Der häufigste Anfängerfehler ist der „dicke Controller", der alles selbst macht. Halte Controller dünn: Sie nehmen die Eingabe entgegen, rufen die Fachlogik (Model/Service) und wählen die Ausgabe. Die eigentliche Logik gehört ins Model bzw. in Services – dort ist sie testbar und wiederverwendbar.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 72</span><h1>Domain-Driven Design</h1></div><p class="lead">Domain-Driven Design (DDD) stellt die Fachlichkeit in den Mittelpunkt: Der Code spricht die Sprache der Domäne, und seine Struktur folgt dem Geschäft, nicht der Technik.</p>
<h2>Allgegenwärtige Sprache</h2>
<p>Klassen und Methoden heißen wie die Fachbegriffe. Ein Fachexperte würde den Code „lesen" können:</p>
<pre><span class="k">class</span> <span class="t">Order</span> {
<span class="k">public function</span> <span class="f">place</span>(): <span class="t">void</span> {}
<span class="k">public function</span> <span class="f">cancel</span>(): <span class="t">void</span> {} <span class="c">// fachliche Verben, keine setStatus()</span>
}</pre>
<h2>Entitäten & Wertobjekte</h2>
<p>Eine <b>Entität</b> hat eine Identität über die Zeit (eine Bestellung mit ID); ein <b>Wertobjekt</b> ist durch seinen Wert definiert (ein Geldbetrag, eine Adresse) und unveränderlich – siehe Teil 12.</p>
<h2>Aggregate</h2>
<p>Ein Aggregat ist eine Einheit aus Objekten mit einer Wurzel, die als einziger Zugang dient und die Regeln wahrt. Posten ändert man nur über die Bestellung, nie direkt:</p>
<pre><span class="k">class</span> <span class="t">Order</span> { <span class="c">// Aggregat-Wurzel</span>
<span class="k">private</span> <span class="t">array</span> <span class="v">$items</span> = [];
<span class="k">public function</span> <span class="f">addItem</span>(<span class="t">Item</span> <span class="v">$p</span>): <span class="t">void</span> {
<span class="k">if</span> (<span class="v">$this</span>-><span class="f">isCompleted</span>()) <span class="k">throw</span> <span class="k">new</span> <span class="t">\DomainException</span>(<span class="s">"abgeschlossen"</span>);
<span class="v">$this</span>-><span class="v">items</span>[] = <span class="v">$p</span>; <span class="c">// Regel an einer Stelle erzwungen</span>
}
<span class="k">private function</span> <span class="f">isCompleted</span>(): <span class="t">bool</span> { <span class="k">return</span> <span class="k">false</span>; }
}</pre>
<h2>Repository</h2>
<p>Lädt und speichert Aggregate so, als wären sie eine Sammlung im Speicher – die Datenbank bleibt dahinter verborgen:</p>
<pre><span class="k">interface</span> <span class="t">OrderRepository</span> {
<span class="k">public function</span> <span class="f">find</span>(<span class="t">int</span> <span class="v">$id</span>): <span class="t">?Bestellung</span>;
<span class="k">public function</span> <span class="f">save</span>(<span class="t">Order</span> <span class="v">$b</span>): <span class="t">void</span>;
}</pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Für komplexe Domänen</b>DDD entfaltet seinen Wert bei fachlich anspruchsvollen Systemen mit vielen Regeln. Für eine simple CRUD-Anwendung ist der volle Apparat überzogen. Der Kerngedanke lohnt aber fast immer: Lass den Code die Fachsprache sprechen und schiebe Technik an den Rand.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 73</span><h1>Event-getriebene Architektur</h1></div><p class="lead">Statt dass Komponenten sich direkt aufrufen, melden sie Ereignisse, auf die andere reagieren. Das entkoppelt: Der Auslöser muss nicht wissen, wer zuhört.</p>
<h2>Ereignis & Zuhörer</h2>
<p>Ein Ereignis ist ein einfaches Objekt mit den Fakten; Zuhörer reagieren darauf:</p>
<pre><span class="k">final class</span> <span class="t">OrderPlaced</span> {
<span class="k">public function</span> <span class="f">__construct</span>(<span class="k">public</span> <span class="k">readonly</span> <span class="t">int</span> <span class="v">$orderId</span>) {}
}
<span class="k">interface</span> <span class="t">Listener</span> { <span class="k">public function</span> <span class="f">__invoke</span>(<span class="t">object</span> <span class="v">$event</span>): <span class="t">void</span>; }</pre>
<h2>Ein einfacher Dispatcher</h2>
<p>Eine zentrale Stelle nimmt Ereignisse an und ruft alle passenden Zuhörer:</p>
<pre><span class="k">class</span> <span class="t">Dispatcher</span> {
<span class="k">private</span> <span class="t">array</span> <span class="v">$listeners</span> = [];
<span class="k">public function</span> <span class="f">on</span>(<span class="t">string</span> <span class="v">$event</span>, <span class="t">callable</span> <span class="v">$z</span>): <span class="t">void</span> { <span class="v">$this</span>-><span class="v">listeners</span>[<span class="v">$event</span>][] = <span class="v">$z</span>; }
<span class="k">public function</span> <span class="f">dispatch</span>(<span class="t">object</span> <span class="v">$e</span>): <span class="t">void</span> {
<span class="k">foreach</span> (<span class="v">$this</span>-><span class="v">listeners</span>[<span class="v">$e</span>::<span class="k">class</span>] ?? [] <span class="k">as</span> <span class="v">$z</span>) <span class="v">$z</span>(<span class="v">$e</span>);
}
}
<span class="v">$d</span> = <span class="k">new</span> <span class="t">Dispatcher</span>();
<span class="v">$d</span>-><span class="f">on</span>(<span class="t">OrderPlaced</span>::<span class="k">class</span>, <span class="k">fn</span>(<span class="v">$e</span>) => <span class="k">print</span>(<span class="s">"Mail für #{</span><span class="v">$e</span>-><span class="v">orderId</span><span class="s">}\n"</span>));
<span class="v">$d</span>-><span class="f">on</span>(<span class="t">OrderPlaced</span>::<span class="k">class</span>, <span class="k">fn</span>(<span class="v">$e</span>) => <span class="k">print</span>(<span class="s">"Lager prüfen #{</span><span class="v">$e</span>-><span class="v">orderId</span><span class="s">}\n"</span>));
<span class="v">$d</span>-><span class="f">dispatch</span>(<span class="k">new</span> <span class="t">OrderPlaced</span>(<span class="s">7</span>)); <span class="c">// beide Zuhörer laufen</span></pre>
<h2>Synchron oder asynchron</h2>
<p>Zuhörer können sofort laufen oder – über eine Queue (Teil 15) – später in einem Hintergrundprozess. So bleibt die Web-Antwort schnell, während Mails und Berichte nebenher entstehen.</p>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Entkopplung mit Augenmaß</b>Events glänzen, wenn ein Vorgang mehrere unabhängige Folgen hat (Bestellung → Mail, Lager, Statistik). Sie erschweren aber das Nachvollziehen des Ablaufs – „wer reagiert eigentlich worauf?". Setze sie dort ein, wo die Entkopplung den Preis wert ist, und dokumentiere die Ereignisse gut.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 74</span><h1>Hexagonale Architektur</h1></div><p class="lead">Die hexagonale Architektur (Ports & Adapters) stellt die Fachlogik in die Mitte und schiebt alle Technik (Web, Datenbank, APIs) an den Rand. Der Kern weiß nichts von der Außenwelt.</p>
<h2>Ports</h2>
<p>Der Kern definiert, was er braucht, als Interface (Port). Wie es erfüllt wird, ist ihm egal:</p>
<pre><span class="c">// im Kern – ein Port (was gebraucht wird)</span>
<span class="k">interface</span> <span class="t">UserRepository</span> {
<span class="k">public function</span> <span class="f">find</span>(<span class="t">int</span> <span class="v">$id</span>): <span class="t">?array</span>;
}
<span class="c">// Fachlogik nutzt nur den Port</span>
<span class="k">class</span> <span class="t">UserService</span> {
<span class="k">public function</span> <span class="f">__construct</span>(<span class="k">private</span> <span class="t">UserRepository</span> <span class="v">$repo</span>) {}
<span class="k">public function</span> <span class="f">name</span>(<span class="t">int</span> <span class="v">$id</span>): <span class="t">string</span> {
<span class="k">return</span> <span class="v">$this</span>-><span class="v">repo</span>-><span class="f">find</span>(<span class="v">$id</span>)[<span class="s">"name"</span>] ?? <span class="s">"unbekannt"</span>;
}
}</pre>
<h2>Adapter</h2>
<p>Ein Adapter erfüllt den Port mit konkreter Technik – hier eine DB-Variante und eine Test-Variante:</p>
<pre><span class="k">class</span> <span class="t">PdoUserRepository</span> <span class="k">implements</span> <span class="t">UserRepository</span> {
<span class="k">public function</span> <span class="f">find</span>(<span class="t">int</span> <span class="v">$id</span>): <span class="t">?array</span> { <span class="c">/* echte DB */</span> <span class="k">return</span> [<span class="s">"name"</span> => <span class="s">"Anna"</span>]; }
}
<span class="k">class</span> <span class="t">ArrayUserRepository</span> <span class="k">implements</span> <span class="t">UserRepository</span> {
<span class="k">public function</span> <span class="f">find</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> => <span class="s">"Test"</span>]; } <span class="c">// für Tests</span>
}
<span class="k">echo</span> (<span class="k">new</span> <span class="t">UserService</span>(<span class="k">new</span> <span class="t">PdoUserRepository</span>()))-><span class="f">name</span>(<span class="s">1</span>); <span class="c">// Anna</span>
<span class="k">echo</span> (<span class="k">new</span> <span class="t">UserService</span>(<span class="k">new</span> <span class="t">ArrayUserRepository</span>()))-><span class="f">name</span>(<span class="s">1</span>); <span class="c">// Test</span></pre>
<p>Derselbe Kern läuft mit echter Datenbank im Betrieb und mit einer Attrappe im Test – ohne eine Zeile Fachlogik zu ändern.</p>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Der Kern bleibt rein</b>Die Leitidee aller dieser Architekturen ist dieselbe: Abhängigkeiten zeigen nach innen, zur Fachlogik – nie umgekehrt. Der Kern kennt nur Interfaces; Datenbank, Framework und HTTP sind austauschbare Details am Rand. Das macht die Logik langlebig und gründlich testbar.</div>
</div></section><section class="part-divider"><div class="part-kicker">Teil 14</div><h1>Qualität & Performance</h1><div class="part-desc">Was professionellen Code ausmacht: automatisierte Tests, statische Analyse, einheitlicher Stil, Debugging – und das Verständnis für Tempo und Speicher.</div><div class="part-chapters"><span>75 · Testen mit PHPUnit</span><span>76 · Statische Analyse</span><span>77 · Code-Style & Tooling</span><span>78 · Debugging & Xdebug</span><span>79 · Performance & OPcache</span><span>80 · Speicher & Referenzen</span></div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 75</span><h1>Testen mit PHPUnit</h1></div><p class="lead">Automatisierte Tests prüfen bei jeder Änderung, ob der Code noch tut, was er soll. PHPUnit ist der Standard – ein Test ist einfach Code, der deinen Code aufruft und das Ergebnis behauptet.</p>
<h2>Ein einfacher Test</h2>
<pre><span class="k">use</span> <span class="t">PHPUnit\Framework\TestCase</span>;
<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">testAdds</span>(): <span class="t">void</span> {
<span class="v">$r</span> = <span class="k">new</span> <span class="t">Calculator</span>();
<span class="v">$this</span>-><span class="f">assertSame</span>(<span class="s">5</span>, <span class="v">$r</span>-><span class="f">add</span>(<span class="s">2</span>, <span class="s">3</span>)); <span class="c">// erwartet 5</span>
}
}</pre>
<p>Ausgeführt mit <code class="inline">vendor/bin/phpunit</code>. Grün heißt: Behauptung erfüllt.</p>
<h2>Wichtige Assertions</h2>
<pre><span class="v">$this</span>-><span class="f">assertSame</span>(<span class="s">5</span>, <span class="v">$value</span>); <span class="c">// gleich in Wert UND Typ (===)</span>
<span class="v">$this</span>-><span class="f">assertEquals</span>(<span class="s">5</span>, <span class="v">$value</span>); <span class="c">// gleich im Wert (==)</span>
<span class="v">$this</span>-><span class="f">assertTrue</span>(<span class="v">$condition</span>);
<span class="v">$this</span>-><span class="f">assertCount</span>(<span class="s">3</span>, <span class="v">$list</span>);
<span class="v">$this</span>-><span class="f">assertInstanceOf</span>(<span class="t">Email</span>::<span class="k">class</span>, <span class="v">$obj</span>);</pre>
<h2>Erwartete Ausnahmen</h2>
<pre><span class="k">public function</span> <span class="f">testThrowsOnInvalid</span>(): <span class="t">void</span> {
<span class="v">$this</span>-><span class="f">expectException</span>(<span class="t">\InvalidArgumentException</span>::<span class="k">class</span>);
<span class="k">new</span> <span class="t">Email</span>(<span class="s">"kaputt"</span>); <span class="c">// soll werfen</span>
}</pre>
<h2>Daten-Provider</h2>
<p>Liefert mehrere Eingabe-/Erwartungs-Kombinationen, mit denen derselbe Test automatisch mehrfach läuft:</p>
<pre><span class="k">public static function</span> <span class="f">cases</span>(): <span class="t">array</span> {
<span class="k">return</span> [[<span class="s">2</span>, <span class="s">3</span>, <span class="s">5</span>], [<span class="s">0</span>, <span class="s">0</span>, <span class="s">0</span>], [-<span class="s">1</span>, <span class="s">1</span>, <span class="s">0</span>]];
}
<span class="a">#[\PHPUnit\Framework\Attributes\DataProvider(</span><span class="s">"cases"</span><span class="a">)]</span>
<span class="k">public function</span> <span class="f">testSum</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>-><span class="f">assertSame</span>(<span class="v">$expected</span>, (<span class="k">new</span> <span class="t">Calculator</span>())-><span class="f">add</span>(<span class="v">$a</span>, <span class="v">$b</span>));
}</pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Testbarer Code ist guter Code</b>Lässt sich etwas schwer testen, liegt das meist am Design (versteckte Abhängigkeiten, zu viel in einer Methode). Genau hier zahlt sich Dependency Injection aus: Du reichst Attrappen statt echter Datenbanken hinein. Beginne mit Tests für die Fachlogik – dort steckt der Wert.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 76</span><h1>Statische Analyse</h1></div><p class="lead">Statische Analyse prüft den Code, <i>ohne</i> ihn auszuführen – sie findet Tippfehler, falsche Typen und unmögliche Zustände, bevor sie zu Bugs werden. Die Werkzeuge: PHPStan und Psalm.</p>
<h2>Was sie findet</h2>
<p>Ein Aufruf mit falschem Typ, ein Zugriff auf eine vielleicht-<code class="inline">null</code>-Variable, eine Methode, die es nicht gibt – alles vor dem ersten Ausführen:</p>
<pre><span class="k">function</span> <span class="f">length</span>(<span class="t">?string</span> <span class="v">$s</span>): <span class="t">int</span> {
<span class="k">return</span> <span class="k">strlen</span>(<span class="v">$s</span>); <span class="c">// PHPStan: $s könnte null sein!</span>
}</pre>
<p>Korrekt mit vorheriger Prüfung:</p>
<pre><span class="k">function</span> <span class="f">length</span>(<span class="t">?string</span> <span class="v">$s</span>): <span class="t">int</span> {
<span class="k">return</span> <span class="v">$s</span> === <span class="k">null</span> ? <span class="s">0</span> : <span class="k">strlen</span>(<span class="v">$s</span>); <span class="c">// jetzt sauber</span>
}</pre>
<h2>Ausführen & Strenge-Stufen</h2>
<pre><span class="c"># Analyse über das ganze Projekt</span>
vendor/bin/phpstan analyse src --level 9</pre>
<p>Die Stufen reichen von 0 (locker) bis 9/max (sehr streng). Beginne niedrig, hebe schrittweise an.</p>
<h2>DocBlocks für mehr Präzision</h2>
<p>Wo PHPs Typsystem an Grenzen stößt (Array-Inhalte!), helfen Annotationen, die die Analyse versteht:</p>
<pre><span class="c">/** @param list<int> $numbers @return list<int> */</span>
<span class="k">function</span> <span class="f">double</span>(<span class="t">array</span> <span class="v">$numbers</span>): <span class="t">array</span> {
<span class="k">return</span> <span class="k">array_map</span>(<span class="k">fn</span>(<span class="t">int</span> <span class="v">$n</span>) => <span class="v">$n</span> * <span class="s">2</span>, <span class="v">$numbers</span>);
}</pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>In die CI einbauen</b>Statische Analyse fängt eine ganze Fehlerklasse ab, die Tests übersehen, und kostet keine Laufzeit. Lass sie bei jedem Commit automatisch laufen (Continuous Integration). Zusammen mit <code class="inline">declare(strict_types=1)</code> und vollständigen Typdeklarationen ist sie der wirksamste Hebel für robusten Code.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 77</span><h1>Code-Style & Tooling</h1></div><p class="lead">Einheitlicher Stil macht Code lesbar und Diffs klein. Statt im Team darüber zu streiten, lässt man Werkzeuge automatisch formatieren – nach dem Standard PSR-12.</p>
<h2>Automatisch formatieren</h2>
<p>PHP-CS-Fixer oder PHP_CodeSniffer bringen den ganzen Code auf einen einheitlichen Stil:</p>
<pre><span class="c"># prüfen, was nicht dem Standard entspricht</span>
vendor/bin/php-cs-fixer fix --dry-run --diff
<span class="c"># automatisch korrigieren</span>
vendor/bin/php-cs-fixer fix</pre>
<h2>Was der Standard regelt</h2>
<p>PSR-12 legt Dinge fest wie: vier Leerzeichen Einrückung, geschweifte Klammer der Methode in neuer Zeile, ein Leerzeichen nach Schlüsselwörtern. Vorher/nachher:</p>
<pre><span class="c">// vorher – uneinheitlich</span>
<span class="k">function</span> <span class="f">x</span>(<span class="v">$a</span>,<span class="v">$b</span>){<span class="k">return</span> <span class="v">$a</span>+<span class="v">$b</span>;}
<span class="c">// nachher – PSR-12</span>
<span class="k">function</span> <span class="f">x</span>(<span class="v">$a</span>, <span class="v">$b</span>)
{
<span class="k">return</span> <span class="v">$a</span> + <span class="v">$b</span>;
}</pre>
<h2>Vor jedem Commit prüfen</h2>
<p>Ein Git-Hook (z. B. über das Paket <code class="inline">captainhook</code> oder ein einfaches Skript) lässt Formatierung, Analyse und Tests automatisch laufen, bevor etwas eingecheckt wird – so kommt nichts Ungeprüftes ins Repository.</p>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Nicht über Stil streiten</b>Welcher Stil „besser" ist, ist meist Geschmack – wichtig ist nur, dass alle <i>denselben</i> nutzen. Lege die Konfiguration einmal ins Projekt und lass das Werkzeug entscheiden. Das spart Diskussionen und hält Code-Reviews bei der Sache statt bei Leerzeichen.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 78</span><h1>Debugging & Xdebug</h1></div><p class="lead">Wenn etwas nicht stimmt, willst du <i>sehen</i>, was passiert. Xdebug ist die mächtigste Erweiterung dafür: Statt Werte zu erraten, hältst du das Programm an und schaust hinein.</p>
<h2>Schnelle Diagnose ohne Tooling</h2>
<p>Für einen schnellen Blick reichen die eingebauten Funktionen:</p>
<pre><span class="k">var_dump</span>(<span class="v">$value</span>); <span class="c">// Typ + Inhalt, ausführlich</span>
<span class="k">print_r</span>(<span class="v">$array</span>, <span class="k">true</span>); <span class="c">// lesbare Struktur (true: als String)</span>
<span class="k">error_log</span>(<span class="k">print_r</span>(<span class="v">$data</span>, <span class="k">true</span>)); <span class="c">// ins Log statt in die Ausgabe</span></pre>
<h2>Schrittweise mit dem Debugger</h2>
<p>Mit Xdebug und der IDE setzt du einen Haltepunkt (Breakpoint) und läufst Zeile für Zeile durch – alle Variablen sind jederzeit einsehbar. Das ersetzt das mühsame Streuen von <code class="inline">var_dump</code>:</p>
<pre><span class="c"># Xdebug installieren und im php.ini aktivieren:</span>
<span class="c"># zend_extension=xdebug</span>
<span class="c"># xdebug.mode=debug</span>
<span class="c"># xdebug.start_with_request=yes</span></pre>
<p>Danach in der IDE „Listen for debug connections" einschalten, Breakpoint setzen, Seite aufrufen – das Programm hält an.</p>
<h2>Bessere Stacktraces</h2>
<p>Schon im Modus <code class="inline">develop</code> macht Xdebug Fehlermeldungen und Stacktraces deutlich lesbarer und zeigt die Variablenwerte im Aufrufpfad – oft sieht man die Ursache sofort.</p>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Xdebug nur in der Entwicklung</b>Aktives Xdebug bremst PHP erheblich – es gehört niemals auf einen Produktionsserver. Dort nutzt man stattdessen Logging und, für Performance-Analysen, leichtgewichtige Profiler. Vergiss außerdem keine <code class="inline">var_dump</code>-Reste im ausgelieferten Code (die statische Analyse warnt davor).</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 79</span><h1>Performance & OPcache</h1></div><p class="lead">PHP ist schneller, als viele denken – wenn ein paar Grundlagen stimmen. Die größten Hebel sind selten der PHP-Code selbst, sondern Caching und die Datenbank.</p>
<h2>OPcache</h2>
<p>Ohne OPcache übersetzt PHP jede Datei bei jeder Anfrage neu. Mit OPcache wird der kompilierte Bytecode im Speicher gehalten – ein gewaltiger Unterschied, ganz ohne Code-Änderung:</p>
<pre><span class="c"># php.ini (Produktion)</span>
<span class="c"># opcache.enable=1</span>
<span class="c"># opcache.validate_timestamps=0 ; im Betrieb: Dateien nicht ständig prüfen</span>
<span class="c"># opcache.memory_consumption=256</span></pre>
<p>OPcache ist auf Produktionsservern Pflicht – es kann die Last vervielfachen.</p>
<h2>Das N+1-Problem</h2>
<p>Der häufigste Performance-Killer: in einer Schleife immer wieder die Datenbank fragen. Statt 1 + N Abfragen eine einzige mit <code class="inline">JOIN</code> oder <code class="inline">IN</code>:</p>
<pre><span class="c">// schlecht: pro Bestellung eine Extra-Abfrage</span>
<span class="k">foreach</span> (<span class="v">$orders</span> <span class="k">as</span> <span class="v">$b</span>) {
<span class="v">$user</span> = <span class="v">$pdo</span>-><span class="f">query</span>(<span class="s">"SELECT * FROM nutzer WHERE id = {</span><span class="v">$b</span>[<span class="s">'nutzer_id'</span>]<span class="s">}"</span>); <span class="c">// N Abfragen!</span>
}
<span class="c">// gut: alle auf einmal holen und im PHP zuordnen</span></pre>
<h2>Messen statt raten</h2>
<p>Optimiere nie auf Verdacht. Miss zuerst, wo die Zeit hingeht – grob mit <code class="inline">hrtime</code>, gründlich mit einem Profiler:</p>
<pre><span class="v">$start</span> = <span class="k">hrtime</span>(<span class="k">true</span>);
<span class="c">// ... Code ...</span>
<span class="k">echo</span> (<span class="k">hrtime</span>(<span class="k">true</span>) - <span class="v">$start</span>) / <span class="s">1_000_000</span> . <span class="s">" ms"</span>;</pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Die Reihenfolge der Hebel</b>OPcache aktivieren (riesig, gratis), N+1-Abfragen beseitigen, sinnvolle Datenbank-Indizes setzen (Teil 9), teure Ergebnisse cachen. Erst danach lohnt Mikro-Optimierung im PHP-Code – und auch die nur dort, wo der Profiler einen Engpass zeigt.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 80</span><h1>Speicher & Referenzen</h1></div><p class="lead">Meist musst du dich um Speicher nicht kümmern – PHP räumt selbst auf. Bei großen Datenmengen und langlebigen Prozessen lohnt es aber zu verstehen, wie Kopien, Referenzen und die Speicherbereinigung arbeiten.</p>
<h2>Copy-on-Write</h2>
<p>PHP kopiert Werte nicht sofort, sondern erst bei der ersten Änderung. Eine Zuweisung ist also billig, solange beide gleich bleiben:</p>
<pre><span class="v">$a</span> = <span class="k">range</span>(<span class="s">1</span>, <span class="s">100000</span>);
<span class="v">$b</span> = <span class="v">$a</span>; <span class="c">// noch keine echte Kopie – beide teilen die Daten</span>
<span class="v">$b</span>[] = <span class="s">1</span>; <span class="c">// jetzt wird kopiert (Copy-on-Write)</span></pre>
<h2>Referenzen sparsam einsetzen</h2>
<p>Mit <code class="inline">&</code> teilen sich zwei Variablen denselben Speicher; eine Änderung wirkt bei beiden:</p>
<pre><span class="v">$x</span> = <span class="s">1</span>;
<span class="v">$y</span> = &<span class="v">$x</span>;
<span class="v">$y</span> = <span class="s">9</span>;
<span class="k">echo</span> <span class="v">$x</span>; <span class="c">// 9</span></pre>
<p>Referenzen sind selten nötig und oft eine Fehlerquelle – nutze sie bewusst, etwa um ein großes Array in einer Funktion direkt zu verändern statt es zu kopieren.</p>
<h2>Generatoren gegen Speicherspitzen</h2>
<p>Der wirksamste Speichertrick bei großen Mengen: nicht alles laden, sondern strömen (vgl. Teil 6). So bleibt der Verbrauch konstant statt linear:</p>
<pre><span class="k">function</span> <span class="f">lines</span>(<span class="t">string</span> <span class="v">$file</span>): <span class="t">\Generator</span> {
<span class="v">$f</span> = <span class="k">fopen</span>(<span class="v">$file</span>, <span class="s">"r"</span>);
<span class="k">while</span> ((<span class="v">$z</span> = <span class="k">fgets</span>(<span class="v">$f</span>)) !== <span class="k">false</span>) <span class="k">yield</span> <span class="v">$z</span>; <span class="c">// nur eine Zeile im Speicher</span>
<span class="k">fclose</span>(<span class="v">$f</span>);
}</pre>
<h2>Verbrauch messen</h2>
<pre><span class="k">echo</span> <span class="k">memory_get_peak_usage</span>(<span class="k">true</span>) / <span class="s">1024</span> / <span class="s">1024</span> . <span class="s">" MB"</span>; <span class="c">// Spitzenverbrauch</span></pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Zirkelbezüge & lange Prozesse</b>Bei normalen Web-Requests endet der Prozess schnell und gibt allen Speicher frei. In langlaufenden Workern (Teil 15) zählt jedes Leck: Achte auf Zirkelbezüge zwischen Objekten und gib große Strukturen mit <code class="inline">unset</code> frei. Ein Worker, der Speicher anhäuft, wird üblicherweise nach N Aufträgen kontrolliert neu gestartet.</div>
</div></section><section class="part-divider"><div class="part-kicker">Teil 15</div><h1>CLI, Prozesse & asynchrones PHP</h1><div class="part-desc">Jenseits des Webrequests: Kommandozeilen-Programme, externe Prozesse und die Welt der Fibers, Event-Loops und spezialisierten Laufzeiten.</div><div class="part-chapters"><span>81 · CLI-Programme bauen</span><span>82 · Prozesse & FFI</span><span>83 · Das klassische Modell & Fibers</span><span>84 · Event-Loops & ReactPHP</span><span>85 · Swoole, Amp & FrankenPHP</span><span>86 · Echte Parallelität</span></div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 81</span><h1>CLI-Programme bauen</h1></div><p class="lead">PHP läuft nicht nur im Web. Auf der Kommandozeile baust du Wartungsskripte, Cronjobs, Importer und Hintergrund-Worker – ganz ohne Webserver.</p>
<h2>Argumente lesen</h2>
<p><code class="inline">$argv</code> enthält die übergebenen Argumente, <code class="inline">$argc</code> ihre Anzahl. Der erste Eintrag ist der Skriptname:</p>
<pre><span class="c">// aufruf: php import.php datei.csv --schnell</span>
<span class="k">echo</span> <span class="v">$argv</span>[<span class="s">1</span>] ?? <span class="s">"keine Datei"</span>; <span class="c">// datei.csv</span>
<span class="k">var_dump</span>(<span class="k">in_array</span>(<span class="s">"--schnell"</span>, <span class="v">$argv</span>, <span class="k">true</span>)); <span class="c">// true</span></pre>
<p>Für komfortable Optionen (<code class="inline">--name=wert</code>) gibt es <code class="inline">getopt</code>; größere CLI-Apps nutzen <code class="inline">symfony/console</code>.</p>
<h2>Ein- und Ausgabe</h2>
<pre><span class="k">fwrite</span>(<span class="t">STDOUT</span>, <span class="s">"Fortschritt...\n"</span>);
<span class="k">fwrite</span>(<span class="t">STDERR</span>, <span class="s">"Warnung\n"</span>); <span class="c">// Fehlerkanal, getrennt umleitbar</span>
<span class="v">$line</span> = <span class="k">trim</span>(<span class="k">fgets</span>(<span class="t">STDIN</span>)); <span class="c">// eine Eingabezeile lesen</span></pre>
<h2>Exit-Codes</h2>
<p>Der Rückgabewert sagt der Shell, ob alles gut ging – 0 für Erfolg, alles andere für Fehler. Wichtig für Cron und CI:</p>
<pre><span class="k">if</span> (!<span class="k">file_exists</span>(<span class="v">$argv</span>[<span class="s">1</span>] ?? <span class="s">""</span>)) {
<span class="k">fwrite</span>(<span class="t">STDERR</span>, <span class="s">"Datei fehlt\n"</span>);
<span class="k">exit</span>(<span class="s">1</span>); <span class="c">// Fehler signalisieren</span>
}
<span class="k">exit</span>(<span class="s">0</span>); <span class="c">// Erfolg</span></pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>CLI hat kein Zeitlimit</b>Anders als Web-Requests laufen CLI-Skripte ohne <code class="inline">max_execution_time</code>-Begrenzung – ideal für lange Importe. Trenne Ausgaben sauber in STDOUT (Ergebnis) und STDERR (Meldungen) und setze sinnvolle Exit-Codes, damit sich das Skript in Cron, Pipes und CI sauber einfügt.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 82</span><h1>Prozesse & FFI</h1></div><p class="lead">Manchmal muss PHP andere Programme starten oder direkt mit C-Bibliotheken sprechen. Dafür gibt es Prozess-Funktionen und – für Fortgeschrittene – FFI.</p>
<h2>Ein Programm aufrufen</h2>
<p><code class="inline">shell_exec</code> liefert die Ausgabe, <code class="inline">exec</code> zusätzlich Zeilen und Exit-Code:</p>
<pre><span class="k">echo</span> <span class="k">shell_exec</span>(<span class="s">"date +%Y"</span>); <span class="c">// z. B. 2026</span>
<span class="k">exec</span>(<span class="s">"ls -1"</span>, <span class="v">$lines</span>, <span class="v">$code</span>);
<span class="k">echo</span> <span class="v">$code</span>; <span class="c">// 0 bei Erfolg</span></pre>
<h2>Eingaben absichern</h2>
<p>Baust du Argumente aus Variablen, drohen Command-Injection-Angriffe. Maskiere immer:</p>
<pre><span class="v">$file</span> = <span class="k">escapeshellarg</span>(<span class="v">$userInput</span>); <span class="c">// sicher gequotet</span>
<span class="k">exec</span>(<span class="s">"wc -l "</span> . <span class="v">$file</span>, <span class="v">$out</span>);</pre>
<h2>Volle Kontrolle mit proc_open</h2>
<p>Für echte Interaktion (eigene STDIN/STDOUT/STDERR) öffnest du den Prozess mit Pipes:</p>
<pre><span class="v">$pipes</span> = [];
<span class="v">$p</span> = <span class="k">proc_open</span>(<span class="s">"cat"</span>, [[<span class="s">"pipe"</span>, <span class="s">"r"</span>], [<span class="s">"pipe"</span>, <span class="s">"w"</span>], [<span class="s">"pipe"</span>, <span class="s">"w"</span>]], <span class="v">$pipes</span>);
<span class="k">fwrite</span>(<span class="v">$pipes</span>[<span class="s">0</span>], <span class="s">"hallo"</span>); <span class="k">fclose</span>(<span class="v">$pipes</span>[<span class="s">0</span>]);
<span class="k">echo</span> <span class="k">stream_get_contents</span>(<span class="v">$pipes</span>[<span class="s">1</span>]); <span class="c">// hallo</span>
<span class="k">proc_close</span>(<span class="v">$p</span>);</pre>
<h2>FFI</h2>
<p>Die Foreign Function Interface ruft Funktionen aus geteilten C-Bibliotheken auf, ohne eine PHP-Erweiterung zu schreiben:</p>
<pre><span class="v">$libc</span> = <span class="t">\FFI</span>::<span class="f">cdef</span>(<span class="s">"int strlen(const char *s);"</span>, <span class="s">"libc.so.6"</span>);
<span class="k">echo</span> <span class="v">$libc</span>-><span class="f">strlen</span>(<span class="s">"hallo"</span>); <span class="c">// 5 (aus der C-Bibliothek)</span></pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Mit Vorsicht</b>Externe Programme und FFI verlassen die sichere PHP-Welt. Bei Prozessaufrufen niemals ungeprüfte Eingaben in den Befehl einbauen (<code class="inline">escapeshellarg</code>/<code class="inline">escapeshellcmd</code> nutzen). FFI ist mächtig, aber fehleranfällig und umgeht PHPs Speichersicherheit – nur einsetzen, wenn es keine PHP-Lösung gibt.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 83</span><h1>Das klassische Modell & Fibers</h1></div><p class="lead">Klassisches PHP ist synchron: Jede Anweisung wartet, bis die vorige fertig ist. Das ist einfach, aber bei vielem Warten (Netzwerk!) ineffizient. Fibers (seit PHP 8.1) bringen einen Ausweg.</p>
<h2>Das blockierende Modell</h2>
<p>Drei API-Aufrufe nacheinander dauern so lange wie ihre Summe – auch wenn jeder nur auf Antwort wartet:</p>
<pre><span class="v">$a</span> = <span class="k">file_get_contents</span>(<span class="s">"https://api.example.com/1"</span>); <span class="c">// warten</span>
<span class="v">$b</span> = <span class="k">file_get_contents</span>(<span class="s">"https://api.example.com/2"</span>); <span class="c">// warten</span>
<span class="c">// nacheinander – die Wartezeiten summieren sich</span></pre>
<h2>Was Fibers können</h2>
<p>Eine Fiber ist eine Funktion, die sich selbst <i>anhalten</i> (<code class="inline">suspend</code>) und später fortsetzen kann. Damit lässt sich Warten unterbrechen und etwas anderes tun:</p>
<pre><span class="v">$fiber</span> = <span class="k">new</span> <span class="t">\Fiber</span>(<span class="k">function</span>(): <span class="t">void</span> {
<span class="k">echo</span> <span class="s">"Start\n"</span>;
<span class="v">$value</span> = <span class="t">\Fiber</span>::<span class="f">suspend</span>(<span class="s">"pause"</span>); <span class="c">// hier anhalten, "pause" zurückgeben</span>
<span class="k">echo</span> <span class="s">"Weiter mit: $value\n"</span>;
});
<span class="k">echo</span> <span class="v">$fiber</span>-><span class="f">start</span>(); <span class="c">// Start / liefert "pause"</span>
<span class="v">$fiber</span>-><span class="f">resume</span>(<span class="s">"42"</span>); <span class="c">// Weiter mit: 42</span></pre>
<h2>Wofür das gut ist</h2>
<p>Fibers sind das Fundament, nicht das Werkzeug für den Alltag: Bibliotheken wie ReactPHP und Amp nutzen sie, um vielen wartenden Aufgaben <i>einen</i> Thread teilen zu lassen. Du selbst arbeitest meist mit deren komfortabler API (nächste Kapitel), nicht direkt mit <code class="inline">Fiber</code>.</p>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Nebenläufig ≠ parallel</b>Fibers schalten <i>kooperativ</i> zwischen Aufgaben um, während eine wartet – auf einem einzigen Kern. Das beschleunigt I/O-lastige Arbeit (viele Netzwerkaufrufe), nicht rechenintensive (das wäre echte Parallelität, letztes Kapitel dieses Teils).</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 84</span><h1>Event-Loops & ReactPHP</h1></div><p class="lead">Asynchrones PHP dreht sich um die Event-Loop: eine Schleife, die viele Aufgaben verwaltet und immer dort weitermacht, wo gerade etwas fertig ist – statt untätig zu warten.</p>
<h2>Die Idee der Event-Loop</h2>
<p>Statt „mach A, warte, mach B, warte" registrierst du Aufgaben und Rückrufe. Die Loop arbeitet sie ab, sobald Daten bereit sind. Selbst ein Timer ist nur eine geplante Aufgabe:</p>
<pre><span class="k">use</span> <span class="t">React\EventLoop\Loop</span>;
<span class="t">Loop</span>::<span class="f">addTimer</span>(<span class="s">1.0</span>, <span class="k">fn</span>() => <span class="k">print</span>(<span class="s">"nach 1s\n"</span>));
<span class="t">Loop</span>::<span class="f">addTimer</span>(<span class="s">0.5</span>, <span class="k">fn</span>() => <span class="k">print</span>(<span class="s">"nach 0.5s\n"</span>));
<span class="c">// Ausgabe: erst "nach 0.5s", dann "nach 1s" – nicht in Code-Reihenfolge</span></pre>
<h2>Versprechen (Promises)</h2>
<p>Ein Promise steht für ein Ergebnis, das später kommt. Du hängst eine Reaktion an, statt zu warten:</p>
<pre><span class="k">use</span> <span class="t">React\Http\Browser</span>;
<span class="v">$browser</span> = <span class="k">new</span> <span class="t">Browser</span>();
<span class="v">$browser</span>-><span class="f">get</span>(<span class="s">"https://example.com"</span>)-><span class="f">then</span>(
<span class="k">fn</span>(<span class="v">$response</span>) => <span class="k">print</span>(<span class="s">"fertig: "</span> . <span class="v">$response</span>-><span class="f">getStatusCode</span>() . <span class="s">"\n"</span>)
);
<span class="c">// das Programm läuft weiter, der Rückruf feuert, wenn die Antwort da ist</span></pre>
<h2>Vorteil bei vielem Warten</h2>
<p>Mehrere Anfragen starten quasi gleichzeitig; die Gesamtdauer entspricht der langsamsten, nicht der Summe – ideal für APIs, die aufeinander warten.</p>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Wann sich der Wechsel lohnt</b>Asynchrone Frameworks glänzen bei I/O-lastigen Diensten: viele gleichzeitige Verbindungen, WebSockets, das Bündeln vieler API-Aufrufe. Für klassische Web-Anwendungen mit etwas Logik und einer DB bleibt das synchrone PHP einfacher und völlig ausreichend.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 85</span><h1>Swoole, Amp & FrankenPHP</h1></div><p class="lead">Drei Wege, PHP über das klassische „pro Request neu starten" hinaus laufen zu lassen – mit unterschiedlichen Stärken.</p>
<h2>Swoole</h2>
<p>Eine C-Erweiterung, die PHP zu einem im Speicher residenten, hochnebenläufigen Server macht. Der Code lädt einmal und bedient dann viele Anfragen ohne Neustart:</p>
<pre><span class="v">$server</span> = <span class="k">new</span> <span class="t">\Swoole\Http\Server</span>(<span class="s">"0.0.0.0"</span>, <span class="s">9501</span>);
<span class="v">$server</span>-><span class="f">on</span>(<span class="s">"request"</span>, <span class="k">function</span>(<span class="v">$req</span>, <span class="v">$res</span>) {
<span class="v">$res</span>-><span class="f">end</span>(<span class="s">"Hallo von Swoole"</span>);
});
<span class="v">$server</span>-><span class="f">start</span>(); <span class="c">// läuft dauerhaft, sehr schnell</span></pre>
<h2>Amp</h2>
<p>Amp bietet (wie ReactPHP) eine Event-Loop, nutzt aber Fibers, sodass async-Code <i>aussieht</i> wie synchroner – ohne Rückruf-Verschachtelung:</p>
<pre><span class="k">use function</span> <span class="t">Amp\async</span>;
<span class="v">$results</span> = <span class="t">Amp\Future\await</span>([
<span class="f">async</span>(<span class="v">$fetchPage</span>, <span class="s">"https://a.example"</span>),
<span class="f">async</span>(<span class="v">$fetchPage</span>, <span class="s">"https://b.example"</span>),
]); <span class="c">// beide gleichzeitig, dann gemeinsam abwarten</span></pre>
<h2>FrankenPHP</h2>
<p>Ein in Go geschriebener Server, der PHP einbettet. Er hält den Code im Speicher (Worker-Modus) und bringt HTTP/2, HTTP/3 und einfache Bereitstellung mit – oft ohne Code-Änderung deutlich schneller als das klassische Modell.</p>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Speicher im Blick behalten</b>Sobald PHP zwischen Anfragen im Speicher bleibt (Swoole, FrankenPHP-Worker), verschwindet der Zustand nicht mehr automatisch. Globale und statische Variablen leben weiter – ein Leck wächst über die Zeit. Solche Anwendungen müssen sauber mit Zustand umgehen und Worker periodisch neu starten.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 86</span><h1>Echte Parallelität</h1></div><p class="lead">Nebenläufigkeit (Fibers, Event-Loops) hilft beim Warten. Für rechenintensive Arbeit, die mehrere CPU-Kerne nutzen soll, braucht es echte Parallelität – mehrere Ausführungspfade gleichzeitig.</p>
<h2>Mehrere Prozesse</h2>
<p>Der klassische Weg: das Programm in unabhängige Prozesse aufteilen. <code class="inline">pcntl_fork</code> (nur CLI, Unix) spaltet den laufenden Prozess:</p>
<pre><span class="v">$pid</span> = <span class="k">pcntl_fork</span>();
<span class="k">if</span> (<span class="v">$pid</span> === <span class="s">0</span>) {
<span class="k">echo</span> <span class="s">"Kindprozess rechnet\n"</span>; <span class="c">// läuft parallel</span>
<span class="k">exit</span>(<span class="s">0</span>);
} <span class="k">else</span> {
<span class="k">pcntl_wait</span>(<span class="v">$status</span>); <span class="c">// Elternprozess wartet auf das Kind</span>
}</pre>
<h2>Worker-Pools über eine Queue</h2>
<p>In der Praxis verteilt man Arbeit selten von Hand auf Prozesse, sondern über eine Job-Queue: Web-Requests legen Aufgaben ab, mehrere Worker-Prozesse arbeiten sie parallel ab:</p>
<pre><span class="c">// vereinfachtes Bild:</span>
<span class="c">// Web: queue.push(new BildVerkleinern($id)); // schnell zurück</span>
<span class="c">// Worker: while (true) { $job = queue.pop(); $job->ausfuehren(); } // im Hintergrund</span></pre>
<p>So bleibt die Web-Antwort schnell, und schwere Arbeit (Bilder, Mails, Berichte) läuft auf mehreren Kernen nebenher. Werkzeuge dafür: Symfony Messenger, Laravel Queues, Gearman.</p>
<h2>Threads</h2>
<p>Echte Threads in einem Prozess sind in PHP unüblich; das frühere <code class="inline">pthreads</code> ist überholt. Der Nachfolger <code class="inline">parallel</code> erlaubt Threads, ist aber eine Nische – für die meisten Aufgaben sind Prozesse und Queues der pragmatische Weg.</p>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Das richtige Werkzeug</b>Viel Warten auf I/O? Nebenläufigkeit (Fibers, async). Viel Rechnen auf mehreren Kernen? Mehrere Prozesse, meist über eine Queue. Für die allermeisten Web-Anwendungen reicht jedoch: schnelle Antwort liefern, schwere Arbeit in einen Hintergrund-Worker auslagern – ganz ohne exotische Erweiterungen.</div>
</div></section><section class="part-divider"><div class="part-kicker">Teil 16</div><h1>Das Ökosystem</h1><div class="part-desc">PHP lebt von Composer, gemeinsamen Standards und großen Frameworks. Der Überblick über die Welt rund um deinen Code.</div><div class="part-chapters"><span>87 · Composer in der Tiefe</span><span>88 · Die PSR-Standards</span><span>89 · Symfony im Überblick</span><span>90 · Laravel im Überblick</span><span>91 · Wichtige Bibliotheken</span></div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 87</span><h1>Composer in der Tiefe</h1></div><p class="lead">Composer hast du in Teil 3 kennengelernt. Hier geht es um die Feinheiten, die im Projektalltag zählen: Versionsregeln, Skripte und der Unterschied zwischen Entwicklung und Produktion.</p>
<h2>Versionsregeln verstehen</h2>
<p>Die Operatoren steuern, welche Updates erlaubt sind. <code class="inline">^</code> erlaubt alles bis zur nächsten Hauptversion, <code class="inline">~</code> nur die letzte genannte Stelle:</p>
<pre><span class="s">"monolog/monolog"</span>: <span class="s">"^3.5"</span> <span class="c">// >=3.5.0, <4.0.0</span>
<span class="s">"guzzlehttp/guzzle"</span>: <span class="s">"~7.8.0"</span> <span class="c">// >=7.8.0, <7.9.0</span>
<span class="s">"vlucas/phpdotenv"</span>: <span class="s">"5.*"</span> <span class="c">// jede 5er-Version</span></pre>
<h2>require vs. require-dev</h2>
<p>Werkzeuge, die nur beim Entwickeln gebraucht werden (Tests, Analyse), gehören in <code class="inline">require-dev</code> – auf dem Produktionsserver werden sie weggelassen:</p>
<pre><span class="c"># nur für die Entwicklung</span>
composer require --dev phpunit/phpunit phpstan/phpstan
<span class="c"># auf dem Server: dev-Pakete und optimierter Autoloader</span>
composer install --no-dev --optimize-autoloader</pre>
<h2>Skripte</h2>
<p>In <code class="inline">composer.json</code> definierst du Kurzbefehle für wiederkehrende Aufgaben:</p>
<pre><span class="s">"scripts"</span>: {
<span class="s">"test"</span>: <span class="s">"phpunit"</span>,
<span class="s">"check"</span>: [<span class="s">"phpstan analyse src"</span>, <span class="s">"php-cs-fixer fix --dry-run"</span>]
}
<span class="c"># Aufruf: composer test / composer check</span></pre>
<h2>Sicherheit & Aktualität prüfen</h2>
<pre><span class="c"># bekannte Sicherheitslücken in Abhängigkeiten finden</span>
composer audit
<span class="c"># welche Pakete sind veraltet?</span>
composer outdated --direct</pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Lock-Datei ist Gesetz</b>Die <code class="inline">composer.lock</code> gehört ins Repository und legt die exakten Versionen fest. <code class="inline">composer install</code> folgt ihr (identische Stände überall), <code class="inline">composer update</code> ändert sie bewusst. So baut der Server garantiert dasselbe wie deine Maschine.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 88</span><h1>Die PSR-Standards</h1></div><p class="lead">PSR steht für „PHP Standard Recommendation" – Vereinbarungen der PHP-FIG, damit Code und Bibliotheken verschiedener Hersteller zusammenpassen. Sie sind der Grund, warum das Ökosystem so gut ineinandergreift.</p>
<h2>PSR-4</h2>
<p>Bildet Namespaces auf Verzeichnisse ab – die Basis dafür, dass Composer jede Klasse findet (Teil 3):</p>
<pre><span class="c">// App\Service\Mailer => src/Service/Mailer.php</span>
<span class="s">"autoload"</span>: { <span class="s">"psr-4"</span>: { <span class="s">"App\\"</span>: <span class="s">"src/"</span> } }</pre>
<h2>PSR-12</h2>
<p>Der gemeinsame Formatierungsstandard (Teil 14). Werkzeuge wie PHP-CS-Fixer setzen ihn automatisch durch, sodass Code aus aller Welt gleich aussieht.</p>
<h2>PSR-3</h2>
<p>Ein einheitliches Logger-Interface. Dein Code hängt nur davon ab – welche Implementierung (Monolog o. a.) dahintersteckt, ist austauschbar:</p>
<pre><span class="k">use</span> <span class="t">Psr\Log\LoggerInterface</span>;
<span class="k">class</span> <span class="t">Import</span> {
<span class="k">public function</span> <span class="f">__construct</span>(<span class="k">private</span> <span class="t">LoggerInterface</span> <span class="v">$log</span>) {}
<span class="k">public function</span> <span class="f">run</span>(): <span class="t">void</span> { <span class="v">$this</span>-><span class="v">log</span>-><span class="f">info</span>(<span class="s">"Import gestartet"</span>); }
}</pre>
<h2>PSR-7, -15, -17</h2>
<p>Standardisierte Objekte für HTTP-Requests/Responses (PSR-7), Middleware (PSR-15) und deren Erzeugung (PSR-17). Dadurch lassen sich Middleware und Frameworks frei kombinieren:</p>
<pre><span class="k">interface</span> <span class="t">MiddlewareInterface</span> {
<span class="k">public function</span> <span class="f">process</span>(<span class="t">ServerRequestInterface</span> <span class="v">$req</span>, <span class="t">RequestHandlerInterface</span> <span class="v">$next</span>): <span class="t">ResponseInterface</span>;
}</pre>
<h2>PSR-11</h2>
<p>Ein einheitliches Interface für DI-Container mit nur zwei Methoden – <code class="inline">get()</code> und <code class="inline">has()</code> – sodass Frameworks und Bibliotheken denselben Container nutzen können.</p>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Warum das wichtig ist</b>Dank PSR kannst du einen Logger, HTTP-Client oder Container austauschen, ohne deinen Code umzuschreiben – du hängst von Interfaces ab, nicht von Produkten. Das ist Dependency Inversion (Teil 12) auf Ökosystem-Ebene und ein Hauptgrund für die Reife der PHP-Welt.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 89</span><h1>Symfony im Überblick</h1></div><p class="lead">Symfony ist zweierlei: eine Sammlung wiederverwendbarer Komponenten und ein darauf aufbauendes Full-Stack-Framework. Viele andere Projekte – auch Laravel – nutzen Symfony-Komponenten im Unterbau.</p>
<h2>Komponenten einzeln nutzen</h2>
<p>Du musst nicht das ganze Framework einsetzen – einzelne Komponenten gibt es als eigenständige Pakete:</p>
<pre><span class="c"># nur die HTTP-Foundation, ohne das ganze Framework</span>
composer require symfony/http-foundation
<span class="k">use</span> <span class="t">Symfony\Component\HttpFoundation\Request</span>;
<span class="v">$request</span> = <span class="t">Request</span>::<span class="f">createFromGlobals</span>();
<span class="k">echo</span> <span class="v">$request</span>-><span class="f">getMethod</span>(); <span class="c">// GET</span></pre>
<h2>Typische Bausteine</h2>
<p>Routing per Attribut, Controller als Klassen, Dependency Injection ab Werk, das Template-System Twig:</p>
<pre><span class="k">use</span> <span class="t">Symfony\Component\Routing\Attribute\Route</span>;
<span class="k">class</span> <span class="t">StartController</span> {
<span class="a">#[Route(</span><span class="s">"/"</span><span class="a">, name: </span><span class="s">"start"</span><span class="a">)]</span>
<span class="k">public function</span> <span class="f">index</span>(): <span class="t">Response</span> {
<span class="k">return</span> <span class="k">new</span> <span class="t">Response</span>(<span class="s">"Startseite"</span>);
}
}</pre>
<h2>Versionen & Ausrichtung</h2>
<p>Symfony erscheint zeitbasiert: alle sechs Monate eine Minor-Version, alle zwei Jahre eine Major. Aktuell ist <b>Symfony 8.1</b> (Mai 2026, benötigt PHP 8.4+); für Projekte mit langem Planungshorizont gibt es die LTS-Reihe (derzeit <b>7.4</b> mit drei Jahren Support). Symfony gilt als besonders stark bei großen, langlebigen und Enterprise-Anwendungen.</p>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Komponentendenken</b>Der größte Wert von Symfony liegt in seinen entkoppelten Komponenten – Console, HttpFoundation, Validator, Mailer, Yaml und viele mehr. Selbst wenn du ein anderes Framework nutzt, begegnest du ihnen ständig. Das Full-Stack-Framework setzt sie zu einem stimmigen Ganzen zusammen.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 90</span><h1>Laravel im Überblick</h1></div><p class="lead">Laravel ist das meistgenutzte PHP-Framework und legt den Fokus auf Entwicklerfreude und schnelle Ergebnisse. Es bringt für fast jede Aufgabe eine elegante, fertige Lösung mit.</p>
<h2>Eloquent</h2>
<p>Laravels ORM macht Datenbankzugriff besonders knapp. Eine Klasse pro Tabelle, ausdrucksstarke Abfragen:</p>
<pre><span class="k">class</span> <span class="t">Product</span> <span class="k">extends</span> <span class="t">Model</span> {}
<span class="v">$expensive</span> = <span class="t">Product</span>::<span class="f">where</span>(<span class="s">"cent"</span>, <span class="s">">"</span>, <span class="s">5000</span>)-><span class="f">orderBy</span>(<span class="s">"name"</span>)-><span class="f">get</span>();
<span class="v">$p</span> = <span class="t">Product</span>::<span class="f">find</span>(<span class="s">1</span>);
<span class="v">$p</span>-><span class="v">name</span> = <span class="s">"Buch"</span>;
<span class="v">$p</span>-><span class="f">save</span>();</pre>
<h2>Artisan</h2>
<p>Das Kommandozeilen-Werkzeug von Laravel – es erzeugt Gerüste, migriert die Datenbank und führt eigene Befehle aus:</p>
<pre><span class="c"># Gerüste erzeugen, Datenbank migrieren, eigene Befehle</span>
php artisan make:model Produkt -mc
php artisan migrate
php artisan tinker <span class="c"># interaktive Konsole</span></pre>
<h2>Routing & Controller</h2>
<pre><span class="k">use</span> <span class="t">Illuminate\Support\Facades\Route</span>;
<span class="t">Route</span>::<span class="f">get</span>(<span class="s">"/produkte/{id}"</span>, [<span class="t">ProductController</span>::<span class="k">class</span>, <span class="s">"zeigen"</span>]);</pre>
<h2>Das Ökosystem drumherum</h2>
<p>Laravel bietet ein dichtes Umfeld fertiger Pakete: Queues, Broadcasting/WebSockets (Reverb), Volltextsuche (Scout), Authentifizierung (Sanctum), Admin- und Monitoring-Werkzeuge. Aktuell ist <b>Laravel 13</b> (2026, benötigt mindestens PHP 8.3). Es erscheint jährlich eine Major-Version mit dem erklärten Ziel möglichst weniger Brüche.</p>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body"><b>Laravel oder Symfony?</b>Beide sind exzellent und teilen sich Unterbau. Faustregel: <b>Laravel</b> für schnelle Entwicklung, viel „Magie" und ein rundes Gesamtpaket; <b>Symfony</b> für maximale Kontrolle, Komponentendenken und große, langlebige Systeme. Die Wahl hängt mehr vom Team und Projekt ab als von technischer Überlegenheit.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 91</span><h1>Wichtige Bibliotheken</h1></div><p class="lead">Bestimmte Pakete tauchen in fast jedem ernsthaften PHP-Projekt auf. Sie zu kennen, erspart dir, das Rad neu zu erfinden – hier die wichtigsten mit ihrem Zweck und einem kurzen Blick.</p>
<h2>Monolog</h2>
<p>Die verbreitete Logging-Bibliothek; sie schreibt Meldungen über austauschbare Handler an unterschiedliche Ziele:</p>
<pre><span class="k">use</span> <span class="t">Monolog\Logger</span>;
<span class="k">use</span> <span class="t">Monolog\Handler\StreamHandler</span>;
<span class="v">$log</span> = <span class="k">new</span> <span class="t">Logger</span>(<span class="s">"app"</span>);
<span class="v">$log</span>-><span class="f">pushHandler</span>(<span class="k">new</span> <span class="t">StreamHandler</span>(<span class="s">"app.log"</span>));
<span class="v">$log</span>-><span class="f">warning</span>(<span class="s">"Speicher knapp"</span>); <span class="c">// in app.log</span></pre>
<h2>Guzzle</h2>
<p>Der Standard für ausgehende HTTP-Anfragen (Teil 10) – komfortabel und gut testbar.</p>
<h2>Carbon</h2>
<p>Erweitert <code class="inline">DateTime</code> um eine ausdrucksstarke, lesbare API:</p>
<pre><span class="k">use</span> <span class="t">Carbon\Carbon</span>;
<span class="k">echo</span> <span class="t">Carbon</span>::<span class="f">now</span>()-><span class="f">addDays</span>(<span class="s">3</span>)-><span class="f">diffForHumans</span>(); <span class="c">// "in 3 Tagen"</span></pre>
<h2>Doctrine</h2>
<p>Das große, datenbankunabhängige ORM (Teil 9) – Herzstück vieler Symfony-Projekte.</p>
<h2>PHPUnit, PHPStan, Rector</h2>
<p>Die Qualitäts-Werkzeuge aus Teil 14: Tests, statische Analyse – und <b>Rector</b>, das Code automatisch umbaut (etwa bei Upgrades, siehe nächster Teil).</p>
<h2>Pakete finden & bewerten</h2>
<p>Bibliotheken liegen auf <code class="inline">packagist.org</code>. Achte vor dem Einbinden auf: aktive Wartung (letzter Commit), Download-Zahlen, PHP-Versionsanforderung, offene Sicherheitsmeldungen und eine klare Lizenz.</p>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Nicht selbst bauen, was es gibt</b>Logging, HTTP, Datum, Validierung, UUID, Geld-Arithmetik – für all das gibt es ausgereifte, getestete Pakete. Eigenbau ist fast immer schlechter und teurer in der Pflege. Prüfe aber jede Abhängigkeit: Jedes Paket ist Code, für den du mitverantwortlich wirst (<code class="inline">composer audit</code>).</div>
</div></section><section class="part-divider"><div class="part-kicker">Teil 17</div><h1>In Produktion</h1><div class="part-desc">Code, der nur lokal läuft, hilft niemandem: Projektstruktur, Deployment, Beobachtbarkeit, Internationalisierung, Migration – und wie es weitergeht.</div><div class="part-chapters"><span>92 · Projektstruktur & Organisation</span><span>93 · Deployment & Betrieb</span><span>94 · Logging & Monitoring</span><span>95 · Internationalisierung</span><span>96 · Migration: von PHP 5 zu 8.5</span><span>97 · Weiterlernen & Community</span></div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 92</span><h1>Projektstruktur & Organisation</h1></div><p class="lead">Eine klare Ordnerstruktur macht ein Projekt für jeden sofort verständlich. In der PHP-Welt hat sich ein Aufbau etabliert, an den sich Frameworks und Werkzeuge halten.</p>
<h2>Der übliche Aufbau</h2>
<pre>projekt/
├─ public/ <span class="c"># einziges web-erreichbares Verzeichnis (index.php)</span>
├─ src/ <span class="c"># dein Code (PSR-4, Namespace App\)</span>
├─ tests/ <span class="c"># die Tests</span>
├─ config/ <span class="c"># Konfiguration</span>
├─ vendor/ <span class="c"># Composer-Abhängigkeiten (nicht eingecheckt)</span>
├─ .env <span class="c"># lokale Umgebungswerte (nicht eingecheckt)</span>
├─ composer.json
└─ composer.lock</pre>
<h2>Nur public/ ist öffentlich</h2>
<p>Der Webserver zeigt ausschließlich auf <code class="inline">public/</code>. So liegen Code, Konfiguration und <code class="inline">.env</code> außerhalb der Reichweite des Browsers – niemand kann sie abrufen:</p>
<pre><span class="c"># public/index.php – der einzige Einstiegspunkt</span>
<span class="k">require</span> __DIR__ . <span class="s">"/../vendor/autoload.php"</span>;
<span class="c">// ... Anwendung starten ...</span></pre>
<h2>Konfiguration über Umgebung</h2>
<p>Zugangsdaten und umgebungsabhängige Werte gehören nicht in den Code, sondern in Umgebungsvariablen (12-Factor-Prinzip). Lokal über eine <code class="inline">.env</code>-Datei, die <b>nie</b> ins Repository kommt:</p>
<pre><span class="c"># .env (nur lokal, in .gitignore)</span>
<span class="c"># DB_DSN=mysql:host=localhost;dbname=shop</span>
<span class="v">$dsn</span> = <span class="k">getenv</span>(<span class="s">"DB_DSN"</span>); <span class="c">// liest die Umgebungsvariable</span></pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Geheimnisse niemals einchecken</b>Passwörter, API-Schlüssel und <code class="inline">.env</code>-Dateien gehören in die <code class="inline">.gitignore</code>, nie ins Repository. Versehentlich committete Schlüssel gelten als kompromittiert und müssen ausgetauscht werden. Eine <code class="inline">.env.example</code> ohne echte Werte dokumentiert, welche Variablen nötig sind.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 93</span><h1>Deployment & Betrieb</h1></div><p class="lead">Deployment bringt deinen Code auf den Server – idealerweise vorhersehbar, wiederholbar und ohne Ausfall. Ein paar Bausteine machen aus „Dateien hochladen" einen verlässlichen Prozess.</p>
<h2>Die Schritte eines Deployments</h2>
<pre><span class="c"># auf dem Server, in dieser Reihenfolge:</span>
git pull
composer install --no-dev --optimize-autoloader <span class="c"># nur Produktionspakete</span>
php bin/migrate <span class="c"># DB-Schema aktualisieren</span>
<span class="c"># Caches aufwärmen, OPcache zurücksetzen</span></pre>
<h2>Atomares Deployment</h2>
<p>Statt im laufenden Verzeichnis zu arbeiten, baust du eine neue Version daneben auf und schaltest am Ende per Symlink um – so ist der Wechsel augenblicklich und rückkehrbar:</p>
<pre>releases/2026-05-31-1430/ <span class="c"># neue Version, fertig gebaut</span>
current -> releases/2026-05-31-1430 <span class="c"># Symlink zeigt auf aktuell</span>
<span class="c"># Rollback = Symlink auf die vorige Release zurücksetzen</span></pre>
<p>Werkzeuge wie Deployer oder die Pipelines deiner CI automatisieren genau das.</p>
<h2>Produktionseinstellungen</h2>
<p>Auf dem Server gelten andere Regeln als lokal – Fehler werden geloggt, nicht angezeigt, und OPcache läuft:</p>
<pre><span class="c"># php.ini (Produktion)</span>
<span class="c"># display_errors = Off ; keine internen Details an Nutzer</span>
<span class="c"># log_errors = On</span>
<span class="c"># opcache.enable = 1</span>
<span class="c"># opcache.validate_timestamps = 0 ; max. Tempo</span></pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Reproduzierbar statt manuell</b>Manuelles Hochladen per FTP ist fehleranfällig. Ziel ist ein Deployment, das per Knopfdruck immer dieselben Schritte ausführt, sich bei Fehlern sauber zurückrollen lässt und keine Downtime erzeugt. Halte App und Konfiguration getrennt (Umgebungsvariablen), dann läuft derselbe Code in Test und Produktion.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 94</span><h1>Logging & Monitoring</h1></div><p class="lead">In Produktion siehst du nicht zu, wie der Code läuft – du musst es ablesen können. Logging schreibt mit, was passiert; Monitoring schlägt Alarm, wenn etwas aus dem Ruder läuft.</p>
<h2>Sinnvoll loggen mit Stufen</h2>
<p>Nicht alles ist gleich wichtig. Die PSR-3-Stufen (debug, info, warning, error …) trennen Rauschen von echten Problemen:</p>
<pre><span class="v">$log</span>-><span class="f">info</span>(<span class="s">"Bestellung aufgegeben"</span>, [<span class="s">"id"</span> => <span class="s">42</span>]);
<span class="v">$log</span>-><span class="f">warning</span>(<span class="s">"Zahlung verzögert"</span>, [<span class="s">"id"</span> => <span class="s">42</span>]);
<span class="v">$log</span>-><span class="f">error</span>(<span class="s">"Zahlung fehlgeschlagen"</span>, [<span class="s">"id"</span> => <span class="s">42</span>, <span class="s">"grund"</span> => <span class="v">$e</span>-><span class="f">getMessage</span>()]);</pre>
<h2>Strukturiert loggen</h2>
<p>Logs als JSON statt Fließtext lassen sich maschinell durchsuchen und auswerten – wichtig, sobald mehrere Server schreiben:</p>
<pre><span class="c">// {"level":"error","msg":"Zahlung fehlgeschlagen","id":42,"zeit":"2026-05-31T14:30:00Z"}</span>
<span class="c">// Der Kontext (zweites Argument) landet als Felder im JSON.</span></pre>
<h2>Fehler zentral erfassen</h2>
<p>Ein Dienst wie Sentry sammelt unbehandelte Ausnahmen samt Stacktrace und Kontext an einer Stelle – du erfährst von Fehlern, bevor Nutzer sie melden. Angebunden über den zentralen Exception-Handler (Teil 12).</p>
<h2>Gesundheit prüfbar machen</h2>
<p>Ein schlanker Endpunkt erlaubt automatische Überwachung – ist die App erreichbar, die DB verbunden?</p>
<pre><span class="c">// GET /health</span>
<span class="k">try</span> {
<span class="v">$pdo</span>-><span class="f">query</span>(<span class="s">"SELECT 1"</span>);
<span class="k">echo</span> <span class="k">json_encode</span>([<span class="s">"status"</span> => <span class="s">"ok"</span>]);
} <span class="k">catch</span> (<span class="t">\Throwable</span>) {
<span class="k">http_response_code</span>(<span class="s">503</span>);
<span class="k">echo</span> <span class="k">json_encode</span>([<span class="s">"status"</span> => <span class="s">"db-fehler"</span>]);
}</pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body"><b>Keine Geheimnisse in Logs</b>Logge niemals Passwörter, Tokens, Kreditkarten oder vollständige personenbezogene Daten – Logs werden weitergeleitet, archiviert und von vielen gelesen. Logge IDs und Kontext, nicht Inhalte. Beachte dabei auch Datenschutz (DSGVO): so wenig personenbezogen wie möglich.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 95</span><h1>Internationalisierung</h1></div><p class="lead">Sobald eine Anwendung mehrere Sprachen oder Regionen bedient, reicht fest verdrahteter Text nicht mehr. Internationalisierung (i18n) trennt Inhalte und Formate von der Sprache des Nutzers.</p>
<h2>Texte übersetzbar machen</h2>
<p>Statt Texte im Code zu verteilen, hältst du sie in Übersetzungsdateien und referenzierst Schlüssel:</p>
<pre><span class="c">// de.php: return ["gruss" => "Hallo, :name"];</span>
<span class="c">// en.php: return ["gruss" => "Hello, :name"];</span>
<span class="v">$texts</span> = <span class="k">require</span> <span class="s">"lang/$sprache.php"</span>;
<span class="k">echo</span> <span class="k">strtr</span>(<span class="v">$texts</span>[<span class="s">"gruss"</span>], [<span class="s">":name"</span> => <span class="s">"Anna"</span>]); <span class="c">// Hallo, Anna</span></pre>
<h2>Zahlen, Währung & Datum lokal formatieren</h2>
<p>Die <code class="inline">intl</code>-Erweiterung formatiert korrekt für jede Region – Tausendertrenner, Währungssymbol, Datumsreihenfolge:</p>
<pre><span class="v">$f</span> = <span class="k">new</span> <span class="t">\NumberFormatter</span>(<span class="s">"de_DE"</span>, <span class="t">\NumberFormatter</span>::<span class="t">CURRENCY</span>);
<span class="k">echo</span> <span class="v">$f</span>-><span class="f">formatCurrency</span>(<span class="s">1234.5</span>, <span class="s">"EUR"</span>); <span class="c">// 1.234,50 €</span>
<span class="v">$us</span> = <span class="k">new</span> <span class="t">\NumberFormatter</span>(<span class="s">"en_US"</span>, <span class="t">\NumberFormatter</span>::<span class="t">CURRENCY</span>);
<span class="k">echo</span> <span class="v">$us</span>-><span class="f">formatCurrency</span>(<span class="s">1234.5</span>, <span class="s">"USD"</span>); <span class="c">// $1,234.50</span></pre>
<h2>Pluralregeln</h2>
<p>Sprachen pluralisieren unterschiedlich. Der <code class="inline">MessageFormatter</code> wählt die richtige Form:</p>
<pre><span class="v">$m</span> = <span class="s">"{n, plural, =0{keine Nachrichten} one{eine Nachricht} other{# Nachrichten}}"</span>;
<span class="k">echo</span> <span class="t">\MessageFormatter</span>::<span class="f">formatMessage</span>(<span class="s">"de_DE"</span>, <span class="v">$m</span>, [<span class="s">"n"</span> => <span class="s">3</span>]); <span class="c">// 3 Nachrichten</span></pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Von Anfang an trennen</b>Es ist viel teurer, Texte nachträglich aus dem Code zu lösen, als sie gleich auszulagern. Halte Datum, Zahl und Währung konsequent über <code class="inline">intl</code> formatiert und speichere intern neutral (UTC, Cent, Roh-Werte) – formatiert wird erst bei der Ausgabe, passend zur Region des Nutzers.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 96</span><h1>Migration: von PHP 5 zu 8.5</h1></div><p class="lead">Viele Bestandssysteme laufen noch auf alten PHP-Versionen. Der Sprung auf 8.5 bringt enorme Vorteile bei Tempo, Sicherheit und Sprache – mit der richtigen Strategie ist er gut beherrschbar.</p>
<h2>Warum überhaupt migrieren</h2>
<p>Alte Versionen bekommen keine Sicherheitsupdates mehr und sind damit ein Risiko. Dazu kommt: PHP 8 ist um ein Vielfaches schneller als PHP 5 und bringt Typen, Enums und vieles mehr.</p>
<h2>Schrittweise statt in einem Sprung</h2>
<p>Gehe Version für Version (5.6 → 7.x → 8.x) und teste nach jeder Stufe. Die häufigsten Brüche: entfernte Funktionen, strengere Typprüfung, geändertes Standardverhalten.</p>
<pre><span class="c">// typischer Bruch: früher Warnung, heute TypeError</span>
<span class="k">function</span> <span class="f">x</span>(<span class="t">array</span> <span class="v">$a</span>) {}
<span class="f">x</span>(<span class="s">"kein array"</span>); <span class="c">// PHP 8: TypeError statt stiller Umwandlung</span></pre>
<h2>Werkzeuge nehmen Arbeit ab</h2>
<p><b>Rector</b> baut Code automatisch auf eine neue PHP-Version um; <b>PHPStan/Psalm</b> finden vorab, was brechen wird:</p>
<pre><span class="c"># Vorschau, was Rector ändern würde</span>
vendor/bin/rector process src --dry-run</pre>
<h2>Absichern durch Tests</h2>
<p>Vor einer Migration ist eine Testabdeckung Gold wert: Sie zeigt nach jedem Schritt, ob noch alles funktioniert. Fehlen Tests, schreibe wenigstens für die kritischsten Pfade welche, bevor du beginnst.</p>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Erst sichtbar machen, dann ändern</b>Aktiviere Fehlerberichte für Deprecations, lass statische Analyse und Rector laufen, migriere in kleinen Schritten und teste nach jedem. So wird aus einem gefürchteten Großprojekt eine Reihe überschaubarer, jeweils überprüfbarer Änderungen.</div>
</div></section><section class="chapter"><div class="chapter-head"><span class="chapter-num">Kapitel 97</span><h1>Weiterlernen & Community</h1></div><p class="lead">Du hast den Bogen von der ersten Zeile bis in Architektur, Async und Betrieb gespannt. Sprachen und Ökosysteme entwickeln sich weiter – hier, wie du am Ball bleibst und wo es weitergeht.</p>
<h2>Die verlässlichen Quellen</h2>
<p>Das offizielle Handbuch auf <code class="inline">php.net</code> ist erstaunlich gut und immer die erste Adresse für Funktionen und Verhalten. Neuerungen entstehen transparent als <b>RFCs</b> im PHP-Wiki – wer sie verfolgt, sieht die Zukunft der Sprache.</p>
<h2>Qualität im Ökosystem</h2>
<p>Pakete findest und bewertest du auf <code class="inline">packagist.org</code>; die Standards der PHP-FIG (PSR) zeigen, wie sauberer, kompatibler Code aussieht. Die Dokumentationen von Symfony und Laravel sind selbst ohne deren Nutzung lehrreich.</p>
<h2>Dranbleiben</h2>
<p>PHP bekommt jährlich im November eine neue Version. Lies die Migrationsleitfäden, probiere Neues in einem kleinen Projekt aus und halte Abhängigkeiten regelmäßig aktuell (<code class="inline">composer outdated</code>, <code class="inline">composer audit</code>). Konferenzen und lokale User-Groups verbinden mit Menschen, die vor denselben Fragen stehen.</p>
<h2>Wie es weitergeht</h2>
<p>Der nächste Schritt ist kein weiteres Kapitel, sondern ein eigenes Projekt. Bau etwas, das dich interessiert – klein anfangen, die Prinzipien aus diesem Guide anwenden, an echten Problemen wachsen. Genau so wird aus Wissen Können.</p>
<div class="callout note">
<div class="callout-icon">✓</div>
<div class="callout-body"><b>Das eine Prinzip</b>Wenn von diesem Guide eine Sache bleiben soll: Schreib Code für den nächsten Menschen, der ihn liest – oft bist das du selbst in einem halben Jahr. Klarheit schlägt Cleverness, Lesbarkeit schlägt Kürze, und ein verständliches Programm ist mehr wert als ein raffiniertes. Viel Freude beim Bauen.</div>
</div></section></body></html>